From f9a0630114665e18afd85a203e96c0cdf07dd718 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 15 Sep 2014 11:33:12 +1000 Subject: [PATCH] Bug 1058433 - nsPermissionManager now records the mod-time of a permission and allows removal of ones modified since a specified time. r=ehsan --- build/automation.py.in | 5 +- dom/ipc/ContentChild.cpp | 4 + extensions/cookie/nsPermissionManager.cpp | 190 +++++++++++++++--- extensions/cookie/nsPermissionManager.h | 16 +- .../test/unit/test_permmanager_defaults.js | 49 +++++ .../test_permmanager_load_invalid_entries.js | 18 +- .../test/unit/test_permmanager_removesince.js | 69 +++++++ extensions/cookie/test/unit/xpcshell.ini | 1 + netwerk/base/public/nsIPermissionManager.idl | 7 +- .../mozprofile/mozprofile/permissions.py | 6 +- .../mozbase/mozprofile/tests/permissions.py | 19 +- 11 files changed, 348 insertions(+), 36 deletions(-) create mode 100644 extensions/cookie/test/unit/test_permmanager_removesince.js diff --git a/build/automation.py.in b/build/automation.py.in index ac4fa28a239..bccf2d253cc 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -303,7 +303,7 @@ class Automation(object): permDB = sqlite3.connect(os.path.join(profileDir, "permissions.sqlite")) cursor = permDB.cursor(); - cursor.execute("PRAGMA user_version=3"); + cursor.execute("PRAGMA user_version=4"); # SQL copied from nsPermissionManager.cpp cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( @@ -313,13 +313,14 @@ class Automation(object): permission INTEGER, expireType INTEGER, expireTime INTEGER, + modificationTime INTEGER, appId INTEGER, isInBrowserElement INTEGER)""") # Insert desired permissions for perm in permissions.keys(): for host,allow in permissions[perm]: - cursor.execute("INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0)", + cursor.execute("INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0, 0)", (host, perm, 1 if allow else 2)) # Commit and close diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 8546eb95e22..27e16b88214 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1714,12 +1714,16 @@ ContentChild::RecvAddPermission(const IPC::Permission& permission) getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, true); + // child processes don't care about modification time. + int64_t modificationTime = 0; + permissionManager->AddInternal(principal, nsCString(permission.type), permission.capability, 0, permission.expireType, permission.expireTime, + modificationTime, nsPermissionManager::eNotify, nsPermissionManager::eNoDBOperation); #endif diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp index 2382b9a56b2..066374dc2a9 100644 --- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -357,7 +357,7 @@ nsPermissionManager::AppClearDataObserverInit() // nsPermissionManager Implementation static const char kPermissionsFileName[] = "permissions.sqlite"; -#define HOSTS_SCHEMA_VERSION 3 +#define HOSTS_SCHEMA_VERSION 4 static const char kHostpermFileName[] = "hostperm.1"; @@ -432,8 +432,12 @@ nsPermissionManager::Init() rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); + // The child process doesn't care about modification times - it neither + // reads nor writes, nor removes them based on the date - so 0 (which + // will end up as now()) is fine. + uint64_t modificationTime = 0; AddInternal(principal, perm.type, perm.capability, 0, perm.expireType, - perm.expireTime, eNotify, eNoDBOperation); + perm.expireTime, modificationTime, eNotify, eNoDBOperation); } // Stop here; we don't need the DB in the child process @@ -546,6 +550,23 @@ nsPermissionManager::InitDB(bool aRemoveFile) // fall through to the next upgrade + // Version 3->4 is the creation of the modificationTime field. + case 3: + { + rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE moz_hosts ADD modificationTime INTEGER")); + NS_ENSURE_SUCCESS(rv, rv); + + // We leave the modificationTime at zero for all existing records; using + // now() would mean, eg, that doing "remove all from the last hour" + // within the first hour after migration would remove all permissions. + + rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); + NS_ENSURE_SUCCESS(rv, rv); + } + + // fall through to the next upgrade + // current version. case HOSTS_SCHEMA_VERSION: break; @@ -561,7 +582,7 @@ nsPermissionManager::InitDB(bool aRemoveFile) // check if all the expected columns exist nsCOMPtr stmt; rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"), + "SELECT host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement FROM moz_hosts"), getter_AddRefs(stmt)); if (NS_SUCCEEDED(rv)) break; @@ -583,8 +604,8 @@ nsPermissionManager::InitDB(bool aRemoveFile) // cache frequently used statements (for insertion, deletion, and updating) rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( "INSERT INTO moz_hosts " - "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert)); + "(id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) " + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"), getter_AddRefs(mStmtInsert)); NS_ENSURE_SUCCESS(rv, rv); rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( @@ -594,7 +615,7 @@ nsPermissionManager::InitDB(bool aRemoveFile) rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( "UPDATE moz_hosts " - "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"), + "SET permission = ?2, expireType= ?3, expireTime = ?4, modificationTime = ?5 WHERE id = ?1"), getter_AddRefs(mStmtUpdate)); NS_ENSURE_SUCCESS(rv, rv); @@ -626,6 +647,7 @@ nsPermissionManager::CreateTable() ",permission INTEGER" ",expireType INTEGER" ",expireTime INTEGER" + ",modificationTime INTEGER" ",appId INTEGER" ",isInBrowserElement INTEGER" ")")); @@ -679,8 +701,11 @@ nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal, return NS_ERROR_INVALID_ARG; } + // A modificationTime of zero will cause AddInternal to use now(). + int64_t modificationTime = 0; + return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0, - aExpireType, aExpireTime, eNotify, eWriteToDB); + aExpireType, aExpireTime, modificationTime, eNotify, eWriteToDB); } nsresult @@ -690,6 +715,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, int64_t aID, uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, NotifyOperationType aNotifyOperation, DBOperationType aDBOperation) { @@ -760,15 +786,27 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, // even if the new permission is UNKNOWN_ACTION (which means a "logical // remove" of the default) op = eOperationReplacingDefault; + else if (aID == cIDPermissionIsDefault) + // We are adding a default permission but a "real" permission already + // exists. This almost-certainly means we just did a removeAllSince and + // are re-importing defaults - so we can ignore this. + op = eOperationNone; else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) op = eOperationRemoving; else op = eOperationChanging; } + // child processes should *always* be passed a modificationTime of zero. + MOZ_ASSERT(!IsChildProcess() || aModificationTime == 0); + // do the work for adding, deleting, or changing a permission: // update the in-memory list, write to the db, and notify consumers. int64_t id; + if (aModificationTime == 0) { + aModificationTime = PR_Now() / 1000; + } + switch (op) { case eOperationNone: { @@ -786,7 +824,9 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, id = aID; } - entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime)); + entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, + aExpireType, aExpireTime, + aModificationTime)); if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) { uint32_t appId; @@ -797,7 +837,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); NS_ENSURE_SUCCESS(rv, rv); - UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement); + UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement); } if (aNotifyOperation == eNotify) { @@ -824,7 +864,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, // We care only about the id here so we pass dummy values for all other // parameters. UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, - nsIPermissionManager::EXPIRE_NEVER, 0, 0, false); + nsIPermissionManager::EXPIRE_NEVER, 0, 0, 0, false); if (aNotifyOperation == eNotify) { NotifyObserversWithPermission(host, @@ -866,12 +906,13 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, entry->GetPermissions()[index].mPermission = aPermission; entry->GetPermissions()[index].mExpireType = aExpireType; entry->GetPermissions()[index].mExpireTime = aExpireTime; + entry->GetPermissions()[index].mModificationTime = aModificationTime; if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) - // We care only about the id, the permission and expireType/expireTime here. + // We care only about the id, the permission and expireType/expireTime/modificationTime here. // We pass dummy values for all other parameters. UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), - aPermission, aExpireType, aExpireTime, 0, false); + aPermission, aExpireType, aExpireTime, aModificationTime, 0, false); if (aNotifyOperation == eNotify) { NotifyObserversWithPermission(host, @@ -915,6 +956,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, entry->GetPermissions()[index].mPermission = aPermission; entry->GetPermissions()[index].mExpireType = aExpireType; entry->GetPermissions()[index].mExpireTime = aExpireTime; + entry->GetPermissions()[index].mModificationTime = aModificationTime; // If requested, create the entry in the DB. if (aDBOperation == eWriteToDB) { @@ -926,7 +968,8 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); NS_ENSURE_SUCCESS(rv, rv); - UpdateDB(eOperationAdding, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement); + UpdateDB(eOperationAdding, mStmtInsert, id, host, aType, aPermission, + aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement); } if (aNotifyOperation == eNotify) { @@ -983,6 +1026,7 @@ nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal, 0, nsIPermissionManager::EXPIRE_NEVER, 0, + 0, eNotify, eWriteToDB); } @@ -994,6 +1038,13 @@ nsPermissionManager::RemoveAll() return RemoveAllInternal(true); } +NS_IMETHODIMP +nsPermissionManager::RemoveAllSince(int64_t aSince) +{ + ENSURE_NOT_CHILD_PROCESS; + return RemoveAllModifiedSince(aSince); +} + void nsPermissionManager::CloseDB(bool aRebuildOnSuccess) { @@ -1323,12 +1374,16 @@ nsPermissionManager::GetPermissionHashKey(const nsACString& aHost, // helper struct for passing arguments into hash enumeration callback. struct nsGetEnumeratorData { - nsGetEnumeratorData(nsCOMArray *aArray, const nsTArray *aTypes) + nsGetEnumeratorData(nsCOMArray *aArray, + const nsTArray *aTypes, + int64_t aSince = 0) : array(aArray) - , types(aTypes) {} + , types(aTypes) + , since(aSince) {} nsCOMArray *array; const nsTArray *types; + int64_t since; }; static PLDHashOperator @@ -1395,6 +1450,76 @@ NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aT return NS_OK; } +static PLDHashOperator +AddPermissionsModifiedSinceToList( + nsPermissionManager::PermissionHashKey* entry, void* arg) +{ + nsGetEnumeratorData* data = static_cast(arg); + + for (size_t i = 0; i < entry->GetPermissions().Length(); ++i) { + const nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; + + if (data->since > permEntry.mModificationTime) { + continue; + } + + nsPermission* perm = new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, + data->types->ElementAt(permEntry.mType), + permEntry.mPermission, + permEntry.mExpireType, + permEntry.mExpireTime); + + data->array->AppendObject(perm); + } + return PL_DHASH_NEXT; +} + +nsresult +nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime) +{ + ENSURE_NOT_CHILD_PROCESS; + + // roll an nsCOMArray of all our permissions, then hand out an enumerator + nsCOMArray array; + nsGetEnumeratorData data(&array, &mTypeArray, aModificationTime); + + mPermissionTable.EnumerateEntries(AddPermissionsModifiedSinceToList, &data); + + for (int32_t i = 0; iGetHost(host); + array[i]->GetIsInBrowserElement(&isInBrowserElement); + array[i]->GetType(type); + array[i]->GetAppId(&appId); + + nsCOMPtr principal; + if (NS_FAILED(GetPrincipal(host, appId, isInBrowserElement, + getter_AddRefs(principal)))) { + NS_ERROR("GetPrincipal() failed!"); + continue; + } + // AddInternal handles removal, so let it do the work... + AddInternal( + principal, + type, + nsIPermissionManager::UNKNOWN_ACTION, + 0, + nsIPermissionManager::EXPIRE_NEVER, 0, 0, + nsPermissionManager::eNotify, + nsPermissionManager::eWriteToDB); + } + // now re-import any defaults as they may now be required if we just deleted + // an override. + ImportDefaults(); + return NS_OK; +} + PLDHashOperator nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg) { @@ -1475,6 +1600,7 @@ nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly) 0, nsIPermissionManager::EXPIRE_NEVER, 0, + 0, nsPermissionManager::eNotify, nsPermissionManager::eNoDBOperation); } @@ -1646,7 +1772,7 @@ nsPermissionManager::Read() nsCOMPtr stmt; rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement " + "SELECT id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement " "FROM moz_hosts"), getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); @@ -1655,6 +1781,7 @@ nsPermissionManager::Read() uint32_t permission; uint32_t expireType; int64_t expireTime; + int64_t modificationTime; uint32_t appId; bool isInBrowserElement; bool hasResult; @@ -1682,15 +1809,17 @@ nsPermissionManager::Read() permission = stmt->AsInt32(3); expireType = stmt->AsInt32(4); - // convert into int64_t value (milliseconds) + // convert into int64_t values (milliseconds) expireTime = stmt->AsInt64(5); + modificationTime = stmt->AsInt64(6); - if (stmt->AsInt64(6) < 0) { + if (stmt->AsInt64(7) < 0) { readError = true; continue; } - appId = static_cast(stmt->AsInt64(6)); - isInBrowserElement = static_cast(stmt->AsInt32(7)); + + appId = static_cast(stmt->AsInt64(7)); + isInBrowserElement = static_cast(stmt->AsInt32(8)); nsCOMPtr principal; nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal)); @@ -1700,7 +1829,7 @@ nsPermissionManager::Read() } rv = AddInternal(principal, type, permission, id, expireType, expireTime, - eDontNotify, eNoDBOperation); + modificationTime, eDontNotify, eNoDBOperation); if (NS_FAILED(rv)) { readError = true; continue; @@ -1848,8 +1977,14 @@ nsPermissionManager::_DoImport(nsIInputStream *inputStream, mozIStorageConnectio nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); + // the import file format doesn't handle modification times, so we use + // 0, which AddInternal will convert to now() + int64_t modificationTime = 0; + rv = AddInternal(principal, lineArray[1], permission, id, - nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, operation); + nsIPermissionManager::EXPIRE_NEVER, 0, + modificationTime, + eDontNotify, operation); NS_ENSURE_SUCCESS(rv, rv); } @@ -1880,6 +2015,7 @@ nsPermissionManager::UpdateDB(OperationType aOp, uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, uint32_t aAppId, bool aIsInBrowserElement) { @@ -1912,10 +2048,13 @@ nsPermissionManager::UpdateDB(OperationType aOp, rv = aStmt->BindInt64ByIndex(5, aExpireTime); if (NS_FAILED(rv)) break; - rv = aStmt->BindInt64ByIndex(6, aAppId); + rv = aStmt->BindInt64ByIndex(6, aModificationTime); if (NS_FAILED(rv)) break; - rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement); + rv = aStmt->BindInt64ByIndex(7, aAppId); + if (NS_FAILED(rv)) break; + + rv = aStmt->BindInt64ByIndex(8, aIsInBrowserElement); break; } @@ -1937,6 +2076,9 @@ nsPermissionManager::UpdateDB(OperationType aOp, if (NS_FAILED(rv)) break; rv = aStmt->BindInt64ByIndex(3, aExpireTime); + if (NS_FAILED(rv)) break; + + rv = aStmt->BindInt64ByIndex(4, aModificationTime); break; } diff --git a/extensions/cookie/nsPermissionManager.h b/extensions/cookie/nsPermissionManager.h index 1755d891bc2..4bbc0db94bf 100644 --- a/extensions/cookie/nsPermissionManager.h +++ b/extensions/cookie/nsPermissionManager.h @@ -37,12 +37,14 @@ public: { public: PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission, - uint32_t aExpireType, int64_t aExpireTime) + uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime) : mID(aID) , mType(aType) , mPermission(aPermission) , mExpireType(aExpireType) , mExpireTime(aExpireTime) + , mModificationTime(aModificationTime) , mNonSessionPermission(aPermission) , mNonSessionExpireType(aExpireType) , mNonSessionExpireTime(aExpireTime) @@ -53,6 +55,7 @@ public: uint32_t mPermission; uint32_t mExpireType; int64_t mExpireTime; + int64_t mModificationTime; uint32_t mNonSessionPermission; uint32_t mNonSessionExpireType; uint32_t mNonSessionExpireTime; @@ -154,7 +157,7 @@ public: // unknown permission... return relevant data return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION, - nsIPermissionManager::EXPIRE_NEVER, 0); + nsIPermissionManager::EXPIRE_NEVER, 0, 0); } private: @@ -200,6 +203,7 @@ public: int64_t aID, uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, NotifyOperationType aNotifyOperation, DBOperationType aDBOperation); @@ -260,6 +264,7 @@ private: uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, + int64_t aModificationTime, uint32_t aAppId, bool aIsInBrowserElement); @@ -299,6 +304,13 @@ private: RemoveExpiredPermissionsForAppEnumerator(PermissionHashKey* entry, void* nonused); + + /** + * This method removes all permissions modified after the specified time. + */ + nsresult + RemoveAllModifiedSince(int64_t aModificationTime); + nsCOMPtr mObserverService; nsCOMPtr mIDNService; diff --git a/extensions/cookie/test/unit/test_permmanager_defaults.js b/extensions/cookie/test/unit/test_permmanager_defaults.js index ff0c454262a..2137f5b8b7a 100644 --- a/extensions/cookie/test/unit/test_permmanager_defaults.js +++ b/extensions/cookie/test/unit/test_permmanager_defaults.js @@ -3,8 +3,15 @@ // The origin we use in most of the tests. const TEST_ORIGIN = "example.org"; +const TEST_ORIGIN_2 = "example.com"; const TEST_PERMISSION = "test-permission"; +function promiseTimeout(delay) { + let deferred = Promise.defer(); + do_timeout(delay, deferred.resolve); + return deferred.promise; +} + function run_test() { run_next_test(); } @@ -28,6 +35,7 @@ add_task(function* do_test() { conv.writeString("# this is a comment\n"); conv.writeString("\n"); // a blank line! conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN + "\n"); + conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2 + "\n"); ostream.close(); // Set the preference used by the permission manager so the file is read. @@ -92,6 +100,47 @@ add_task(function* do_test() { do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION, findCapabilityViaEnum()); yield checkCapabilityViaDB(Ci.nsIPermissionManager.PROMPT_ACTION); + // -------------------------------------------------------------- + // check default permissions and removeAllSince work as expected. + pm.removeAll(); // ensure only defaults are there. + + let permURI2 = NetUtil.newURI("http://" + TEST_ORIGIN_2); + let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI2); + + // default for both principals is allow. + do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, + pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); + do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, + pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)); + + // Add a default override for TEST_ORIGIN_2 - this one should *not* be + // restored in removeAllSince() + pm.addFromPrincipal(principal2, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION); + do_check_eq(Ci.nsIPermissionManager.DENY_ACTION, + pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)); + yield promiseTimeout(20); + + let since = Number(Date.now()); + yield promiseTimeout(20); + + // explicitly add a permission which overrides the default for the first + // principal - this one *should* be removed by removeAllSince. + pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION); + do_check_eq(Ci.nsIPermissionManager.DENY_ACTION, + pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); + + // do a removeAllSince. + pm.removeAllSince(since); + + // the default for the first principal should re-appear as we modified it + // later then |since| + do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, + pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); + + // but the permission for principal2 should remain as we added that before |since|. + do_check_eq(Ci.nsIPermissionManager.DENY_ACTION, + pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION)); + // remove the temp file we created. file.remove(false); }); diff --git a/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js index a8066653de3..b96813b98aa 100644 --- a/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js +++ b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js @@ -113,13 +113,25 @@ function run_test() { ); } + let earliestNow = Number(Date.now()); // Initialize the permission manager service var pm = Cc["@mozilla.org/permissionmanager;1"] .getService(Ci.nsIPermissionManager); + let latestNow = Number(Date.now()); - // The schema should still be 3. We want this test to be updated for each - // schema update. - do_check_eq(connection.schemaVersion, 3); + // The schema should be upgraded to 4, and a 'modificationTime' column should + // exist with all records having a value of 0. + do_check_eq(connection.schemaVersion, 4); + + let select = connection.createStatement("SELECT modificationTime FROM moz_hosts") + let numMigrated = 0; + while (select.executeStep()) { + let thisModTime = select.getInt64(0); + do_check_true(thisModTime == 0, "new modifiedTime field is correct"); + numMigrated += 1; + } + // check we found at least 1 record that was migrated. + do_check_true(numMigrated > 0, "we found at least 1 record that was migrated"); // This permission should always be there. let principal = Cc["@mozilla.org/scriptsecuritymanager;1"] diff --git a/extensions/cookie/test/unit/test_permmanager_removesince.js b/extensions/cookie/test/unit/test_permmanager_removesince.js new file mode 100644 index 00000000000..e7d4dadd546 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_removesince.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that removing permissions since a specified time behaves as expected. + +let test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function continue_test() +{ + do_run_generator(test_generator); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + let pm = Services.perms; + + // to help with testing edge-cases, we will arrange for .removeAllSince to + // remove *all* permissions from one principal and one permission from another. + let permURI1 = NetUtil.newURI("http://example.com"); + let principal1 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI1); + + let permURI2 = NetUtil.newURI("http://example.org"); + let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI2); + + // add a permission now - this isn't going to be removed. + pm.addFromPrincipal(principal1, "test/remove-since", 1); + + // sleep briefly, then record the time - we'll remove all since then. + do_timeout(20, continue_test); + yield; + + let since = Number(Date.now()); + + // *sob* - on Windows at least, the now recorded by nsPermissionManager.cpp + // might be a couple of ms *earlier* than what JS sees. So another sleep + // to ensure our |since| is greater than the time of the permissions we + // are now adding. Sadly this means we'll never be able to test when since + // exactly equals the modTime, but there you go... + do_timeout(20, continue_test); + yield; + + // add another item - this second one should get nuked. + pm.addFromPrincipal(principal1, "test/remove-since-2", 1); + + // add 2 items for the second principal - both will be removed. + pm.addFromPrincipal(principal2, "test/remove-since", 1); + pm.addFromPrincipal(principal2, "test/remove-since-2", 1); + + // do the removal. + pm.removeAllSince(since); + + // principal1 - the first one should remain. + do_check_eq(1, pm.testPermissionFromPrincipal(principal1, "test/remove-since")); + // but the second should have been removed. + do_check_eq(0, pm.testPermissionFromPrincipal(principal1, "test/remove-since-2")); + + // principal2 - both should have been removed. + do_check_eq(0, pm.testPermissionFromPrincipal(principal2, "test/remove-since")); + do_check_eq(0, pm.testPermissionFromPrincipal(principal2, "test/remove-since-2")); + + do_finish_generator_test(test_generator); +} diff --git a/extensions/cookie/test/unit/xpcshell.ini b/extensions/cookie/test/unit/xpcshell.ini index 070183a2554..26d6d42f42d 100644 --- a/extensions/cookie/test/unit/xpcshell.ini +++ b/extensions/cookie/test/unit/xpcshell.ini @@ -23,6 +23,7 @@ support-files = [test_permmanager_getPermissionObject.js] [test_permmanager_notifications.js] [test_permmanager_removeall.js] +[test_permmanager_removesince.js] [test_permmanager_load_invalid_entries.js] skip-if = debug == true [test_permmanager_idn.js] diff --git a/netwerk/base/public/nsIPermissionManager.idl b/netwerk/base/public/nsIPermissionManager.idl index 62fb0b8eeec..dc0523255a0 100644 --- a/netwerk/base/public/nsIPermissionManager.idl +++ b/netwerk/base/public/nsIPermissionManager.idl @@ -37,7 +37,7 @@ interface nsIDOMWindow; interface nsIPermission; interface nsISimpleEnumerator; -[scriptable, uuid(c9fec678-f194-43c9-96b0-7bd9dbdd6bb0)] +[scriptable, uuid(620d9b61-8997-4d13-aa64-ec03341dd75b)] interface nsIPermissionManager : nsISupports { /** @@ -132,6 +132,11 @@ interface nsIPermissionManager : nsISupports */ void removeAll(); + /** + * Clear all permission information added since the specified time. + */ + void removeAllSince(in int64_t since); + /** * Test whether a website has permission to perform the given action. * @param uri the uri to be tested diff --git a/testing/mozbase/mozprofile/mozprofile/permissions.py b/testing/mozbase/mozprofile/mozprofile/permissions.py index aaecd5dc59e..126d0f91f99 100644 --- a/testing/mozbase/mozprofile/mozprofile/permissions.py +++ b/testing/mozbase/mozprofile/mozprofile/permissions.py @@ -241,8 +241,12 @@ class Permissions(object): rows = cursor.execute("PRAGMA table_info(moz_hosts)") count = len(rows.fetchall()) + # if the db contains 9 columns, we're using user_version 4 + if count == 9: + statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0, 0)" + cursor.execute("PRAGMA user_version=4;") # if the db contains 8 columns, we're using user_version 3 - if count == 8: + elif count == 8: statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0)" cursor.execute("PRAGMA user_version=3;") else: diff --git a/testing/mozbase/mozprofile/tests/permissions.py b/testing/mozbase/mozprofile/tests/permissions.py index 801570aad69..b402991d929 100755 --- a/testing/mozbase/mozprofile/tests/permissions.py +++ b/testing/mozbase/mozprofile/tests/permissions.py @@ -40,7 +40,18 @@ http://127.0.0.1:8888 privileged cursor.execute("PRAGMA user_version=%d;" % version) - if version == 3: + if version == 4: + cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( + id INTEGER PRIMARY KEY, + host TEXT, + type TEXT, + permission INTEGER, + expireType INTEGER, + expireTime INTEGER, + modificationTime INTEGER, + appId INTEGER, + isInBrowserElement INTEGER)""") + elif version == 3: cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( id INTEGER PRIMARY KEY, host TEXT, @@ -59,7 +70,7 @@ http://127.0.0.1:8888 privileged expireType INTEGER, expireTime INTEGER)""") else: - raise Exception("version must be 2 or 3") + raise Exception("version must be 2, 3 or 4") permDB.commit() cursor.close() @@ -149,7 +160,7 @@ http://127.0.0.1:8888 privileged self.assertEqual(len(entries), 3) - columns = 8 if version == 3 else 6 + columns = 9 if version == 4 else (8 if version == 3 else 6) self.assertEqual(len(entries[0]), columns) for x in range(4, columns): self.assertEqual(entries[0][x], 0) @@ -160,6 +171,8 @@ http://127.0.0.1:8888 privileged def test_existing_permissions_db_v3(self): self.verify_user_version(3) + def test_existing_permissions_db_v4(self): + self.verify_user_version(4) if __name__ == '__main__': unittest.main()