...
 
Commits (7)
......@@ -17,6 +17,12 @@ enum Preferences: String {
case BackgroundImage
/// The alpha value of the background image as an `Int`.
case BackgroundImageAlpha
/// Should we check the domain user's password at every login? Set as a `Bool`
case CheckDomainUserEveryLogin
/// Should we check every user again a domain, regardless of whether NoLo created them initially? Set as a `Bool`
case CheckEveryUserEveryLogin
/// An array of users to exclude from the CheckEveryUserEveryLogin option. Populate this `Array` with `Strings` of your local accounts, i.e. 'admin'.
case CheckEveryUserExclusions
/// Should new users be created as local administrators? Set as a `Bool`.
case CreateAdminUser
/// List of groups that should have its members created as local administrators. Set as an Array of Strings of the group name.
......
This diff is collapsed.
......@@ -30,6 +30,8 @@ class NoLoMechanism: NSObject {
/// A convience property to access the `AuthorizationEngineRef` of the Authorization Mechanism.
let mechEngine: AuthorizationEngineRef
static let nomadMetaPrefix: NSString = "_nomad"
//MARK: - Initializer
......@@ -251,6 +253,36 @@ class NoLoMechanism: NSObject {
os_log("Results of local user check %{public}@", log: noLoMechlog, type: .debug, isLocal.description)
return isLocal
}
/// Checks to see if a given user exits in the DSLocal OD node AND was created by NoLo
///
/// - Parameter name: The shortname of the user to check as a `String`.
/// - Returns: `true` if the user already exists locally and was created by NoLo. Otherwise `false`.
class func checkForNoMADUser(name: String) -> Bool {
os_log("Checking for local username", log: noLoMechlog, type: .debug)
var records = [ODRecord]()
let odsession = ODSession.default()
do {
let node = try ODNode.init(session: odsession, type: ODNodeType(kODNodeTypeLocalNodes))
let query = try ODQuery.init(node: node, forRecordTypes: kODRecordTypeUsers, attribute: kODAttributeTypeRecordName, matchType: ODMatchType(kODMatchEqualTo), queryValues: name, returnAttributes: kODAttributeTypeAllAttributes, maximumResults: 0)
records = try query.resultsAllowingPartial(false) as! [ODRecord]
} catch {
let errorText = error.localizedDescription
os_log("ODError while trying to check for local user: %{public}@", log: noLoMechlog, type: .error, errorText)
return false
}
for record in records {
if let values = try? record.values(forAttribute: "dsAttrTypeNative:\(nomadMetaPrefix)_didCreateUser") {
for value in values {
if value as? String == "1" {
os_log("User was created by NoLo", log: noLoMechlog, type: .debug)
return true
}
}
}
}
return false
}
class func verifyUser(name: String, auth: String) -> Bool {
os_log("Finding user record", log: noLoMechlog, type: .debug)
......@@ -296,6 +328,64 @@ class NoLoMechanism: NSObject {
return records.first?.recordName
}
}
/// Decides if we should skip straight to a local login for a user
///
/// - Parameters:
/// - name: the shortname of the user to check as a `String`.
/// - Returns: `true` if we should skip straight to a local logi, skipping account creation and updating.
class func checkIfLocalOnlyUser(name: String) -> Bool {
// true = should skip all AD checks and account creation
// false = should do an AD check if specified, and create or update if specified in prefs
if NoLoMechanism.checkForLocalUser(name: name) {
// user exists on the system
// Should we check creds at login?
var checkDomainUserEveryLogin = false
if let prefVal = getManagedPreference(key: .CheckDomainUserEveryLogin) as? Bool {
checkDomainUserEveryLogin = prefVal
}
var checkEveryUser = false
if let prefVal = getManagedPreference(key: .CheckEveryUserEveryLogin) as? Bool {
checkEveryUser = prefVal
}
if checkDomainUserEveryLogin {
// we should check user creds at login
// Is the user a nomad user?
if NoLoMechanism.checkForNoMADUser(name: name) {
// yes, user is a nomad user. We should do a check.
return false
} else {
// user is not a nomad user
// what about should we check every user?
if checkEveryUser {
// yes we should check every user
// except those on the exclusion list
var checkEveryUserExclusions = ["admin", "administrator"]
if let users = getManagedPreference(key: .CheckEveryUserExclusions) as? [String] {
checkEveryUserExclusions = users
}
if checkEveryUserExclusions.contains(name) {
// user is on the exclusion list, local only login
return true
} else {
// user is not excluded, proceed with the check!
return false
}
} else {
// no, we should not check every user.
return true
}
}
} else {
// no, we should not chekc at login. Local-only login
return true
}
}
// user does not exist on the system, so no chance for local login
return false
}
}
......
......@@ -225,6 +225,19 @@ class SignIn: NSWindowController {
username.isEnabled = !username.isEnabled
password.isEnabled = !password.isEnabled
}
// Sequence to perform a local login
fileprivate func localLogin() {
os_log("Verify local user login for %{public}@", log: uiLog, type: .default, shortName)
if NoLoMechanism.verifyUser(name: shortName, auth: passString) {
os_log("Allowing local user login for %{public}@", log: uiLog, type: .default, shortName)
setRequiredHintsAndContext()
completeLogin(authResult: .allow)
} else {
os_log("Could not verify %{public}@", log: uiLog, type: .default, shortName)
completeLogin(authResult: .deny)
}
}
/// When the sign in button is clicked we check a few things.
......@@ -242,32 +255,28 @@ class SignIn: NSWindowController {
}
loginStartedUI()
prepareAccountStrings()
if NoLoMechanism.checkForLocalUser(name: shortName) {
os_log("Verify local user login for %{public}@", log: uiLog, type: .default, shortName)
if NoLoMechanism.verifyUser(name: shortName, auth: passString) {
os_log("Allowing local user login for %{public}@", log: uiLog, type: .default, shortName)
setRequiredHintsAndContext()
completeLogin(authResult: .allow)
} else {
os_log("Could not verify %{public}@", log: uiLog, type: .default, shortName)
completeLogin(authResult: .deny)
}
} else {
session = NoMADSession.init(domain: domainName, user: shortName)
os_log("NoMAD Login User: %{public}@, Domain: %{public}@", log: uiLog, type: .default, shortName, domainName)
guard let session = session else {
os_log("Could not create NoMADSession from SignIn window", log: uiLog, type: .error)
return
}
session.useSSL = isSSLRequired
session.userPass = passString
session.delegate = self
if let ignoreSites = getManagedPreference(key: .IgnoreSites) as? Bool {
session.siteIgnore = ignoreSites
}
os_log("Attempt to authenticate user", log: uiLog, type: .debug)
session.authenticate()
// With the intro of checkDomainUserEveryLogin, we need an additional check to tell if a local account should be subjected to an AD check (i.e., created by NoLo)
if NoLoMechanism.checkIfLocalOnlyUser(name: shortName) {
localLogin()
return
}
// Not a local user, or a NoLo user with checkDomainUserEveryLogin turned on
session = NoMADSession.init(domain: domainName, user: shortName)
os_log("NoMAD Login User: %{public}@, Domain: %{public}@", log: uiLog, type: .default, shortName, domainName)
guard let session = session else {
os_log("Could not create NoMADSession from SignIn window", log: uiLog, type: .error)
return
}
session.useSSL = isSSLRequired
session.userPass = passString
session.delegate = self
if let ignoreSites = getManagedPreference(key: .IgnoreSites) as? Bool {
session.siteIgnore = ignoreSites
}
os_log("Attempt to authenticate user", log: uiLog, type: .debug)
session.authenticate()
}
......@@ -412,6 +421,10 @@ extension SignIn: NoMADUserSessionDelegate {
os_log("Password is expired or requires change.", log: uiLog, type: .default)
showResetUI()
return
case .OffDomain:
os_log("Network is not available, falling back to local accounts", log: uiLog, type: .default)
localLogin()
return
default:
os_log("NoMAD Login Authentication failed with: %{public}@", log: uiLog, type: .error, description)
completeLogin(authResult: .deny)
......