Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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,
#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<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.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<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);
}
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<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) {
nsCOMPtr<nsIVariant> 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<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;
}
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<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::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<mozIStorageStatement> 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<int64_t>(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<mozIStorageStatement> 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<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->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<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);
auto constexpr deleteTimeQueryLightweight =
"DELETE FROM lightweight_identity WHERE modificationTime > ?1 and "
"modificationTime "
"< ?2"_ns;
nsCOMPtr<mozIStorageStatement> 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<mozIStorageStatement> 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<mozIStorageStatement> 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<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);
auto constexpr deleteBaseDomainQueryLightweight =
"DELETE FROM lightweight_identity WHERE idpBaseDomain=?1"_ns;
nsCOMPtr<mozIStorageStatement> 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<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);
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<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);
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<mozIStorageStatement>& aStmt,
const RefPtr<nsIPrincipal>& aIDPPrincipal,
nsTArray<mozilla::dom::IPCIdentityCredential>& 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<nsCString> resultName, resultIconDataURL, resultDynamicAllowEndpoint,
resultEffectiveType;
Maybe<nsString> resultToken;
nsTArray<nsCString> allowListArray;
Maybe<int64_t> resultUserDataExpireTime;
RefPtr<nsIPrincipal> 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<RefPtr<nsIPrincipal>> const& aIDPPrincipals,
nsTArray<mozilla::dom::IPCIdentityCredential>& 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<mozIStorageStatement> stmt;
rv = mMemoryDatabaseConnection->CreateStatement(selectQuery,
getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
for (const RefPtr<nsIPrincipal>& 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<mozilla::dom::IPCIdentityCredential>& 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<mozIStorageStatement> 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<IdentityCredentialStorageService> 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<IdentityCredentialStorageService> 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<IdentityCredentialStorageService> 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<IdentityCredentialStorageService> 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<IdentityCredentialStorageService> 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<IdentityCredentialStorageService> self = this;
RefPtr<nsIPrincipal> 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<IdentityCredentialStorageService> 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<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->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<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