001
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.security.RandomUtil;
021 import com.liferay.portal.kernel.security.SecureRandom;
022 import com.liferay.portal.kernel.util.ArrayUtil;
023 import com.liferay.portal.kernel.util.StringBundler;
024 import com.liferay.portal.kernel.util.Validator;
025 import com.liferay.portal.model.PasswordPolicy;
026 import com.liferay.portal.model.User;
027 import com.liferay.portal.service.PasswordTrackerLocalServiceUtil;
028 import com.liferay.portal.service.UserLocalServiceUtil;
029 import com.liferay.portal.util.PropsValues;
030 import com.liferay.portal.words.WordsUtil;
031 import com.liferay.util.PwdGenerator;
032
033 import java.util.Arrays;
034 import java.util.Date;
035 import java.util.Random;
036
037
041 public class PasswordPolicyToolkit extends BasicToolkit {
042
043 public PasswordPolicyToolkit() {
044 _generatorLowerCaseCharsetArray = getSortedCharArray(
045 PropsValues.
046 PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR_CHARSET_LOWERCASE);
047 _generatorNumbersCharsetArray = getSortedCharArray(
048 PropsValues.
049 PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR_CHARSET_NUMBERS);
050 _generatorSymbolsCharsetArray = getSortedCharArray(
051 PropsValues.
052 PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR_CHARSET_SYMBOLS);
053 _generatorUpperCaseCharsetArray = getSortedCharArray(
054 PropsValues.
055 PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR_CHARSET_UPPERCASE);
056
057 _generatorAlphanumericCharsetArray = ArrayUtil.append(
058 _generatorLowerCaseCharsetArray, _generatorUpperCaseCharsetArray,
059 _generatorNumbersCharsetArray);
060
061 Arrays.sort(_generatorAlphanumericCharsetArray);
062
063 StringBundler sb = new StringBundler(4);
064
065 sb.append(
066 PropsValues.
067 PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR_CHARSET_LOWERCASE);
068 sb.append(
069 PropsValues.
070 PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR_CHARSET_NUMBERS);
071 sb.append(
072 PropsValues.
073 PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR_CHARSET_SYMBOLS);
074 sb.append(
075 PropsValues.
076 PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR_CHARSET_UPPERCASE);
077
078 _generatorCompleteCharset = sb.toString();
079
080 _validatorLowerCaseCharsetArray = getSortedCharArray(
081 PropsValues.
082 PASSWORDS_PASSWORDPOLICYTOOLKIT_VALIDATOR_CHARSET_LOWERCASE);
083 _validatorNumbersCharsetArray = getSortedCharArray(
084 PropsValues.
085 PASSWORDS_PASSWORDPOLICYTOOLKIT_VALIDATOR_CHARSET_NUMBERS);
086 _validatorSymbolsCharsetArray = getSortedCharArray(
087 PropsValues.
088 PASSWORDS_PASSWORDPOLICYTOOLKIT_VALIDATOR_CHARSET_SYMBOLS);
089 _validatorUpperCaseCharsetArray = getSortedCharArray(
090 PropsValues.
091 PASSWORDS_PASSWORDPOLICYTOOLKIT_VALIDATOR_CHARSET_UPPERCASE);
092
093 _validatorAlphanumericCharsetArray = ArrayUtil.append(
094 _validatorLowerCaseCharsetArray, _validatorUpperCaseCharsetArray,
095 _validatorNumbersCharsetArray);
096
097 Arrays.sort(_validatorAlphanumericCharsetArray);
098 }
099
100 @Override
101 public String generate(PasswordPolicy passwordPolicy) {
102 if (PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_GENERATOR.equals(
103 "static")) {
104
105 return generateStatic(passwordPolicy);
106 }
107 else {
108 return generateDynamic(passwordPolicy);
109 }
110 }
111
112 @Override
113 public void validate(
114 long userId, String password1, String password2,
115 PasswordPolicy passwordPolicy)
116 throws PortalException, SystemException {
117
118 if (passwordPolicy.isCheckSyntax()) {
119 if (!passwordPolicy.isAllowDictionaryWords() &&
120 WordsUtil.isDictionaryWord(password1)) {
121
122 throw new UserPasswordException(
123 UserPasswordException.PASSWORD_CONTAINS_TRIVIAL_WORDS);
124 }
125
126 if (password1.length() < passwordPolicy.getMinLength()) {
127 throw new UserPasswordException(
128 UserPasswordException.PASSWORD_LENGTH);
129 }
130
131 if ((getUsageCount(password1, _validatorAlphanumericCharsetArray) <
132 passwordPolicy.getMinAlphanumeric()) ||
133 (getUsageCount(password1, _validatorLowerCaseCharsetArray) <
134 passwordPolicy.getMinLowerCase()) ||
135 (getUsageCount(password1, _validatorNumbersCharsetArray) <
136 passwordPolicy.getMinNumbers()) ||
137 (getUsageCount(password1, _validatorSymbolsCharsetArray) <
138 passwordPolicy.getMinSymbols()) ||
139 (getUsageCount(password1, _validatorUpperCaseCharsetArray) <
140 passwordPolicy.getMinUpperCase())) {
141
142 throw new UserPasswordException(
143 UserPasswordException.PASSWORD_TOO_TRIVIAL);
144 }
145
146 if (Validator.isNotNull(passwordPolicy.getRegex()) &&
147 !password1.matches(passwordPolicy.getRegex())) {
148
149 throw new UserPasswordException(
150 UserPasswordException.PASSWORD_INVALID);
151 }
152 }
153
154 if (!passwordPolicy.isChangeable() && (userId != 0)) {
155 throw new UserPasswordException(
156 UserPasswordException.PASSWORD_NOT_CHANGEABLE);
157 }
158
159 if (userId == 0) {
160 return;
161 }
162
163 User user = UserLocalServiceUtil.getUserById(userId);
164
165 Date passwordModfiedDate = user.getPasswordModifiedDate();
166
167 if (passwordModfiedDate != null) {
168 Date now = new Date();
169
170 long passwordModificationElapsedTime =
171 now.getTime() - passwordModfiedDate.getTime();
172
173 long minAge = passwordPolicy.getMinAge() * 1000;
174
175 if ((passwordModificationElapsedTime < minAge) &&
176 !user.getPasswordReset()) {
177
178 throw new UserPasswordException(
179 UserPasswordException.PASSWORD_TOO_YOUNG);
180 }
181 }
182
183 if (PasswordTrackerLocalServiceUtil.isSameAsCurrentPassword(
184 userId, password1)) {
185
186 throw new UserPasswordException(
187 UserPasswordException.PASSWORD_SAME_AS_CURRENT);
188 }
189 else if (!PasswordTrackerLocalServiceUtil.isValidPassword(
190 userId, password1)) {
191
192 throw new UserPasswordException(
193 UserPasswordException.PASSWORD_ALREADY_USED);
194 }
195 }
196
197 protected String generateDynamic(PasswordPolicy passwordPolicy) {
198 int alphanumericActualMinLength =
199 passwordPolicy.getMinLowerCase() + passwordPolicy.getMinNumbers() +
200 passwordPolicy.getMinUpperCase();
201
202 int alphanumericMinLength = Math.max(
203 passwordPolicy.getMinAlphanumeric(), alphanumericActualMinLength);
204 int passwordMinLength = Math.max(
205 passwordPolicy.getMinLength(),
206 alphanumericMinLength + passwordPolicy.getMinSymbols());
207
208 StringBundler sb = new StringBundler(6);
209
210 if (passwordPolicy.getMinLowerCase() > 0) {
211 sb.append(
212 getRandomString(
213 passwordPolicy.getMinLowerCase(),
214 _generatorLowerCaseCharsetArray));
215 }
216
217 if (passwordPolicy.getMinNumbers() > 0) {
218 sb.append(
219 getRandomString(
220 passwordPolicy.getMinNumbers(),
221 _generatorNumbersCharsetArray));
222 }
223
224 if (passwordPolicy.getMinSymbols() > 0) {
225 sb.append(
226 getRandomString(
227 passwordPolicy.getMinSymbols(),
228 _generatorSymbolsCharsetArray));
229 }
230
231 if (passwordPolicy.getMinUpperCase() > 0) {
232 sb.append(
233 getRandomString(
234 passwordPolicy.getMinUpperCase(),
235 _generatorUpperCaseCharsetArray));
236 }
237
238 if (alphanumericMinLength > alphanumericActualMinLength) {
239 int count = alphanumericMinLength - alphanumericActualMinLength;
240
241 sb.append(
242 getRandomString(count, _generatorAlphanumericCharsetArray));
243 }
244
245 if (passwordMinLength >
246 (alphanumericMinLength + passwordPolicy.getMinSymbols())) {
247
248 int count =
249 passwordMinLength -
250 (alphanumericMinLength + passwordPolicy.getMinSymbols());
251
252 sb.append(
253 PwdGenerator.getPassword(_generatorCompleteCharset, count));
254 }
255
256 if (sb.index() == 0) {
257 sb.append(
258 PwdGenerator.getPassword(
259 _generatorCompleteCharset,
260 PropsValues.PASSWORDS_DEFAULT_POLICY_MIN_LENGTH));
261 }
262
263 return RandomUtil.shuffle(new SecureRandom(), sb.toString());
264 }
265
266 protected String generateStatic(PasswordPolicy passwordPolicy) {
267 return PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_STATIC;
268 }
269
270 protected String getRandomString(int count, char[] chars) {
271 Random random = new SecureRandom();
272
273 StringBundler sb = new StringBundler(count);
274
275 for (int i = 0; i < count; i++) {
276 int index = random.nextInt(chars.length);
277
278 sb.append(chars[index]);
279 }
280
281 return sb.toString();
282 }
283
284 protected char[] getSortedCharArray(String s) {
285 char[] chars = s.toCharArray();
286
287 Arrays.sort(chars);
288
289 return chars;
290 }
291
292 protected int getUsageCount(String s, char[] chars) {
293 int count = 0;
294
295 for (int i = 0; i < s.length(); i++) {
296 if (Arrays.binarySearch(chars, s.charAt(i)) >= 0) {
297 count++;
298 }
299 }
300
301 return count;
302 }
303
304 private char[] _generatorAlphanumericCharsetArray;
305 private String _generatorCompleteCharset;
306 private char[] _generatorLowerCaseCharsetArray;
307 private char[] _generatorNumbersCharsetArray;
308 private char[] _generatorSymbolsCharsetArray;
309 private char[] _generatorUpperCaseCharsetArray;
310 private char[] _validatorAlphanumericCharsetArray;
311 private char[] _validatorLowerCaseCharsetArray;
312 private char[] _validatorNumbersCharsetArray;
313 private char[] _validatorSymbolsCharsetArray;
314 private char[] _validatorUpperCaseCharsetArray;
315
316 }