diff --git a/libraries-security/pom.xml b/libraries-security/pom.xml index a57f029702..3077abc29c 100644 --- a/libraries-security/pom.xml +++ b/libraries-security/pom.xml @@ -38,6 +38,16 @@ ${junit.version} test + + org.passay + passay + 1.3.1 + + + org.cryptacular + cryptacular + 1.2.2 + diff --git a/libraries-security/src/test/java/com/baeldung/passay/NegativeMatchingRulesUnitTest.java b/libraries-security/src/test/java/com/baeldung/passay/NegativeMatchingRulesUnitTest.java new file mode 100644 index 0000000000..5054a5880e --- /dev/null +++ b/libraries-security/src/test/java/com/baeldung/passay/NegativeMatchingRulesUnitTest.java @@ -0,0 +1,149 @@ +package com.baeldung.passay; + +import org.cryptacular.bean.EncodingHashBean; +import org.cryptacular.spec.CodecSpec; +import org.cryptacular.spec.DigestSpec; +import org.junit.Assert; +import org.junit.Test; +import org.passay.DictionaryRule; +import org.passay.DictionarySubstringRule; +import org.passay.DigestHistoryRule; +import org.passay.EnglishSequenceData; +import org.passay.HistoryRule; +import org.passay.IllegalCharacterRule; +import org.passay.IllegalRegexRule; +import org.passay.IllegalSequenceRule; +import org.passay.NumberRangeRule; +import org.passay.PasswordData; +import org.passay.PasswordValidator; +import org.passay.RepeatCharacterRegexRule; +import org.passay.RuleResult; +import org.passay.SourceRule; +import org.passay.UsernameRule; +import org.passay.WhitespaceRule; +import org.passay.dictionary.ArrayWordList; +import org.passay.dictionary.WordListDictionary; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class NegativeMatchingRulesUnitTest { + + @Test + public void givenDictionaryRules_whenValidatePassword_thenFoundIllegalWordsFromDictionary() { + ArrayWordList arrayWordList = new ArrayWordList(new String[] { "bar", "foobar" }); + + WordListDictionary wordListDictionary = new WordListDictionary(arrayWordList); + + DictionaryRule dictionaryRule = new DictionaryRule(wordListDictionary); + DictionarySubstringRule dictionarySubstringRule = new DictionarySubstringRule(wordListDictionary); + + PasswordValidator passwordValidator = new PasswordValidator(dictionaryRule, dictionarySubstringRule); + RuleResult validate = passwordValidator.validate(new PasswordData("foobar")); + + assertFalse(validate.isValid()); + assertEquals("ILLEGAL_WORD:{matchingWord=foobar}", getDetail(validate, 0)); + assertEquals("ILLEGAL_WORD:{matchingWord=bar}", getDetail(validate, 1)); + } + + @Test + public void givenHistoryRule_whenValidatePassword_thenFoundIllegalWordsFromHistory() { + HistoryRule historyRule = new HistoryRule(); + + PasswordData passwordData = new PasswordData("123"); + passwordData.setPasswordReferences(new PasswordData.HistoricalReference("12345"), new PasswordData.HistoricalReference("1234"), new PasswordData.HistoricalReference("123")); + + PasswordValidator passwordValidator = new PasswordValidator(historyRule); + + RuleResult validate = passwordValidator.validate(passwordData); + + assertFalse(validate.isValid()); + assertEquals("HISTORY_VIOLATION:{historySize=3}", getDetail(validate, 0)); + } + + @Test + public void givenSeveralIllegalRules_whenValidatePassword_thenFoundSeveralIllegalPatterns() { + IllegalCharacterRule illegalCharacterRule = new IllegalCharacterRule(new char[] { 'a' }); + IllegalRegexRule illegalRegexRule = new IllegalRegexRule("\\w{2}\\d{2}"); + IllegalSequenceRule illegalSequenceRule = new IllegalSequenceRule(EnglishSequenceData.Alphabetical, 3, true); + NumberRangeRule numberRangeRule = new NumberRangeRule(1, 10); + WhitespaceRule whitespaceRule = new WhitespaceRule(); + + PasswordValidator passwordValidator = new PasswordValidator(illegalCharacterRule, illegalRegexRule, illegalSequenceRule, numberRangeRule, whitespaceRule); + + RuleResult validate = passwordValidator.validate(new PasswordData("abcd22 ")); + + assertFalse(validate.isValid()); + assertEquals("ILLEGAL_CHAR:{illegalCharacter=a, matchBehavior=contains}", getDetail(validate, 0)); + assertEquals("ILLEGAL_MATCH:{match=cd22, pattern=\\w{2}\\d{2}}", getDetail(validate, 1)); + assertEquals("ILLEGAL_ALPHABETICAL_SEQUENCE:{sequence=abc}", getDetail(validate, 2)); + assertEquals("ILLEGAL_ALPHABETICAL_SEQUENCE:{sequence=bcd}", getDetail(validate, 3)); + assertEquals("ILLEGAL_NUMBER_RANGE:{number=2, matchBehavior=contains}", getDetail(validate, 4)); + assertEquals("ILLEGAL_WHITESPACE:{whitespaceCharacter= , matchBehavior=contains}", getDetail(validate, 5)); + } + + @Test + public void givenSourceRule_whenValidatePassword_thenFoundIllegalWordsFromSource() { + SourceRule sourceRule = new SourceRule(); + + PasswordData passwordData = new PasswordData("password"); + passwordData.setPasswordReferences(new PasswordData.SourceReference("source", "password")); + + PasswordValidator passwordValidator = new PasswordValidator(sourceRule); + RuleResult validate = passwordValidator.validate(passwordData); + + assertFalse(validate.isValid()); + assertEquals("SOURCE_VIOLATION:{source=source}", getDetail(validate, 0)); + } + + @Test + public void givenRepeatCharacterRegexRuleRule_whenValidatePassword_thenFoundIllegalPatternMatches() { + RepeatCharacterRegexRule repeatCharacterRegexRule = new RepeatCharacterRegexRule(3); + + PasswordValidator passwordValidator = new PasswordValidator(repeatCharacterRegexRule); + + RuleResult validate = passwordValidator.validate(new PasswordData("aaabbb")); + + assertFalse(validate.isValid()); + assertEquals("ILLEGAL_MATCH:{match=aaa, pattern=([^\\x00-\\x1F])\\1{2}}", getDetail(validate, 0)); + assertEquals("ILLEGAL_MATCH:{match=bbb, pattern=([^\\x00-\\x1F])\\1{2}}", getDetail(validate, 1)); + } + + @Test + public void givenUserNameRule_whenValidatePassword_thenFoundUserNameInPassword() { + PasswordValidator passwordValidator = new PasswordValidator(new UsernameRule()); + + PasswordData passwordData = new PasswordData("testuser1234"); + passwordData.setUsername("testuser"); + + RuleResult validate = passwordValidator.validate(passwordData); + + assertFalse(validate.isValid()); + assertEquals("ILLEGAL_USERNAME:{username=testuser, matchBehavior=contains}", getDetail(validate, 0)); + } + + @Test + public void givenPasswordAndHashBeanAndEncryptedReferences_whenValidate_thenPasswordValidationShouldPass() { + List historicalReferences = Arrays.asList(new PasswordData.HistoricalReference("SHA256", "2e4551de804e27aacf20f9df5be3e8cd384ed64488b21ab079fb58e8c90068ab")); + PasswordData passwordData = new PasswordData("example!"); + passwordData.setPasswordReferences(historicalReferences); + + EncodingHashBean encodingHashBean = new EncodingHashBean(new CodecSpec("Base64"), new DigestSpec("SHA256"), 1, false); + + PasswordValidator passwordValidator = new PasswordValidator(new DigestHistoryRule(encodingHashBean)); + + RuleResult validate = passwordValidator.validate(passwordData); + + Assert.assertTrue(validate.isValid()); + } + + private String getDetail(RuleResult validate, int i) { + return validate.getDetails() + .get(i) + .toString(); + } + +} diff --git a/libraries-security/src/test/java/com/baeldung/passay/PasswordGeneratorUnitTest.java b/libraries-security/src/test/java/com/baeldung/passay/PasswordGeneratorUnitTest.java new file mode 100644 index 0000000000..ff279e9317 --- /dev/null +++ b/libraries-security/src/test/java/com/baeldung/passay/PasswordGeneratorUnitTest.java @@ -0,0 +1,50 @@ +package com.baeldung.passay; + +import org.junit.Assert; +import org.junit.Test; +import org.passay.CharacterData; +import org.passay.CharacterRule; +import org.passay.EnglishCharacterData; +import org.passay.PasswordGenerator; + +import java.util.stream.Stream; + +public class PasswordGeneratorUnitTest { + + @Test + public void givenDigitsGenerator_whenGeneratingPassword_thenPasswordContainsDigitsHasLength10() { + CharacterRule digits = new CharacterRule(EnglishCharacterData.Digit); + + PasswordGenerator passwordGenerator = new PasswordGenerator(); + String password = passwordGenerator.generatePassword(10, digits); + + Assert.assertTrue(password.length() == 10); + Assert.assertTrue(containsOnlyCharactersFromSet(password, "0123456789")); + } + + @Test + public void givenCustomizedRule_whenGenerating_thenGeneratedPasswordContainsCustomizedCharacters() { + CharacterRule specialCharacterRule = new CharacterRule(new CharacterData() { + @Override + public String getErrorCode() { + return "SAMPLE_ERROR_CODE"; + } + + @Override + public String getCharacters() { + return "ABCxyz123!@#"; + } + }); + + PasswordGenerator passwordGenerator = new PasswordGenerator(); + String password = passwordGenerator.generatePassword(10, specialCharacterRule); + + Assert.assertTrue(containsOnlyCharactersFromSet(password, "ABCxyz123!@#")); + } + + private boolean containsOnlyCharactersFromSet(String password, String setOfCharacters) { + return Stream.of(password.split("")) + .allMatch(it -> setOfCharacters.contains(it)); + } + +} diff --git a/libraries-security/src/test/java/com/baeldung/passay/PasswordValidatorUnitTest.java b/libraries-security/src/test/java/com/baeldung/passay/PasswordValidatorUnitTest.java new file mode 100644 index 0000000000..3fc59a82d5 --- /dev/null +++ b/libraries-security/src/test/java/com/baeldung/passay/PasswordValidatorUnitTest.java @@ -0,0 +1,81 @@ +package com.baeldung.passay; + +import org.junit.Assert; +import org.junit.Test; +import org.passay.LengthRule; +import org.passay.MessageResolver; +import org.passay.PasswordData; +import org.passay.PasswordValidator; +import org.passay.PropertiesMessageResolver; +import org.passay.RuleResult; +import org.passay.RuleResultDetail; +import org.passay.RuleResultMetadata; +import org.passay.WhitespaceRule; + +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class PasswordValidatorUnitTest { + + @Test + public void givenPasswordValidatorWithLengthRule_whenValidation_thenTooShortPassword() { + PasswordData passwordData = new PasswordData("1234"); + + PasswordValidator passwordValidator = new PasswordValidator(new LengthRule(5)); + + RuleResult validate = passwordValidator.validate(passwordData); + assertEquals(false, validate.isValid()); + + RuleResultDetail ruleResultDetail = validate.getDetails() + .get(0); + assertEquals("TOO_SHORT", ruleResultDetail.getErrorCode()); + assertEquals(5, ruleResultDetail.getParameters() + .get("minimumLength")); + assertEquals(5, ruleResultDetail.getParameters() + .get("maximumLength")); + + Integer lengthCount = validate.getMetadata() + .getCounts() + .get(RuleResultMetadata.CountCategory.Length); + assertEquals(Integer.valueOf(4), lengthCount); + } + + @Test + public void givenPasswordValidatorWithLengthRule_whenValidation_thenTooLongPassword() { + PasswordData passwordData = new PasswordData("123456"); + + PasswordValidator passwordValidator = new PasswordValidator(new LengthRule(5)); + + RuleResult validate = passwordValidator.validate(passwordData); + assertFalse(validate.isValid()); + Assert.assertEquals("TOO_LONG", validate.getDetails() + .get(0) + .getErrorCode()); + } + + @Test + public void givenPasswordValidatorWithLengthRule_whenValidation_thenCustomizedMeesagesAvailable() throws IOException { + URL resource = this.getClass() + .getClassLoader() + .getResource("messages.properties"); + Properties props = new Properties(); + props.load(new FileInputStream(resource.getPath())); + + MessageResolver resolver = new PropertiesMessageResolver(props); + PasswordValidator validator = new PasswordValidator(resolver, new LengthRule(8, 16), new WhitespaceRule()); + + RuleResult tooShort = validator.validate(new PasswordData("XXXX")); + RuleResult tooLong = validator.validate(new PasswordData("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ")); + + assertEquals("Password must not contain less characters than 16.", validator.getMessages(tooShort) + .get(0)); + assertEquals("Password must not have more characters than 16.", validator.getMessages(tooLong) + .get(0)); + } + +} diff --git a/libraries-security/src/test/java/com/baeldung/passay/PositiveMatchingRulesUnitTest.java b/libraries-security/src/test/java/com/baeldung/passay/PositiveMatchingRulesUnitTest.java new file mode 100644 index 0000000000..0da1b43335 --- /dev/null +++ b/libraries-security/src/test/java/com/baeldung/passay/PositiveMatchingRulesUnitTest.java @@ -0,0 +1,76 @@ +package com.baeldung.passay; + +import org.junit.Test; +import org.passay.AllowedCharacterRule; +import org.passay.AllowedRegexRule; +import org.passay.CharacterCharacteristicsRule; +import org.passay.CharacterRule; +import org.passay.EnglishCharacterData; +import org.passay.LengthComplexityRule; +import org.passay.LengthRule; +import org.passay.PasswordData; +import org.passay.PasswordValidator; +import org.passay.RuleResult; + +import static org.junit.Assert.*; + +public class PositiveMatchingRulesUnitTest { + + @Test + public void givenPasswordValidationRules_whenValidatingPassword_thenPosswordIsNotValidWithSeveralErrors() { + PasswordValidator passwordValidator = new PasswordValidator(new AllowedCharacterRule(new char[] { 'a', 'b', 'c' }), new AllowedRegexRule("\\d{2}\\w{10}"), new CharacterRule(EnglishCharacterData.LowerCase, 5), new LengthRule(8, 10)); + + RuleResult validate = passwordValidator.validate(new PasswordData("12abc")); + + assertFalse(validate.isValid()); + assertEquals("ALLOWED_CHAR:{illegalCharacter=1, matchBehavior=contains}", getDetail(validate, 0)); + assertEquals("ALLOWED_CHAR:{illegalCharacter=2, matchBehavior=contains}", getDetail(validate, 1)); + assertEquals("ALLOWED_MATCH:{pattern=\\d{2}\\w{10}}", getDetail(validate, 2)); + assertEquals("INSUFFICIENT_LOWERCASE:{" + "minimumRequired=5, matchingCharacterCount=3, " + "validCharacters=abcdefghijklmnopqrstuvwxyz, " + "matchingCharacters=abc}", getDetail(validate, 3)); + assertEquals("TOO_SHORT:{minimumLength=8, maximumLength=10}", getDetail(validate, 4)); + } + + @Test + public void givenRulesForDifferentPasswordLength_whenValidatingTwoDifferentPassword_thenBothOfThemAreInvalid() { + PasswordData shortPassword = new PasswordData("12ab"); + PasswordData longPassword = new PasswordData("1234abcde"); + + LengthComplexityRule lengthComplexityRule = new LengthComplexityRule(); + lengthComplexityRule.addRules("[1,5]", new CharacterRule(EnglishCharacterData.LowerCase, 5)); + lengthComplexityRule.addRules("[6,10]", new AllowedCharacterRule(new char[] { 'a', 'b', 'c', 'd' })); + + PasswordValidator passwordValidator = new PasswordValidator(lengthComplexityRule); + + RuleResult validateShort = passwordValidator.validate(shortPassword); + RuleResult validateLong = passwordValidator.validate(longPassword); + + assertFalse(validateShort.isValid()); + assertFalse(validateLong.isValid()); + + assertEquals("INSUFFICIENT_LOWERCASE:{" + "minimumRequired=5, " + "matchingCharacterCount=2, " + "validCharacters=abcdefghijklmnopqrstuvwxyz, " + "matchingCharacters=ab}", getDetail(validateShort, 0)); + assertEquals("ALLOWED_CHAR:{illegalCharacter=1, matchBehavior=contains}", getDetail(validateLong, 0)); + } + + @Test + public void givenCharacterCharacteristicsRule_whenValidatingPassword_thenItIsInvalidAsItBreaksToManyRules() { + PasswordData shortPassword = new PasswordData(); + shortPassword.setPassword("12345abcde!"); + + CharacterCharacteristicsRule characterCharacteristicsRule = new CharacterCharacteristicsRule(4, new CharacterRule(EnglishCharacterData.LowerCase, 5), new CharacterRule(EnglishCharacterData.UpperCase, 5), new CharacterRule(EnglishCharacterData.Digit), + new CharacterRule(EnglishCharacterData.Special)); + + PasswordValidator passwordValidator = new PasswordValidator(characterCharacteristicsRule); + + RuleResult validate = passwordValidator.validate(shortPassword); + assertFalse(validate.isValid()); + + assertEquals("INSUFFICIENT_UPPERCASE:{" + "minimumRequired=5, " + "matchingCharacterCount=0, " + "validCharacters=ABCDEFGHIJKLMNOPQRSTUVWXYZ, " + "matchingCharacters=}", getDetail(validate, 0)); + assertEquals("INSUFFICIENT_CHARACTERISTICS:{" + "successCount=3, " + "minimumRequired=4, " + "ruleCount=4}", getDetail(validate, 1)); + } + + private String getDetail(RuleResult validate, int i) { + return validate.getDetails() + .get(i) + .toString(); + } +} diff --git a/libraries-security/src/test/resources/messages.properties b/libraries-security/src/test/resources/messages.properties new file mode 100644 index 0000000000..ad0039d71d --- /dev/null +++ b/libraries-security/src/test/resources/messages.properties @@ -0,0 +1,2 @@ +TOO_LONG=Password must not have more characters than %2$s. +TOO_SHORT=Password must not contain less characters than %2$s. \ No newline at end of file