001    /**
002     * Copyright (c) 2000-2012 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                            User user = UserLocalServiceUtil.getUserById(userId);
122    
123                            Date passwordModfiedDate = user.getPasswordModifiedDate();
124    
125                            if (passwordModfiedDate != null) {
126    
127                                    // LEP-2961
128    
129                                    Date now = new Date();
130    
131                                    long passwordModificationElapsedTime =
132                                            now.getTime() - passwordModfiedDate.getTime();
133    
134                                    long userCreationElapsedTime =
135                                            now.getTime() - user.getCreateDate().getTime();
136    
137                                    long minAge = passwordPolicy.getMinAge() * 1000;
138    
139                                    if ((passwordModificationElapsedTime < minAge) &&
140                                            (userCreationElapsedTime > minAge)) {
141    
142                                            throw new UserPasswordException(
143                                                    UserPasswordException.PASSWORD_TOO_YOUNG);
144                                    }
145                            }
146    
147                            if (PasswordTrackerLocalServiceUtil.isSameAsCurrentPassword(
148                                            userId, password1)) {
149    
150                                    throw new UserPasswordException(
151                                            UserPasswordException.PASSWORD_SAME_AS_CURRENT);
152                            }
153                            else if (!PasswordTrackerLocalServiceUtil.isValidPassword(
154                                                    userId, password1)) {
155    
156                                    throw new UserPasswordException(
157                                            UserPasswordException.PASSWORD_ALREADY_USED);
158                            }
159                    }
160            }
161    
162            protected String generateDynamic(PasswordPolicy passwordPolicy) {
163                    int alphanumericMinLength = Math.max(
164                            passwordPolicy.getMinAlphanumeric(),
165                            passwordPolicy.getMinLowerCase() + passwordPolicy.getMinNumbers() +
166                                    passwordPolicy.getMinUpperCase());
167                    int passwordMinLength = Math.max(
168                            passwordPolicy.getMinLength(),
169                            alphanumericMinLength + passwordPolicy.getMinSymbols());
170    
171                    StringBundler sb = new StringBundler(passwordMinLength);
172    
173                    if (passwordPolicy.getMinLowerCase() > 0) {
174                            sb.append(
175                                    getRandomString(
176                                            passwordPolicy.getMinLowerCase(), _lowerCaseCharsetArray));
177                    }
178    
179                    if (passwordPolicy.getMinNumbers() > 0) {
180                            sb.append(
181                                    getRandomString(
182                                            passwordPolicy.getMinNumbers(), _numbersCharsetArray));
183                    }
184    
185                    if (passwordPolicy.getMinSymbols() > 0) {
186                            sb.append(
187                                    getRandomString(
188                                            passwordPolicy.getMinSymbols(), _symbolsCharsetArray));
189                    }
190    
191                    if (passwordPolicy.getMinUpperCase() > 0) {
192                            sb.append(
193                                    getRandomString(
194                                            passwordPolicy.getMinUpperCase(), _upperCaseCharsetArray));
195                    }
196    
197                    if (alphanumericMinLength > passwordPolicy.getMinAlphanumeric()) {
198                            int count =
199                                    alphanumericMinLength - passwordPolicy.getMinAlphanumeric();
200    
201                            sb.append(getRandomString(count, _alphanumericCharsetArray));
202                    }
203    
204                    if (passwordMinLength >
205                                    (alphanumericMinLength + passwordPolicy.getMinSymbols())) {
206    
207                            int count =
208                                    passwordMinLength - (alphanumericMinLength +
209                                            passwordPolicy.getMinSymbols());
210    
211                            sb.append(PwdGenerator.getPassword(_completeCharset, count));
212                    }
213    
214                    Randomizer randomizer = Randomizer.getInstance();
215    
216                    return randomizer.randomize(sb.toString());
217            }
218    
219            protected String generateStatic(PasswordPolicy passwordPolicy) {
220                    return PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_STATIC;
221            }
222    
223            protected String getRandomString(int count, char[] chars) {
224                    StringBundler sb = new StringBundler(count);
225    
226                    Randomizer randomizer = Randomizer.getInstance();
227    
228                    for (int i = 0; i < count; i++) {
229                            int index = randomizer.nextInt(chars.length);
230    
231                            sb.append(chars[index]);
232                    }
233    
234                    return sb.toString();
235            }
236    
237            protected char[] getSortedCharArray(String s) {
238                    char[] chars = s.toCharArray();
239    
240                    Arrays.sort(chars);
241    
242                    return chars;
243            }
244    
245            protected int getUsageCount(String s, char[] chars) {
246                    int count = 0;
247    
248                    for (int i = 0; i < s.length(); i++) {
249                            if (Arrays.binarySearch(chars, s.charAt(i)) >= 0) {
250                                    count++;
251                            }
252                    }
253    
254                    return count;
255            }
256    
257            private char[] _alphanumericCharsetArray;
258            private String _completeCharset;
259            private char[] _lowerCaseCharsetArray;
260            private char[] _numbersCharsetArray;
261            private char[] _symbolsCharsetArray;
262            private char[] _upperCaseCharsetArray;
263    
264    }