Skip to content

Commit

Permalink
Merge pull request #374 from auth0/feature_central_error_dispatch
Browse files Browse the repository at this point in the history
Add centralized error processing to Dispatcher
  • Loading branch information
hzalaz authored Jan 17, 2017
2 parents e4d342b + cc20121 commit 8ab2b5e
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 7 deletions.
4 changes: 3 additions & 1 deletion Lock/Auth0OAuth2Interactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ struct Auth0OAuth2Interactor: OAuth2Authenticatable {
.start { result in
switch result {
case .success(let credentials):
self.dispatcher.dispatch(result: .auth(credentials))
callback(nil)
self.dispatcher.dispatch(result: .auth(credentials))
case .failure(WebAuthError.userCancelled):
callback(.cancelled)
self.dispatcher.dispatch(result: .error(WebAuthError.userCancelled))
case .failure:
callback(.couldNotAuthenticate)
self.dispatcher.dispatch(result: .error(OAuth2AuthenticatableError.couldNotAuthenticate))
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions Lock/ConnectionLoadingPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ class ConnectionLoadingPresenter: Presentable, Loggable {
let loader: RemoteConnectionLoader
let navigator: Navigable
let options: Options
let dispatcher: Dispatcher

init(loader: RemoteConnectionLoader, navigator: Navigable, options: Options) {
init(loader: RemoteConnectionLoader, navigator: Navigable, dispatcher: Dispatcher, options: Options) {
self.loader = loader
self.navigator = navigator
self.options = options
self.dispatcher = dispatcher
}

var view: View {
Expand All @@ -44,9 +46,12 @@ class ConnectionLoadingPresenter: Presentable, Loggable {
#endif
return Queue.main.async {
self.navigator.navigate(.unrecoverableError(error: error!))
self.dispatcher.dispatch(result: .error(error!))
}
}
guard let connections = connections, !connections.isEmpty else { return self.navigator.exit(withError: UnrecoverableError.clientWithNoConnections) }
guard let connections = connections, !connections.isEmpty else {
return self.navigator.exit(withError: UnrecoverableError.clientWithNoConnections)
}
Queue.main.async {
self.logger.debug("Loaded connections. Moving to root view")
self.navigator.reload(withConnections: connections)
Expand Down
14 changes: 14 additions & 0 deletions Lock/DatabaseInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,22 @@ struct DatabaseInteractor: DatabaseAuthenticatable, DatabaseUserCreator, Loggabl
}
case .failure(let cause as AuthenticationError) where cause.isPasswordNotStrongEnough:
callback(.passwordTooWeak, nil)
self.dispatcher.dispatch(result: .error(DatabaseUserCreatorError.passwordTooWeak))
case .failure(let cause as AuthenticationError) where cause.isPasswordAlreadyUsed:
callback(.passwordAlreadyUsed, nil)
self.dispatcher.dispatch(result: .error(DatabaseUserCreatorError.passwordAlreadyUsed))
case .failure(let cause as AuthenticationError) where cause.code == "invalid_password" && cause.value("name") == "PasswordDictionaryError":
callback(.passwordTooCommon, nil)
self.dispatcher.dispatch(result: .error(DatabaseUserCreatorError.passwordTooCommon))
case .failure(let cause as AuthenticationError) where cause.code == "invalid_password" && cause.value("name") == "PasswordNoUserInfoError":
callback(.passwordHasUserInfo, nil)
self.dispatcher.dispatch(result: .error(DatabaseUserCreatorError.passwordHasUserInfo))
case .failure(let cause as AuthenticationError) where cause.code == "invalid_password":
callback(.passwordInvalid, nil)
self.dispatcher.dispatch(result: .error(DatabaseUserCreatorError.passwordInvalid))
case .failure:
callback(.couldNotCreateUser, nil)
self.dispatcher.dispatch(result: .error(DatabaseUserCreatorError.couldNotCreateUser))
}
}
}
Expand Down Expand Up @@ -180,27 +186,35 @@ struct DatabaseInteractor: DatabaseAuthenticatable, DatabaseUserCreator, Loggabl
case .failure(let cause as AuthenticationError) where cause.isMultifactorRequired || cause.isMultifactorEnrollRequired:
self.logger.error("Multifactor is required for user <\(self.identifier)>")
callback(.multifactorRequired)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.multifactorRequired))
case .failure(let cause as AuthenticationError) where cause.isTooManyAttempts:
self.logger.error("Blocked user <\(self.identifier)> for too many login attempts")
callback(.tooManyAttempts)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.tooManyAttempts))
case .failure(let cause as AuthenticationError) where cause.isInvalidCredentials:
self.logger.error("Invalid credentials of user <\(self.identifier)>")
callback(.invalidEmailPassword)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.invalidEmailPassword))
case .failure(let cause as AuthenticationError) where cause.isMultifactorCodeInvalid:
self.logger.error("Multifactor code is invalid for user <\(self.identifier)>")
callback(.multifactorInvalid)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.multifactorInvalid))
case .failure(let cause as AuthenticationError) where cause.isRuleError && cause.description.lowercased() == "user is blocked":
self.logger.error("Blocked user <\(self.identifier)>")
callback(.userBlocked)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.userBlocked))
case .failure(let cause as AuthenticationError) where cause.code == "password_change_required":
self.logger.error("Change password required for user <\(self.identifier)>")
callback(.passwordChangeRequired)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.passwordChangeRequired))
case .failure(let cause as AuthenticationError) where cause.code == "password_leaked":
self.logger.error("The password of user <\(self.identifier)> was leaked")
callback(.passwordLeaked)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.passwordLeaked))
case .failure(let cause):
self.logger.error("Failed login of user <\(self.identifier)> with error \(cause)")
callback(.couldNotLogin)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.couldNotLogin))
case .success(let credentials):
self.logger.info("Authenticated user <\(self.identifier)>")
callback(nil)
Expand Down
5 changes: 4 additions & 1 deletion Lock/DatabasePasswordInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ struct DatabasePasswordInteractor: PasswordRecoverable {
self.authentication
.resetPassword(email: email, connection: connection)
.start {
guard case .success = $0 else { return callback(.emailNotSent) }
guard case .success = $0 else {
callback(.emailNotSent)
return self.dispatcher.dispatch(result: .error(PasswordRecoverableError.emailNotSent))
}
self.dispatcher.dispatch(result: .forgotPassword(email))
callback(nil)
}
Expand Down
8 changes: 8 additions & 0 deletions Lock/EnterpriseActiveAuthInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,27 +127,35 @@ struct EnterpriseActiveAuthInteractor: DatabaseAuthenticatable, Loggable {
case .failure(let cause as AuthenticationError) where cause.isMultifactorRequired || cause.isMultifactorEnrollRequired:
self.logger.error("Multifactor is required for user <\(self.identifier)>")
callback(.multifactorRequired)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.multifactorRequired))
case .failure(let cause as AuthenticationError) where cause.isTooManyAttempts:
self.logger.error("Blocked user <\(self.identifier)> for too many login attempts")
callback(.tooManyAttempts)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.tooManyAttempts))
case .failure(let cause as AuthenticationError) where cause.isInvalidCredentials:
self.logger.error("Invalid credentials of user <\(self.identifier)>")
callback(.invalidEmailPassword)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.invalidEmailPassword))
case .failure(let cause as AuthenticationError) where cause.isMultifactorCodeInvalid:
self.logger.error("Multifactor code is invalid for user <\(self.identifier)>")
callback(.multifactorInvalid)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.multifactorInvalid))
case .failure(let cause as AuthenticationError) where cause.isRuleError && cause.description.lowercased() == "user is blocked":
self.logger.error("Blocked user <\(self.identifier)>")
callback(.userBlocked)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.userBlocked))
case .failure(let cause as AuthenticationError) where cause.code == "password_change_required":
self.logger.error("Change password required for user <\(self.identifier)>")
callback(.passwordChangeRequired)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.passwordChangeRequired))
case .failure(let cause as AuthenticationError) where cause.code == "password_leaked":
self.logger.error("The password of user <\(self.identifier)> was leaked")
callback(.passwordLeaked)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.passwordLeaked))
case .failure(let cause):
self.logger.error("Failed login of user <\(self.identifier)> with error \(cause)")
callback(.couldNotLogin)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.couldNotLogin))
case .success(let credentials):
self.logger.info("Authenticated user <\(self.identifier)>")
callback(nil)
Expand Down
2 changes: 2 additions & 0 deletions Lock/MultifactorInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ struct MultifactorInteractor: MultifactorAuthenticatable {
switch result {
case .failure(let cause as AuthenticationError) where cause.isMultifactorCodeInvalid:
callback(.multifactorInvalid)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.multifactorInvalid))
case .failure:
callback(.couldNotLogin)
self.dispatcher.dispatch(result: .error(DatabaseAuthenticatableError.couldNotLogin))
case .success(let credentials):
callback(nil)
self.dispatcher.dispatch(result: .auth(credentials))
Expand Down
2 changes: 1 addition & 1 deletion Lock/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ struct Router: Navigable {
guard !connections.isEmpty else {
self.lock.logger.debug("No connections configured. Loading client info from Auth0...")
let interactor = CDNLoaderInteractor(baseURL: self.lock.authentication.url, clientId: self.lock.authentication.clientId)
return ConnectionLoadingPresenter(loader: interactor, navigator: self, options: self.lock.options)
return ConnectionLoadingPresenter(loader: interactor, navigator: self, dispatcher: lock.observerStore, options: self.lock.options)
}
if let database = connections.database {
guard self.lock.options.allow != [.ResetPassword] && self.lock.options.initialScreen != .resetPassword else { return forgotPassword }
Expand Down
2 changes: 1 addition & 1 deletion LockTests/Interactors/CDNLoaderInteractorSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class CDNLoaderInteractorSpec: QuickSpec {
}
}

context("remote connection errors") {
context("remote connectin errors") {

it("should return invalid client error") {
stub(condition: isCDN(forClientId: clientId)) { _ in OHHTTPStubsResponse(data: Data(), statusCode: 403, headers: [:]) }
Expand Down
4 changes: 3 additions & 1 deletion LockTests/Presenters/ConnectionLoadingPresenterSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ class ConnectionLoadingPresenterSpec: QuickSpec {
var messagePresenter: MockMessagePresenter!
var navigator: MockNavigator!
var options: OptionBuildable!
var dispatcher: Dispatcher!

beforeEach {
options = LockOptions()
messagePresenter = MockMessagePresenter()
interactor = MockConnectionsLoader()
navigator = MockNavigator()
presenter = ConnectionLoadingPresenter(loader: interactor, navigator: navigator, options: options)
dispatcher = ObserverStore()
presenter = ConnectionLoadingPresenter(loader: interactor, navigator: navigator, dispatcher: dispatcher, options: options)
presenter.messagePresenter = messagePresenter
}

Expand Down

0 comments on commit 8ab2b5e

Please sign in to comment.