diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 30a0e3a8f..035ae43c6 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -18943,8 +18943,8 @@ class UserSessionStoreMock: UserSessionStoreProtocol, @unchecked Sendable { var userSessionForSessionDirectoriesPassphraseCalled: Bool { return userSessionForSessionDirectoriesPassphraseCallsCount > 0 } - var userSessionForSessionDirectoriesPassphraseReceivedArguments: (client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?)? - var userSessionForSessionDirectoriesPassphraseReceivedInvocations: [(client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?)] = [] + var userSessionForSessionDirectoriesPassphraseReceivedArguments: (client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String)? + var userSessionForSessionDirectoriesPassphraseReceivedInvocations: [(client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String)] = [] var userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue: Result! var userSessionForSessionDirectoriesPassphraseReturnValue: Result! { @@ -18970,9 +18970,9 @@ class UserSessionStoreMock: UserSessionStoreProtocol, @unchecked Sendable { } } } - var userSessionForSessionDirectoriesPassphraseClosure: ((ClientProtocol, SessionDirectories, String?) async -> Result)? + var userSessionForSessionDirectoriesPassphraseClosure: ((ClientProtocol, SessionDirectories, String) async -> Result)? - func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result { + func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String) async -> Result { userSessionForSessionDirectoriesPassphraseCallsCount += 1 userSessionForSessionDirectoriesPassphraseReceivedArguments = (client: client, sessionDirectories: sessionDirectories, passphrase: passphrase) DispatchQueue.main.async { diff --git a/ElementX/Sources/Services/UserSession/RestorationToken.swift b/ElementX/Sources/Services/UserSession/RestorationToken.swift index b1e39c666..6f8ae0ab1 100644 --- a/ElementX/Sources/Services/UserSession/RestorationToken.swift +++ b/ElementX/Sources/Services/UserSession/RestorationToken.swift @@ -15,7 +15,7 @@ enum RestorationTokenError: Error { struct RestorationToken: Equatable { let session: MatrixRustSDK.Session let sessionDirectories: SessionDirectories - let passphrase: String? + let passphrase: String let pusherNotificationClientIdentifier: String? enum CodingKeys: CodingKey { @@ -32,22 +32,18 @@ extension RestorationToken: Codable { let container = try decoder.container(keyedBy: CodingKeys.self) let session = try container.decode(Session.self, forKey: .session) - let dataDirectory = try container.decodeIfPresent(URL.self, forKey: .sessionDirectory) + let dataDirectory = try container.decode(URL.self, forKey: .sessionDirectory) let cacheDirectory = try container.decodeIfPresent(URL.self, forKey: .cacheDirectory) - let sessionDirectories = if let dataDirectory { - if let cacheDirectory { - SessionDirectories(dataDirectory: dataDirectory, cacheDirectory: cacheDirectory) - } else { - SessionDirectories(dataDirectory: dataDirectory) - } + let sessionDirectories = if let cacheDirectory { + SessionDirectories(dataDirectory: dataDirectory, cacheDirectory: cacheDirectory) } else { - SessionDirectories(userID: session.userId) + SessionDirectories(dataDirectory: dataDirectory) } self = try .init(session: session, sessionDirectories: sessionDirectories, - passphrase: container.decodeIfPresent(String.self, forKey: .passphrase), + passphrase: container.decode(String.self, forKey: .passphrase), pusherNotificationClientIdentifier: container.decodeIfPresent(String.self, forKey: .pusherNotificationClientIdentifier)) } diff --git a/ElementX/Sources/Services/UserSession/SessionDirectories.swift b/ElementX/Sources/Services/UserSession/SessionDirectories.swift index 035d1937b..1db5f5a6b 100644 --- a/ElementX/Sources/Services/UserSession/SessionDirectories.swift +++ b/ElementX/Sources/Services/UserSession/SessionDirectories.swift @@ -75,12 +75,6 @@ extension SessionDirectories { cacheDirectory = .sessionCachesBaseDirectory.appending(component: sessionDirectoryName) } - /// Creates the session directories for a user who signed in before the data directory was stored. - init(userID: String) { - dataDirectory = .legacySessionDirectory(for: userID) - cacheDirectory = .sessionCachesBaseDirectory.appending(component: dataDirectory.lastPathComponent) - } - /// Creates the session directories for a user who has a single session directory stored without a separate caches directory. init(dataDirectory: URL) { self.dataDirectory = dataDirectory @@ -93,18 +87,3 @@ extension SessionDirectories: CustomStringConvertible { "Data: \(dataPath) Caches: \(cachePath)" } } - -// MARK: Migrations - -private extension URL { - /// Gets the store directory of a legacy session that hasn't been migrated to the new token format. - /// - /// This should only be used to fill in the missing value when restoring a token as older versions of - /// the SDK set the session directory for us, based on the user's ID. Newer sessions now use a UUID, - /// which is generated app side during authentication. - static func legacySessionDirectory(for userID: String) -> URL { - // Rust sanitises the user ID replacing invalid characters with an _ - let sanitisedUserID = userID.replacingOccurrences(of: ":", with: "_") - return .sessionsBaseDirectory.appendingPathComponent(sanitisedUserID) - } -} diff --git a/ElementX/Sources/Services/UserSession/UserSessionStore.swift b/ElementX/Sources/Services/UserSession/UserSessionStore.swift index 9122d62f3..f69f75b4f 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStore.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStore.swift @@ -60,7 +60,7 @@ class UserSessionStore: UserSessionStoreProtocol { } } - func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result { + func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String) async -> Result { do { let session = try client.session() let userID = try client.userId() diff --git a/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift b/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift index d834226d1..b13e92068 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift @@ -33,7 +33,7 @@ protocol UserSessionStoreProtocol { func restoreUserSession() async -> Result /// Creates a user session for a new client from the SDK along with the passphrase used for the data stores. - func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result + func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String) async -> Result /// Logs out of the specified session. func logout(userSession: UserSessionProtocol) diff --git a/UnitTests/Sources/KeychainControllerTests.swift b/UnitTests/Sources/KeychainControllerTests.swift index dca2f9f18..628567796 100644 --- a/UnitTests/Sources/KeychainControllerTests.swift +++ b/UnitTests/Sources/KeychainControllerTests.swift @@ -127,13 +127,16 @@ class KeychainControllerTests: XCTestCase { XCTAssertTrue(underlyingKeychain.allKeys().isEmpty, "The keychain should be empty to begin with.") do { - let unsupportedToken = RestorationTokenV1(session: SessionV1(accessToken: "1234", + let unsupportedToken = RestorationTokenV4(session: SessionV1(accessToken: "1234", refreshToken: nil, userId: "@test:example.com", deviceId: "D3V1C3", homeserverUrl: "https://matrix.example.com", oidcData: nil, - slidingSyncVersion: .proxy(url: "https://sync.example.com"))) + slidingSyncVersion: .proxy(url: "https://sync.example.com")), + sessionDirectory: .sessionsBaseDirectory.appending(component: UUID().uuidString), + passphrase: "passphrase", + pusherNotificationClientIdentifier: "pusherClientID") let tokenData = try JSONEncoder().encode(unsupportedToken) try underlyingKeychain.set(tokenData, key: "@test:example.com") XCTAssertEqual(underlyingKeychain.allKeys().count, 1) diff --git a/UnitTests/Sources/RestorationTokenTests.swift b/UnitTests/Sources/RestorationTokenTests.swift index dbde7774c..03d7ad968 100644 --- a/UnitTests/Sources/RestorationTokenTests.swift +++ b/UnitTests/Sources/RestorationTokenTests.swift @@ -11,15 +11,18 @@ import XCTest import MatrixRustSDK class RestorationTokenTests: XCTestCase { - func testDecodeTokenV1WithSlidingSyncProxy() throws { + func testDecodeTokenWithSlidingSyncProxy() throws { // Given an encoded restoration token that contains a session with a sliding sync proxy. - let originalToken = RestorationTokenV1(session: SessionV1(accessToken: "1234", - refreshToken: nil, + let originalToken = RestorationTokenV4(session: SessionV1(accessToken: "1234", + refreshToken: "5678", userId: "@user:example.com", deviceId: "D3V1C3", homeserverUrl: "https://matrix.example.com", - oidcData: nil, - slidingSyncVersion: .proxy(url: "https://sync.example.com"))) + oidcData: "data-from-mas", + slidingSyncVersion: .proxy(url: "https://sync.example.com")), + sessionDirectory: .sessionsBaseDirectory.appending(component: UUID().uuidString), + passphrase: "passphrase", + pusherNotificationClientIdentifier: "pusher-identifier") let data = try JSONEncoder().encode(originalToken) // When decoding the data to the current restoration token format. @@ -34,32 +37,6 @@ class RestorationTokenTests: XCTestCase { } } - /// Honestly not sure if this test is needed or not – it is quite possible that `RestorationTokenV1` was only encoded at a time - /// when the sliding sync proxy was the only option. But lets keep the test for completeness of the session directory migration coverage. - func testDecodeFromTokenV1() throws { - // Given an encoded restoration token in the original format that only contains a Session from the SDK. - let originalToken = RestorationTokenV1(session: SessionV1(accessToken: "1234", - refreshToken: nil, - userId: "@user:example.com", - deviceId: "D3V1C3", - homeserverUrl: "https://matrix.example.com", - oidcData: nil, - slidingSyncVersion: .native)) - let data = try JSONEncoder().encode(originalToken) - - // When decoding the data to the current restoration token format. - let decodedToken = try JSONDecoder().decode(RestorationToken.self, from: data) - - // Then the output should be a valid token with the expected store directories. - assertEqual(session: decodedToken.session, originalSession: originalToken.session) - XCTAssertNil(decodedToken.passphrase, "There should not be a passphrase.") - XCTAssertNil(decodedToken.pusherNotificationClientIdentifier, "There should not be a push notification client ID.") - XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, .sessionsBaseDirectory.appending(component: "@user_example.com"), - "The session directory should match the original location set by the Rust SDK from our base directory.") - XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: "@user_example.com"), - "The cache directory should be derived from the session directory but in the caches directory.") - } - func testDecodeFromTokenV4() throws { // Given an encoded restoration token in the 4th format that contains a stored session directory. let sessionDirectoryName = UUID().uuidString @@ -152,14 +129,10 @@ class RestorationTokenTests: XCTestCase { // MARK: - Token formats -struct RestorationTokenV1: Equatable, Codable { - let session: SessionV1 -} - struct RestorationTokenV4: Equatable, Codable { let session: SessionV1 let sessionDirectory: URL - let passphrase: String? + let passphrase: String? // Optional but has always been encoded for sessions that use native sliding sync. let pusherNotificationClientIdentifier: String? } @@ -167,7 +140,7 @@ struct RestorationTokenV5: Equatable, Codable { let session: SessionV1 let sessionDirectory: URL let cacheDirectory: URL - let passphrase: String? + let passphrase: String? // Optional but has always been encoded for sessions that use native sliding sync. let pusherNotificationClientIdentifier: String? } diff --git a/UnitTests/Sources/SessionDirectoriesTests.swift b/UnitTests/Sources/SessionDirectoriesTests.swift index 8b3c5f0c7..706cd7b3d 100644 --- a/UnitTests/Sources/SessionDirectoriesTests.swift +++ b/UnitTests/Sources/SessionDirectoriesTests.swift @@ -12,18 +12,6 @@ import XCTest class SessionDirectoriesTests: XCTestCase { let fileManager = FileManager.default - func testInitWithUserID() { - // Given only a user ID. - let userID = "@user:matrix.org" - - // When creating the session directories using this. - let sessionDirectories = SessionDirectories(userID: userID) - - // Then the directories should be generated in the correct location, using an escaped version of the user ID - XCTAssertEqual(sessionDirectories.dataDirectory, .sessionsBaseDirectory.appending(component: "@user_matrix.org")) - XCTAssertEqual(sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: "@user_matrix.org")) - } - func testInitWithDataDirectory() { // Given only a session directory without a caches directory. let sessionDirectoryName = UUID().uuidString