Index: 3rdParty_sources/passpol/com/codahale/passpol/BreachDatabase.java =================================================================== diff -u --- 3rdParty_sources/passpol/com/codahale/passpol/BreachDatabase.java (revision 0) +++ 3rdParty_sources/passpol/com/codahale/passpol/BreachDatabase.java (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -0,0 +1,115 @@ +/* + * Copyright © 2018 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codahale.passpol; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.http.HttpClient; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Objects; + +/** A database of passwords found in data breaches. */ +public interface BreachDatabase { + + /** + * Returns whether or not the database contains the given password. + * + * @param password a candidate password + * @return {@code true} if the database contains {@code password} + * @throws IOException if there was a problem communicating with the database + */ + boolean contains(String password) throws IOException; + + /** + * A client for Have I Been Pwned's online password + * checking. Uses a k-anonymous API which transmits only 20 bits of a password hash. + * + * @return an online database of breached passwords + */ + static BreachDatabase haveIBeenPwned() { + return haveIBeenPwned(1); + } + + /** + * A client for Have I Been Pwned's online password + * checking. Uses a k-anonymous API which transmits only 20 bits of a password hash. + * + * @param threshold The number of breaches a password can be found in which makes it invalid. + * @return an online database of breached passwords + */ + static BreachDatabase haveIBeenPwned(int threshold) { + return haveIBeenPwned(HttpClient.newHttpClient(), threshold); + } + + /** + * A client for Have I Been Pwned's online password + * checking. Uses a k-anonymous API which transmits only 20 bits of a password hash. + * + * @param client The HTTP client to use + * @param threshold The number of breaches a password can be found in which makes it invalid. + * @return an online database of breached passwords + */ + static BreachDatabase haveIBeenPwned(HttpClient client, int threshold) { + return new HaveIBeenPwned(Objects.requireNonNull(client), threshold); + } + + /** + * Returns an offline database of the given passwords. + * + * @param passwords a collection of unusable passwords + * @return an offline database of the given passwords + */ + static BreachDatabase passwordSet(Collection passwords) { + return new PasswordSet(Objects.requireNonNull(passwords).stream()); + } + + /** + * Returns an offline database of the 100,000 most common passwords. + * + * @return an offline database of the 100,000 most common passwords + */ + static BreachDatabase top100K() { + try (var in = BreachDatabase.class.getResourceAsStream("weak-passwords.txt"); + var r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + return new PasswordSet(r.lines()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Returns a database which checks the given databases in order. + * + * @param databases a set of databases + * @return a database which checks the given databases in order + */ + static BreachDatabase anyOf(BreachDatabase... databases) { + for (var database : databases) { + Objects.requireNonNull(database); + } + + return password -> { + for (var database : databases) { + if (database.contains(password)) { + return true; + } + } + return false; + }; + } +} Index: 3rdParty_sources/passpol/com/codahale/passpol/HaveIBeenPwned.java =================================================================== diff -u --- 3rdParty_sources/passpol/com/codahale/passpol/HaveIBeenPwned.java (revision 0) +++ 3rdParty_sources/passpol/com/codahale/passpol/HaveIBeenPwned.java (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -0,0 +1,77 @@ +/* + * Copyright © 2018 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codahale.passpol; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +class HaveIBeenPwned implements BreachDatabase { + private static final int HASH_LENGTH = (160 / 8) * 2; + private static final int PREFIX_LENGTH = 5; + private static final int SUFFIX_LENGTH = HASH_LENGTH - PREFIX_LENGTH; + private static final int DELIM_LENGTH = 1; + private static final URI BASE_URI = URI.create("https://api.pwnedpasswords.com/range/"); + private final HttpClient client; + private final int threshold; + + HaveIBeenPwned(HttpClient client, int threshold) { + this.client = client; + this.threshold = threshold; + } + + @Override + public boolean contains(String password) throws IOException { + try { + var hash = hex(MessageDigest.getInstance("SHA1").digest(PasswordPolicy.normalize(password))); + var request = + HttpRequest.newBuilder() + .GET() + .uri(BASE_URI.resolve(hash.substring(0, PREFIX_LENGTH))) + .header("User-Agent", "passpol") + .build(); + var response = client.send(request, BodyHandlers.ofLines()); + if (response.statusCode() != 200) { + throw new IOException("Unexpected response from server: " + response.statusCode()); + } + return response + .body() + .filter(s -> s.regionMatches(0, hash, PREFIX_LENGTH, SUFFIX_LENGTH)) + .map(s -> s.substring(HASH_LENGTH + DELIM_LENGTH)) + .mapToInt(Integer::parseInt) + .anyMatch(t -> t >= threshold); + } catch (NoSuchAlgorithmException | InterruptedException | IndexOutOfBoundsException e) { + throw new IOException(e); + } + } + + private static final char[] HEX = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + private static String hex(byte[] bytes) { + var b = new StringBuilder(HASH_LENGTH); + for (var v : bytes) { + b.append(HEX[(v & 0xFF) >> 4]); + b.append(HEX[(v & 0x0F)]); + } + return b.toString(); + } +} Index: 3rdParty_sources/passpol/com/codahale/passpol/PasswordPolicy.java =================================================================== diff -u --- 3rdParty_sources/passpol/com/codahale/passpol/PasswordPolicy.java (revision 0) +++ 3rdParty_sources/passpol/com/codahale/passpol/PasswordPolicy.java (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -0,0 +1,113 @@ +/* + * Copyright © 2018 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codahale.passpol; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * A password policy which validates candidate passwords according to NIST's draft {@code + * SP-800-63B}, which recommend passwords have a minimum required length, a maximum required length, + * ad be checked against a list of weak passwords ({@code SP-800-63B 5.1.1.2}). + * + *

This uses a static list of 10,000 weak passwords downloaded from Carey Li's NBP project. + * + * @see Draft NIST SP-800-63B + * @see NBP + */ +public class PasswordPolicy { + + /** The recommended minimum password length, per {@code SP-800-63B 5.1.1.2}. */ + public static final int RECOMMENDED_MIN_LENGTH = 8; + + /** The recommended maximum password length, per {@code SP-800-63B 5.1.1.2}. */ + public static final int RECOMMENDED_MAX_LENGTH = 64; + + private final int minLength; + private final int maxLength; + private final BreachDatabase breachDatabase; + + /** + * Creates a {@link PasswordPolicy} with a minimum password length of {@code 8} and a maximum + * password length of {@code 64}, as recommended in {@code SP-800-63B 5.1.1.2}. Uses the offline + * database of weak passwords. + * + * @see BreachDatabase#top100K() + * @see PasswordPolicy#RECOMMENDED_MIN_LENGTH + * @see PasswordPolicy#RECOMMENDED_MAX_LENGTH + */ + public PasswordPolicy() { + this(BreachDatabase.top100K(), RECOMMENDED_MIN_LENGTH, RECOMMENDED_MAX_LENGTH); + } + + /** + * Creates a {@link PasswordPolicy} with the given password length requirements. + * + * @param minLength the minimum length of passwords + * @param maxLength the maximum length of passwords + * @param breachDatabase a {@link BreachDatabase} instance + */ + public PasswordPolicy(BreachDatabase breachDatabase, int minLength, int maxLength) { + if (maxLength < minLength) { + throw new IllegalArgumentException("minLength must be less than maxLength"); + } + this.breachDatabase = breachDatabase; + this.minLength = minLength; + this.maxLength = maxLength; + } + + /** + * Normalizes the given password as Unicode NFKC and returns it as UTF-8 encoded bytes, ready to + * be passed to a password hashing algorithm like {@code bcrypt}. + * + *

This is the process recommended in {@code NIST SP-800-63B 5.1.1.2}. + * + * @param password an arbitrary string + * @return a series of bytes suitable for hashing + */ + public static byte[] normalize(String password) { + return PasswordSet.normalize(Objects.requireNonNull(password)).getBytes(StandardCharsets.UTF_8); + } + + /** + * Checks the acceptability of a candidate password. + * + * @param password a candidate password + * @return the status of {@code password} + */ + public Status check(String password) { + var len = Objects.requireNonNull(password).codePointCount(0, password.length()); + + if (len < minLength) { + return Status.TOO_SHORT; + } + + if (len > maxLength) { + return Status.TOO_LONG; + } + + try { + if (breachDatabase.contains(password)) { + return Status.BREACHED; + } + } catch (IOException e) { + return Status.OK; + } + + return Status.OK; + } +} Index: 3rdParty_sources/passpol/com/codahale/passpol/PasswordSet.java =================================================================== diff -u --- 3rdParty_sources/passpol/com/codahale/passpol/PasswordSet.java (revision 0) +++ 3rdParty_sources/passpol/com/codahale/passpol/PasswordSet.java (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -0,0 +1,62 @@ +/* + * Copyright © 2018 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codahale.passpol; + +import java.text.Normalizer; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class PasswordSet implements BreachDatabase { + + private final Set passwords; + + PasswordSet(Stream passwords) { + this.passwords = passwords.map(PasswordSet::normalize).collect(Collectors.toUnmodifiableSet()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof PasswordSet)) { + return false; + } + var that = (PasswordSet) o; + return Objects.equals(passwords, that.passwords); + } + + @Override + public int hashCode() { + return Objects.hash(passwords); + } + + @Override + public String toString() { + return passwords.toString(); + } + + @Override + public boolean contains(String password) { + return passwords.contains(normalize(password)); + } + + static String normalize(String s) { + return Normalizer.normalize(s, Normalizer.Form.NFKC); + } +} Index: 3rdParty_sources/passpol/com/codahale/passpol/Status.java =================================================================== diff -u --- 3rdParty_sources/passpol/com/codahale/passpol/Status.java (revision 0) +++ 3rdParty_sources/passpol/com/codahale/passpol/Status.java (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -0,0 +1,31 @@ +/* + * Copyright © 2018 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codahale.passpol; + +/** The status of a given candidate password. */ +public enum Status { + /** The candidate password is acceptable. */ + OK, + + /** The candidate password is too short. */ + TOO_SHORT, + + /** The candidate password is too long. */ + TOO_LONG, + + /** The candidate password has previously appeared in a data breach. */ + BREACHED +} Index: 3rdParty_sources/versions.txt =================================================================== diff -u -r329cf66d1bc9d7aa6fb468d187ed81c8780193b4 -re88830091139dd447cdd6a7f30c129c777104dac --- 3rdParty_sources/versions.txt (.../versions.txt) (revision 329cf66d1bc9d7aa6fb468d187ed81c8780193b4) +++ 3rdParty_sources/versions.txt (.../versions.txt) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -55,6 +55,8 @@ Quartz 2.2.3 +passpol 0.7.1-SNAPSHOT + picketbox 5.0.3 Servlet API 4.0.0 Index: lams_admin/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_admin/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_admin/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -474,6 +474,7 @@ label.password.must.number = at least 1 number label.password.must.symbol = at least 1 symbol label.password.user.details = must not be the same as user login, ID, email or names +label.password.common = must not be common or breached sysadmin.batch.preview.lesson.delete = Delete old preview lessons msg.cleanup.preview.lesson.confirm = Are you sure you want to delete all preview lessons? msg.cleanup.preview.lesson.error = Error while deleting preview lessons Index: lams_admin/web/import/importexcel.jsp =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_admin/web/import/importexcel.jsp (.../importexcel.jsp) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_admin/web/import/importexcel.jsp (.../importexcel.jsp) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -181,6 +181,9 @@

  • +
  • + +
  • Index: lams_admin/web/orgPasswordChange.jsp =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_admin/web/orgPasswordChange.jsp (.../orgPasswordChange.jsp) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_admin/web/orgPasswordChange.jsp (.../orgPasswordChange.jsp) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -377,6 +377,9 @@
  • +
  • + +
  • Index: lams_admin/web/user.jsp =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_admin/web/user.jsp (.../user.jsp) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_admin/web/user.jsp (.../user.jsp) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -243,7 +243,9 @@
  • - +
  • + +
  • Index: lams_admin/web/userChangePass.jsp =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_admin/web/userChangePass.jsp (.../userChangePass.jsp) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_admin/web/userChangePass.jsp (.../userChangePass.jsp) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -135,7 +135,9 @@
  • - +
  • + +
  • Index: lams_build/3rdParty.userlibraries =================================================================== diff -u -r7060fa5b595fa324e138924099faf98e52cb5ca1 -re88830091139dd447cdd6a7f30c129c777104dac --- lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision 7060fa5b595fa324e138924099faf98e52cb5ca1) +++ lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -7,8 +7,8 @@ - - + + @@ -18,14 +18,14 @@ - + - + @@ -39,10 +39,11 @@ - + + Index: lams_build/build.xml =================================================================== diff -u -r7060fa5b595fa324e138924099faf98e52cb5ca1 -re88830091139dd447cdd6a7f30c129c777104dac --- lams_build/build.xml (.../build.xml) (revision 7060fa5b595fa324e138924099faf98e52cb5ca1) +++ lams_build/build.xml (.../build.xml) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -425,6 +425,14 @@ + + + + + + + Index: lams_build/conf/j2ee/jboss-deployment-structure.xml =================================================================== diff -u -r7060fa5b595fa324e138924099faf98e52cb5ca1 -re88830091139dd447cdd6a7f30c129c777104dac --- lams_build/conf/j2ee/jboss-deployment-structure.xml (.../jboss-deployment-structure.xml) (revision 7060fa5b595fa324e138924099faf98e52cb5ca1) +++ lams_build/conf/j2ee/jboss-deployment-structure.xml (.../jboss-deployment-structure.xml) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -55,6 +55,7 @@ + + + + + + + Index: lams_build/liblist.txt =================================================================== diff -u -r7060fa5b595fa324e138924099faf98e52cb5ca1 -re88830091139dd447cdd6a7f30c129c777104dac --- lams_build/liblist.txt (.../liblist.txt) (revision 7060fa5b595fa324e138924099faf98e52cb5ca1) +++ lams_build/liblist.txt (.../liblist.txt) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -56,6 +56,8 @@ odmg odmg-3.0.jar 3.0 +passpol passpol-0.7.1-SNAPSHOT.jar 0.7.1 Apache License 2.0 Codahale For checking for weak or compromised password. Recompiled manually with Java 11. + quartz quartz-2.2.3.jar 2.2.3 Apache License 2.0 Terracotta For running scheduled jobs spring spring-core-4.3.12.RELEASE.jar 4.3.12 Apache License 2.0 Pivotal programming and configuration model for modern Java-based enterprise applications Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -415,6 +415,7 @@ label.password.must.number = at least 1 number label.password.must.symbol = at least 1 symbol label.password.user.details = must not be the same as user login, ID, email or names +label.password.common = must not be common or breached label.password.history = must not be the same as last {0} passwords label.create.lesson = Create new lesson label.organisations = Select course with the lessons that needs to be export Index: lams_central/web/forgotPasswordChange.jsp =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_central/web/forgotPasswordChange.jsp (.../forgotPasswordChange.jsp) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_central/web/forgotPasswordChange.jsp (.../forgotPasswordChange.jsp) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -128,6 +128,9 @@
  • +
  • + +
  • Index: lams_central/web/passwordChangeContent.jsp =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_central/web/passwordChangeContent.jsp (.../passwordChangeContent.jsp) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_central/web/passwordChangeContent.jsp (.../passwordChangeContent.jsp) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -141,6 +141,9 @@
  • +
  • + +
  • Index: lams_central/web/signup/singupTab.jsp =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_central/web/signup/singupTab.jsp (.../singupTab.jsp) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_central/web/signup/singupTab.jsp (.../singupTab.jsp) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -186,6 +186,9 @@
  • +
  • + +
  • Index: lams_common/src/java/org/lamsfoundation/lams/util/ValidationUtil.java =================================================================== diff -u -r26a83b93c1ce8fa610895f50b57d44d6b7cc11db -re88830091139dd447cdd6a7f30c129c777104dac --- lams_common/src/java/org/lamsfoundation/lams/util/ValidationUtil.java (.../ValidationUtil.java) (revision 26a83b93c1ce8fa610895f50b57d44d6b7cc11db) +++ lams_common/src/java/org/lamsfoundation/lams/util/ValidationUtil.java (.../ValidationUtil.java) (revision e88830091139dd447cdd6a7f30c129c777104dac) @@ -27,13 +27,20 @@ import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; import org.lamsfoundation.lams.usermanagement.User; +import com.codahale.passpol.BreachDatabase; +import com.codahale.passpol.PasswordPolicy; +import com.codahale.passpol.Status; + /** * Utility methods for String validation. */ public class ValidationUtil { + private static Logger log = Logger.getLogger(ValidationUtil.class); + private final static Pattern REGEX_USER_NAME = Pattern.compile("^[^<>^!#&()/\\|\"?,:{}= ~`*%$]*$"); private final static Pattern REGEX_FIRST_LAST_NAME = Pattern.compile("^[\\p{L}]++(?:[' -][\\p{L}]++)*+\\.?$"); @@ -142,7 +149,12 @@ } - return ValidationUtil.isPasswordNotUserDetails(password, user); + boolean isPasswordNotUserDetails = ValidationUtil.isPasswordNotUserDetails(password, user); + if (!isPasswordNotUserDetails) { + return false; + } + + return ValidationUtil.isPasswordNotBreached(password); } /** @@ -191,6 +203,26 @@ return true; } + public static boolean isPasswordNotBreached(String password) { + try { + // check for a static list of 100k known weak passwords + PasswordPolicy commonPasswordPolicy = new PasswordPolicy(BreachDatabase.top100K(), 0, Integer.MAX_VALUE); + if (Status.OK != commonPasswordPolicy.check(password)) { + return false; + } + + // check online DB + PasswordPolicy pwnedPolicy = new PasswordPolicy(BreachDatabase.haveIBeenPwned(), 0, Integer.MAX_VALUE); + if (Status.OK != pwnedPolicy.check(password)) { + return false; + } + + } catch (Exception e) { + log.error("Error while checking password for breach", e); + } + return true; + } + /** * Checks whether supplied email address is valid. It validates email only if USER_VALIDATION_REQUIRED_EMAIL LAMS * configuration is ON.