Implement support for new security policies
* Minimum complex characters * Password history (i.e. disallow re-use of past n passwords) * Password expiration * Password expiration is NOT yet supported in the framework; there is a TODO in this CL and a trivial change will be needed when support arrives; for now, we report this as unsupported * The two implemented policies are testable Change-Id: I477adbc000577c57d1ab1788378c97a60018c10c
This commit is contained in:
parent
fa0e6adb72
commit
9b4988de43
|
@ -76,7 +76,7 @@ public class Account {
|
|||
int mSyncWindow;
|
||||
int mBackupFlags; // for account backups only
|
||||
String mProtocolVersion; // for account backups only
|
||||
int mSecurityFlags; // for account backups only
|
||||
long mSecurityFlags; // for account backups only
|
||||
String mSignature; // for account backups only
|
||||
|
||||
/**
|
||||
|
@ -177,7 +177,7 @@ public class Account {
|
|||
mBackupFlags = preferences.mSharedPreferences.getInt(mUuid + KEY_BACKUP_FLAGS, 0);
|
||||
mProtocolVersion = preferences.mSharedPreferences.getString(mUuid + KEY_PROTOCOL_VERSION,
|
||||
null);
|
||||
mSecurityFlags = preferences.mSharedPreferences.getInt(mUuid + KEY_SECURITY_FLAGS, 0);
|
||||
mSecurityFlags = preferences.mSharedPreferences.getLong(mUuid + KEY_SECURITY_FLAGS, 0);
|
||||
mSignature = preferences.mSharedPreferences.getString(mUuid + KEY_SIGNATURE, null);
|
||||
}
|
||||
|
||||
|
@ -353,7 +353,7 @@ public class Account {
|
|||
editor.putInt(mUuid + KEY_SYNC_WINDOW, mSyncWindow);
|
||||
editor.putInt(mUuid + KEY_BACKUP_FLAGS, mBackupFlags);
|
||||
editor.putString(mUuid + KEY_PROTOCOL_VERSION, mProtocolVersion);
|
||||
editor.putInt(mUuid + KEY_SECURITY_FLAGS, mSecurityFlags);
|
||||
editor.putLong(mUuid + KEY_SECURITY_FLAGS, mSecurityFlags);
|
||||
editor.putString(mUuid + KEY_SIGNATURE, mSignature);
|
||||
|
||||
// The following fields are *not* written because they need to be more fine-grained
|
||||
|
|
|
@ -51,7 +51,7 @@ public class SecurityPolicy {
|
|||
private PolicySet mAggregatePolicy;
|
||||
|
||||
/* package */ static final PolicySet NO_POLICY_SET =
|
||||
new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false);
|
||||
new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0);
|
||||
|
||||
/**
|
||||
* This projection on Account is for scanning/reading
|
||||
|
@ -110,11 +110,14 @@ public class SecurityPolicy {
|
|||
* max password fails take the min
|
||||
* max screen lock time take the min
|
||||
* require remote wipe take the max (logical or)
|
||||
* password history take the max (strongest mode)
|
||||
* password expiration take the max (strongest mode)
|
||||
* password complex chars take the max (strongest mode)
|
||||
*
|
||||
* @return a policy representing the strongest aggregate. If no policy sets are defined,
|
||||
* a lightweight "nothing required" policy will be returned. Never null.
|
||||
*/
|
||||
/* package */ PolicySet computeAggregatePolicy() {
|
||||
/*package*/ PolicySet computeAggregatePolicy() {
|
||||
boolean policiesFound = false;
|
||||
|
||||
int minPasswordLength = Integer.MIN_VALUE;
|
||||
|
@ -122,12 +125,15 @@ public class SecurityPolicy {
|
|||
int maxPasswordFails = Integer.MAX_VALUE;
|
||||
int maxScreenLockTime = Integer.MAX_VALUE;
|
||||
boolean requireRemoteWipe = false;
|
||||
int passwordHistory = Integer.MIN_VALUE;
|
||||
int passwordExpiration = Integer.MIN_VALUE;
|
||||
int passwordComplexChars = Integer.MIN_VALUE;
|
||||
|
||||
Cursor c = mContext.getContentResolver().query(Account.CONTENT_URI,
|
||||
ACCOUNT_SECURITY_PROJECTION, WHERE_ACCOUNT_SECURITY_NONZERO, null, null);
|
||||
try {
|
||||
while (c.moveToNext()) {
|
||||
int flags = c.getInt(ACCOUNT_SECURITY_COLUMN_FLAGS);
|
||||
long flags = c.getLong(ACCOUNT_SECURITY_COLUMN_FLAGS);
|
||||
if (flags != 0) {
|
||||
PolicySet p = new PolicySet(flags);
|
||||
minPasswordLength = Math.max(p.mMinPasswordLength, minPasswordLength);
|
||||
|
@ -138,6 +144,16 @@ public class SecurityPolicy {
|
|||
if (p.mMaxScreenLockTime > 0) {
|
||||
maxScreenLockTime = Math.min(p.mMaxScreenLockTime, maxScreenLockTime);
|
||||
}
|
||||
if (p.mPasswordHistory > 0) {
|
||||
passwordHistory = Math.max(p.mPasswordHistory, passwordHistory);
|
||||
}
|
||||
if (p.mPasswordExpiration > 0) {
|
||||
passwordExpiration = Math.max(p.mPasswordExpiration, passwordExpiration);
|
||||
}
|
||||
if (p.mPasswordComplexChars > 0) {
|
||||
passwordComplexChars = Math.max(p.mPasswordComplexChars,
|
||||
passwordComplexChars);
|
||||
}
|
||||
requireRemoteWipe |= p.mRequireRemoteWipe;
|
||||
policiesFound = true;
|
||||
}
|
||||
|
@ -151,9 +167,13 @@ public class SecurityPolicy {
|
|||
if (passwordMode == Integer.MIN_VALUE) passwordMode = 0;
|
||||
if (maxPasswordFails == Integer.MAX_VALUE) maxPasswordFails = 0;
|
||||
if (maxScreenLockTime == Integer.MAX_VALUE) maxScreenLockTime = 0;
|
||||
if (passwordHistory == Integer.MIN_VALUE) passwordHistory = 0;
|
||||
if (passwordExpiration == Integer.MIN_VALUE) passwordExpiration = 0;
|
||||
if (passwordComplexChars == Integer.MIN_VALUE) passwordComplexChars = 0;
|
||||
|
||||
return new PolicySet(minPasswordLength, passwordMode, maxPasswordFails,
|
||||
maxScreenLockTime, requireRemoteWipe);
|
||||
maxScreenLockTime, requireRemoteWipe, passwordExpiration, passwordHistory,
|
||||
passwordComplexChars);
|
||||
} else {
|
||||
return NO_POLICY_SET;
|
||||
}
|
||||
|
@ -242,6 +262,19 @@ public class SecurityPolicy {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (policies.mPasswordExpiration > 0) {
|
||||
// TODO Complete when DPM supports this
|
||||
}
|
||||
if (policies.mPasswordHistory > 0) {
|
||||
if (dpm.getPasswordHistoryLength(mAdminName) < policies.mPasswordHistory) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (policies.mPasswordComplexChars > 0) {
|
||||
if (dpm.getPasswordMinimumNonLetter(mAdminName) < policies.mPasswordComplexChars) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// password failures are counted locally - no test required here
|
||||
// no check required for remote wipe (it's supported, if we're the admin)
|
||||
|
||||
|
@ -273,6 +306,12 @@ public class SecurityPolicy {
|
|||
dpm.setMaximumTimeToLock(mAdminName, policies.mMaxScreenLockTime * 1000);
|
||||
// local wipe (failed passwords limit)
|
||||
dpm.setMaximumFailedPasswordsForWipe(mAdminName, policies.mMaxPasswordFails);
|
||||
// password expiration (days until a password expires)
|
||||
// TODO set this when DPM allows it
|
||||
// password history length (number of previous passwords that may not be reused)
|
||||
dpm.setPasswordHistoryLength(mAdminName, policies.mPasswordHistory);
|
||||
// password minimum complex characters
|
||||
dpm.setPasswordMinimumNonLetter(mAdminName, policies.mPasswordComplexChars);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,12 +454,27 @@ public class SecurityPolicy {
|
|||
public static final int SCREEN_LOCK_TIME_MAX = 2047;
|
||||
// bit 25: remote wipe capability required
|
||||
private static final int REQUIRE_REMOTE_WIPE = 1 << 25;
|
||||
// bit 26..35: password expiration (days; 0=not required)
|
||||
private static final int PASSWORD_EXPIRATION_SHIFT = 26;
|
||||
private static final long PASSWORD_EXPIRATION_MASK = 1023L << PASSWORD_EXPIRATION_SHIFT;
|
||||
public static final int PASSWORD_EXPIRATION_MAX = 1023;
|
||||
// bit 35..42: password history (length; 0=not required)
|
||||
private static final int PASSWORD_HISTORY_SHIFT = 36;
|
||||
private static final long PASSWORD_HISTORY_MASK = 255L << PASSWORD_HISTORY_SHIFT;
|
||||
public static final int PASSWORD_HISTORY_MAX = 255;
|
||||
// bit 42..46: min complex characters (0=not required)
|
||||
private static final int PASSWORD_COMPLEX_CHARS_SHIFT = 44;
|
||||
private static final long PASSWORD_COMPLEX_CHARS_MASK = 31L << PASSWORD_COMPLEX_CHARS_SHIFT;
|
||||
public static final int PASSWORD_COMPLEX_CHARS_MAX = 31;
|
||||
|
||||
/*package*/ final int mMinPasswordLength;
|
||||
/*package*/ final int mPasswordMode;
|
||||
/*package*/ final int mMaxPasswordFails;
|
||||
/*package*/ final int mMaxScreenLockTime;
|
||||
/*package*/ final boolean mRequireRemoteWipe;
|
||||
/*package*/ final int mPasswordExpiration;
|
||||
/*package*/ final int mPasswordHistory;
|
||||
/*package*/ final int mPasswordComplexChars;
|
||||
|
||||
public int getMinPasswordLength() {
|
||||
return mMinPasswordLength;
|
||||
|
@ -452,18 +506,29 @@ public class SecurityPolicy {
|
|||
* @throws IllegalArgumentException for illegal arguments.
|
||||
*/
|
||||
public PolicySet(int minPasswordLength, int passwordMode, int maxPasswordFails,
|
||||
int maxScreenLockTime, boolean requireRemoteWipe) throws IllegalArgumentException {
|
||||
// Check against hard limits
|
||||
// EAS doesn't generate values outside these limits anyway
|
||||
int maxScreenLockTime, boolean requireRemoteWipe, int passwordExpiration,
|
||||
int passwordHistory, int passwordComplexChars) throws IllegalArgumentException {
|
||||
// This value has a hard limit which cannot be supported if exceeded. Setting the
|
||||
// exceeded value will force isSupported() to return false.
|
||||
if (minPasswordLength > PASSWORD_LENGTH_MAX) {
|
||||
throw new IllegalArgumentException("password length");
|
||||
}
|
||||
if (passwordMode < PASSWORD_MODE_NONE || passwordMode > PASSWORD_MODE_STRONG) {
|
||||
if ((passwordMode != PASSWORD_MODE_NONE) && (passwordMode != PASSWORD_MODE_SIMPLE) &&
|
||||
(passwordMode != PASSWORD_MODE_STRONG)) {
|
||||
throw new IllegalArgumentException("password mode");
|
||||
}
|
||||
if (maxScreenLockTime > SCREEN_LOCK_TIME_MAX) {
|
||||
throw new IllegalArgumentException("screen lock time");
|
||||
}
|
||||
if (passwordExpiration > PASSWORD_EXPIRATION_MAX) {
|
||||
throw new IllegalArgumentException("password expiration");
|
||||
}
|
||||
if (passwordHistory > PASSWORD_HISTORY_MAX) {
|
||||
throw new IllegalArgumentException("password history");
|
||||
}
|
||||
if (passwordComplexChars > PASSWORD_COMPLEX_CHARS_MAX) {
|
||||
throw new IllegalArgumentException("complex chars");
|
||||
}
|
||||
// This value can be reduced (which actually increases security) if necessary
|
||||
if (maxPasswordFails > PASSWORD_MAX_FAILS_MAX) {
|
||||
maxPasswordFails = PASSWORD_MAX_FAILS_MAX;
|
||||
|
@ -472,12 +537,14 @@ public class SecurityPolicy {
|
|||
if (maxScreenLockTime > SCREEN_LOCK_TIME_MAX) {
|
||||
maxScreenLockTime = SCREEN_LOCK_TIME_MAX;
|
||||
}
|
||||
|
||||
mMinPasswordLength = minPasswordLength;
|
||||
mPasswordMode = passwordMode;
|
||||
mMaxPasswordFails = maxPasswordFails;
|
||||
mMaxScreenLockTime = maxScreenLockTime;
|
||||
mRequireRemoteWipe = requireRemoteWipe;
|
||||
mPasswordExpiration = passwordExpiration;
|
||||
mPasswordHistory = passwordHistory;
|
||||
mPasswordComplexChars = passwordComplexChars;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -491,16 +558,22 @@ public class SecurityPolicy {
|
|||
/**
|
||||
* Create from values encoded in an account flags int
|
||||
*/
|
||||
public PolicySet(int flags) {
|
||||
public PolicySet(long flags) {
|
||||
mMinPasswordLength =
|
||||
(flags & PASSWORD_LENGTH_MASK) >> PASSWORD_LENGTH_SHIFT;
|
||||
(int) ((flags & PASSWORD_LENGTH_MASK) >> PASSWORD_LENGTH_SHIFT);
|
||||
mPasswordMode =
|
||||
(flags & PASSWORD_MODE_MASK);
|
||||
(int) (flags & PASSWORD_MODE_MASK);
|
||||
mMaxPasswordFails =
|
||||
(flags & PASSWORD_MAX_FAILS_MASK) >> PASSWORD_MAX_FAILS_SHIFT;
|
||||
(int) ((flags & PASSWORD_MAX_FAILS_MASK) >> PASSWORD_MAX_FAILS_SHIFT);
|
||||
mMaxScreenLockTime =
|
||||
(flags & SCREEN_LOCK_TIME_MASK) >> SCREEN_LOCK_TIME_SHIFT;
|
||||
(int) ((flags & SCREEN_LOCK_TIME_MASK) >> SCREEN_LOCK_TIME_SHIFT);
|
||||
mRequireRemoteWipe = 0 != (flags & REQUIRE_REMOTE_WIPE);
|
||||
mPasswordExpiration =
|
||||
(int) ((flags & PASSWORD_EXPIRATION_MASK) >> PASSWORD_EXPIRATION_SHIFT);
|
||||
mPasswordHistory =
|
||||
(int) ((flags & PASSWORD_HISTORY_MASK) >> PASSWORD_HISTORY_SHIFT);
|
||||
mPasswordComplexChars =
|
||||
(int) ((flags & PASSWORD_COMPLEX_CHARS_MASK) >> PASSWORD_COMPLEX_CHARS_SHIFT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -531,7 +604,7 @@ public class SecurityPolicy {
|
|||
*/
|
||||
public boolean writeAccount(Account account, String syncKey, boolean update,
|
||||
Context context) {
|
||||
int newFlags = hashCode();
|
||||
long newFlags = getSecurityCode();
|
||||
boolean dirty = (newFlags != account.mSecurityFlags);
|
||||
account.mSecurityFlags = newFlags;
|
||||
account.mSecuritySyncKey = syncKey;
|
||||
|
@ -548,32 +621,33 @@ public class SecurityPolicy {
|
|||
return dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof PolicySet) {
|
||||
PolicySet other = (PolicySet)o;
|
||||
return (this.mMinPasswordLength == other.mMinPasswordLength)
|
||||
&& (this.mPasswordMode == other.mPasswordMode)
|
||||
&& (this.mMaxPasswordFails == other.mMaxPasswordFails)
|
||||
&& (this.mMaxScreenLockTime == other.mMaxScreenLockTime)
|
||||
&& (this.mRequireRemoteWipe == other.mRequireRemoteWipe);
|
||||
return (this.getSecurityCode() == other.getSecurityCode());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: the hash code is defined as the encoding used in Account
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int flags = 0;
|
||||
flags = mMinPasswordLength << PASSWORD_LENGTH_SHIFT;
|
||||
long code = getSecurityCode();
|
||||
return (int) code;
|
||||
}
|
||||
|
||||
public long getSecurityCode() {
|
||||
long flags = 0;
|
||||
flags = (long)mMinPasswordLength << PASSWORD_LENGTH_SHIFT;
|
||||
flags |= mPasswordMode;
|
||||
flags |= mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT;
|
||||
flags |= mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT;
|
||||
flags |= (long)mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT;
|
||||
flags |= (long)mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT;
|
||||
if (mRequireRemoteWipe) {
|
||||
flags |= REQUIRE_REMOTE_WIPE;
|
||||
}
|
||||
flags |= (long)mPasswordHistory << PASSWORD_HISTORY_SHIFT;
|
||||
flags |= (long)mPasswordExpiration << PASSWORD_EXPIRATION_SHIFT;
|
||||
flags |= (long)mPasswordComplexChars << PASSWORD_COMPLEX_CHARS_SHIFT;
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
@ -581,7 +655,10 @@ public class SecurityPolicy {
|
|||
public String toString() {
|
||||
return "{ " + "pw-len-min=" + mMinPasswordLength + " pw-mode=" + mPasswordMode
|
||||
+ " pw-fails-max=" + mMaxPasswordFails + " screenlock-max="
|
||||
+ mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe + "}";
|
||||
+ mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe
|
||||
+ " pw-expiration=" + mPasswordExpiration
|
||||
+ " pw-history=" + mPasswordHistory
|
||||
+ " pw-complex-chars=" + mPasswordComplexChars + "}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -860,7 +860,7 @@ public abstract class EmailContent {
|
|||
public String mRingtoneUri;
|
||||
public String mProtocolVersion;
|
||||
public int mNewMessageCount;
|
||||
public int mSecurityFlags;
|
||||
public long mSecurityFlags;
|
||||
public String mSecuritySyncKey;
|
||||
public String mSignature;
|
||||
|
||||
|
@ -988,7 +988,7 @@ public abstract class EmailContent {
|
|||
mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN);
|
||||
mProtocolVersion = cursor.getString(CONTENT_PROTOCOL_VERSION_COLUMN);
|
||||
mNewMessageCount = cursor.getInt(CONTENT_NEW_MESSAGE_COUNT_COLUMN);
|
||||
mSecurityFlags = cursor.getInt(CONTENT_SECURITY_FLAGS_COLUMN);
|
||||
mSecurityFlags = cursor.getLong(CONTENT_SECURITY_FLAGS_COLUMN);
|
||||
mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN);
|
||||
mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN);
|
||||
return this;
|
||||
|
@ -1549,7 +1549,7 @@ public abstract class EmailContent {
|
|||
dest.writeString(mRingtoneUri);
|
||||
dest.writeString(mProtocolVersion);
|
||||
dest.writeInt(mNewMessageCount);
|
||||
dest.writeInt(mSecurityFlags);
|
||||
dest.writeLong(mSecurityFlags);
|
||||
dest.writeString(mSecuritySyncKey);
|
||||
dest.writeString(mSignature);
|
||||
|
||||
|
@ -1588,7 +1588,7 @@ public abstract class EmailContent {
|
|||
mRingtoneUri = in.readString();
|
||||
mProtocolVersion = in.readString();
|
||||
mNewMessageCount = in.readInt();
|
||||
mSecurityFlags = in.readInt();
|
||||
mSecurityFlags = in.readLong();
|
||||
mSecuritySyncKey = in.readString();
|
||||
mSignature = in.readString();
|
||||
|
||||
|
|
|
@ -65,6 +65,10 @@ public class ProvisionParser extends Parser {
|
|||
int passwordMode = PolicySet.PASSWORD_MODE_NONE;
|
||||
int maxPasswordFails = 0;
|
||||
int maxScreenLockTime = 0;
|
||||
int passwordExpiration = 0;
|
||||
int passwordHistory = 0;
|
||||
int passwordComplexChars = 0;
|
||||
boolean canSupport = true;
|
||||
|
||||
while (nextTag(Tags.PROVISION_EAS_PROVISION_DOC) != END) {
|
||||
boolean supported = true;
|
||||
|
@ -91,6 +95,16 @@ public class ProvisionParser extends Parser {
|
|||
case Tags.PROVISION_MAX_DEVICE_PASSWORD_FAILED_ATTEMPTS:
|
||||
maxPasswordFails = getValueInt();
|
||||
break;
|
||||
case Tags.PROVISION_DEVICE_PASSWORD_EXPIRATION:
|
||||
passwordExpiration = getValueInt();
|
||||
// We don't yet support this
|
||||
if (passwordExpiration > 0) {
|
||||
supported = false;
|
||||
}
|
||||
break;
|
||||
case Tags.PROVISION_DEVICE_PASSWORD_HISTORY:
|
||||
passwordHistory = getValueInt();
|
||||
break;
|
||||
case Tags.PROVISION_ALLOW_SIMPLE_DEVICE_PASSWORD:
|
||||
// Ignore this unless there's any MSFT documentation for what this means
|
||||
// Hint: I haven't seen any that's more specific than "simple"
|
||||
|
@ -123,8 +137,6 @@ public class ProvisionParser extends Parser {
|
|||
// The following policies, if true, can't be supported at the moment
|
||||
case Tags.PROVISION_DEVICE_ENCRYPTION_ENABLED:
|
||||
case Tags.PROVISION_PASSWORD_RECOVERY_ENABLED:
|
||||
case Tags.PROVISION_DEVICE_PASSWORD_EXPIRATION:
|
||||
case Tags.PROVISION_DEVICE_PASSWORD_HISTORY:
|
||||
case Tags.PROVISION_REQUIRE_DEVICE_ENCRYPTION:
|
||||
case Tags.PROVISION_REQUIRE_SIGNED_SMIME_MESSAGES:
|
||||
case Tags.PROVISION_REQUIRE_ENCRYPTED_SMIME_MESSAGES:
|
||||
|
@ -143,7 +155,9 @@ public class ProvisionParser extends Parser {
|
|||
break;
|
||||
// Complex character setting is only used if we're in "strong" (alphanumeric) mode
|
||||
case Tags.PROVISION_MIN_DEVICE_PASSWORD_COMPLEX_CHARS:
|
||||
if ((passwordMode == PolicySet.PASSWORD_MODE_STRONG) && (getValueInt() > 0)) {
|
||||
passwordComplexChars = getValueInt();
|
||||
if ((passwordMode == PolicySet.PASSWORD_MODE_STRONG) &&
|
||||
(passwordComplexChars > 0)) {
|
||||
supported = false;
|
||||
}
|
||||
break;
|
||||
|
@ -190,7 +204,8 @@ public class ProvisionParser extends Parser {
|
|||
}
|
||||
|
||||
mPolicySet = new SecurityPolicy.PolicySet(minPasswordLength, passwordMode,
|
||||
maxPasswordFails, maxScreenLockTime, true);
|
||||
maxPasswordFails, maxScreenLockTime, true, passwordExpiration, passwordHistory,
|
||||
passwordComplexChars);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -219,6 +234,9 @@ public class ProvisionParser extends Parser {
|
|||
int mPasswordMode = PolicySet.PASSWORD_MODE_NONE;
|
||||
int mMaxPasswordFails = 0;
|
||||
int mMaxScreenLockTime = 0;
|
||||
int mPasswordExpiration = 0;
|
||||
int mPasswordHistory = 0;
|
||||
int mPasswordComplexChars = 0;
|
||||
}
|
||||
|
||||
/*package*/ void parseProvisionDocXml(String doc) throws IOException {
|
||||
|
@ -243,7 +261,8 @@ public class ProvisionParser extends Parser {
|
|||
}
|
||||
|
||||
mPolicySet = new PolicySet(sps.mMinPasswordLength, sps.mPasswordMode, sps.mMaxPasswordFails,
|
||||
sps.mMaxScreenLockTime, true);
|
||||
sps.mMaxScreenLockTime, true, sps.mPasswordExpiration, sps.mPasswordHistory,
|
||||
sps.mPasswordComplexChars);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,14 +33,17 @@ import android.test.suitebuilder.annotation.SmallTest;
|
|||
|
||||
/**
|
||||
* This is a series of unit tests for backup/restore of the SecurityPolicy class.
|
||||
* You can run this entire test case with:
|
||||
* runtest -c com.android.email.SecurityPolicyTests email
|
||||
*/
|
||||
|
||||
@MediumTest
|
||||
public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
||||
|
||||
private Context mMockContext;
|
||||
|
||||
private static final PolicySet EMPTY_POLICY_SET =
|
||||
new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false);
|
||||
new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0);
|
||||
|
||||
public SecurityPolicyTests() {
|
||||
super(EmailProvider.class, EmailProvider.EMAIL_AUTHORITY);
|
||||
|
@ -99,18 +102,18 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
// We know that EMPTY_POLICY_SET doesn't generate an Exception or we wouldn't be here
|
||||
// Try some illegal parameters
|
||||
try {
|
||||
new PolicySet(100, PolicySet.PASSWORD_MODE_NONE, 0, 0, false);
|
||||
new PolicySet(100, PolicySet.PASSWORD_MODE_NONE, 0, 0, false, 0, 0, 0);
|
||||
fail("Too-long password allowed");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG + 1, 0, 0, false);
|
||||
new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG + 1, 0, 0, false, 0, 0, 0);
|
||||
fail("Illegal password mode allowed");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
new PolicySet(0, PolicySet.PASSWORD_MODE_NONE, 0,
|
||||
PolicySet.SCREEN_LOCK_TIME_MAX + 1, false);
|
||||
PolicySet.SCREEN_LOCK_TIME_MAX + 1, false, 0, 0, 0);
|
||||
fail("Too-long screen lock time allowed");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
|
@ -123,7 +126,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
SecurityPolicy sp = getSecurityPolicy();
|
||||
|
||||
// with no accounts, should return empty set
|
||||
assertTrue(EMPTY_POLICY_SET.equals(sp.computeAggregatePolicy()));
|
||||
assertEquals(EMPTY_POLICY_SET, sp.computeAggregatePolicy());
|
||||
|
||||
// with accounts having no security, empty set
|
||||
Account a1 = ProviderTestUtils.setupAccount("no-sec-1", false, mMockContext);
|
||||
|
@ -132,19 +135,19 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Account a2 = ProviderTestUtils.setupAccount("no-sec-2", false, mMockContext);
|
||||
a2.mSecurityFlags = 0;
|
||||
a2.save(mMockContext);
|
||||
assertTrue(EMPTY_POLICY_SET.equals(sp.computeAggregatePolicy()));
|
||||
assertEquals(EMPTY_POLICY_SET, sp.computeAggregatePolicy());
|
||||
|
||||
// with a single account in security mode, should return same security as in account
|
||||
// first test with partially-populated policies
|
||||
Account a3 = ProviderTestUtils.setupAccount("sec-3", false, mMockContext);
|
||||
PolicySet p3ain = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false);
|
||||
PolicySet p3ain = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0);
|
||||
p3ain.writeAccount(a3, null, true, mMockContext);
|
||||
PolicySet p3aout = sp.computeAggregatePolicy();
|
||||
assertNotNull(p3aout);
|
||||
assertEquals(p3ain, p3aout);
|
||||
|
||||
// Repeat that test with fully-populated policies
|
||||
PolicySet p3bin = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 15, 16, false);
|
||||
PolicySet p3bin = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 15, 16, false, 1, 2, 3);
|
||||
p3bin.writeAccount(a3, null, true, mMockContext);
|
||||
PolicySet p3bout = sp.computeAggregatePolicy();
|
||||
assertNotNull(p3bout);
|
||||
|
@ -154,7 +157,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
// pw length and pw mode - max logic - will change because larger #s here
|
||||
// fail count and lock timer - min logic - will *not* change because larger #s here
|
||||
// wipe required - OR logic - will *not* change here because false
|
||||
PolicySet p4in = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false);
|
||||
PolicySet p4in = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 0, 5, 7);
|
||||
Account a4 = ProviderTestUtils.setupAccount("sec-4", false, mMockContext);
|
||||
p4in.writeAccount(a4, null, true, mMockContext);
|
||||
PolicySet p4out = sp.computeAggregatePolicy();
|
||||
|
@ -163,13 +166,17 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
assertEquals(PolicySet.PASSWORD_MODE_STRONG, p4out.mPasswordMode);
|
||||
assertEquals(15, p4out.mMaxPasswordFails);
|
||||
assertEquals(16, p4out.mMaxScreenLockTime);
|
||||
assertEquals(1, p4out.mPasswordExpiration);
|
||||
assertEquals(5, p4out.mPasswordHistory);
|
||||
assertEquals(7, p4out.mPasswordComplexChars);
|
||||
assertFalse(p4out.mRequireRemoteWipe);
|
||||
|
||||
// add another account which mixes it up (the remaining fields will change)
|
||||
// pw length and pw mode - max logic - will *not* change because smaller #s here
|
||||
// fail count and lock timer - min logic - will change because smaller #s here
|
||||
// password exp will change (max logic), but history and complex chars will be as before
|
||||
// wipe required - OR logic - will change here because true
|
||||
PolicySet p5in = new PolicySet(4, PolicySet.PASSWORD_MODE_NONE, 5, 6, true);
|
||||
PolicySet p5in = new PolicySet(4, PolicySet.PASSWORD_MODE_NONE, 5, 6, true, 6, 0, 0);
|
||||
Account a5 = ProviderTestUtils.setupAccount("sec-5", false, mMockContext);
|
||||
p5in.writeAccount(a5, null, true, mMockContext);
|
||||
PolicySet p5out = sp.computeAggregatePolicy();
|
||||
|
@ -178,6 +185,9 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
assertEquals(PolicySet.PASSWORD_MODE_STRONG, p5out.mPasswordMode);
|
||||
assertEquals(5, p5out.mMaxPasswordFails);
|
||||
assertEquals(6, p5out.mMaxScreenLockTime);
|
||||
assertEquals(6, p5out.mPasswordExpiration);
|
||||
assertEquals(5, p4out.mPasswordHistory);
|
||||
assertEquals(7, p4out.mPasswordComplexChars);
|
||||
assertTrue(p5out.mRequireRemoteWipe);
|
||||
}
|
||||
|
||||
|
@ -197,7 +207,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
Account a2 = ProviderTestUtils.setupAccount("no-sec-2", false, mMockContext);
|
||||
a2.mSecurityFlags = 0;
|
||||
a2.save(mMockContext);
|
||||
assertTrue(EMPTY_POLICY_SET.equals(sp.computeAggregatePolicy()));
|
||||
assertEquals(EMPTY_POLICY_SET, sp.computeAggregatePolicy());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -206,40 +216,85 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
*/
|
||||
@SmallTest
|
||||
public void testFieldIsolation() {
|
||||
PolicySet p = new PolicySet(PolicySet.PASSWORD_LENGTH_MAX, 0, 0, 0, false);
|
||||
PolicySet p = new PolicySet(PolicySet.PASSWORD_LENGTH_MAX, 0, 0, 0, false, 0, 0 ,0);
|
||||
assertEquals(PolicySet.PASSWORD_LENGTH_MAX, p.mMinPasswordLength);
|
||||
assertEquals(0, p.mPasswordMode);
|
||||
assertEquals(0, p.mMaxPasswordFails);
|
||||
assertEquals(0, p.mMaxScreenLockTime);
|
||||
assertEquals(0, p.mPasswordExpiration);
|
||||
assertEquals(0, p.mPasswordHistory);
|
||||
assertEquals(0, p.mPasswordComplexChars);
|
||||
assertFalse(p.mRequireRemoteWipe);
|
||||
|
||||
p = new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG, 0, 0, false);
|
||||
p = new PolicySet(0, PolicySet.PASSWORD_MODE_STRONG, 0, 0, false, 0, 0, 0);
|
||||
assertEquals(0, p.mMinPasswordLength);
|
||||
assertEquals(PolicySet.PASSWORD_MODE_STRONG, p.mPasswordMode);
|
||||
assertEquals(0, p.mMaxPasswordFails);
|
||||
assertEquals(0, p.mMaxScreenLockTime);
|
||||
assertEquals(0, p.mPasswordExpiration);
|
||||
assertEquals(0, p.mPasswordHistory);
|
||||
assertEquals(0, p.mPasswordComplexChars);
|
||||
assertFalse(p.mRequireRemoteWipe);
|
||||
|
||||
p = new PolicySet(0, 0, PolicySet.PASSWORD_MAX_FAILS_MAX, 0, false);
|
||||
p = new PolicySet(0, 0, PolicySet.PASSWORD_MAX_FAILS_MAX, 0, false, 0, 0, 0);
|
||||
assertEquals(0, p.mMinPasswordLength);
|
||||
assertEquals(0, p.mPasswordMode);
|
||||
assertEquals(PolicySet.PASSWORD_MAX_FAILS_MAX, p.mMaxPasswordFails);
|
||||
assertEquals(0, p.mMaxScreenLockTime);
|
||||
assertEquals(0, p.mPasswordExpiration);
|
||||
assertEquals(0, p.mPasswordHistory);
|
||||
assertEquals(0, p.mPasswordComplexChars);
|
||||
assertFalse(p.mRequireRemoteWipe);
|
||||
|
||||
p = new PolicySet(0, 0, 0, PolicySet.SCREEN_LOCK_TIME_MAX, false);
|
||||
p = new PolicySet(0, 0, 0, PolicySet.SCREEN_LOCK_TIME_MAX, false, 0, 0, 0);
|
||||
assertEquals(0, p.mMinPasswordLength);
|
||||
assertEquals(0, p.mPasswordMode);
|
||||
assertEquals(0, p.mMaxPasswordFails);
|
||||
assertEquals(PolicySet.SCREEN_LOCK_TIME_MAX, p.mMaxScreenLockTime);
|
||||
assertEquals(0, p.mPasswordExpiration);
|
||||
assertEquals(0, p.mPasswordHistory);
|
||||
assertEquals(0, p.mPasswordComplexChars);
|
||||
assertFalse(p.mRequireRemoteWipe);
|
||||
|
||||
p = new PolicySet(0, 0, 0, 0, true);
|
||||
p = new PolicySet(0, 0, 0, 0, true, 0, 0, 0);
|
||||
assertEquals(0, p.mMinPasswordLength);
|
||||
assertEquals(0, p.mPasswordMode);
|
||||
assertEquals(0, p.mMaxPasswordFails);
|
||||
assertEquals(0, p.mMaxScreenLockTime);
|
||||
assertEquals(0, p.mPasswordExpiration);
|
||||
assertEquals(0, p.mPasswordHistory);
|
||||
assertEquals(0, p.mPasswordComplexChars);
|
||||
assertTrue(p.mRequireRemoteWipe);
|
||||
|
||||
p = new PolicySet(0, 0, 0, 0, false, PolicySet.PASSWORD_EXPIRATION_MAX, 0, 0);
|
||||
assertEquals(0, p.mMinPasswordLength);
|
||||
assertEquals(0, p.mPasswordMode);
|
||||
assertEquals(0, p.mMaxPasswordFails);
|
||||
assertEquals(0, p.mMaxScreenLockTime);
|
||||
assertEquals(PolicySet.PASSWORD_EXPIRATION_MAX, p.mPasswordExpiration);
|
||||
assertEquals(0, p.mPasswordHistory);
|
||||
assertEquals(0, p.mPasswordComplexChars);
|
||||
assertFalse(p.mRequireRemoteWipe);
|
||||
|
||||
p = new PolicySet(0, 0, 0, 0, false, 0, PolicySet.PASSWORD_HISTORY_MAX, 0);
|
||||
assertEquals(0, p.mMinPasswordLength);
|
||||
assertEquals(0, p.mPasswordMode);
|
||||
assertEquals(0, p.mMaxPasswordFails);
|
||||
assertEquals(0, p.mMaxScreenLockTime);
|
||||
assertEquals(0, p.mPasswordExpiration);
|
||||
assertEquals(PolicySet.PASSWORD_HISTORY_MAX, p.mPasswordHistory);
|
||||
assertEquals(0, p.mPasswordComplexChars);
|
||||
assertFalse(p.mRequireRemoteWipe);
|
||||
|
||||
p = new PolicySet(0, 0, 0, 0, false, 0, 0, PolicySet.PASSWORD_COMPLEX_CHARS_MAX);
|
||||
assertEquals(0, p.mMinPasswordLength);
|
||||
assertEquals(0, p.mPasswordMode);
|
||||
assertEquals(0, p.mMaxPasswordFails);
|
||||
assertEquals(0, p.mMaxScreenLockTime);
|
||||
assertEquals(0, p.mPasswordExpiration);
|
||||
assertEquals(0, p.mPasswordHistory);
|
||||
assertEquals(PolicySet.PASSWORD_COMPLEX_CHARS_MAX, p.mPasswordComplexChars);
|
||||
assertFalse(p.mRequireRemoteWipe);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -247,7 +302,7 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
*/
|
||||
@SmallTest
|
||||
public void testAccountEncoding() {
|
||||
PolicySet p1 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true);
|
||||
PolicySet p1 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9);
|
||||
Account a = new Account();
|
||||
final String SYNC_KEY = "test_sync_key";
|
||||
p1.writeAccount(a, SYNC_KEY, false, null);
|
||||
|
@ -256,18 +311,16 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Test equality & hash. Note, the tests for inequality are poor, as each field should
|
||||
* Test equality. Note, the tests for inequality are poor, as each field should
|
||||
* be tested individually.
|
||||
*/
|
||||
@SmallTest
|
||||
public void testEqualsAndHash() {
|
||||
PolicySet p1 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true);
|
||||
PolicySet p2 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true);
|
||||
PolicySet p3 = new PolicySet(2, PolicySet.PASSWORD_MODE_SIMPLE, 5, 6, true);
|
||||
public void testEquals() {
|
||||
PolicySet p1 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9);
|
||||
PolicySet p2 = new PolicySet(1, PolicySet.PASSWORD_MODE_STRONG, 3, 4, true, 7, 8, 9);
|
||||
PolicySet p3 = new PolicySet(2, PolicySet.PASSWORD_MODE_SIMPLE, 5, 6, true, 7, 8, 9);
|
||||
assertTrue(p1.equals(p2));
|
||||
assertFalse(p2.equals(p3));
|
||||
assertTrue(p1.hashCode() == p2.hashCode());
|
||||
assertFalse(p2.hashCode() == p3.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,11 +381,11 @@ public class SecurityPolicyTests extends ProviderTestCase2<EmailProvider> {
|
|||
*/
|
||||
public void testDisableAdmin() {
|
||||
Account a1 = ProviderTestUtils.setupAccount("disable-1", false, mMockContext);
|
||||
PolicySet p1 = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false);
|
||||
PolicySet p1 = new PolicySet(10, PolicySet.PASSWORD_MODE_SIMPLE, 0, 0, false, 0, 0, 0);
|
||||
p1.writeAccount(a1, "sync-key-1", true, mMockContext);
|
||||
|
||||
Account a2 = ProviderTestUtils.setupAccount("disable-2", false, mMockContext);
|
||||
PolicySet p2 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false);
|
||||
PolicySet p2 = new PolicySet(20, PolicySet.PASSWORD_MODE_STRONG, 25, 26, false, 0, 0, 0);
|
||||
p2.writeAccount(a2, "sync-key-2", true, mMockContext);
|
||||
|
||||
Account a3 = ProviderTestUtils.setupAccount("disable-3", false, mMockContext);
|
||||
|
|
Loading…
Reference in New Issue