mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-18 23:48:29 +02:00
Add tests for FlowResult.
This commit is contained in:
@@ -15,91 +15,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// The parameters used for registration requests.
|
||||
struct RegistrationParameters: Codable {
|
||||
/// Authentication parameters
|
||||
var auth: AuthenticationParameters?
|
||||
|
||||
/// The account username
|
||||
var username: String?
|
||||
|
||||
/// The account password
|
||||
var password: String?
|
||||
|
||||
/// Device name
|
||||
var initialDeviceDisplayName: String?
|
||||
|
||||
/// Temporary flag to notify the server that we support MSISDN flow. Used to prevent old app
|
||||
/// versions to end up in fallback because the HS returns the MSISDN flow which they don't support
|
||||
var xShowMSISDN: Bool?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case auth
|
||||
case username
|
||||
case password
|
||||
case initialDeviceDisplayName = "initial_device_display_name"
|
||||
case xShowMSISDN = "x_show_msisdn"
|
||||
}
|
||||
|
||||
/// The parameters as a JSON dictionary for use in MXRestClient.
|
||||
func dictionary() throws -> [String: Any] {
|
||||
let jsonData = try JSONEncoder().encode(self)
|
||||
let object = try JSONSerialization.jsonObject(with: jsonData)
|
||||
guard let dictionary = object as? [String: Any] else {
|
||||
MXLog.error("[RegistrationParameters] dictionary: Unexpected type decoded \(type(of: object)). Expected a Dictionary.")
|
||||
throw AuthenticationError.dictionaryError
|
||||
}
|
||||
|
||||
return dictionary
|
||||
}
|
||||
}
|
||||
|
||||
/// The data passed to the `auth` parameter in authentication requests.
|
||||
struct AuthenticationParameters: Codable {
|
||||
/// The type of authentication taking place. The identifier from `MXLoginFlowType`.
|
||||
let type: String
|
||||
|
||||
/// Note: session can be null for reset password request
|
||||
var session: String?
|
||||
|
||||
/// parameter for "m.login.recaptcha" type
|
||||
var captchaResponse: String?
|
||||
|
||||
/// parameter for "m.login.email.identity" type
|
||||
var threePIDCredentials: ThreePIDCredentials?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case session
|
||||
case captchaResponse = "response"
|
||||
case threePIDCredentials = "threepid_creds"
|
||||
}
|
||||
|
||||
/// Creates the authentication parameters for a captcha step.
|
||||
static func captchaParameters(session: String, captchaResponse: String) -> AuthenticationParameters {
|
||||
AuthenticationParameters(type: kMXLoginFlowTypeRecaptcha, session: session, captchaResponse: captchaResponse)
|
||||
}
|
||||
|
||||
/// Creates the authentication parameters for a third party ID step using an email address.
|
||||
static func emailIdentityParameters(session: String, threePIDCredentials: ThreePIDCredentials) -> AuthenticationParameters {
|
||||
AuthenticationParameters(type: kMXLoginFlowTypeEmailIdentity, session: session, threePIDCredentials: threePIDCredentials)
|
||||
}
|
||||
|
||||
// Note that there is a bug in Synapse (needs investigation), but if we pass .msisdn,
|
||||
// the homeserver answer with the login flow with MatrixError fields and not with a simple MatrixError 401.
|
||||
/// Creates the authentication parameters for a third party ID step using a phone number.
|
||||
static func msisdnIdentityParameters(session: String, threePIDCredentials: ThreePIDCredentials) -> AuthenticationParameters {
|
||||
AuthenticationParameters(type: kMXLoginFlowTypeMSISDN, session: session, threePIDCredentials: threePIDCredentials)
|
||||
}
|
||||
|
||||
/// Creates the authentication parameters for a password reset step.
|
||||
static func resetPasswordParameters(clientSecret: String, sessionID: String) -> AuthenticationParameters {
|
||||
AuthenticationParameters(type: kMXLoginFlowTypeEmailIdentity,
|
||||
session: nil,
|
||||
threePIDCredentials: ThreePIDCredentials(clientSecret: clientSecret, sessionID: sessionID))
|
||||
}
|
||||
}
|
||||
import OrderedCollections
|
||||
|
||||
/// The result from a registration screen's coordinator
|
||||
enum AuthenticationRegistrationStageResult {
|
||||
@@ -126,24 +42,24 @@ struct FlowResult {
|
||||
|
||||
/// A stage in the authentication flow.
|
||||
enum Stage {
|
||||
/// The stage with the type `m.login.recaptcha`.
|
||||
case reCaptcha(isMandatory: Bool, siteKey: String)
|
||||
|
||||
/// The stage with the type `m.login.email.identity`.
|
||||
case email(isMandatory: Bool)
|
||||
|
||||
/// The stage with the type `m.login.msisdn`.
|
||||
case msisdn(isMandatory: Bool)
|
||||
|
||||
/// The stage with the type `m.login.terms`.
|
||||
case terms(isMandatory: Bool, terms: MXLoginTerms?)
|
||||
|
||||
/// The stage with the type `m.login.recaptcha`.
|
||||
case reCaptcha(isMandatory: Bool, siteKey: String)
|
||||
|
||||
/// The stage with the type `m.login.dummy`.
|
||||
///
|
||||
/// This stage can be mandatory if there is no other stages. In this case the account cannot
|
||||
/// be created by just sending a username and a password, the dummy stage has to be completed.
|
||||
case dummy(isMandatory: Bool)
|
||||
|
||||
/// The stage with the type `m.login.terms`.
|
||||
case terms(isMandatory: Bool, terms: MXLoginTerms?)
|
||||
|
||||
/// A stage of an unknown type.
|
||||
case other(isMandatory: Bool, type: String, params: [AnyHashable: Any])
|
||||
|
||||
@@ -172,8 +88,8 @@ struct FlowResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines the next stage to be completed in the flow.
|
||||
var nextUncompletedStage: Stage? {
|
||||
/// Determines the next stage to be completed in the flow, following the order Email → Terms → ReCaptcha.
|
||||
var nextUncompletedStageOrdered: Stage? {
|
||||
if let emailStage = missingStages.first(where: { if case .email = $0 { return true } else { return false } }) {
|
||||
return emailStage
|
||||
}
|
||||
@@ -183,16 +99,23 @@ struct FlowResult {
|
||||
if let reCaptchaStage = missingStages.first(where: { if case .reCaptcha = $0 { return true } else { return false } }) {
|
||||
return reCaptchaStage
|
||||
}
|
||||
if let msisdnStage = missingStages.first(where: { if case .msisdn = $0 { return true } else { return false } }) {
|
||||
return msisdnStage
|
||||
}
|
||||
|
||||
MXLog.failure("[FlowResult.Stage] nextUncompletedStage: The dummy stage should be handled silently and any other stages should trigger the fallback flow.")
|
||||
return nextUncompletedStage
|
||||
}
|
||||
|
||||
/// Determines the next stage to be completed in the flow honouring the server's ordering.
|
||||
/// This ordering is slightly broken when the are multiple flows as mandatory stages are
|
||||
/// shown first and then optional ones afterwards.
|
||||
var nextUncompletedStage: Stage? {
|
||||
if let mandatoryStage = missingStages.filter(\.isMandatory).first {
|
||||
return mandatoryStage
|
||||
}
|
||||
return missingStages.first
|
||||
}
|
||||
|
||||
/// Whether fallback registration should be used due to unsupported stages.
|
||||
var needsFallback : Bool {
|
||||
missingStages.filter { $0.isMandatory }.contains { stage in
|
||||
missingStages.filter(\.isMandatory).contains { stage in
|
||||
if case .other = stage { return true } else { return false }
|
||||
}
|
||||
}
|
||||
@@ -201,7 +124,7 @@ struct FlowResult {
|
||||
extension MXAuthenticationSession {
|
||||
/// The flows from the session mapped as a `FlowResult` value.
|
||||
var flowResult: FlowResult {
|
||||
let allFlowTypes = Set(flows.flatMap { $0.stages ?? [] }) // Using a Set here loses the order, but an order is forced during presentation anyway.
|
||||
let allFlowTypes = OrderedSet(flows.flatMap { $0.stages ?? [] })
|
||||
var missingStages = [FlowResult.Stage]()
|
||||
var completedStages = [FlowResult.Stage]()
|
||||
|
||||
@@ -239,3 +162,28 @@ extension MXAuthenticationSession {
|
||||
return FlowResult(missingStages: missingStages, completedStages: completedStages)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
|
||||
extension FlowResult.Stage: Equatable {
|
||||
// The [AnyHashable: Any] dictionary breaks automatic conformance, so add manually (but ignore this value).
|
||||
static func == (lhs: FlowResult.Stage, rhs: FlowResult.Stage) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.email(let lhsMandatory), .email(let rhsMandatory)):
|
||||
return lhsMandatory == rhsMandatory
|
||||
case (.msisdn(let lhsMandatory), .msisdn(let rhsMandatory)):
|
||||
return lhsMandatory == rhsMandatory
|
||||
case (.terms(let lhsMandatory, let lhsTerms), .terms(let rhsMandatory, let rhsTerms)):
|
||||
// TODO: Add comprehensive Equatable conformance on MXLoginTerms
|
||||
return lhsMandatory == rhsMandatory && lhsTerms?.policies == rhsTerms?.policies
|
||||
case (.reCaptcha(let lhsMandatory, let lhsSiteKey), .reCaptcha(let rhsMandatory, let rhsSiteKey)):
|
||||
return lhsMandatory == rhsMandatory && lhsSiteKey == rhsSiteKey
|
||||
case (.dummy(let lhsMandatory), .dummy(let rhsMandatory)):
|
||||
return lhsMandatory == rhsMandatory
|
||||
case (.other(let lhsMandatory, let lhsType, _), .other(let rhsMandatory, let rhsType, _)):
|
||||
return lhsMandatory == rhsMandatory && lhsType == rhsType
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user