amplify-swift/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/StateMachine/Resolvers/Authentication/AuthenticationState+Resolve...

276 lines
12 KiB
Swift

//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
extension AuthenticationState {
struct Resolver: StateMachineResolver {
typealias StateType = AuthenticationState
let defaultState = AuthenticationState.notConfigured
init() { }
func resolve(
oldState: StateType,
byApplying event: StateMachineEvent
) -> StateResolution<StateType> {
switch oldState {
case .notConfigured:
if let authEvent = event as? AuthenticationEvent {
return resolveNotConfigured(byApplying: authEvent)
} else if let authZEvent = event.isAuthorizationEvent,
case .startFederationToIdentityPool = authZEvent {
return .init(newState: .federatingToIdentityPool)
} else {
return .from(oldState)
}
case .configured:
if let authEvent = event as? AuthenticationEvent {
return resolveConfigured(byApplying: authEvent)
} else {
return .from(oldState)
}
case .signingOut(let signOutState):
return resolveSigningOutState(byApplying: event,
to: signOutState)
case .signedOut(let signedOutData):
if let authEvent = event as? AuthenticationEvent {
return resolveSignedOut( byApplying: authEvent, to: signedOutData)
} else if let authZEvent = event.isAuthorizationEvent,
case .startFederationToIdentityPool = authZEvent {
return .init(newState: .federatingToIdentityPool)
} else {
return .from(oldState)
}
case .signingIn:
return resolveSigningInState(oldState: oldState, event: event)
case .signedIn(let signedInData):
if let authEvent = event as? AuthenticationEvent {
return resolveSignedIn(byApplying: authEvent, to: signedInData)
} else if let deleteUserEvent = event as? DeleteUserEvent {
return resolveDeleteUser(
byApplying: deleteUserEvent,
to: .notStarted,
with: signedInData)
} else {
return .from(oldState)
}
case .federatedToIdentityPool:
if let authEvent = event as? AuthenticationEvent,
case .clearFederationToIdentityPool = authEvent.eventType {
return .init(
newState: .clearingFederation,
actions: [
ClearFederationToIdentityPool()
])
} else if let authZEvent = event.isAuthorizationEvent,
case .startFederationToIdentityPool = authZEvent {
return .init(newState: .federatingToIdentityPool)
} else {
return .from(oldState)
}
case .federatingToIdentityPool:
guard let authZEvent = event.isAuthorizationEvent else {
return .from(oldState)
}
switch authZEvent {
case .sessionEstablished:
return .init(newState: .federatedToIdentityPool)
case .throwError(let authZError):
let authNError = AuthenticationError.service(
message: "Authorization error: \(authZError)")
return .init(newState: .error(authNError))
case .receivedSessionError(let sessionError):
let authNError = AuthenticationError.service(
message: "Session error: \(sessionError)")
return .init(newState: .error(authNError))
default:
return .from(oldState)
}
case .clearingFederation:
if let authEvent = event as? AuthenticationEvent,
case .clearedFederationToIdentityPool = authEvent.eventType {
return .init(newState: .signedOut(SignedOutData(lastKnownUserName: nil)))
} else if let authEvent = event as? AuthenticationEvent,
case .error(let authenticationError) = authEvent.eventType {
return .init(newState: .error(authenticationError))
} else {
return .from(oldState)
}
case .deletingUser(let signedInData, let deleteUserState):
if case let .userSignedOutAndDeleted(signedOutData) = event.isDeleteUserEvent {
return .init(newState: AuthenticationState.signedOut(signedOutData))
} else {
return resolveDeleteUser(
byApplying: event,
to: deleteUserState,
with: signedInData)
}
case .error:
if let authEvent = event as? AuthenticationEvent,
case .cancelSignIn = authEvent.eventType {
return .init(newState: .signedOut(SignedOutData()))
} else if let authZEvent = event.isAuthorizationEvent,
case .startFederationToIdentityPool = authZEvent {
return .init(newState: .federatingToIdentityPool)
} else if let authEvent = event as? AuthenticationEvent,
case .clearFederationToIdentityPool = authEvent.eventType {
return .init(
newState: .clearingFederation,
actions: [
ClearFederationToIdentityPool()
])
} else {
return .from(oldState)
}
}
}
private func resolveNotConfigured(
byApplying authEvent: AuthenticationEvent
) -> StateResolution<StateType> {
switch authEvent.eventType {
case .configure(let authConfig, let cognitoCredentials):
let action = ConfigureAuthentication(configuration: authConfig, storedCredentials: cognitoCredentials)
let resolution = StateResolution(
newState: AuthenticationState.configured,
actions: [action]
)
return resolution
default:
return .from(.notConfigured)
}
}
private func resolveConfigured(
byApplying authEvent: AuthenticationEvent
) -> StateResolution<StateType> {
switch authEvent.eventType {
case .initializedSignedIn(let signedInData):
return .from(.signedIn(signedInData))
case .initializedSignedOut(let signedOutData):
return .from(.signedOut(signedOutData))
case .initializedFederated:
return .from(.federatedToIdentityPool)
default:
return .from(.configured)
}
}
private func resolveSignedOut(
byApplying authEvent: AuthenticationEvent,
to currentSignedOutData: SignedOutData
) -> StateResolution<StateType> {
switch authEvent.eventType {
case .signInRequested(let signInData):
let action = InitializeSignInFlow(signInEventData: signInData)
return .init(newState: .signingIn(.notStarted), actions: [action])
case .signOutRequested(let eventData):
let action = InitiateGuestSignOut(signOutEventData: eventData)
let signOutState = SignOutState.notStarted
return .init(
newState: AuthenticationState.signingOut(signOutState),
actions: [action]
)
default:
return .from(.signedOut(currentSignedOutData))
}
}
private func resolveSignedIn(
byApplying authEvent: AuthenticationEvent,
to currentSignedInData: SignedInData
) -> StateResolution<StateType> {
switch authEvent.eventType {
case .signOutRequested(let signOutEventData):
let action = InitiateSignOut(signedInData: currentSignedInData,
signOutEventData: signOutEventData)
let signOutState = SignOutState.notStarted
let resolution = StateResolution(
newState: AuthenticationState.signingOut(signOutState),
actions: [action]
)
return resolution
default:
return .from(.signedIn(currentSignedInData))
}
}
private func resolveDeleteUser(
byApplying deleteUserEvent: StateMachineEvent,
to oldState: DeleteUserState,
with signedInData: SignedInData) -> StateResolution<StateType> {
let resolver = DeleteUserState.Resolver(signedInData: signedInData)
let resolution = resolver.resolve(oldState: oldState, byApplying: deleteUserEvent)
let newState = AuthenticationState.deletingUser(signedInData, resolution.newState)
return .init(newState: newState, actions: resolution.actions)
}
private func resolveSigningInState(oldState: AuthenticationState,
event: StateMachineEvent) -> StateResolution<StateType> {
if let authEvent = event as? AuthenticationEvent,
case .error(let error) = authEvent.eventType {
let action = CancelSignIn()
return .init(newState: .error(error), actions: [action])
}
/// Move to signedOut state if cancelSignIn
if let authEvent = event as? AuthenticationEvent,
case .cancelSignIn = authEvent.eventType {
let signedOutData = SignedOutData(lastKnownUserName: nil)
return .from(.signedOut(signedOutData))
}
guard case .signingIn(let signInState) = oldState else {
return .from(oldState)
}
// Move to signedIn state if signin flow completed
if let authEvent = event as? AuthenticationEvent,
case .signInCompleted(let signedInData) = authEvent.eventType {
return .init(newState: .signedIn(signedInData))
}
let resolution = SignInState.Resolver().resolve(oldState: signInState,
byApplying: event)
return .init(newState: .signingIn(resolution.newState), actions: resolution.actions)
}
private func resolveSigningOutState(
byApplying event: StateMachineEvent,
to signOutState: SignOutState
) -> StateResolution<StateType> {
if let authenEvent = event.isAuthenticationEvent,
case .cancelSignOut(let data) = authenEvent {
if let signedInData = data {
return .from(.signedIn(signedInData))
} else {
return .from(.signedOut(.init()))
}
}
let resolver = SignOutState.Resolver()
let resolution = resolver.resolve(oldState: signOutState, byApplying: event)
switch resolution.newState {
case .signedOut(let signedOutData):
let newState = AuthenticationState.signedOut(signedOutData)
return .init(newState: newState, actions: resolution.actions)
default:
let newState = AuthenticationState.signingOut(resolution.newState)
return .init(newState: newState, actions: resolution.actions)
}
}
}
}