diff options
author | Ivan Motsch | 2021-08-30 12:35:46 +0000 |
---|---|---|
committer | Ivan Motsch | 2021-08-30 12:45:20 +0000 |
commit | 0f6ed3122717de60c2236494fbffc8765e5f6e82 (patch) | |
tree | dc54db523064ee297eab03808d4311e048390ef0 | |
parent | 9b2ee7b87433cb15f1fb62ecdea389cac7315ddc (diff) | |
download | org.eclipse.scout.rt-releases/11.0.tar.gz org.eclipse.scout.rt-releases/11.0.tar.xz org.eclipse.scout.rt-releases/11.0.zip |
Security functions reviewreleases/11.0
- update and centralize password policy
- force use of hashPassword function in SecurityUtility
- add verifyPassword function for safe and centralized salted hashed
password check
Signed-off-by: Ivan Motsch <ivan.motsch@bsiag.com>
9 files changed, 186 insertions, 110 deletions
diff --git a/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/security/SecurityUtilityTest.java b/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/security/SecurityUtilityTest.java index ea08703877..d8034fdb25 100644 --- a/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/security/SecurityUtilityTest.java +++ b/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/security/SecurityUtilityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 BSI Business Systems Integration AG. + * Copyright (c) 2010-2021 BSI Business Systems Integration AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -170,7 +170,7 @@ public class SecurityUtilityTest { public void testHashPassword() { final byte[] salt = SecurityUtility.createRandomBytes(); final byte[] salt2 = SecurityUtility.createRandomBytes(); - final int iterations = 10000; + final int iterations = ISecurityProvider.MIN_PASSWORD_HASH_ITERATIONS; // test hash byte[] hash1 = SecurityUtility.hashPassword(PASSWORD, salt, iterations); @@ -228,30 +228,11 @@ public class SecurityUtilityTest { ok = true; } Assert.assertTrue(ok); - ok = false; - try { - SecurityUtility.hashPassword(PASSWORD, salt, 0); - } - catch (AssertionException e) { - ok = true; - } - Assert.assertTrue(ok); - ok = false; - try { - SecurityUtility.hashPassword(PASSWORD, salt, -1); - } - catch (AssertionException e) { - ok = true; - } - Assert.assertTrue(ok); - ok = false; - try { - SecurityUtility.hashPassword(PASSWORD, salt, 9999); - } - catch (AssertionException e) { - ok = true; - } - Assert.assertTrue(ok); + + //iterations is max(min_iter, arg) + SecurityUtility.hashPassword(PASSWORD, salt, 0); + SecurityUtility.hashPassword(PASSWORD, salt, -1); + SecurityUtility.hashPassword(PASSWORD, salt, 9999); } @Test diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/ConfigFileCredentialVerifier.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/ConfigFileCredentialVerifier.java index db5c0e705d..72b0e02541 100644 --- a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/ConfigFileCredentialVerifier.java +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/ConfigFileCredentialVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2018 BSI Business Systems Integration AG. + * Copyright (c) 2010-2021 BSI Business Systems Integration AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -262,16 +262,12 @@ public class ConfigFileCredentialVerifier implements ICredentialVerifier { private HashedPassword(final char[] password, final byte[] salt) { m_salt = salt; - m_hash = createPasswordHash(password, salt); + m_hash = SecurityUtility.hashPassword(password, salt); } @Override public boolean isEqual(final char[] password) { - return Arrays.equals(m_hash, createPasswordHash(password, m_salt)); - } - - protected byte[] createPasswordHash(final char[] password, final byte[] salt) { - return SecurityUtility.hash(toBytes(password), salt); + return SecurityUtility.verifyPasswordHash(password, m_salt, m_hash); } protected byte[] toBytes(final char[] password) { diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/ISecurityProvider.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/ISecurityProvider.java index e5ef621e3a..3bb4664717 100644 --- a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/ISecurityProvider.java +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/ISecurityProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2020 BSI Business Systems Integration AG. + * Copyright (c) 2010-2021 BSI Business Systems Integration AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,6 +13,7 @@ package org.eclipse.scout.rt.platform.security; import java.io.InputStream; import java.io.OutputStream; import java.security.SecureRandom; +import java.util.Arrays; import org.eclipse.scout.rt.platform.ApplicationScoped; import org.eclipse.scout.rt.platform.exception.ProcessingException; @@ -28,6 +29,17 @@ import org.eclipse.scout.rt.platform.util.Assertions.AssertionException; public interface ISecurityProvider { /** + * Specifies the minimum of password hash iterations with PBKDF2-HMAC-SHA512. + * <p> + * https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html + */ + int MIN_PASSWORD_HASH_ITERATIONS_2016 = 10000; + int MIN_PASSWORD_HASH_ITERATIONS_2019 = 20000; + int MIN_PASSWORD_HASH_ITERATIONS_2021 = 120000; + + int MIN_PASSWORD_HASH_ITERATIONS = MIN_PASSWORD_HASH_ITERATIONS_2021; + + /** * Create a Message Authentication Code (MAC) for the given data and password. * * @param password @@ -108,6 +120,13 @@ public interface ISecurityProvider { byte[] createHash(InputStream data, byte[] salt, int iterations); /** + * see {@link #createPasswordHash(char[], byte[], int)} with default iteration count + */ + default byte[] createPasswordHash(char[] password, byte[] salt) { + return createPasswordHash(password, salt, MIN_PASSWORD_HASH_ITERATIONS); + } + + /** * Creates a hash for the given password.<br> * * @param password @@ -119,8 +138,8 @@ public interface ISecurityProvider { * @param iterations * Specifies how many times the method executes its underlying algorithm. A higher value is safer.<br> * While there is a minimum number of iterations recommended to ensure data safety, this value changes every - * year as technology improves. As by May 2016 at least 10000 iterations are recommended. Therefore this - * method will not accept values below that limit.<br> + * year as technology improves. As by Aug 2021 at least 120000 iterations are recommended, see + * https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html.<br> * Experimentation is important. To provide a good security use an iteration count so that the call to this * method requires one half second to execute (on the production system). Also consider the number of users * and the number of logins executed to find a value that scales in your environment. @@ -138,6 +157,28 @@ public interface ISecurityProvider { byte[] createPasswordHash(char[] password, byte[] salt, int iterations); /** + * This method is recommended in combination with {@link #createPasswordHash(char[], byte[])} where the iteration + * count is omitted. This has the advantage that the check of the password hash is independent of the creation of the + * hash. In case the iteration count is increased yearly, this method checks if the hash is valid + * + * @return true if calculated password has with {@link #createPasswordHash(char[], byte[], int)} matches the expected + * hash. + * @since 11.0 + */ + default boolean verifyPasswordHash(char[] password, byte[] salt, byte[] expectedHash) { + if (Arrays.equals(expectedHash, createPasswordHash(password, salt, MIN_PASSWORD_HASH_ITERATIONS))) { + return true; + } + if (Arrays.equals(expectedHash, createPasswordHash(password, salt, MIN_PASSWORD_HASH_ITERATIONS_2019))) { + return true; + } + if (Arrays.equals(expectedHash, createPasswordHash(password, salt, MIN_PASSWORD_HASH_ITERATIONS_2016))) { + return true; + } + return false; + } + + /** * Encrypts the given data using the given {@link EncryptionKey}.<br> * Use {@link #decrypt(InputStream, OutputStream, EncryptionKey)} to decrypt the data again using the same key. * diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/PasswordPolicy.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/PasswordPolicy.java new file mode 100644 index 0000000000..894b0ea9a5 --- /dev/null +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/PasswordPolicy.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010-2021 BSI Business Systems Integration AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * BSI Business Systems Integration AG - initial API and implementation + */ +package org.eclipse.scout.rt.platform.security; + +import java.util.Arrays; + +import org.eclipse.scout.rt.platform.Bean; +import org.eclipse.scout.rt.platform.exception.VetoException; +import org.eclipse.scout.rt.platform.text.TEXTS; + +/** + * This {@link Bean} replaces the former IPasswordPolicy and DefaultPasswordPolicy + * + * @since 11.0 + */ +@Bean +public class PasswordPolicy { + public static final int MIN_PASSWORD_LENGTH = 12; + + private static boolean containsUsername(char[] newPassword, String userName) { + if (userName == null) { + return false; + } + StringBuilder b = new StringBuilder(newPassword.length); + for (char c : newPassword) { + b.append(Character.toUpperCase(c)); + } + return b.indexOf(userName.toUpperCase()) >= 0; + } + + private static boolean containsOneOf(char[] charsOfPasswordSorted, String charsToSearch) { + for (char toFind : charsToSearch.toCharArray()) { + if (Arrays.binarySearch(charsOfPasswordSorted, toFind) >= 0) { + return true; + } + } + return false; + } + + public String getText() { + return TEXTS.get("DefaultPasswordPolicyText"); + } + + public void check(String userName, char[] newPassword, int historyIndex) { + if (newPassword == null || newPassword.length < MIN_PASSWORD_LENGTH) { + throw new VetoException(TEXTS.get("PasswordMin8Chars")); + } + if (historyIndex >= 0) { + throw new VetoException(TEXTS.get("PasswordNotSameAsLasts")); + } + + char[] charsInPasswordSorted = Arrays.copyOf(newPassword, newPassword.length); + Arrays.sort(charsInPasswordSorted); + if (!containsOneOf(charsInPasswordSorted, "0123456789")) { + throw new VetoException(TEXTS.get("PasswordMinOneDigit")); + } + if (!containsOneOf(charsInPasswordSorted, "abcdefghijklmnopqrstuvwxyz")) { + throw new VetoException(TEXTS.get("PasswordMinOneChar", "a-z")); + } + if (!containsOneOf(charsInPasswordSorted, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) { + throw new VetoException(TEXTS.get("PasswordMinOneChar", "A-Z")); + } + if (!containsOneOf(charsInPasswordSorted, "!@#$%^&*()_+|~-=\\`{}[]:\";'<>?,./")) { + throw new VetoException(TEXTS.get("PasswordMinOnNonStdChar")); + } + if (containsUsername(newPassword, userName)) { + throw new VetoException(TEXTS.get("PasswordUsernameNotPartOfPass")); + } + } +} diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/SecurityUtility.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/SecurityUtility.java index 450ccefef2..5673b2be20 100644 --- a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/SecurityUtility.java +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/SecurityUtility.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2020 BSI Business Systems Integration AG. + * Copyright (c) 2010-2021 BSI Business Systems Integration AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -218,6 +218,15 @@ public final class SecurityUtility { } /** + * @see ISecurityProvider#createPasswordHash(char[], byte[]) + */ + public static byte[] hashPassword(char[] password, byte[] salt) { + return SECURITY_PROVIDER.get().createPasswordHash(password, salt); + } + + /** + * It is recommended to use {@link #hashPassword(char[], byte[])} without iteration count. + * * @see ISecurityProvider#createPasswordHash(char[], byte[], int) */ public static byte[] hashPassword(char[] password, byte[] salt, int iterations) { @@ -225,6 +234,18 @@ public final class SecurityUtility { } /** + * This method is recommended in combination with {@link #hashPassword(char[], byte[])} where the iteration count is + * omitted. This has the advantage that the check of the password hash is independent of the creation of the hash. In + * case the iteration count is increased yearly, this method checks if the hash is valid + * + * @return true if calculated password has with {@link #hashPassword(char[], byte[], int)} matches the expected hash. + * @since 11.0 + */ + public static boolean verifyPasswordHash(char[] password, byte[] salt, byte[] expectedHash) { + return SECURITY_PROVIDER.get().verifyPasswordHash(password, salt, expectedHash); + } + + /** * Creates a hash for the given data using the given salt.<br> * <br> * <b>Important:</b> For hashing of passwords use {@link #hashPassword(char[], byte[], int)}! diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/SunSecurityProvider.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/SunSecurityProvider.java index 6501efc14d..2613356f68 100644 --- a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/SunSecurityProvider.java +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/SunSecurityProvider.java @@ -77,10 +77,6 @@ import sun.security.x509.SubjectAlternativeNameExtension; */ @Order(5500) public class SunSecurityProvider implements ISecurityProvider { - /** - * Specifies the minimum of password hash iterations. - */ - protected static final int MIN_PASSWORD_HASH_ITERATIONS = 10000; /** * Buffer size for {@link InputStream} read. @@ -193,7 +189,7 @@ public class SunSecurityProvider implements ISecurityProvider { public byte[] createPasswordHash(char[] password, byte[] salt, int iterations) { assertGreater(assertNotNull(password, "password must not be null.").length, 0, "empty password is not allowed."); assertGreater(assertNotNull(salt, "salt must not be null.").length, 0, "empty salt is not allowed."); - assertGreaterOrEqual(iterations, MIN_PASSWORD_HASH_ITERATIONS, "iterations must be > {}", MIN_PASSWORD_HASH_ITERATIONS); + iterations = Math.max(MIN_PASSWORD_HASH_ITERATIONS, iterations); // other checks are done by the PBEKeySpec constructor try { diff --git a/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/services/common/pwd/DefaultPasswordPolicyTest.java b/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/services/common/pwd/DefaultPasswordPolicyTest.java index fd57b0ac2a..2dba6b5886 100644 --- a/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/services/common/pwd/DefaultPasswordPolicyTest.java +++ b/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/services/common/pwd/DefaultPasswordPolicyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 BSI Business Systems Integration AG. + * Copyright (c) 2010-2021 BSI Business Systems Integration AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,6 +11,7 @@ package org.eclipse.scout.rt.server.services.common.pwd; import org.eclipse.scout.rt.platform.exception.VetoException; +import org.eclipse.scout.rt.platform.security.PasswordPolicy; import org.junit.Assert; import org.junit.Test; @@ -18,26 +19,26 @@ public class DefaultPasswordPolicyTest { @Test public void testPolicy() { - IPasswordPolicy policy = new DefaultPasswordPolicy(); - assertVetoException(policy, "11", "whatEver".toCharArray(), "uname", 1); - assertVetoException(policy, "11", "whatEver".toCharArray(), "uname", 0); - assertVetoException(policy, "11", "11unameA22_".toCharArray(), "uname", -1); - assertVetoException(policy, "11", "11unameA22_".toCharArray(), "UNAME", -1); - assertVetoException(policy, "11", null, "uname", -1); - assertVetoException(policy, "11", "".toCharArray(), "uname", -1); - assertVetoException(policy, "11", "whatEver".toCharArray(), "uname", -1); - assertVetoException(policy, "11", "123456789".toCharArray(), "uname", -1); - assertVetoException(policy, "11", "1234567ABCabc".toCharArray(), "uname", -1); - assertVetoException(policy, "11", "WHATEVER1_".toCharArray(), "uname", -1); + PasswordPolicy policy = new PasswordPolicy(); + assertVetoException(policy, "uname", "12characters".toCharArray(), 1); + assertVetoException(policy, "uname", "12characters".toCharArray(), 0); + assertVetoException(policy, "uname", "12charactersunameA22_".toCharArray(), -1); + assertVetoException(policy, "UNAME", "12charactersunameA22_".toCharArray(), -1); + assertVetoException(policy, "uname", null, -1); + assertVetoException(policy, "uname", "".toCharArray(), -1); + assertVetoException(policy, "uname", "12characters".toCharArray(), -1); + assertVetoException(policy, "uname", "123456789012".toCharArray(), -1); + assertVetoException(policy, "uname", "1234567ABCabc".toCharArray(), -1); + assertVetoException(policy, "uname", "12CHARACTERS_".toCharArray(), -1); - policy.check("11", "whatEver1_".toCharArray(), "uid", -1); - policy.check("11", "whatEver1_".toCharArray(), null, -1); + policy.check("uid", "12Characters_".toCharArray(), -1); + policy.check(null, "12Characters_".toCharArray(), -1); } - private void assertVetoException(IPasswordPolicy policy, String userId, char[] newPassword, String userName, int historyIndex) { + private void assertVetoException(PasswordPolicy policy, String userName, char[] newPassword, int historyIndex) { boolean hasException = false; try { - policy.check(userId, newPassword, userName, historyIndex); + policy.check(userName, newPassword, historyIndex); } catch (VetoException e) { hasException = true; diff --git a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/pwd/DefaultPasswordPolicy.java b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/pwd/DefaultPasswordPolicy.java index 6b30ff3180..f4ee1dcb1b 100644 --- a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/pwd/DefaultPasswordPolicy.java +++ b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/pwd/DefaultPasswordPolicy.java @@ -10,65 +10,22 @@ */ package org.eclipse.scout.rt.server.services.common.pwd; -import java.util.Arrays; - -import org.eclipse.scout.rt.platform.exception.VetoException; -import org.eclipse.scout.rt.platform.text.TEXTS; +import org.eclipse.scout.rt.platform.BEANS; +import org.eclipse.scout.rt.platform.security.PasswordPolicy; +/** + * @deprecated in 11.0, moved to {@link PasswordPolicy} + */ +@Deprecated public class DefaultPasswordPolicy implements IPasswordPolicy { - private static final int MIN_PASSWORD_LENGTH = 12; - @Override public String getText() { - return TEXTS.get("DefaultPasswordPolicyText"); + return BEANS.get(PasswordPolicy.class).getText(); } @Override public void check(String userId, char[] newPassword, String userName, int historyIndex) { - if (newPassword == null || newPassword.length < MIN_PASSWORD_LENGTH) { - throw new VetoException(TEXTS.get("PasswordMin8Chars")); - } - if (historyIndex >= 0) { - throw new VetoException(TEXTS.get("PasswordNotSameAsLasts")); - } - - char[] charsInPasswordSorted = Arrays.copyOf(newPassword, newPassword.length); - Arrays.sort(charsInPasswordSorted); - if (!containsOneOf(charsInPasswordSorted, "0123456789")) { - throw new VetoException(TEXTS.get("PasswordMinOneDigit")); - } - if (!containsOneOf(charsInPasswordSorted, "abcdefghijklmnopqrstuvwxyz")) { - throw new VetoException(TEXTS.get("PasswordMinOneChar", "a-z")); - } - if (!containsOneOf(charsInPasswordSorted, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) { - throw new VetoException(TEXTS.get("PasswordMinOneChar", "A-Z")); - } - if (!containsOneOf(charsInPasswordSorted, "!@#$%^&*()_+|~-=\\`{}[]:\";'<>?,./")) { - throw new VetoException(TEXTS.get("PasswordMinOnNonStdChar")); - } - if (containsUsername(newPassword, userName)) { - throw new VetoException(TEXTS.get("PasswordUsernameNotPartOfPass")); - } - } - - private static boolean containsUsername(char[] newPassword, String userName) { - if (userName == null) { - return false; - } - StringBuilder b = new StringBuilder(newPassword.length); - for (char c : newPassword) { - b.append(Character.toUpperCase(c)); - } - return b.indexOf(userName.toUpperCase()) >= 0; - } - - private static boolean containsOneOf(char[] charsOfPasswordSorted, String charsToSearch) { - for (char toFind : charsToSearch.toCharArray()) { - if (Arrays.binarySearch(charsOfPasswordSorted, toFind) >= 0) { - return true; - } - } - return false; + BEANS.get(PasswordPolicy.class).check(userName, newPassword, historyIndex); } } diff --git a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/pwd/IPasswordPolicy.java b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/pwd/IPasswordPolicy.java index 542c2a6fd0..d19af630cd 100644 --- a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/pwd/IPasswordPolicy.java +++ b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/pwd/IPasswordPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2017 BSI Business Systems Integration AG. + * Copyright (c) 2010-2021 BSI Business Systems Integration AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,7 +12,12 @@ package org.eclipse.scout.rt.server.services.common.pwd; import org.eclipse.scout.rt.platform.exception.ProcessingException; import org.eclipse.scout.rt.platform.nls.NlsLocale; +import org.eclipse.scout.rt.platform.security.PasswordPolicy; +/** + * @deprecated in 11.0, moved to {@link PasswordPolicy} + */ +@Deprecated public interface IPasswordPolicy { /** |