All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rodeo.password.pgencheck.PasswordMaker Maven / Gradle / Ivy

Go to download

This project provides classes to create and validate passwords. A number of criteria can be provided (minimum and maximum length, presence of certain character types, etc.) to validate and create passwords. Any Unicode character can be used in the generation and validation of passwords.

The newest version!
package rodeo.password.pgencheck;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static rodeo.password.pgencheck.ErrorMessages.AT_LEAST_ONE_CHAR;
import static rodeo.password.pgencheck.ErrorMessages.NO_MAKER_CHAR_SET_PROVIDED;
import static rodeo.password.pgencheck.ErrorMessages.TOO_MANY_CHAR_BY_TYPE_FOR_LENGTH;
import static rodeo.password.pgencheck.ErrorMessages.TOO_MANY_RESTRICTIONS_ON_CHAR_BY_TYPE_FOR_LENGTH1;
import static rodeo.password.pgencheck.ErrorMessages.TOO_MANY_RESTRICTIONS_ON_CHAR_BY_TYPE_FOR_LENGTH2;

/**
 * Create passwords according to predefined criteria.
 * 

* The following criteria are available: *

    *
  • length;
  • *
  • * constraints on characters composing the password: *
      *
    • character must belong to a predefined group of characters;
    • *
    • minimum and maximum number of characters per group can be specified;
    • *
    *
  • *
  • * the way random numbers are used in password generation can be configured by implementing a * {@link RandomUIntGenerator RandomUIntGenerator}. *
  • *
*/ public final class PasswordMaker extends PasswordData { private final int length; private final RandomUIntGenerator randomUIntGenerator; private final List passwordChars = new ArrayList<>(); private final List> groupPasswordChars = new ArrayList<>(); private PasswordMaker( int length, List charGroups, List groupMinCounts, List groupMaxCounts, RandomUIntGenerator randomUIntGenerator) { super(charGroups, groupMinCounts, groupMaxCounts); this.length = length; this.randomUIntGenerator = randomUIntGenerator; for (int groupIndex = 0; groupIndex < charGroups.size(); ++groupIndex) { var pcs = new ArrayList(); for (int codePoint: charGroups.get(groupIndex).codePoints().toArray()) pcs.add(new PasswordChar(codePoint, groupIndex)); passwordChars.addAll(pcs); groupPasswordChars.add(pcs); } } /** * Create a factory to specify password generation criteria and create a PasswordMaker object. * @return an internal PasswordMaker factory * @see PasswordMaker.Factory */ public static Factory factory() { return new Factory(); } /** * Returns the specified length for generated passwords. * @return the specified length for generated passwords */ public int getLength() { return length; } /** * Returns the implementation of the random number generator used to create passwords. * @return the implementation of the random number generator used to create passwords * @see RandomUIntGenerator * @see DefaultUIntGenerator */ public RandomUIntGenerator getRandomUIntGenerator() { return randomUIntGenerator; } /** * Generate a new password. * @return the generated password */ public String create() { var charList = passwordChars; var groupCounts = initGroupCounts(); var passwordChars = new ArrayList(); for (int i = 0; i < groupCounts.size(); i++) { for (int j = 0; j < groupCounts.get(i).min; j++) { passwordChars.add(getRandomCharacter(groupPasswordChars.get(i)).getCodePoint()); groupCounts.get(i).count++; } } while (passwordChars.size() < length) { var pc = getRandomCharacter(charList); var groupCount = groupCounts.get(pc.getCharGroupIndex()); if (groupCount.canAddChar()) { passwordChars.add(pc.getCodePoint()); groupCount.count++; } else charList = updateCharacterList(groupCounts); } Collections.shuffle(passwordChars, randomUIntGenerator.random()); var password = new StringBuilder(); for (int cp: passwordChars) password.appendCodePoint(cp); return password.toString(); } private List initGroupCounts() { var groupCounts = new ArrayList(); for (int groupIndex = 0; groupIndex < charGroups().size(); ++groupIndex) groupCounts.add(new GroupCount(groupMinCounts().get(groupIndex), groupMaxCounts().get(groupIndex))); return groupCounts; } private PasswordChar getRandomCharacter(List passwordChars) { return passwordChars.get(randomUIntGenerator.getNextUInt(passwordChars.size())); } private List updateCharacterList(List groupCounts) { var passwordChars = new ArrayList(); for (int i = 0; i < groupCounts.size(); i++) if (groupCounts.get(i).canAddChar()) passwordChars.addAll(groupPasswordChars.get(i)); return passwordChars; } private static final class PasswordChar { private final int codePoint; private final int charGroupIndex; PasswordChar(int codePoint, int charGroupIndex) { this.codePoint = codePoint; this.charGroupIndex = charGroupIndex; } int getCodePoint() { return codePoint; } int getCharGroupIndex() { return charGroupIndex; } } private static class GroupCount { final int min; final int max; int count = 0; GroupCount(int min, int max) { this.min = min; this.max = max; } boolean canAddChar() { return max == 0 || count < max; } } /** * Internal factory to create PasswordMakers. *

* This factory class allows you to build a PasswordMaker using a fluent interface. You create a * Factory by calling {@link PasswordMaker#factory() PasswordMaker.factory()}. * Once all the criteria have been specified, you call the {@link #create() create} function * to create a PasswordMaker object. */ public static final class Factory extends AbstractFactory { private int length = 16; private RandomUIntGenerator randomUIntGenerator = DefaultUIntGenerator.GENERATOR; private Factory() { } /** * Sets the length of generated password. * @param length the password length * @return this factory */ public Factory setLength(int length) { if (length < 1) throw new IllegalArgumentException(AT_LEAST_ONE_CHAR + length); this.length = length; return this; } /** * Specify the method used to generate random numbers used for password generation. * @param randomUIntGenerator an implementation of the {@link RandomUIntGenerator RandomUIntGenerator} * interface * @return this factory * @see RandomUIntGenerator * @see DefaultUIntGenerator */ public Factory setRandomUIntGenerator(RandomUIntGenerator randomUIntGenerator) { this.randomUIntGenerator = randomUIntGenerator; return this; } /** * Create a PasswordMaker according to the specified criteria. * @return a new PasswordMaker matching the specified criteria * @throws IllegalStateException if no character group has been specified * @throws IllegalStateException if password generation would be impossible because the minimum count * requirements on character groups would exceed the maximum length allowed for the password * @throws IllegalStateException if password generation would be impossible because too many restrictions * are placed on the maximum character count of each character group so that the specified password length * could not be reached. */ public PasswordMaker create() { if (charGroups().isEmpty()) throw new IllegalStateException(NO_MAKER_CHAR_SET_PROVIDED); if (sumOfRequiredCharactersIsGreaterThanPasswordLength()) throw new IllegalStateException(TOO_MANY_CHAR_BY_TYPE_FOR_LENGTH); if (passwordTooLongForPerCharTypeCountRestrictions()) throw new IllegalStateException(TOO_MANY_RESTRICTIONS_ON_CHAR_BY_TYPE_FOR_LENGTH1 + length + TOO_MANY_RESTRICTIONS_ON_CHAR_BY_TYPE_FOR_LENGTH2); return new PasswordMaker(length, charGroupsCopy(), groupMinCountsCopy(), groupMaxCountsCopy(), randomUIntGenerator); } private boolean sumOfRequiredCharactersIsGreaterThanPasswordLength() { int sum = groupMaxCounts().stream().reduce(0, Integer::sum); return sum > length; } private boolean passwordTooLongForPerCharTypeCountRestrictions() { int sum = 0; for (int maxCount: groupMaxCounts()) { if (maxCount == 0) return false; sum += maxCount; } return sum < length; } @Override Factory getThis() { return this; } // !! The 4 methods below are only overloaded for documentation purpose !! /** * Add a group of allowed characters in the composition of the password. * @param charGroup a String containing all characters allowed in this group * @return this factory * @throws IllegalArgumentException if the character group contains duplicates or if the character group * contains characters already present in other character groups, unless duplicates have been explicitly * allowed by calling disallowDuplicateCharacters(false) * @see #disallowDuplicateCharacters(boolean) */ @Override public Factory addCharGroup(String charGroup) { return super.addCharGroup(charGroup); } /** * Add a group of allowed characters in the composition of the password and specifies a minimum character * count. * @param charGroup a String containing all characters allowed in this group * @param minCount minimum number of characters from this group that must be present in the password * @return this factory * @throws IllegalArgumentException if the character group contains duplicates or if the character group * contains characters already present in other character groups, unless duplicates have been explicitly * allowed by calling disallowDuplicateCharacters(false) * @throws IllegalArgumentException if minCount < 0 * @see #disallowDuplicateCharacters(boolean) */ @Override public Factory addCharGroup(String charGroup, int minCount) { return super.addCharGroup(charGroup, minCount, 0); } /** * Add a group of allowed characters in the composition of the password and specifies a minimum and a maximum * character count. * @param charGroup a String containing all characters allowed in this group * @param minCount minimum number of characters from this group that must be present in the password * @param maxCount maximum number of characters from this group allowed in the password; a value of * 0 (zero) means "unlimited" (same as calling * {@link #addCharGroup(String, int) addCharGroup(String, int)}) * @return this factory * @throws IllegalArgumentException if the character group contains duplicates or if the character group * contains characters already present in other character groups, unless duplicates have been explicitly * allowed by calling disallowDuplicateCharacters(false) * @throws IllegalArgumentException if minCount < 0, or maxCount < 0, or * maxCount < minCount (unless maxCount == 0) * @see #disallowDuplicateCharacters(boolean) */ public Factory addCharGroup(String charGroup, int minCount, int maxCount) { return super.addCharGroup(charGroup, minCount, maxCount); } /** * Disallow or allow duplicate character groups and between character groups. Allowing duplicate is * usually unnecessary and error-prone. * @param disallowDuplicateCharacters true if duplicates character should be disallowed (default), * false otherwise * @return this factory */ @Override public Factory disallowDuplicateCharacters(boolean disallowDuplicateCharacters) { return super.disallowDuplicateCharacters(disallowDuplicateCharacters); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy