PageRenderTime 53ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/domain-management/src/main/java/org/jboss/as/domain/management/security/password/simple/SimplePasswordStrengthChecker.java

https://bitbucket.org/cprenzberg/wildfly
Java | 384 lines | 288 code | 55 blank | 41 comment | 58 complexity | d9b0669738dd182816f090755fe5455e MD5 | raw file
  1. /*
  2. * JBoss, Home of Professional Open Source.
  3. * Copyright 2012, Red Hat, Inc., and individual contributors
  4. * as indicated by the @author tags. See the copyright.txt file in the
  5. * distribution for a full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jboss.as.domain.management.security.password.simple;
  23. import static org.jboss.as.domain.management.DomainManagementMessages.MESSAGES;
  24. import java.util.ArrayList;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import java.util.regex.Matcher;
  28. import java.util.regex.Pattern;
  29. import org.jboss.as.domain.management.security.password.Dictionary;
  30. import org.jboss.as.domain.management.security.password.Keyboard;
  31. import org.jboss.as.domain.management.security.password.LengthRestriction;
  32. import org.jboss.as.domain.management.security.password.PasswordRestriction;
  33. import org.jboss.as.domain.management.security.password.PasswordStrengthCheckResult;
  34. import org.jboss.as.domain.management.security.password.PasswordStrengthChecker;
  35. import org.jboss.as.domain.management.security.password.RegexRestriction;
  36. /**
  37. * @author baranowb
  38. *
  39. */
  40. public class SimplePasswordStrengthChecker implements PasswordStrengthChecker {
  41. public static final String REGEX_DIGITS = "[0-9]";
  42. public static final String REGEX_SYMBOLS = "[^0-9a-zA-Z]";
  43. public static final String REGEX_ALPHA_UC = "[A-Z]";
  44. public static final String REGEX_ALPHA_LC = "[a-z]";
  45. public static final String REGEX_ALPHA = "[a-zA-Z]";
  46. public static final PasswordRestriction RESTRICTION_ALPHA = new RegexRestriction(".*" + REGEX_ALPHA + ".*", MESSAGES.passwordMustHaveAlpha());
  47. public static final PasswordRestriction RESTRICTION_DIGITS = new RegexRestriction(".*" + REGEX_DIGITS + ".*", MESSAGES.passwordMustHaveDigit());
  48. public static final PasswordRestriction RESTRICTION_SYMBOLS = new RegexRestriction(".*" + REGEX_SYMBOLS + ".*", MESSAGES.passwordMustHaveSymbol());
  49. public static final PasswordRestriction RESTRICTION_LENGTH = new LengthRestriction(8);
  50. public static final List<PasswordRestriction> DEFAULT_RESTRICTIONS;
  51. static {
  52. List<PasswordRestriction> list = new ArrayList<PasswordRestriction>();
  53. list.add(RESTRICTION_LENGTH);
  54. list.add(RESTRICTION_ALPHA);
  55. list.add(RESTRICTION_DIGITS);
  56. list.add(RESTRICTION_SYMBOLS);
  57. DEFAULT_RESTRICTIONS = Collections.unmodifiableList(list);
  58. }
  59. protected static final int PWD_LEN_WEIGHT = 2;
  60. protected static final int REQUIREMENTS_WEIGHT = 10;
  61. protected static final int SYMBOLS_WEIGHT = 6;
  62. protected static final int DIGITS_WEIGHT = 4;
  63. protected static final int MIDDLE_NONCHAR_WEIGHT = 2;
  64. protected static final int ALPHA_WEIGHT = 2;
  65. protected static final int ALPHA_ONLY_WEIGHT = 2;
  66. protected static final int DIGITS_ONLY_WEIGHT = 4;
  67. protected static final int CONSECUTIVE_ALPHA_WEIGHT = 2;
  68. protected static final int CONSECUTIVE_DIGITS_WEIGHT = 2;
  69. protected static final int CONSECUTIVE_SYMBOLS_WEIGHT = 2;
  70. protected static final int DICTIONARY_WORD_WEIGHT = 1;
  71. protected static final int SEQUENTIAL_WEIGHT = 3;
  72. // this is not thread safe.
  73. private String password;
  74. private int passwordLength;
  75. private final List<PasswordRestriction> restrictionsInPlace;
  76. private final Dictionary dictionary;
  77. private final Keyboard keyboard;
  78. private List<PasswordRestriction> adHocRestrictions;
  79. private SimplePasswordStrengthCheckResult result;
  80. public SimplePasswordStrengthChecker() {
  81. this.restrictionsInPlace = DEFAULT_RESTRICTIONS;
  82. this.dictionary = new SimpleDictionary();
  83. this.keyboard = new SimpleKeyboard();
  84. }
  85. public SimplePasswordStrengthChecker(final List<PasswordRestriction> initRestrictions, final Dictionary dictionary,
  86. final Keyboard keyboard) {
  87. if (initRestrictions == null) {
  88. throw new IllegalArgumentException("Initial restrictions must not be null.");
  89. }
  90. this.restrictionsInPlace = Collections.unmodifiableList(initRestrictions);
  91. this.dictionary = dictionary;
  92. this.keyboard = keyboard;
  93. }
  94. /*
  95. * (non-Javadoc)
  96. *
  97. * @see org.jboss.as.domain.management.security.password.PasswordStrengthChecker#check(java.lang.String, java.util.List)
  98. */
  99. @Override
  100. public PasswordStrengthCheckResult check(String password, List<PasswordRestriction> restictions) {
  101. try {
  102. this.password = password;
  103. this.passwordLength = this.password.length();
  104. this.adHocRestrictions = restictions;
  105. this.result = new SimplePasswordStrengthCheckResult();
  106. this.checkRestrictions();
  107. // positive checks
  108. result.positive(password.length() * PWD_LEN_WEIGHT);
  109. this.checkSymbols();
  110. this.checkDigits();
  111. this.checkMiddleNonChar();
  112. this.checkAlpha();
  113. // negatives checks
  114. this.checkAlphaOnly();
  115. this.checkNumbersOnly();
  116. this.checkConsecutiveAlpha();
  117. this.checkConsecutiveNumbers();
  118. this.checkConsecutiveSymbols();
  119. this.checkSequential();
  120. // now evaluate.
  121. this.result.calculateStrength();
  122. } finally {
  123. this.password = null;
  124. this.passwordLength = 0;
  125. this.adHocRestrictions = null;
  126. }
  127. return result;
  128. }
  129. protected void checkRestrictions() {
  130. int met = 0;
  131. // check addhoc first, those may be more important
  132. if (this.adHocRestrictions != null) {
  133. for (PasswordRestriction pr : this.adHocRestrictions) {
  134. if (pr.pass(this.password)) {
  135. result.addPassedRestriction(pr);
  136. met++;
  137. } else {
  138. result.addFailedRestriction(pr);
  139. }
  140. }
  141. }
  142. for (PasswordRestriction pr : this.restrictionsInPlace) {
  143. if (pr.pass(this.password)) {
  144. result.addPassedRestriction(pr);
  145. met++;
  146. } else {
  147. result.addFailedRestriction(pr);
  148. }
  149. }
  150. this.result.positive(met * REQUIREMENTS_WEIGHT);
  151. }
  152. protected void checkSymbols() {
  153. int met = 0;
  154. Pattern symbolsPatter = Pattern.compile(REGEX_SYMBOLS + "?");
  155. Matcher matcher = symbolsPatter.matcher(this.password);
  156. while (matcher.find()) {
  157. int start = matcher.start();
  158. int end = matcher.end();
  159. if (start == end) {
  160. continue;
  161. }
  162. met++;
  163. }
  164. this.result.positive(met * REQUIREMENTS_WEIGHT);
  165. }
  166. protected void checkDigits() {
  167. int met = 0;
  168. Pattern symbolsPatter = Pattern.compile(REGEX_DIGITS + "?");
  169. Matcher matcher = symbolsPatter.matcher(this.password);
  170. while (matcher.find()) {
  171. int start = matcher.start();
  172. int end = matcher.end();
  173. if (start == end) {
  174. continue;
  175. }
  176. met++;
  177. }
  178. this.result.positive(met * DIGITS_WEIGHT);
  179. }
  180. protected void checkMiddleNonChar() {
  181. int met = 0;
  182. Pattern symbolsPatter = Pattern.compile(REGEX_SYMBOLS + "?");
  183. Matcher matcher = symbolsPatter.matcher(this.password);
  184. while (matcher.find()) {
  185. int start = matcher.start();
  186. int end = matcher.end();
  187. if (start == end && start != 0 && end != this.passwordLength) {
  188. continue;
  189. }
  190. met++;
  191. }
  192. this.result.positive(met * MIDDLE_NONCHAR_WEIGHT);
  193. }
  194. protected void checkAlpha() {
  195. int met = 0;
  196. Pattern symbolsPatter = Pattern.compile(REGEX_ALPHA_UC + "?");
  197. Matcher matcher = symbolsPatter.matcher(this.password);
  198. while (matcher.find()) {
  199. int start = matcher.start();
  200. int end = matcher.end();
  201. if (start == end) {
  202. continue;
  203. }
  204. met++;
  205. }
  206. met = 0;
  207. symbolsPatter = Pattern.compile(REGEX_ALPHA_LC + "?");
  208. matcher = symbolsPatter.matcher(this.password);
  209. while (matcher.find()) {
  210. int start = matcher.start();
  211. int end = matcher.end();
  212. if (start == end) {
  213. continue;
  214. }
  215. met++;
  216. }
  217. this.result.positive((this.passwordLength - met) * ALPHA_WEIGHT);
  218. }
  219. protected void checkAlphaOnly() {
  220. Pattern symbolsPatter = Pattern.compile(REGEX_ALPHA + "*");
  221. Matcher matcher = symbolsPatter.matcher(this.password);
  222. if (matcher.find()) {
  223. if (matcher.end() == this.passwordLength) {
  224. // negative.
  225. this.result.negative(this.passwordLength * ALPHA_ONLY_WEIGHT);
  226. }
  227. }
  228. }
  229. protected void checkNumbersOnly() {
  230. Pattern symbolsPatter = Pattern.compile(REGEX_DIGITS + "*");
  231. Matcher matcher = symbolsPatter.matcher(this.password);
  232. if (matcher.find()) {
  233. if (matcher.end() == this.passwordLength) {
  234. // negative.
  235. this.result.negative(this.passwordLength * DIGITS_ONLY_WEIGHT);
  236. }
  237. }
  238. }
  239. // those could be incorporated with above, but that would blurry everything.
  240. protected void checkConsecutiveAlpha() {
  241. Pattern symbolsPatter = Pattern.compile(REGEX_ALPHA_UC + "+");
  242. Matcher matcher = symbolsPatter.matcher(this.password);
  243. int met = 0;
  244. while (matcher.find()) {
  245. int start = matcher.start();
  246. int end = matcher.end();
  247. if (start == end) {
  248. continue;
  249. }
  250. int diff = end - start;
  251. if (diff >= 3) {
  252. met += diff;
  253. }
  254. }
  255. this.result.negative(met * CONSECUTIVE_ALPHA_WEIGHT);
  256. // alpha lower case
  257. symbolsPatter = Pattern.compile(REGEX_ALPHA_LC + "+");
  258. matcher = symbolsPatter.matcher(this.password);
  259. met = 0;
  260. while (matcher.find()) {
  261. int start = matcher.start();
  262. int end = matcher.end();
  263. if (start == end) {
  264. continue;
  265. }
  266. int diff = end - start;
  267. if (diff >= 3) {
  268. met += diff;
  269. }
  270. }
  271. this.result.negative(met * CONSECUTIVE_ALPHA_WEIGHT);
  272. }
  273. protected void checkConsecutiveNumbers() {
  274. Pattern symbolsPatter = Pattern.compile(REGEX_DIGITS + "+");
  275. Matcher matcher = symbolsPatter.matcher(this.password);
  276. int met = 0;
  277. while (matcher.find()) {
  278. int start = matcher.start();
  279. int end = matcher.end();
  280. if (start == end) {
  281. continue;
  282. }
  283. int diff = end - start;
  284. if (diff >= 3) {
  285. met += diff;
  286. }
  287. }
  288. this.result.negative(met * CONSECUTIVE_DIGITS_WEIGHT);
  289. }
  290. protected void checkConsecutiveSymbols() {
  291. Pattern symbolsPatter = Pattern.compile(REGEX_SYMBOLS + "+");
  292. Matcher matcher = symbolsPatter.matcher(this.password);
  293. int met = 0;
  294. while (matcher.find()) {
  295. int start = matcher.start();
  296. int end = matcher.end();
  297. if (start == end) {
  298. continue;
  299. }
  300. int diff = end - start;
  301. if (diff >= 3) {
  302. met += diff;
  303. }
  304. }
  305. this.result.negative(met * CONSECUTIVE_SYMBOLS_WEIGHT);
  306. }
  307. protected void checkSequential() {
  308. // iterate over chars and check if siblings, if so, check how long is chain
  309. int chainSize = 0;
  310. for (int index = 0; index < this.passwordLength - 1; index++) {
  311. if (this.keyboard.siblings(this.password, index)) {
  312. chainSize += this.keyboard.sequence(this.password, index);
  313. } else {
  314. // nop
  315. }
  316. }
  317. if (chainSize > 0) {
  318. this.result.negative(chainSize * SEQUENTIAL_WEIGHT);
  319. }
  320. }
  321. protected void checkDictionary() {
  322. if (dictionary != null) {
  323. int score = dictionary.dictionarySequence(password);
  324. if (score > 0) {
  325. this.result.negative(score * DICTIONARY_WORD_WEIGHT);
  326. }
  327. }
  328. }
  329. }