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.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.StringBundler;
023 import com.liferay.portal.kernel.util.Validator;
024 import com.liferay.portal.kernel.words.WordsUtil;
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.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 {
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() && (userId != 0)) {
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 Date now = new Date();
139
140 long passwordModificationElapsedTime =
141 now.getTime() - passwordModfiedDate.getTime();
142
143 long minAge = passwordPolicy.getMinAge() * 1000;
144
145 if ((passwordModificationElapsedTime < minAge) &&
146 !user.getPasswordReset()) {
147
148 throw new UserPasswordException(
149 UserPasswordException.PASSWORD_TOO_YOUNG);
150 }
151 }
152
153 if (PasswordTrackerLocalServiceUtil.isSameAsCurrentPassword(
154 userId, password1)) {
155
156 throw new UserPasswordException(
157 UserPasswordException.PASSWORD_SAME_AS_CURRENT);
158 }
159 else if (!PasswordTrackerLocalServiceUtil.isValidPassword(
160 userId, password1)) {
161
162 throw new UserPasswordException(
163 UserPasswordException.PASSWORD_ALREADY_USED);
164 }
165 }
166
167 protected String generateDynamic(PasswordPolicy passwordPolicy) {
168 int alphanumericActualMinLength =
169 passwordPolicy.getMinLowerCase() + passwordPolicy.getMinNumbers() +
170 passwordPolicy.getMinUpperCase();
171
172 int alphanumericMinLength = Math.max(
173 passwordPolicy.getMinAlphanumeric(), alphanumericActualMinLength);
174 int passwordMinLength = Math.max(
175 passwordPolicy.getMinLength(),
176 alphanumericMinLength + passwordPolicy.getMinSymbols());
177
178 StringBundler sb = new StringBundler(6);
179
180 if (passwordPolicy.getMinLowerCase() > 0) {
181 sb.append(
182 getRandomString(
183 passwordPolicy.getMinLowerCase(), _lowerCaseCharsetArray));
184 }
185
186 if (passwordPolicy.getMinNumbers() > 0) {
187 sb.append(
188 getRandomString(
189 passwordPolicy.getMinNumbers(), _numbersCharsetArray));
190 }
191
192 if (passwordPolicy.getMinSymbols() > 0) {
193 sb.append(
194 getRandomString(
195 passwordPolicy.getMinSymbols(), _symbolsCharsetArray));
196 }
197
198 if (passwordPolicy.getMinUpperCase() > 0) {
199 sb.append(
200 getRandomString(
201 passwordPolicy.getMinUpperCase(), _upperCaseCharsetArray));
202 }
203
204 if (alphanumericMinLength > alphanumericActualMinLength) {
205 int count = alphanumericMinLength - alphanumericActualMinLength;
206
207 sb.append(getRandomString(count, _alphanumericCharsetArray));
208 }
209
210 if (passwordMinLength >
211 (alphanumericMinLength + passwordPolicy.getMinSymbols())) {
212
213 int count =
214 passwordMinLength -
215 (alphanumericMinLength + passwordPolicy.getMinSymbols());
216
217 sb.append(PwdGenerator.getPassword(_completeCharset, count));
218 }
219
220 if (sb.index() == 0) {
221 sb.append(
222 PwdGenerator.getPassword(
223 _completeCharset,
224 PropsValues.PASSWORDS_DEFAULT_POLICY_MIN_LENGTH));
225 }
226
227 return RandomUtil.shuffle(new SecureRandom(), sb.toString());
228 }
229
230 protected String generateStatic(PasswordPolicy passwordPolicy) {
231 return PropsValues.PASSWORDS_PASSWORDPOLICYTOOLKIT_STATIC;
232 }
233
234 protected String getRandomString(int count, char[] chars) {
235 Random random = new SecureRandom();
236
237 StringBundler sb = new StringBundler(count);
238
239 for (int i = 0; i < count; i++) {
240 int index = random.nextInt(chars.length);
241
242 sb.append(chars[index]);
243 }
244
245 return sb.toString();
246 }
247
248 protected char[] getSortedCharArray(String s) {
249 char[] chars = s.toCharArray();
250
251 Arrays.sort(chars);
252
253 return chars;
254 }
255
256 protected int getUsageCount(String s, char[] chars) {
257 int count = 0;
258
259 for (int i = 0; i < s.length(); i++) {
260 if (Arrays.binarySearch(chars, s.charAt(i)) >= 0) {
261 count++;
262 }
263 }
264
265 return count;
266 }
267
268 private char[] _alphanumericCharsetArray;
269 private String _completeCharset;
270 private char[] _lowerCaseCharsetArray;
271 private char[] _numbersCharsetArray;
272 private char[] _symbolsCharsetArray;
273 private char[] _upperCaseCharsetArray;
274
275 }