diff --git a/Profile Manifest/com.twocanoes.xcreds.plist b/Profile Manifest/com.twocanoes.xcreds.plist index 9838bb61..ba51a2f4 100644 --- a/Profile Manifest/com.twocanoes.xcreds.plist +++ b/Profile Manifest/com.twocanoes.xcreds.plist @@ -5,7 +5,7 @@ pfm_app_url https://github.com/twocanoes/xcreds pfm_description - XCreds 3.3 (5273) OAuth Settings + XCreds 3.3 (5310) OAuth Settings pfm_documentation_url https://twocanoes.com/knowledge-base/xcreds-admin-guide/#preferences pfm_domain @@ -194,6 +194,25 @@ A profile can consist of payloads with different version numbers. For example, c pfm_type array + + pfm_description + List of claims that should be added to the user local account. Will be prefixed with _xcreds_oidc_. Set as an Array of Strings of the claim. + pfm_name + claimsToAddToLocalUserAccount + pfm_subkeys + + + pfm_name + claim + pfm_type + string + + + pfm_title + Claims To Add To Local User Account + pfm_type + array + pfm_default diff --git a/XCreds/PrefKeys.swift b/XCreds/PrefKeys.swift index a77b9f26..57269e13 100644 --- a/XCreds/PrefKeys.swift +++ b/XCreds/PrefKeys.swift @@ -9,7 +9,7 @@ import Foundation enum PrefKeys: String { case clientID, clientSecret, password="xcreds local password",discoveryURL, redirectURI, scopes, accessToken, idToken, refreshToken, tokenEndpoint, expirationDate, invalidToken, refreshRateHours,refreshRateMinutes, showDebug, verifyPassword, shouldShowQuitMenu, shouldShowPreferencesOnStart, shouldSetGoogleAccessTypeToOffline, passwordChangeURL, shouldShowAboutMenu, username, idpHostName, passwordElementID, shouldFindPasswordElement, shouldShowVersionInfo, shouldShowSupportStatus,shouldShowConfigureWifiButton,shouldShowMacLoginButton, loginWindowBackgroundImageURL, shouldShowCloudLoginByDefault, shouldPreferLocalLoginInsteadOfCloudLogin, idpHostNames,autoRefreshLoginTimer, loginWindowWidth, loginWindowHeight, shouldShowRefreshBanner, shouldSwitchToLoginWindowWhenLocked,accounts = "Accounts", - windowSignIn = "WindowSignIn", settingsOverrideScriptPath, localAdminUserName, localAdminPassword, usernamePlaceholder, passwordPlaceholder, shouldShowLocalOnlyCheckbox, shouldShowTokenUpdateStatus, shouldDetectNetworkToDetermineLoginWindow, showLoginWindowDelaySeconds, shouldPromptForMigration, shouldAllowKeyComboForMacLoginWindow, aliasName + windowSignIn = "WindowSignIn", settingsOverrideScriptPath, localAdminUserName, localAdminPassword, usernamePlaceholder, passwordPlaceholder, shouldShowLocalOnlyCheckbox, shouldShowTokenUpdateStatus, shouldDetectNetworkToDetermineLoginWindow, showLoginWindowDelaySeconds, shouldPromptForMigration, shouldAllowKeyComboForMacLoginWindow, aliasName,claimsToAddToLocalUserAccount //, filePathToPreventShowingUI case ropgClientID case ropgClientSecret diff --git a/XCreds/defaults.plist b/XCreds/defaults.plist index 8aff7e8f..ec21d283 100644 --- a/XCreds/defaults.plist +++ b/XCreds/defaults.plist @@ -58,5 +58,9 @@ shouldAllowKeyComboForMacLoginWindow + claimsToAddToLocalUserAccount + + groups + diff --git a/XCredsLoginPlugIn/ContextAndHintHandling.swift b/XCredsLoginPlugIn/ContextAndHintHandling.swift index e6133ee2..cc86c985 100644 --- a/XCredsLoginPlugIn/ContextAndHintHandling.swift +++ b/XCredsLoginPlugIn/ContextAndHintHandling.swift @@ -31,9 +31,10 @@ enum HintType: String { // case noMADLast // case noMADFull case adGroups - case oidcSub - case oidcIssuer +// case oidcSub +// case oidcIssuer case aliasName + case claimsToAddToLocalUserAccount } diff --git a/XCredsLoginPlugIn/LoginWindow/LoginWebViewWindowController.swift b/XCredsLoginPlugIn/LoginWindow/LoginWebViewWindowController.swift index 000ce8ed..0f1ca443 100644 --- a/XCredsLoginPlugIn/LoginWindow/LoginWebViewWindowController.swift +++ b/XCredsLoginPlugIn/LoginWindow/LoginWebViewWindowController.swift @@ -261,9 +261,9 @@ class LoginWebViewWindowController: WebViewWindowController, DSQueryable { let standardUsers = try? getAllStandardUsers() let existingUser = try? getUserRecord(sub: subValue, iss: issuerValue) - TCSLogWithMark("setting issuer and sub hint from OIDC token") - delegate.setHint(type: .oidcSub, hint: "\(subValue)") - delegate.setHint(type: .oidcIssuer, hint: "\(issuerValue)") +// TCSLogWithMark("setting issuer and sub hint from OIDC token") +// delegate.setHint(type: .oidcSub, hint: "\(subValue)") +// delegate.setHint(type: .oidcIssuer, hint: "\(issuerValue)") let aliasClaim = DefaultsOverride.standardOverride.string(forKey: PrefKeys.aliasName.rawValue) if let aliasClaim = aliasClaim, let aliasClaimValue = idTokenInfo[aliasClaim] { TCSLogWithMark("found alias claim: \(aliasClaim):\(aliasClaimValue)") diff --git a/XCredsLoginPlugIn/Mechanisms/XCredsBaseMechanism.swift b/XCredsLoginPlugIn/Mechanisms/XCredsBaseMechanism.swift index ce03a476..f8f7cc38 100644 --- a/XCredsLoginPlugIn/Mechanisms/XCredsBaseMechanism.swift +++ b/XCredsLoginPlugIn/Mechanisms/XCredsBaseMechanism.swift @@ -274,52 +274,7 @@ protocol XCredsMechanismProtocol { return true } - class func updateOIDCInfo(user: String, iss:String?, sub:String?, groups:[String]?) -> Bool { - os_log("Checking for local username", log: noLoMechlog, type: .default) - 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: user, 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 - } - - let isLocal = records.isEmpty ? false : true - os_log("Results of local user check %{public}@", log: noLoMechlog, type: .default, isLocal.description) - - if !isLocal { - return isLocal - } - - // now to update the attribute - - do { - os_log("updating sub",log: noLoMechlog, type: .error) - - try records.first?.setValue(sub, forAttribute: "dsAttrTypeNative:_xcreds_oidc_sub") - - - os_log("updating iss",log: noLoMechlog, type: .error) - - try records.first?.setValue(iss, forAttribute: "dsAttrTypeNative:_xcreds_oidc_iss") - - - if let groups = groups?.joined(separator: ";") { - try records.first?.setValue(groups, forAttribute: "dsAttrTypeNative:_xcreds_oidc_groups") - - } - } catch { - os_log("Unable to add OIDC Info", log: noLoMechlog, type: .error) - return false - } - - return true - - } + /// Set one of the known `AuthorizationTags` values to be used during mechanism evaluation. /// /// - Parameters: diff --git a/XCredsLoginPlugIn/Mechanisms/XCredsCreateUser.swift b/XCredsLoginPlugIn/Mechanisms/XCredsCreateUser.swift index 7d5747fa..519f7aa9 100644 --- a/XCredsLoginPlugIn/Mechanisms/XCredsCreateUser.swift +++ b/XCredsLoginPlugIn/Mechanisms/XCredsCreateUser.swift @@ -122,12 +122,6 @@ class XCredsCreateUser: XCredsBaseMechanism, DSQueryable { let currentDate = ISO8601DateFormatter().string(from: Date()) customAttributes["dsAttrTypeNative:\(metaPrefix)_creationDate"] = currentDate - if let oidcSubHint = getHint(type: .oidcSub) as? String { - customAttributes["dsAttrTypeNative:\(metaPrefix)_oidc_sub"] = oidcSubHint - } - if let oidcIssHint = getHint(type: .oidcIssuer) as? String { - customAttributes["dsAttrTypeNative:\(metaPrefix)_oidc_iss"] = oidcIssHint - } guard let xcredsFirst=xcredsFirst, let xcredsLast = xcredsLast else { TCSLogErrorWithMark("first or last name not defined. bailing") @@ -200,24 +194,15 @@ class XCredsCreateUser: XCredsBaseMechanism, DSQueryable { } } - - var sub:String? - var iss:String? var alias:String? - if let oidcSubHint = getHint(type: .oidcSub) as? String { - sub=oidcSubHint - } - if let oidcIssHint = getHint(type: .oidcIssuer) as? String { - iss=oidcIssHint - } + if let aliasHint = getHint(type: .aliasName) as? String { alias=aliasHint } // Set the xcreds attributes to stamp this account as the mapped one setTimestampFor(xcredsUser ?? "") - if let iss = iss, let sub = sub { - updateOIDCInfo(xcredsUser ?? "", iss: iss, sub:sub, groups:groups) - } + let _ = updateOIDCInfo(user: xcredsUser ?? "") + if let alias = alias, let xcredsUser = xcredsUser { if XCredsCreateUser.addAlias(name: xcredsUser, alias: alias)==false { os_log("error adding alias", log: createUserLog, type: .debug) @@ -245,6 +230,114 @@ class XCredsCreateUser: XCredsBaseMechanism, DSQueryable { os_log("CreateUser mech complete", log: createUserLog, type: .debug) } + func updateOIDCInfo(user: String) -> Bool { + os_log("Checking for local username", log: noLoMechlog, type: .default) + 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: user, 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 + } + + let isLocal = records.isEmpty ? false : true + os_log("Results of local user check %{public}@", log: noLoMechlog, type: .default, isLocal.description) + + if !isLocal { + return false + } + + // now to update the attribute + TCSLogWithMark("updating claims in DS") + let claimsToDSArray = (DefaultsOverride.standardOverride.array(forKey: PrefKeys.claimsToAddToLocalUserAccount.rawValue) ?? []) as? [String] + + let tokenArray = getHint(type: .tokens) as? Array + + if let tokenArray = tokenArray , tokenArray.count>0{ + TCSLogWithMark("Found claims") + let idToken = tokenArray[0] + let idTokenInfo = jwtDecode(value: idToken) //dictionary for mapping + if let idTokenInfo = idTokenInfo { + TCSLogWithMark("Decoded Claims") + if var claimsToDSArray = claimsToDSArray { + + claimsToDSArray.append("iss") + claimsToDSArray.append("sub") + + for currClaim in claimsToDSArray { + TCSLogWithMark("Found Matching Claim: \(currClaim)") + if let value = idTokenInfo[currClaim] as? String { + let sanitizedKey = currClaim.oidc_allowed_chars + if sanitizedKey.count<20 || value.count<256 { + TCSLogWithMark("Adding \(sanitizedKey) = \(value)") + try? records.first?.setValue(value, forAttribute: "dsAttrTypeNative:_xcreds_oidc_\(sanitizedKey)") + + } + else { + TCSLogWithMark("key or value too long to put into DS") + } + + } + else if let value = idTokenInfo[currClaim] as? Array { + let sanitizedKey = currClaim.oidc_allowed_chars + let oneLine = value.joined(separator: ";") + if sanitizedKey.count<256 || oneLine.count<20 { + TCSLogWithMark("Adding \(sanitizedKey) = \(oneLine)") + + try? records.first?.setValue(oneLine, forAttribute: "dsAttrTypeNative:_xcreds_oidc_\(sanitizedKey)") + } + else { + TCSLogWithMark("key or value too long to put into DS") + } + } + } + } + } + } + +// var sub:String? +// var iss:String? +// if let oidcSubHint = getHint(type: .oidcSub) as? String { +// sub=oidcSubHint +// } +// if let oidcIssHint = getHint(type: .oidcIssuer) as? String { +// iss=oidcIssHint +// } +// +// if let oidcSubHint = getHint(type: .oidcSub) as? String { +// customAttributes["dsAttrTypeNative:\(metaPrefix)_oidc_sub"] = oidcSubHint +// } +// if let oidcIssHint = getHint(type: .oidcIssuer) as? String { +// customAttributes["dsAttrTypeNative:\(metaPrefix)_oidc_iss"] = oidcIssHint +// } + +// do { +// os_log("updating sub",log: noLoMechlog, type: .error) +// +// try records.first?.setValue(sub, forAttribute: "dsAttrTypeNative:_xcreds_oidc_sub") +// +// +// os_log("updating iss",log: noLoMechlog, type: .error) +// +// try records.first?.setValue(iss, forAttribute: "dsAttrTypeNative:_xcreds_oidc_iss") +// +// +//// if let groups = groups?.joined(separator: ";") { +//// try records.first?.setValue(groups, forAttribute: "dsAttrTypeNative:_xcreds_oidc_groups") +//// +//// } +// } catch { +// os_log("Unable to add OIDC Info", log: noLoMechlog, type: .error) +// return false +// } + + return true + + } func createHome(xcredsUser:String, uid:String) { TCSLogWithMark("Creating local homefolder for \(xcredsUser)") createHomeDirFor(xcredsUser) @@ -672,13 +765,7 @@ class XCredsCreateUser: XCredsBaseMechanism, DSQueryable { } } - fileprivate func updateOIDCInfo(_ user: String, iss:String, sub:String,groups:[String]?) { - if XCredsCreateUser.updateOIDCInfo(user:user, iss: iss, sub:sub, groups:groups) { - os_log("updateOIDCInfo updated", log: createUserLog, type: .default) - } else { - os_log("Could not add updateOIDCInfo", log: createUserLog, type: .error) - } - } + fileprivate func addSecureToken(_ username: String, _ userPass: String?,_ adminUsername: String,_ adminPassword: String?) { @@ -813,3 +900,11 @@ class XCredsCreateUser: XCredsBaseMechanism, DSQueryable { // } } +extension String { + var oidc_allowed_chars: String { + var allowed = CharacterSet() + allowed.formUnion(CharacterSet.alphanumerics) + allowed.insert(charactersIn: "_#") + return self.components(separatedBy: allowed.inverted).joined() + } +} diff --git a/xCreds.xcodeproj/project.pbxproj b/xCreds.xcodeproj/project.pbxproj index 8067bc9f..ac37a79b 100644 --- a/xCreds.xcodeproj/project.pbxproj +++ b/xCreds.xcodeproj/project.pbxproj @@ -1253,7 +1253,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 5300; + CURRENT_PROJECT_VERSION = 5310; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = UXP6YEHSPW; FRAMEWORK_SEARCH_PATHS = ( @@ -1291,7 +1291,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 5300; + CURRENT_PROJECT_VERSION = 5310; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = UXP6YEHSPW; FRAMEWORK_SEARCH_PATHS = ( @@ -1411,7 +1411,7 @@ CODE_SIGN_ENTITLEMENTS = "XCreds Login Overlay/XCreds_Login_Overlay.entitlements"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 5300; + CURRENT_PROJECT_VERSION = 5310; DEVELOPMENT_TEAM = UXP6YEHSPW; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -1448,7 +1448,7 @@ CODE_SIGN_ENTITLEMENTS = "XCreds Login Overlay/XCreds_Login_Overlay.entitlements"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 5300; + CURRENT_PROJECT_VERSION = 5310; DEVELOPMENT_TEAM = UXP6YEHSPW; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -1598,7 +1598,7 @@ CODE_SIGN_ENTITLEMENTS = XCreds/xCreds.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 5300; + CURRENT_PROJECT_VERSION = 5310; DEVELOPMENT_TEAM = UXP6YEHSPW; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -1639,7 +1639,7 @@ CODE_SIGN_ENTITLEMENTS = XCreds/xCreds.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 5300; + CURRENT_PROJECT_VERSION = 5310; DEVELOPMENT_TEAM = UXP6YEHSPW; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( diff --git a/xCreds.xcodeproj/project.xcworkspace/xcuserdata/tperfitt.xcuserdatad/UserInterfaceState.xcuserstate b/xCreds.xcodeproj/project.xcworkspace/xcuserdata/tperfitt.xcuserdatad/UserInterfaceState.xcuserstate index e50b261a..e7044449 100644 Binary files a/xCreds.xcodeproj/project.xcworkspace/xcuserdata/tperfitt.xcuserdatad/UserInterfaceState.xcuserstate and b/xCreds.xcodeproj/project.xcworkspace/xcuserdata/tperfitt.xcuserdatad/UserInterfaceState.xcuserstate differ