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