Enforce password complexity in lockscreen setting

Enforce a lock screen that adheres with the required complexity set by
the admin.

This is done by querying the DevicePolicyManager for the complexity set
for the given user, and merging it with the complexity from the "change
lock screen" intent (if any).

If the admin sets a higher complexity requirement than the app
triggering the lock screen change request, then the admin-set complexity
is enforced and the user is not shown information about the requesting
app.

Bug: 165573442
Test: Manually, set complexity using TestDPC and see it applies.
Test: m RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.password.ChooseLockGenericTest
Test: m RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.password.ChooseLockPasswordTest
Change-Id: If3f24f7430bdcbcd34265339f7d2a1ff82a44fc1
This commit is contained in:
Eran Messeri
2020-11-13 15:35:46 +00:00
parent 61464f21a5
commit d8e49b45b1
5 changed files with 147 additions and 9 deletions

View File

@@ -21,6 +21,7 @@ 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_COMPLEX;
import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.KEY_LOCK_SETTINGS_FOOTER;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
@@ -90,6 +91,7 @@ public class ChooseLockGenericTest {
Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
ShadowStorageManager.reset();
ShadowPersistentDataBlockManager.reset();
ShadowLockPatternUtils.reset();
}
@Test
@@ -377,6 +379,64 @@ public class ChooseLockGenericTest {
ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL)).isNotNull();
}
@Test
public void updatePreferencesOrFinish_ComplexityIsReadFromDPM() {
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
initActivity(null);
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
assertThat(footer.getTitle()).isEqualTo(null);
Intent intent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
assertThat(intent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
}
@Test
public void updatePreferencesOrFinish_ComplexityIsMergedWithDPM() {
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
Intent intent = new Intent()
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
initActivity(intent);
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
// Footer should be null because admin complexity wins.
FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
assertThat(footer.getTitle()).isEqualTo(null);
Intent passwordIntent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
assertThat(passwordIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
}
@Test
public void updatePreferencesOrFinish_ComplexityIsMergedWithDPM_AppIsHigher() {
ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
Intent intent = new Intent()
.putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
initActivity(intent);
mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
// Footer should include app name because app requirement is higher.
CharSequence expectedTitle =
mActivity.getString(R.string.unlock_footer_high_complexity_requested, "app name");
FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
assertThat(footer.getTitle()).isEqualTo(expectedTitle);
Intent passwordIntent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
assertThat(passwordIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
}
private void initActivity(@Nullable Intent intent) {
if (intent == null) {
intent = new Intent();

View File

@@ -31,6 +31,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY;
import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -67,6 +68,7 @@ import org.robolectric.shadows.ShadowDrawable;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
SettingsShadowResources.class,
ShadowLockPatternUtils.class,
ShadowUtils.class,
ShadowDevicePolicyManager.class,
})
@@ -84,6 +86,7 @@ public class ChooseLockPasswordTest {
@After
public void tearDown() {
SettingsShadowResources.reset();
ShadowLockPatternUtils.reset();
}
@Test
@@ -378,6 +381,29 @@ public class ChooseLockPasswordTest {
assertThat(drawable.getCreatedFromResId()).isNotEqualTo(R.drawable.ic_fingerprint_header);
}
@Test
public void validateComplexityMergedFromDpmOnCreate() {
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
assertPasswordValidationResult(
/* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
/* passwordType= */ PASSWORD_QUALITY_NUMERIC,
/* userEnteredPassword= */ LockscreenCredential.createNone(),
"PIN must be at least 8 digits");
}
@Test
public void validateComplexityMergedFromUnificationUserOnCreate() {
ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
ShadowLockPatternUtils.setRequiredPasswordComplexity(123, PASSWORD_COMPLEXITY_HIGH);
Intent intent = createIntentForPasswordValidation(PASSWORD_COMPLEXITY_NONE,
PASSWORD_QUALITY_NUMERIC);
intent.putExtra(EXTRA_KEY_UNIFICATION_PROFILE_ID, 123);
assertPasswordValidationResultForIntent(LockscreenCredential.createNone(), intent,
"PIN must be at least 8 digits");
}
private ChooseLockPassword buildChooseLockPasswordActivity(Intent intent) {
return Robolectric.buildActivity(ChooseLockPassword.class, intent).setup().get();
}
@@ -400,14 +426,27 @@ public class ChooseLockPasswordTest {
private void assertPasswordValidationResult(@PasswordComplexity int minComplexity,
int passwordType, LockscreenCredential userEnteredPassword,
String... expectedValidationResult) {
Intent intent = new Intent();
intent.putExtra(CONFIRM_CREDENTIALS, false);
intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
Intent intent = createIntentForPasswordValidation(minComplexity, passwordType);
assertPasswordValidationResultForIntent(userEnteredPassword, intent,
expectedValidationResult);
}
private void assertPasswordValidationResultForIntent(LockscreenCredential userEnteredPassword,
Intent intent, String... expectedValidationResult) {
ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
fragment.validatePassword(userEnteredPassword);
String[] messages = fragment.convertErrorCodeToMessages();
assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
assertThat(messages).asList().containsExactly(expectedValidationResult);
}
private Intent createIntentForPasswordValidation(
@PasswordComplexity int minComplexity,
int passwordType) {
Intent intent = new Intent();
intent.putExtra(CONFIRM_CREDENTIALS, false);
intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
return intent;
}
}