Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2013 Regents of the University of California and the
4
 *             National Center for Ecological Analysis and Synthesis
5
 *
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 */
21
package edu.ucsb.nceas.metacat.authentication;
22

    
23
import java.io.File;
24
import java.io.FileOutputStream;
25
import java.io.IOException;
26
import java.io.OutputStreamWriter;
27
import java.io.UnsupportedEncodingException;
28
import java.net.ConnectException;
29
import java.security.GeneralSecurityException;
30
import java.util.Enumeration;
31
import java.util.HashMap;
32
import java.util.Hashtable;
33
import java.util.List;
34
import java.util.Random;
35
import java.util.Vector;
36

    
37
import javax.crypto.Cipher;
38
import javax.crypto.SecretKey;
39
import javax.crypto.SecretKeyFactory;
40
import javax.crypto.spec.PBEKeySpec;
41
import javax.crypto.spec.PBEParameterSpec;
42

    
43
import org.apache.commons.codec.binary.Base64;
44
import org.apache.commons.configuration.ConfigurationException;
45
import org.apache.commons.configuration.XMLConfiguration;
46
import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
47
import org.apache.commons.logging.Log;
48
import org.apache.commons.logging.LogFactory;
49

    
50
import edu.ucsb.nceas.metacat.AuthInterface;
51
import edu.ucsb.nceas.metacat.AuthLdap;
52
import edu.ucsb.nceas.metacat.properties.PropertyService;
53
import edu.ucsb.nceas.metacat.util.SystemUtil;
54
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
55

    
56
/**
57
 * This an authentication class base on a username/password file.
58
 * It is an alternative authentication mechanism of the ldap authentication.
59
 * This is a singleton class and the password file looks like:
60
 *<?xml version="1.0" encoding="UTF-8" ?>
61
 * <subjects>
62
 *  <users>
63
 *      <user name="uid=tao,o=NCEAS,dc=ecoinformatics,dc=org">
64
 *          <password>*******</password>
65
 *          <group>nceas-dev</group>
66
 *      </user>
67
 *  </users>
68
 *  <groups>
69
 *    <group name="nceas-dev"/>
70
 *  </groups>
71
 * </subjects>
72
 * http://commons.apache.org/proper/commons-configuration/userguide/howto_xml.html
73
 * @author tao
74
 *
75
 */
