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

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

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

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

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

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

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

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

    
562
    /*
563
     * Transform a byte array to a string
564
     */
565
    private static String base64Encode(byte[] bytes) {
566
        return Base64.encodeBase64String(bytes);
567
    }
568

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

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

    
636
}
(1-1/2)