/* -*- 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/dom/BindingDeclarations.h" #include "mozilla/dom/IdentityCredential.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 2 #define MODIFIED_NOW PR_Now() namespace mozilla { StaticRefPtr gIdentityCredentialStorageService; NS_IMPL_ISUPPORTS(IdentityCredentialStorageService, nsIIdentityCredentialStorageService, nsIObserver, nsIAsyncShutdownBlocker) already_AddRefed IdentityCredentialStorageService::GetSingleton() { AssertIsOnMainThread(); MOZ_ASSERT(XRE_IsParentProcess()); if (!gIdentityCredentialStorageService) { gIdentityCredentialStorageService = new IdentityCredentialStorageService(); ClearOnShutdown(&gIdentityCredentialStorageService); nsresult rv = gIdentityCredentialStorageService->Init(); NS_ENSURE_SUCCESS(rv, nullptr); } RefPtr 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 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 IdentityCredentialStorageService::GetAsyncShutdownBarrier() const { nsresult rv; nsCOMPtr svc = components::AsyncShutdown::Service(); MOZ_RELEASE_ASSERT(svc); nsCOMPtr 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 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 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 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.NotifyAll(); return; } rv = self->LoadMemoryTableFromDisk(); if (NS_WARN_IF(NS_FAILED(rv))) { self->mErrored.Flip(); self->mMonitor.NotifyAll(); return; } self->mInitialized.Flip(); self->mMonitor.NotifyAll(); }), 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 asc = GetAsyncShutdownBarrier(); MOZ_ASSERT(asc); DebugOnly 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, false); } nsresult IdentityCredentialStorageService::GetDiskDatabaseConnection() { NS_ENSURE_TRUE(mDatabaseFile, NS_ERROR_NULL_POINTER); return IdentityCredentialStorageService::GetDatabaseConnectionInternal( getter_AddRefs(mDiskDatabaseConnection), mDatabaseFile, true); } // static nsresult IdentityCredentialStorageService::GetDatabaseConnectionInternal( mozIStorageConnection** aDatabase, nsIFile* aFile, bool aRetry) { NS_ENSURE_TRUE(aDatabase, NS_ERROR_UNEXPECTED); NS_ENSURE_STATE(!(*aDatabase)); nsCOMPtr 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); if (!ready && aRetry) { rv = aFile->Remove(false); NS_ENSURE_SUCCESS(rv, rv); return IdentityCredentialStorageService::GetDatabaseConnectionInternal( aDatabase, aFile, false); } else { 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); } tableExists = false; aDatabase->TableExists("lightweight_identity"_ns, &tableExists); if (!tableExists) { // The definition uses no explicit rowid column, instead primary // keying on the tuple defined in the spec. nsresult rv = aDatabase->SetSchemaVersion(SCHEMA_VERSION); NS_ENSURE_SUCCESS(rv, rv); rv = aDatabase->ExecuteSimpleSQL( "CREATE TABLE lightweight_identity (" "idpOrigin TEXT NOT NULL" ",credentialId TEXT NOT NULL" ",name TEXT" ",iconDataURL TEXT" ",originAllowlist TEXT" ",dynamicAllowEndpoint TEXT" ",userDataExpireTime INTEGER" ",token TEXT" ",effectiveType TEXT" ",modificationTime INTEGER" ",idpBaseDomain TEXT" ",PRIMARY KEY (idpOrigin, credentialId)" ")"_ns); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } // Ensure the schema is up to date if the table already exists int32_t schema; nsresult rv = aDatabase->GetSchemaVersion(&schema); NS_ENSURE_SUCCESS(rv, rv); switch (schema) { case 1: rv = aDatabase->ExecuteSimpleSQL( "ALTER TABLE lightweight_identity ADD COLUMN token TEXT;"_ns); NS_ENSURE_SUCCESS(rv, rv); rv = aDatabase->ExecuteSimpleSQL( "ALTER TABLE lightweight_identity ADD COLUMN effectiveType TEXT;"_ns); NS_ENSURE_SUCCESS(rv, rv); [[fallthrough]]; default: rv = aDatabase->SetSchemaVersion(SCHEMA_VERSION); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } nsresult IdentityCredentialStorageService::LoadLightweightMemoryTableFromDisk() { MOZ_ASSERT(!NS_IsMainThread(), "Must not load the table from disk in the main thread."); auto constexpr selectAllQuery = "SELECT idpOrigin, credentialId, name, iconDataURL, " "originAllowlist, dynamicAllowEndpoint, userDataExpireTime," "token, effectiveType," "modificationTime, idpBaseDomain FROM lightweight_identity;"_ns; auto constexpr insertQuery = "INSERT INTO lightweight_identity(idpOrigin, credentialId, " "name, iconDataURL, originAllowlist, dynamicAllowEndpoint, " "userDataExpireTime, token, effectiveType," "modificationTime, idpBaseDomain) VALUES (:idpOrigin, :credentialId, " ":name, :iconDataURL, :originAllowlist, :dynamicAllowEndpoint, " ":userDataExpireTime," ":token, :effectiveType, :modificationTime, :idpBaseDomain);"_ns; nsCOMPtr writeStmt; nsresult rv = mMemoryDatabaseConnection->CreateStatement( insertQuery, getter_AddRefs(writeStmt)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr readStmt; rv = mDiskDatabaseConnection->CreateStatement(selectAllQuery, getter_AddRefs(readStmt)); NS_ENSURE_SUCCESS(rv, rv); bool hasResult; while (NS_SUCCEEDED(readStmt->ExecuteStep(&hasResult)) && hasResult) { nsCOMPtr modificationTime, userDataExpireTime, idpOrigin, credentialId, idpBaseDomain, name, iconDataURL, originAllowlist, dynamicAllowEndpoint, token, effectiveType; // Read values from disk query rv = readStmt->GetVariant(0, getter_AddRefs(idpOrigin)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(1, getter_AddRefs(credentialId)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(2, getter_AddRefs(name)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(3, getter_AddRefs(iconDataURL)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(4, getter_AddRefs(originAllowlist)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(5, getter_AddRefs(dynamicAllowEndpoint)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(6, getter_AddRefs(userDataExpireTime)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(7, getter_AddRefs(token)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(8, getter_AddRefs(effectiveType)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(9, getter_AddRefs(modificationTime)); NS_ENSURE_SUCCESS(rv, rv); rv = readStmt->GetVariant(10, getter_AddRefs(idpBaseDomain)); NS_ENSURE_SUCCESS(rv, rv); // Write values to memory database rv = writeStmt->BindByName("idpOrigin"_ns, idpOrigin); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("credentialId"_ns, credentialId); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("name"_ns, name); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("iconDataURL"_ns, iconDataURL); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("originAllowlist"_ns, originAllowlist); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("dynamicAllowEndpoint"_ns, dynamicAllowEndpoint); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("userDataExpireTime"_ns, userDataExpireTime); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("token"_ns, token); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("effectiveType"_ns, effectiveType); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("modificationTime"_ns, modificationTime); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->BindByName("idpBaseDomain"_ns, idpBaseDomain); NS_ENSURE_SUCCESS(rv, rv); rv = writeStmt->Execute(); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } nsresult IdentityCredentialStorageService::LoadHeavyweightMemoryTableFromDisk() { 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 writeStmt; nsresult rv = mMemoryDatabaseConnection->CreateStatement( insertQuery, getter_AddRefs(writeStmt)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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; } nsresult IdentityCredentialStorageService::LoadMemoryTableFromDisk() { nsresult rv = LoadHeavyweightMemoryTableFromDisk(); NS_ENSURE_SUCCESS(rv, rv); return LoadLightweightMemoryTableFromDisk(); } void IdentityCredentialStorageService::IncrementPendingWrites() { MonitorAutoLock lock(mMonitor); MOZ_ASSERT(mPendingWrites < std::numeric_limits::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 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 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::DisconnectData( mozIStorageConnection* aDatabaseConnection, nsIPrincipal* aRPPrincipal, nsIPrincipal* aIDPPrincipal) { 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"_ns; nsCOMPtr 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->Execute(); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } // static nsresult IdentityCredentialStorageService::UpsertLightweightData( mozIStorageConnection* aDatabaseConnection, const dom::IPCIdentityCredential& aData) { NS_ENSURE_ARG_POINTER(aDatabaseConnection); NS_ENSURE_ARG_POINTER(aData.identityProvider()); nsresult rv; constexpr auto upsertQuery = "INSERT INTO lightweight_identity(idpOrigin, credentialId, " "name, iconDataURL, originAllowlist, dynamicAllowEndpoint, " "userDataExpireTime, token, effectiveType," "modificationTime, idpBaseDomain) VALUES (:idpOrigin, :credentialId, " ":name, " ":iconDataURL, :originAllowlist, :dynamicAllowEndpoint, " ":userDataExpireTime, :token, :effectiveType," ":modificationTime, :idpBaseDomain)" "ON CONFLICT(idpOrigin, credentialId)" "DO UPDATE SET name=excluded.name, " "iconDataURL=excluded.iconDataURL, " "originAllowlist=excluded.originAllowlist, " "dynamicAllowEndpoint=excluded.dynamicAllowEndpoint, " "userDataExpireTime=excluded.userDataExpireTime, " "token=excluded.token, " "effectiveType=excluded.effectiveType, " "modificationTime=excluded.modificationTime"_ns; nsCOMPtr stmt; rv = aDatabaseConnection->CreateStatement(upsertQuery, getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); nsCString idpOrigin; rv = aData.identityProvider()->GetOrigin(idpOrigin); NS_ENSURE_SUCCESS(rv, rv); nsCString idpBaseDomain; rv = aData.identityProvider()->GetBaseDomain(idpBaseDomain); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindUTF8StringByName("idpOrigin"_ns, idpOrigin); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindUTF8StringByName("credentialId"_ns, NS_ConvertUTF16toUTF8(aData.id())); NS_ENSURE_SUCCESS(rv, rv); if (aData.name().isSome()) { rv = stmt->BindUTF8StringByName("name"_ns, aData.name().value()); } else { rv = stmt->BindNullByName("name"_ns); } NS_ENSURE_SUCCESS(rv, rv); if (aData.iconURL().isSome()) { rv = stmt->BindUTF8StringByName("iconDataURL"_ns, aData.iconURL().value()); } else { rv = stmt->BindNullByName("iconDataURL"_ns); } NS_ENSURE_SUCCESS(rv, rv); if (aData.effectiveOrigins().Length()) { rv = stmt->BindUTF8StringByName( "originAllowlist"_ns, StringJoin("|"_ns, aData.effectiveOrigins())); } else { rv = stmt->BindNullByName("originAllowlist"_ns); } NS_ENSURE_SUCCESS(rv, rv); if (aData.effectiveQueryURL().isSome()) { rv = stmt->BindUTF8StringByName("dynamicAllowEndpoint"_ns, aData.effectiveQueryURL().value()); } else { rv = stmt->BindNullByName("dynamicAllowEndpoint"_ns); } NS_ENSURE_SUCCESS(rv, rv); if (aData.effectiveType().isSome()) { rv = stmt->BindUTF8StringByName("effectiveType"_ns, aData.effectiveType().value()); } else { rv = stmt->BindNullByName("effectiveType"_ns); } NS_ENSURE_SUCCESS(rv, rv); if (aData.token().isSome()) { rv = stmt->BindUTF8StringByName( "token"_ns, NS_ConvertUTF16toUTF8(aData.token().value())); } else { rv = stmt->BindNullByName("token"_ns); } NS_ENSURE_SUCCESS(rv, rv); if (aData.infoExpiresAt().isSome() && aData.infoExpiresAt().value() <= INT64_MAX) { rv = stmt->BindInt64ByName( "userDataExpireTime"_ns, static_cast(aData.infoExpiresAt().value())); } else { rv = stmt->BindNullByName("userDataExpireTime"_ns); } NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindInt64ByName("modificationTime"_ns, MODIFIED_NOW); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindUTF8StringByName("idpBaseDomain"_ns, idpBaseDomain); NS_ENSURE_SUCCESS(rv, rv); return stmt->Execute(); } // static nsresult IdentityCredentialStorageService::DeleteLightweightData( mozIStorageConnection* aDatabaseConnection, const dom::IPCIdentityCredential& aData) { NS_ENSURE_ARG_POINTER(aDatabaseConnection); NS_ENSURE_ARG_POINTER(aData.identityProvider()); nsresult rv; constexpr auto deleteQuery = "DELETE FROM lightweight_identity WHERE" "idpOrigin = ?2 AND credentialId = ?3;"_ns; nsCOMPtr stmt; rv = aDatabaseConnection->CreateStatement(deleteQuery, getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); nsCString idpOrigin; rv = aData.identityProvider()->GetOrigin(idpOrigin); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindUTF8StringByName("idpOrigin"_ns, idpOrigin); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindUTF8StringByName("credentialId"_ns, NS_ConvertUTF16toUTF8(aData.id())); NS_ENSURE_SUCCESS(rv, rv); return stmt->Execute(); } // static nsresult IdentityCredentialStorageService::ClearData( mozIStorageConnection* aDatabaseConnection) { NS_ENSURE_ARG_POINTER(aDatabaseConnection); nsresult rv = aDatabaseConnection->ExecuteSimpleSQL("DELETE FROM identity;"_ns); NS_ENSURE_SUCCESS(rv, rv); rv = aDatabaseConnection->ExecuteSimpleSQL( "DELETE FROM lightweight_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 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->ExecuteSimpleSQL( "DELETE FROM lightweight_identity WHERE " "ORIGIN_ATTRS_PATTERN_MATCH_ORIGIN(idpOrigin);"_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 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); auto constexpr deleteTimeQueryLightweight = "DELETE FROM lightweight_identity WHERE modificationTime > ?1 and " "modificationTime " "< ?2"_ns; nsCOMPtr stmtLightweight; rv = aDatabaseConnection->CreateStatement(deleteTimeQueryLightweight, getter_AddRefs(stmtLightweight)); NS_ENSURE_SUCCESS(rv, rv); rv = stmtLightweight->BindInt64ByIndex(0, aStart); NS_ENSURE_SUCCESS(rv, rv); rv = stmtLightweight->BindInt64ByIndex(1, aEnd); NS_ENSURE_SUCCESS(rv, rv); return stmtLightweight->Execute(); } // static nsresult IdentityCredentialStorageService::DeleteDataFromPrincipal( mozIStorageConnection* aDatabaseConnection, nsIPrincipal* aPrincipal) { NS_ENSURE_ARG_POINTER(aDatabaseConnection); NS_ENSURE_ARG_POINTER(aPrincipal); nsCString origin; nsresult rv = aPrincipal->GetOrigin(origin); NS_ENSURE_SUCCESS(rv, rv); auto constexpr deletePrincipalQuery = "DELETE FROM identity WHERE rpOrigin=?1"_ns; nsCOMPtr stmt; rv = aDatabaseConnection->CreateStatement(deletePrincipalQuery, getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindUTF8StringByIndex(0, origin); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->Execute(); NS_ENSURE_SUCCESS(rv, rv); auto constexpr deletePrincipalQueryLightweight = "DELETE FROM lightweight_identity WHERE idpOrigin=:idpOrigin"_ns; nsCOMPtr stmtLightweight; rv = aDatabaseConnection->CreateStatement(deletePrincipalQueryLightweight, getter_AddRefs(stmtLightweight)); NS_ENSURE_SUCCESS(rv, rv); rv = stmtLightweight->BindUTF8StringByName("idpOrigin"_ns, origin); NS_ENSURE_SUCCESS(rv, rv); return stmtLightweight->Execute(); } // 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 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); auto constexpr deleteBaseDomainQueryLightweight = "DELETE FROM lightweight_identity WHERE idpBaseDomain=?1"_ns; nsCOMPtr stmtLightweight; rv = aDatabaseConnection->CreateStatement(deleteBaseDomainQueryLightweight, getter_AddRefs(stmtLightweight)); NS_ENSURE_SUCCESS(rv, rv); rv = stmtLightweight->BindUTF8StringByIndex(0, aBaseDomain); NS_ENSURE_SUCCESS(rv, rv); return stmtLightweight->Execute(); } 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 self = this; RefPtr rpPrincipal = aRPPrincipal; RefPtr 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); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), 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 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 self = this; RefPtr rpPrincipal = aRPPrincipal; RefPtr idpPrincipal = aIDPPrincipal; nsCString credentialID(aCredentialID); mBackgroundThread->Dispatch( NS_NewRunnableFunction("IdentityCredentialStorageService::Init", [self, rpPrincipal, idpPrincipal, credentialID]() { nsresult rv = DeleteData( self->mDiskDatabaseConnection, rpPrincipal, idpPrincipal, credentialID); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), NS_DISPATCH_EVENT_MAY_BLOCK); return NS_OK; } NS_IMETHODIMP IdentityCredentialStorageService::Connected( nsIPrincipal* aRPPrincipal, nsIPrincipal* aIDPPrincipal, bool* aConnected) { AssertIsOnMainThread(); NS_ENSURE_ARG_POINTER(aRPPrincipal); NS_ENSURE_ARG_POINTER(aIDPPrincipal); *aConnected = false; nsresult rv; rv = WaitForInitialization(); NS_ENSURE_SUCCESS(rv, rv); rv = IdentityCredentialStorageService::ValidatePrincipal(aRPPrincipal); NS_ENSURE_SUCCESS(rv, rv); rv = IdentityCredentialStorageService::ValidatePrincipal(aIDPPrincipal); NS_ENSURE_SUCCESS(rv, rv); auto constexpr selectQuery = "SELECT registered FROM identity WHERE rpOrigin=?1 AND " "idpOrigin=?2"_ns; nsCOMPtr 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); bool hasResult; // If we find a result, return it if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { int64_t registeredInt; rv = stmt->GetInt64(0, ®isteredInt); NS_ENSURE_SUCCESS(rv, rv); *aConnected = true; return NS_OK; } *aConnected = false; return NS_OK; } NS_IMETHODIMP IdentityCredentialStorageService::Disconnect( nsIPrincipal* aRPPrincipal, nsIPrincipal* aIDPPrincipal) { 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 = IdentityCredentialStorageService::ValidatePrincipal(aIDPPrincipal); NS_ENSURE_SUCCESS(rv, rv); rv = DisconnectData(mMemoryDatabaseConnection, aRPPrincipal, aIDPPrincipal); NS_ENSURE_SUCCESS(rv, rv); IncrementPendingWrites(); RefPtr self = this; RefPtr rpPrincipal = aRPPrincipal; RefPtr idpPrincipal = aIDPPrincipal; mBackgroundThread->Dispatch( NS_NewRunnableFunction("IdentityCredentialStorageService::Init", [self, rpPrincipal, idpPrincipal]() { nsresult rv = DisconnectData(self->mDiskDatabaseConnection, rpPrincipal, idpPrincipal); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), NS_DISPATCH_EVENT_MAY_BLOCK); return NS_OK; } // Helper function to get credentials from the database and put them into an // array of IPCIdentityCredentials. aStmt must be a SELECT query that give // fields SELECT credentialId, name, iconDataURL, userDataExpireTime, // originAllowList, dynamicAllowEndpoint, effectiveType, token, and [idpOrigin // if aIDPPrincipal is null] nsresult GetCredentialsHelper( const nsCOMPtr& aStmt, const RefPtr& aIDPPrincipal, nsTArray& aResult) { bool hasResult; nsresult rv; // For each result, we append it to the array to return while (NS_SUCCEEDED(aStmt->ExecuteStep(&hasResult)) && hasResult) { nsAutoString id, name, iconDataURL, originAllowList, dynamicAllowEndpoint, effectiveType, token, matchedOrigin; int64_t userDataExpireTime; rv = aStmt->GetString(0, id); NS_ENSURE_SUCCESS(rv, rv); rv = aStmt->GetString(1, name); NS_ENSURE_SUCCESS(rv, rv); rv = aStmt->GetString(2, iconDataURL); NS_ENSURE_SUCCESS(rv, rv); rv = aStmt->GetInt64(3, &userDataExpireTime); NS_ENSURE_SUCCESS(rv, rv); rv = aStmt->GetString(4, originAllowList); NS_ENSURE_SUCCESS(rv, rv); rv = aStmt->GetString(5, dynamicAllowEndpoint); NS_ENSURE_SUCCESS(rv, rv); rv = aStmt->GetString(6, effectiveType); NS_ENSURE_SUCCESS(rv, rv); rv = aStmt->GetString(7, token); NS_ENSURE_SUCCESS(rv, rv); if (!aIDPPrincipal) { rv = aStmt->GetString(8, matchedOrigin); NS_ENSURE_SUCCESS(rv, rv); } Maybe resultName, resultIconDataURL, resultDynamicAllowEndpoint, resultEffectiveType; Maybe resultToken; nsTArray allowListArray; Maybe resultUserDataExpireTime; RefPtr idpPrincipal; if (!name.IsVoid() && name.Length()) { resultName = Some(NS_ConvertUTF16toUTF8(name)); } if (!iconDataURL.IsVoid() && iconDataURL.Length()) { resultIconDataURL = Some(NS_ConvertUTF16toUTF8(iconDataURL)); } if (!effectiveType.IsVoid() && effectiveType.Length()) { resultEffectiveType = Some(NS_ConvertUTF16toUTF8(effectiveType)); } if (!token.IsVoid() && token.Length()) { resultToken = Some(token); } for (const auto& origin : originAllowList.Split('|')) { allowListArray.AppendElement(NS_ConvertUTF16toUTF8(origin)); } if (!dynamicAllowEndpoint.IsVoid() && dynamicAllowEndpoint.Length()) { resultDynamicAllowEndpoint = Some(NS_ConvertUTF16toUTF8(dynamicAllowEndpoint)); } if (!aStmt->IsNull(3) && userDataExpireTime > 0) { resultUserDataExpireTime = Some(userDataExpireTime); } if (aIDPPrincipal) { idpPrincipal = aIDPPrincipal; } else { nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(ssm, NS_ERROR_NOT_AVAILABLE); rv = ssm->CreateContentPrincipalFromOrigin( NS_ConvertUTF16toUTF8(matchedOrigin), getter_AddRefs(idpPrincipal)); NS_ENSURE_SUCCESS(rv, rv); } dom::IPCIdentityCredential result( id, resultToken, resultName, resultIconDataURL, allowListArray, resultDynamicAllowEndpoint, resultEffectiveType, resultUserDataExpireTime, idpPrincipal); aResult.AppendElement(result); } return NS_OK; } NS_IMETHODIMP IdentityCredentialStorageService:: IdentityCredentialStorageService::GetIdentityCredentials( nsTArray> const& aIDPPrincipals, nsTArray& aResult) { AssertIsOnMainThread(); nsresult rv = WaitForInitialization(); NS_ENSURE_SUCCESS(rv, rv); auto constexpr selectQuery = "SELECT credentialId, name, iconDataURL, userDataExpireTime, originAllowList, dynamicAllowEndpoint, effectiveType, token FROM lightweight_identity WHERE idpOrigin=?1"_ns; nsCOMPtr stmt; rv = mMemoryDatabaseConnection->CreateStatement(selectQuery, getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); for (const RefPtr& idpPrincipal : aIDPPrincipals) { rv = IdentityCredentialStorageService::ValidatePrincipal(idpPrincipal); NS_ENSURE_SUCCESS(rv, rv); nsCString idpOrigin; rv = idpPrincipal->GetOrigin(idpOrigin); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindUTF8StringByIndex(0, idpOrigin); NS_ENSURE_SUCCESS(rv, rv); rv = GetCredentialsHelper(stmt, idpPrincipal, aResult); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } NS_IMETHODIMP IdentityCredentialStorageService:: IdentityCredentialStorageService::GetIdentityCredentialsOfType( const nsACString& aType, nsTArray& aResult) { AssertIsOnMainThread(); nsresult rv = WaitForInitialization(); NS_ENSURE_SUCCESS(rv, rv); auto constexpr selectQuery = "SELECT credentialId, name, iconDataURL, userDataExpireTime, originAllowList, dynamicAllowEndpoint, effectiveType, token, idpOrigin FROM lightweight_identity WHERE effectiveType=?1"_ns; nsCOMPtr stmt; rv = mMemoryDatabaseConnection->CreateStatement(selectQuery, getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); rv = stmt->BindUTF8StringByIndex(0, aType); NS_ENSURE_SUCCESS(rv, rv); rv = GetCredentialsHelper(stmt, nullptr, aResult); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP IdentityCredentialStorageService::StoreIdentityCredential( const mozilla::dom::IPCIdentityCredential& aCredential) { AssertIsOnMainThread(); NS_ENSURE_ARG_POINTER(aCredential.identityProvider()); nsresult rv; rv = WaitForInitialization(); NS_ENSURE_SUCCESS(rv, rv); rv = UpsertLightweightData(mMemoryDatabaseConnection, aCredential); NS_ENSURE_SUCCESS(rv, rv); IncrementPendingWrites(); RefPtr self = this; return mBackgroundThread->Dispatch( NS_NewRunnableFunction( "IdentityCredentialStorageService::StoreIdentityCredential", [self, aCredential]() { nsresult rv = UpsertLightweightData(self->mDiskDatabaseConnection, aCredential); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), NS_DISPATCH_EVENT_MAY_BLOCK); } NS_IMETHODIMP IdentityCredentialStorageService::DeleteIdentityCredential( const mozilla::dom::IPCIdentityCredential& aCredential) { AssertIsOnMainThread(); NS_ENSURE_ARG_POINTER(aCredential.identityProvider()); nsresult rv; rv = WaitForInitialization(); NS_ENSURE_SUCCESS(rv, rv); rv = DeleteLightweightData(mMemoryDatabaseConnection, aCredential); NS_ENSURE_SUCCESS(rv, rv); IncrementPendingWrites(); RefPtr self = this; return mBackgroundThread->Dispatch( NS_NewRunnableFunction( "IdentityCredentialStorageService::DeleteIdentityCredential", [self, aCredential]() { nsresult rv = DeleteLightweightData(self->mDiskDatabaseConnection, aCredential); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), NS_DISPATCH_EVENT_MAY_BLOCK); } NS_IMETHODIMP IdentityCredentialStorageService::Clear() { AssertIsOnMainThread(); nsresult rv; rv = WaitForInitialization(); NS_ENSURE_SUCCESS(rv, rv); rv = ClearData(mMemoryDatabaseConnection); NS_ENSURE_SUCCESS(rv, rv); IncrementPendingWrites(); RefPtr self = this; mBackgroundThread->Dispatch( NS_NewRunnableFunction("IdentityCredentialStorageService::Init", [self]() { nsresult rv = ClearData(self->mDiskDatabaseConnection); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), 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 self = this; mBackgroundThread->Dispatch( NS_NewRunnableFunction( "IdentityCredentialStorageService::Init", [self, oaPattern]() { nsresult rv = DeleteDataFromOriginAttributesPattern( self->mDiskDatabaseConnection, oaPattern); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), 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 self = this; mBackgroundThread->Dispatch( NS_NewRunnableFunction("IdentityCredentialStorageService::Init", [self, aStart, aEnd]() { nsresult rv = DeleteDataFromTimeRange( self->mDiskDatabaseConnection, aStart, aEnd); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), 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 self = this; RefPtr principal = aRPPrincipal; mBackgroundThread->Dispatch( NS_NewRunnableFunction("IdentityCredentialStorageService::Init", [self, principal]() { nsresult rv = DeleteDataFromPrincipal( self->mDiskDatabaseConnection, principal); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), 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 self = this; nsCString baseDomain(aBaseDomain); mBackgroundThread->Dispatch( NS_NewRunnableFunction("IdentityCredentialStorageService::Init", [self, baseDomain]() { nsresult rv = DeleteDataFromBaseDomain( self->mDiskDatabaseConnection, baseDomain); self->DecrementPendingWrites(); NS_ENSURE_SUCCESS_VOID(rv); }), 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 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->ExecuteSimpleSQL( "DELETE FROM lightweight_identity WHERE " "PRIVATE_BROWSING_PATTERN_MATCH_ORIGIN(idpOrigin);"_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 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 outVar(new nsVariant()); rv = outVar->SetAsBool(result); NS_ENSURE_SUCCESS(rv, rv); outVar.forget(aResult); return NS_OK; } } // namespace mozilla