001    /**
002     * Copyright (c) 2000-2013 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.portal.words.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            @Override
069            public String generate(PasswordPolicy passwordPolicy) {
070                    if (PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR.equals(
071                                    "static")) {
072    
073                            return generateStatic(passwordPolicy);
074                    }
075                    else {
076                            return generateDynamic(passwordPolicy);
077                    }
078            }
079    
080            @Override
081            public void validate(
082                            long userId, String password1, String password2,
083                            PasswordPolicy passwordPolicy)
084                    throws PortalException, SystemException {
085    
086                    if (passwordPolicy.isCheckSyntax()) {
087                            if (!passwordPolicy.isAllowDictionaryWords() &&
088                                    WordsUtil.isDictionaryWord(password1)) {
089    
090                                    throw new UserPasswordException(
091                                            UserPasswordException.PASSWORD_CONTAINS_TRIVIAL_WORDS);
092                            }
093    
094                            if (password1.length() < passwordPolicy.getMinLength()) {
095                                    throw new UserPasswordException(
096                                            UserPasswordException.PASSWORD_LENGTH);
097                            }
098    
099                            if ((getUsageCount(password1, _alphanumericCharsetArray) <
100                                            passwordPolicy.getMinAlphanumeric()) ||
101                                    (getUsageCount(password1, _lowerCaseCharsetArray) <
102                                            passwordPolicy.getMinLowerCase()) ||
103                                    (getUsageCount(password1, _numbersCharsetArray) <
104                                            passwordPolicy.getMinNumbers()) ||
105                                    (getUsageCount(password1, _symbolsCharsetArray) <
106                                            passwordPolicy.getMinSymbols()) ||
107                                    (getUsageCount(password1, _upperCaseCharsetArray) <
108                                            passwordPolicy.getMinUpperCase())) {
109    
110                                    throw new UserPasswordException(
111                                            UserPasswordException.PASSWORD_TOO_TRIVIAL);
112                            }
113                    }
114    
115                    if (!passwordPolicy.isChangeable()) {
116                            throw new UserPasswordException(
117                                    UserPasswordException.PASSWORD_NOT_CHANGEABLE);
118                    }
119    
120                    if (userId == 0) {
121                            return;
122                    }
123    
124                    User user = UserLocalServiceUtil.getUserById(userId);
125    
126                    Date passwordModfiedDate = user.getPasswordModifiedDate();
127    
128                    if (passwordModfiedDate != null) {
129    
130                            // LEP-2961
131    
132                            Date now = new Date();
133    
134                            long passwordModificationElapsedTime =
135                                    now.getTime() - passwordModfiedDate.getTime();
136    
137                            long userCreationElapsedTime =
138                                    now.getTime() - user.getCreateDate().getTime();
139    
140                            long minAge = passwordPolicy.getMinAge() * 1000;
141    
142                            if ((passwordModificationElapsedTime < minAge) &&
143                                    (userCreationElapsedTime > minAge)) {
144    
145                                    throw new UserPasswordException(
146                                            UserPasswordException.PASSWORD_TOO_YOUNG);
147                            }
148                    }
149    
150                    if (PasswordTrackerLocalServiceUtil.isSameAsCurrentPassword(
151                                    userId, password1)) {
152    
153                            throw new UserPasswordException(
154                                    UserPasswordException.PASSWORD_SAME_AS_CURRENT);
155                    }
156                    else if (!PasswordTrackerLocalServiceUtil.isValidPassword(
157                                            userId, password1)) {
158    
159                            throw new UserPasswordException(
160                                    UserPasswordException.PASSWORD_ALREADY_USED);
161                    }
162            }
163    
164            protected String generateDynamic(PasswordPolicy passwordPolicy) {
165                    int alphanumericActualMinLength =
166                            passwordPolicy.getMinLowerCase() + passwordPolicy.getMinNumbers() +
167                                    passwordPolicy.getMinUpperCase();
168    
169                    int alphanumericMinLength = Math.max(
170                            passwordPolicy.getMinAlphanumeric(), alphanumericActualMinLength);
171                    int passwordMinLength = Math.max(
172                            passwordPolicy.getMinLength(),
173                            alphanumericMinLength + passwordPolicy.getMinSymbols());
174    
175                    StringBundler sb = new StringBundler(6);
176    
177                    if (passwordPolicy.getMinLowerCase() > 0) {
178                            sb.append(
179                                    getRandomString(
180                                            passwordPolicy.getMinLowerCase(), _lowerCaseCharsetArray));
181                    }
182    
183                    if (passwordPolicy.getMinNumbers() > 0) {
184                            sb.append(
185                                    getRandomString(
186                                            passwordPolicy.getMinNumbers(), _numbersCharsetArray));
187                    }
188    
189                    if (passwordPolicy.getMinSymbols() > 0) {
190                            sb.append(
191                                    getRandomString(
192                                            passwordPolicy.getMinSymbols(), _symbolsCharsetArray));
193                    }
194    
195                    if (passwordPolicy.getMinUpperCase() > 0) {
196                            sb.append(
197                                    getRandomString(
198                                            passwordPolicy.getMinUpperCase(), _upperCaseCharsetArray));
199                    }
200    
201                    if (alphanumericMinLength > alphanumericActualMinLength) {
202                            int count = alphanumericMinLength - alphanumericActualMinLength;
203    
204                            sb.append(getRandomString(count, _alphanumericCharsetArray));
205                    }
206    
207                    if (passwordMinLength >
208                                    (alphanumericMinLength + passwordPolicy.getMinSymbols())) {
209    
210                            int count =
211                                    passwordMinLength -
212                                            (alphanumericMinLength + passwordPolicy.getMinSymbols());
213    
214                            sb.append(PwdGenerator.getPassword(_completeCharset, count));
215                    }
216    
217                    if (sb.index() == 0) {
218                            sb.append(
219                                    PwdGenerator.getPassword(
220                                            _completeCharset, _PASSWORDS_DEFAULT_POLICY_MIN_LENGTH));
221                    }
222    
223                    Randomizer randomizer = Randomizer.getInstance();
224    
225                    return randomizer.randomize(sb.toString());
226            }
227    
228            protected String generateStatic(PasswordPolicy passwordPolicy) {
229                    return PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_STATIC;
230            }
231    
232            protected String getRandomString(int count, char[] chars) {
233                    StringBundler sb = new StringBundler(count);
234    
235                    Randomizer randomizer = Randomizer.getInstance();
236    
237                    for (int i = 0; i < count; i++) {
238                            int index = randomizer.nextInt(chars.length);
239    
240                            sb.append(chars[index]);
241                    }
242    
243                    return sb.toString();
244            }
245    
246            protected char[] getSortedCharArray(String s) {
247                    char[] chars = s.toCharArray();
248    
249                    Arrays.sort(chars);
250    
251                    return chars;
252            }
253    
254            protected int getUsageCount(String s, char[] chars) {
255                    int count = 0;
256    
257                    for (int i = 0; i < s.length(); i++) {
258                            if (Arrays.binarySearch(chars, s.charAt(i)) >= 0) {
259                                    count++;
260                            }
261                    }
262    
263                    return count;
264            }
265    
266            private static final int _PASSWORDS_DEFAULT_POLICY_MIN_LENGTH = 6;
267    
268            private char[] _alphanumericCharsetArray;
269            private String _completeCharset;
270            private char[] _lowerCaseCharsetArray;
271            private char[] _numbersCharsetArray;
272            private char[] _symbolsCharsetArray;
273            private char[] _upperCaseCharsetArray;
274    
275    }