Commit 5fd54506 authored by Joel Rennich's avatar Joel Rennich

Merge branch 'Barzona' into 'Experimental'

Barzona Merge into Experimental with 1.0.5 release

See merge request !66
parents f1c455cf 7795b37e
This diff is collapsed.
......@@ -97,7 +97,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
allowLocationSimulation = "YES"
language = "nl">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
......
......@@ -58,28 +58,12 @@
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "NoMAD/NoMADMenuController.swift"
timestampString = "514159644.35557"
timestampString = "517192674.028293"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "442"
endingLineNumber = "442"
landmarkName = "NoMADMenuClickGetCertificate(_:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "NoMAD/LoginWindow.swift"
timestampString = "499803535.822901"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "270"
endingLineNumber = "270"
landmarkName = "LogInClick(_:)"
startingLineNumber = "464"
endingLineNumber = "464"
landmarkName = "lockScreenImmediate()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
......
......@@ -17,16 +17,6 @@ public func setDefaults() {
if let autoConfigure = defaults.string(forKey: Preferences.autoConfigure) {
switch autoConfigure {
case "TSL":
defaults.set("trusourcelabs.com", forKey: Preferences.aDDomain)
defaults.set("TRUSOURCELABS.COM", forKey: Preferences.kerberosRealm)
defaults.set(true, forKey: Preferences.verbose)
defaults.set("", forKey: Preferences.userCommandHotKey1)
defaults.set("", forKey: Preferences.userCommandName1)
defaults.set("", forKey: Preferences.userCommandTask1)
defaults.set(7200, forKey: Preferences.secondsToRenew)
defaults.set(1, forKey: Preferences.renewTickets)
defaults.set("", forKey: Preferences.autoConfigure)
case "JODA":
defaults.set("jodapro.com", forKey: Preferences.aDDomain)
......
......@@ -2,5 +2,5 @@
"CFBundleName" = "NoMAD";
/* (No Commment) */
"NSHumanReadableCopyright" = "Copyright 2016 Trusource Labs. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright 2016 Orchard & Grove Inc. All rights reserved.";
This diff is collapsed.
......@@ -62,6 +62,7 @@ class HistoryAndThanks {
esotericBits.append("The original repo was on a private BitBucket account, which I think still exists.\n")
esotericBits.append("It really was an attempt to get Ben Toms to do more with Swift, and wasn't really intended to be a project on it's own.\n")
esotericBits.append("Carrie the Caribou, the NoMAD mascot, is named after Carrie Fisher who died as we were designing the logo.\n")
esotericBits.append("Although I'm not really sure if female caribou have antlers... should probalby look into that.\n")
esotericBits.append("The caribou is the most nomadic land mammal, hence why a such a majastic gentle giant of the North graces the NoMAD icon.\n")
esotericBits.append("It's even possible that we are here because of the caribou - https://en.wikipedia.org/wiki/Gwich%27in.\n")
esotericBits.append("I first publicly discussed NoMAD on the MacAdmins podcast on Sept. 5, 2016. My second appearance in that podcast was where I didin't record my audio and we had to do it all over again.\n")
......
......@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.4</string>
<string>1.0.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
......@@ -39,9 +39,13 @@
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
<key>NSExceptionDomains</key>
<dict/>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Trusource Labs. All rights reserved.</string>
<string>Copyright © 2016 Orchard &amp; Grove. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
......
......@@ -23,6 +23,7 @@
- (NSString *)changeKerbPassword:(NSString *)oldPassword :(NSString *)newPassword :(NSString *)userPrincipal;
- (int)checkPassword:(NSString *)myPassword;
- (int)changeKeychainPassword:(NSString *)oldPassword :(NSString *)newPassword;
- (OSStatus)resetKeychain:(NSString *)password;
@end
......
......@@ -28,6 +28,7 @@
extern OSStatus SecKeychainChangePassword(SecKeychainRef keychainRef, UInt32 oldPasswordLength, const void* oldPassword, UInt32 newPasswordLength, const void* newPassword);
extern OSStatus SecKeychainResetLogin(UInt32 passwordLength, const void* password, Boolean resetSearchList);
- (NSString *)getKerbCredentials:(NSString *)password :(NSString *)userPrincipal {
......@@ -163,4 +164,8 @@ extern OSStatus SecKeychainChangePassword(SecKeychainRef keychainRef, UInt32 old
}
}
- (OSStatus)resetKeychain:(NSString *)password {
return SecKeychainResetLogin((UInt32)password.length, [password UTF8String], YES);
}
@end
//
// KeychainMinder.swift
// NoMAD
//
// Created by Joel Rennich on 4/25/17.
// Copyright © 2017 Trusource Labs. All rights reserved.
//
import Foundation
let keychainMinder = KeychainMinder()
class KeychainMinder : NSWindowController, NSWindowDelegate {
// class to manage the Keychain Minder window
// UI bits
@IBOutlet weak var messageText: NSTextField!
@IBOutlet weak var oldPassword: NSSecureTextField!
@IBOutlet weak var newPassword: NSSecureTextField!
@IBOutlet weak var changeButton: NSButton!
@IBOutlet weak var ignoreButton: NSButton!
@IBOutlet weak var newKeychainButton: NSButton!
@IBOutlet weak var activitySpinner: NSProgressIndicator!
// variables
// overrides
override var windowNibName: String? {
return "KeychainMinder"
}
override func windowDidLoad() {
clearWindow()
}
func windowWillClose(_ notification: Notification) {
stopOperations()
clearWindow()
}
// functions
func clearWindow() {
// clear out the fields
oldPassword.stringValue = ""
newPassword.stringValue = ""
activitySpinner.stopAnimation(nil)
activitySpinner.isHidden = true
}
func startOperations() {
activitySpinner.startAnimation(nil)
activitySpinner.isHidden = false
changeButton.isEnabled = false
newPassword.isEnabled = false
oldPassword.isEnabled = false
}
func stopOperations() {
activitySpinner.stopAnimation(nil)
activitySpinner.isHidden = true
newPassword.isEnabled = true
oldPassword.isEnabled = true
}
func changePassword() -> String {
let new = newPassword.stringValue
let old = oldPassword.stringValue
do {
// get a NoMADUser object to do all the work
let noMADUser = try NoMADUser(kerberosPrincipal: "someone@SOMEWHERE.COM")
// check the new password
if noMADUser.checkCurrentConsoleUserPassword(new) != "Valid" {
// return "New password does not match your current local user password."
}
// check the old password
if try noMADUser.checkKeychainPassword(old, true) {
try noMADUser.changeKeychainPassword(old, newPassword1: new, newPassword2: new)
}
} catch {
return error.localizedDescription
}
return ""
}
func resetLocalKeychain() -> String {
let new = newPassword.stringValue
do {
// get a NoMADUser object to do all the work
let noMADUser = try NoMADUser(kerberosPrincipal: "someone@SOMEWHERE.COM")
// check the new password
if noMADUser.checkCurrentConsoleUserPassword(new) != "Valid" {
// return "New password does not match your current local user password."
}
// check the old password
try noMADUser.resetLocalKeychain(new)
} catch {
return error.localizedDescription
}
return ""
}
func showAlert(message: String) {
let myAlert = NSAlert()
myAlert.messageText = message
myAlert.beginSheetModal(for: NSApp.mainWindow!, completionHandler: nil)
}
// button actions
@IBAction func changeButtonClick(_ sender: Any) {
startOperations()
let message = changePassword()
stopOperations()
if message != "" {
showAlert(message: message)
} else {
self.window?.close()
}
}
@IBAction func ignoreButtonClick(_ sender: Any) {
// go away
self.window?.close()
}
@IBAction func newButtonClick(_ sender: Any) {
startOperations()
let message = resetLocalKeychain()
stopOperations()
if message != "" {
showAlert(message: message)
} else {
self.window?.close()
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12118" systemVersion="16E192b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12118"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="KeychainMinder" customModule="NoMAD" customModuleProvider="target">
<connections>
<outlet property="activitySpinner" destination="sHR-sF-gSE" id="Mp0-ks-qqB"/>
<outlet property="changeButton" destination="AWJ-Vo-Avn" id="8aP-xK-7BX"/>
<outlet property="messageText" destination="IcT-UP-guZ" id="YPF-er-Lvl"/>
<outlet property="newKeychainButton" destination="iCE-Vp-c8T" id="qXa-qs-LKb"/>
<outlet property="newPassword" destination="wQb-t6-rHY" id="UwV-4J-M6q"/>
<outlet property="oldPassword" destination="wTr-Qp-fEJ" id="Mkb-4f-rkk"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Keychain Locked" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="429" height="214"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="429" height="214"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wQb-t6-rHY" customClass="NSSecureTextField">
<rect key="frame" x="141" y="49" width="268" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="p8b-o1-mFv">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IcT-UP-guZ">
<rect key="frame" x="18" y="130" width="393" height="64"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" id="ZX9-VK-2YO">
<font key="font" metaFont="system"/>
<string key="title">Your current network password does not match your Keychain password. Please enter your old password and your current network password to correct this.</string>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wTr-Qp-fEJ" customClass="NSSecureTextField">
<rect key="frame" x="141" y="81" width="268" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="Qra-tX-9xM">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eMo-jv-rQP">
<rect key="frame" x="18" y="51" width="117" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Network Password" id="N3B-RK-VVf">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AWJ-Vo-Avn">
<rect key="frame" x="312" y="13" width="103" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Change" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="fO5-rG-FqV">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="changeButtonClick:" target="-2" id="bgq-xS-t1e"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iCE-Vp-c8T">
<rect key="frame" x="186" y="13" width="126" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="New Keychain" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="fGe-Fd-Ltb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="newButtonClick:" target="-2" id="Bg7-9K-jNO"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzP-vw-R0k">
<rect key="frame" x="48" y="84" width="87" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Old Password" id="pCL-Yt-KFX">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<progressIndicator wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="sHR-sF-gSE">
<rect key="frame" x="168" y="23" width="16" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</progressIndicator>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="Phu-BF-Z2I"/>
</connections>
<point key="canvasLocation" x="77.5" y="133"/>
</window>
</objects>
</document>
......@@ -51,6 +51,8 @@ class KeychainUtil {
return myErr
}
// update the password
func updatePassword(_ name: String, pass: String) -> Bool {
if (try? findPassword(name)) != nil {
......@@ -71,6 +73,33 @@ class KeychainUtil {
myErr = SecKeychainItemDelete(myKeychainItem!)
return myErr
}
// check to see if the deafult Keychain is locked
func checkLockedKeychain() -> Bool {
var myKeychain: SecKeychain?
var myKeychainStatus = SecKeychainStatus()
// get the default keychain
myErr = SecKeychainCopyDefault(&myKeychain)
if myErr == OSStatus(errSecSuccess) {
myErr = SecKeychainGetStatus(myKeychain, &myKeychainStatus)
if Int(myKeychainStatus) == 2 {
myLogger.logit(.debug, message: "Keychain is locked")
return true
}
myLogger.logit(.debug, message: "Keychain is unlocked")
return false
} else {
myLogger.logit(.debug, message: "Error checking to see if the Keychain is locked, assuming it is.")
return true
}
}
// convience functions
......@@ -109,7 +138,7 @@ class KeychainUtil {
let identitySearchDict: [String:AnyObject] = [
kSecClass as String: kSecClassIdentity,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate as String as String as AnyObject,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate as AnyObject,
// this matches e-mail address
//kSecMatchEmailAddressIfPresent as String : identifier as CFString,
......@@ -198,7 +227,6 @@ myLogger.logit(.debug, message: "Certificate doesn't match current user principa
}
}
}
}
myLogger.logit(.debug, message: "Found " + String(matchingCerts.count) + " certificates.")
myLogger.logit(.debug, message: "Found certificates: " + String(describing: matchingCerts) )
......
......@@ -75,13 +75,17 @@ class KlistUtil {
if returnAllTickets().contains("@" + defaults.string(forKey: "KerberosRealm")!) {
myLogger.logit(.base, message:"Ticket found for domain: " + defaults.string(forKey: "KerberosRealm")!)
state = true
defaults.set(state, forKey: Preferences.signedIn)
} else {
myLogger.logit(.base, message:"No ticket found for domain: " + defaults.string(forKey: "KerberosRealm")!)
state = false
defaults.set(state, forKey: Preferences.signedIn)
}
} else {
myLogger.logit(.base, message:"No tickets found.")
state = false
defaults.set(state, forKey: Preferences.signedIn)
}
}
......@@ -102,6 +106,13 @@ class KlistUtil {
cache = jsonDict?["cache"] as! String
principal = jsonDict?["principal"] as! String
if principal.contains("$@") {
myLogger.logit(.base, message: "Found kerberos ticket for machine account. Removing.")
// kill it with fire
cliTask("/usr/bin/kdestroy")
getDetails()
}
short = principal.replacingOccurrences(of: "@" + defaults.string(forKey: "KerberosRealm")!, with: "").trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
state = false
......@@ -111,19 +122,20 @@ class KlistUtil {
myLogger.logit(.debug, message: "Looking at ticket: " + String(describing: ticket))
if let tick = ticket["Principal"] as? String {
print(ticket)
let issue = dateFormatter.date(from: (ticket["Issued"] as? String)!)
let expire = dateFormatter.date(from: (ticket["Expires"] as? String)!)
let issue = dateFormatter.date(from: (ticket["Issued"] as? String)!.replacingOccurrences(of: " ", with: "0"))
let expire = dateFormatter.date(from: (ticket["Expires"] as? String)!.replacingOccurrences(of: " ", with: "0"))
let myTicket = Ticket(Issued: issue!, Expires: expire!, Principal: tick )
myLogger.logit(.debug, message: "Appending ticket: " + String(describing: myTicket))
allTickets.append(myTicket)
allTickets.append(myTicket)
state = true
}
}
}
} catch {
myLogger.logit(.debug, message: "No tickets found")
state = false }
state = false
defaults.set(state, forKey: Preferences.signedIn)
}
getExpiration()
// write out if we have tickets
......
This diff is collapsed.
//
// NSTaskWrapper.swift
// NoAD
//
// Created by Joel Rennich on 3/29/16.
// Copyright © 2016 Trusource Labs. All rights reserved.
// Copyright © 2017 Orchard & Grove Inc. All rights reserved.
//
// v. 1.4.1
......
This diff is collapsed.
......@@ -86,6 +86,12 @@ class NoMADUser {
myLogger.logit(LogLevel.debug, message: "Current Console User is an AD user.")
return originalNodeName.contains("/Active Directory")
}
} else if let authAuthority = try? String(describing: currentConsoleUserRecord.values(forAttribute: kODAttributeTypeAuthenticationAuthority)) {
if authAuthority.contains("NetLogon") {
// network only AD account
myLogger.logit(.debug, message: "Network only AD account, so treating like an AD account.")
return true
}
} else {
myLogger.logit(LogLevel.debug, message: "Current Console User is not an AD user.")
}
......@@ -321,6 +327,13 @@ class NoMADUser {
}
}
do {
try preflightCurrentConsoleUserPassword(newPassword1)
} catch let unknownError as NSError {
myLogger.logit(LogLevel.base, message: "New password does not meet local complexity requirements. Error: " + unknownError.description)
return unknownError.localizedDescription
}
do {
try currentConsoleUserRecord.changePassword(oldPassword, toPassword: newPassword1)
return ""
......@@ -386,6 +399,18 @@ class NoMADUser {
return status
}
// resets local keychain
func resetLocalKeychain(_ newPassword: String) throws {
let kerbUtil = KerbUtil()
let myError = kerbUtil.resetKeychain(newPassword)
if myError != noErr {
throw NoMADUserError.invalidResult("Unable to reset local keychain.")
}
}
// MARK: Class Functions
......@@ -395,15 +420,14 @@ class NoMADUser {
let session = ODSession.default()
var records = [ODRecord]()
do {
//let node = try ODNode.init(session: session, type: UInt32(kODNodeTypeAuthentication))
let node = try ODNode.init(session: session, type: UInt32(kODNodeTypeLocalNodes))
let node = try ODNode.init(session: session, type: UInt32(kODNodeTypeAuthentication))
//let node = try ODNode.init(session: session, type: UInt32(kODNodeTypeLocalNodes))
let query = try ODQuery.init(node: node, forRecordTypes: kODRecordTypeUsers, attribute: kODAttributeTypeRecordName, matchType: UInt32(kODMatchEqualTo), queryValues: currentConsoleUserName, returnAttributes: kODAttributeTypeNativeOnly, maximumResults: 0)
records = try query.resultsAllowingPartial(false) as! [ODRecord]
} catch {
myLogger.logit(LogLevel.base, message: "Unable to get local user account ODRecords")
}
// We may have gotten multiple ODRecords that match username,
// So make sure it also matches the UID.
if ( records != nil ) {
......
......@@ -22,7 +22,16 @@ class PasswordChangeWindow: NSWindowController, NSWindowDelegate, NSTextFieldDel
@IBOutlet weak var passwordChangeButton: NSButton!
@IBOutlet weak var HelpButton: NSButton!
@IBOutlet weak var passwordChangeSpinner: NSProgressIndicator!
// policy pop over
@IBOutlet var popController: NSViewController!
@IBOutlet var pop: NSPopover!
@IBOutlet weak var popScroll: NSScrollView!
//@IBOutlet weak var popText: NSTextView!
@IBOutlet weak var popText: NSTextField!
// password policy
@IBOutlet weak var secondaryAlert: NSButton!
......@@ -39,6 +48,8 @@ class PasswordChangeWindow: NSWindowController, NSWindowDelegate, NSTextFieldDel
var minNumber: String = "0"
var minSymbol: String = "0"
var minMatches: String = "0"
var policy: PasswordPolicy?
override var windowNibName: String! {
return "PasswordChangeWindow"
......@@ -57,16 +68,10 @@ class PasswordChangeWindow: NSWindowController, NSWindowDelegate, NSTextFieldDel
// load in the password policy
if defaults.dictionary(forKey: Preferences.passwordPolicy) != nil {
passwordPolicy = defaults.dictionary(forKey: Preferences.passwordPolicy)! as [String : AnyObject ]
minLength = passwordPolicy["minLength"] as! String
minUpperCase = passwordPolicy["minUpperCase"] as! String
minLowerCase = passwordPolicy["minLowerCase"] as! String
minNumber = passwordPolicy["minNumber"] as! String
minSymbol = passwordPolicy["minSymbol"] as! String
if passwordPolicy["minMatches"] != nil {
minMatches = passwordPolicy["minMatches"] as! String
}
passwordPolicy = defaults.dictionary(forKey: Preferences.passwordPolicy)! as [String : AnyObject ]
policy = PasswordPolicy(policy: passwordPolicy)
// set up a text field delegate
newPassword.delegate = self
newPasswordAgain.delegate = self
......@@ -94,6 +99,11 @@ class PasswordChangeWindow: NSWindowController, NSWindowDelegate, NSTextFieldDel
// set the button text
passwordChangeButton.title = "NoMADMenuController-ChangePassword".translate
self.window?.title = "NoMADMenuController-ChangePassword".translate
// set up the popover view
popScroll.frame = NSRect.init(origin: CGPoint.init(x: 0, y: 0), size: CGSize.init(width: 300, height: 100))
popText.frame = NSRect.init(origin: CGPoint.init(x: 10, y: 10), size: CGSize.init(width: 280, height: 80))
}
......@@ -188,7 +198,7 @@ class PasswordChangeWindow: NSWindowController, NSWindowDelegate, NSTextFieldDel
@IBAction func HelpButtonClicked(_ sender: Any) {
let alertController = NSAlert()
alertController.messageText = defaults.string(forKey: Preferences.messagePasswordChangePolicy)!
alertController.messageText = defaults.string(forKey: Preferences.messagePasswordChangePolicy)!.replacingOccurrences(of: "***", with: "\n")
alertController.beginSheetModal(for: self.window!, completionHandler: nil)
}
......@@ -239,30 +249,65 @@ class PasswordChangeWindow: NSWindowController, NSWindowDelegate, NSTextFieldDel
return result
}
// password complexity checks
// make popover
func showPopover(object: NSView) {
popText.stringValue = policy?.checkPassword(pass: newPassword.stringValue, username: defaults.string(forKey: Preferences.userShortName)!) ?? "All policies have been met."
pop.show(relativeTo: object.visibleRect, of: object, preferredEdge: .minY)
}
override func controlTextDidEndEditing(_ obj: Notification) {
if obj.object.unsafelyUnwrapped as! NSSecureTextField == newPassword {
if policyAlert.image == NSImage.init(imageLiteralResourceName: NSImageNameStatusUnavailable) {
showPopover(object: newPassword)