From e7f4173fafe2f716e1ba39432022e6a095138c46 Mon Sep 17 00:00:00 2001 From: Martin Walsh Date: Wed, 9 Nov 2016 19:12:05 -0300 Subject: [PATCH 1/5] Enterprise routing for db + enterprise Added SSO Header --- Lock/DatabasePresenter.swift | 16 ++++++++++++++-- Lock/EnterpriseDomainPresenter.swift | 8 ++++++++ Lock/EnterpriseDomainView.swift | 19 ++++++++++++++++++- Lock/Router.swift | 23 +++++++++++++++++++++++ Lock/Routes.swift | 3 ++- 5 files changed, 65 insertions(+), 4 deletions(-) diff --git a/Lock/DatabasePresenter.swift b/Lock/DatabasePresenter.swift index 883e0c94b..0fbdf5308 100644 --- a/Lock/DatabasePresenter.swift +++ b/Lock/DatabasePresenter.swift @@ -34,10 +34,13 @@ class DatabasePresenter: Presentable, Loggable { var messagePresenter: MessagePresenter? var authPresenter: AuthPresenter? + var enterprisePresenter: EnterpriseDomainPresenter? var initialEmail: String? { return self.authenticator.validEmail ? self.authenticator.email : nil } var initialUsername: String? { return self.authenticator.validUsername ? self.authenticator.username : nil } - + + var mode:DatabaseModeSwitcher? + convenience init(interactor: DatabaseInteractor, connection: DatabaseConnection, navigator: Navigable, options: Options) { self.init(authenticator: interactor, creator: interactor, connection: connection, navigator: navigator, options: options) } @@ -74,6 +77,7 @@ class DatabasePresenter: Presentable, Loggable { database.switcher?.selected = .Signup showSignup(inView: database, username: initialUsername, email: initialEmail) } + self.mode = database.switcher return database } @@ -191,7 +195,15 @@ class DatabasePresenter: Presentable, Loggable { guard let attr = attribute else { return } do { try self.authenticator.update(attr, value: input.text) - input.showValid() + // Check Enterprise connection + if let mode = self.mode, enterprise = self.enterprisePresenter { + if mode.selected == DatabaseModeSwitcher.Mode.Login { + try enterprise.interactor.updateEmail(input.text) + if enterprise.interactor.connection != nil { + self.navigator.navigate(.Enterprise) + } + } + } } catch let error as InputValidationError { input.showError(error.localizedMessage(withConnection: self.database)) } catch { diff --git a/Lock/EnterpriseDomainPresenter.swift b/Lock/EnterpriseDomainPresenter.swift index 11b8a506b..10eae26da 100644 --- a/Lock/EnterpriseDomainPresenter.swift +++ b/Lock/EnterpriseDomainPresenter.swift @@ -41,8 +41,14 @@ class EnterpriseDomainPresenter: Presentable, Loggable { let authCollectionView = self.authPresenter?.newViewToEmbed(withInsets: UIEdgeInsetsMake(0, 0, 0, 0), isLogin: true) let view = EnterpriseDomainView(email: email, authCollectionView: authCollectionView) let form = view.form + + if self.interactor.connection != nil { + view.header.hidden = false + } + view.form?.onValueChange = { input in self.messagePresenter?.hideCurrent() + view.header.hidden = true guard case .Email = input.type else { return } do { @@ -50,6 +56,7 @@ class EnterpriseDomainPresenter: Presentable, Loggable { input.showValid() if let connection = self.interactor.connection { self.logger.debug("Enterprise connection match: \(connection)") + view.header.hidden = false } } catch { input.showError() @@ -75,6 +82,7 @@ class EnterpriseDomainPresenter: Presentable, Loggable { } } + view.primaryButton?.onPress = action view.form?.onReturn = {_ in guard let button = view.primaryButton else { return } diff --git a/Lock/EnterpriseDomainView.swift b/Lock/EnterpriseDomainView.swift index 5969544ef..2ff71c615 100644 --- a/Lock/EnterpriseDomainView.swift +++ b/Lock/EnterpriseDomainView.swift @@ -25,6 +25,7 @@ import UIKit class EnterpriseDomainView: UIView, View { weak var form: Form? + weak var header: UILabel! weak var primaryButton: PrimaryButton? weak var authCollectionView: AuthCollectionView? @@ -34,6 +35,7 @@ class EnterpriseDomainView: UIView, View { let primaryButton = PrimaryButton() let domainView = EnterpriseSingleInputView() let container = UIStackView() + let header = UILabel() self.primaryButton = primaryButton self.form = domainView @@ -41,9 +43,17 @@ class EnterpriseDomainView: UIView, View { super.init(frame: CGRectZero) + self.addSubview(header) self.addSubview(container) self.addSubview(primaryButton) + header.text = "SINGLE SIGN-ON ENABLED".i18n(key: "com.auth0.lock.enterprise.sso", comment: "SSO Header") + header.textAlignment = .Center + header.textColor = UIColor ( red: 0.5725, green: 0.5804, blue: 0.5843, alpha: 1.0 ) + header.backgroundColor = UIColor ( red: 0.9333, green: 0.9333, blue: 0.9333, alpha: 1.0 ) + header.hidden = true + self.header = header + container.alignment = .Fill container.axis = .Vertical container.distribution = .EqualSpacing @@ -63,7 +73,14 @@ class EnterpriseDomainView: UIView, View { container.addArrangedSubview(domainView) container.addArrangedSubview(strutView()) - constraintEqual(anchor: container.topAnchor, toAnchor: self.topAnchor) + constraintEqual(anchor: header.topAnchor, toAnchor: self.topAnchor) + constraintEqual(anchor: header.leftAnchor, toAnchor: self.leftAnchor, constant: 0) + constraintEqual(anchor: header.rightAnchor, toAnchor: self.rightAnchor, constant: 0) + constraintEqual(anchor: header.bottomAnchor, toAnchor: container.topAnchor) + dimension(header.heightAnchor, greaterThanOrEqual: 30) + header.translatesAutoresizingMaskIntoConstraints = false + + constraintEqual(anchor: container.topAnchor, toAnchor: header.bottomAnchor) constraintEqual(anchor: container.leftAnchor, toAnchor: self.leftAnchor, constant: 20) constraintEqual(anchor: container.rightAnchor, toAnchor: self.rightAnchor, constant: -20) constraintEqual(anchor: container.bottomAnchor, toAnchor: primaryButton.topAnchor) diff --git a/Lock/Router.swift b/Lock/Router.swift index 2b8914b73..c267e1dc9 100644 --- a/Lock/Router.swift +++ b/Lock/Router.swift @@ -86,10 +86,17 @@ struct Router: Navigable { let authentication = self.lock.authentication let interactor = DatabaseInteractor(connection: database, authentication: authentication, user: self.user, options: self.lock.options, callback: self.onAuthentication) let presenter = DatabasePresenter(interactor: interactor, connection: database, navigator: self, options: self.lock.options) + // Add Social if !connections.oauth2.isEmpty { let interactor = Auth0OAuth2Interactor(webAuth: self.lock.webAuth, onCredentials: self.onAuthentication, options: self.lock.options) presenter.authPresenter = AuthPresenter(connections: connections, interactor: interactor, customStyle: self.lock.style.oauth2) } + // Add Enterprise + if !connections.enterprise.isEmpty { + let authInteractor = Auth0OAuth2Interactor(webAuth: self.lock.webAuth, onCredentials: self.onAuthentication, options: self.lock.options) + let interactor = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authInteractor) + presenter.enterprisePresenter = EnterpriseDomainPresenter(interactor: interactor) + } return presenter } if !connections.enterprise.isEmpty { @@ -134,6 +141,20 @@ struct Router: Navigable { presenter.customLogger = self.lock.logger return presenter } + + var enterprise: Presentable? { + let connections = self.lock.connections + guard !connections.isEmpty else { + exit(withError: UnrecoverableError.ClientWithNoConnections) + return nil + } + + let authInteractor = Auth0OAuth2Interactor(webAuth: self.lock.webAuth, onCredentials: self.onAuthentication, options: self.lock.options) + var interactor = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authInteractor) + _ = try? interactor.updateEmail(self.user.email) + let presenter = EnterpriseDomainPresenter(interactor: interactor) + return presenter + } var showBack: Bool { guard let routes = self.controller?.routes else { return false } @@ -160,6 +181,8 @@ struct Router: Navigable { presentable = self.forgotPassword case .Multifactor: presentable = self.multifactor + case .Enterprise: + presentable = self.enterprise default: self.lock.logger.warn("Ignoring navigation \(route)") return diff --git a/Lock/Routes.swift b/Lock/Routes.swift index f96fba4ff..a2baa15cd 100644 --- a/Lock/Routes.swift +++ b/Lock/Routes.swift @@ -48,4 +48,5 @@ enum Route { case Root case ForgotPassword case Multifactor -} \ No newline at end of file + case Enterprise +} From 675dfcf19e176aab4b2dc657c20fe74877a1c646 Mon Sep 17 00:00:00 2001 From: Martin Walsh Date: Thu, 10 Nov 2016 18:17:01 -0300 Subject: [PATCH 2/5] Enterprise support added to DB EnterpriseInteractor embedded in the now multi role Database Presenter Created InfoBar Component for SSO Message Retired Enterprise Route, now handled in DatabaseView Seamless keyboard behaviour between enterprise mode switch --- Lock.xcodeproj/project.pbxproj | 4 + Lock/DatabaseOnlyView.swift | 90 ++++++++++++++++------ Lock/DatabasePresenter.swift | 104 ++++++++++++++++---------- Lock/EnterpriseDomainInteractor.swift | 10 +++ Lock/EnterpriseDomainPresenter.swift | 6 +- Lock/EnterpriseDomainView.swift | 32 ++++---- Lock/InfoBarView.swift | 99 ++++++++++++++++++++++++ Lock/InputField.swift | 1 + Lock/Router.swift | 18 +---- Lock/Routes.swift | 1 - 10 files changed, 262 insertions(+), 103 deletions(-) create mode 100644 Lock/InfoBarView.swift diff --git a/Lock.xcodeproj/project.pbxproj b/Lock.xcodeproj/project.pbxproj index d13b3bbb6..b55965b23 100644 --- a/Lock.xcodeproj/project.pbxproj +++ b/Lock.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 5B09717E1DC8F292003AA88F /* EnterpriseDomainInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B09717D1DC8F292003AA88F /* EnterpriseDomainInteractor.swift */; }; 5B0971801DC8F5C4003AA88F /* EnterpriseDomainPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B09717F1DC8F5C4003AA88F /* EnterpriseDomainPresenter.swift */; }; 5B0971821DC8FAC5003AA88F /* EnterpriseDomainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0971811DC8FAC5003AA88F /* EnterpriseDomainView.swift */; }; + 5B2981781DD51A460062535C /* InfoBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2981771DD51A460062535C /* InfoBarView.swift */; }; 5BA563F11DD117550002D3AB /* EnterpriseDomainInteractorSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA563EF1DD1171F0002D3AB /* EnterpriseDomainInteractorSpec.swift */; }; 5BCED4C71DD1FEAA00E2CE8A /* EnterpriseDomainPresenterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCED4C51DD1FCF200E2CE8A /* EnterpriseDomainPresenterSpec.swift */; }; 5F14565A1D5130E80085DF9C /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1456591D5130E80085DF9C /* Colors.swift */; }; @@ -201,6 +202,7 @@ 5B09717D1DC8F292003AA88F /* EnterpriseDomainInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EnterpriseDomainInteractor.swift; path = Lock/EnterpriseDomainInteractor.swift; sourceTree = SOURCE_ROOT; }; 5B09717F1DC8F5C4003AA88F /* EnterpriseDomainPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EnterpriseDomainPresenter.swift; path = Lock/EnterpriseDomainPresenter.swift; sourceTree = SOURCE_ROOT; }; 5B0971811DC8FAC5003AA88F /* EnterpriseDomainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EnterpriseDomainView.swift; path = Lock/EnterpriseDomainView.swift; sourceTree = SOURCE_ROOT; }; + 5B2981771DD51A460062535C /* InfoBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InfoBarView.swift; path = Lock/InfoBarView.swift; sourceTree = SOURCE_ROOT; }; 5BA563EF1DD1171F0002D3AB /* EnterpriseDomainInteractorSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterpriseDomainInteractorSpec.swift; sourceTree = ""; }; 5BCED4C51DD1FCF200E2CE8A /* EnterpriseDomainPresenterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterpriseDomainPresenterSpec.swift; sourceTree = ""; }; 5F1456591D5130E80085DF9C /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Colors.swift; path = Lock/Colors.swift; sourceTree = SOURCE_ROOT; }; @@ -621,6 +623,7 @@ 5FEAE20E1D1A5682005C0028 /* Components */ = { isa = PBXGroup; children = ( + 5B2981771DD51A460062535C /* InfoBarView.swift */, 5F99AA951D1C4AF400D27842 /* CredentialView.swift */, 5F99AA8F1D1B9E7100D27842 /* DatabaseModeSwitcher.swift */, 5FC4348B1D1DFC5A005188BC /* Form.swift */, @@ -956,6 +959,7 @@ 5F73CDD81D3093BF00D8D8D1 /* PasswordRecoverable.swift in Sources */, 5F99AA841D1B0BCE00D27842 /* Lock.swift in Sources */, 5FBE5CBE1D3E5C7B0038536D /* MultifactorAuthenticatable.swift in Sources */, + 5B2981781DD51A460062535C /* InfoBarView.swift in Sources */, 5FBE5CC01D3E5E0A0038536D /* MultifactorInteractor.swift in Sources */, 5F50900E1D1DF40400EAA650 /* DatabaseOnlyView.swift in Sources */, 5F70F1E91D7907D5004698DA /* LockOptions.swift in Sources */, diff --git a/Lock/DatabaseOnlyView.swift b/Lock/DatabaseOnlyView.swift index 2d012453e..54498d541 100644 --- a/Lock/DatabaseOnlyView.swift +++ b/Lock/DatabaseOnlyView.swift @@ -23,7 +23,7 @@ import UIKit class DatabaseOnlyView: UIView, DatabaseView { - + weak var form: Form? weak var secondaryButton: SecondaryButton? weak var primaryButton: PrimaryButton? @@ -31,58 +31,60 @@ class DatabaseOnlyView: UIView, DatabaseView { weak var authCollectionView: AuthCollectionView? weak var separator: UILabel? weak var secondaryStrut: UIView? - + weak var infoBar: InfoBarView? + weak var spacer: UIView? + private weak var container: UIStackView? - + let allowedModes: DatabaseMode - + init(allowedModes: DatabaseMode = [.Login, .Signup, .ResetPassword]) { let primaryButton = PrimaryButton() let container = UIStackView() - + self.allowedModes = allowedModes self.primaryButton = primaryButton self.container = container - + super.init(frame: CGRectZero) - + self.addSubview(container) self.addSubview(primaryButton) - + container.alignment = .Fill container.axis = .Vertical container.distribution = .EqualSpacing container.spacing = 10 - + constraintEqual(anchor: container.leftAnchor, toAnchor: self.leftAnchor) constraintEqual(anchor: container.topAnchor, toAnchor: self.topAnchor) constraintEqual(anchor: container.rightAnchor, toAnchor: self.rightAnchor) constraintEqual(anchor: container.bottomAnchor, toAnchor: primaryButton.topAnchor) container.translatesAutoresizingMaskIntoConstraints = false - + self.layoutSwitcher(allowedModes.contains(.Login) && allowedModes.contains(.Signup)) - + constraintEqual(anchor: primaryButton.leftAnchor, toAnchor: self.leftAnchor) constraintEqual(anchor: primaryButton.rightAnchor, toAnchor: self.rightAnchor) constraintEqual(anchor: primaryButton.bottomAnchor, toAnchor: self.bottomAnchor) primaryButton.translatesAutoresizingMaskIntoConstraints = false } - + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + // MARK:- Layout - + private let switcherIndex = 0 private let formOnlyIndex = 1 private let formBelowSocialIndex = 3 private let separatorIndex = 2 private let socialIndex = 1 - + func showLogin(withIdentifierStyle style: DatabaseIdentifierStyle, identifier: String? = nil, authCollectionView: AuthCollectionView? = nil) { let form = CredentialView() - + let type: InputField.InputType switch style { case [.Email, .Username]: @@ -92,7 +94,7 @@ class DatabaseOnlyView: UIView, DatabaseView { default: type = .Email } - + form.identityField.text = identifier form.identityField.type = type form.identityField.returnKey = .Next @@ -102,7 +104,7 @@ class DatabaseOnlyView: UIView, DatabaseView { self.layoutSecondaryButton(self.allowedModes.contains(.ResetPassword)) self.form = form } - + func showSignUp(withUsername showUsername: Bool, username: String?, email: String?, authCollectionView: AuthCollectionView? = nil, additionalFields: [CustomTextField]) { let form = SignUpView(additionalFields: additionalFields) form.showUsername = showUsername @@ -117,7 +119,47 @@ class DatabaseOnlyView: UIView, DatabaseView { self.layoutSecondaryButton(true) self.form = form } - + + func presentEnterprise() { + if self.infoBar != nil { return } + + let form = self.form as! CredentialView + let infoBar = InfoBarView() + let spacer = strutView(withHeight: 40) + + infoBar.title = "SINGLE SIGN-ON ENABLED".i18n(key: "com.auth0.lock.enterprise.sso", comment: "SSO Header") + infoBar.setIcon("ic_lock") + infoBar.hidden = false + + self.container?.insertArrangedSubview(infoBar, atIndex: 0) + //self.container?.insertArrangedSubview(spacer, atIndex: 5) + self.infoBar = infoBar + self.spacer = spacer + + form.passwordField.hidden = true + form.identityField.nextField = nil + form.identityField.returnKey = .Done + form.identityField.onReturn = form.passwordField.onReturn + + self.switcher?.hidden = true + self.secondaryButton?.hidden = true + } + + func removeEnterprise() { + if self.infoBar == nil { return } + + let form = self.form as! CredentialView + self.infoBar?.removeFromSuperview() + self.spacer?.removeFromSuperview() + + form.passwordField.hidden = false + form.identityField.nextField = form.passwordField + form.identityField.returnKey = .Next + + self.switcher?.hidden = false + self.secondaryButton?.hidden = false + } + private func layoutSecondaryButton(enabled: Bool) { self.secondaryStrut?.removeFromSuperview() self.secondaryButton?.removeFromSuperview() @@ -131,7 +173,7 @@ class DatabaseOnlyView: UIView, DatabaseView { self.container?.addArrangedSubview(view) } } - + private func layoutSwitcher(enabled: Bool) { self.container?.arrangedSubviews.first?.removeFromSuperview() if enabled { @@ -143,14 +185,14 @@ class DatabaseOnlyView: UIView, DatabaseView { self.container?.insertArrangedSubview(view, atIndex: switcherIndex) } } - + private func layoutInStack(view: UIView, authCollectionView: AuthCollectionView?) { if let current = self.form as? UIView { current.removeFromSuperview() } self.authCollectionView?.removeFromSuperview() self.separator?.removeFromSuperview() - + if let social = authCollectionView { let label = UILabel() label.text = "or".i18n(key: "com.auth0.lock.database.separator", comment: "Social separator") @@ -166,9 +208,9 @@ class DatabaseOnlyView: UIView, DatabaseView { self.container?.insertArrangedSubview(view, atIndex: formOnlyIndex) } } - + // MARK:- Styling - + func apply(style style: Style) { primaryButton?.apply(style: style) } diff --git a/Lock/DatabasePresenter.swift b/Lock/DatabasePresenter.swift index 0fbdf5308..f13c5ac8a 100644 --- a/Lock/DatabasePresenter.swift +++ b/Lock/DatabasePresenter.swift @@ -24,27 +24,27 @@ import Foundation import SafariServices class DatabasePresenter: Presentable, Loggable { - + let database: DatabaseConnection let options: Options - + var authenticator: DatabaseAuthenticatable var creator: DatabaseUserCreator var navigator: Navigable - + var messagePresenter: MessagePresenter? var authPresenter: AuthPresenter? - var enterprisePresenter: EnterpriseDomainPresenter? - + var enterpriseInteractor: EnterpriseDomainInteractor? + var initialEmail: String? { return self.authenticator.validEmail ? self.authenticator.email : nil } var initialUsername: String? { return self.authenticator.validUsername ? self.authenticator.username : nil } - var mode:DatabaseModeSwitcher? + weak var databaseView:DatabaseOnlyView? convenience init(interactor: DatabaseInteractor, connection: DatabaseConnection, navigator: Navigable, options: Options) { self.init(authenticator: interactor, creator: interactor, connection: connection, navigator: navigator, options: options) } - + init(authenticator: DatabaseAuthenticatable, creator: DatabaseUserCreator, connection: DatabaseConnection, navigator: Navigable, options: Options) { self.authenticator = authenticator self.creator = creator @@ -52,7 +52,7 @@ class DatabasePresenter: Presentable, Loggable { self.navigator = navigator self.options = options } - + var view: View { let initialScreen = self.options.initialScreen let allow = self.options.allow @@ -69,7 +69,7 @@ class DatabasePresenter: Presentable, Loggable { self.showLogin(inView: view, identifier: self.authenticator.identifier) } } - + if allow.contains(.Login) && initialScreen == .Login { database.switcher?.selected = .Login showLogin(inView: database, identifier: authenticator.identifier) @@ -77,10 +77,10 @@ class DatabasePresenter: Presentable, Loggable { database.switcher?.selected = .Signup showSignup(inView: database, username: initialUsername, email: initialEmail) } - self.mode = database.switcher + self.databaseView = database return database } - + private func showLogin(inView view: DatabaseView, identifier: String?) { self.messagePresenter?.hideCurrent() let authCollectionView = self.authPresenter?.newViewToEmbed(withInsets: UIEdgeInsetsMake(0, 18, 0, 18), isLogin: true) @@ -88,29 +88,51 @@ class DatabasePresenter: Presentable, Loggable { view.showLogin(withIdentifierStyle: style, identifier: identifier, authCollectionView: authCollectionView) let form = view.form form?.onValueChange = self.handleInput - + let action = { [weak form] (button: PrimaryButton) in self.messagePresenter?.hideCurrent() - self.logger.info("perform login for email \(self.authenticator.email)") - let interactor = self.authenticator + self.logger.info("Perform login for email: \(self.authenticator.email)") button.inProgress = true - interactor.login { error in - Queue.main.async { - button.inProgress = false - guard let error = error else { - self.logger.debug("Logged in!") - return - } - if case .MultifactorRequired = error { - self.navigator.navigate(.Multifactor) - } else { + + // Enterprise Authentication + if self.enterpriseInteractor?.isEnterprise(self.authenticator.email) == true { + self.enterpriseInteractor?.login { error in + Queue.main.async { + button.inProgress = false form?.needsToUpdateState() - self.messagePresenter?.showError(error) - self.logger.error("Failed with error \(error)") + if let error = error { + self.messagePresenter?.showError(error) + self.logger.error("Enterprise connection failed: \(error)") + self.messagePresenter?.showError(error) + } else { + self.logger.debug("Enterprise authenticator launched") + } + } + + } + + } else { + // Database Authentication + let interactor = self.authenticator + interactor.login { error in + Queue.main.async { + button.inProgress = false + guard let error = error else { + self.logger.debug("Logged in!") + return + } + if case .MultifactorRequired = error { + self.navigator.navigate(.Multifactor) + } else { + form?.needsToUpdateState() + self.messagePresenter?.showError(error) + self.logger.error("Failed with error \(error)") + } } } } } + view.form?.onReturn = { field in guard let button = view.primaryButton where field.returnKey == .Done else { return } // FIXME: Log warn action(button) @@ -122,7 +144,7 @@ class DatabasePresenter: Presentable, Loggable { self.navigator.navigate(.ForgotPassword) } } - + private func showSignup(inView view: DatabaseView, username: String?, email: String?) { self.messagePresenter?.hideCurrent() let authCollectionView = self.authPresenter?.newViewToEmbed(withInsets: UIEdgeInsetsMake(0, 18, 0, 18), isLogin: false) @@ -137,7 +159,7 @@ class DatabasePresenter: Presentable, Loggable { interactor.create { createError, loginError in Queue.main.async { button.inProgress = false - + guard createError != nil || loginError != nil else { self.logger.debug("Logged in!") return @@ -146,16 +168,16 @@ class DatabasePresenter: Presentable, Loggable { self.navigator.navigate(.Multifactor) return } - + let error: LocalizableError = createError ?? loginError! form?.needsToUpdateState() self.messagePresenter?.showError(error) self.logger.error("Failed with error \(error)") - + } } } - + view.form?.onReturn = { field in guard let button = view.primaryButton where field.returnKey == .Done else { return } // FIXME: Log warn action(button) @@ -172,9 +194,11 @@ class DatabasePresenter: Presentable, Loggable { self.navigator.present(alert) } } - + private func handleInput(input: InputField) { self.messagePresenter?.hideCurrent() + self.databaseView?.removeEnterprise() + self.logger.verbose("new value: \(input.text) for type: \(input.type)") let attribute: UserAttribute? switch input.type { @@ -191,25 +215,25 @@ class DatabasePresenter: Presentable, Loggable { default: attribute = nil } - + guard let attr = attribute else { return } do { try self.authenticator.update(attr, value: input.text) - // Check Enterprise connection - if let mode = self.mode, enterprise = self.enterprisePresenter { - if mode.selected == DatabaseModeSwitcher.Mode.Login { - try enterprise.interactor.updateEmail(input.text) - if enterprise.interactor.connection != nil { - self.navigator.navigate(.Enterprise) - } + // Check for Entperise domain match in login view + if var enterpriseInteractor = self.enterpriseInteractor, let mode = self.databaseView?.switcher?.selected { + if enterpriseInteractor.isEnterprise(input.text) && mode == .Login { + logger.debug("Enterprise connection detected: \(enterpriseInteractor.connection)") + self.databaseView?.presentEnterprise() } } + input.showValid() } catch let error as InputValidationError { input.showError(error.localizedMessage(withConnection: self.database)) } catch { input.showError() } } + } private func safariBuilder(forURL url: NSURL, navigator: Navigable) -> (UIAlertAction) -> () { diff --git a/Lock/EnterpriseDomainInteractor.swift b/Lock/EnterpriseDomainInteractor.swift index 10223eada..96141fb45 100644 --- a/Lock/EnterpriseDomainInteractor.swift +++ b/Lock/EnterpriseDomainInteractor.swift @@ -64,4 +64,14 @@ struct EnterpriseDomainInteractor: HRDAuthenticatable { authenticator.login(connection.name, callback: callback) } + + mutating func isEnterprise(value: String?) -> Bool { + do { + try updateEmail(value) + if self.connection != nil { return true } + } catch { + return false + } + return false + } } diff --git a/Lock/EnterpriseDomainPresenter.swift b/Lock/EnterpriseDomainPresenter.swift index 10eae26da..903ccd50b 100644 --- a/Lock/EnterpriseDomainPresenter.swift +++ b/Lock/EnterpriseDomainPresenter.swift @@ -43,12 +43,12 @@ class EnterpriseDomainPresenter: Presentable, Loggable { let form = view.form if self.interactor.connection != nil { - view.header.hidden = false + view.infoBar?.hidden = false } view.form?.onValueChange = { input in self.messagePresenter?.hideCurrent() - view.header.hidden = true + view.infoBar?.hidden = true guard case .Email = input.type else { return } do { @@ -56,7 +56,7 @@ class EnterpriseDomainPresenter: Presentable, Loggable { input.showValid() if let connection = self.interactor.connection { self.logger.debug("Enterprise connection match: \(connection)") - view.header.hidden = false + view.infoBar?.hidden = false } } catch { input.showError() diff --git a/Lock/EnterpriseDomainView.swift b/Lock/EnterpriseDomainView.swift index 2ff71c615..29a568819 100644 --- a/Lock/EnterpriseDomainView.swift +++ b/Lock/EnterpriseDomainView.swift @@ -25,7 +25,7 @@ import UIKit class EnterpriseDomainView: UIView, View { weak var form: Form? - weak var header: UILabel! + weak var infoBar: InfoBarView? weak var primaryButton: PrimaryButton? weak var authCollectionView: AuthCollectionView? @@ -35,7 +35,7 @@ class EnterpriseDomainView: UIView, View { let primaryButton = PrimaryButton() let domainView = EnterpriseSingleInputView() let container = UIStackView() - let header = UILabel() + let infoBar = InfoBarView() self.primaryButton = primaryButton self.form = domainView @@ -43,23 +43,21 @@ class EnterpriseDomainView: UIView, View { super.init(frame: CGRectZero) - self.addSubview(header) + self.addSubview(infoBar) self.addSubview(container) self.addSubview(primaryButton) - header.text = "SINGLE SIGN-ON ENABLED".i18n(key: "com.auth0.lock.enterprise.sso", comment: "SSO Header") - header.textAlignment = .Center - header.textColor = UIColor ( red: 0.5725, green: 0.5804, blue: 0.5843, alpha: 1.0 ) - header.backgroundColor = UIColor ( red: 0.9333, green: 0.9333, blue: 0.9333, alpha: 1.0 ) - header.hidden = true - self.header = header + infoBar.title = "SINGLE SIGN-ON ENABLED".i18n(key: "com.auth0.lock.enterprise.sso", comment: "SSO Header") + infoBar.setIcon("ic_lock") + infoBar.hidden = true + self.infoBar = infoBar container.alignment = .Fill container.axis = .Vertical container.distribution = .EqualSpacing container.spacing = 5 - container.addArrangedSubview(strutView()) + container.addArrangedSubview(strutView(withHeight: 25)) if let authCollectionView = authCollectionView { self.authCollectionView = authCollectionView container.addArrangedSubview(authCollectionView) @@ -73,14 +71,13 @@ class EnterpriseDomainView: UIView, View { container.addArrangedSubview(domainView) container.addArrangedSubview(strutView()) - constraintEqual(anchor: header.topAnchor, toAnchor: self.topAnchor) - constraintEqual(anchor: header.leftAnchor, toAnchor: self.leftAnchor, constant: 0) - constraintEqual(anchor: header.rightAnchor, toAnchor: self.rightAnchor, constant: 0) - constraintEqual(anchor: header.bottomAnchor, toAnchor: container.topAnchor) - dimension(header.heightAnchor, greaterThanOrEqual: 30) - header.translatesAutoresizingMaskIntoConstraints = false + constraintEqual(anchor: infoBar.topAnchor, toAnchor: self.topAnchor) + constraintEqual(anchor: infoBar.leftAnchor, toAnchor: self.leftAnchor, constant: 0) + constraintEqual(anchor: infoBar.rightAnchor, toAnchor: self.rightAnchor, constant: 0) + constraintEqual(anchor: infoBar.bottomAnchor, toAnchor: container.topAnchor) + infoBar.translatesAutoresizingMaskIntoConstraints = false - constraintEqual(anchor: container.topAnchor, toAnchor: header.bottomAnchor) + constraintEqual(anchor: container.topAnchor, toAnchor: infoBar.bottomAnchor) constraintEqual(anchor: container.leftAnchor, toAnchor: self.leftAnchor, constant: 20) constraintEqual(anchor: container.rightAnchor, toAnchor: self.rightAnchor, constant: -20) constraintEqual(anchor: container.bottomAnchor, toAnchor: primaryButton.topAnchor) @@ -91,7 +88,6 @@ class EnterpriseDomainView: UIView, View { constraintEqual(anchor: primaryButton.bottomAnchor, toAnchor: self.bottomAnchor) primaryButton.translatesAutoresizingMaskIntoConstraints = false - domainView.type = .Email domainView.returnKey = .Done domainView.value = email diff --git a/Lock/InfoBarView.swift b/Lock/InfoBarView.swift new file mode 100644 index 000000000..986c3f18c --- /dev/null +++ b/Lock/InfoBarView.swift @@ -0,0 +1,99 @@ +// InfoBarView.swift +// +// Copyright (c) 2016 Auth0 (http://auth0.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +public class InfoBarView: UIView { + + weak var container: UIView? + weak var iconView: UIImageView? + weak var titleView: UILabel? + + public var title: String? { + get { + return self.titleView?.text + } + set { + self.titleView?.text = newValue + self.setNeedsUpdateConstraints() + } + } + + public convenience init() { + self.init(frame: CGRectZero) + } + + required override public init(frame: CGRect) { + super.init(frame: frame) + self.layoutHeader() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.layoutHeader() + } + + private func layoutHeader() { + let container = UIView() + let titleView = UILabel() + let iconView = UIImageView() + + self.addSubview(container) + container.addSubview(titleView) + container.addSubview(iconView) + + constraintEqual(anchor: container.leftAnchor, toAnchor: self.leftAnchor) + constraintEqual(anchor: container.bottomAnchor, toAnchor: self.bottomAnchor) + constraintEqual(anchor: container.topAnchor, toAnchor: self.topAnchor) + constraintEqual(anchor: container.rightAnchor, toAnchor: self.rightAnchor) + container.translatesAutoresizingMaskIntoConstraints = false + + constraintEqual(anchor: titleView.centerXAnchor, toAnchor: container.centerXAnchor) + constraintEqual(anchor: titleView.centerYAnchor, toAnchor: container.centerYAnchor) + titleView.translatesAutoresizingMaskIntoConstraints = false + + constraintEqual(anchor: iconView.rightAnchor, toAnchor: titleView.leftAnchor, constant: -5) + constraintEqual(anchor: iconView.bottomAnchor, toAnchor: titleView.bottomAnchor, constant: -1) + iconView.translatesAutoresizingMaskIntoConstraints = false + + container.backgroundColor = UIColor(red:0.97, green:0.97, blue:0.97, alpha:1.0) + + titleView.font = UIFont.systemFontOfSize(17) + titleView.textColor = UIColor(red:0.45, green:0.45, blue:0.45, alpha:1.0) + + self.titleView = titleView + self.iconView = iconView + + self.clipsToBounds = true + } + + func setIcon(name: String) { + self.iconView?.image = image(named: name) + self.iconView?.tintColor = UIColor ( red: 0.5725, green: 0.5804, blue: 0.5843, alpha: 1.0 ) + } + + public override func intrinsicContentSize() -> CGSize { + return CGSize(width: 200, height: 40) + } + + +} diff --git a/Lock/InputField.swift b/Lock/InputField.swift index 9ca6c9336..76bc976bc 100644 --- a/Lock/InputField.swift +++ b/Lock/InputField.swift @@ -63,6 +63,7 @@ public class InputField: UIView, UITextFieldDelegate { } set { self.textField?.returnKeyType = newValue + self.textField?.reloadInputViews() } } diff --git a/Lock/Router.swift b/Lock/Router.swift index c267e1dc9..ce48aa1ff 100644 --- a/Lock/Router.swift +++ b/Lock/Router.swift @@ -95,7 +95,7 @@ struct Router: Navigable { if !connections.enterprise.isEmpty { let authInteractor = Auth0OAuth2Interactor(webAuth: self.lock.webAuth, onCredentials: self.onAuthentication, options: self.lock.options) let interactor = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authInteractor) - presenter.enterprisePresenter = EnterpriseDomainPresenter(interactor: interactor) + presenter.enterpriseInteractor = interactor } return presenter } @@ -141,20 +141,6 @@ struct Router: Navigable { presenter.customLogger = self.lock.logger return presenter } - - var enterprise: Presentable? { - let connections = self.lock.connections - guard !connections.isEmpty else { - exit(withError: UnrecoverableError.ClientWithNoConnections) - return nil - } - - let authInteractor = Auth0OAuth2Interactor(webAuth: self.lock.webAuth, onCredentials: self.onAuthentication, options: self.lock.options) - var interactor = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authInteractor) - _ = try? interactor.updateEmail(self.user.email) - let presenter = EnterpriseDomainPresenter(interactor: interactor) - return presenter - } var showBack: Bool { guard let routes = self.controller?.routes else { return false } @@ -181,8 +167,6 @@ struct Router: Navigable { presentable = self.forgotPassword case .Multifactor: presentable = self.multifactor - case .Enterprise: - presentable = self.enterprise default: self.lock.logger.warn("Ignoring navigation \(route)") return diff --git a/Lock/Routes.swift b/Lock/Routes.swift index a2baa15cd..736a6831c 100644 --- a/Lock/Routes.swift +++ b/Lock/Routes.swift @@ -48,5 +48,4 @@ enum Route { case Root case ForgotPassword case Multifactor - case Enterprise } From d0b6dcbac22574bdbd3f4b8c46b6733fa2e79de3 Mon Sep 17 00:00:00 2001 From: Martin Walsh Date: Fri, 11 Nov 2016 17:09:33 -0300 Subject: [PATCH 3/5] Domain with Enterprise tests More swift in places View spacing --- Lock/DatabaseOnlyView.swift | 17 +- Lock/DatabasePresenter.swift | 1 - Lock/InfoBarView.swift | 2 +- .../Presenters/DatabasePresenterSpec.swift | 259 ++++++++++++------ 4 files changed, 192 insertions(+), 87 deletions(-) diff --git a/Lock/DatabaseOnlyView.swift b/Lock/DatabaseOnlyView.swift index 54498d541..2483fc57f 100644 --- a/Lock/DatabaseOnlyView.swift +++ b/Lock/DatabaseOnlyView.swift @@ -125,14 +125,16 @@ class DatabaseOnlyView: UIView, DatabaseView { let form = self.form as! CredentialView let infoBar = InfoBarView() - let spacer = strutView(withHeight: 40) + let viewCount = self.container!.subviews.count + let spacer = strutView(withHeight: 125 - CGFloat(viewCount) * 25) infoBar.title = "SINGLE SIGN-ON ENABLED".i18n(key: "com.auth0.lock.enterprise.sso", comment: "SSO Header") infoBar.setIcon("ic_lock") infoBar.hidden = false self.container?.insertArrangedSubview(infoBar, atIndex: 0) - //self.container?.insertArrangedSubview(spacer, atIndex: 5) + self.container?.addArrangedSubview(spacer) + self.infoBar = infoBar self.spacer = spacer @@ -146,11 +148,11 @@ class DatabaseOnlyView: UIView, DatabaseView { } func removeEnterprise() { - if self.infoBar == nil { return } - + guard let infoBar = self.infoBar, spacer = self.spacer else { return } let form = self.form as! CredentialView - self.infoBar?.removeFromSuperview() - self.spacer?.removeFromSuperview() + + infoBar.removeFromSuperview() + spacer.removeFromSuperview() form.passwordField.hidden = false form.identityField.nextField = form.passwordField @@ -158,6 +160,9 @@ class DatabaseOnlyView: UIView, DatabaseView { self.switcher?.hidden = false self.secondaryButton?.hidden = false + + self.infoBar = nil + self.spacer = nil } private func layoutSecondaryButton(enabled: Bool) { diff --git a/Lock/DatabasePresenter.swift b/Lock/DatabasePresenter.swift index f13c5ac8a..8aec3ec8e 100644 --- a/Lock/DatabasePresenter.swift +++ b/Lock/DatabasePresenter.swift @@ -103,7 +103,6 @@ class DatabasePresenter: Presentable, Loggable { if let error = error { self.messagePresenter?.showError(error) self.logger.error("Enterprise connection failed: \(error)") - self.messagePresenter?.showError(error) } else { self.logger.debug("Enterprise authenticator launched") } diff --git a/Lock/InfoBarView.swift b/Lock/InfoBarView.swift index 986c3f18c..43b8a78e7 100644 --- a/Lock/InfoBarView.swift +++ b/Lock/InfoBarView.swift @@ -92,7 +92,7 @@ public class InfoBarView: UIView { } public override func intrinsicContentSize() -> CGSize { - return CGSize(width: 200, height: 40) + return CGSize(width: 200, height: 35) } diff --git a/LockTests/Presenters/DatabasePresenterSpec.swift b/LockTests/Presenters/DatabasePresenterSpec.swift index dc066e04c..5705b7656 100644 --- a/LockTests/Presenters/DatabasePresenterSpec.swift +++ b/LockTests/Presenters/DatabasePresenterSpec.swift @@ -26,18 +26,24 @@ import Nimble @testable import Lock class DatabasePresenterSpec: QuickSpec { - + override func spec() { - + var enterpriseInteractor: EnterpriseDomainInteractor! + var oauth2: MockOAuth2! + var connections: OfflineConnections! var interactor: MockDBInteractor! var presenter: DatabasePresenter! var view: DatabaseOnlyView! var messagePresenter: MockMessagePresenter! var authPresenter: MockAuthPresenter! var navigator: MockNavigator! - + beforeEach { - authPresenter = MockAuthPresenter(connections: OfflineConnections(), interactor: MockAuthInteractor(), customStyle: [:]) + oauth2 = MockOAuth2() + connections = OfflineConnections() + + enterpriseInteractor = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: oauth2) + authPresenter = MockAuthPresenter(connections: connections, interactor: MockAuthInteractor(), customStyle: [:]) messagePresenter = MockMessagePresenter() interactor = MockDBInteractor() navigator = MockNavigator() @@ -45,21 +51,21 @@ class DatabasePresenterSpec: QuickSpec { presenter.messagePresenter = messagePresenter view = presenter.view as! DatabaseOnlyView } - + describe("auth buttons") { - + it("should init view with social view") { presenter.authPresenter = authPresenter let view = presenter.view as? DatabaseView expect(view?.authCollectionView) == authPresenter.authView } - + it("should init view with not social view") { presenter.authPresenter = nil let view = presenter.view as? DatabaseView expect(view?.authCollectionView).to(beNil()) } - + it("should set a new one when switching tabs") { presenter.authPresenter = authPresenter let view = presenter.view as? DatabaseView @@ -68,35 +74,35 @@ class DatabasePresenterSpec: QuickSpec { view?.switcher?.onSelectionChange((view?.switcher)!) expect(view?.authCollectionView) == newView } - + } - + describe("user state") { - + it("should return initial valid email") { interactor.validEmail = true interactor.email = email expect(presenter.initialEmail) == email } - + it("should not return initial invalid email") { interactor.validEmail = false interactor.email = email expect(presenter.initialEmail).to(beNil()) } - + it("should return initial valid username") { interactor.validUsername = true interactor.username = username expect(presenter.initialUsername) == username } - + it("should not return initial invalid email") { interactor.validUsername = false interactor.username = username expect(presenter.initialUsername).to(beNil()) } - + it("should set identifier default in login") { interactor.identifier = email view.switcher?.selected = .Login @@ -104,7 +110,7 @@ class DatabasePresenterSpec: QuickSpec { let view = view.form as! CredentialView expect(view.identityField.text) == email } - + it("should set email and password in signup") { interactor.validEmail = true interactor.email = email @@ -116,11 +122,11 @@ class DatabasePresenterSpec: QuickSpec { expect(view.emailField.text) == email expect(view.usernameField?.text) == username } - + } - + describe("allowed modes & initial screen") { - + it("should keep switcher when login and signup are allowed") { var options = LockOptions() options.allow = [.Login, .Signup] @@ -128,7 +134,7 @@ class DatabasePresenterSpec: QuickSpec { view = presenter.view as! DatabaseOnlyView expect(view.switcher).toNot(beNil()) } - + it("should remove switcher when login or signup are not allowed") { var options = LockOptions() options.allow = [.Login] @@ -136,7 +142,7 @@ class DatabasePresenterSpec: QuickSpec { view = presenter.view as! DatabaseOnlyView expect(view.switcher).to(beNil()) } - + it("should remove forgot button if it's not allowed") { var options = LockOptions() options.allow = [.Login] @@ -144,7 +150,7 @@ class DatabasePresenterSpec: QuickSpec { view = presenter.view as! DatabaseOnlyView expect(view.secondaryButton).to(beNil()) } - + it("should show login if is allowed and is initial screen") { var options = LockOptions() options.allow = [.Login] @@ -153,7 +159,7 @@ class DatabasePresenterSpec: QuickSpec { view = presenter.view as! DatabaseOnlyView expect(view.form as? CredentialView).toNot(beNil()) } - + it("should show signup if login is not allowed") { var options = LockOptions() options.allow = [.Signup] @@ -161,7 +167,7 @@ class DatabasePresenterSpec: QuickSpec { view = presenter.view as! DatabaseOnlyView expect(view.form as? SignUpView).toNot(beNil()) } - + it("should show signup if is the initial screen") { var options = LockOptions() options.allow = [.Signup, .Login] @@ -170,7 +176,7 @@ class DatabasePresenterSpec: QuickSpec { view = presenter.view as! DatabaseOnlyView expect(view.form as? SignUpView).toNot(beNil()) } - + it("should always show terms button in signup") { var options = LockOptions() options.allow = [.Signup] @@ -178,26 +184,26 @@ class DatabasePresenterSpec: QuickSpec { view = presenter.view as! DatabaseOnlyView expect(view.secondaryButton).toNot(beNil()) } - + } - + describe("login") { - + beforeEach { view.switcher?.selected = .Login view.switcher?.onSelectionChange(view.switcher!) } - + it("should set title for secondary button") { expect(view.secondaryButton?.title) == "Don’t remember your password?" } - + it("should reset scroll") { expect(navigator.resetted) == true } - + describe("user input") { - + it("should clear global message") { messagePresenter.showError(DatabaseAuthenticatableError.CouldNotLogin) let input = mockInput(.Email, value: email) @@ -205,31 +211,31 @@ class DatabasePresenterSpec: QuickSpec { expect(messagePresenter.error).to(beNil()) expect(messagePresenter.message).to(beNil()) } - + it("should update email") { let input = mockInput(.Email, value: email) view.form?.onValueChange(input) expect(interactor.email) == email } - + it("should update username") { let input = mockInput(.Username, value: username) view.form?.onValueChange(input) expect(interactor.username) == username } - + it("should update password") { let input = mockInput(.Password, value: password) view.form?.onValueChange(input) expect(interactor.password) == password } - + it("should update username or email") { let input = mockInput(.EmailOrUsername, value: username) view.form?.onValueChange(input) expect(interactor.username) == username } - + it("should not update if type is not valid for db connection") { let input = mockInput(.Phone, value: "+1234567890") view.form?.onValueChange(input) @@ -237,23 +243,23 @@ class DatabasePresenterSpec: QuickSpec { expect(interactor.email).to(beNil()) expect(interactor.password).to(beNil()) } - + it("should hide the field error if value is valid") { let input = mockInput(.Username, value: username) view.form?.onValueChange(input) expect(input.valid) == true } - + it("should show field error if value is invalid") { let input = mockInput(.Username, value: "invalid") view.form?.onValueChange(input) expect(input.valid) == false } - + } - + describe("login action") { - + it("should trigger action on return of last field") { let input = mockInput(.Password, value: password) input.returnKey = .Done @@ -265,7 +271,7 @@ class DatabasePresenterSpec: QuickSpec { view.form?.onReturn(input) } } - + it("should not trigger action if return key is not .Done") { let input = mockInput(.Password, value: password) input.returnKey = .Next @@ -276,7 +282,7 @@ class DatabasePresenterSpec: QuickSpec { expect(messagePresenter.message).toEventually(beNil()) expect(messagePresenter.error).toEventually(beNil()) } - + it("should clear global message") { messagePresenter.showError(DatabaseAuthenticatableError.CouldNotLogin) interactor.onLogin = { @@ -286,7 +292,7 @@ class DatabasePresenterSpec: QuickSpec { expect(messagePresenter.error).toEventually(beNil()) expect(messagePresenter.message).toEventually(beNil()) } - + it("should show global error message") { interactor.onLogin = { return .CouldNotLogin @@ -294,7 +300,7 @@ class DatabasePresenterSpec: QuickSpec { view.primaryButton?.onPress(view.primaryButton!) expect(messagePresenter.error).toEventually(beError(error: DatabaseAuthenticatableError.CouldNotLogin)) } - + it("should navigate to multifactor required screen") { interactor.onLogin = { return .MultifactorRequired @@ -302,7 +308,7 @@ class DatabasePresenterSpec: QuickSpec { view.primaryButton?.onPress(view.primaryButton!) expect(navigator.route).toEventually(equal(Route.Multifactor)) } - + it("should trigger login on button press") { waitUntil { done in interactor.onLogin = { @@ -312,7 +318,7 @@ class DatabasePresenterSpec: QuickSpec { view.primaryButton?.onPress(view.primaryButton!) } } - + it("should set button in progress on button press") { let button = view.primaryButton! waitUntil { done in @@ -324,13 +330,13 @@ class DatabasePresenterSpec: QuickSpec { button.onPress(button) } } - + it("should set button to normal after login") { let button = view.primaryButton! button.onPress(button) expect(button.inProgress).toEventually(beFalse()) } - + it("should navigate to forgot password") { let button = view.secondaryButton! button.onPress(button) @@ -338,20 +344,20 @@ class DatabasePresenterSpec: QuickSpec { } } } - + describe("sign up") { - + beforeEach { view.switcher?.selected = .Signup view.switcher?.onSelectionChange(view.switcher!) } - + it("should set title for secondary button") { expect(view.secondaryButton?.title).notTo(beNil()) } - + describe("user input") { - + it("should clear global message") { messagePresenter.showError(DatabaseUserCreatorError.CouldNotCreateUser) let input = mockInput(.Email, value: email) @@ -359,32 +365,32 @@ class DatabasePresenterSpec: QuickSpec { expect(messagePresenter.error).to(beNil()) expect(messagePresenter.message).to(beNil()) } - + it("should update email") { let input = mockInput(.Email, value: email) view.form?.onValueChange(input) expect(interactor.email) == email } - + it("should update username") { let input = mockInput(.Username, value: username) view.form?.onValueChange(input) expect(interactor.username) == username } - + it("should update password") { let input = mockInput(.Password, value: password) view.form?.onValueChange(input) expect(interactor.password) == password } - + it("should custom field") { let name = "Auth0" let input = mockInput(.Custom(name: "first_name", placeholder: "Name", icon: LazyImage(name: "ic_auth0", bundle: Lock.bundle), keyboardType: .Default, autocorrectionType: .No, secure: false), value: name) view.form?.onValueChange(input) expect(interactor.custom["first_name"]) == name } - + it("should not update if type is not valid for db connection") { let input = mockInput(.Phone, value: "+1234567890") view.form?.onValueChange(input) @@ -392,23 +398,23 @@ class DatabasePresenterSpec: QuickSpec { expect(interactor.email).to(beNil()) expect(interactor.password).to(beNil()) } - + it("should hide the field error if value is valid") { let input = mockInput(.Username, value: username) view.form?.onValueChange(input) expect(input.valid) == true } - + it("should show field error if value is invalid") { let input = mockInput(.Username, value: "invalid") view.form?.onValueChange(input) expect(input.valid) == false } - + } - + describe("sign up action") { - + it("should trigger action on return of last field") { let input = mockInput(.Password, value: password) input.returnKey = .Done @@ -420,7 +426,7 @@ class DatabasePresenterSpec: QuickSpec { view.form?.onReturn(input) } } - + it("should not trigger action if return key is not .Done") { let input = mockInput(.Password, value: password) input.returnKey = .Next @@ -431,7 +437,7 @@ class DatabasePresenterSpec: QuickSpec { expect(messagePresenter.message).toEventually(beNil()) expect(messagePresenter.error).toEventually(beNil()) } - + it("should not trigger action with nil button") { let input = mockInput(.OneTimePassword, value: "123456") input.returnKey = .Done @@ -443,7 +449,7 @@ class DatabasePresenterSpec: QuickSpec { expect(messagePresenter.message).toEventually(beNil()) expect(messagePresenter.error).toEventually(beNil()) } - + it("should clear global message") { messagePresenter.showError(DatabaseUserCreatorError.CouldNotCreateUser) interactor.onSignUp = { @@ -453,7 +459,7 @@ class DatabasePresenterSpec: QuickSpec { expect(messagePresenter.error).toEventually(beNil()) expect(messagePresenter.message).toEventually(beNil()) } - + it("should show global error message") { interactor.onSignUp = { return .CouldNotCreateUser @@ -461,7 +467,7 @@ class DatabasePresenterSpec: QuickSpec { view.primaryButton?.onPress(view.primaryButton!) expect(messagePresenter.error).toEventually(beError(error: DatabaseUserCreatorError.CouldNotCreateUser)) } - + it("should show global error message for login") { interactor.onLogin = { return .CouldNotLogin @@ -469,7 +475,7 @@ class DatabasePresenterSpec: QuickSpec { view.primaryButton?.onPress(view.primaryButton!) expect(messagePresenter.error).toEventually(beError(error: DatabaseAuthenticatableError.CouldNotLogin)) } - + it("should navigate to multifactor required screen") { interactor.onLogin = { return .MultifactorRequired @@ -477,7 +483,7 @@ class DatabasePresenterSpec: QuickSpec { view.primaryButton?.onPress(view.primaryButton!) expect(navigator.route).toEventually(equal(Route.Multifactor)) } - + it("should trigger sign up on button press") { waitUntil { done in interactor.onSignUp = { @@ -487,7 +493,7 @@ class DatabasePresenterSpec: QuickSpec { view.primaryButton?.onPress(view.primaryButton!) } } - + it("should set button in progress on button press") { let button = view.primaryButton! waitUntil { done in @@ -499,26 +505,26 @@ class DatabasePresenterSpec: QuickSpec { button.onPress(button) } } - + it("should set button to normal after signup") { let button = view.primaryButton! button.onPress(button) expect(button.inProgress).toEventually(beFalse()) } } - + describe("tos action") { - + beforeEach { let button = view.secondaryButton! button.onPress(button) } - + it("should present alert controller for ToS") { expect(navigator.presented as? UIAlertController).toEventuallyNot(beNil()) expect(navigator.presented?.title).toEventually(beNil()) } - + it("should have actions") { let alert = navigator.presented as? UIAlertController expect(alert?.message).toEventually(beNil()) @@ -528,6 +534,101 @@ class DatabasePresenterSpec: QuickSpec { expect(alert?.actions).to(haveAction("Privacy Policy", style: .Default)) } } + + describe("enterprise support") { + + beforeEach { + connections = OfflineConnections() + connections.enterprise(name: "validAD", domains: ["valid.com"]) + + enterpriseInteractor = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: oauth2) + presenter.enterpriseInteractor = enterpriseInteractor + + view = presenter.view as! DatabaseOnlyView + } + + it("should not have enterprise support") { + presenter.enterpriseInteractor = nil + expect(presenter.enterpriseInteractor).to(beNil()) + } + + it("should have enterprise support") { + expect(presenter.enterpriseInteractor).toNot(beNil()) + } + + it("should return false for invalid enterprise domain") { + expect(presenter.enterpriseInteractor?.isEnterprise("user@invalid.com")).to(beFalse()) + } + + it("should return true for valid enterprise domain") { + expect(presenter.enterpriseInteractor?.isEnterprise("user@valid.com")).to(beTrue()) + } + + it("should return connection for valid enterprise domain") { + presenter.enterpriseInteractor?.isEnterprise("user@valid.com") + expect(presenter.enterpriseInteractor?.connection).toNot(beNil()) + } + + it("should match connection name enterprise domain") { + presenter.enterpriseInteractor?.isEnterprise("user@valid.com") + expect(presenter.enterpriseInteractor?.connection?.name).to(equal("validAD")) + } + + it("should match connection name enterprise domain") { + presenter.enterpriseInteractor?.isEnterprise("user@valid.com") + expect(presenter.enterpriseInteractor?.connection?.name).to(equal("validAD")) + } + + it("should modify display with enterprise changes") { + let input = mockInput(.Email, value: "user@valid.com") + view.form?.onValueChange(input) + expect(view.infoBar).toNot(beNil()) + } + + it("should not modify display with enterprise changes") { + let input = mockInput(.Email, value: "user@invalid.com") + view.form?.onValueChange(input) + expect(view.infoBar).to(beNil()) + } + + context("with existing enterprise display") { + + beforeEach { + let input = mockInput(.Email, value: "user@valid.com") + view.form?.onValueChange(input) + } + + it("should show infobar") { + expect(view.infoBar).toNot(beNil()) + } + + it("should not show infobar") { + let input = mockInput(.Email, value: "user@invalid.com") + view.form?.onValueChange(input) + expect(view.infoBar).to(beNil()) + } + + it("should return identity returntype as .Done") { + let form = view.form as! CredentialView + expect(form.identityField.returnKey).to(equal(UIReturnKeyType.Done)) + } + + it("should restore identity returntype as .Next") { + let input = mockInput(.Email, value: "user@invalid.com") + view.form?.onValueChange(input) + + let form = view.form as! CredentialView + expect(form.identityField.returnKey).to(equal(UIReturnKeyType.Next)) + } + + it("should launch OAuth") { + + } + + } + + } + } } } @@ -542,4 +643,4 @@ func haveAction(title: String, style: UIAlertActionStyle) -> MatcherFunc<[UIAler } return false } -} \ No newline at end of file +} From b3af3f7addebc10f4ed3c9803635b788fab8e75e Mon Sep 17 00:00:00 2001 From: Martin Walsh Date: Wed, 16 Nov 2016 14:37:51 -0300 Subject: [PATCH 4/5] DatabasePresenter login streamlined Feedback changes EnterpriseDomainInteractor refactored Tests updated --- Lock/DatabaseModeSwitcher.swift | 2 +- Lock/DatabaseOnlyView.swift | 16 +-- Lock/DatabasePresenter.swift | 107 ++++++++---------- Lock/EnterpriseDomainInteractor.swift | 17 +-- Lock/EnterpriseDomainPresenter.swift | 6 +- .../Presenters/DatabasePresenterSpec.swift | 21 ++-- 6 files changed, 70 insertions(+), 99 deletions(-) diff --git a/Lock/DatabaseModeSwitcher.swift b/Lock/DatabaseModeSwitcher.swift index 8d5caac78..3ae5b9199 100644 --- a/Lock/DatabaseModeSwitcher.swift +++ b/Lock/DatabaseModeSwitcher.swift @@ -110,7 +110,7 @@ public class DatabaseModeSwitcher: UIView { } public override func intrinsicContentSize() -> CGSize { - return CGSize(width: 280, height: 55) + return CGSize(width: UIViewNoIntrinsicMetric, height: 55) } // MARK:- Internal diff --git a/Lock/DatabaseOnlyView.swift b/Lock/DatabaseOnlyView.swift index 2483fc57f..5e5b7f9a1 100644 --- a/Lock/DatabaseOnlyView.swift +++ b/Lock/DatabaseOnlyView.swift @@ -121,11 +121,12 @@ class DatabaseOnlyView: UIView, DatabaseView { } func presentEnterprise() { - if self.infoBar != nil { return } + guard let form = self.form as? CredentialView else { return } + + print("presentEnterprise") - let form = self.form as! CredentialView let infoBar = InfoBarView() - let viewCount = self.container!.subviews.count + let viewCount = self.container?.subviews.count ?? 0 let spacer = strutView(withHeight: 125 - CGFloat(viewCount) * 25) infoBar.title = "SINGLE SIGN-ON ENABLED".i18n(key: "com.auth0.lock.enterprise.sso", comment: "SSO Header") @@ -134,7 +135,7 @@ class DatabaseOnlyView: UIView, DatabaseView { self.container?.insertArrangedSubview(infoBar, atIndex: 0) self.container?.addArrangedSubview(spacer) - + self.infoBar = infoBar self.spacer = spacer @@ -148,9 +149,10 @@ class DatabaseOnlyView: UIView, DatabaseView { } func removeEnterprise() { - guard let infoBar = self.infoBar, spacer = self.spacer else { return } - let form = self.form as! CredentialView - + guard let infoBar = self.infoBar, spacer = self.spacer, form = self.form as? CredentialView else { return } + + print("removeEnterprise") + infoBar.removeFromSuperview() spacer.removeFromSuperview() diff --git a/Lock/DatabasePresenter.swift b/Lock/DatabasePresenter.swift index 8aec3ec8e..88bf658e1 100644 --- a/Lock/DatabasePresenter.swift +++ b/Lock/DatabasePresenter.swift @@ -24,27 +24,27 @@ import Foundation import SafariServices class DatabasePresenter: Presentable, Loggable { - + let database: DatabaseConnection let options: Options - + var authenticator: DatabaseAuthenticatable var creator: DatabaseUserCreator var navigator: Navigable - + var messagePresenter: MessagePresenter? var authPresenter: AuthPresenter? var enterpriseInteractor: EnterpriseDomainInteractor? - + var initialEmail: String? { return self.authenticator.validEmail ? self.authenticator.email : nil } var initialUsername: String? { return self.authenticator.validUsername ? self.authenticator.username : nil } - - weak var databaseView:DatabaseOnlyView? - + + weak var databaseView: DatabaseOnlyView? + convenience init(interactor: DatabaseInteractor, connection: DatabaseConnection, navigator: Navigable, options: Options) { self.init(authenticator: interactor, creator: interactor, connection: connection, navigator: navigator, options: options) } - + init(authenticator: DatabaseAuthenticatable, creator: DatabaseUserCreator, connection: DatabaseConnection, navigator: Navigable, options: Options) { self.authenticator = authenticator self.creator = creator @@ -52,7 +52,7 @@ class DatabasePresenter: Presentable, Loggable { self.navigator = navigator self.options = options } - + var view: View { let initialScreen = self.options.initialScreen let allow = self.options.allow @@ -69,7 +69,7 @@ class DatabasePresenter: Presentable, Loggable { self.showLogin(inView: view, identifier: self.authenticator.identifier) } } - + if allow.contains(.Login) && initialScreen == .Login { database.switcher?.selected = .Login showLogin(inView: database, identifier: authenticator.identifier) @@ -80,7 +80,7 @@ class DatabasePresenter: Presentable, Loggable { self.databaseView = database return database } - + private func showLogin(inView view: DatabaseView, identifier: String?) { self.messagePresenter?.hideCurrent() let authCollectionView = self.authPresenter?.newViewToEmbed(withInsets: UIEdgeInsetsMake(0, 18, 0, 18), isLogin: true) @@ -88,50 +88,39 @@ class DatabasePresenter: Presentable, Loggable { view.showLogin(withIdentifierStyle: style, identifier: identifier, authCollectionView: authCollectionView) let form = view.form form?.onValueChange = self.handleInput - + let action = { [weak form] (button: PrimaryButton) in self.messagePresenter?.hideCurrent() self.logger.info("Perform login for email: \(self.authenticator.email)") button.inProgress = true - - // Enterprise Authentication - if self.enterpriseInteractor?.isEnterprise(self.authenticator.email) == true { - self.enterpriseInteractor?.login { error in - Queue.main.async { - button.inProgress = false + + let errorHandler: LocalizableError? -> () = { error in + Queue.main.async { + button.inProgress = false + guard let error = error else { + self.logger.debug("Logged in!") + return + } + if case DatabaseAuthenticatableError.MultifactorRequired = error { + self.navigator.navigate(.Multifactor) + } else { form?.needsToUpdateState() - if let error = error { - self.messagePresenter?.showError(error) - self.logger.error("Enterprise connection failed: \(error)") - } else { - self.logger.debug("Enterprise authenticator launched") - } + self.messagePresenter?.showError(error) + self.logger.error("Failed with error \(error)") } - } - + } + + // Enterprise Authentication + if self.enterpriseInteractor?.connection != nil { + self.enterpriseInteractor?.login(errorHandler) } else { // Database Authentication - let interactor = self.authenticator - interactor.login { error in - Queue.main.async { - button.inProgress = false - guard let error = error else { - self.logger.debug("Logged in!") - return - } - if case .MultifactorRequired = error { - self.navigator.navigate(.Multifactor) - } else { - form?.needsToUpdateState() - self.messagePresenter?.showError(error) - self.logger.error("Failed with error \(error)") - } - } - } + self.authenticator.login(errorHandler) } + } - + view.form?.onReturn = { field in guard let button = view.primaryButton where field.returnKey == .Done else { return } // FIXME: Log warn action(button) @@ -143,7 +132,7 @@ class DatabasePresenter: Presentable, Loggable { self.navigator.navigate(.ForgotPassword) } } - + private func showSignup(inView view: DatabaseView, username: String?, email: String?) { self.messagePresenter?.hideCurrent() let authCollectionView = self.authPresenter?.newViewToEmbed(withInsets: UIEdgeInsetsMake(0, 18, 0, 18), isLogin: false) @@ -158,7 +147,7 @@ class DatabasePresenter: Presentable, Loggable { interactor.create { createError, loginError in Queue.main.async { button.inProgress = false - + guard createError != nil || loginError != nil else { self.logger.debug("Logged in!") return @@ -167,16 +156,16 @@ class DatabasePresenter: Presentable, Loggable { self.navigator.navigate(.Multifactor) return } - + let error: LocalizableError = createError ?? loginError! form?.needsToUpdateState() self.messagePresenter?.showError(error) self.logger.error("Failed with error \(error)") - + } } } - + view.form?.onReturn = { field in guard let button = view.primaryButton where field.returnKey == .Done else { return } // FIXME: Log warn action(button) @@ -193,11 +182,10 @@ class DatabasePresenter: Presentable, Loggable { self.navigator.present(alert) } } - + private func handleInput(input: InputField) { self.messagePresenter?.hideCurrent() - self.databaseView?.removeEnterprise() - + self.logger.verbose("new value: \(input.text) for type: \(input.type)") let attribute: UserAttribute? switch input.type { @@ -214,16 +202,17 @@ class DatabasePresenter: Presentable, Loggable { default: attribute = nil } - + guard let attr = attribute else { return } do { try self.authenticator.update(attr, value: input.text) // Check for Entperise domain match in login view - if var enterpriseInteractor = self.enterpriseInteractor, let mode = self.databaseView?.switcher?.selected { - if enterpriseInteractor.isEnterprise(input.text) && mode == .Login { - logger.debug("Enterprise connection detected: \(enterpriseInteractor.connection)") - self.databaseView?.presentEnterprise() - } + if self.enterpriseInteractor?.matchDomain(input.text) != nil, let mode = self.databaseView?.switcher?.selected where mode == .Login { + try self.enterpriseInteractor?.updateEmail(input.text) + self.logger.verbose("Enterprise connection detected: \(self.enterpriseInteractor?.connection)") + if self.databaseView?.infoBar == nil { self.databaseView?.presentEnterprise() } + } else { + self.databaseView?.removeEnterprise() } input.showValid() } catch let error as InputValidationError { @@ -232,7 +221,7 @@ class DatabasePresenter: Presentable, Loggable { input.showError() } } - + } private func safariBuilder(forURL url: NSURL, navigator: Navigable) -> (UIAlertAction) -> () { diff --git a/Lock/EnterpriseDomainInteractor.swift b/Lock/EnterpriseDomainInteractor.swift index 96141fb45..66783e62f 100644 --- a/Lock/EnterpriseDomainInteractor.swift +++ b/Lock/EnterpriseDomainInteractor.swift @@ -38,14 +38,12 @@ struct EnterpriseDomainInteractor: HRDAuthenticatable { self.authenticator = authentication } - private func validateDomain(connections: [EnterpriseConnection], value: String?) -> EnterpriseConnection? { - + func matchDomain(value: String?) -> EnterpriseConnection? { guard let domain = value?.componentsSeparatedByString("@").last else { return nil } return connections.filter { $0.domains.contains(domain) }.first } mutating func updateEmail(value: String?) throws { - validEmail = false connection = nil @@ -55,23 +53,12 @@ struct EnterpriseDomainInteractor: HRDAuthenticatable { } validEmail = true - self.connection = validateDomain(connections, value: email) + connection = matchDomain(value) } func login(callback: (OAuth2AuthenticatableError?) -> ()) { guard let connection = self.connection else { return callback(.NoConnectionAvailable) } - authenticator.login(connection.name, callback: callback) - } - mutating func isEnterprise(value: String?) -> Bool { - do { - try updateEmail(value) - if self.connection != nil { return true } - } catch { - return false - } - return false - } } diff --git a/Lock/EnterpriseDomainPresenter.swift b/Lock/EnterpriseDomainPresenter.swift index 903ccd50b..6e2e4ddcf 100644 --- a/Lock/EnterpriseDomainPresenter.swift +++ b/Lock/EnterpriseDomainPresenter.swift @@ -41,10 +41,8 @@ class EnterpriseDomainPresenter: Presentable, Loggable { let authCollectionView = self.authPresenter?.newViewToEmbed(withInsets: UIEdgeInsetsMake(0, 0, 0, 0), isLogin: true) let view = EnterpriseDomainView(email: email, authCollectionView: authCollectionView) let form = view.form - - if self.interactor.connection != nil { - view.infoBar?.hidden = false - } + + view.infoBar?.hidden = self.interactor.connection != nil view.form?.onValueChange = { input in self.messagePresenter?.hideCurrent() diff --git a/LockTests/Presenters/DatabasePresenterSpec.swift b/LockTests/Presenters/DatabasePresenterSpec.swift index 5705b7656..3084e1886 100644 --- a/LockTests/Presenters/DatabasePresenterSpec.swift +++ b/LockTests/Presenters/DatabasePresenterSpec.swift @@ -556,27 +556,22 @@ class DatabasePresenterSpec: QuickSpec { expect(presenter.enterpriseInteractor).toNot(beNil()) } - it("should return false for invalid enterprise domain") { - expect(presenter.enterpriseInteractor?.isEnterprise("user@invalid.com")).to(beFalse()) - } - - it("should return true for valid enterprise domain") { - expect(presenter.enterpriseInteractor?.isEnterprise("user@valid.com")).to(beTrue()) + it("should return nil for invalid enterprise domain") { + expect(presenter.enterpriseInteractor?.matchDomain("user@invalid.com")).to(beNil()) } it("should return connection for valid enterprise domain") { - presenter.enterpriseInteractor?.isEnterprise("user@valid.com") - expect(presenter.enterpriseInteractor?.connection).toNot(beNil()) + expect(presenter.enterpriseInteractor?.matchDomain("user@valid.com")).toNot(beNil()) } - + it("should match connection name enterprise domain") { - presenter.enterpriseInteractor?.isEnterprise("user@valid.com") - expect(presenter.enterpriseInteractor?.connection?.name).to(equal("validAD")) + let connection = presenter.enterpriseInteractor?.matchDomain("user@valid.com") + expect(connection?.name).to(equal("validAD")) } it("should match connection name enterprise domain") { - presenter.enterpriseInteractor?.isEnterprise("user@valid.com") - expect(presenter.enterpriseInteractor?.connection?.name).to(equal("validAD")) + let connection = presenter.enterpriseInteractor?.matchDomain("user@valid.com") + expect(connection?.name).to(equal("validAD")) } it("should modify display with enterprise changes") { From 4bb2c25f6a1c9ea7de7999cf37e05f30a586b712 Mon Sep 17 00:00:00 2001 From: Hernan Zalazar Date: Wed, 16 Nov 2016 14:42:54 -0300 Subject: [PATCH 5/5] Remove print statement --- Lock/DatabaseOnlyView.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lock/DatabaseOnlyView.swift b/Lock/DatabaseOnlyView.swift index 5e5b7f9a1..60123b992 100644 --- a/Lock/DatabaseOnlyView.swift +++ b/Lock/DatabaseOnlyView.swift @@ -123,8 +123,6 @@ class DatabaseOnlyView: UIView, DatabaseView { func presentEnterprise() { guard let form = self.form as? CredentialView else { return } - print("presentEnterprise") - let infoBar = InfoBarView() let viewCount = self.container?.subviews.count ?? 0 let spacer = strutView(withHeight: 125 - CGFloat(viewCount) * 25) @@ -151,8 +149,6 @@ class DatabaseOnlyView: UIView, DatabaseView { func removeEnterprise() { guard let infoBar = self.infoBar, spacer = self.spacer, form = self.form as? CredentialView else { return } - print("removeEnterprise") - infoBar.removeFromSuperview() spacer.removeFromSuperview()