mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Make the SessionDirectories type responsible for cleaning up data. (#3261)
This commit is contained in:
parent
84a3ffc135
commit
af3a6ccbed
@ -57,4 +57,8 @@ extension FileManager {
|
|||||||
|
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func numberOfItems(at url: URL) throws -> Int {
|
||||||
|
try contentsOfDirectory(at: url, includingPropertiesForKeys: nil).count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,13 +140,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func rotateSessionDirectory() {
|
private func rotateSessionDirectory() {
|
||||||
if FileManager.default.directoryExists(at: sessionDirectories.dataDirectory) {
|
sessionDirectories.delete()
|
||||||
try? FileManager.default.removeItem(at: sessionDirectories.dataDirectory)
|
|
||||||
}
|
|
||||||
if FileManager.default.directoryExists(at: sessionDirectories.cacheDirectory) {
|
|
||||||
try? FileManager.default.removeItem(at: sessionDirectories.cacheDirectory)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionDirectories = .init()
|
sessionDirectories = .init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,8 +110,7 @@ class KeychainController: KeychainControllerProtocol {
|
|||||||
fatalError("Something has gone mega wrong, all bets are off.")
|
fatalError("Something has gone mega wrong, all bets are off.")
|
||||||
}
|
}
|
||||||
let restorationToken = RestorationToken(session: session,
|
let restorationToken = RestorationToken(session: session,
|
||||||
sessionDirectory: oldToken.sessionDirectory,
|
sessionDirectories: oldToken.sessionDirectories,
|
||||||
cacheDirectory: oldToken.cacheDirectory,
|
|
||||||
passphrase: oldToken.passphrase,
|
passphrase: oldToken.passphrase,
|
||||||
pusherNotificationClientIdentifier: oldToken.pusherNotificationClientIdentifier)
|
pusherNotificationClientIdentifier: oldToken.pusherNotificationClientIdentifier)
|
||||||
setRestorationToken(restorationToken, forUsername: session.userId)
|
setRestorationToken(restorationToken, forUsername: session.userId)
|
||||||
|
@ -77,13 +77,7 @@ final class QRCodeLoginService: QRCodeLoginServiceProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func rotateSessionDirectory() {
|
private func rotateSessionDirectory() {
|
||||||
if FileManager.default.directoryExists(at: sessionDirectories.dataDirectory) {
|
sessionDirectories.delete()
|
||||||
try? FileManager.default.removeItem(at: sessionDirectories.dataDirectory)
|
|
||||||
}
|
|
||||||
if FileManager.default.directoryExists(at: sessionDirectories.cacheDirectory) {
|
|
||||||
try? FileManager.default.removeItem(at: sessionDirectories.cacheDirectory)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionDirectories = .init()
|
sessionDirectories = .init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,10 +10,17 @@ import MatrixRustSDK
|
|||||||
|
|
||||||
struct RestorationToken: Equatable {
|
struct RestorationToken: Equatable {
|
||||||
let session: MatrixRustSDK.Session
|
let session: MatrixRustSDK.Session
|
||||||
let sessionDirectory: URL
|
let sessionDirectories: SessionDirectories
|
||||||
let cacheDirectory: URL
|
|
||||||
let passphrase: String?
|
let passphrase: String?
|
||||||
let pusherNotificationClientIdentifier: String?
|
let pusherNotificationClientIdentifier: String?
|
||||||
|
|
||||||
|
enum CodingKeys: CodingKey {
|
||||||
|
case session
|
||||||
|
case sessionDirectory
|
||||||
|
case cacheDirectory
|
||||||
|
case passphrase
|
||||||
|
case pusherNotificationClientIdentifier
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RestorationToken: Codable {
|
extension RestorationToken: Codable {
|
||||||
@ -35,11 +42,19 @@ extension RestorationToken: Codable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self = try .init(session: session,
|
self = try .init(session: session,
|
||||||
sessionDirectory: sessionDirectories.dataDirectory,
|
sessionDirectories: sessionDirectories,
|
||||||
cacheDirectory: sessionDirectories.cacheDirectory,
|
|
||||||
passphrase: container.decodeIfPresent(String.self, forKey: .passphrase),
|
passphrase: container.decodeIfPresent(String.self, forKey: .passphrase),
|
||||||
pusherNotificationClientIdentifier: container.decodeIfPresent(String.self, forKey: .pusherNotificationClientIdentifier))
|
pusherNotificationClientIdentifier: container.decodeIfPresent(String.self, forKey: .pusherNotificationClientIdentifier))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encode(to encoder: any Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
try container.encode(session, forKey: .session)
|
||||||
|
try container.encode(sessionDirectories.dataDirectory, forKey: .sessionDirectory)
|
||||||
|
try container.encode(sessionDirectories.cacheDirectory, forKey: .cacheDirectory)
|
||||||
|
try container.encode(passphrase, forKey: .passphrase)
|
||||||
|
try container.encode(pusherNotificationClientIdentifier, forKey: .pusherNotificationClientIdentifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MatrixRustSDK.Session: Codable {
|
extension MatrixRustSDK.Session: Codable {
|
||||||
|
@ -13,6 +13,50 @@ struct SessionDirectories: Hashable, Codable {
|
|||||||
|
|
||||||
var dataPath: String { dataDirectory.path(percentEncoded: false) }
|
var dataPath: String { dataDirectory.path(percentEncoded: false) }
|
||||||
var cachePath: String { cacheDirectory.path(percentEncoded: false) }
|
var cachePath: String { cacheDirectory.path(percentEncoded: false) }
|
||||||
|
|
||||||
|
// MARK: Data Management
|
||||||
|
|
||||||
|
/// Removes the directories from disk if they have been created.
|
||||||
|
func delete() {
|
||||||
|
do {
|
||||||
|
if FileManager.default.directoryExists(at: dataDirectory) {
|
||||||
|
try FileManager.default.removeItem(at: dataDirectory)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
MXLog.failure("Failed deleting the session data: \(error)")
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if FileManager.default.directoryExists(at: cacheDirectory) {
|
||||||
|
try FileManager.default.removeItem(at: cacheDirectory)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
MXLog.failure("Failed deleting the session caches: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the Rust state store and event cache data, leaving the crypto store and both
|
||||||
|
/// session directories in place along with any other data that may have been written in them.
|
||||||
|
func deleteTransientUserData() {
|
||||||
|
do {
|
||||||
|
let prefix = "matrix-sdk-state"
|
||||||
|
try deleteFiles(at: dataDirectory, with: prefix)
|
||||||
|
} catch {
|
||||||
|
MXLog.failure("Failed clearing state store: \(error)")
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let prefix = "matrix-sdk-event-cache"
|
||||||
|
try deleteFiles(at: cacheDirectory, with: prefix)
|
||||||
|
} catch {
|
||||||
|
MXLog.failure("Failed clearing event cache store: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deleteFiles(at url: URL, with prefix: String) throws {
|
||||||
|
let sessionDirectoryContents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
|
||||||
|
for url in sessionDirectoryContents where url.lastPathComponent.hasPrefix(prefix) {
|
||||||
|
try FileManager.default.removeItem(at: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SessionDirectories {
|
extension SessionDirectories {
|
||||||
|
@ -14,7 +14,6 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
private let appSettings: AppSettings
|
private let appSettings: AppSettings
|
||||||
private let networkMonitor: NetworkMonitorProtocol
|
private let networkMonitor: NetworkMonitorProtocol
|
||||||
private let appHooks: AppHooks
|
private let appHooks: AppHooks
|
||||||
private let matrixSDKStateKey = "matrix-sdk-state"
|
|
||||||
|
|
||||||
/// Whether or not there are sessions in the store.
|
/// Whether or not there are sessions in the store.
|
||||||
var hasSessions: Bool { !keychainController.restorationTokens().isEmpty }
|
var hasSessions: Bool { !keychainController.restorationTokens().isEmpty }
|
||||||
@ -55,7 +54,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
|
|
||||||
// On any restoration failure reset the token and restart
|
// On any restoration failure reset the token and restart
|
||||||
keychainController.removeRestorationTokenForUsername(credentials.userID)
|
keychainController.removeRestorationTokenForUsername(credentials.userID)
|
||||||
deleteSessionDirectories(for: credentials)
|
credentials.restorationToken.sessionDirectories.delete()
|
||||||
|
|
||||||
return .failure(error)
|
return .failure(error)
|
||||||
}
|
}
|
||||||
@ -68,8 +67,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
let clientProxy = await setupProxyForClient(client)
|
let clientProxy = await setupProxyForClient(client)
|
||||||
|
|
||||||
keychainController.setRestorationToken(RestorationToken(session: session,
|
keychainController.setRestorationToken(RestorationToken(session: session,
|
||||||
sessionDirectory: sessionDirectories.dataDirectory,
|
sessionDirectories: sessionDirectories,
|
||||||
cacheDirectory: sessionDirectories.cacheDirectory,
|
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
pusherNotificationClientIdentifier: clientProxy.pusherNotificationClientIdentifier),
|
pusherNotificationClientIdentifier: clientProxy.pusherNotificationClientIdentifier),
|
||||||
forUsername: userID)
|
forUsername: userID)
|
||||||
@ -87,7 +85,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
keychainController.removeRestorationTokenForUsername(userID)
|
keychainController.removeRestorationTokenForUsername(userID)
|
||||||
|
|
||||||
if let credentials {
|
if let credentials {
|
||||||
deleteSessionDirectories(for: credentials)
|
credentials.restorationToken.sessionDirectories.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +94,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
MXLog.error("Failed to clearing caches: Credentials missing")
|
MXLog.error("Failed to clearing caches: Credentials missing")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
deleteCaches(for: credentials)
|
credentials.restorationToken.sessionDirectories.deleteTransientUserData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
@ -125,8 +123,8 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
slidingSync: .restored,
|
slidingSync: .restored,
|
||||||
sessionDelegate: keychainController,
|
sessionDelegate: keychainController,
|
||||||
appHooks: appHooks)
|
appHooks: appHooks)
|
||||||
.sessionPaths(dataPath: credentials.restorationToken.sessionDirectory.path(percentEncoded: false),
|
.sessionPaths(dataPath: credentials.restorationToken.sessionDirectories.dataPath,
|
||||||
cachePath: credentials.restorationToken.cacheDirectory.path(percentEncoded: false))
|
cachePath: credentials.restorationToken.sessionDirectories.cachePath)
|
||||||
.username(username: credentials.userID)
|
.username(username: credentials.userID)
|
||||||
.homeserverUrl(url: homeserverURL)
|
.homeserverUrl(url: homeserverURL)
|
||||||
.passphrase(passphrase: credentials.restorationToken.passphrase)
|
.passphrase(passphrase: credentials.restorationToken.passphrase)
|
||||||
@ -148,37 +146,4 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
networkMonitor: networkMonitor,
|
networkMonitor: networkMonitor,
|
||||||
appSettings: appSettings)
|
appSettings: appSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func deleteSessionDirectories(for credentials: KeychainCredentials) {
|
|
||||||
do {
|
|
||||||
try FileManager.default.removeItem(at: credentials.restorationToken.sessionDirectory)
|
|
||||||
} catch {
|
|
||||||
MXLog.failure("Failed deleting the session data: \(error)")
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try FileManager.default.removeItem(at: credentials.restorationToken.cacheDirectory)
|
|
||||||
} catch {
|
|
||||||
MXLog.failure("Failed deleting the session caches: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func deleteCaches(for credentials: KeychainCredentials) {
|
|
||||||
do {
|
|
||||||
try deleteContentsOfDirectory(at: credentials.restorationToken.sessionDirectory)
|
|
||||||
} catch {
|
|
||||||
MXLog.failure("Failed clearing state store: \(error)")
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try deleteContentsOfDirectory(at: credentials.restorationToken.cacheDirectory)
|
|
||||||
} catch {
|
|
||||||
MXLog.failure("Failed clearing event cache store: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func deleteContentsOfDirectory(at url: URL) throws {
|
|
||||||
let sessionDirectoryContents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
|
|
||||||
for url in sessionDirectoryContents where url.path.contains(matrixSDKStateKey) {
|
|
||||||
try FileManager.default.removeItem(at: url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ final class NSEUserSession {
|
|||||||
slidingSync: .restored,
|
slidingSync: .restored,
|
||||||
sessionDelegate: clientSessionDelegate,
|
sessionDelegate: clientSessionDelegate,
|
||||||
appHooks: appHooks)
|
appHooks: appHooks)
|
||||||
.sessionPaths(dataPath: credentials.restorationToken.sessionDirectory.path(percentEncoded: false),
|
.sessionPaths(dataPath: credentials.restorationToken.sessionDirectories.dataPath,
|
||||||
cachePath: credentials.restorationToken.cacheDirectory.path(percentEncoded: false))
|
cachePath: credentials.restorationToken.sessionDirectories.cachePath)
|
||||||
.username(username: credentials.userID)
|
.username(username: credentials.userID)
|
||||||
.homeserverUrl(url: homeserverURL)
|
.homeserverUrl(url: homeserverURL)
|
||||||
.passphrase(passphrase: credentials.restorationToken.passphrase)
|
.passphrase(passphrase: credentials.restorationToken.passphrase)
|
||||||
|
@ -31,8 +31,7 @@ class KeychainControllerTests: XCTestCase {
|
|||||||
homeserverUrl: "homeserverUrl",
|
homeserverUrl: "homeserverUrl",
|
||||||
oidcData: "oidcData",
|
oidcData: "oidcData",
|
||||||
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
|
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
|
||||||
sessionDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
sessionDirectories: .init(),
|
||||||
cacheDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
|
||||||
passphrase: "passphrase",
|
passphrase: "passphrase",
|
||||||
pusherNotificationClientIdentifier: "pusherClientID")
|
pusherNotificationClientIdentifier: "pusherClientID")
|
||||||
keychain.setRestorationToken(restorationToken, forUsername: username)
|
keychain.setRestorationToken(restorationToken, forUsername: username)
|
||||||
@ -51,8 +50,7 @@ class KeychainControllerTests: XCTestCase {
|
|||||||
homeserverUrl: "homeserverUrl",
|
homeserverUrl: "homeserverUrl",
|
||||||
oidcData: "oidcData",
|
oidcData: "oidcData",
|
||||||
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
|
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
|
||||||
sessionDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
sessionDirectories: .init(),
|
||||||
cacheDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
|
||||||
passphrase: "passphrase",
|
passphrase: "passphrase",
|
||||||
pusherNotificationClientIdentifier: "pusherClientID")
|
pusherNotificationClientIdentifier: "pusherClientID")
|
||||||
keychain.setRestorationToken(restorationToken, forUsername: username)
|
keychain.setRestorationToken(restorationToken, forUsername: username)
|
||||||
@ -77,8 +75,7 @@ class KeychainControllerTests: XCTestCase {
|
|||||||
homeserverUrl: "homeserverUrl",
|
homeserverUrl: "homeserverUrl",
|
||||||
oidcData: "oidcData",
|
oidcData: "oidcData",
|
||||||
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
|
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
|
||||||
sessionDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
sessionDirectories: .init(),
|
||||||
cacheDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
|
||||||
passphrase: "passphrase",
|
passphrase: "passphrase",
|
||||||
pusherNotificationClientIdentifier: "pusherClientID")
|
pusherNotificationClientIdentifier: "pusherClientID")
|
||||||
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
|
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
|
||||||
@ -102,8 +99,7 @@ class KeychainControllerTests: XCTestCase {
|
|||||||
homeserverUrl: "homeserverUrl",
|
homeserverUrl: "homeserverUrl",
|
||||||
oidcData: "oidcData",
|
oidcData: "oidcData",
|
||||||
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
|
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
|
||||||
sessionDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
sessionDirectories: .init(),
|
||||||
cacheDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
|
||||||
passphrase: "passphrase",
|
passphrase: "passphrase",
|
||||||
pusherNotificationClientIdentifier: "pusherClientID")
|
pusherNotificationClientIdentifier: "pusherClientID")
|
||||||
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
|
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
|
||||||
@ -135,8 +131,7 @@ class KeychainControllerTests: XCTestCase {
|
|||||||
homeserverUrl: "homeserverUrl",
|
homeserverUrl: "homeserverUrl",
|
||||||
oidcData: "oidcData",
|
oidcData: "oidcData",
|
||||||
slidingSyncVersion: .native),
|
slidingSyncVersion: .native),
|
||||||
sessionDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
sessionDirectories: .init(),
|
||||||
cacheDirectory: .homeDirectory.appending(component: UUID().uuidString),
|
|
||||||
passphrase: "passphrase",
|
passphrase: "passphrase",
|
||||||
pusherNotificationClientIdentifier: "pusherClientID")
|
pusherNotificationClientIdentifier: "pusherClientID")
|
||||||
keychain.setRestorationToken(restorationToken, forUsername: username)
|
keychain.setRestorationToken(restorationToken, forUsername: username)
|
||||||
|
@ -29,9 +29,9 @@ class RestorationTokenTests: XCTestCase {
|
|||||||
XCTAssertEqual(decodedToken.session, originalToken.session, "The session should not be changed.")
|
XCTAssertEqual(decodedToken.session, originalToken.session, "The session should not be changed.")
|
||||||
XCTAssertNil(decodedToken.passphrase, "There should not be a passphrase.")
|
XCTAssertNil(decodedToken.passphrase, "There should not be a passphrase.")
|
||||||
XCTAssertNil(decodedToken.pusherNotificationClientIdentifier, "There should not be a push notification client ID.")
|
XCTAssertNil(decodedToken.pusherNotificationClientIdentifier, "There should not be a push notification client ID.")
|
||||||
XCTAssertEqual(decodedToken.sessionDirectory, .sessionsBaseDirectory.appending(component: "@user_example.com"),
|
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.")
|
"The session directory should match the original location set by the Rust SDK from our base directory.")
|
||||||
XCTAssertEqual(decodedToken.cacheDirectory, .cachesBaseDirectory.appending(component: "@user_example.com"),
|
XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .cachesBaseDirectory.appending(component: "@user_example.com"),
|
||||||
"The cache directory should be derived from the session directory but in the caches directory.")
|
"The cache directory should be derived from the session directory but in the caches directory.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,15 +58,16 @@ class RestorationTokenTests: XCTestCase {
|
|||||||
XCTAssertEqual(decodedToken.passphrase, originalToken.passphrase, "The passphrase should not be changed.")
|
XCTAssertEqual(decodedToken.passphrase, originalToken.passphrase, "The passphrase should not be changed.")
|
||||||
XCTAssertEqual(decodedToken.pusherNotificationClientIdentifier, originalToken.pusherNotificationClientIdentifier,
|
XCTAssertEqual(decodedToken.pusherNotificationClientIdentifier, originalToken.pusherNotificationClientIdentifier,
|
||||||
"The push notification client identifier should not be changed.")
|
"The push notification client identifier should not be changed.")
|
||||||
XCTAssertEqual(decodedToken.sessionDirectory, originalToken.sessionDirectory, "The session directory should not be changed.")
|
XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, originalToken.sessionDirectory,
|
||||||
XCTAssertEqual(decodedToken.cacheDirectory, .cachesBaseDirectory.appending(component: sessionDirectoryName),
|
"The session directory should not be changed.")
|
||||||
|
XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .cachesBaseDirectory.appending(component: sessionDirectoryName),
|
||||||
"The cache directory should be derived from the session directory but in the caches directory.")
|
"The cache directory should be derived from the session directory but in the caches directory.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDecodeFromCurrentToken() throws {
|
func testDecodeFromTokenV5() throws {
|
||||||
// Given an encoded restoration token in the current format.
|
// Given an encoded restoration token in the 5th format that contains separate directories for session data and caches.
|
||||||
let sessionDirectoryName = UUID().uuidString
|
let sessionDirectoryName = UUID().uuidString
|
||||||
let originalToken = RestorationToken(session: Session(accessToken: "1234",
|
let originalToken = RestorationTokenV5(session: Session(accessToken: "1234",
|
||||||
refreshToken: "5678",
|
refreshToken: "5678",
|
||||||
userId: "@user:example.com",
|
userId: "@user:example.com",
|
||||||
deviceId: "D3V1C3",
|
deviceId: "D3V1C3",
|
||||||
@ -82,6 +83,34 @@ class RestorationTokenTests: XCTestCase {
|
|||||||
// When decoding the data.
|
// When decoding the data.
|
||||||
let decodedToken = try JSONDecoder().decode(RestorationToken.self, from: data)
|
let decodedToken = try JSONDecoder().decode(RestorationToken.self, from: data)
|
||||||
|
|
||||||
|
// Then the output should be a valid token.
|
||||||
|
XCTAssertEqual(decodedToken.session, originalToken.session, "The session should not be changed.")
|
||||||
|
XCTAssertEqual(decodedToken.passphrase, originalToken.passphrase, "The passphrase should not be changed.")
|
||||||
|
XCTAssertEqual(decodedToken.pusherNotificationClientIdentifier, originalToken.pusherNotificationClientIdentifier,
|
||||||
|
"The push notification client identifier should not be changed.")
|
||||||
|
XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, originalToken.sessionDirectory,
|
||||||
|
"The session directory should not be changed.")
|
||||||
|
XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, originalToken.cacheDirectory,
|
||||||
|
"The cache directory should not be changed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDecodeFromCurrentToken() throws {
|
||||||
|
// Given an encoded restoration token in the current format.
|
||||||
|
let originalToken = RestorationToken(session: Session(accessToken: "1234",
|
||||||
|
refreshToken: "5678",
|
||||||
|
userId: "@user:example.com",
|
||||||
|
deviceId: "D3V1C3",
|
||||||
|
homeserverUrl: "https://matrix.example.com",
|
||||||
|
oidcData: "data-from-mas",
|
||||||
|
slidingSyncVersion: .native),
|
||||||
|
sessionDirectories: .init(),
|
||||||
|
passphrase: "passphrase",
|
||||||
|
pusherNotificationClientIdentifier: "pusher-identifier")
|
||||||
|
let data = try JSONEncoder().encode(originalToken)
|
||||||
|
|
||||||
|
// When decoding the data.
|
||||||
|
let decodedToken = try JSONDecoder().decode(RestorationToken.self, from: data)
|
||||||
|
|
||||||
// Then the output should be a valid token.
|
// Then the output should be a valid token.
|
||||||
XCTAssertEqual(decodedToken, originalToken, "The token should remain identical.")
|
XCTAssertEqual(decodedToken, originalToken, "The token should remain identical.")
|
||||||
}
|
}
|
||||||
@ -97,3 +126,11 @@ struct RestorationTokenV4: Equatable, Codable {
|
|||||||
let passphrase: String?
|
let passphrase: String?
|
||||||
let pusherNotificationClientIdentifier: String?
|
let pusherNotificationClientIdentifier: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RestorationTokenV5: Equatable, Codable {
|
||||||
|
let session: MatrixRustSDK.Session
|
||||||
|
let sessionDirectory: URL
|
||||||
|
let cacheDirectory: URL
|
||||||
|
let passphrase: String?
|
||||||
|
let pusherNotificationClientIdentifier: String?
|
||||||
|
}
|
||||||
|
@ -10,6 +10,8 @@ import XCTest
|
|||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
|
|
||||||
class SessionDirectoriesTests: XCTestCase {
|
class SessionDirectoriesTests: XCTestCase {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
|
||||||
func testInitWithUserID() {
|
func testInitWithUserID() {
|
||||||
// Given only a user ID.
|
// Given only a user ID.
|
||||||
let userID = "@user:matrix.org"
|
let userID = "@user:matrix.org"
|
||||||
@ -51,4 +53,67 @@ class SessionDirectoriesTests: XCTestCase {
|
|||||||
XCTAssertEqual(returnedDataPath, originalDataPath)
|
XCTAssertEqual(returnedDataPath, originalDataPath)
|
||||||
XCTAssertEqual(returnedCachePath, originalCachePath)
|
XCTAssertEqual(returnedCachePath, originalCachePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDeleteDirectories() throws {
|
||||||
|
// Given a new set of session directories.
|
||||||
|
let sessionDirectories = SessionDirectories()
|
||||||
|
try fileManager.createDirectory(at: sessionDirectories.dataDirectory, withIntermediateDirectories: true)
|
||||||
|
try fileManager.createDirectory(at: sessionDirectories.cacheDirectory, withIntermediateDirectories: true)
|
||||||
|
XCTAssertTrue(fileManager.directoryExists(at: sessionDirectories.dataDirectory))
|
||||||
|
XCTAssertTrue(fileManager.directoryExists(at: sessionDirectories.cacheDirectory))
|
||||||
|
|
||||||
|
// When deleting the directories.
|
||||||
|
sessionDirectories.delete()
|
||||||
|
|
||||||
|
// Then neither directory should exist on disk.
|
||||||
|
XCTAssertFalse(fileManager.directoryExists(at: sessionDirectories.dataDirectory))
|
||||||
|
XCTAssertFalse(fileManager.directoryExists(at: sessionDirectories.cacheDirectory))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDeleteTransientUserData() throws {
|
||||||
|
// Given a set of session directories with some databases.
|
||||||
|
let sessionDirectories = SessionDirectories()
|
||||||
|
try fileManager.createDirectory(at: sessionDirectories.dataDirectory, withIntermediateDirectories: true)
|
||||||
|
try fileManager.createDirectory(at: sessionDirectories.cacheDirectory, withIntermediateDirectories: true)
|
||||||
|
XCTAssertTrue(fileManager.directoryExists(at: sessionDirectories.dataDirectory))
|
||||||
|
XCTAssertTrue(fileManager.directoryExists(at: sessionDirectories.cacheDirectory))
|
||||||
|
|
||||||
|
sessionDirectories.generateMockData()
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: sessionDirectories.mockStateStorePath))
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: sessionDirectories.mockCryptoStorePath))
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: sessionDirectories.mockEventCachePath))
|
||||||
|
XCTAssertEqual(try fileManager.numberOfItems(at: sessionDirectories.dataDirectory), 6)
|
||||||
|
XCTAssertEqual(try fileManager.numberOfItems(at: sessionDirectories.cacheDirectory), 3)
|
||||||
|
|
||||||
|
// When deleting transient user data.
|
||||||
|
sessionDirectories.deleteTransientUserData()
|
||||||
|
|
||||||
|
// Then the data directory should only contain the crypto store and the cache directory should remain but be empty.
|
||||||
|
XCTAssertTrue(fileManager.directoryExists(at: sessionDirectories.dataDirectory))
|
||||||
|
XCTAssertEqual(try fileManager.numberOfItems(at: sessionDirectories.dataDirectory), 3)
|
||||||
|
XCTAssertFalse(fileManager.fileExists(atPath: sessionDirectories.mockStateStorePath))
|
||||||
|
XCTAssertTrue(fileManager.fileExists(atPath: sessionDirectories.mockCryptoStorePath))
|
||||||
|
|
||||||
|
XCTAssertTrue(fileManager.directoryExists(at: sessionDirectories.cacheDirectory))
|
||||||
|
XCTAssertEqual(try fileManager.numberOfItems(at: sessionDirectories.cacheDirectory), 0)
|
||||||
|
XCTAssertFalse(fileManager.fileExists(atPath: sessionDirectories.mockEventCachePath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension SessionDirectories {
|
||||||
|
var mockStateStorePath: String { dataDirectory.appending(component: "matrix-sdk-state.sqlite3").path(percentEncoded: false) }
|
||||||
|
var mockCryptoStorePath: String { dataDirectory.appending(component: "matrix-sdk-crypto.sqlite3").path(percentEncoded: false) }
|
||||||
|
var mockEventCachePath: String { cacheDirectory.appending(component: "matrix-sdk-event-cache.sqlite3").path(percentEncoded: false) }
|
||||||
|
|
||||||
|
func generateMockData() {
|
||||||
|
generateMockDatabase(atPath: mockStateStorePath)
|
||||||
|
generateMockDatabase(atPath: mockCryptoStorePath)
|
||||||
|
generateMockDatabase(atPath: mockEventCachePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func generateMockDatabase(atPath path: String) {
|
||||||
|
FileManager.default.createFile(atPath: path, contents: nil)
|
||||||
|
FileManager.default.createFile(atPath: path + "-shm", contents: nil)
|
||||||
|
FileManager.default.createFile(atPath: path + "-wal", contents: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user