Support serializing package signatures for domain verification state
To prepare for backup/restore changes which need to verify that the package signatures match. Bug: 182207215 Test: atest DomainVerificationPersistenceTest Change-Id: I9e4bd24256604b649acf2c925cce2ac65331fa2c
This commit is contained in:
@@ -18,10 +18,12 @@ package com.android.server.pm.verify.domain;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.verify.domain.DomainVerificationState;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.PackageUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.TypedXmlSerializer;
|
||||
@@ -36,6 +38,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class DomainVerificationPersistence {
|
||||
|
||||
@@ -49,6 +52,7 @@ public class DomainVerificationPersistence {
|
||||
private static final String ATTR_PACKAGE_NAME = "packageName";
|
||||
private static final String ATTR_ID = "id";
|
||||
private static final String ATTR_HAS_AUTO_VERIFY_DOMAINS = "hasAutoVerifyDomains";
|
||||
private static final String ATTR_SIGNATURE = "signature";
|
||||
private static final String TAG_USER_STATES = "user-states";
|
||||
|
||||
public static final String TAG_USER_STATE = "user-state";
|
||||
@@ -62,10 +66,18 @@ public class DomainVerificationPersistence {
|
||||
public static final String ATTR_NAME = "name";
|
||||
public static final String ATTR_STATE = "state";
|
||||
|
||||
/**
|
||||
* @param pkgNameToSignature Converts package name to a string representation of its signature.
|
||||
* Usually this is the SHA-256 hash from
|
||||
* {@link PackageUtils#computeSignaturesSha256Digest(Signature[])},
|
||||
* but can be an arbitrary string for testing purposes. Pass non-null
|
||||
* to write out signatures, or null to ignore.
|
||||
*/
|
||||
public static void writeToXml(@NonNull TypedXmlSerializer xmlSerializer,
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> attached,
|
||||
@NonNull ArrayMap<String, DomainVerificationPkgState> pending,
|
||||
@NonNull ArrayMap<String, DomainVerificationPkgState> restored) throws IOException {
|
||||
@NonNull ArrayMap<String, DomainVerificationPkgState> restored,
|
||||
@Nullable Function<String, String> pkgNameToSignature) throws IOException {
|
||||
try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) {
|
||||
try (SettingsXml.WriteSection ignored = serializer.startSection(
|
||||
TAG_DOMAIN_VERIFICATIONS)) {
|
||||
@@ -86,25 +98,26 @@ public class DomainVerificationPersistence {
|
||||
}
|
||||
|
||||
try (SettingsXml.WriteSection activeSection = serializer.startSection(TAG_ACTIVE)) {
|
||||
writePackageStates(activeSection, active);
|
||||
writePackageStates(activeSection, active, pkgNameToSignature);
|
||||
}
|
||||
|
||||
try (SettingsXml.WriteSection restoredSection = serializer.startSection(
|
||||
TAG_RESTORED)) {
|
||||
writePackageStates(restoredSection, restored.values());
|
||||
writePackageStates(restoredSection, restored.values(), pkgNameToSignature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writePackageStates(@NonNull SettingsXml.WriteSection section,
|
||||
@NonNull Collection<DomainVerificationPkgState> states) throws IOException {
|
||||
@NonNull Collection<DomainVerificationPkgState> states,
|
||||
@Nullable Function<String, String> pkgNameToSignature) throws IOException {
|
||||
if (states.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (DomainVerificationPkgState state : states) {
|
||||
writePkgStateToXml(section, state);
|
||||
writePkgStateToXml(section, state, pkgNameToSignature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,11 +159,12 @@ public class DomainVerificationPersistence {
|
||||
* been entered.
|
||||
*/
|
||||
@Nullable
|
||||
public static DomainVerificationPkgState createPkgStateFromXml(
|
||||
private static DomainVerificationPkgState createPkgStateFromXml(
|
||||
@NonNull SettingsXml.ReadSection section) {
|
||||
String packageName = section.getString(ATTR_PACKAGE_NAME);
|
||||
String idString = section.getString(ATTR_ID);
|
||||
boolean hasAutoVerifyDomains = section.getBoolean(ATTR_HAS_AUTO_VERIFY_DOMAINS);
|
||||
String signature = section.getString(ATTR_SIGNATURE);
|
||||
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(idString)) {
|
||||
return null;
|
||||
}
|
||||
@@ -172,7 +186,7 @@ public class DomainVerificationPersistence {
|
||||
}
|
||||
|
||||
return new DomainVerificationPkgState(packageName, id, hasAutoVerifyDomains, stateMap,
|
||||
userStates);
|
||||
userStates, signature);
|
||||
}
|
||||
|
||||
private static void readUserStates(@NonNull SettingsXml.ReadSection section,
|
||||
@@ -196,14 +210,26 @@ public class DomainVerificationPersistence {
|
||||
}
|
||||
}
|
||||
|
||||
public static void writePkgStateToXml(@NonNull SettingsXml.WriteSection parentSection,
|
||||
@NonNull DomainVerificationPkgState pkgState) throws IOException {
|
||||
private static void writePkgStateToXml(@NonNull SettingsXml.WriteSection parentSection,
|
||||
@NonNull DomainVerificationPkgState pkgState,
|
||||
@Nullable Function<String, String> pkgNameToSignature) throws IOException {
|
||||
String packageName = pkgState.getPackageName();
|
||||
String signature = pkgNameToSignature == null
|
||||
? null : pkgNameToSignature.apply(packageName);
|
||||
if (signature == null) {
|
||||
// If a package isn't available to get its signature, fallback to the previously stored
|
||||
// result, which can occur if the package has been marked for restore but hasn't
|
||||
// been installed on the new device yet.
|
||||
signature = pkgState.getBackupSignatureHash();
|
||||
}
|
||||
|
||||
try (SettingsXml.WriteSection ignored =
|
||||
parentSection.startSection(TAG_PACKAGE_STATE)
|
||||
.attribute(ATTR_PACKAGE_NAME, pkgState.getPackageName())
|
||||
.attribute(ATTR_PACKAGE_NAME, packageName)
|
||||
.attribute(ATTR_ID, pkgState.getId().toString())
|
||||
.attribute(ATTR_HAS_AUTO_VERIFY_DOMAINS,
|
||||
pkgState.isHasAutoVerifyDomains())) {
|
||||
pkgState.isHasAutoVerifyDomains())
|
||||
.attribute(ATTR_SIGNATURE, signature)) {
|
||||
writeStateMap(parentSection, pkgState.getStateMap());
|
||||
writeUserStates(parentSection, pkgState.getUserStates());
|
||||
}
|
||||
@@ -245,7 +271,7 @@ public class DomainVerificationPersistence {
|
||||
* entered.
|
||||
*/
|
||||
@Nullable
|
||||
public static DomainVerificationInternalUserState createUserStateFromXml(
|
||||
private static DomainVerificationInternalUserState createUserStateFromXml(
|
||||
@NonNull SettingsXml.ReadSection section) {
|
||||
int userId = section.getInt(ATTR_USER_ID);
|
||||
if (userId == -1) {
|
||||
@@ -274,7 +300,7 @@ public class DomainVerificationPersistence {
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection,
|
||||
private static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection,
|
||||
@NonNull DomainVerificationInternalUserState userState) throws IOException {
|
||||
try (SettingsXml.WriteSection section =
|
||||
parentSection.startSection(TAG_USER_STATE)
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.PackageUtils;
|
||||
import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
@@ -842,7 +843,8 @@ public class DomainVerificationService extends SystemService
|
||||
+ ", new pkg = " + newPkg, new Exception());
|
||||
|
||||
DomainVerificationPkgState newPkgState = new DomainVerificationPkgState(
|
||||
pkgName, newDomainSetId, true, newStateMap, newUserStates);
|
||||
pkgName, newDomainSetId, true, newStateMap, newUserStates,
|
||||
null /* signature */);
|
||||
mAttachedPkgStates.put(pkgName, newDomainSetId, newPkgState);
|
||||
return;
|
||||
}
|
||||
@@ -894,7 +896,8 @@ public class DomainVerificationService extends SystemService
|
||||
sendBroadcast = hasAutoVerifyDomains && needsBroadcast;
|
||||
|
||||
mAttachedPkgStates.put(pkgName, newDomainSetId, new DomainVerificationPkgState(
|
||||
pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates));
|
||||
pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates,
|
||||
null /* signature */));
|
||||
}
|
||||
|
||||
if (sendBroadcast) {
|
||||
@@ -1019,9 +1022,22 @@ public class DomainVerificationService extends SystemService
|
||||
|
||||
@Override
|
||||
public void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException {
|
||||
synchronized (mLock) {
|
||||
mSettings.writeSettings(serializer, mAttachedPkgStates);
|
||||
}
|
||||
mConnection.withPackageSettingsThrowing(pkgSettings -> {
|
||||
synchronized (mLock) {
|
||||
mSettings.writeSettings(serializer, mAttachedPkgStates, pkgName -> {
|
||||
PackageSetting pkgSetting = pkgSettings.apply(pkgName);
|
||||
if (pkgSetting == null) {
|
||||
// If querying for a user restored package that isn't installed on the
|
||||
// device yet, there will be no signature to write out. In that case,
|
||||
// it's expected that this returns null and it falls back to the restored
|
||||
// state's stored signature if it exists.
|
||||
return null;
|
||||
}
|
||||
|
||||
return PackageUtils.computeSignaturesSha256Digest(pkgSetting.getSignatures());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
mLegacySettings.writeSettings(serializer);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
|
||||
class DomainVerificationSettings {
|
||||
|
||||
@@ -68,18 +69,19 @@ class DomainVerificationSettings {
|
||||
|
||||
|
||||
public void writeSettings(@NonNull TypedXmlSerializer xmlSerializer,
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
|
||||
throws IOException {
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
|
||||
@NonNull Function<String, String> pkgSignatureFunction) throws IOException {
|
||||
synchronized (mLock) {
|
||||
DomainVerificationPersistence.writeToXml(xmlSerializer, liveState,
|
||||
mPendingPkgStates, mRestoredPkgStates);
|
||||
mPendingPkgStates, mRestoredPkgStates, pkgSignatureFunction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a previously stored set of states and merges them with {@param liveState}, directly
|
||||
* mutating the values. This is intended for reading settings written by {@link
|
||||
* #writeSettings(TypedXmlSerializer, DomainVerificationStateMap)} on the same device setup.
|
||||
* #writeSettings(TypedXmlSerializer, DomainVerificationStateMap, Function)} on the same device
|
||||
* setup.
|
||||
*/
|
||||
public void readSettings(@NonNull TypedXmlPullParser parser,
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.pm.verify.domain.models;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.verify.domain.DomainVerificationState;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.SparseArray;
|
||||
@@ -63,15 +64,28 @@ public class DomainVerificationPkgState {
|
||||
@NonNull
|
||||
private final SparseArray<DomainVerificationInternalUserState> mUserStates;
|
||||
|
||||
/**
|
||||
* If previously recorded, the SHA-256 signing cert digest of the package to attach to.
|
||||
* When doing restoration of a previously backed up state, if the signature does not
|
||||
* match the package being scanned/installed on device, it will be rejected.
|
||||
*
|
||||
* It's assumed the domain verification agent will eventually re-verify this domain
|
||||
* and revoke if necessary.
|
||||
*
|
||||
* @see android.util.PackageUtils#computeSignaturesSha256Digest(Signature[])
|
||||
*/
|
||||
@Nullable
|
||||
private final String mBackupSignatureHash;
|
||||
|
||||
public DomainVerificationPkgState(@NonNull String packageName, @NonNull UUID id,
|
||||
boolean hasAutoVerifyDomains) {
|
||||
this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0));
|
||||
this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0), null);
|
||||
}
|
||||
|
||||
public DomainVerificationPkgState(@NonNull DomainVerificationPkgState pkgState,
|
||||
@NonNull UUID id, boolean hasAutoVerifyDomains) {
|
||||
this(pkgState.getPackageName(), id, hasAutoVerifyDomains, pkgState.getStateMap(),
|
||||
pkgState.getUserStates());
|
||||
pkgState.getUserStates(), null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -109,7 +123,7 @@ public class DomainVerificationPkgState {
|
||||
|
||||
|
||||
|
||||
// Code below generated by codegen v1.0.22.
|
||||
// Code below generated by codegen v1.0.23.
|
||||
//
|
||||
// DO NOT MODIFY!
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
@@ -137,6 +151,13 @@ public class DomainVerificationPkgState {
|
||||
*
|
||||
* TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
|
||||
* such as storing no state when the package is marked as a linked app in SystemConfig.
|
||||
* @param backupSignatureHash
|
||||
* If previously recorded, the SHA-256 signing cert digest of the package to attach to.
|
||||
* When doing restoration of a previously backed up state, if the signature does not
|
||||
* match the package being scanned/installed on device, it will be rejected.
|
||||
*
|
||||
* It's assumed the domain verification agent will eventually re-verify this domain
|
||||
* and revoke if necessary.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public DomainVerificationPkgState(
|
||||
@@ -144,7 +165,8 @@ public class DomainVerificationPkgState {
|
||||
@NonNull UUID id,
|
||||
boolean hasAutoVerifyDomains,
|
||||
@NonNull ArrayMap<String,Integer> stateMap,
|
||||
@NonNull SparseArray<DomainVerificationInternalUserState> userStates) {
|
||||
@NonNull SparseArray<DomainVerificationInternalUserState> userStates,
|
||||
@Nullable String backupSignatureHash) {
|
||||
this.mPackageName = packageName;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mPackageName);
|
||||
@@ -158,6 +180,7 @@ public class DomainVerificationPkgState {
|
||||
this.mUserStates = userStates;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mUserStates);
|
||||
this.mBackupSignatureHash = backupSignatureHash;
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
@@ -201,6 +224,21 @@ public class DomainVerificationPkgState {
|
||||
return mUserStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* If previously recorded, the SHA-256 signing cert digest of the package to attach to.
|
||||
* When doing restoration of a previously backed up state, if the signature does not
|
||||
* match the package being scanned/installed on device, it will be rejected.
|
||||
*
|
||||
* It's assumed the domain verification agent will eventually re-verify this domain
|
||||
* and revoke if necessary.
|
||||
*
|
||||
* @see android.util.PackageUtils#computeSignaturesSha256Digest(Signature[])
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @Nullable String getBackupSignatureHash() {
|
||||
return mBackupSignatureHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public String toString() {
|
||||
@@ -212,7 +250,8 @@ public class DomainVerificationPkgState {
|
||||
"id = " + mId + ", " +
|
||||
"hasAutoVerifyDomains = " + mHasAutoVerifyDomains + ", " +
|
||||
"stateMap = " + mStateMap + ", " +
|
||||
"userStates = " + mUserStates +
|
||||
"userStates = " + mUserStates + ", " +
|
||||
"backupSignatureHash = " + mBackupSignatureHash +
|
||||
" }";
|
||||
}
|
||||
|
||||
@@ -233,7 +272,8 @@ public class DomainVerificationPkgState {
|
||||
&& Objects.equals(mId, that.mId)
|
||||
&& mHasAutoVerifyDomains == that.mHasAutoVerifyDomains
|
||||
&& Objects.equals(mStateMap, that.mStateMap)
|
||||
&& userStatesEquals(that.mUserStates);
|
||||
&& userStatesEquals(that.mUserStates)
|
||||
&& Objects.equals(mBackupSignatureHash, that.mBackupSignatureHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,14 +288,15 @@ public class DomainVerificationPkgState {
|
||||
_hash = 31 * _hash + Boolean.hashCode(mHasAutoVerifyDomains);
|
||||
_hash = 31 * _hash + Objects.hashCode(mStateMap);
|
||||
_hash = 31 * _hash + userStatesHashCode();
|
||||
_hash = 31 * _hash + Objects.hashCode(mBackupSignatureHash);
|
||||
return _hash;
|
||||
}
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1614818362549L,
|
||||
codegenVersion = "1.0.22",
|
||||
time = 1617315369614L,
|
||||
codegenVersion = "1.0.23",
|
||||
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java",
|
||||
inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState> mUserStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getUserState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getOrCreateUserState(int)\npublic void setId(java.util.UUID)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userStatesHashCode()\nprivate boolean userStatesEquals(android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
|
||||
inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState> mUserStates\nprivate final @android.annotation.Nullable java.lang.String mBackupSignatureHash\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getUserState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getOrCreateUserState(int)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userStatesHashCode()\nprivate boolean userStatesEquals(android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.pm.test.verify.domain
|
||||
|
||||
import android.content.pm.verify.domain.DomainVerificationState
|
||||
import android.util.ArrayMap
|
||||
import android.util.SparseArray
|
||||
import android.util.TypedXmlPullParser
|
||||
import android.util.TypedXmlSerializer
|
||||
import android.util.Xml
|
||||
@@ -84,23 +85,32 @@ class DomainVerificationPersistenceTest {
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun writeAndReadBackNormal() {
|
||||
private fun mockWriteValues(
|
||||
pkgNameToSignature: (String) -> String? = { null }
|
||||
): Triple<DomainVerificationStateMap<DomainVerificationPkgState>,
|
||||
ArrayMap<String, DomainVerificationPkgState>,
|
||||
ArrayMap<String, DomainVerificationPkgState>> {
|
||||
val attached = DomainVerificationStateMap<DomainVerificationPkgState>().apply {
|
||||
mockPkgState(0).let { put(it.packageName, it.id, it) }
|
||||
mockPkgState(1).let { put(it.packageName, it.id, it) }
|
||||
mockPkgState(0, pkgNameToSignature).let { put(it.packageName, it.id, it) }
|
||||
mockPkgState(1, pkgNameToSignature).let { put(it.packageName, it.id, it) }
|
||||
}
|
||||
val pending = ArrayMap<String, DomainVerificationPkgState>().apply {
|
||||
mockPkgState(2).let { put(it.packageName, it) }
|
||||
mockPkgState(3).let { put(it.packageName, it) }
|
||||
mockPkgState(2, pkgNameToSignature).let { put(it.packageName, it) }
|
||||
mockPkgState(3, pkgNameToSignature).let { put(it.packageName, it) }
|
||||
}
|
||||
val restored = ArrayMap<String, DomainVerificationPkgState>().apply {
|
||||
mockPkgState(4).let { put(it.packageName, it) }
|
||||
mockPkgState(5).let { put(it.packageName, it) }
|
||||
mockPkgState(4, pkgNameToSignature).let { put(it.packageName, it) }
|
||||
mockPkgState(5, pkgNameToSignature).let { put(it.packageName, it) }
|
||||
}
|
||||
|
||||
return Triple(attached, pending, restored)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun writeAndReadBackNormal() {
|
||||
val (attached, pending, restored) = mockWriteValues()
|
||||
val file = tempFolder.newFile().writeXml {
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored)
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored, null)
|
||||
}
|
||||
|
||||
val xml = file.readText()
|
||||
@@ -114,9 +124,57 @@ class DomainVerificationPersistenceTest {
|
||||
assertWithMessage(xml).that(readRestored.values).containsExactlyElementsIn(restored.values)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun writeAndReadBackWithSignature() {
|
||||
val (attached, pending, restored) = mockWriteValues()
|
||||
val file = tempFolder.newFile().writeXml {
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored) {
|
||||
"SIGNATURE_$it"
|
||||
}
|
||||
}
|
||||
|
||||
val (readActive, readRestored) = file.readXml {
|
||||
DomainVerificationPersistence.readFromXml(it)
|
||||
}
|
||||
|
||||
// Assign the signatures to a fresh set of data structures, to ensure the previous write
|
||||
// call did not use the signatures from the data structure. This is because the method is
|
||||
// intended to optionally append signatures, regardless of if the existing data structures
|
||||
// contain them or not.
|
||||
val (attached2, pending2, restored2) = mockWriteValues { "SIGNATURE_$it" }
|
||||
|
||||
assertThat(readActive.values)
|
||||
.containsExactlyElementsIn(attached2.values() + pending2.values)
|
||||
assertThat(readRestored.values).containsExactlyElementsIn(restored2.values)
|
||||
|
||||
(readActive + readRestored).forEach { (_, value) ->
|
||||
assertThat(value.backupSignatureHash).isEqualTo("SIGNATURE_${value.packageName}")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun writeStateSignatureIfFunctionReturnsNull() {
|
||||
val (attached, pending, restored) = mockWriteValues { "SIGNATURE_$it" }
|
||||
val file = tempFolder.newFile().writeXml {
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored) { null }
|
||||
}
|
||||
|
||||
val (readActive, readRestored) = file.readXml {
|
||||
DomainVerificationPersistence.readFromXml(it)
|
||||
}
|
||||
|
||||
assertThat(readActive.values)
|
||||
.containsExactlyElementsIn(attached.values() + pending.values)
|
||||
assertThat(readRestored.values).containsExactlyElementsIn(restored.values)
|
||||
|
||||
(readActive + readRestored).forEach { (_, value) ->
|
||||
assertThat(value.backupSignatureHash).isEqualTo("SIGNATURE_${value.packageName}")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readMalformed() {
|
||||
val stateZero = mockEmptyPkgState(0).apply {
|
||||
val stateZero = mockEmptyPkgState(0, pkgNameToSignature = { "ACTIVE" }).apply {
|
||||
stateMap["example.com"] = DomainVerificationState.STATE_SUCCESS
|
||||
stateMap["example.org"] = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED
|
||||
|
||||
@@ -128,7 +186,7 @@ class DomainVerificationPersistenceTest {
|
||||
isLinkHandlingAllowed = true
|
||||
}
|
||||
}
|
||||
val stateOne = mockEmptyPkgState(1).apply {
|
||||
val stateOne = mockEmptyPkgState(1, pkgNameToSignature = { "RESTORED" }).apply {
|
||||
// It's valid to have a user selection without any autoVerify domains
|
||||
userStates[1] = DomainVerificationInternalUserState(1).apply {
|
||||
addHosts(setOf("example-user1.com", "example-user1.org"))
|
||||
@@ -156,6 +214,7 @@ class DomainVerificationPersistenceTest {
|
||||
packageName="${stateZero.packageName}"
|
||||
id="${stateZero.id}"
|
||||
hasAutoVerifyDomains="true"
|
||||
signature="ACTIVE"
|
||||
>
|
||||
<state>
|
||||
<domain name="example.com" state="${
|
||||
@@ -191,6 +250,7 @@ class DomainVerificationPersistenceTest {
|
||||
packageName="${stateOne.packageName}"
|
||||
id="${stateOne.id}"
|
||||
hasAutoVerifyDomains="true"
|
||||
signature="RESTORED"
|
||||
>
|
||||
<state/>
|
||||
<user-states>
|
||||
@@ -226,20 +286,30 @@ class DomainVerificationPersistenceTest {
|
||||
|
||||
private fun mockEmptyPkgState(
|
||||
id: Int,
|
||||
hasAutoVerifyDomains: Boolean = true
|
||||
hasAutoVerifyDomains: Boolean = true,
|
||||
pkgNameToSignature: (String) -> String? = { null }
|
||||
): DomainVerificationPkgState {
|
||||
val pkgName = pkgName(id)
|
||||
val domainSetId = UUID(0L, id.toLong())
|
||||
return DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains)
|
||||
return DomainVerificationPkgState(
|
||||
pkgName,
|
||||
domainSetId,
|
||||
hasAutoVerifyDomains,
|
||||
ArrayMap(),
|
||||
SparseArray(),
|
||||
pkgNameToSignature(pkgName)
|
||||
)
|
||||
}
|
||||
|
||||
private fun mockPkgState(id: Int) = mockEmptyPkgState(id).apply {
|
||||
stateMap["$packageName.com"] = id
|
||||
userStates[id] = DomainVerificationInternalUserState(id).apply {
|
||||
addHosts(setOf("$packageName-user.com"))
|
||||
isLinkHandlingAllowed = true
|
||||
}
|
||||
}
|
||||
private fun mockPkgState(id: Int, pkgNameToSignature: (String) -> String? = { null }) =
|
||||
mockEmptyPkgState(id, pkgNameToSignature = pkgNameToSignature)
|
||||
.apply {
|
||||
stateMap["$packageName.com"] = id
|
||||
userStates[id] = DomainVerificationInternalUserState(id).apply {
|
||||
addHosts(setOf("$packageName-user.com"))
|
||||
isLinkHandlingAllowed = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun pkgName(id: Int) = "$PKG_PREFIX.pkg$id"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user