Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enterprise support with db #340

Merged
merged 5 commits into from
Nov 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lock.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -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 = "<group>"; };
5BCED4C51DD1FCF200E2CE8A /* EnterpriseDomainPresenterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterpriseDomainPresenterSpec.swift; sourceTree = "<group>"; };
5F1456591D5130E80085DF9C /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Colors.swift; path = Lock/Colors.swift; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -621,6 +623,7 @@
5FEAE20E1D1A5682005C0028 /* Components */ = {
isa = PBXGroup;
children = (
5B2981771DD51A460062535C /* InfoBarView.swift */,
5F99AA951D1C4AF400D27842 /* CredentialView.swift */,
5F99AA8F1D1B9E7100D27842 /* DatabaseModeSwitcher.swift */,
5FC4348B1D1DFC5A005188BC /* Form.swift */,
Expand Down Expand Up @@ -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 */,
Expand Down
2 changes: 1 addition & 1 deletion Lock/DatabaseModeSwitcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
93 changes: 69 additions & 24 deletions Lock/DatabaseOnlyView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,66 +23,68 @@
import UIKit

class DatabaseOnlyView: UIView, DatabaseView {

weak var form: Form?
weak var secondaryButton: SecondaryButton?
weak var primaryButton: PrimaryButton?
weak var switcher: DatabaseModeSwitcher?
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]:
Expand All @@ -92,7 +94,7 @@ class DatabaseOnlyView: UIView, DatabaseView {
default:
type = .Email
}

form.identityField.text = identifier
form.identityField.type = type
form.identityField.returnKey = .Next
Expand All @@ -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
Expand All @@ -117,7 +119,50 @@ class DatabaseOnlyView: UIView, DatabaseView {
self.layoutSecondaryButton(true)
self.form = form
}


func presentEnterprise() {
guard let form = self.form as? CredentialView else { return }

let infoBar = InfoBarView()
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")
infoBar.setIcon("ic_lock")
infoBar.hidden = false

self.container?.insertArrangedSubview(infoBar, atIndex: 0)
self.container?.addArrangedSubview(spacer)

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() {
guard let infoBar = self.infoBar, spacer = self.spacer, form = self.form as? CredentialView else { return }

infoBar.removeFromSuperview()
spacer.removeFromSuperview()

form.passwordField.hidden = false
form.identityField.nextField = form.passwordField
form.identityField.returnKey = .Next

self.switcher?.hidden = false
self.secondaryButton?.hidden = false

self.infoBar = nil
self.spacer = nil
}

private func layoutSecondaryButton(enabled: Bool) {
self.secondaryStrut?.removeFromSuperview()
self.secondaryButton?.removeFromSuperview()
Expand All @@ -131,7 +176,7 @@ class DatabaseOnlyView: UIView, DatabaseView {
self.container?.addArrangedSubview(view)
}
}

private func layoutSwitcher(enabled: Bool) {
self.container?.arrangedSubviews.first?.removeFromSuperview()
if enabled {
Expand All @@ -143,14 +188,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")
Expand All @@ -166,9 +211,9 @@ class DatabaseOnlyView: UIView, DatabaseView {
self.container?.insertArrangedSubview(view, atIndex: formOnlyIndex)
}
}

// MARK:- Styling

func apply(style style: Style) {
primaryButton?.apply(style: style)
}
Expand Down
32 changes: 28 additions & 4 deletions Lock/DatabasePresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ class DatabasePresenter: Presentable, Loggable {

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?

convenience init(interactor: DatabaseInteractor, connection: DatabaseConnection, navigator: Navigable, options: Options) {
self.init(authenticator: interactor, creator: interactor, connection: connection, navigator: navigator, options: options)
}
Expand Down Expand Up @@ -74,6 +77,7 @@ class DatabasePresenter: Presentable, Loggable {
database.switcher?.selected = .Signup
showSignup(inView: database, username: initialUsername, email: initialEmail)
}
self.databaseView = database
return database
}

Expand All @@ -87,17 +91,17 @@ class DatabasePresenter: Presentable, Loggable {

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

let errorHandler: LocalizableError? -> () = { error in
Queue.main.async {
button.inProgress = false
guard let error = error else {
self.logger.debug("Logged in!")
return
}
if case .MultifactorRequired = error {
if case DatabaseAuthenticatableError.MultifactorRequired = error {
self.navigator.navigate(.Multifactor)
} else {
form?.needsToUpdateState()
Expand All @@ -106,7 +110,17 @@ class DatabasePresenter: Presentable, Loggable {
}
}
}

// Enterprise Authentication
if self.enterpriseInteractor?.connection != nil {
self.enterpriseInteractor?.login(errorHandler)
} else {
// Database Authentication
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)
Expand Down Expand Up @@ -171,6 +185,7 @@ class DatabasePresenter: Presentable, Loggable {

private func handleInput(input: InputField) {
self.messagePresenter?.hideCurrent()

self.logger.verbose("new value: \(input.text) for type: \(input.type)")
let attribute: UserAttribute?
switch input.type {
Expand All @@ -191,13 +206,22 @@ class DatabasePresenter: Presentable, Loggable {
guard let attr = attribute else { return }
do {
try self.authenticator.update(attr, value: input.text)
// Check for Entperise domain match in login view
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 {
input.showError(error.localizedMessage(withConnection: self.database))
} catch {
input.showError()
}
}

}

private func safariBuilder(forURL url: NSURL, navigator: Navigable) -> (UIAlertAction) -> () {
Expand Down
9 changes: 3 additions & 6 deletions Lock/EnterpriseDomainInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -55,13 +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)

}

}
6 changes: 6 additions & 0 deletions Lock/EnterpriseDomainPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ 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

view.infoBar?.hidden = self.interactor.connection != nil

view.form?.onValueChange = { input in
self.messagePresenter?.hideCurrent()
view.infoBar?.hidden = true

guard case .Email = input.type else { return }
do {
try self.interactor.updateEmail(input.text)
input.showValid()
if let connection = self.interactor.connection {
self.logger.debug("Enterprise connection match: \(connection)")
view.infoBar?.hidden = false
}
} catch {
input.showError()
Expand All @@ -75,6 +80,7 @@ class EnterpriseDomainPresenter: Presentable, Loggable {

}
}

view.primaryButton?.onPress = action
view.form?.onReturn = {_ in
guard let button = view.primaryButton else { return }
Expand Down
Loading