Merge "New extra and helper methods to set screenlock to a specific complexity level"

This commit is contained in:
TreeHugger Robot
2019-01-23 13:59:21 +00:00
committed by Android (Google) Code Review
4 changed files with 343 additions and 58 deletions

View File

@@ -6823,6 +6823,7 @@ package android.app.admin {
field public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
field public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
field public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
field @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY";
field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";

View File

@@ -1362,16 +1362,23 @@ public class DevicePolicyManager {
public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
/**
* Activity action: have the user enter a new password. This activity should
* be launched after using {@link #setPasswordQuality(ComponentName, int)},
* or {@link #setPasswordMinimumLength(ComponentName, int)} to have the user
* enter a new password that meets the current requirements. You can use
* {@link #isActivePasswordSufficient()} to determine whether you need to
* have the user select a new password in order to meet the current
* constraints. Upon being resumed from this activity, you can check the new
* Activity action: have the user enter a new password.
*
* <p>For admin apps, this activity should be launched after using {@link
* #setPasswordQuality(ComponentName, int)}, or {@link
* #setPasswordMinimumLength(ComponentName, int)} to have the user enter a new password that
* meets the current requirements. You can use {@link #isActivePasswordSufficient()} to
* determine whether you need to have the user select a new password in order to meet the
* current constraints. Upon being resumed from this activity, you can check the new
* password characteristics to see if they are sufficient.
*
* If the intent is launched from within a managed profile with a profile
* <p>Non-admin apps can use {@link #getPasswordComplexity()} to check the current screen lock
* complexity, and use this activity with extra {@link #EXTRA_PASSWORD_COMPLEXITY} to suggest
* to users how complex the app wants the new screen lock to be. Note that both {@link
* #getPasswordComplexity()} and the extra {@link #EXTRA_PASSWORD_COMPLEXITY} require the
* calling app to have the permission {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}.
*
* <p>If the intent is launched from within a managed profile with a profile
* owner built against {@link android.os.Build.VERSION_CODES#M} or before,
* this will trigger entering a new password for the parent of the profile.
* For all other cases it will trigger entering a new password for the user
@@ -1383,6 +1390,24 @@ public class DevicePolicyManager {
public static final String ACTION_SET_NEW_PASSWORD
= "android.app.action.SET_NEW_PASSWORD";
/**
* An integer indicating the complexity level of the new password an app would like the user to
* set when launching the action {@link #ACTION_SET_NEW_PASSWORD}.
*
* <p>Must be one of
* <ul>
* <li>{@link #PASSWORD_COMPLEXITY_HIGH}
* <li>{@link #PASSWORD_COMPLEXITY_MEDIUM}
* <li>{@link #PASSWORD_COMPLEXITY_LOW}
* <li>{@link #PASSWORD_COMPLEXITY_NONE}
* </ul>
*
* <p>If an invalid value is used, it will be treated as {@link #PASSWORD_COMPLEXITY_NONE}.
*/
@RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY)
public static final String EXTRA_PASSWORD_COMPLEXITY =
"android.app.extra.PASSWORD_COMPLEXITY";
/**
* Constant for {@link #getPasswordComplexity()}: no password.
*

View File

@@ -20,6 +20,11 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -27,6 +32,8 @@ import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -85,6 +92,101 @@ public class PasswordMetrics implements Parcelable {
nonLetter = in.readInt();
}
/** Returns the min quality allowed by {@code complexityLevel}. */
public static int complexityLevelToMinQuality(@PasswordComplexity int complexityLevel) {
// this would be the quality of the first metrics since mMetrics is sorted in ascending
// order of quality
return PasswordComplexityBucket
.complexityLevelToBucket(complexityLevel).mMetrics[0].quality;
}
/**
* Returns a merged minimum {@link PasswordMetrics} requirements that a new password must meet
* to fulfil {@code requestedQuality}, {@code requiresNumeric} and {@code
* requiresLettersOrSymbols}, which are derived from {@link DevicePolicyManager} requirements,
* and {@code complexityLevel}.
*
* <p>Note that we are taking {@code userEnteredPasswordQuality} into account because there are
* more than one set of metrics to meet the minimum complexity requirement and inspecting what
* the user has entered can help determine whether the alphabetic or alphanumeric set of metrics
* should be used. For example, suppose minimum complexity requires either ALPHABETIC(8+), or
* ALPHANUMERIC(6+). If the user has entered "a", the length requirement displayed on the UI
* would be 8. Then the user appends "1" to make it "a1". We now know the user is entering
* an alphanumeric password so we would update the min complexity required min length to 6.
*/
public static PasswordMetrics getMinimumMetrics(@PasswordComplexity int complexityLevel,
int userEnteredPasswordQuality, int requestedQuality, boolean requiresNumeric,
boolean requiresLettersOrSymbols) {
int targetQuality = Math.max(
userEnteredPasswordQuality,
getActualRequiredQuality(
requestedQuality, requiresNumeric, requiresLettersOrSymbols));
return getTargetQualityMetrics(complexityLevel, targetQuality);
}
/**
* Returns the {@link PasswordMetrics} at {@code complexityLevel} which the metrics quality
* is the same as {@code targetQuality}.
*
* <p>If {@code complexityLevel} does not allow {@code targetQuality}, returns the metrics
* with the min quality at {@code complexityLevel}.
*/
// TODO(bernardchau): update tests to test getMinimumMetrics and change this to be private
@VisibleForTesting
public static PasswordMetrics getTargetQualityMetrics(
@PasswordComplexity int complexityLevel, int targetQuality) {
PasswordComplexityBucket targetBucket =
PasswordComplexityBucket.complexityLevelToBucket(complexityLevel);
for (PasswordMetrics metrics : targetBucket.mMetrics) {
if (targetQuality == metrics.quality) {
return metrics;
}
}
// none of the metrics at complexityLevel has targetQuality, return metrics with min quality
// see test case testGetMinimumMetrics_actualRequiredQualityStricter for an example, where
// min complexity allows at least NUMERIC_COMPLEX, user has not entered anything yet, and
// requested quality is NUMERIC
return targetBucket.mMetrics[0];
}
/**
* Finds out the actual quality requirement based on whether quality is {@link
* DevicePolicyManager#PASSWORD_QUALITY_COMPLEX} and whether digits, letters or symbols are
* required.
*/
@VisibleForTesting
// TODO(bernardchau): update tests to test getMinimumMetrics and change this to be private
public static int getActualRequiredQuality(
int requestedQuality, boolean requiresNumeric, boolean requiresLettersOrSymbols) {
if (requestedQuality != PASSWORD_QUALITY_COMPLEX) {
return requestedQuality;
}
// find out actual password quality from complex requirements
if (requiresNumeric && requiresLettersOrSymbols) {
return PASSWORD_QUALITY_ALPHANUMERIC;
}
if (requiresLettersOrSymbols) {
return PASSWORD_QUALITY_ALPHABETIC;
}
if (requiresNumeric) {
// cannot specify numeric complex using complex quality so this must be numeric
return PASSWORD_QUALITY_NUMERIC;
}
// reaching here means dpm sets quality to complex without specifying any requirements
return PASSWORD_QUALITY_UNSPECIFIED;
}
/**
* Returns {@code complexityLevel} or {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}
* if {@code complexityLevel} is not valid.
*/
@PasswordComplexity
public static int sanitizeComplexityLevel(@PasswordComplexity int complexityLevel) {
return PasswordComplexityBucket.complexityLevelToBucket(complexityLevel).mComplexityLevel;
}
public boolean isDefault() {
return quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
&& length == 0 && letters == 0 && upperCase == 0 && lowerCase == 0
@@ -280,7 +382,7 @@ public class PasswordMetrics implements Parcelable {
@PasswordComplexity
public int determineComplexity() {
for (PasswordComplexityBucket bucket : PasswordComplexityBucket.BUCKETS) {
if (satisfiesBucket(bucket.getMetrics())) {
if (satisfiesBucket(bucket.mMetrics)) {
return bucket.mComplexityLevel;
}
}
@@ -290,7 +392,7 @@ public class PasswordMetrics implements Parcelable {
/**
* Requirements in terms of {@link PasswordMetrics} for each {@link PasswordComplexity}.
*/
public static class PasswordComplexityBucket {
private static class PasswordComplexityBucket {
/**
* Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_HIGH} in terms of
* {@link PasswordMetrics}.
@@ -299,12 +401,13 @@ public class PasswordMetrics implements Parcelable {
new PasswordComplexityBucket(
PASSWORD_COMPLEXITY_HIGH,
new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 6),
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
8),
new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 6),
new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
8));
DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */
6));
/**
* Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_MEDIUM} in terms of
@@ -314,11 +417,12 @@ public class PasswordMetrics implements Parcelable {
new PasswordComplexityBucket(
PASSWORD_COMPLEXITY_MEDIUM,
new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 4),
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
4),
new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 4),
new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */
4));
/**
@@ -328,11 +432,11 @@ public class PasswordMetrics implements Parcelable {
private static final PasswordComplexityBucket LOW =
new PasswordComplexityBucket(
PASSWORD_COMPLEXITY_LOW,
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING));
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC));
/**
* A special bucket to represent {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}.
@@ -348,19 +452,27 @@ public class PasswordMetrics implements Parcelable {
private final int mComplexityLevel;
private final PasswordMetrics[] mMetrics;
/**
* @param metricsArray must be sorted in ascending order of {@link #quality}.
*/
private PasswordComplexityBucket(@PasswordComplexity int complexityLevel,
PasswordMetrics... metrics) {
this.mComplexityLevel = complexityLevel;
this.mMetrics = metrics;
}
PasswordMetrics... metricsArray) {
int previousQuality = PASSWORD_QUALITY_UNSPECIFIED;
for (PasswordMetrics metrics : metricsArray) {
if (metrics.quality < previousQuality) {
throw new IllegalArgumentException("metricsArray must be sorted in ascending"
+ " order of quality");
}
previousQuality = metrics.quality;
}
this.mMetrics = metricsArray;
this.mComplexityLevel = complexityLevel;
/** Returns the {@link PasswordMetrics} that meet the min requirements of this bucket. */
public PasswordMetrics[] getMetrics() {
return mMetrics;
}
/** Returns the bucket that {@code complexityLevel} represents. */
public static PasswordComplexityBucket complexityLevelToBucket(
private static PasswordComplexityBucket complexityLevelToBucket(
@PasswordComplexity int complexityLevel) {
for (PasswordComplexityBucket bucket : BUCKETS) {
if (bucket.mComplexityLevel == complexityLevel) {

View File

@@ -20,12 +20,23 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static android.app.admin.PasswordMetrics.complexityLevelToMinQuality;
import static android.app.admin.PasswordMetrics.getActualRequiredQuality;
import static android.app.admin.PasswordMetrics.getMinimumMetrics;
import static android.app.admin.PasswordMetrics.getTargetQualityMetrics;
import static android.app.admin.PasswordMetrics.sanitizeComplexityLevel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.app.admin.PasswordMetrics.PasswordComplexityBucket;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
@@ -109,7 +120,7 @@ public class PasswordMetricsTest {
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
PasswordMetrics.computeForPassword("1").quality);
// contains a long sequence so isn't complex
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
assertEquals(PASSWORD_QUALITY_NUMERIC,
PasswordMetrics.computeForPassword("1234").quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
PasswordMetrics.computeForPassword("").quality);
@@ -145,7 +156,7 @@ public class PasswordMetricsTest {
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 5));
assertNotEquals(new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 4),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, 4));
new PasswordMetrics(PASSWORD_QUALITY_COMPLEX, 4));
metrics0 = PasswordMetrics.computeForPassword("1234abcd,./");
metrics1 = PasswordMetrics.computeForPassword("1234abcd,./");
@@ -176,9 +187,9 @@ public class PasswordMetricsTest {
@Test
public void testConstructQuality() {
PasswordMetrics expected = new PasswordMetrics();
expected.quality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
expected.quality = PASSWORD_QUALITY_COMPLEX;
PasswordMetrics actual = new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
PasswordMetrics actual = new PasswordMetrics(PASSWORD_QUALITY_COMPLEX);
assertEquals(expected, actual);
}
@@ -256,42 +267,178 @@ public class PasswordMetricsTest {
}
@Test
public void testComplexityLevelToBucket_none() {
PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
PASSWORD_COMPLEXITY_NONE).getMetrics();
public void testSanitizeComplexityLevel_none() {
assertEquals(PASSWORD_COMPLEXITY_NONE, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_NONE));
for (PasswordMetrics metrics : bucket) {
assertEquals(PASSWORD_COMPLEXITY_NONE, metrics.determineComplexity());
}
}
@Test
public void testComplexityLevelToBucket_low() {
PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
PASSWORD_COMPLEXITY_LOW).getMetrics();
for (PasswordMetrics metrics : bucket) {
assertEquals(PASSWORD_COMPLEXITY_LOW, metrics.determineComplexity());
}
public void testSanitizeComplexityLevel_low() {
assertEquals(PASSWORD_COMPLEXITY_LOW, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_LOW));
}
@Test
public void testComplexityLevelToBucket_medium() {
PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
PASSWORD_COMPLEXITY_MEDIUM).getMetrics();
for (PasswordMetrics metrics : bucket) {
assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metrics.determineComplexity());
}
public void testSanitizeComplexityLevel_medium() {
assertEquals(
PASSWORD_COMPLEXITY_MEDIUM, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_MEDIUM));
}
@Test
public void testComplexityLevelToBucket_high() {
PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
PASSWORD_COMPLEXITY_HIGH).getMetrics();
public void testSanitizeComplexityLevel_high() {
assertEquals(PASSWORD_COMPLEXITY_HIGH, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_HIGH));
}
for (PasswordMetrics metrics : bucket) {
assertEquals(PASSWORD_COMPLEXITY_HIGH, metrics.determineComplexity());
}
@Test
public void testSanitizeComplexityLevel_invalid() {
assertEquals(PASSWORD_COMPLEXITY_NONE, sanitizeComplexityLevel(-1));
}
@Test
public void testComplexityLevelToMinQuality_none() {
assertEquals(PASSWORD_QUALITY_UNSPECIFIED,
complexityLevelToMinQuality(PASSWORD_COMPLEXITY_NONE));
}
@Test
public void testComplexityLevelToMinQuality_low() {
assertEquals(PASSWORD_QUALITY_SOMETHING,
complexityLevelToMinQuality(PASSWORD_COMPLEXITY_LOW));
}
@Test
public void testComplexityLevelToMinQuality_medium() {
assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX,
complexityLevelToMinQuality(PASSWORD_COMPLEXITY_MEDIUM));
}
@Test
public void testComplexityLevelToMinQuality_high() {
assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX,
complexityLevelToMinQuality(PASSWORD_COMPLEXITY_HIGH));
}
@Test
public void testComplexityLevelToMinQuality_invalid() {
assertEquals(PASSWORD_QUALITY_UNSPECIFIED, complexityLevelToMinQuality(-1));
}
@Test
public void testGetTargetQualityMetrics_noneComplexityReturnsDefaultMetrics() {
PasswordMetrics metrics =
getTargetQualityMetrics(PASSWORD_COMPLEXITY_NONE, PASSWORD_QUALITY_ALPHANUMERIC);
assertTrue(metrics.isDefault());
}
@Test
public void testGetTargetQualityMetrics_qualityNotAllowedReturnsMinQualityMetrics() {
PasswordMetrics metrics =
getTargetQualityMetrics(PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_NUMERIC);
assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
assertEquals(/* expected= */ 4, metrics.length);
}
@Test
public void testGetTargetQualityMetrics_highComplexityNumericComplex() {
PasswordMetrics metrics = getTargetQualityMetrics(
PASSWORD_COMPLEXITY_HIGH, PASSWORD_QUALITY_NUMERIC_COMPLEX);
assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
assertEquals(/* expected= */ 8, metrics.length);
}
@Test
public void testGetTargetQualityMetrics_mediumComplexityAlphabetic() {
PasswordMetrics metrics = getTargetQualityMetrics(
PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_ALPHABETIC);
assertEquals(PASSWORD_QUALITY_ALPHABETIC, metrics.quality);
assertEquals(/* expected= */ 4, metrics.length);
}
@Test
public void testGetTargetQualityMetrics_lowComplexityAlphanumeric() {
PasswordMetrics metrics = getTargetQualityMetrics(
PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_ALPHANUMERIC);
assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, metrics.quality);
assertEquals(/* expected= */ 4, metrics.length);
}
@Test
public void testGetActualRequiredQuality_nonComplex() {
int actual = getActualRequiredQuality(
PASSWORD_QUALITY_NUMERIC_COMPLEX,
/* requiresNumeric= */ false,
/* requiresLettersOrSymbols= */ false);
assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, actual);
}
@Test
public void testGetActualRequiredQuality_complexRequiresNone() {
int actual = getActualRequiredQuality(
PASSWORD_QUALITY_COMPLEX,
/* requiresNumeric= */ false,
/* requiresLettersOrSymbols= */ false);
assertEquals(PASSWORD_QUALITY_UNSPECIFIED, actual);
}
@Test
public void testGetActualRequiredQuality_complexRequiresNumeric() {
int actual = getActualRequiredQuality(
PASSWORD_QUALITY_COMPLEX,
/* requiresNumeric= */ true,
/* requiresLettersOrSymbols= */ false);
assertEquals(PASSWORD_QUALITY_NUMERIC, actual);
}
@Test
public void testGetActualRequiredQuality_complexRequiresLetters() {
int actual = getActualRequiredQuality(
PASSWORD_QUALITY_COMPLEX,
/* requiresNumeric= */ false,
/* requiresLettersOrSymbols= */ true);
assertEquals(PASSWORD_QUALITY_ALPHABETIC, actual);
}
@Test
public void testGetActualRequiredQuality_complexRequiresNumericAndLetters() {
int actual = getActualRequiredQuality(
PASSWORD_QUALITY_COMPLEX,
/* requiresNumeric= */ true,
/* requiresLettersOrSymbols= */ true);
assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, actual);
}
@Test
public void testGetMinimumMetrics_userInputStricter() {
PasswordMetrics metrics = getMinimumMetrics(
PASSWORD_COMPLEXITY_HIGH,
PASSWORD_QUALITY_ALPHANUMERIC,
PASSWORD_QUALITY_NUMERIC,
/* requiresNumeric= */ false,
/* requiresLettersOrSymbols= */ false);
assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, metrics.quality);
assertEquals(/* expected= */ 6, metrics.length);
}
@Test
public void testGetMinimumMetrics_actualRequiredQualityStricter() {
PasswordMetrics metrics = getMinimumMetrics(
PASSWORD_COMPLEXITY_HIGH,
PASSWORD_QUALITY_UNSPECIFIED,
PASSWORD_QUALITY_NUMERIC,
/* requiresNumeric= */ false,
/* requiresLettersOrSymbols= */ false);
assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
assertEquals(/* expected= */ 8, metrics.length);
}
}