1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.security.pwd;
16  
17  import com.liferay.portal.UserPasswordException;
18  import com.liferay.portal.kernel.exception.PortalException;
19  import com.liferay.portal.kernel.exception.SystemException;
20  import com.liferay.portal.kernel.util.ArrayUtil;
21  import com.liferay.portal.kernel.util.Randomizer;
22  import com.liferay.portal.kernel.util.StringBundler;
23  import com.liferay.portal.model.PasswordPolicy;
24  import com.liferay.portal.model.User;
25  import com.liferay.portal.service.PasswordTrackerLocalServiceUtil;
26  import com.liferay.portal.service.UserLocalServiceUtil;
27  import com.liferay.portal.util.PropsValues;
28  import com.liferay.portlet.words.util.WordsUtil;
29  import com.liferay.util.PwdGenerator;
30  
31  import java.util.Arrays;
32  import java.util.Date;
33  
34  /**
35   * <a href="PasswordPolicyToolkit.java.html"><b><i>View Source</i></b></a>
36   *
37   * @author Scott Lee
38   * @author Mika Koivisto
39   */
40  public class PasswordPolicyToolkit extends BasicToolkit {
41  
42      public PasswordPolicyToolkit() {
43          _lowerCaseCharsetArray = getSortedCharArray(
44              PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_LOWERCASE);
45          _numbersCharsetArray = getSortedCharArray(
46              PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_NUMBERS);
47          _symbolsCharsetArray = getSortedCharArray(
48              PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_SYMBOLS);
49          _upperCaseCharsetArray = getSortedCharArray(
50              PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_UPPERCASE);
51  
52          _alphanumericCharsetArray = ArrayUtil.append(
53              _lowerCaseCharsetArray, _upperCaseCharsetArray,
54              _numbersCharsetArray);
55  
56          Arrays.sort(_alphanumericCharsetArray);
57  
58          StringBundler sb = new StringBundler(4);
59  
60          sb.append(
61              PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_LOWERCASE);
62          sb.append(PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_NUMBERS);
63          sb.append(PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_SYMBOLS);
64          sb.append(
65              PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_CHARSET_UPPERCASE);
66  
67          _completeCharset = sb.toString();
68      }
69  
70      public String generate(PasswordPolicy passwordPolicy) {
71          if (PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR.equals(
72                  "static")) {
73  
74              return generateStatic(passwordPolicy);
75          }
76          else {
77              return generateDynamic(passwordPolicy);
78          }
79      }
80  
81      public void validate(
82              long userId, String password1, String password2,
83              PasswordPolicy passwordPolicy)
84          throws PortalException, SystemException {
85  
86          if (passwordPolicy.isCheckSyntax()) {
87              if (!passwordPolicy.isAllowDictionaryWords() &&
88                      WordsUtil.isDictionaryWord(password1)) {
89  
90                  throw new UserPasswordException(
91                      UserPasswordException.PASSWORD_CONTAINS_TRIVIAL_WORDS);
92              }
93  
94              if (password1.length() < passwordPolicy.getMinLength()) {
95                  throw new UserPasswordException(
96                      UserPasswordException.PASSWORD_LENGTH);
97              }
98  
99              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[] charArray) {
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(charArray.length);
230 
231             sb.append(charArray[index]);
232         }
233 
234         return sb.toString();
235     }
236 
237     protected char[] getSortedCharArray(String s) {
238         char[] charArray = s.toCharArray();
239 
240         Arrays.sort(charArray);
241 
242         return charArray;
243     }
244 
245     protected int getUsageCount(String s, char[] charArray) {
246         int count = 0;
247 
248         for (int i = 0; i < s.length(); i++) {
249             if (Arrays.binarySearch(charArray, 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 }