fune/toolkit/components/credentialmanagement/IdentityCredentialStorageService.cpp
Tamas Szentpeteri 41444f1fe8 Backed out 11 changesets (bug 1892009, bug 1892020, bug 1892014, bug 1895750, bug 1892015, bug 1896920, bug 1892013, bug 1892010) for causing gv-junit fails related to PromptDelegateTest#fedCMProviderPromptTest. CLOSED TREE
Backed out changeset 737fde266c8d (bug 1896920)
Backed out changeset 1f466fdca856 (bug 1896920)
Backed out changeset 0843abab6f3b (bug 1896920)
Backed out changeset 64ba753ffaef (bug 1895750)
Backed out changeset cdcb4997f7bc (bug 1892020)
Backed out changeset a22c76f82cbd (bug 1895750)
Backed out changeset 01da838f2fd0 (bug 1892009)
Backed out changeset f09f5993b414 (bug 1892015)
Backed out changeset da1e40d9d0ca (bug 1892014)
Backed out changeset d2be17744b0b (bug 1892013)
Backed out changeset 2afba8f5e25b (bug 1892010)
2024-06-04 04:22:15 +03:00

892 lines
31 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ErrorList.h"
#include "IdentityCredentialStorageService.h"
#include "MainThreadUtils.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/Base64.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Components.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPtr.h"
#include "mozIStorageService.h"
#include "mozIStorageConnection.h"
#include "mozIStorageStatement.h"
#include "mozStorageCID.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsComponentManagerUtils.h"
#include "nsCRT.h"
#include "nsDebug.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIObserverService.h"
#include "nsIWritablePropertyBag2.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsVariant.h"
#include "prtime.h"
#define ACCOUNT_STATE_FILENAME "credentialstate.sqlite"_ns
#define SCHEMA_VERSION 1
#define MODIFIED_NOW PR_Now()
namespace mozilla {
StaticRefPtr<IdentityCredentialStorageService>
gIdentityCredentialStorageService;
NS_IMPL_ISUPPORTS(IdentityCredentialStorageService,
nsIIdentityCredentialStorageService, nsIObserver,
nsIAsyncShutdownBlocker)
already_AddRefed<IdentityCredentialStorageService>
IdentityCredentialStorageService::GetSingleton() {
AssertIsOnMainThread();
MOZ_ASSERT(XRE_IsParentProcess());
if (!gIdentityCredentialStorageService) {
gIdentityCredentialStorageService = new IdentityCredentialStorageService();
ClearOnShutdown(&gIdentityCredentialStorageService);
nsresult rv = gIdentityCredentialStorageService->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
}
RefPtr<IdentityCredentialStorageService> service =
gIdentityCredentialStorageService;
return service.forget();
}
NS_IMETHODIMP IdentityCredentialStorageService::GetName(nsAString& aName) {
aName = u"IdentityCredentialStorageService: Flushing data"_ns;
return NS_OK;
}
NS_IMETHODIMP IdentityCredentialStorageService::BlockShutdown(
nsIAsyncShutdownClient* aClient) {
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = WaitForInitialization();
NS_ENSURE_SUCCESS(rv, rv);
MonitorAutoLock lock(mMonitor);
mShuttingDown.Flip();
if (mMemoryDatabaseConnection) {
Unused << mMemoryDatabaseConnection->Close();
mMemoryDatabaseConnection = nullptr;
}
RefPtr<IdentityCredentialStorageService> self = this;
mBackgroundThread->Dispatch(
NS_NewRunnableFunction(
"IdentityCredentialStorageService::BlockShutdown",
[self]() {
MonitorAutoLock lock(self->mMonitor);
MOZ_ASSERT(self->mPendingWrites == 0);
if (self->mDiskDatabaseConnection) {
Unused << self->mDiskDatabaseConnection->Close();
self->mDiskDatabaseConnection = nullptr;
}
self->mFinalized.Flip();
self->mMonitor.NotifyAll();
NS_DispatchToMainThread(NS_NewRunnableFunction(
"IdentityCredentialStorageService::BlockShutdown "
"- mainthread callback",
[self]() { self->Finalize(); }));
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP
IdentityCredentialStorageService::GetState(nsIPropertyBag** aBagOut) {
return NS_OK;
}
already_AddRefed<nsIAsyncShutdownClient>
IdentityCredentialStorageService::GetAsyncShutdownBarrier() const {
nsresult rv;
nsCOMPtr<nsIAsyncShutdownService> svc = components::AsyncShutdown::Service();
MOZ_RELEASE_ASSERT(svc);
nsCOMPtr<nsIAsyncShutdownClient> client;
rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
MOZ_RELEASE_ASSERT(client);
return client.forget();
}
nsresult IdentityCredentialStorageService::Init() {
AssertIsOnMainThread();
nsCOMPtr<nsIAsyncShutdownClient> asc = GetAsyncShutdownBarrier();
if (!asc) {
return NS_ERROR_NOT_AVAILABLE;
}
// We should only allow this service to start before its
// shutdown barrier is closed, so it never leaks.
bool closed;
nsresult rv = asc->GetIsClosed(&closed);
if (closed || NS_WARN_IF(NS_FAILED(rv))) {
MonitorAutoLock lock(mMonitor);
mShuttingDown.Flip();
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
rv = asc->AddBlocker(this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
u""_ns);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(mDatabaseFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDatabaseFile->AppendNative(ACCOUNT_STATE_FILENAME);
NS_ENSURE_SUCCESS(rv, rv);
// Register the PBMode cleaner (IdentityCredentialStorageService::Observe) as
// an observer.
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
NS_ENSURE_TRUE(observerService, NS_ERROR_FAILURE);
observerService->AddObserver(this, "last-pb-context-exited", false);
rv = GetMemoryDatabaseConnection();
if (NS_WARN_IF(NS_FAILED(rv))) {
MonitorAutoLock lock(mMonitor);
mErrored.Flip();
return rv;
}
NS_ENSURE_SUCCESS(
NS_CreateBackgroundTaskQueue("IdentityCredentialStorage",
getter_AddRefs(mBackgroundThread)),
NS_ERROR_FAILURE);
RefPtr<IdentityCredentialStorageService> self = this;
mBackgroundThread->Dispatch(
NS_NewRunnableFunction("IdentityCredentialStorageService::Init",
[self]() {
MonitorAutoLock lock(self->mMonitor);
nsresult rv = self->GetDiskDatabaseConnection();
if (NS_WARN_IF(NS_FAILED(rv))) {
self->mErrored.Flip();
self->mMonitor.Notify();
return;
}
rv = self->LoadMemoryTableFromDisk();
if (NS_WARN_IF(NS_FAILED(rv))) {
self->mErrored.Flip();
self->mMonitor.Notify();
return;
}
self->mInitialized.Flip();
self->mMonitor.Notify();
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
nsresult IdentityCredentialStorageService::WaitForInitialization() {
MOZ_ASSERT(NS_IsMainThread(),
"Must only wait for initialization in the main thread.");
MonitorAutoLock lock(mMonitor);
while (!mInitialized && !mErrored && !mShuttingDown) {
mMonitor.Wait();
}
if (mErrored) {
return NS_ERROR_FAILURE;
}
if (mShuttingDown) {
return NS_ERROR_NOT_AVAILABLE;
}
return NS_OK;
}
void IdentityCredentialStorageService::Finalize() {
nsCOMPtr<nsIAsyncShutdownClient> asc = GetAsyncShutdownBarrier();
MOZ_ASSERT(asc);
DebugOnly<nsresult> rv = asc->RemoveBlocker(this);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// static
nsresult IdentityCredentialStorageService::ValidatePrincipal(
nsIPrincipal* aPrincipal) {
// We add some constraints on the RP principal where it is provided to reduce
// edge cases in implementation. These are reasonable constraints with the
// semantics of the store: it must be a http or https content principal.
NS_ENSURE_ARG_POINTER(aPrincipal);
NS_ENSURE_TRUE(aPrincipal->GetIsContentPrincipal(), NS_ERROR_FAILURE);
nsCString scheme;
nsresult rv = aPrincipal->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(scheme.Equals("http"_ns) || scheme.Equals("https"_ns),
NS_ERROR_FAILURE);
return NS_OK;
}
nsresult IdentityCredentialStorageService::GetMemoryDatabaseConnection() {
return IdentityCredentialStorageService::GetDatabaseConnectionInternal(
getter_AddRefs(mMemoryDatabaseConnection), nullptr);
}
nsresult IdentityCredentialStorageService::GetDiskDatabaseConnection() {
NS_ENSURE_TRUE(mDatabaseFile, NS_ERROR_NULL_POINTER);
return IdentityCredentialStorageService::GetDatabaseConnectionInternal(
getter_AddRefs(mDiskDatabaseConnection), mDatabaseFile);
}
// static
nsresult IdentityCredentialStorageService::GetDatabaseConnectionInternal(
mozIStorageConnection** aDatabase, nsIFile* aFile) {
NS_ENSURE_TRUE(aDatabase, NS_ERROR_UNEXPECTED);
NS_ENSURE_STATE(!(*aDatabase));
nsCOMPtr<mozIStorageService> storage =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
nsresult rv;
if (aFile) {
rv = storage->OpenDatabase(aFile, mozIStorageService::CONNECTION_DEFAULT,
aDatabase);
if (rv == NS_ERROR_FILE_CORRUPTED) {
rv = aFile->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
rv = storage->OpenDatabase(aFile, mozIStorageService::CONNECTION_DEFAULT,
aDatabase);
}
NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = storage->OpenSpecialDatabase(
kMozStorageMemoryStorageKey, "icsprivatedb"_ns,
mozIStorageService::CONNECTION_DEFAULT, aDatabase);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ENSURE_TRUE(*aDatabase, NS_ERROR_UNEXPECTED);
bool ready = false;
(*aDatabase)->GetConnectionReady(&ready);
NS_ENSURE_TRUE(ready, NS_ERROR_UNEXPECTED);
rv = EnsureTable(*aDatabase);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// static
nsresult IdentityCredentialStorageService::EnsureTable(
mozIStorageConnection* aDatabase) {
NS_ENSURE_ARG_POINTER(aDatabase);
bool tableExists = false;
aDatabase->TableExists("identity"_ns, &tableExists);
if (!tableExists) {
// Currently there is only one schema version, so we just need to create the
// table. The definition uses no explicit rowid column, instead primary
// keying on the tuple defined in the spec. We store two bits and some
// additional data to make integration with the ClearDataService
// easier/possible.
nsresult rv = aDatabase->SetSchemaVersion(SCHEMA_VERSION);
NS_ENSURE_SUCCESS(rv, rv);
rv = aDatabase->ExecuteSimpleSQL(
"CREATE TABLE identity ("
"rpOrigin TEXT NOT NULL"
",idpOrigin TEXT NOT NULL"
",credentialId TEXT NOT NULL"
",registered INTEGER"
",allowLogout INTEGER"
",modificationTime INTEGER"
",rpBaseDomain TEXT"
",PRIMARY KEY (rpOrigin, idpOrigin, credentialId)"
")"_ns);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult IdentityCredentialStorageService::LoadMemoryTableFromDisk() {
MOZ_ASSERT(!NS_IsMainThread(),
"Must not load the table from disk in the main thread.");
auto constexpr selectAllQuery =
"SELECT rpOrigin, idpOrigin, credentialId, registered, allowLogout, "
"modificationTime, rpBaseDomain FROM identity;"_ns;
auto constexpr insertQuery =
"INSERT INTO identity(rpOrigin, idpOrigin, credentialId, registered, "
"allowLogout, modificationTime, rpBaseDomain) VALUES (?1, ?2, ?3, ?4, "
"?5, ?6, ?7);"_ns;
nsCOMPtr<mozIStorageStatement> writeStmt;
nsresult rv = mMemoryDatabaseConnection->CreateStatement(
insertQuery, getter_AddRefs(writeStmt));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> readStmt;
rv = mDiskDatabaseConnection->CreateStatement(selectAllQuery,
getter_AddRefs(readStmt));
NS_ENSURE_SUCCESS(rv, rv);
bool hasResult;
while (NS_SUCCEEDED(readStmt->ExecuteStep(&hasResult)) && hasResult) {
int64_t registered, allowLogout, modificationTime;
nsCString rpOrigin, idpOrigin, credentialID, rpBaseDomain;
// Read values from disk query
rv = readStmt->GetUTF8String(0, rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = readStmt->GetUTF8String(1, idpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = readStmt->GetUTF8String(2, credentialID);
NS_ENSURE_SUCCESS(rv, rv);
rv = readStmt->GetInt64(3, &registered);
NS_ENSURE_SUCCESS(rv, rv);
rv = readStmt->GetInt64(4, &allowLogout);
NS_ENSURE_SUCCESS(rv, rv);
rv = readStmt->GetInt64(5, &modificationTime);
NS_ENSURE_SUCCESS(rv, rv);
rv = readStmt->GetUTF8String(6, rpBaseDomain);
NS_ENSURE_SUCCESS(rv, rv);
// Write values to memory database
rv = writeStmt->BindUTF8StringByIndex(0, rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = writeStmt->BindUTF8StringByIndex(1, idpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = writeStmt->BindUTF8StringByIndex(2, credentialID);
NS_ENSURE_SUCCESS(rv, rv);
rv = writeStmt->BindInt64ByIndex(3, registered);
NS_ENSURE_SUCCESS(rv, rv);
rv = writeStmt->BindInt64ByIndex(4, allowLogout);
NS_ENSURE_SUCCESS(rv, rv);
rv = writeStmt->BindInt64ByIndex(5, modificationTime);
NS_ENSURE_SUCCESS(rv, rv);
rv = writeStmt->BindUTF8StringByIndex(6, rpBaseDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = writeStmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
void IdentityCredentialStorageService::IncrementPendingWrites() {
MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mPendingWrites < std::numeric_limits<uint32_t>::max());
mPendingWrites++;
}
void IdentityCredentialStorageService::DecrementPendingWrites() {
MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mPendingWrites > 0);
mPendingWrites--;
}
// static
nsresult IdentityCredentialStorageService::UpsertData(
mozIStorageConnection* aDatabaseConnection, nsIPrincipal* aRPPrincipal,
nsIPrincipal* aIDPPrincipal, nsACString const& aCredentialID,
bool aRegistered, bool aAllowLogout) {
NS_ENSURE_ARG_POINTER(aDatabaseConnection);
NS_ENSURE_ARG_POINTER(aRPPrincipal);
NS_ENSURE_ARG_POINTER(aIDPPrincipal);
nsresult rv;
constexpr auto upsert_query =
"INSERT INTO identity(rpOrigin, idpOrigin, credentialId, "
"registered, allowLogout, modificationTime, rpBaseDomain)"
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"
"ON CONFLICT(rpOrigin, idpOrigin, credentialId)"
"DO UPDATE SET registered=excluded.registered, "
"allowLogout=excluded.allowLogout, "
"modificationTime=excluded.modificationTime"_ns;
nsCOMPtr<mozIStorageStatement> stmt;
rv = aDatabaseConnection->CreateStatement(upsert_query, getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
nsCString rpOrigin;
rv = aRPPrincipal->GetOrigin(rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
nsCString idpOrigin;
rv = aIDPPrincipal->GetOrigin(idpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
nsCString rpBaseDomain;
rv = aRPPrincipal->GetBaseDomain(rpBaseDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(0, rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(1, idpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(2, aCredentialID);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByIndex(3, aRegistered ? 1 : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByIndex(4, aAllowLogout ? 1 : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByIndex(5, MODIFIED_NOW);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(6, rpBaseDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// static
nsresult IdentityCredentialStorageService::DeleteData(
mozIStorageConnection* aDatabaseConnection, nsIPrincipal* aRPPrincipal,
nsIPrincipal* aIDPPrincipal, nsACString const& aCredentialID) {
NS_ENSURE_ARG_POINTER(aDatabaseConnection);
NS_ENSURE_ARG_POINTER(aRPPrincipal);
NS_ENSURE_ARG_POINTER(aIDPPrincipal);
auto constexpr deleteQuery =
"DELETE FROM identity WHERE rpOrigin=?1 AND idpOrigin=?2 AND "
"credentialId=?3"_ns;
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv =
aDatabaseConnection->CreateStatement(deleteQuery, getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
nsCString rpOrigin;
rv = aRPPrincipal->GetOrigin(rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
nsCString idpOrigin;
rv = aIDPPrincipal->GetOrigin(idpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(0, rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(1, idpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(2, aCredentialID);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// static
nsresult IdentityCredentialStorageService::ClearData(
mozIStorageConnection* aDatabaseConnection) {
NS_ENSURE_ARG_POINTER(aDatabaseConnection);
nsresult rv =
aDatabaseConnection->ExecuteSimpleSQL("DELETE FROM identity;"_ns);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// static
nsresult
IdentityCredentialStorageService::DeleteDataFromOriginAttributesPattern(
mozIStorageConnection* aDatabaseConnection,
OriginAttributesPattern const& aOriginAttributesPattern) {
NS_ENSURE_ARG_POINTER(aDatabaseConnection);
nsCOMPtr<mozIStorageFunction> patternMatchFunction(
new OriginAttrsPatternMatchOriginSQLFunction(aOriginAttributesPattern));
nsresult rv = aDatabaseConnection->CreateFunction(
"ORIGIN_ATTRS_PATTERN_MATCH_ORIGIN"_ns, 1, patternMatchFunction);
NS_ENSURE_SUCCESS(rv, rv);
rv = aDatabaseConnection->ExecuteSimpleSQL(
"DELETE FROM identity WHERE "
"ORIGIN_ATTRS_PATTERN_MATCH_ORIGIN(rpOrigin);"_ns);
NS_ENSURE_SUCCESS(rv, rv);
rv = aDatabaseConnection->RemoveFunction(
"ORIGIN_ATTRS_PATTERN_MATCH_ORIGIN"_ns);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// static
nsresult IdentityCredentialStorageService::DeleteDataFromTimeRange(
mozIStorageConnection* aDatabaseConnection, int64_t aStart, int64_t aEnd) {
NS_ENSURE_ARG_POINTER(aDatabaseConnection);
auto constexpr deleteTimeQuery =
"DELETE FROM identity WHERE modificationTime > ?1 and modificationTime "
"< ?2"_ns;
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = aDatabaseConnection->CreateStatement(deleteTimeQuery,
getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByIndex(0, aStart);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByIndex(1, aEnd);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// static
nsresult IdentityCredentialStorageService::DeleteDataFromPrincipal(
mozIStorageConnection* aDatabaseConnection, nsIPrincipal* aPrincipal) {
NS_ENSURE_ARG_POINTER(aDatabaseConnection);
NS_ENSURE_ARG_POINTER(aPrincipal);
nsCString rpOrigin;
nsresult rv = aPrincipal->GetOrigin(rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
auto constexpr deletePrincipalQuery =
"DELETE FROM identity WHERE rpOrigin=?1"_ns;
nsCOMPtr<mozIStorageStatement> stmt;
rv = aDatabaseConnection->CreateStatement(deletePrincipalQuery,
getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(0, rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// static
nsresult IdentityCredentialStorageService::DeleteDataFromBaseDomain(
mozIStorageConnection* aDatabaseConnection, nsACString const& aBaseDomain) {
NS_ENSURE_ARG_POINTER(aDatabaseConnection);
auto constexpr deleteBaseDomainQuery =
"DELETE FROM identity WHERE rpBaseDomain=?1"_ns;
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = aDatabaseConnection->CreateStatement(deleteBaseDomainQuery,
getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(0, aBaseDomain);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP IdentityCredentialStorageService::SetState(
nsIPrincipal* aRPPrincipal, nsIPrincipal* aIDPPrincipal,
nsACString const& aCredentialID, bool aRegistered, bool aAllowLogout) {
AssertIsOnMainThread();
NS_ENSURE_ARG_POINTER(aRPPrincipal);
NS_ENSURE_ARG_POINTER(aIDPPrincipal);
nsresult rv;
rv = WaitForInitialization();
NS_ENSURE_SUCCESS(rv, rv);
rv = IdentityCredentialStorageService::ValidatePrincipal(aRPPrincipal);
NS_ENSURE_SUCCESS(rv, rv);
rv = UpsertData(mMemoryDatabaseConnection, aRPPrincipal, aIDPPrincipal,
aCredentialID, aRegistered, aAllowLogout);
NS_ENSURE_SUCCESS(rv, rv);
IncrementPendingWrites();
RefPtr<IdentityCredentialStorageService> self = this;
RefPtr<nsIPrincipal> rpPrincipal = aRPPrincipal;
RefPtr<nsIPrincipal> idpPrincipal = aIDPPrincipal;
nsCString credentialID(aCredentialID);
mBackgroundThread->Dispatch(
NS_NewRunnableFunction("IdentityCredentialStorageService::Init",
[self, rpPrincipal, idpPrincipal, credentialID,
aRegistered, aAllowLogout]() {
nsresult rv = UpsertData(
self->mDiskDatabaseConnection, rpPrincipal,
idpPrincipal, credentialID, aRegistered,
aAllowLogout);
NS_ENSURE_SUCCESS_VOID(rv);
self->DecrementPendingWrites();
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP IdentityCredentialStorageService::GetState(
nsIPrincipal* aRPPrincipal, nsIPrincipal* aIDPPrincipal,
nsACString const& aCredentialID, bool* aRegistered, bool* aAllowLogout) {
AssertIsOnMainThread();
NS_ENSURE_ARG_POINTER(aRPPrincipal);
NS_ENSURE_ARG_POINTER(aIDPPrincipal);
nsresult rv;
rv = WaitForInitialization();
NS_ENSURE_SUCCESS(rv, rv);
rv = IdentityCredentialStorageService::ValidatePrincipal(aRPPrincipal);
NS_ENSURE_SUCCESS(rv, rv);
auto constexpr selectQuery =
"SELECT registered, allowLogout FROM identity WHERE rpOrigin=?1 AND "
"idpOrigin=?2 AND credentialId=?3"_ns;
nsCOMPtr<mozIStorageStatement> stmt;
rv = mMemoryDatabaseConnection->CreateStatement(selectQuery,
getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
nsCString rpOrigin;
nsCString idpOrigin;
rv = aRPPrincipal->GetOrigin(rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = aIDPPrincipal->GetOrigin(idpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(0, rpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(1, idpOrigin);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindUTF8StringByIndex(2, aCredentialID);
NS_ENSURE_SUCCESS(rv, rv);
bool hasResult;
// If we find a result, return it
if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
int64_t registeredInt, allowLogoutInt;
rv = stmt->GetInt64(0, &registeredInt);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->GetInt64(1, &allowLogoutInt);
NS_ENSURE_SUCCESS(rv, rv);
*aRegistered = registeredInt != 0;
*aAllowLogout = allowLogoutInt != 0;
return NS_OK;
}
// The tuple was not found on disk or in memory, use the defaults.
*aRegistered = false;
*aAllowLogout = false;
return NS_OK;
}
NS_IMETHODIMP IdentityCredentialStorageService::Delete(
nsIPrincipal* aRPPrincipal, nsIPrincipal* aIDPPrincipal,
nsACString const& aCredentialID) {
AssertIsOnMainThread();
NS_ENSURE_ARG_POINTER(aRPPrincipal);
NS_ENSURE_ARG_POINTER(aIDPPrincipal);
nsresult rv;
rv = WaitForInitialization();
NS_ENSURE_SUCCESS(rv, rv);
rv = IdentityCredentialStorageService::ValidatePrincipal(aRPPrincipal);
NS_ENSURE_SUCCESS(rv, rv);
rv = DeleteData(mMemoryDatabaseConnection, aRPPrincipal, aIDPPrincipal,
aCredentialID);
NS_ENSURE_SUCCESS(rv, rv);
IncrementPendingWrites();
RefPtr<IdentityCredentialStorageService> self = this;
RefPtr<nsIPrincipal> rpPrincipal = aRPPrincipal;
RefPtr<nsIPrincipal> idpPrincipal = aIDPPrincipal;
nsCString credentialID(aCredentialID);
mBackgroundThread->Dispatch(
NS_NewRunnableFunction("IdentityCredentialStorageService::Init",
[self, rpPrincipal, idpPrincipal, credentialID]() {
nsresult rv = DeleteData(
self->mDiskDatabaseConnection, rpPrincipal,
idpPrincipal, credentialID);
NS_ENSURE_SUCCESS_VOID(rv);
self->DecrementPendingWrites();
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP IdentityCredentialStorageService::Clear() {
AssertIsOnMainThread();
nsresult rv;
rv = WaitForInitialization();
NS_ENSURE_SUCCESS(rv, rv);
rv = ClearData(mMemoryDatabaseConnection);
NS_ENSURE_SUCCESS(rv, rv);
IncrementPendingWrites();
RefPtr<IdentityCredentialStorageService> self = this;
mBackgroundThread->Dispatch(
NS_NewRunnableFunction("IdentityCredentialStorageService::Init",
[self]() {
nsresult rv =
ClearData(self->mDiskDatabaseConnection);
NS_ENSURE_SUCCESS_VOID(rv);
self->DecrementPendingWrites();
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP
IdentityCredentialStorageService::DeleteFromOriginAttributesPattern(
nsAString const& aOriginAttributesPattern) {
AssertIsOnMainThread();
NS_ENSURE_FALSE(aOriginAttributesPattern.IsEmpty(), NS_ERROR_FAILURE);
OriginAttributesPattern oaPattern;
if (!oaPattern.Init(aOriginAttributesPattern)) {
NS_ERROR("Could not parse the argument for OriginAttributes");
return NS_ERROR_FAILURE;
}
nsresult rv;
rv = WaitForInitialization();
NS_ENSURE_SUCCESS(rv, rv);
rv = DeleteDataFromOriginAttributesPattern(mMemoryDatabaseConnection,
oaPattern);
NS_ENSURE_SUCCESS(rv, rv);
IncrementPendingWrites();
RefPtr<IdentityCredentialStorageService> self = this;
mBackgroundThread->Dispatch(
NS_NewRunnableFunction(
"IdentityCredentialStorageService::Init",
[self, oaPattern]() {
nsresult rv = DeleteDataFromOriginAttributesPattern(
self->mDiskDatabaseConnection, oaPattern);
NS_ENSURE_SUCCESS_VOID(rv);
self->DecrementPendingWrites();
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP IdentityCredentialStorageService::DeleteFromTimeRange(
int64_t aStart, int64_t aEnd) {
AssertIsOnMainThread();
nsresult rv;
rv = WaitForInitialization();
NS_ENSURE_SUCCESS(rv, rv);
rv = DeleteDataFromTimeRange(mMemoryDatabaseConnection, aStart, aEnd);
NS_ENSURE_SUCCESS(rv, rv);
IncrementPendingWrites();
RefPtr<IdentityCredentialStorageService> self = this;
mBackgroundThread->Dispatch(
NS_NewRunnableFunction("IdentityCredentialStorageService::Init",
[self, aStart, aEnd]() {
nsresult rv = DeleteDataFromTimeRange(
self->mDiskDatabaseConnection, aStart, aEnd);
NS_ENSURE_SUCCESS_VOID(rv);
self->DecrementPendingWrites();
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP IdentityCredentialStorageService::
IdentityCredentialStorageService::DeleteFromPrincipal(
nsIPrincipal* aRPPrincipal) {
AssertIsOnMainThread();
NS_ENSURE_ARG_POINTER(aRPPrincipal);
nsresult rv =
IdentityCredentialStorageService::ValidatePrincipal(aRPPrincipal);
NS_ENSURE_SUCCESS(rv, rv);
rv = DeleteDataFromPrincipal(mMemoryDatabaseConnection, aRPPrincipal);
NS_ENSURE_SUCCESS(rv, rv);
IncrementPendingWrites();
RefPtr<IdentityCredentialStorageService> self = this;
RefPtr<nsIPrincipal> principal = aRPPrincipal;
mBackgroundThread->Dispatch(
NS_NewRunnableFunction("IdentityCredentialStorageService::Init",
[self, principal]() {
nsresult rv = DeleteDataFromPrincipal(
self->mDiskDatabaseConnection, principal);
NS_ENSURE_SUCCESS_VOID(rv);
self->DecrementPendingWrites();
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP IdentityCredentialStorageService::DeleteFromBaseDomain(
nsACString const& aBaseDomain) {
AssertIsOnMainThread();
nsresult rv;
rv = WaitForInitialization();
NS_ENSURE_SUCCESS(rv, rv);
rv = DeleteDataFromBaseDomain(mMemoryDatabaseConnection, aBaseDomain);
NS_ENSURE_SUCCESS(rv, rv);
IncrementPendingWrites();
RefPtr<IdentityCredentialStorageService> self = this;
nsCString baseDomain(aBaseDomain);
mBackgroundThread->Dispatch(
NS_NewRunnableFunction("IdentityCredentialStorageService::Init",
[self, baseDomain]() {
nsresult rv = DeleteDataFromBaseDomain(
self->mDiskDatabaseConnection, baseDomain);
NS_ENSURE_SUCCESS_VOID(rv);
self->DecrementPendingWrites();
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
return NS_OK;
}
NS_IMETHODIMP
IdentityCredentialStorageService::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData) {
AssertIsOnMainThread();
// Double check that we have the right topic.
if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) {
MonitorAutoLock lock(mMonitor);
if (mInitialized && mMemoryDatabaseConnection) {
nsCOMPtr<mozIStorageFunction> patternMatchFunction(
new PrivateBrowsingOriginSQLFunction());
nsresult rv = mMemoryDatabaseConnection->CreateFunction(
"PRIVATE_BROWSING_PATTERN_MATCH_ORIGIN"_ns, 1, patternMatchFunction);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMemoryDatabaseConnection->ExecuteSimpleSQL(
"DELETE FROM identity WHERE "
"PRIVATE_BROWSING_PATTERN_MATCH_ORIGIN(rpOrigin);"_ns);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMemoryDatabaseConnection->RemoveFunction(
"PRIVATE_BROWSING_PATTERN_MATCH_ORIGIN"_ns);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
NS_IMPL_ISUPPORTS(OriginAttrsPatternMatchOriginSQLFunction, mozIStorageFunction)
NS_IMETHODIMP
OriginAttrsPatternMatchOriginSQLFunction::OnFunctionCall(
mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
nsresult rv;
nsAutoCString origin;
rv = aFunctionArguments->GetUTF8String(0, origin);
NS_ENSURE_SUCCESS(rv, rv);
nsCString originNoSuffix;
OriginAttributes oa;
bool parsedSuccessfully = oa.PopulateFromOrigin(origin, originNoSuffix);
NS_ENSURE_TRUE(parsedSuccessfully, NS_ERROR_FAILURE);
bool result = mPattern.Matches(oa);
RefPtr<nsVariant> outVar(new nsVariant());
rv = outVar->SetAsBool(result);
NS_ENSURE_SUCCESS(rv, rv);
outVar.forget(aResult);
return NS_OK;
}
NS_IMPL_ISUPPORTS(PrivateBrowsingOriginSQLFunction, mozIStorageFunction)
NS_IMETHODIMP
PrivateBrowsingOriginSQLFunction::OnFunctionCall(
mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
nsresult rv;
nsAutoCString origin;
rv = aFunctionArguments->GetUTF8String(0, origin);
NS_ENSURE_SUCCESS(rv, rv);
bool result = OriginAttributes::IsPrivateBrowsing(origin);
RefPtr<nsVariant> outVar(new nsVariant());
rv = outVar->SetAsBool(result);
NS_ENSURE_SUCCESS(rv, rv);
outVar.forget(aResult);
return NS_OK;
}
} // namespace mozilla