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