76
public class AuthFile implements AuthInterface {
77
    private static final String ORGANIZATION = "UNkown";
78
    private static final String NAME = "name";
79
    private static final String PASSWORD = "password";
80
    private static final String SLASH = "/";
81
    private static final String AT = "@";
82
    private static final String SUBJECTS = "subjects";
83
    private static final String USERS = "users";
84
    private static final String USER = "user";
85
    private static final String GROUPS = "groups";
86
    private static final String GROUP = "group";
87
    private static final String INITCONTENT = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"+
88
                                    "<"+SUBJECTS+">\n"+"<"+USERS+">\n"+"</"+USERS+">\n"+"<"+GROUPS+">\n"+"</"+GROUPS+">\n"+"</"+SUBJECTS+">\n";
89
    
90
    private static final byte[] SALT = {
91
        (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
92
        (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
93
    };
94
    private static Log log = LogFactory.getLog(AuthFile.class);
95
    private static AuthFile authFile = null;
96
    private XMLConfiguration userpassword = null;
97
    private String authURI = null;
98
    private static String passwordFilePath = null;
99
    private static  char[] masterPass = "enfldsgbnlsngdlksdsgm".toCharArray();
100
    /**
101
     * Get the instance of the AuthFile
102
     * @return
103
     * @throws AuthenticationException
104
     */
105
    public static AuthFile getInstance() throws AuthenticationException {
106
        if(authFile == null) {
107
            authFile = new AuthFile();
108
        }
109
        return authFile;
110
    }
111
    
112
    /**
113
     * Get the instance of the AuthFile from specified password file
114
     * @return
115
     * @throws AuthenticationException
116
     */
117
    public static AuthFile getInstance(String passwordFile) throws AuthenticationException {
118
        passwordFilePath = passwordFile;
119
        if(authFile == null) {
120
            authFile = new AuthFile();
121
        }
122
        return authFile;
123
    }
124
    
125
    /**
126
     * Constructor
127
     */
128
    private AuthFile() throws AuthenticationException {
129
        try {
130
            init();
131
        } catch (Exception e) {
132
            throw new AuthenticationException(e.getMessage());
133
        }
134
        
135
    }
136
    
137
    /*
138
     * Initialize the user/password configuration
139
     */
140
    private void init() throws PropertyNotFoundException, IOException, ConfigurationException {
141
        if(passwordFilePath == null) {
142
            passwordFilePath  = PropertyService.getProperty("auth.file.path");
143
        }
144
        File passwordFile = new File(passwordFilePath);
145
        try {
146
            String password = PropertyService.getProperty("auth.file.pass");
147
            if(password != null && !password.trim().equals("")) {
148
                masterPass = password.toCharArray();
149
            }
150
            authURI = SystemUtil.getContextURL();
151
        }catch(PropertyNotFoundException e) {
152
            log.warn("AuthFile.init - can't find the auth.file.pass in the metacat.properties. Metacat will use the default one as password.");
153
        }
154
       
155
        //if the password file doesn't exist, create a new one and set the initial content
156
        if(!passwordFile.exists()) {
157
            passwordFile.createNewFile();
158
            OutputStreamWriter writer = null;
159
            FileOutputStream output = null;
160
            try {
161
              output = new FileOutputStream(passwordFile);
162
              writer = new OutputStreamWriter(output, "UTF-8");
163
              writer.write(INITCONTENT);
164
            } finally {
165
              writer.close();
166
              output.close();
167
            }
168
          }
169
          userpassword = new XMLConfiguration(passwordFile);
170
          userpassword.setExpressionEngine(new XPathExpressionEngine());
171
          userpassword.setAutoSave(true);
172
          userpassword.setDelimiterParsingDisabled(true);
173
          userpassword.setAttributeSplittingDisabled(true);
174
    }
175
    
176
    @Override
177
    public boolean authenticate(String user, String password)
178
                    throws AuthenticationException {
179
        String passwordRecord = userpassword.getString(USERS+SLASH+USER+"["+AT+NAME+"='"+user+"']"+SLASH+PASSWORD);
180
        if(passwordRecord != null) {
181
            try {
182
                passwordRecord = decrypt(passwordRecord);
183
            } catch (Exception e) {
184
                throw new AuthenticationException("AuthFile.authenticate - can't decrypt the password for the user "+user+" since "+e.getMessage());
185
            }
186
            if(passwordRecord.equals(password)) {
187
                return true;
188
            }
189
        }
190
        return false;
191
    }
192
    
193
    @Override
194
    /**
195
     * Get all users. This is two-dimmention array. Each row is a user. The first element of
196
     * a row is the user name.
197
     */
198
    public String[][] getUsers(String user, String password)
199
                    throws ConnectException {
200
        List<Object> users = userpassword.getList(USERS+SLASH+USER+SLASH+AT+NAME);
201
        if(users != null && users.size() > 0) {
202
            String[][] usersArray = new String[users.size()][1];
203
            for(int i=0; i<users.size(); i++) {
204
                usersArray[i][0] = (String) users.get(i);
205
            }
206
            return usersArray;
207
        }
208
        return null;
209
    }
210
    
211
    @Override
212
    public String[] getUserInfo(String user, String password)
213
                    throws ConnectException {
214
        // TODO Auto-generated method stub
215
        return null;
216
    }
217
    
218
    
219
    @Override
220
    /**
221
     * Get the users for a particular group from the authentication service
222
     * The null will return if there is no user.
223
     * @param user
224
     *            the user for authenticating against the service
225
     * @param password
226
     *            the password for authenticating against the service
227
     * @param group
228
     *            the group whose user list should be returned
229
     * @returns string array of the user names belonging to the group
230
     */
231
    public String[] getUsers(String user, String password, String group)
232
                    throws ConnectException {
233
        List<Object> users = userpassword.getList(USERS+SLASH+USER+"["+GROUP+"='"+group+"']"+SLASH+AT+NAME);
234
        if(users != null && users.size() > 0) {
235
            String[] usersArray = new String[users.size()];
236
            for(int i=0; i<users.size(); i++) {
237
                usersArray[i] = (String) users.get(i);
238
            }
239
            return usersArray;
240
        }
241
        return null;
242
    }
243
    
244
    @Override
245
    /**
246
     * Get all groups from the authentication service. It returns a two dimmension array. Each row is a
247
     * group. The first column is the group name. The null will return if no group found.
248
     */
249
    public String[][] getGroups(String user, String password)
250
                    throws ConnectException {
251
        List<Object> groups = userpassword.getList(GROUPS+SLASH+GROUP+SLASH+AT+NAME);
252
        if(groups!= null && groups.size() >0) {
253
            String[][] groupsArray = new String[groups.size()][1];
254
            for(int i=0; i<groups.size(); i++) {
255
                groupsArray[i][0] = (String) groups.get(i);
256
            }
257
            return groupsArray;
258
        }
259
        return null;
260
    }
261
    
262
    @Override
263
    /**
264
     * Get groups from a specified user. It returns two dimmension array. Each row is a
265
     * group. The first column is the group name. The null will return if no group found.
266
     */
267
    public String[][] getGroups(String user, String password, String foruser)
268
                    throws ConnectException {
269
        List<Object> groups = userpassword.getList(USERS+SLASH+USER+"["+AT+NAME+"='"+foruser+"']"+SLASH+GROUP);
270
        if(groups != null && groups.size() > 0) {
271
            String[][] groupsArray = new String[groups.size()][1];
272
            for(int i=0; i<groups.size(); i++) {
273
                groupsArray[i][0] = (String) groups.get(i);
274
            }
275
            return groupsArray;
276
        }
277
        return null;
278
    }
279
    
280
    @Override
281
    public HashMap<String, Vector<String>> getAttributes(String foruser)
282
                    throws ConnectException {
283
        // TODO Auto-generated method stub
284
        return null;
285
    }
286
    
287
    @Override
288
    public HashMap<String, Vector<String>> getAttributes(String user,
289
                    String password, String foruser) throws ConnectException {
290
        // TODO Auto-generated method stub
291
        return null;
292
    }
293
    
294
    @Override
295
    public String getPrincipals(String user, String password)
296
                    throws ConnectException {
297
            StringBuffer out = new StringBuffer();
298

    
299
            out.append("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
300
            out.append("<principals>\n");
301
            out.append("  <authSystem URI=\"" +authURI
302
                    + "\" organization=\"" + ORGANIZATION + "\">\n");
303

    
304
            // get all groups for directory context
305
            String[][] groups = getGroups(user, password);
306
            String[][] users = getUsers(user, password);
307
            int userIndex = 0;
308

    
309
            // for the groups and users that belong to them
310
            if (groups != null && users != null && groups.length > 0) {
311
                for (int i = 0; i < groups.length; i++) {
312
                    out.append("    <group>\n");
313
                    out.append("      <groupname>" + groups[i][0] + "</groupname>\n");
314
                    if(groups[i].length > 1) {
315
                        out.append("      <description>" + groups[i][1] + "</description>\n");
316
                    }
317
                    String[] usersForGroup = getUsers(user, password, groups[i][0]);
318
                    if(usersForGroup != null) {
319
                        for (int j = 0; j < usersForGroup.length; j++) {
320
                            userIndex = AuthLdap.searchUser(usersForGroup[j], users);
321
                            out.append("      <user>\n");
322

    
323
                            if (userIndex < 0) {
324
                                out.append("        <username>" + usersForGroup[j]
325
                                        + "</username>\n");
326
                            } else {
327
                                out.append("        <username>" + users[userIndex][0]
328
                                        + "</username>\n");
329
                                if(users[userIndex].length >=2) {
330
                                    out.append("        <name>" + users[userIndex][1]
331
                                                    + "</name>\n");
332
                                }
333
                                if(users[userIndex].length >=3) {
334
                                    out.append("        <email>" + users[userIndex][2]
335
                                                    + "</email>\n");
336
                                }
337
                               
338
                            }
339

    
340
                            out.append("      </user>\n");
341
                        }
342
                    }
343
                   
344
                    out.append("    </group>\n");
345
                }
346
            }
347

    
348
            if (users != null) {
349
                // for the users not belonging to any grou8p
350
                for (int j = 0; j < users.length; j++) {
351
                    out.append("    <user>\n");
352
                    out.append("      <username>" + users[j][0] + "</username>\n");
353
                    if(users[userIndex].length >=2) {
354
                        out.append("      <name>" + users[j][1] + "</name>\n");
355
                    }
356
                    if(users[userIndex].length >=3) {
357
                        out.append("      <email>" + users[j][2] + "</email>\n");
358
                    }
359
                   
360
                    out.append("    </user>\n");
361
                }
362
            }
363

    
364
            out.append("  </authSystem>\n");
365
        
366
        out.append("</principals>");
367
        return out.toString();
368
    }
369
    
370
    /**
371
     * Add a user to the file
372
     * @param userName the name of the user
373
     * @param groups  the groups the user belong to. The group should exist in the file
374
     * @param password  the password of the user
375
     */
376
    public void addUser(String userName, String[] groups, String password) throws AuthenticationException{
377
        if(userName == null || userName.trim().equals("")) {
378
            throw new AuthenticationException("AuthFile.addUser - can't add a user whose name is null or blank.");
379
        }
380
        if(password == null || password.trim().equals("")) {
381
            throw new AuthenticationException("AuthFile.addUser - can't add a user whose password is null or blank.");
382
        }
383
        try {
384
            password = encrypt(password);
385
        } catch (Exception e) {
386
            throw new AuthenticationException("AuthFile.addUser - can't encript the password since "+e.getMessage());
387
        }
388
        
389
        if(!userExists(userName)) {
390
            if(userpassword != null) {
391
              userpassword.addProperty(USERS+" "+USER+AT+NAME, userName);
392
              userpassword.addProperty(USERS+SLASH+USER+"["+AT+NAME+"='"+userName+"']"+" "+PASSWORD, password);
393
              if(groups != null) {
394
                  for(int i=0; i<groups.length; i++) {
395
                      String group = groups[i];
396
                      if(group != null && !group.trim().equals("")) {
397
                          if(groupExists(group)) {
398
                              userpassword.addProperty(USERS+SLASH+USER+"["+AT+NAME+"='"+userName+"']"+" "+GROUP, group);
399
                          }
400
                      }
401
                  }
402
              }
403
              //userpassword.reload();
404
             }
405
        } else {
406
            throw new AuthenticationException("AuthFile.addUser - can't add the user "+userName+" since it already exists.");
407
        }
408
    }
409
    
410
    /**
411
     * Add a group into the file
412
     * @param groupName the name of group
413
     */
414
    public void addGroup(String groupName) throws AuthenticationException{
415
        if(groupName == null || groupName.trim().equals("")) {
416
            throw new AuthenticationException("AuthFile.addGroup - can't add a group whose name is null or blank.");
417
        }
418
        if(!groupExists(groupName)) {
419
            if(userpassword != null) {
420
              userpassword.addProperty(GROUPS+" "+GROUP+AT+NAME, groupName);
421
              //userpassword.reload();
422
             }
423
        } else {
424
            throw new AuthenticationException("AuthFile.addGroup - can't add the group "+groupName+" since it already exists.");
425
        }
426
    }
427
    
428
    /**
429
     * Reset the password for the user
430
     * @param userName  the name of the user. The user should already exist
431
     * @param password  the password of the user.
432
     * @return
433
     */
434
    public String resetPassword(String userName) throws AuthenticationException {
435
        String password = new String(RandomPasswordGenerator.generatePswd(10, 12, 4, 3, 2));
436
        changePassword(userName, password);
437
        return password;
438
    }
439
    
440
    /**
441
     * Change the password of the user to the new one. But we need to know the old password
442
     * @param usrName the specified user.   
443
     * @param oldPassword the old password of the user      
444
     * @param newPassword the new password which will be set
445
     */
446
    public void modifyPassword(String userName, String oldPassword, String newPassword) throws AuthenticationException {
447
        if(!authenticate(userName, oldPassword)) {
448
            throw new AuthenticationException("AuthFile.modifyUserPassword - the username or the old password is not correct");
449
        }
450
        changePassword(userName, newPassword);
451
    }
452
    
453
    /**
454
     * Add a user to a group
455
     * @param userName  the name of the user. the user should already exist
456
     * @param group  the name of the group. the group should already exist
457
     */
458
    public void addUserToGroup(String userName, String group) throws AuthenticationException {
459
        if(!userExists(userName)) {
460
            throw new AuthenticationException("AuthFile.addUserToGroup - the user "+userName+ " doesn't exist.");
461
        }
462
        if(!groupExists(group)) {
463
            throw new AuthenticationException("AuthFile.addUserToGroup - the group "+group+ " doesn't exist.");
464
        }
465
        List<Object> existingGroups = userpassword.getList(USERS+SLASH+USER+"["+AT+NAME+"='"+userName+"']"+SLASH+GROUP);
466
        if(existingGroups.contains(group)) {
467
            throw new AuthenticationException("AuthFile.addUserToGroup - the user "+userName+ " already is the memember of the group "+group);
468
        }
469
        userpassword.addProperty(USERS+SLASH+USER+"["+AT+NAME+"='"+userName+"']"+" "+GROUP, group);
470
    }
471
    
472
    /**
473
     * Remove a user from a group.
474
     * @param userName  the name of the user. the user should already exist.
475
     * @param group the name of the group
476
     */
477
    public void removeUserFromGroup(String userName, String group) throws AuthenticationException{
478
        if(!userExists(userName)) {
479
            throw new AuthenticationException("AuthFile.removeUserFromGroup - the user "+userName+ " doesn't exist.");
480
        }
481
        if(!groupExists(group)) {
482
            throw new AuthenticationException("AuthFile.removeUserFromGroup - the group "+group+ " doesn't exist.");
483
        }
484
        String key = USERS+SLASH+USER+"["+AT+NAME+"='"+userName+"']"+SLASH+GROUP;
485
        List<Object> existingGroups = userpassword.getList(key);
486
        if(!existingGroups.contains(group)) {
487
            throw new AuthenticationException("AuthFile.removeUserFromGroup - the user "+userName+ " isn't the memember of the group "+group);
488
        } else {
489
            userpassword.clearProperty(key+"[.='"+group+"']");
490
        }
491
    }
492
    
493
    /**
494
     * Change the password of the user to the specified one
495
     * @param userName
496
     * @param password
497
     */
498
    private void changePassword(String userName, String password) throws AuthenticationException{
499
        if(!userExists(userName)) {
500
            throw new AuthenticationException("AuthFile.changePassword - can't change the password for the user "+userName+" since it doesn't eixt.");
501
        }
502
        String encryped = null;
503
        try {
504
            encryped = encrypt(password);
505
        } catch (Exception e) {
506
            throw new AuthenticationException("AuthFile.changepassword - can't encrype the new password for the user "+userName+" since "+e.getMessage());
507
        }
508
        userpassword.setProperty(USERS+SLASH+USER+"["+AT+NAME+"='"+userName+"']"+SLASH+PASSWORD, encryped);
509
    }
510
    
511
    /**
512
     * If the specified user name exist or not
513
     * @param userName the name of the user
514
     * @return true if the user eixsit
515
     */
516
    private boolean userExists(String userName) throws AuthenticationException{
517
        if(userName == null || userName.trim().equals("")) {
518
            throw new AuthenticationException("AuthFile.userExist - can't judge if a user exists when its name is null or blank.");
519
        }
520
        List<Object> users = userpassword.getList(USERS+SLASH+USER+SLASH+AT+NAME);
521
        if(users != null && users.contains(userName)) {
522
            return true;
523
        } else {
524
            return false;
525
        }
526
    }
527
    
528
    /**
529
     * If the specified group exist or not
530
     * @param groupName the name of the group
531
     * @return true if the user exists
532
     */
533
    private boolean groupExists(String groupName) throws AuthenticationException{
534
        if(groupName == null || groupName.trim().equals("")) {
535
            throw new AuthenticationException("AuthFile.groupExist - can't judge if a group exists when its name is null or blank.");
536
        }
537
        List<Object> groups = userpassword.getList(GROUPS+SLASH+GROUP+SLASH+AT+NAME);
538
        if(groups != null && groups.contains(groupName)) {
539
            return true;
540
        } else {
541
            return false;
542
        }
543
    }
544
    
545
    /*
546
     * Encrypt a string
547
     */
548
    private static String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
549
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
550
        //System.out.println("===================== tha master password "+masterPass);
551
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(masterPass));
552
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
553
        pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
554
        return base64Encode(pbeCipher.doFinal(property.getBytes("UTF-8")));
555
    }
556

    
557
    /*
558
     * Transform a byte array to a string
559
     */
560
    private static String base64Encode(byte[] bytes) {
561
        return Base64.encodeBase64String(bytes);
562
    }
563

    
564
    /*
565
     * Decrypt a string
566
     */
567
    private static String decrypt(String property) throws GeneralSecurityException, IOException {
568
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
569
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(masterPass));
570
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
571
        pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
572
        return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
573
    }
574

    
575
    /*
576
     * Transform a string to a byte array
577
     */
578
    private static byte[] base64Decode(String property) throws IOException {
579
        return Base64.decodeBase64(property);
580
    }
581
    
582
    /**
583
     * A internal class to generate random passowrd
584
     * @author tao
585
     *
586
     */
587
    static class RandomPasswordGenerator {
588
        private static final String ALPHA_CAPS  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
589
        private static final String ALPHA   = "abcdefghijklmnopqrstuvwxyz";
590
        private static final String NUM     = "0123456789";
591
        private static final String SPL_CHARS   = "!$^_-/";
592
     
593
        public static char[] generatePswd(int minLen, int maxLen, int noOfCAPSAlpha,
594
                int noOfDigits, int noOfSplChars) {
595
            if(minLen > maxLen)
596
                throw new IllegalArgumentException("Min. Length > Max. Length!");
597
            if( (noOfCAPSAlpha + noOfDigits + noOfSplChars) > minLen )
598
                throw new IllegalArgumentException
599
                ("Min. Length should be atleast sum of (CAPS, DIGITS, SPL CHARS) Length!");
600
            Random rnd = new Random();
601
            int len = rnd.nextInt(maxLen - minLen + 1) + minLen;
602
            char[] pswd = new char[len];
603
            int index = 0;
604
            for (int i = 0; i < noOfCAPSAlpha; i++) {
605
                index = getNextIndex(rnd, len, pswd);
606
                pswd[index] = ALPHA_CAPS.charAt(rnd.nextInt(ALPHA_CAPS.length()));
607
            }
608
            for (int i = 0; i < noOfDigits; i++) {
609
                index = getNextIndex(rnd, len, pswd);
610
                pswd[index] = NUM.charAt(rnd.nextInt(NUM.length()));
611
            }
612
            for (int i = 0; i < noOfSplChars; i++) {
613
                index = getNextIndex(rnd, len, pswd);
614
                pswd[index] = SPL_CHARS.charAt(rnd.nextInt(SPL_CHARS.length()));
615
            }
616
            for(int i = 0; i < len; i++) {
617
                if(pswd[i] == 0) {
618
                    pswd[i] = ALPHA.charAt(rnd.nextInt(ALPHA.length()));
619
                }
620
            }
621
            return pswd;
622
        }
623
     
624
        private static int getNextIndex(Random rnd, int len, char[] pswd) {
625
            int index = rnd.nextInt(len);
626
            while(pswd[index = rnd.nextInt(len)] != 0);
627
            return index;
628
        }
629
    }
630

    
631
}
(1-1/2)