mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Verify Element X with an existing Element Classic account. (#5374)
* Read and import the secrets from ClassicAppAccounts. * Record snapshots. * Add some documentation, tidy up tests and fix the dismissal of the backup instructions. * Workaround flakey tests (the fulfilments weren't always firing). * Allow a custom Classic App deep link URL to be configured.
This commit is contained in:
@@ -11,13 +11,19 @@ import MatrixRustSDK
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol ClassicAppManagerProtocol {
|
||||
/// Loads all of the accounts found in the Classic app's file store.
|
||||
func loadAccounts() throws -> [ClassicAppAccount]
|
||||
/// Determines which secrets will be available when loading the secrets bundle for a given account.
|
||||
func availableSecrets(for account: ClassicAppAccount) async throws -> ClassicAppAccount.AvailableSecrets
|
||||
/// Loads the secrets bundle for a given account.
|
||||
func secretsBundle(for account: ClassicAppAccount) async throws -> SecretsBundleWithUserId
|
||||
}
|
||||
|
||||
enum ClassicAppManagerError: Error {
|
||||
case invalidAppGroupIdentifier(String)
|
||||
case missingAccountKeys
|
||||
case missingCryptoStorePassphrase
|
||||
case missingKeyBackupVersion
|
||||
}
|
||||
|
||||
/// Reads accounts from Element Classic's shared storage.
|
||||
@@ -45,7 +51,6 @@ final class ClassicAppManager: ClassicAppManagerProtocol {
|
||||
keychain = Keychain(service: classicAppKeychainServiceIdentifier, accessGroup: classicAppKeychainAccessGroupIdentifier)
|
||||
}
|
||||
|
||||
/// Loads all of the active accounts from the Classic app.
|
||||
func loadAccounts() throws -> [ClassicAppAccount] {
|
||||
// The account data is stored in the App Group container.
|
||||
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: classicAppGroupIdentifier) else {
|
||||
@@ -69,4 +74,41 @@ final class ClassicAppManager: ClassicAppManagerProtocol {
|
||||
accountManager.loadAccounts()
|
||||
return accountManager.accounts
|
||||
}
|
||||
|
||||
func availableSecrets(for account: ClassicAppAccount) async throws -> ClassicAppAccount.AvailableSecrets {
|
||||
switch try await databaseContainsSecretsBundle(databasePath: account.cryptoStoreURL.path(percentEncoded: false),
|
||||
passphrase: account.cryptoStorePassphrase,
|
||||
backupInfo: keyBackupVersion(for: account)) {
|
||||
case .complete: .complete
|
||||
case .none: .unavailable
|
||||
case .withoutBackup, .unusedBackup: .requiresBackup
|
||||
}
|
||||
}
|
||||
|
||||
func secretsBundle(for account: ClassicAppAccount) async throws -> SecretsBundleWithUserId {
|
||||
guard let keyBackupVersion = try await keyBackupVersion(for: account) else {
|
||||
throw ClassicAppManagerError.missingKeyBackupVersion
|
||||
}
|
||||
|
||||
return try await SecretsBundleWithUserId.fromDatabase(databasePath: account.cryptoStoreURL.path(percentEncoded: false),
|
||||
passphrase: account.cryptoStorePassphrase,
|
||||
backupInfo: keyBackupVersion)
|
||||
}
|
||||
|
||||
/// Fetches the current key backup version from the homeserver. This is needed to determine whether
|
||||
/// the backup key from the crypto store is for the backup currently being used by the account.
|
||||
private func keyBackupVersion(for account: ClassicAppAccount) async throws -> String? {
|
||||
let url = account.homeserverURL.appending(path: "_matrix/client/v3/room_keys/version")
|
||||
var request = URLRequest(url: url)
|
||||
request.setValue("Bearer \(account.accessToken)", forHTTPHeaderField: "Authorization")
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
|
||||
if let httpResponse = response as? HTTPURLResponse,
|
||||
httpResponse.statusCode != 200 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user