Skip to content

Commit

Permalink
Merge pull request #343 from auth0/enterprise_single_latest
Browse files Browse the repository at this point in the history
Enterprise single domain support
  • Loading branch information
hzalaz authored Nov 24, 2016
2 parents 9ff4f8c + 93057a5 commit af1330b
Show file tree
Hide file tree
Showing 18 changed files with 265 additions and 62 deletions.
2 changes: 1 addition & 1 deletion Lock/AuthCollectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func oauth2Buttons(forConnections connections: [OAuth2Connection], customStyle:
return connections.map { connection -> AuthButton in
let style = customStyle[connection.name] ?? connection.style
let button = AuthButton(size: .Big)
button.title = login ? style.localizedLoginTitle.uppercaseString : style.localizedSignUpTitle.uppercaseString
button.title = login ? style.localizedLoginTitle().uppercaseString : style.localizedSignUpTitle().uppercaseString
button.normalColor = style.normalColor
button.highlightedColor = style.highlightedColor
button.titleColor = style.foregroundColor
Expand Down
16 changes: 10 additions & 6 deletions Lock/AuthStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@ import UIKit
public class AuthStyle {

/// Name that will be used for titles. e.g. 'Login with Auth0'
public let name: String
let name: String
let image: LazyImage
let foregroundColor: UIColor
let normalColor: UIColor
let highlightedColor: UIColor

var localizedLoginTitle: String {
func localizedLoginTitle(title: String? = nil) -> String {
let format = "Log in with %@".i18n(key: "com.auth0.lock.strategy.login.title", comment: "Log in action format")
return String(format: format, self.name)
return String(format: format, title ?? self.name)
}

var localizedSignUpTitle: String {
func localizedSignUpTitle(title: String? = nil) -> String {
let format = "Sign up with %@".i18n(key: "com.auth0.lock.strategy.signup.title", comment: "Sign up action format")
return String(format: format, self.name)
return String(format: format, title ?? self.name)
}

/**
Expand Down Expand Up @@ -270,6 +270,8 @@ extension AuthStyle {

static func style(forStrategy strategy: String, connectionName: String) -> AuthStyle {
switch strategy.lowercaseString {
case "ad", "adfs":
return .Microsoft
case "amazon":
return .Amazon
case "aol":
Expand Down Expand Up @@ -340,10 +342,12 @@ extension AuthStyle {
return .Yammer
case "yandex":
return .Yandex
case "waad":
return .Google
case "weibo":
return .Weibo
default:
return AuthStyle(name: connectionName)
}
}
}
}
4 changes: 2 additions & 2 deletions Lock/CDNLoaderInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ struct CDNLoaderInteractor: RemoteConnectionLoader, Loggable {
}
info.enterprise.forEach { strategy in
strategy.connections.forEach { connection in
let domain = connection.json["domain_aliases"] as! [String]
connections.enterprise(name: connection.name, domains: domain)
let domains = connection.json["domain_aliases"] as! [String]
connections.enterprise(name: connection.name, domains: domains, style: AuthStyle.style(forStrategy: strategy.name, connectionName: domains.first!))
}
}
info.oauth2.forEach { strategy in
Expand Down
2 changes: 1 addition & 1 deletion Lock/ConnectionBuildable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public protocol ConnectionBuildable: Connections {
- parameter name: name of the connection
- parameter domain: array of enterprise domains
*/
mutating func enterprise(name name: String, domains: [String])
mutating func enterprise(name name: String, domains: [String], style: AuthStyle)
}

public extension ConnectionBuildable {
Expand Down
8 changes: 7 additions & 1 deletion Lock/Connections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,11 @@ public struct SocialConnection: OAuth2Connection {
public struct EnterpriseConnection : OAuth2Connection {
public let name: String
public let domains: [String]
public var style: AuthStyle { return AuthStyle(name: self.name) }
public let style: AuthStyle

init(name: String, domains: [String], style: AuthStyle? = nil) {
self.name = name
self.domains = domains
self.style = style ?? AuthStyle(name: name)
}
}
7 changes: 6 additions & 1 deletion Lock/EnterpriseDomainInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@ struct EnterpriseDomainInteractor: HRDAuthenticatable {
init(connections: [EnterpriseConnection], authentication: OAuth2Authenticatable) {
self.connections = connections
self.authenticator = authentication

// Single Enterprise, defaulting connection
if self.connections.count == 1 {
self.connection = self.connections.first
}
}

func matchDomain(value: String?) -> EnterpriseConnection? {
guard let domain = value?.componentsSeparatedByString("@").last else { return nil }
return connections.filter { $0.domains.contains(domain) }.first
Expand Down
60 changes: 46 additions & 14 deletions Lock/EnterpriseDomainPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,54 @@
import Foundation

class EnterpriseDomainPresenter: Presentable, Loggable {

var interactor: EnterpriseDomainInteractor
var customLogger: Logger?
var user: User
var options: Options

// Social connections
var authPresenter: AuthPresenter?

init(interactor: EnterpriseDomainInteractor, navigator: Navigable, user: User, options: Options) {
self.interactor = interactor
self.navigator = navigator
self.user = user
self.options = options
}

var messagePresenter: MessagePresenter?
var navigator: Navigable?

var view: View {
let email = self.interactor.validEmail ? self.interactor.email : nil
let authCollectionView = self.authPresenter?.newViewToEmbed(withInsets: UIEdgeInsetsMake(0, 0, 0, 0), isLogin: true)

// Single Enterprise Domain
if let enterpriseButton = EnterpriseButton(forConnections: interactor.connections, customStyle: [:], isLogin: true, onAction: {
self.interactor.login { error in
Queue.main.async {
if let error = error {
self.messagePresenter?.showError(error)
self.logger.error("Enterprise connection failed: \(error)")
} else {
self.logger.debug("Enterprise authenticator launched")
}
}

}}) {
let view = EnterpriseDomainView(authButton: enterpriseButton, authCollectionView: authCollectionView)
return view
}

let view = EnterpriseDomainView(email: email, authCollectionView: authCollectionView)
let form = view.form

view.ssoBar?.hidden = self.interactor.connection == nil

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

guard case .Email = input.type else { return }
do {
try self.interactor.updateEmail(input.text)
Expand All @@ -67,14 +84,13 @@ class EnterpriseDomainPresenter: Presentable, Loggable {
input.showError()
}
}

let action = { (button: PrimaryButton) in

let action = { (button: PrimaryButton) in
// Check for credential auth
if let connection = self.interactor.connection where self.options.enterpriseConnectionUsingActiveAuth.contains(connection.name) {
guard self.navigator?.navigate(.EnterpriseActiveAuth(connection: connection)) == nil else { return }
}

self.messagePresenter?.hideCurrent()
self.logger.info("Enterprise connection started: \(self.interactor.email), \(self.interactor.connection)")
let interactor = self.interactor
Expand All @@ -90,17 +106,33 @@ class EnterpriseDomainPresenter: Presentable, Loggable {
self.logger.debug("Enterprise authenticator launched")
}
}

}

}

view.primaryButton?.onPress = action
view.form?.onReturn = {_ in
guard let button = view.primaryButton else { return }
action(button)
}

return view
}


}

func EnterpriseButton(forConnections connections: [EnterpriseConnection], customStyle: [String: AuthStyle], isLogin login: Bool, onAction: () -> () ) -> AuthButton? {
guard let connection = connections.first where connections.count == 1 else { return nil }
let style = customStyle[connection.name] ?? connection.style
let button = AuthButton(size: .Big)
button.title = style.localizedLoginTitle(connection.domains.first).uppercaseString
button.normalColor = style.normalColor
button.highlightedColor = style.highlightedColor
button.titleColor = style.foregroundColor
button.icon = style.image.image(compatibleWithTraits: button.traitCollection)
button.onPress = { _ in
onAction()
}
return button
}
41 changes: 38 additions & 3 deletions Lock/EnterpriseDomainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class EnterpriseDomainView: UIView, View {
weak var ssoBar: InfoBarView?
weak var primaryButton: PrimaryButton?
weak var authCollectionView: AuthCollectionView?

private weak var container: UIStackView?
weak var container: UIStackView?
weak var authButton: AuthButton?

init(email: String?, authCollectionView: AuthCollectionView? = nil) {
let primaryButton = PrimaryButton()
Expand Down Expand Up @@ -92,7 +92,42 @@ class EnterpriseDomainView: UIView, View {
domainView.value = email

}


init(authButton: AuthButton, authCollectionView: AuthCollectionView? = nil) {
let container = UIStackView()
self.container = container

super.init(frame: CGRectZero)

self.addSubview(container)
self.authButton = authButton

container.alignment = .Fill
container.axis = .Vertical
container.distribution = .EqualSpacing
container.spacing = 5

container.addArrangedSubview(strutView(withHeight: 25))
if let authCollectionView = authCollectionView {
self.authCollectionView = authCollectionView
container.addArrangedSubview(authCollectionView)
let label = UILabel()
label.text = "or".i18n(key: "com.auth0.lock.database.separator", comment: "Social separator")
label.font = mediumSystemFont(size: 13.75)
label.textColor = UIColor ( red: 0.0, green: 0.0, blue: 0.0, alpha: 0.54 )
label.textAlignment = .Center
container.addArrangedSubview(label)
}
container.addArrangedSubview(authButton)
container.addArrangedSubview(strutView())

constraintEqual(anchor: container.topAnchor, toAnchor: self.topAnchor)
constraintEqual(anchor: container.leftAnchor, toAnchor: self.leftAnchor, constant: 20)
constraintEqual(anchor: container.rightAnchor, toAnchor: self.rightAnchor, constant: -20)
constraintEqual(anchor: container.bottomAnchor, toAnchor: self.bottomAnchor)
container.translatesAutoresizingMaskIntoConstraints = false
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Expand Down
2 changes: 1 addition & 1 deletion Lock/InfoBarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class InfoBarView: UIView {
}

public override func intrinsicContentSize() -> CGSize {
return CGSize(width: 200, height: 35)
return CGSize(width: UIViewNoIntrinsicMetric, height: 35)
}


Expand Down
4 changes: 2 additions & 2 deletions Lock/OfflineConnections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ struct OfflineConnections: ConnectionBuildable {
self.oauth2.append(social)
}

mutating func enterprise(name name: String, domains: [String]) {
let enterprise = EnterpriseConnection(name: name, domains: domains)
mutating func enterprise(name name: String, domains: [String], style: AuthStyle) {
let enterprise = EnterpriseConnection(name: name, domains: domains, style: style)
self.enterprise.append(enterprise)
}

Expand Down
11 changes: 6 additions & 5 deletions Lock/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,13 @@ 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)
// Single enterprise in active auth mode
if let connection = interactor.connection where self.lock.options.enterpriseConnectionUsingActiveAuth.contains(connection.name) {
return EnterpriseActiveAuth(connection)
}
let presenter = EnterpriseDomainPresenter(interactor: interactor, navigator: self, user: self.user, options: self.lock.options)
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)
presenter.authPresenter = AuthPresenter(connections: connections, interactor: authInteractor, customStyle: self.lock.style.oauth2)
}
return presenter
}
Expand Down Expand Up @@ -141,14 +144,12 @@ struct Router: Navigable {
presenter.customLogger = self.lock.logger
return presenter
}

func EnterpriseActiveAuth(connection: EnterpriseConnection) -> Presentable? {

let authentication = self.lock.authentication
let interactor = EnterpriseActiveAuthInteractor(connection: connection, authentication: authentication, user: self.user, options: self.lock.options, callback: self.onAuthentication)
let presenter = EnterpriseActiveAuthPresenter(interactor: interactor)
presenter.customLogger = self.lock.logger

return presenter
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class EnterpriseActiveAuthInteractorSpec: QuickSpec {
var connection: EnterpriseConnection!

beforeEach {
connection = EnterpriseConnection(name: "TestAD", domains: ["test.com"])
connection = EnterpriseConnection(name: "TestAD", domains: ["test.com"], style: AuthStyle(name: "ad"))
user = User()
options = LockOptions()
interactor = EnterpriseActiveAuthInteractor(connection: connection, authentication: authentication, user: user, options: options, callback: {_ in})
Expand Down
Loading

0 comments on commit af1330b

Please sign in to comment.