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 single domain support #343

Merged
merged 7 commits into from
Nov 24, 2016
Merged
Show file tree
Hide file tree
Changes from 6 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
8 changes: 6 additions & 2 deletions Lock/AuthStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import UIKit
public class AuthStyle {

/// Name that will be used for titles. e.g. 'Login with Auth0'
public let name: String
public var name: String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why var?

let image: LazyImage
let foregroundColor: UIColor
let normalColor: UIColor
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to have a way to add a new enterprise connection without defining an AuthStyle

}

public extension ConnectionBuildable {
Expand Down
2 changes: 1 addition & 1 deletion Lock/Connections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ 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
}
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
61 changes: 47 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,34 @@ 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
style.name = connection.domains.first!
let button = AuthButton(size: .Big)
button.title = login ? style.localizedLoginTitle.uppercaseString : style.localizedSignUpTitle.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
33 changes: 26 additions & 7 deletions LockTests/Interactors/EnterpriseDomainInteractorSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class EnterpriseDomainInteractorSpec: QuickSpec {

beforeEach {
connections = OfflineConnections()
connections.enterprise(name: "TestAD", domains: ["test.com"])
connections.enterprise(name: "TestAD", domains: ["test.com"], style: AuthStyle(name: "ad"))
connections.enterprise(name: "validAD", domains: ["valid.com"], style: AuthStyle(name: "ad"))

credentials = nil
webAuth = MockWebAuth()
Expand All @@ -57,6 +58,25 @@ class EnterpriseDomainInteractorSpec: QuickSpec {
it("should have an entperise object") {
expect(enterprise).toNot(beNil())
}

it("connection should be nil") {
expect(enterprise.connection).to(beNil())
}

context("connection with single enterprise conection") {

beforeEach {
connections = OfflineConnections()
connections.enterprise(name: "TestAD", domains: [], style: AuthStyle(name: "ad"))

enterprise = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authentication)
}

it("connection should not default to single connection") {
expect(enterprise.connection).toNot(beNil())
}

}

}

Expand All @@ -66,7 +86,7 @@ class EnterpriseDomainInteractorSpec: QuickSpec {

beforeEach {
connections = OfflineConnections()
connections.enterprise(name: "TestAD", domains: [])
connections.enterprise(name: "TestAD", domains: [], style: AuthStyle(name: "ad"))
enterprise = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authentication)
}

Expand All @@ -78,11 +98,10 @@ class EnterpriseDomainInteractorSpec: QuickSpec {
}

context("connection with one domain") {



beforeEach {
connections = OfflineConnections()
connections.enterprise(name: "TestAD", domains: ["test.com"])
connections.enterprise(name: "TestAD", domains: ["test.com"], style: AuthStyle(name: "ad"))
enterprise = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authentication)
}

Expand Down Expand Up @@ -116,7 +135,7 @@ class EnterpriseDomainInteractorSpec: QuickSpec {

beforeEach {
connections = OfflineConnections()
connections.enterprise(name: "TestAD", domains: ["test.com","pepe.com"])
connections.enterprise(name: "TestAD", domains: ["test.com","pepe.com"], style: AuthStyle(name: "ad"))

enterprise = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authentication)
}
Expand All @@ -142,7 +161,7 @@ class EnterpriseDomainInteractorSpec: QuickSpec {
error = nil

connections = OfflineConnections()
connections.enterprise(name: "TestAD", domains: ["test.com"])
connections.enterprise(name: "TestAD", domains: ["test.com"], style: AuthStyle(name: "ad"))
enterprise = EnterpriseDomainInteractor(connections: connections.enterprise, authentication: authentication)
}

Expand Down
Loading