diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /toolkit/components/credentialmanagement/IdentityCredentialStorageService.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/credentialmanagement/IdentityCredentialStorageService.cpp')
-rw-r--r-- | toolkit/components/credentialmanagement/IdentityCredentialStorageService.cpp | 887 |
1 files changed, 887 insertions, 0 deletions
diff --git a/toolkit/components/credentialmanagement/IdentityCredentialStorageService.cpp b/toolkit/components/credentialmanagement/IdentityCredentialStorageService.cpp new file mode 100644 index 0000000000..f3994d1a86 --- /dev/null +++ b/toolkit/components/credentialmanagement/IdentityCredentialStorageService.cpp @@ -0,0 +1,887 @@ +/* -*- 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(); + + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + MonitorAutoLock lock(mMonitor); + mShuttingDown.Flip(); + return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; + } + + nsCOMPtr<nsIAsyncShutdownClient> asc = GetAsyncShutdownBarrier(); + if (!asc) { + return NS_ERROR_NOT_AVAILABLE; + } + nsresult 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, ®istered); + 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, ®isteredInt); + 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 |