Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
twocanoes committed Jul 5, 2022
1 parent 85545c2 commit e755e30
Show file tree
Hide file tree
Showing 17 changed files with 839 additions and 68 deletions.
86 changes: 31 additions & 55 deletions XCreds/MainController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class MainController: NSObject {
//now we set the password.

DispatchQueue.main.async {
let keychainUtil = KeychainUtil()
mainMenu.webView?.window?.close()

guard let tokenInfo = notification.userInfo else {
Expand All @@ -36,77 +35,54 @@ class MainController: NSObject {
guard let tokens = tokenInfo["tokens"] as? Tokens else {
return
}
if tokens.accessToken.count>0{
let _ = keychainUtil.updatePassword(PrefKeys.accessToken.rawValue, pass: tokens.accessToken)
}

if tokens.idToken.count>0{
let _ = keychainUtil.updatePassword(PrefKeys.idToken.rawValue, pass: tokens.idToken)
}

if tokens.refreshToken.count>0 {
let _ = keychainUtil.updatePassword(PrefKeys.refreshToken.rawValue, pass: tokens.refreshToken)
mainMenu.statusBarItem.button?.image=NSImage(named: "xcreds menu icon check")


}
let cloudPassword = tokens.password
if cloudPassword.count>0 {
let localPassword = self.localPassword()

if let localPassword = localPassword {
if UserDefaults.standard.bool(forKey: PrefKeys.verifyPassword.rawValue)==true {
let verifyOIDPassword = VerifyOIDCPasswordWindowController.init(windowNibName: NSNib.Name("VerifyOIDCPassword"))
NSApp.activate(ignoringOtherApps: true)

while true {
let response = NSApp.runModal(for: verifyOIDPassword.window!)
if response == .cancel {

let alert = NSAlert()
alert.addButton(withTitle: "Skip Updating Password")
alert.addButton(withTitle: "Cancel")
alert.messageText="Are you sure you want to skip updating the local password and keychain? You local password and keychain will be out of sync with your cloud password. "
let resp = alert.runModal()
if resp == .alertFirstButtonReturn {
NSApp.stopModal(withCode: .cancel)
verifyOIDPassword.window?.close()
break

}
}
let verifyCloudPassword = verifyOIDPassword.password
if verifyCloudPassword == cloudPassword {
try? PasswordUtils.changeLocalUserAndKeychainPassword(localPassword, newPassword1: cloudPassword, newPassword2: cloudPassword)
let err = keychainUtil.updatePassword("local password", pass: cloudPassword)
if err == false {
//TODO: Log Error
}
verifyOIDPassword.window?.close()
break;
}
else {
verifyOIDPassword.window?.shake(self)
}
var updatePassword = true
if UserDefaults.standard.bool(forKey: PrefKeys.verifyPassword.rawValue)==true {
let verifyOIDPassword = VerifyOIDCPasswordWindowController.init(windowNibName: NSNib.Name("VerifyOIDCPassword"))
NSApp.activate(ignoringOtherApps: true)

while true {
let response = NSApp.runModal(for: verifyOIDPassword.window!)
if response == .cancel {

let alert = NSAlert()
alert.addButton(withTitle: "Skip Updating Password")
alert.addButton(withTitle: "Cancel")
alert.messageText="Are you sure you want to skip updating the local password and keychain? You local password and keychain will be out of sync with your cloud password. "
let resp = alert.runModal()
if resp == .alertFirstButtonReturn {
NSApp.stopModal(withCode: .cancel)
verifyOIDPassword.window?.close()
updatePassword=false
break

}
}
else {
let verifyCloudPassword = verifyOIDPassword.password
if verifyCloudPassword == cloudPassword {
try? PasswordUtils.changeLocalUserAndKeychainPassword(localPassword, newPassword1: cloudPassword, newPassword2: cloudPassword)
let err = keychainUtil.updatePassword("local password", pass: cloudPassword)
updatePassword=true
if err == false {
//TODO: Log Error
}
verifyOIDPassword.window?.close()
break;
}
else {
verifyOIDPassword.window?.shake(self)
}


}

ScheduleManager.shared.startCredentialCheck()
}
//check for updatepassword and see if we need to pass with tokens to update passwords
//add tokens xyzzy
ScheduleManager.shared.startCredentialCheck()

}
}
ScheduleManager.shared.startCredentialCheck()

}

Expand Down
140 changes: 139 additions & 1 deletion XCreds/PasswordUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import SystemConfiguration
import SecurityFoundation
import OpenDirectory

enum DSQueryableErrors: Error {
case notLocalUser
case multipleUsersFound
}

enum PasswordError: Error, CustomStringConvertible {
case itemNotFound(String)
case invalidParamater(String)
Expand Down Expand Up @@ -44,7 +49,6 @@ class PasswordUtils: NSObject {

}


// We may have gotten multiple ODRecords that match username,
// So make sure it also matches the UID.
if ( records != nil ) {
Expand All @@ -60,6 +64,17 @@ class PasswordUtils: NSObject {
return nil
}

class func verifyPassword(password:String) -> Bool {
let currentUser = PasswordUtils.getCurrentConsoleUserRecord()
do {
try currentUser?.verifyPassword(password)
}
catch {
return false

}
return true
}

class func verifyCurrentUserPassword(password:String) -> Bool {
let currentUser = PasswordUtils.getCurrentConsoleUserRecord()
Expand Down Expand Up @@ -135,4 +150,127 @@ class PasswordUtils: NSObject {
throw PasswordError.unknownError("Unknown error: " + err.description)
}
}
/// `ODNode` to DSLocal for queries and account manipulation.
public class var localNode: ODNode? {
do {
TCSLog("Finding the DSLocal node")
return try ODNode.init(session: ODSession.default(), type: ODNodeType(kODNodeTypeLocalNodes))
} catch {
TCSLog("ODError creating local node.")
return nil
}
}

/// Conviennce function to discover if a shortname has an existing local account.
///
/// - Parameter shortName: The name of the user to search for as a `String`.
/// - Returns: `true` if the user exists in DSLocal, `false` if not.
/// - Throws: Either an `ODFrameworkErrors` or a `DSQueryableErrors` if there is an error or the user is not local.
public class func isUserLocal(_ shortName: String) throws -> Bool {
do {
_ = try getLocalRecord(shortName)
} catch DSQueryableErrors.notLocalUser {
return false
} catch {
throw error
}
return true
}

/// Checks a local username and password to see if they are valid.
///
/// - Parameters:
/// - userName: The name of the user to search for as a `String`.
/// - userPass: The password for the user being tested as a `String`.
/// - Returns: `true` if the name and password combo are valid locally. `false` if the validation fails.
/// - Throws: Either an `ODFrameworkErrors` or a `DSQueryableErrors` if there is an error.
public class func isLocalPasswordValid(userName: String, userPass: String) throws -> Bool {
do {
let userRecord = try PasswordUtils.getLocalRecord(userName)
try userRecord.verifyPassword(userPass)
} catch {
let castError = error as NSError
switch castError.code {
case Int(kODErrorCredentialsInvalid.rawValue):
TCSLog("Tested password for user account: %{public}@ is not valid.")
return false
default:
throw error
}
}
return true
}

/// Searches DSLocal for an account short name and returns the `ODRecord` for the user if found.
///
/// - Parameter shortName: The name of the user to search for as a `String`.
/// - Returns: The `ODRecord` of the user if one is found in DSLocal.
/// - Throws: Either an `ODFrameworkErrors` or a `DSQueryableErrors` if there is an error or the user is not local.
public class func getLocalRecord(_ shortName: String) throws -> ODRecord {
do {
TCSLog("Building OD query for name \(shortName)")
let query = try ODQuery.init(node: localNode,
forRecordTypes: kODRecordTypeUsers,
attribute: kODAttributeTypeRecordName,
matchType: ODMatchType(kODMatchEqualTo),
queryValues: shortName,
returnAttributes: kODAttributeTypeNativeOnly,
maximumResults: 0)
let records = try query.resultsAllowingPartial(false) as! [ODRecord]

if records.count > 1 {
TCSLog("More than one local user found for name.")
throw DSQueryableErrors.multipleUsersFound
}
guard let record = records.first else {
TCSLog("No local user found. Passing on demobilizing allow login.")
throw DSQueryableErrors.notLocalUser
}
TCSLog("Found local user: \(record)")
return record
} catch {
TCSLog("ODError while trying to check for local user: %{public}@")
throw error
}
}

/// Finds all local user records on the Mac.
///
/// - Returns: A `Array` that contains the `ODRecord` for every account in DSLocal.
/// - Throws: An error from `ODFrameworkErrors` if something fails.
public class func getAllLocalUserRecords() throws -> [ODRecord] {
do {
let query = try ODQuery.init(node: localNode,
forRecordTypes: kODRecordTypeUsers,
attribute: kODAttributeTypeRecordName,
matchType: ODMatchType(kODMatchEqualTo),
queryValues: kODMatchAny,
returnAttributes: kODAttributeTypeAllAttributes,
maximumResults: 0)
return try query.resultsAllowingPartial(false) as! [ODRecord]
} catch {
TCSLog("ODError while finding local users.")
throw error
}
}

/// Returns all the non-system users on a system above UID 500.
///
/// - Returns: A `Array` that contains the `ODRecord` of all the non-system user accounts in DSLocal.
/// - Throws: An error from `ODFrameworkErrors` if something fails.
public func getAllNonSystemUsers() throws -> [ODRecord] {
do {
let allRecords = try PasswordUtils.getAllLocalUserRecords()
let nonSystem = try allRecords.filter { (record) -> Bool in
guard let uid = try record.values(forAttribute: kODAttributeTypeUniqueID) as? [String] else {
return false
}
return Int(uid.first ?? "") ?? 0 > 500 && record.recordName.first != "_"
}
return nonSystem
} catch {
TCSLog("ODError while finding local users.")
throw error
}
}
}
6 changes: 6 additions & 0 deletions XCreds/TCSUnifiedLogger.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ + (TCSUnifiedLogger *)sharedLogger
}
return sharedLogger;
}
//os_log("Unable to get home directory path.", log: "", type: .error)
- (void)os_log:(NSString *)inStr log:(NSString *)level type:(id)type{

}


- (void)logString:(NSString *)inStr level:(LogLevel)level
{
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
Expand Down
34 changes: 34 additions & 0 deletions XCreds/TokenManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,40 @@ class TokenManager {
let defaults = UserDefaults.standard
var timer: Timer?

// func saveTokensToKeychain(tokens:Tokens) -> Bool {
// let keychainUtil = KeychainUtil()
//
// if tokens.accessToken.count>0{
// let _ = keychainUtil.updatePassword(PrefKeys.accessToken.rawValue, pass: tokens.accessToken)
// }
//
// if tokens.idToken.count>0{
// let _ = keychainUtil.updatePassword(PrefKeys.idToken.rawValue, pass: tokens.idToken)
// }
//
// if tokens.refreshToken.count>0 {
// let _ = keychainUtil.updatePassword(PrefKeys.refreshToken.rawValue, pass: tokens.refreshToken)
//
//
// }
// let cloudPassword = tokens.password
//// if cloudPassword.count>0 {
//// let localPassword = self.localPassword()
////
//// if let localPassword = localPassword {
////
////// try? PasswordUtils.changeLocalUserAndKeychainPassword(localPassword, newPassword1: cloudPassword, newPassword2: cloudPassword)
////// let err = keychainUtil.updatePassword("local password", pass: cloudPassword)
////// if err == false {
////// //TODO: Log Error
////// }
////
////
////
//// }
////
//// }
// }
func getNewAccessToken(completion:@escaping (_ isSuccessful:Bool,_ hadConnectionError:Bool)->Void) -> Void {

guard let url = URL(string: defaults.string(forKey: PrefKeys.tokenEndpoint.rawValue) ?? "") else {
Expand Down
1 change: 1 addition & 0 deletions XCreds/Window+ForceToFront.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Cocoa
extension NSWindow {
@objc func forceToFrontAndFocus(_ sender: AnyObject?) {
NSApp.activate(ignoringOtherApps: true)
TCSLog("forcing front")
self.makeKeyAndOrderFront(sender);
}
}
2 changes: 2 additions & 0 deletions XCredsLoginPlugIn/ContextAndHintHandling.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ enum HintType: String {
case kerberos_principal
case passwordOverwrite // stomp on the password
case ntName
case tokens

}

// attribute statics
Expand Down

0 comments on commit e755e30

Please sign in to comment.