001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.security.pwd;
016    
017    import com.liferay.portal.UserPasswordException;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.util.ArrayUtil;
021    import com.liferay.portal.kernel.util.Randomizer;
022    import com.liferay.portal.kernel.util.StringBundler;
023    import com.liferay.portal.model.PasswordPolicy;
024    import com.liferay.portal.model.User;
025    import com.liferay.portal.service.PasswordTrackerLocalServiceUtil;
026    import com.liferay.portal.service.UserLocalServiceUtil;
027    import com.liferay.portal.util.PropsValues;
028    import com.liferay.portlet.words.util.WordsUtil;
029    import com.liferay.util.PwdGenerator;
030    
031    import java.util.Arrays;
032    import java.util.Date;
033    
034    /**
035     * @author Scott Lee
036     * @author Mika Koivisto
037     */
038    public class PasswordPolicyToolkit extends BasicToolkit {
039    
040            public PasswordPolicyToolkit() {
041                    _lowerCaseCharsetArray = getSortedCharArray(
042                            PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_LOWERCASE);
043                    _numbersCharsetArray = getSortedCharArray(
044                            PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_NUMBERS);
045                    _symbolsCharsetArray = getSortedCharArray(
046                            PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_SYMBOLS);
047                    _upperCaseCharsetArray = getSortedCharArray(
048                            PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_UPPERCASE);
049    
050                    _alphanumericCharsetArray = ArrayUtil.append(
051                            _lowerCaseCharsetArray, _upperCaseCharsetArray,
052                            _numbersCharsetArray);
053    
054                    Arrays.sort(_alphanumericCharsetArray);
055    
056                    StringBundler sb = new StringBundler(4);
057    
058                    sb.append(
059                            PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_LOWERCASE);
060                    sb.append(PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_NUMBERS);
061                    sb.append(PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_SYMBOLS);
062                    sb.append(
063                            PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_UPPERCASE);
064    
065                    _completeCharset = sb.toString();
066            }
067    
068            public String generate(PasswordPolicy passwordPolicy) {
069                    if (PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR.equals(
070                                    "static")) {
071    
072                            return generateStatic(passwordPolicy);
073                    }
074                    else {
075                            return generateDynamic(passwordPolicy);
076                    }
077            }
078    
079            public void validate(
080                            long userId, String password1, String password2,
081                            PasswordPolicy passwordPolicy)
082                    throws PortalException, SystemException {
083    
084                    if (passwordPolicy.isCheckSyntax()) {
085                            if (!passwordPolicy.isAllowDictionaryWords() &&
086                                            WordsUtil.isDictionaryWord(password1)) {
087    
088                                    throw new UserPasswordException(
089                                            UserPasswordException.PASSWORD_CONTAINS_TRIVIAL_WORDS);
090                            }
091    
092                            if (password1.length() < passwordPolicy.getMinLength()) {
093                                    throw new UserPasswordException(
094                                            UserPasswordException.PASSWORD_LENGTH);
095                            }
096    
097                            if ((getUsageCount(password1, _alphanumericCharsetArray) <
098                                            passwordPolicy.getMinAlphanumeric()) ||
099                                    (getUsageCount(password1, _lowerCaseCharsetArray) <
100                                            passwordPolicy.getMinLowerCase()) ||
101                                    (getUsageCount(password1, _numbersCharsetArray) <
102                                            passwordPolicy.getMinNumbers()) ||
103                                    (getUsageCount(password1, _symbolsCharsetArray) <
104                                            passwordPolicy.getMinSymbols()) ||
105                                    (getUsageCount(password1, _upperCaseCharsetArray) <
106                                            passwordPolicy.getMinUpperCase())) {
107    
108                                    throw new UserPasswordException(
109                                            UserPasswordException.PASSWORD_TOO_TRIVIAL);
110                            }
111                    }
112    
113                    if (!passwordPolicy.isChangeable()) {
114                            throw new UserPasswordException(
115                                    UserPasswordException.PASSWORD_NOT_CHANGEABLE);
116                    }
117    
118                    if (userId != 0) {
119                            User user = UserLocalServiceUtil.getUserById(userId);
120    
121                            Date passwordModfiedDate = user.getPasswordModifiedDate();
122    
123                            if (passwordModfiedDate != null) {
124    
125                                    // LEP-2961
126    
127                                    Date now = new Date();
128    
129                                    long passwordModificationElapsedTime =
130                                            now.getTime() - passwordModfiedDate.getTime();
131    
132                                    long userCreationElapsedTime =
133                                            now.getTime() - user.getCreateDate().getTime();
134    
135                                    long minAge = passwordPolicy.getMinAge() * 1000;
136    
137                                    if ((passwordModificationElapsedTime < minAge) &&
138                                            (userCreationElapsedTime > minAge)) {
139    
140                                            throw new UserPasswordException(
141                                                    UserPasswordException.PASSWORD_TOO_YOUNG);
142                                    }
143                            }
144    
145                            if (PasswordTrackerLocalServiceUtil.isSameAsCurrentPassword(
146                                            userId, password1)) {
147    
148                                    throw new UserPasswordException(
149                                            UserPasswordException.PASSWORD_SAME_AS_CURRENT);
150                            }
151                            else if (!PasswordTrackerLocalServiceUtil.isValidPassword(
152                                                    userId, password1)) {
153    
154                                    throw new UserPasswordException(
155                                            UserPasswordException.PASSWORD_ALREADY_USED);
156                            }
157                    }
158            }
159    
160            protected String generateDynamic(PasswordPolicy passwordPolicy) {
161                    int alphanumericMinLength = Math.max(
162                            passwordPolicy.getMinAlphanumeric(),
163                            passwordPolicy.getMinLowerCase() + passwordPolicy.getMinNumbers() +
164                                    passwordPolicy.getMinUpperCase());
165                    int passwordMinLength = Math.max(
166                            passwordPolicy.getMinLength(),
167                            alphanumericMinLength + passwordPolicy.getMinSymbols());
168    
169                    StringBundler sb = new StringBundler(passwordMinLength);
170    
171                    if (passwordPolicy.getMinLowerCase() > 0) {
172                            sb.append(
173                                    getRandomString(
174                                            passwordPolicy.getMinLowerCase(), _lowerCaseCharsetArray));
175                    }
176    
177                    if (passwordPolicy.getMinNumbers() > 0) {
178                            sb.append(
179                                    getRandomString(
180                                            passwordPolicy.getMinNumbers(), _numbersCharsetArray));
181                    }
182    
183                    if (passwordPolicy.getMinSymbols() > 0) {
184                            sb.append(
185                                    getRandomString(
186                                            passwordPolicy.getMinSymbols(), _symbolsCharsetArray));
187                    }
188    
189                    if (passwordPolicy.getMinUpperCase() > 0) {
190                            sb.append(
191                                    getRandomString(
192                                            passwordPolicy.getMinUpperCase(), _upperCaseCharsetArray));
193                    }
194    
195                    if (alphanumericMinLength > passwordPolicy.getMinAlphanumeric()) {
196                            int count =
197                                    alphanumericMinLength - passwordPolicy.getMinAlphanumeric();
198    
199                            sb.append(getRandomString(count, _alphanumericCharsetArray));
200                    }
201    
202                    if (passwordMinLength >
203                                    (alphanumericMinLength + passwordPolicy.getMinSymbols())) {
204    
205                            int count =
206                                    passwordMinLength - (alphanumericMinLength +
207                                            passwordPolicy.getMinSymbols());
208    
209                            sb.append(PwdGenerator.getPassword(_completeCharset, count));
210                    }
211    
212                    Randomizer randomizer = Randomizer.getInstance();
213    
214                    return randomizer.randomize(sb.toString());
215            }
216    
217            protected String generateStatic(PasswordPolicy passwordPolicy) {
218                    return PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_STATIC;
219            }
220    
221            protected String getRandomString(int count, char[] charArray) {
222                    StringBundler sb = new StringBundler(count);
223    
224                    Randomizer randomizer = Randomizer.getInstance();
225    
226                    for (int i = 0; i < count; i++) {
227                            int index = randomizer.nextInt(charArray.length);
228    
229                            sb.append(charArray[index]);
230                    }
231    
232                    return sb.toString();
233            }
234    
235            protected char[] getSortedCharArray(String s) {
236                    char[] charArray = s.toCharArray();
237    
238                    Arrays.sort(charArray);
239    
240                    return charArray;
241            }
242    
243            protected int getUsageCount(String s, char[] charArray) {
244                    int count = 0;
245    
246                    for (int i = 0; i < s.length(); i++) {
247                            if (Arrays.binarySearch(charArray, s.charAt(i)) >= 0) {
248                                    count++;
249                            }
250                    }
251    
252                    return count;
253            }
254    
255            private char[] _alphanumericCharsetArray;
256            private String _completeCharset;
257            private char[] _lowerCaseCharsetArray;
258            private char[] _numbersCharsetArray;
259            private char[] _symbolsCharsetArray;
260            private char[] _upperCaseCharsetArray;
261    
262    }