diff --git a/RiotSwiftUI/Modules/Authentication/ChoosePassword/AuthenticationChoosePasswordModels.swift b/RiotSwiftUI/Modules/Authentication/ChoosePassword/AuthenticationChoosePasswordModels.swift index 4b8d2752a..3e75714e5 100644 --- a/RiotSwiftUI/Modules/Authentication/ChoosePassword/AuthenticationChoosePasswordModels.swift +++ b/RiotSwiftUI/Modules/Authentication/ChoosePassword/AuthenticationChoosePasswordModels.swift @@ -18,11 +18,21 @@ import SwiftUI // MARK: View model -enum AuthenticationChoosePasswordViewModelResult { +enum AuthenticationChoosePasswordViewModelResult: CustomStringConvertible { /// Submit with password and sign out of all devices option case submit(String, Bool) /// Cancel the flow. case cancel + + /// A string representation of the result, ignoring any associated values that could leak PII. + var description: String { + switch self { + case .submit: + return "submit" + case .cancel: + return "cancel" + } + } } // MARK: View diff --git a/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift b/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift index 049bfb65e..c5acf416d 100644 --- a/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift +++ b/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift @@ -18,7 +18,7 @@ import Foundation // MARK: View model -enum AuthenticationLoginViewModelResult { +enum AuthenticationLoginViewModelResult: CustomStringConvertible { /// The user would like to select another server. case selectServer /// Parse the username and update the homeserver if included. @@ -31,6 +31,24 @@ enum AuthenticationLoginViewModelResult { case continueWithSSO(SSOIdentityProvider) /// Continue using the fallback page case fallback + + /// A string representation of the result, ignoring any associated values that could leak PII. + var description: String { + switch self { + case .selectServer: + return "selectServer" + case .parseUsername: + return "parseUsername" + case .forgotPassword: + return "forgotPassword" + case .login: + return "login" + case .continueWithSSO(let provider): + return "continueWithSSO: \(provider)" + case .fallback: + return "fallback" + } + } } // MARK: View diff --git a/RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift b/RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift index cd53c70e8..f73cf3ad8 100644 --- a/RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift +++ b/RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift @@ -25,13 +25,25 @@ struct AuthenticationLoginCoordinatorParameters { let loginMode: LoginMode } -enum AuthenticationLoginCoordinatorResult { +enum AuthenticationLoginCoordinatorResult: CustomStringConvertible { /// Continue using the supplied SSO provider. case continueWithSSO(SSOIdentityProvider) /// Login was successful with the associated session created. case success(session: MXSession, password: String) /// Login requested a fallback case fallback + + /// A string representation of the result, ignoring any associated values that could leak PII. + var description: String { + switch self { + case .continueWithSSO(let provider): + return "continueWithSSO: \(provider)" + case .success: + return "success" + case .fallback: + return "fallback" + } + } } final class AuthenticationLoginCoordinator: Coordinator, Presentable { diff --git a/RiotSwiftUI/Modules/Authentication/Registration/AuthenticationRegistrationModels.swift b/RiotSwiftUI/Modules/Authentication/Registration/AuthenticationRegistrationModels.swift index 429554e4f..531cb1a67 100644 --- a/RiotSwiftUI/Modules/Authentication/Registration/AuthenticationRegistrationModels.swift +++ b/RiotSwiftUI/Modules/Authentication/Registration/AuthenticationRegistrationModels.swift @@ -18,7 +18,7 @@ import Foundation // MARK: View model -enum AuthenticationRegistrationViewModelResult { +enum AuthenticationRegistrationViewModelResult: CustomStringConvertible { /// The user would like to select another server. case selectServer /// Validate the supplied username with the homeserver. @@ -29,6 +29,22 @@ enum AuthenticationRegistrationViewModelResult { case continueWithSSO(SSOIdentityProvider) /// Continue using a fallback case fallback + + /// A string representation of the result, ignoring any associated values that could leak PII. + var description: String { + switch self { + case .selectServer: + return "selectServer" + case .validateUsername: + return "validateUsername" + case .createAccount: + return "createAccount" + case .continueWithSSO(let provider): + return "continueWithSSO: \(provider)" + case .fallback: + return "fallback" + } + } } // MARK: View diff --git a/RiotSwiftUI/Modules/Authentication/Registration/Coordinator/AuthenticationRegistrationCoordinator.swift b/RiotSwiftUI/Modules/Authentication/Registration/Coordinator/AuthenticationRegistrationCoordinator.swift index 1e040b66a..d1adfb74b 100644 --- a/RiotSwiftUI/Modules/Authentication/Registration/Coordinator/AuthenticationRegistrationCoordinator.swift +++ b/RiotSwiftUI/Modules/Authentication/Registration/Coordinator/AuthenticationRegistrationCoordinator.swift @@ -27,13 +27,25 @@ struct AuthenticationRegistrationCoordinatorParameters { let loginMode: LoginMode } -enum AuthenticationRegistrationCoordinatorResult { +enum AuthenticationRegistrationCoordinatorResult: CustomStringConvertible { /// Continue using the supplied SSO provider. case continueWithSSO(SSOIdentityProvider) /// The screen completed with the associated registration result. case completed(result: RegistrationResult, password: String) /// Continue using the fallback case fallback + + /// A string representation of the result, ignoring any associated values that could leak PII. + var description: String { + switch self { + case .continueWithSSO(let provider): + return "continueWithSSO: \(provider)" + case .completed: + return "completed" + case .fallback: + return "fallback" + } + } } final class AuthenticationRegistrationCoordinator: Coordinator, Presentable { diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutModels.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutModels.swift index ab414b306..7222af762 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutModels.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutModels.swift @@ -26,7 +26,7 @@ struct SoftLogoutCredentials { // MARK: View model -enum AuthenticationSoftLogoutViewModelResult { +enum AuthenticationSoftLogoutViewModelResult: CustomStringConvertible { /// Login with password case login(String) /// Forgot password @@ -37,6 +37,22 @@ enum AuthenticationSoftLogoutViewModelResult { case continueWithSSO(SSOIdentityProvider) /// Continue using the fallback page case fallback + + /// A string representation of the result, ignoring any associated values that could leak PII. + var description: String { + switch self { + case .login: + return "login" + case .forgotPassword: + return "forgotPassword" + case .clearAllData: + return "clearAllData" + case .continueWithSSO(let provider): + return "continueWithSSO: \(provider)" + case .fallback: + return "fallback" + } + } } // MARK: View diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/Coordinator/AuthenticationSoftLogoutCoordinator.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/Coordinator/AuthenticationSoftLogoutCoordinator.swift index cae6324f1..d044a14e9 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/Coordinator/AuthenticationSoftLogoutCoordinator.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/Coordinator/AuthenticationSoftLogoutCoordinator.swift @@ -24,7 +24,7 @@ struct AuthenticationSoftLogoutCoordinatorParameters { let keyBackupNeeded: Bool } -enum AuthenticationSoftLogoutCoordinatorResult { +enum AuthenticationSoftLogoutCoordinatorResult: CustomStringConvertible { /// Login was successful with the associated session created. case success(session: MXSession, password: String) /// Clear all user data @@ -33,6 +33,20 @@ enum AuthenticationSoftLogoutCoordinatorResult { case continueWithSSO(SSOIdentityProvider) /// Continue using the fallback page case fallback + + /// A string representation of the result, ignoring any associated values that could leak PII. + var description: String { + switch self { + case .success: + return "success" + case .clearAllData: + return "clearAllData" + case .continueWithSSO(let provider): + return "continueWithSSO: \(provider)" + case .fallback: + return "fallback" + } + } } @available(iOS 14.0, *) diff --git a/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordModels.swift b/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordModels.swift index 591144199..b79c29044 100644 --- a/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordModels.swift +++ b/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordModels.swift @@ -18,9 +18,17 @@ import SwiftUI // MARK: View model -enum ChangePasswordViewModelResult { +enum ChangePasswordViewModelResult: CustomStringConvertible { /// Submit with old and new passwords and sign out of all devices option case submit(oldPassword: String, newPassword: String, signoutAllDevices: Bool) + + /// A string representation of the result, ignoring any associated values that could leak PII. + var description: String { + switch self { + case .submit: + return "submit" + } + } } // MARK: View diff --git a/RiotTests/Modules/Authentication/AuthenticationServiceTests.swift b/RiotTests/Modules/Authentication/AuthenticationServiceTests.swift index 217c5aa8e..ace25dabe 100644 --- a/RiotTests/Modules/Authentication/AuthenticationServiceTests.swift +++ b/RiotTests/Modules/Authentication/AuthenticationServiceTests.swift @@ -316,4 +316,37 @@ import XCTest XCTAssertEqual(viewData.ssoIdentityProviders, [], "There shouldn't be any sso identity providers.") XCTAssertTrue(viewData.showRegistrationForm, "The registration form should be shown.") } + + func testLogsForPassword() { + // Given all of the coordinator and view model results that contain passwords. + let password = "supersecretpassword" + let loginViewModelResult: AuthenticationLoginViewModelResult = .login(username: "Alice", password: password) + let loginCoordinatorResult: AuthenticationLoginCoordinatorResult = .success(session: MXSession(), password: password) + let registerViewModelResult: AuthenticationRegistrationViewModelResult = .createAccount(username: "Alice", password: password) + let registerCoordinatorResult: AuthenticationRegistrationCoordinatorResult = .completed(result: RegistrationResult.success(MXSession()), password: password) + let softLogoutViewModelResult: AuthenticationSoftLogoutViewModelResult = .login(password) + let softLogoutCoordinatorResult: AuthenticationSoftLogoutCoordinatorResult = .success(session: MXSession(), password: password) + let forgotPasswordResult: AuthenticationChoosePasswordViewModelResult = .submit(password, false) + let changePasswordResult: ChangePasswordViewModelResult = .submit(oldPassword: password, newPassword: password, signoutAllDevices: false) + + // When creating a string representation of those results (e.g. for logging). + let loginViewModelString = "\(loginViewModelResult)" + let loginCoordinatorString = "\(loginCoordinatorResult)" + let registerViewModelString = "\(registerViewModelResult)" + let registerCoordinatorString = "\(registerCoordinatorResult)" + let softLogoutViewModelString = "\(softLogoutViewModelResult)" + let softLogoutCoordinatorString = "\(softLogoutCoordinatorResult)" + let forgotPasswordString = "\(forgotPasswordResult)" + let changePasswordString = "\(changePasswordResult)" + + // Then the password should not be included in that string. + XCTAssertFalse(loginViewModelString.contains(password), "The password must not be included in any strings.") + XCTAssertFalse(loginCoordinatorString.contains(password), "The password must not be included in any strings.") + XCTAssertFalse(registerViewModelString.contains(password), "The password must not be included in any strings.") + XCTAssertFalse(registerCoordinatorString.contains(password), "The password must not be included in any strings.") + XCTAssertFalse(softLogoutViewModelString.contains(password), "The password must not be included in any strings.") + XCTAssertFalse(softLogoutCoordinatorString.contains(password), "The password must not be included in any strings.") + XCTAssertFalse(forgotPasswordString.contains(password), "The password must not be included in any strings.") + XCTAssertFalse(changePasswordString.contains(password), "The password must not be included in any strings.") + } } diff --git a/changelog.d/5151.wip b/changelog.d/5151.wip new file mode 100644 index 000000000..d2f9ed726 --- /dev/null +++ b/changelog.d/5151.wip @@ -0,0 +1 @@ +Authentication: Add custom string representations of view model/coordinator results.