diff options
| author | 2019-01-23 13:59:21 +0000 | |
|---|---|---|
| committer | 2019-01-23 13:59:21 +0000 | |
| commit | 2288bdc9abcc29f0a35efd55beccb945d877b1c1 (patch) | |
| tree | cd7af5e8f41a44d01ca7c0e0f6a0d3e84c99238f | |
| parent | 448fb122a1fc31699c709b4d5d0e37f76ca92b3f (diff) | |
| parent | 60e0f7f6d83457bb8211b4695e6812884e732b84 (diff) | |
Merge "New extra and helper methods to set screenlock to a specific complexity level"
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/app/admin/DevicePolicyManager.java | 41 | ||||
| -rw-r--r-- | core/java/android/app/admin/PasswordMetrics.java | 148 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java | 205 |
4 files changed, 340 insertions, 55 deletions
diff --git a/api/current.txt b/api/current.txt index 442be31569e8..944d7425bae0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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"; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 7cc953cc254f..b0a08edfcaaf 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -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 @@ -1384,6 +1391,24 @@ public class DevicePolicyManager { = "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. * * <p>Note that these complexity constants are ordered so that higher values are more complex. diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index 8b41755f6dec..e5df2c70eeec 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -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) { + 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; - this.mMetrics = metrics; - } - /** 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) { diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java index 8d42c74be7b0..5731daa0b2a9 100644 --- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java @@ -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(); + public void testSanitizeComplexityLevel_low() { + assertEquals(PASSWORD_COMPLEXITY_LOW, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_LOW)); + } - for (PasswordMetrics metrics : bucket) { - assertEquals(PASSWORD_COMPLEXITY_LOW, metrics.determineComplexity()); - } + @Test + public void testSanitizeComplexityLevel_medium() { + assertEquals( + PASSWORD_COMPLEXITY_MEDIUM, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_MEDIUM)); } @Test - public void testComplexityLevelToBucket_medium() { - PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( - PASSWORD_COMPLEXITY_MEDIUM).getMetrics(); + public void testSanitizeComplexityLevel_high() { + assertEquals(PASSWORD_COMPLEXITY_HIGH, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_HIGH)); + } - for (PasswordMetrics metrics : bucket) { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metrics.determineComplexity()); - } + @Test + public void testSanitizeComplexityLevel_invalid() { + assertEquals(PASSWORD_COMPLEXITY_NONE, sanitizeComplexityLevel(-1)); } @Test - public void testComplexityLevelToBucket_high() { - PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket( - PASSWORD_COMPLEXITY_HIGH).getMetrics(); + public void testComplexityLevelToMinQuality_none() { + assertEquals(PASSWORD_QUALITY_UNSPECIFIED, + complexityLevelToMinQuality(PASSWORD_COMPLEXITY_NONE)); + } - for (PasswordMetrics metrics : bucket) { - assertEquals(PASSWORD_COMPLEXITY_HIGH, metrics.determineComplexity()); - } + @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); } } |