Skip to content

Commit

Permalink
Add Builder to PasswordGenerator and add unit tests for PasswordGener…
Browse files Browse the repository at this point in the history
…ator
  • Loading branch information
davejbax committed Apr 28, 2018
1 parent ea7e43e commit 738cf7e
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 23 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ dependencies {
implementation 'org.bouncycastle:bcprov-jdk15on:1.59'
// implementation 'de.mkammerer:argon2-jvm:2.4'

// SQLJet for SQLite DB
// JDBC for SQLite DB
implementation 'org.sqldroid:sqldroid:1.0.3'

testImplementation 'junit:junit:4.12'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ public class PasswordGenerator {
*/
public PasswordGenerator(int minLength, int maxLength, boolean symbols, boolean numbers,
boolean upper, boolean lower) {
// Validation: ensure our parameters are sensible
if (minLength > maxLength)
throw new IllegalArgumentException("Min length cannot be > max length");
else if (minLength == 0)
throw new IllegalArgumentException("Min length must be > 0");
else if (maxLength == 0)
throw new IllegalArgumentException("Max length must be > 0");
else if (!symbols && !numbers && !upper && !lower)
throw new IllegalArgumentException("Need at least one charset inclusion");

this.minLength = minLength;
this.maxLength = maxLength;

Expand Down Expand Up @@ -66,4 +76,85 @@ public String generate() {
return builder.toString();
}

/**
* A utility class to build PasswordGenerator instances. This will initialize with working
* default settings, which can be re-set in a builder-like fashion.
*/
public static class Builder {

private int minLength = 16;
private int maxLength = 16;
private boolean upper = true;
private boolean lower = true;
private boolean symbols = true;
private boolean numbers = true;

/** Creates a new Builder with the default settings */
public Builder() {
}

/**
* Sets the min length of the password generator that will be built on {@link #create()}
* @see PasswordGenerator#PasswordGenerator(int, int, boolean, boolean, boolean, boolean)
*/
public Builder minLength(int minLength) {
this.minLength = minLength;
return this;
}

/**
* Sets the max length of the password generator that will be built on {@link #create()}
* @see PasswordGenerator#PasswordGenerator(int, int, boolean, boolean, boolean, boolean)
*/
public Builder maxLength(int maxLength) {
this.maxLength = maxLength;
return this;
}

/**
* Sets the upper boolean of the password generator that will be built on {@link #create()}
* @see PasswordGenerator#PasswordGenerator(int, int, boolean, boolean, boolean, boolean)
*/
public Builder upper(boolean upper) {
this.upper = upper;
return this;
}

/**
* Sets the lower boolean of the password generator that will be built on {@link #create()}
* @see PasswordGenerator#PasswordGenerator(int, int, boolean, boolean, boolean, boolean)
*/
public Builder lower(boolean lower) {
this.lower = lower;
return this;
}

/**
* Sets the symbols boolean of the password generator that will be built on {@link #create()}
* @see PasswordGenerator#PasswordGenerator(int, int, boolean, boolean, boolean, boolean)
*/
public Builder symbols(boolean symbols) {
this.symbols = symbols;
return this;
}

/**
* Sets the numbers boolean of the password generator that will be built on {@link #create()}
* @see PasswordGenerator#PasswordGenerator(int, int, boolean, boolean, boolean, boolean)
*/
public Builder numbers(boolean numbers) {
this.numbers = numbers;
return this;
}

/**
* Creates the PasswordGenerator
* @return New {@link PasswordGenerator} instance
*/
public PasswordGenerator create() {
return new PasswordGenerator(minLength, maxLength, symbols, numbers, upper, lower);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,22 @@ public PasswordGenerator getPasswordGenerator() {
public void reloadFromSettings() {
// Load settings from shared preferences
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
int minLength = prefs.getInt(StorageConstants.PREFS_GEN_MIN_LEN,
StorageConstants.PREFS_GEN_MIN_LEN_DEF);
int maxLength = prefs.getInt(StorageConstants.PREFS_GEN_MAX_LEN,
StorageConstants.PREFS_GEN_MAX_LEN_DEF);
boolean upper = prefs.getBoolean(StorageConstants.PREFS_GEN_UPPER,
StorageConstants.PREFS_GEN_UPPER_DEF);
boolean lower = prefs.getBoolean(StorageConstants.PREFS_GEN_LOWER,
StorageConstants.PREFS_GEN_LOWER_DEF);
boolean symbols = prefs.getBoolean(StorageConstants.PREFS_GEN_SYMBOLS,
StorageConstants.PREFS_GEN_SYMBOLS_DEF);
boolean numbers = prefs.getBoolean(StorageConstants.PREFS_GEN_NUMBERS,
StorageConstants.PREFS_GEN_NUMBERS_DEF);

// Construct a new password generator
this.passwordGenerator = new PasswordGenerator(minLength, maxLength, symbols, numbers,
upper, lower);

// Construct a new password generator, retrieving the parameters from shared preferences
this.passwordGenerator = new PasswordGenerator.Builder()
.minLength(prefs.getInt(StorageConstants.PREFS_GEN_MIN_LEN,
StorageConstants.PREFS_GEN_MIN_LEN_DEF))
.maxLength(prefs.getInt(StorageConstants.PREFS_GEN_MAX_LEN,
StorageConstants.PREFS_GEN_MAX_LEN_DEF))
.upper(prefs.getBoolean(StorageConstants.PREFS_GEN_UPPER,
StorageConstants.PREFS_GEN_UPPER_DEF))
.lower(prefs.getBoolean(StorageConstants.PREFS_GEN_LOWER,
StorageConstants.PREFS_GEN_LOWER_DEF))
.symbols(prefs.getBoolean(StorageConstants.PREFS_GEN_SYMBOLS,
StorageConstants.PREFS_GEN_SYMBOLS_DEF))
.numbers(prefs.getBoolean(StorageConstants.PREFS_GEN_NUMBERS,
StorageConstants.PREFS_GEN_NUMBERS_DEF))
.create();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ private int getIntBE(byte[] bytes, int offset) {
.getInt();
}

private void printBytes(byte[] fourBytes) {
for (byte b : fourBytes)
System.out.printf("%02x", b);
System.out.println();
}

@Test
public void generates_SequentialPart() {
HybridIvFactory factory = new HybridIvFactory();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package uk.co.davidbaxter.letmepass;

import org.junit.Test;

import uk.co.davidbaxter.letmepass.security.PasswordGenerator;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;

public class PasswordGeneratorTest {

@Test
public void validates_AllParamsFalse() {
try {
PasswordGenerator gen = new PasswordGenerator.Builder()
.lower(false)
.upper(false)
.symbols(false)
.numbers(false)
.create();
fail("Password generator should not allow construction with no valid chars");
} catch (Exception e) {}
}

@Test
public void validates_MinGreaterThanMax() {
try {
PasswordGenerator gen = new PasswordGenerator.Builder()
.minLength(10)
.maxLength(1)
.create();
fail("Password generator should not allow construction with minimum > maximum length");
} catch (Exception e) {}
}

@Test
public void validates_MaxZero() {
try {
PasswordGenerator gen = new PasswordGenerator.Builder()
.minLength(0)
.maxLength(0)
.create();
fail("Password generator should not allow construction with maximum length of 0");
} catch (Exception e) {}
}

@Test
public void generates_AllSettings() {
PasswordGenerator.Builder builder = new PasswordGenerator.Builder();

// Loop through all possible combinations of upper, lower, numbers, and symbols flags
// (i.e. from b0001 to b1111; exclude 0 because we need at least 1 flag set)
for (int i = 1; i < 0x10; i++) {
boolean upper = ((i & 0x1) != 0);
boolean lower = ((i & 0x2) != 0);
boolean numbers = ((i & 0x4) != 0);
boolean symbols = ((i & 0x8) != 0);

// Create password generator for this configuration
PasswordGenerator gen = builder.upper(upper)
.lower(lower)
.numbers(numbers)
.symbols(symbols)
.create();

// Ensure that across 100 generations, we do not violate our stated parameters
for (int j = 0; j < 100; j++) {
String s = gen.generate();

// We cannot -guarantee- that a charset will be used, since generation is random.
// However, we can guarantee that it won't be used.
if (!upper) assertFalse(s.matches("[A-Z]"));
if (!lower) assertFalse(s.matches("[a-z]"));
if (!numbers) assertFalse(s.matches("[0-9]"));
if (!symbols) assertFalse(s.matches("[^a-zA-Z0-9]"));
}
}
}

}

0 comments on commit 738cf7e

Please sign in to comment.