feat: complete Ancestor quote bot with production-ready Docker support

This commit is contained in:
unfunny
2026-09-13 14:14:38 -04:00
parent f2016da05a
commit fe7351a7dc
1145 changed files with 30890 additions and 97684 deletions

View File

@@ -1,4 +1,19 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.IndexedDBCryptoStore = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _logger = require("../../logger");
var _localStorageCryptoStore = require("./localStorage-crypto-store");
var _memoryCryptoStore = require("./memory-crypto-store");
var IndexedDBCryptoStoreBackend = _interopRequireWildcard(require("./indexeddb-crypto-store-backend"));
var _errors = require("../../errors");
var IndexedDBHelpers = _interopRequireWildcard(require("../../indexeddb-helpers"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
/*
Copyright 2017 - 2021 The Matrix.org Foundation C.I.C.
@@ -15,14 +30,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { logger } from "../../logger.js";
import { LocalStorageCryptoStore } from "./localStorage-crypto-store.js";
import { MemoryCryptoStore } from "./memory-crypto-store.js";
import * as IndexedDBCryptoStoreBackend from "./indexeddb-crypto-store-backend.js";
import { InvalidCryptoStoreError, InvalidCryptoStoreState } from "../../errors.js";
import * as IndexedDBHelpers from "../../indexeddb-helpers.js";
import { MigrationState, ACCOUNT_OBJECT_KEY_MIGRATION_STATE } from "./base.js";
/*
/**
* Internal module. indexeddb storage for e2e.
*/
@@ -30,53 +38,10 @@ import { MigrationState, ACCOUNT_OBJECT_KEY_MIGRATION_STATE } from "./base.js";
* An implementation of CryptoStore, which is normally backed by an indexeddb,
* but with fallback to MemoryCryptoStore.
*/
export class IndexedDBCryptoStore {
class IndexedDBCryptoStore {
static exists(indexedDB, dbName) {
return IndexedDBHelpers.exists(indexedDB, dbName);
}
/**
* Utility to check if a legacy crypto store exists and has not been migrated.
* Returns true if the store exists and has not been migrated, false otherwise.
*/
static existsAndIsNotMigrated(indexedDb, dbName) {
return new Promise((resolve, reject) => {
let exists = true;
const openDBRequest = indexedDb.open(dbName);
openDBRequest.onupgradeneeded = () => {
// Since we did not provide an explicit version when opening, this event
// should only fire if the DB did not exist before at any version.
exists = false;
};
openDBRequest.onblocked = () => reject(openDBRequest.error);
openDBRequest.onsuccess = () => {
const db = openDBRequest.result;
if (!exists) {
db.close();
// The DB did not exist before, but has been created as part of this
// existence check. Delete it now to restore previous state. Delete can
// actually take a while to complete in some browsers, so don't wait for
// it. This won't block future open calls that a store might issue next to
// properly set up the DB.
indexedDb.deleteDatabase(dbName);
resolve(false);
} else {
const tx = db.transaction([IndexedDBCryptoStore.STORE_ACCOUNT], "readonly");
const objectStore = tx.objectStore(IndexedDBCryptoStore.STORE_ACCOUNT);
const getReq = objectStore.get(ACCOUNT_OBJECT_KEY_MIGRATION_STATE);
getReq.onsuccess = () => {
const migrationState = getReq.result ?? MigrationState.NOT_STARTED;
resolve(migrationState === MigrationState.NOT_STARTED);
};
getReq.onerror = () => {
reject(getReq.error);
};
db.close();
}
};
openDBRequest.onerror = () => reject(openDBRequest.error);
});
}
/**
* Create a new IndexedDBCryptoStore
*
@@ -84,21 +49,10 @@ export class IndexedDBCryptoStore {
* @param dbName - name of db to connect to
*/
constructor(indexedDB, dbName) {
_defineProperty(this, "backendPromise", void 0);
_defineProperty(this, "backend", void 0);
this.indexedDB = indexedDB;
this.dbName = dbName;
}
/**
* Returns true if this CryptoStore has ever been initialised (ie, it might contain data).
*
* Implementation of {@link CryptoStore.containsData}.
*
* @internal
*/
async containsData() {
return IndexedDBCryptoStore.exists(this.indexedDB, this.dbName);
(0, _defineProperty2.default)(this, "backendPromise", void 0);
(0, _defineProperty2.default)(this, "backend", void 0);
}
/**
@@ -119,7 +73,7 @@ export class IndexedDBCryptoStore {
reject(new Error("no indexeddb support available"));
return;
}
logger.log(`connecting to indexeddb ${this.dbName}`);
_logger.logger.log(`connecting to indexeddb ${this.dbName}`);
const req = this.indexedDB.open(this.dbName, IndexedDBCryptoStoreBackend.VERSION);
req.onupgradeneeded = ev => {
const db = req.result;
@@ -127,15 +81,15 @@ export class IndexedDBCryptoStore {
IndexedDBCryptoStoreBackend.upgradeDatabase(db, oldVersion);
};
req.onblocked = () => {
logger.log(`can't yet open IndexedDBCryptoStore because it is open elsewhere`);
_logger.logger.log(`can't yet open IndexedDBCryptoStore because it is open elsewhere`);
};
req.onerror = ev => {
logger.log("Error connecting to indexeddb", ev);
_logger.logger.log("Error connecting to indexeddb", ev);
reject(req.error);
};
req.onsuccess = () => {
const db = req.result;
logger.log(`connected to indexeddb ${this.dbName}`);
_logger.logger.log(`connected to indexeddb ${this.dbName}`);
resolve(new IndexedDBCryptoStoreBackend.Backend(db));
};
}).then(backend => {
@@ -147,20 +101,17 @@ export class IndexedDBCryptoStore {
}).then(() => backend);
}).catch(e => {
if (e.name === "VersionError") {
logger.warn("Crypto DB is too new for us to use!", e);
_logger.logger.warn("Crypto DB is too new for us to use!", e);
// don't fall back to a different store: the user has crypto data
// in this db so we should use it or nothing at all.
throw new InvalidCryptoStoreError(InvalidCryptoStoreState.TooNew);
throw new _errors.InvalidCryptoStoreError(_errors.InvalidCryptoStoreState.TooNew);
}
logger.warn(`unable to connect to indexeddb ${this.dbName}: falling back to localStorage store: ${e}`);
_logger.logger.warn(`unable to connect to indexeddb ${this.dbName}` + `: falling back to localStorage store: ${e}`);
try {
if (!(globalThis.localStorage instanceof Storage)) {
throw new Error("localStorage is not available");
}
return new LocalStorageCryptoStore(globalThis.localStorage);
return new _localStorageCryptoStore.LocalStorageCryptoStore(global.localStorage);
} catch (e) {
logger.warn(`Unable to open localStorage: falling back to in-memory store: ${e}`);
return new MemoryCryptoStore();
_logger.logger.warn(`unable to open localStorage: falling back to in-memory store: ${e}`);
return new _memoryCryptoStore.MemoryCryptoStore();
}
}).then(backend => {
this.backend = backend;
@@ -180,47 +131,118 @@ export class IndexedDBCryptoStore {
reject(new Error("no indexeddb support available"));
return;
}
logger.log(`Removing indexeddb instance: ${this.dbName}`);
_logger.logger.log(`Removing indexeddb instance: ${this.dbName}`);
const req = this.indexedDB.deleteDatabase(this.dbName);
req.onblocked = () => {
logger.log(`can't yet delete IndexedDBCryptoStore because it is open elsewhere`);
_logger.logger.log(`can't yet delete IndexedDBCryptoStore because it is open elsewhere`);
};
req.onerror = ev => {
logger.log("Error deleting data from indexeddb", ev);
_logger.logger.log("Error deleting data from indexeddb", ev);
reject(req.error);
};
req.onsuccess = () => {
logger.log(`Removed indexeddb instance: ${this.dbName}`);
_logger.logger.log(`Removed indexeddb instance: ${this.dbName}`);
resolve();
};
}).catch(e => {
// in firefox, with indexedDB disabled, this fails with a
// DOMError. We treat this as non-fatal, so that people can
// still use the app.
logger.warn(`unable to delete IndexedDBCryptoStore: ${e}`);
_logger.logger.warn(`unable to delete IndexedDBCryptoStore: ${e}`);
});
}
/**
* Get data on how much of the libolm to Rust Crypto migration has been done.
* Look for an existing outgoing room key request, and if none is found,
* add a new one
*
* Implementation of {@link CryptoStore.getMigrationState}.
*
* @internal
* @returns resolves to
* {@link OutgoingRoomKeyRequest}: either the
* same instance as passed in, or the existing one.
*/
getMigrationState() {
return this.backend.getMigrationState();
getOrAddOutgoingRoomKeyRequest(request) {
return this.backend.getOrAddOutgoingRoomKeyRequest(request);
}
/**
* Set data on how much of the libolm to Rust Crypto migration has been done.
* Look for an existing room key request
*
* Implementation of {@link CryptoStore.setMigrationState}.
* @param requestBody - existing request to look for
*
* @internal
* @returns resolves to the matching
* {@link OutgoingRoomKeyRequest}, or null if
* not found
*/
setMigrationState(migrationState) {
return this.backend.setMigrationState(migrationState);
getOutgoingRoomKeyRequest(requestBody) {
return this.backend.getOutgoingRoomKeyRequest(requestBody);
}
/**
* Look for room key requests by state
*
* @param wantedStates - list of acceptable states
*
* @returns resolves to the a
* {@link OutgoingRoomKeyRequest}, or null if
* there are no pending requests in those states. If there are multiple
* requests in those states, an arbitrary one is chosen.
*/
getOutgoingRoomKeyRequestByState(wantedStates) {
return this.backend.getOutgoingRoomKeyRequestByState(wantedStates);
}
/**
* Look for room key requests by state –
* unlike above, return a list of all entries in one state.
*
* @returns Returns an array of requests in the given state
*/
getAllOutgoingRoomKeyRequestsByState(wantedState) {
return this.backend.getAllOutgoingRoomKeyRequestsByState(wantedState);
}
/**
* Look for room key requests by target device and state
*
* @param userId - Target user ID
* @param deviceId - Target device ID
* @param wantedStates - list of acceptable states
*
* @returns resolves to a list of all the
* {@link OutgoingRoomKeyRequest}
*/
getOutgoingRoomKeyRequestsByTarget(userId, deviceId, wantedStates) {
return this.backend.getOutgoingRoomKeyRequestsByTarget(userId, deviceId, wantedStates);
}
/**
* Look for an existing room key request by id and state, and update it if
* found
*
* @param requestId - ID of request to update
* @param expectedState - state we expect to find the request in
* @param updates - name/value map of updates to apply
*
* @returns resolves to
* {@link OutgoingRoomKeyRequest}
* updated request, or null if no matching row was found
*/
updateOutgoingRoomKeyRequest(requestId, expectedState, updates) {
return this.backend.updateOutgoingRoomKeyRequest(requestId, expectedState, updates);
}
/**
* Look for an existing room key request by id and state, and delete it if
* found
*
* @param requestId - ID of request to update
* @param expectedState - state we expect to find the request in
*
* @returns resolves once the operation is completed
*/
deleteOutgoingRoomKeyRequest(requestId, expectedState) {
return this.backend.deleteOutgoingRoomKeyRequest(requestId, expectedState);
}
// Olm Account
@@ -268,6 +290,16 @@ export class IndexedDBCryptoStore {
this.backend.getSecretStorePrivateKey(txn, func, type);
}
/**
* Write the cross-signing keys back to the store
*
* @param txn - An active transaction. See doTxn().
* @param keys - keys object as getCrossSigningKeys()
*/
storeCrossSigningKeys(txn, keys) {
this.backend.storeCrossSigningKeys(txn, keys);
}
/**
* Write the cross-signing private keys back to the store
*
@@ -321,6 +353,17 @@ export class IndexedDBCryptoStore {
this.backend.getEndToEndSessions(deviceKey, txn, func);
}
/**
* Retrieve all end-to-end sessions
* @param txn - An active transaction. See doTxn().
* @param func - Called one for each session with
* an object with, deviceKey, lastReceivedMessageTs, sessionId
* and session keys.
*/
getAllEndToEndSessions(txn, func) {
this.backend.getAllEndToEndSessions(txn, func);
}
/**
* Store a session between the logged-in user and another device
* @param deviceKey - The public key of the other device.
@@ -331,38 +374,14 @@ export class IndexedDBCryptoStore {
storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) {
this.backend.storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn);
}
/**
* Count the number of Megolm sessions in the database.
*
* Implementation of {@link CryptoStore.countEndToEndInboundGroupSessions}.
*
* @internal
*/
countEndToEndInboundGroupSessions() {
return this.backend.countEndToEndInboundGroupSessions();
storeEndToEndSessionProblem(deviceKey, type, fixed) {
return this.backend.storeEndToEndSessionProblem(deviceKey, type, fixed);
}
/**
* Fetch a batch of Olm sessions from the database.
*
* Implementation of {@link CryptoStore.getEndToEndSessionsBatch}.
*
* @internal
*/
getEndToEndSessionsBatch() {
return this.backend.getEndToEndSessionsBatch();
getEndToEndSessionProblem(deviceKey, timestamp) {
return this.backend.getEndToEndSessionProblem(deviceKey, timestamp);
}
/**
* Delete a batch of Olm sessions from the database.
*
* Implementation of {@link CryptoStore.deleteEndToEndSessionsBatch}.
*
* @internal
*/
deleteEndToEndSessionsBatch(sessions) {
return this.backend.deleteEndToEndSessionsBatch(sessions);
filterOutNotifiedErrorDevices(devices) {
return this.backend.filterOutNotifiedErrorDevices(devices);
}
// Inbound group sessions
@@ -380,6 +399,30 @@ export class IndexedDBCryptoStore {
this.backend.getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func);
}
/**
* Fetches all inbound group sessions in the store
* @param txn - An active transaction. See doTxn().
* @param func - Called once for each group session
* in the store with an object having keys `{senderKey, sessionId, sessionData}`,
* then once with null to indicate the end of the list.
*/
getAllEndToEndInboundGroupSessions(txn, func) {
this.backend.getAllEndToEndInboundGroupSessions(txn, func);
}
/**
* Adds an end-to-end inbound group session to the store.
* If there already exists an inbound group session with the same
* senderCurve25519Key and sessionID, the session will not be added.
* @param senderCurve25519Key - The sender's curve 25519 key
* @param sessionId - The ID of the session
* @param sessionData - The session data structure
* @param txn - An active transaction. See doTxn().
*/
addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
this.backend.addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn);
}
/**
* Writes an end-to-end inbound group session to the store.
* If there already exists an inbound group session with the same
@@ -392,27 +435,46 @@ export class IndexedDBCryptoStore {
storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) {
this.backend.storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn);
}
storeEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId, sessionData, txn) {
this.backend.storeEndToEndInboundGroupSessionWithheld(senderCurve25519Key, sessionId, sessionData, txn);
}
// End-to-end device tracking
/**
* Fetch a batch of Megolm sessions from the database.
* Store the state of all tracked devices
* This contains devices for each user, a tracking state for each user
* and a sync token matching the point in time the snapshot represents.
* These all need to be written out in full each time such that the snapshot
* is always consistent, so they are stored in one object.
*
* Implementation of {@link CryptoStore.getEndToEndInboundGroupSessionsBatch}.
*
* @internal
* @param txn - An active transaction. See doTxn().
*/
getEndToEndInboundGroupSessionsBatch() {
return this.backend.getEndToEndInboundGroupSessionsBatch();
storeEndToEndDeviceData(deviceData, txn) {
this.backend.storeEndToEndDeviceData(deviceData, txn);
}
/**
* Delete a batch of Megolm sessions from the database.
* Get the state of all tracked devices
*
* Implementation of {@link CryptoStore.deleteEndToEndInboundGroupSessionsBatch}.
*
* @internal
* @param txn - An active transaction. See doTxn().
* @param func - Function called with the
* device data
*/
deleteEndToEndInboundGroupSessionsBatch(sessions) {
return this.backend.deleteEndToEndInboundGroupSessionsBatch(sessions);
getEndToEndDeviceData(txn, func) {
this.backend.getEndToEndDeviceData(txn, func);
}
// End to End Rooms
/**
* Store the end-to-end state for a room.
* @param roomId - The room's ID.
* @param roomInfo - The end-to-end info for the room.
* @param txn - An active transaction. See doTxn().
*/
storeEndToEndRoom(roomId, roomInfo, txn) {
this.backend.storeEndToEndRoom(roomId, roomInfo, txn);
}
/**
@@ -424,6 +486,37 @@ export class IndexedDBCryptoStore {
this.backend.getEndToEndRooms(txn, func);
}
// session backups
/**
* Get the inbound group sessions that need to be backed up.
* @param limit - The maximum number of sessions to retrieve. 0
* for no limit.
* @returns resolves to an array of inbound group sessions
*/
getSessionsNeedingBackup(limit) {
return this.backend.getSessionsNeedingBackup(limit);
}
/**
* Count the inbound group sessions that need to be backed up.
* @param txn - An active transaction. See doTxn(). (optional)
* @returns resolves to the number of sessions
*/
countSessionsNeedingBackup(txn) {
return this.backend.countSessionsNeedingBackup(txn);
}
/**
* Unmark sessions as needing to be backed up.
* @param sessions - The sessions that need to be backed up.
* @param txn - An active transaction. See doTxn(). (optional)
* @returns resolves when the sessions are unmarked
*/
unmarkSessionsNeedingBackup(sessions, txn) {
return this.backend.unmarkSessionsNeedingBackup(sessions, txn);
}
/**
* Mark sessions as needing to be backed up.
* @param sessions - The sessions that need to be backed up.
@@ -434,6 +527,41 @@ export class IndexedDBCryptoStore {
return this.backend.markSessionsNeedingBackup(sessions, txn);
}
/**
* Add a shared-history group session for a room.
* @param roomId - The room that the key belongs to
* @param senderKey - The sender's curve 25519 key
* @param sessionId - The ID of the session
* @param txn - An active transaction. See doTxn(). (optional)
*/
addSharedHistoryInboundGroupSession(roomId, senderKey, sessionId, txn) {
this.backend.addSharedHistoryInboundGroupSession(roomId, senderKey, sessionId, txn);
}
/**
* Get the shared-history group session for a room.
* @param roomId - The room that the key belongs to
* @param txn - An active transaction. See doTxn(). (optional)
* @returns Promise which resolves to an array of [senderKey, sessionId]
*/
getSharedHistoryInboundGroupSessions(roomId, txn) {
return this.backend.getSharedHistoryInboundGroupSessions(roomId, txn);
}
/**
* Park a shared-history group session for a room we may be invited to later.
*/
addParkedSharedHistory(roomId, parkedData, txn) {
this.backend.addParkedSharedHistory(roomId, parkedData, txn);
}
/**
* Pop out all shared-history group sessions for a room.
*/
takeParkedSharedHistory(roomId, txn) {
return this.backend.takeParkedSharedHistory(roomId, txn);
}
/**
* Perform a transaction on the crypto store. Any store methods
* that require a transaction (txn) object to be passed in may
@@ -460,13 +588,14 @@ export class IndexedDBCryptoStore {
return this.backend.doTxn(mode, stores, func, log);
}
}
_defineProperty(IndexedDBCryptoStore, "STORE_ACCOUNT", "account");
_defineProperty(IndexedDBCryptoStore, "STORE_SESSIONS", "sessions");
_defineProperty(IndexedDBCryptoStore, "STORE_INBOUND_GROUP_SESSIONS", "inbound_group_sessions");
_defineProperty(IndexedDBCryptoStore, "STORE_INBOUND_GROUP_SESSIONS_WITHHELD", "inbound_group_sessions_withheld");
_defineProperty(IndexedDBCryptoStore, "STORE_SHARED_HISTORY_INBOUND_GROUP_SESSIONS", "shared_history_inbound_group_sessions");
_defineProperty(IndexedDBCryptoStore, "STORE_PARKED_SHARED_HISTORY", "parked_shared_history");
_defineProperty(IndexedDBCryptoStore, "STORE_DEVICE_DATA", "device_data");
_defineProperty(IndexedDBCryptoStore, "STORE_ROOMS", "rooms");
_defineProperty(IndexedDBCryptoStore, "STORE_BACKUP", "sessions_needing_backup");
exports.IndexedDBCryptoStore = IndexedDBCryptoStore;
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_ACCOUNT", "account");
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_SESSIONS", "sessions");
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_INBOUND_GROUP_SESSIONS", "inbound_group_sessions");
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_INBOUND_GROUP_SESSIONS_WITHHELD", "inbound_group_sessions_withheld");
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_SHARED_HISTORY_INBOUND_GROUP_SESSIONS", "shared_history_inbound_group_sessions");
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_PARKED_SHARED_HISTORY", "parked_shared_history");
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_DEVICE_DATA", "device_data");
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_ROOMS", "rooms");
(0, _defineProperty2.default)(IndexedDBCryptoStore, "STORE_BACKUP", "sessions_needing_backup");
//# sourceMappingURL=indexeddb-crypto-store.js.map