diff --git a/README.md b/README.md
index 39221e2..bc3df2a 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,8 @@ Access Preferences from the menu bar icon. Here you'll be able to set the follo
- Whether the alert window is displayed at every polling interval or only when the status changes.
- How the menubar icon is displayed. Minimizing will place a thin transparent icon in the menubar.
- Use of a LaunchAgent, to automatically start the app when logging in.
-- Information for your specific Jamf Cloud instance. The account used only needs to be able to authenticate, no need to assign permissions. If your cloud server does not utilize the HTTPS port 443 be sure to include the port you use in the URL.
+- Information for your specific Jamf Cloud instance. Use either a local user account or API client.
+- Most notification can be viewed using an account with no permissions set in Jamf Pro. Using an account with ready-only on all objects ensure you'll see all notifications. If your cloud server does not utilize the standard HTTPS port (443) be sure to include the port you use in the URL.
@@ -119,7 +120,9 @@ Thu Sep 17 20:27:30 Jamf Cloud: All systems go.
## Change log
-2022-10-02: v2.3.6 - Update logging to prevent potential looping.
+2023-11-11: v2.4.0 - Fix issue with notifications not being displayed. Add ability to use API client.
+
+2023-04-07: v2.3.6 - Update logging to prevent potential looping.
2022-10-02: v2.3.2 - Rework authentication/token refresh.
diff --git a/jamfStatus.xcodeproj/project.pbxproj b/jamfStatus.xcodeproj/project.pbxproj
index e297f8e..d1b7481 100644
--- a/jamfStatus.xcodeproj/project.pbxproj
+++ b/jamfStatus.xcodeproj/project.pbxproj
@@ -14,13 +14,13 @@
B51596A11E963D2D00ED3CA3 /* StatusMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51596A01E963D2D00ED3CA3 /* StatusMenuController.swift */; };
B542A1F124BA902C00EE33C8 /* Globals.swift in Sources */ = {isa = PBXBuildFile; fileRef = B542A1F024BA902C00EE33C8 /* Globals.swift */; };
B542A1F324BA90AA00EE33C8 /* WriteToLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = B542A1F224BA90A900EE33C8 /* WriteToLog.swift */; };
- B59970D327FCBB4C005175E2 /* JamfPro.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59970D227FCBB4C005175E2 /* JamfPro.swift */; };
B5A470B02271CE7200C2A88D /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5A470AF2271CE7200C2A88D /* WebKit.framework */; };
B5AC20321F36479A00C16EAA /* com.jamf.cloudmonitor.plist in Resources */ = {isa = PBXBuildFile; fileRef = B5AC20311F36479A00C16EAA /* com.jamf.cloudmonitor.plist */; };
B5AC20351F37858800C16EAA /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = B5AC20341F37858800C16EAA /* index.html */; };
B5AC20371F37859F00C16EAA /* images in Resources */ = {isa = PBXBuildFile; fileRef = B5AC20361F37859F00C16EAA /* images */; };
- B5BD6AD723297762001D244A /* Credentials2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BD6AD623297762001D244A /* Credentials2.swift */; };
+ B5BD6AD723297762001D244A /* Credentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BD6AD623297762001D244A /* Credentials.swift */; };
B5BD6AD9232F108A001D244A /* VersionCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BD6AD8232F108A001D244A /* VersionCheck.swift */; };
+ B5BE0F9E2AFEF434000CBEBE /* TokenDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BE0F9D2AFEF434000CBEBE /* TokenDelegate.swift */; };
B5E62F32231CAB640012FF5A /* UapiCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E62F31231CAB640012FF5A /* UapiCall.swift */; };
B5E62F34231DA09F0012FF5A /* NotificationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E62F33231DA09F0012FF5A /* NotificationAlert.swift */; };
B5FD9DAC2742AAC60044C321 /* Misc in Resources */ = {isa = PBXBuildFile; fileRef = B5FD9DAB2742AAC60044C321 /* Misc */; };
@@ -36,14 +36,14 @@
B51596A01E963D2D00ED3CA3 /* StatusMenuController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusMenuController.swift; sourceTree = ""; };
B542A1F024BA902C00EE33C8 /* Globals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Globals.swift; sourceTree = ""; };
B542A1F224BA90A900EE33C8 /* WriteToLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WriteToLog.swift; sourceTree = ""; };
- B59970D227FCBB4C005175E2 /* JamfPro.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JamfPro.swift; sourceTree = ""; };
B5A46CCE226FB1E300C2A88D /* jamfStatus.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = jamfStatus.entitlements; sourceTree = ""; };
B5A470AF2271CE7200C2A88D /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
B5AC20311F36479A00C16EAA /* com.jamf.cloudmonitor.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.jamf.cloudmonitor.plist; sourceTree = ""; };
B5AC20341F37858800C16EAA /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = ""; };
B5AC20361F37859F00C16EAA /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; path = images; sourceTree = ""; };
- B5BD6AD623297762001D244A /* Credentials2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials2.swift; sourceTree = ""; };
+ B5BD6AD623297762001D244A /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = ""; };
B5BD6AD8232F108A001D244A /* VersionCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionCheck.swift; sourceTree = ""; };
+ B5BE0F9D2AFEF434000CBEBE /* TokenDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenDelegate.swift; sourceTree = ""; };
B5E62F31231CAB640012FF5A /* UapiCall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UapiCall.swift; sourceTree = ""; };
B5E62F33231DA09F0012FF5A /* NotificationAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationAlert.swift; sourceTree = ""; };
B5FD9DAB2742AAC60044C321 /* Misc */ = {isa = PBXFileReference; lastKnownFileType = text; path = Misc; sourceTree = ""; };
@@ -85,11 +85,11 @@
B5A46CCE226FB1E300C2A88D /* jamfStatus.entitlements */,
B5AC20331F37855800C16EAA /* about */,
B5013BC61E95817C0099300A /* AppDelegate.swift */,
- B5BD6AD623297762001D244A /* Credentials2.swift */,
+ B5BD6AD623297762001D244A /* Credentials.swift */,
B542A1F024BA902C00EE33C8 /* Globals.swift */,
- B59970D227FCBB4C005175E2 /* JamfPro.swift */,
B5E62F33231DA09F0012FF5A /* NotificationAlert.swift */,
B51596A01E963D2D00ED3CA3 /* StatusMenuController.swift */,
+ B5BE0F9D2AFEF434000CBEBE /* TokenDelegate.swift */,
B5E62F31231CAB640012FF5A /* UapiCall.swift */,
B5BD6AD8232F108A001D244A /* VersionCheck.swift */,
B542A1F224BA90A900EE33C8 /* WriteToLog.swift */,
@@ -160,7 +160,7 @@
TargetAttributes = {
B5013BC21E95817C0099300A = {
CreatedOnToolsVersion = 8.2.1;
- DevelopmentTeam = PS2F6S478M;
+ DevelopmentTeam = T82LNZG37Z;
LastSwiftMigration = 1020;
ProvisioningStyle = Automatic;
SystemCapabilities = {
@@ -218,10 +218,10 @@
B542A1F324BA90AA00EE33C8 /* WriteToLog.swift in Sources */,
B5E62F32231CAB640012FF5A /* UapiCall.swift in Sources */,
B51596A11E963D2D00ED3CA3 /* StatusMenuController.swift in Sources */,
- B5BD6AD723297762001D244A /* Credentials2.swift in Sources */,
+ B5BD6AD723297762001D244A /* Credentials.swift in Sources */,
B542A1F124BA902C00EE33C8 /* Globals.swift in Sources */,
+ B5BE0F9E2AFEF434000CBEBE /* TokenDelegate.swift in Sources */,
B5013BC71E95817C0099300A /* AppDelegate.swift in Sources */,
- B59970D327FCBB4C005175E2 /* JamfPro.swift in Sources */,
B5BD6AD9232F108A001D244A /* VersionCheck.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -358,11 +358,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = jamfStatus/jamfStatus.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
- DEVELOPMENT_TEAM = "";
+ DEVELOPMENT_TEAM = T82LNZG37Z;
ENABLE_HARDENED_RUNTIME = YES;
EXCLUDED_SOURCE_FILE_NAMES = Notes;
INFOPLIST_FILE = jamfStatus/Info.plist;
@@ -376,7 +377,7 @@
"$(SDKROOT)/usr/lib/system/introspection",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
- MARKETING_VERSION = 2.3.6;
+ MARKETING_VERSION = 2.4.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jamf.pse.jamfStatus;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -390,11 +391,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = jamfStatus/jamfStatus.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
- DEVELOPMENT_TEAM = "";
+ DEVELOPMENT_TEAM = T82LNZG37Z;
ENABLE_HARDENED_RUNTIME = YES;
EXCLUDED_SOURCE_FILE_NAMES = Notes;
INFOPLIST_FILE = jamfStatus/Info.plist;
@@ -408,7 +410,7 @@
"$(SDKROOT)/usr/lib/system/introspection",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
- MARKETING_VERSION = 2.3.6;
+ MARKETING_VERSION = 2.4.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jamf.pse.jamfStatus;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/jamfStatus.xcodeproj/project.xcworkspace/xcuserdata/lesliehelou.xcuserdatad/UserInterfaceState.xcuserstate b/jamfStatus.xcodeproj/project.xcworkspace/xcuserdata/lesliehelou.xcuserdatad/UserInterfaceState.xcuserstate
index 49d4918..e3e77dc 100644
Binary files a/jamfStatus.xcodeproj/project.xcworkspace/xcuserdata/lesliehelou.xcuserdatad/UserInterfaceState.xcuserstate and b/jamfStatus.xcodeproj/project.xcworkspace/xcuserdata/lesliehelou.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/jamfStatus.xcodeproj/xcuserdata/lesliehelou.xcuserdatad/xcschemes/jamfStatus.xcscheme b/jamfStatus.xcodeproj/xcuserdata/lesliehelou.xcuserdatad/xcschemes/jamfStatus.xcscheme
index 7705c3d..a41978f 100644
--- a/jamfStatus.xcodeproj/xcuserdata/lesliehelou.xcuserdatad/xcschemes/jamfStatus.xcscheme
+++ b/jamfStatus.xcodeproj/xcuserdata/lesliehelou.xcuserdatad/xcschemes/jamfStatus.xcscheme
@@ -1,7 +1,7 @@
+ version = "1.7">
@@ -82,6 +82,24 @@
+ revealArchiveInOrganizer = "NO">
+
+
+
+
+
+
+
+
+
+
diff --git a/jamfStatus/AppDelegate.swift b/jamfStatus/AppDelegate.swift
index e638813..aecb7d2 100644
--- a/jamfStatus/AppDelegate.swift
+++ b/jamfStatus/AppDelegate.swift
@@ -13,7 +13,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
@IBOutlet weak var cloudStatusWindow: NSWindow!
@IBOutlet var page_WebView: WKWebView!
- @IBOutlet weak var prefs_Panel: NSPanel!
+ @IBOutlet weak var prefs_Window: NSWindow!
@IBOutlet weak var aboutVersion_TextField: NSTextField!
@IBOutlet weak var aboutHomeUrl_Button: NSButton!
@@ -25,8 +25,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
// site specific settings
@IBOutlet weak var jamfServerUrl_TextField: NSTextField!
+ @IBOutlet weak var username_Label: NSTextField!
+ @IBOutlet weak var password_Label: NSTextField!
@IBOutlet weak var username_TextField: NSTextField!
@IBOutlet weak var password_TextField: NSSecureTextField!
+ @IBOutlet weak var useApiClient_button: NSButton!
+ @IBAction func useApiClient_action(_ sender: NSButton) {
+ setLabels()
+ useApiClient = useApiClient_button.state.rawValue
+ defaults.set(useApiClient_button.state.rawValue, forKey: "useApiClient")
+ fetchPassword()
+ }
@IBOutlet weak var siteConnectionStatus_ImageView: NSImageView!
let statusImage:[NSImage] = [NSImage(named: "red-dot")!,
@@ -45,9 +54,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
var hideIcon: Bool = false
let launchAgentPath = NSHomeDirectory()+"/Library/LaunchAgents/com.jamf.cloudmonitor.plist"
- // let popover = NSPopover()
-
-
@objc func notificationsAction(_ sender: NSMenuItem) {
// print("\(sender.identifier!.rawValue)")
// WriteToLog().message(stringOfText: ["\(sender.identifier!.rawValue)"])
@@ -66,7 +72,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
@IBAction func showAbout_MenuItem(_ sender: NSMenuItem) {
-
let appInfo = Bundle.main.infoDictionary!
let version = appInfo["CFBundleShortVersionString"] as! String
@@ -85,7 +90,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
aboutHomeUrl_Button.attributedTitle = attributedTitle
aboutHomeUrl_Button.toolTip = "https://github.com/jamf/jamfStatus"
- showOnActiveScreen(windowName: about_NSWindow, panelName: prefs_Panel, type: "window")
+ showOnActiveScreen(windowName: about_NSWindow)
}
@IBAction func aboutHomeUrl_Button(_ sender: NSButton) {
@@ -93,7 +98,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
}
@IBAction func checkForUpdates(_ sender: AnyObject) {
-// let verCheck = VersionCheck()
let appInfo = Bundle.main.infoDictionary!
let version = appInfo["CFBundleShortVersionString"] as! String
@@ -119,9 +123,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
}
self.cloudStatusWindow.titleVisibility = .hidden
- self.showOnActiveScreen(windowName: self.cloudStatusWindow, panelName: self.prefs_Panel, type: "window")
-// NSApplication.shared.activate(ignoringOtherApps: true)
-// self.cloudStatusWindow.setIsVisible(true)
+ self.showOnActiveScreen(windowName: self.cloudStatusWindow)
}
}
@@ -140,7 +142,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
pollingInterval_TextField.stringValue = "300"
}
defaults.set(prefs.pollingInterval, forKey: "pollingInterval")
- defaults.synchronize()
+// defaults.synchronize()
prefs.pollingInterval = defaults.object(forKey: "pollingInterval") as? Int
}
@IBAction func prefWindowAlerts_Action(_ sender: NSButton) {
@@ -150,18 +152,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
// }
prefs.hideUntilStatusChange = (prefWindowAlerts_Button.state.rawValue == 0 ? false:true)
defaults.set(prefs.hideUntilStatusChange, forKey: "hideUntilStatusChange")
- defaults.synchronize()
+// defaults.synchronize()
}
@IBAction func hideMenubarIcon_Action(_ sender: NSButton) {
prefs.hideMenubarIcon = (prefWindowIcon_Button.state.rawValue == 0 ? false:true)
defaults.set(prefs.hideMenubarIcon, forKey: "hideMenubarIcon")
- defaults.synchronize()
+// defaults.synchronize()
}
@IBAction func launchAgent_Action(_ sender: NSButton) {
var isDir: ObjCBool = true
prefs.launchAgent = (launchAgent_Button.state.rawValue == 0 ? false:true)
defaults.set(prefs.launchAgent, forKey: "launchAgent")
- defaults.synchronize()
+// defaults.synchronize()
if launchAgent_Button.state.rawValue == 0 {
if fm.fileExists(atPath: launchAgentPath) {
do {
@@ -189,28 +191,43 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
}
@IBAction func credentials_Action(_ sender: Any) {
+ JamfProServer.url = jamfServerUrl_TextField.stringValue
- if let _ = sender as? NSTextField {
-// print("textField: \(textField.identifier!.rawValue)")
- getJamfProVersion(jpURL: jamfServerUrl_TextField.stringValue) {
- (result: [Int]) in
-// print("jamfProVersion: \(result[0]).\(result[1]).\(result[2])")
- }
- }
-
- prefs.jamfServerUrl = jamfServerUrl_TextField.stringValue
- defaults.set(prefs.jamfServerUrl, forKey: "jamfServerUrl")
- defaults.synchronize()
+ let urlRegex = try! NSRegularExpression(pattern: "/?failover(.*?)", options:.caseInsensitive)
+ JamfProServer.url = urlRegex.stringByReplacingMatches(in: JamfProServer.url, options: [], range: NSRange(0.. Void) {
- WriteToLog().message(stringOfText: ["getting Jamf Pro Version for \(jpURL)"])
- var versionString = ""
- var versionArray = [Int]()
- let semaphore = DispatchSemaphore(value: 0)
-
- OperationQueue().addOperation {
- let encodedURL = NSURL(string: "\(jpURL)/JSSCheckConnection")
- let request = NSMutableURLRequest(url: encodedURL! as URL)
- request.httpMethod = "GET"
- let configuration = URLSessionConfiguration.default
- let session = Foundation.URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
- let task = session.dataTask(with: request as URLRequest, completionHandler: {
- (data, response, error) -> Void in
- if let httpResponse = response as? HTTPURLResponse {
-// print("httpResponse: \(httpResponse)")
-// print("raw versionString: \(versionString)")
- versionString = String(data: data!, encoding: .utf8) ?? ""
-
- if versionString != "" {
- let tmpArray = versionString.components(separatedBy: ".")
- if tmpArray.count > 2 {
- for i in 0...2 {
- switch i {
- case 2:
- let tmp = tmpArray[i].components(separatedBy: "-")
- versionArray.append(Int(tmp[0]) ?? 0)
- default:
- versionArray.append(Int(tmpArray[i]) ?? 0)
- }
- }
- }
- }
- } else {
- versionArray = []
- var theError = error?.localizedDescription
- if theError == nil { theError = "unknown" }
- WriteToLog().message(stringOfText: ["error: \(theError!)"])
- }
- completion(versionArray)
- }) // let task = session - end
- task.resume()
- semaphore.wait()
- }
- }
-
+
func saveCreds(server: String, username: String, password: String) {
if ( server != "" && username != "" && password != "" ) {
@@ -298,14 +269,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
JamfProServer.base64Creds = ("\(username):\(password)".data(using: .utf8)?.base64EncodedString())!
token.isValid = false
// update the connection indicator for the site server
- JamfPro().getToken(serverUrl: server, whichServer: "source", base64creds: JamfProServer.base64Creds) {
- (returnedToken: String) in
- if returnedToken != "failed" {
+ TokenDelegate().getToken(serverUrl: server, base64creds: JamfProServer.base64Creds) {
+ (authResult: (Int,String)) in
+
+ if authResult.1 == "success" {
// print("authentication verified")
DispatchQueue.main.async {
self.siteConnectionStatus_ImageView.image = self.statusImage[1]
}
- Credentials2().save(service: "jamfStatus: \(serverFqdn)", account: username, data: password)
+ Credentials().save(service: server.fqdnFromUrl, account: username, data: password)
} else {
print("authentication failed")
DispatchQueue.main.async {
@@ -322,6 +294,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
pollingInterval_TextField.stringValue = "\(String(describing: defaults.object(forKey:"pollingInterval")!))"
prefWindowAlerts_Button.state = NSControl.StateValue.on
+
+ useApiClient_button.state = NSControl.StateValue(rawValue: useApiClient)
if (defaults.bool(forKey: "hideMenubarIcon")) {
prefWindowIcon_Button.state = NSControl.StateValue.on
@@ -344,69 +318,44 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDelegate {
let serverUrl = defaults.string(forKey:"jamfServerUrl") ?? ""
if serverUrl != "" {
jamfServerUrl_TextField.stringValue = serverUrl
- let urlRegex = try! NSRegularExpression(pattern: "http(.*?)://", options:.caseInsensitive)
- let serverFqdn = urlRegex.stringByReplacingMatches(in: serverUrl, options: [], range: NSRange(0..
-
+
-
-
+
+
@@ -26,12 +26,15 @@
+
-
+
+
+
@@ -56,12 +59,20 @@
+
+
+
+
-
diff --git a/jamfStatus/Credentials.swift b/jamfStatus/Credentials.swift
index 47a5c3a..0cbb53d 100644
--- a/jamfStatus/Credentials.swift
+++ b/jamfStatus/Credentials.swift
@@ -1,5 +1,5 @@
//
-// Credentials2.swift
+// Credentials.swift
// jamfStatus
//
// Created by Leslie Helou on 9/11/19.
@@ -18,27 +18,38 @@ class Credentials {
func save(service: String, account: String, data: String) {
+ let theService = (useApiClient == 0) ? "jamfStatus: \(service)":"jamfStatus-apiClient: \(service)"
+
keychainQ.async { [self] in
if let password = data.data(using: String.Encoding.utf8) {
var keychainQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
- kSecAttrService as String: service,
+ kSecAttrService as String: theService,
kSecAttrAccount as String: account,
kSecValueData as String: password]
// see if credentials already exist for server
- let accountCheck = retrieve(service: service)
+ let accountCheck = itemLookup(service: service)
if accountCheck.count == 0 {
// try to add new credentials, if account exists we'll try updating it
let addStatus = SecItemAdd(keychainQuery as CFDictionary, nil)
if (addStatus != errSecSuccess) {
if let addErr = SecCopyErrorMessageString(addStatus, nil) {
print("[addStatus] Write failed for new credentials: \(addErr)")
+ let deleteStatus = SecItemDelete(keychainQuery as CFDictionary)
+ print("[Credentials.save] the deleteStatus: \(deleteStatus)")
+ sleep(1)
+ let addStatus = SecItemAdd(keychainQuery as CFDictionary, nil)
+ if (addStatus != errSecSuccess) {
+ if let addErr = SecCopyErrorMessageString(addStatus, nil) {
+ print("[addStatus] Write failed for new credentials after deleting: \(addErr)")
+ }
+ }
}
}
} else {
// credentials already exist, try to update
keychainQuery = [kSecClass as String: kSecClassGenericPasswordString,
- kSecAttrService as String: service,
+ kSecAttrService as String: theService,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true]
let updateStatus = SecItemUpdate(keychainQuery as CFDictionary, [kSecAttrAccountString:account,kSecValueDataString:password] as CFDictionary)
@@ -52,12 +63,28 @@ class Credentials {
}
} // func save - end
+ func itemLookup(service: String) -> [String] {
+ var existingCredientials = retrieve(service: service)
+ if existingCredientials.count == 0 {
+ existingCredientials = retrieve(service: "\(service)/")
+ if existingCredientials.count == 0 {
+ existingCredientials = retrieve(service: "\(service):8443")
+ if existingCredientials.count == 0 {
+ existingCredientials = retrieve(service: "\(service):8443/")
+ }
+ }
+ }
+ return existingCredientials
+ }
+
func retrieve(service: String) -> [String] {
var storedCreds = [String]()
+ let theService = (useApiClient == 0) ? "jamfStatus: \(service)":"jamfStatus-apiClient: \(service)"
+
let keychainQuery: [String: Any] = [kSecClass as String: kSecClassGenericPasswordString,
- kSecAttrService as String: service,
+ kSecAttrService as String: theService,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true,
kSecReturnData as String: true]
@@ -70,9 +97,10 @@ class Credentials {
let passwordData = existingItem[kSecValueData as String] as? Data,
let account = existingItem[kSecAttrAccount as String] as? String,
let password = String(data: passwordData, encoding: String.Encoding.utf8)
- else {
- return []
+ else {
+ return []
}
+
storedCreds.append(account)
storedCreds.append(password)
return storedCreds
diff --git a/jamfStatus/Globals.swift b/jamfStatus/Globals.swift
index 69b1d20..8a4a67d 100644
--- a/jamfStatus/Globals.swift
+++ b/jamfStatus/Globals.swift
@@ -8,13 +8,17 @@
import Foundation
-struct appInfo {
+let httpSuccess = 200...299
+let refreshInterval: UInt32 = 25*60 // 25 minutes
+var useApiClient = 0
+
+struct AppInfo {
static let dict = Bundle.main.infoDictionary!
static let version = dict["CFBundleShortVersionString"] as! String
static let build = dict["CFBundleVersion"] as! String
static let name = dict["CFBundleExecutable"] as! String
- static let userAgentHeader = "\(String(describing: name.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!))/\(appInfo.version)"
+ static let userAgentHeader = "\(String(describing: name.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!))/\(AppInfo.version)"
static var bundlePath = Bundle.main.bundleURL
static var iconFile = bundlePath.appendingPathComponent("/Resources/AppIcon.icns")
@@ -136,14 +140,23 @@ struct JamfNotification {
}
struct JamfProServer {
+ static var accessToken = ""
+ static var authCreds = ""
+ static var authExpires = 30.0
+ static var authType = "Basic"
+ static var base64Creds = ""
+ static var build = ""
+ static var currentCred = ""
static let maxThreads = 2
static var majorVersion = 0
static var minorVersion = 0
+ static var password = ""
static var patchVersion = 0
- static var build = ""
- static var authType = "Basic"
- static var authCreds = ""
- static var base64Creds = ""
+ static var tokenCreated = Date()
+ static var url = ""
+ static var username = ""
+ static var validToken = false
+ static var version = ""
}
struct Log {
@@ -173,3 +186,41 @@ struct token {
static var startTime = Date()
static var isValid = false
}
+
+public func timeDiff(startTime: Date) -> (Int, Int, Int, Double) {
+ let endTime = Date()
+// let components = Calendar.current.dateComponents([.second, .nanosecond], from: startTime, to: endTime)
+// let timeDifference = Double(components.second!) + Double(components.nanosecond!)/1000000000
+// WriteToLog().message(stringOfText: "[ViewController.download] time difference: \(timeDifference) seconds")
+ let components = Calendar.current.dateComponents([
+ .hour, .minute, .second, .nanosecond], from: startTime, to: endTime)
+ var diffInSeconds = Double(components.hour!)*3600 + Double(components.minute!)*60 + Double(components.second!) + Double(components.nanosecond!)/1000000000
+ diffInSeconds = Double(round(diffInSeconds * 1000) / 1000)
+// let timeDifference = Int(components.second!) //+ Double(components.nanosecond!)/1000000000
+// let (h,r) = timeDifference.quotientAndRemainder(dividingBy: 3600)
+// let (m,s) = r.quotientAndRemainder(dividingBy: 60)
+// WriteToLog().message(stringOfText: "[ViewController.download] download time: \(h):\(m):\(s) (h:m:s)")
+ return (Int(components.hour!), Int(components.minute!), Int(components.second!), diffInSeconds)
+// return (h, m, s)
+}
+
+extension String {
+ var fqdnFromUrl: String {
+ get {
+ var fqdn = ""
+ let nameArray = self.components(separatedBy: "/")
+ if nameArray.count > 2 {
+ fqdn = nameArray[2]
+ } else {
+ fqdn = self
+ }
+ if fqdn.contains(":") {
+ let fqdnArray = fqdn.components(separatedBy: ":")
+ fqdn = fqdnArray[0]
+ }
+ let urlRegex = try! NSRegularExpression(pattern: "/?failover(.*?)", options:.caseInsensitive)
+ fqdn = urlRegex.stringByReplacingMatches(in: fqdn, options: [], range: NSRange(0..]) in
+ UapiCall().get(endpoint: "v1/notifications") { [self]
+ (notificationAlerts: [[String: Any]]) in
if notificationAlerts.count == 0 {
notifications_MenuItem.isHidden = true
@@ -401,22 +402,3 @@ class StatusMenuController: NSObject, URLSessionDelegate, URLSessionTaskDelegate
}
}
-
-extension String {
- var fqdnFromUrl: String {
- get {
- var fqdn = ""
- let nameArray = self.components(separatedBy: "/")
- if nameArray.count > 1 {
- fqdn = nameArray[2]
- } else {
- fqdn = self
- }
- if fqdn.contains(":") {
- let fqdnArray = fqdn.components(separatedBy: ":")
- fqdn = fqdnArray[0]
- }
- return fqdn
- }
- }
-}
diff --git a/jamfStatus/TokenDelegate.swift b/jamfStatus/TokenDelegate.swift
index cb44f87..7190928 100644
--- a/jamfStatus/TokenDelegate.swift
+++ b/jamfStatus/TokenDelegate.swift
@@ -1,6 +1,6 @@
//
// TokenDelegate.swift
-// SYM-Helper
+// jamfStatus
//
import Cocoa
@@ -9,29 +9,11 @@ class TokenDelegate: NSObject, URLSessionDelegate {
let userDefaults = UserDefaults.standard
var components = DateComponents()
- var renewQ = DispatchQueue(label: "com.token_refreshQ", qos: DispatchQoS.background) // running background process for refreshing token
- func getToken(serverUrl: String, whichServer: String = "source", base64creds: String, completion: @escaping (_ authResult: (Int,String)) -> Void) {
-
-
-// writeToLog.message(stringOfText: "[getToken] token for \(whichServer) server: \(serverUrl)")
-// print("[getToken] JamfProServer.username[\(whichServer)]: \(String(describing: JamfProServer.username[whichServer]))")
-// print("[getToken] JamfProServer.password[\(whichServer)]: \(String(describing: JamfProServer.password[whichServer]?.prefix(1)))")
-// print("[getToken] JamfProServer.server[\(whichServer)]: \(String(describing: JamfProServer.source))")
-// print("[getToken] JamfProServer.server[\(whichServer)]: \(String(describing: JamfProServer.url[whichServer]))")
-
-// JamfProServer.url[whichServer] = serverUrl
+ func getToken(serverUrl: String, base64creds: String, completion: @escaping (_ authResult: (Int,String)) -> Void) {
URLCache.shared.removeAllCachedResponses()
-// var tokenUrlString = "\(serverUrl)/api/v1/auth/token"
-
-// var apiClient = ( userDefaults.integer(forKey: "\(whichServer)UseApiClient") == 1 ) ? true:false
-//
-// if apiClient {
-// tokenUrlString = "\(serverUrl)/api/oauth/token"
-// }
-
var tokenUrlString = "\(serverUrl)/api/v1/auth/token"
var apiClient = false
@@ -46,7 +28,7 @@ class TokenDelegate: NSObject, URLSessionDelegate {
let tokenUrl = URL(string: "\(tokenUrlString)")
guard let _ = URL(string: "\(tokenUrlString)") else {
print("problem constructing the URL from \(tokenUrlString)")
- writeToLog.message(stringOfText: "[getToken] problem constructing the URL from \(tokenUrlString)")
+ writeToLog.message(stringOfText: ["[getToken] problem constructing the URL from \(tokenUrlString)"])
completion((500, "failed"))
return
}
@@ -57,14 +39,9 @@ class TokenDelegate: NSObject, URLSessionDelegate {
let (_, _, _, tokenAgeInSeconds) = timeDiff(startTime: JamfProServer.tokenCreated)
- // print("[getToken] JamfProServer.validToken[\(whichServer)]: \(String(describing: JamfProServer.validToken[whichServer]))")
- // print("[getToken] \(whichServer) tokenAgeInSeconds: \(tokenAgeInSeconds)")
- // print("[getToken] \(whichServer) token exipres in: \((JamfProServer.authExpires[whichServer] ?? 30)*60)")
- // print("[getToken] JamfProServer.currentCred[\(whichServer)]: \(String(describing: JamfProServer.currentCred[whichServer]))")
-
if !( JamfProServer.validToken && tokenAgeInSeconds < (JamfProServer.authExpires)*60 ) || (JamfProServer.currentCred != base64creds) {
- writeToLog.message(stringOfText: "[getToken] \(whichServer) tokenAgeInSeconds: \(tokenAgeInSeconds)")
- writeToLog.message(stringOfText: "[getToken] Attempting to retrieve token from \(String(describing: tokenUrl))")
+ writeToLog.message(stringOfText: ["[getToken] tokenAgeInSeconds: \(tokenAgeInSeconds)"])
+ writeToLog.message(stringOfText: ["[getToken] Attempting to retrieve token from \(String(describing: tokenUrl))"])
if apiClient {
let clientId = JamfProServer.username
@@ -80,11 +57,6 @@ class TokenDelegate: NSObject, URLSessionDelegate {
configuration.httpAdditionalHeaders = ["Authorization" : "Basic \(base64creds)", "Content-Type" : "application/json", "Accept" : "application/json", "User-Agent" : AppInfo.userAgentHeader]
JamfProServer.currentCred = base64creds
}
- print("[getToken] tokenUrlString: \(tokenUrlString)")
- print("[getToken] configuration.httpAdditionalHeaders: \(String(describing: configuration.httpAdditionalHeaders))")
-
-// print("[getToken] \(whichServer) tokenUrlString: \(tokenUrlString)")
-// print("[getToken] \(whichServer) base64creds: \(base64creds)")
let session = Foundation.URLSession(configuration: configuration, delegate: self as URLSessionDelegate, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request as URLRequest, completionHandler: { [self]
@@ -107,7 +79,7 @@ class TokenDelegate: NSObject, URLSessionDelegate {
JamfProServer.authType = "Bearer"
// print("[JamfPro] result of token request: \(endpointJSON)")
- writeToLog.message(stringOfText: "[getToken] new token created for \(serverUrl)")
+ writeToLog.message(stringOfText: ["[getToken] new token created for \(serverUrl)"])
if JamfProServer.version == "" {
// get Jamf Pro version - start
@@ -116,7 +88,7 @@ class TokenDelegate: NSObject, URLSessionDelegate {
let versionString = result["version"] as! String
if versionString != "" {
- writeToLog.message(stringOfText: "[JamfPro.getVersion] Jamf Pro Version: \(versionString)")
+ writeToLog.message(stringOfText: ["[JamfPro.getVersion] Jamf Pro Version: \(versionString)"])
JamfProServer.version = versionString
let tmpArray = versionString.components(separatedBy: ".")
if tmpArray.count > 2 {
@@ -138,12 +110,12 @@ class TokenDelegate: NSObject, URLSessionDelegate {
}
if ( JamfProServer.majorVersion > 10 || (JamfProServer.majorVersion > 9 && JamfProServer.minorVersion > 34) ) {
JamfProServer.authType = "Bearer"
- writeToLog.message(stringOfText: "[JamfPro.getVersion] \(serverUrl) set to use OAuth")
+ writeToLog.message(stringOfText: ["[JamfPro.getVersion] \(serverUrl) set to use OAuth"])
} else {
JamfProServer.authType = "Basic"
JamfProServer.accessToken = base64creds
- writeToLog.message(stringOfText: "[JamfPro.getVersion] \(serverUrl) set to use Basic")
+ writeToLog.message(stringOfText: ["[JamfPro.getVersion] \(serverUrl) set to use Basic"])
}
completion((200, "success"))
return
@@ -156,29 +128,26 @@ class TokenDelegate: NSObject, URLSessionDelegate {
return
}
} else { // if let endpointJSON error
- writeToLog.message(stringOfText: "[getToken] JSON error.\n\(String(describing: json))")
+ writeToLog.message(stringOfText: ["[getToken] JSON error.\n\(String(describing: json))"])
JamfProServer.validToken = false
completion((httpResponse.statusCode, "failed"))
return
}
} else {
// server down?
- _ = Alert().display(header: "", message: "Failed to get an expected response from \(String(describing: serverUrl)).", secondButton: "")
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] Failed to get an expected response from \(String(describing: serverUrl)). Status Code: \(httpResponse.statusCode)")
+ writeToLog.message(stringOfText: ["[TokenDelegate.getToken] Failed to get an expected response from \(String(describing: serverUrl)). Status Code: \(httpResponse.statusCode)"])
JamfProServer.validToken = false
completion((httpResponse.statusCode, "failed"))
return
}
} else { // if httpResponse.statusCode <200 or >299
- _ = Alert().display(header: "\(serverUrl)", message: "Failed to authenticate to \(serverUrl). \nStatus Code: \(httpResponse.statusCode)", secondButton: "")
- writeToLog.message(stringOfText: "[getToken] Failed to authenticate to \(serverUrl). Response error: \(httpResponse.statusCode)")
+ writeToLog.message(stringOfText: ["[getToken] Failed to authenticate to \(serverUrl). Response error: \(httpResponse.statusCode)"])
JamfProServer.validToken = false
completion((httpResponse.statusCode, "failed"))
return
}
} else {
- _ = Alert().display(header: "\(serverUrl)", message: "Failed to connect. \nUnknown error, verify url and port.", secondButton: "")
- writeToLog.message(stringOfText: "[getToken] token response error from \(serverUrl). Verify url and port")
+ writeToLog.message(stringOfText: ["[getToken] token response error from \(serverUrl). Verify url and port"])
JamfProServer.validToken = false
completion((0, "failed"))
return
@@ -191,197 +160,6 @@ class TokenDelegate: NSObject, URLSessionDelegate {
return
}
}
- /*
- func getToken(whichServer: String, serverUrl: String, base64creds: String, completion: @escaping (_ authResult: (Int,String)) -> Void) {
- let forceBasicAuth = (defaults.integer(forKey: "forceBasicAuth") == 1) ? true:false
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] Force basic authentication on \(serverUrl): \(forceBasicAuth)")
-
- URLCache.shared.removeAllCachedResponses()
-
- var tokenUrlString = "\(serverUrl)/api/v1/auth/token"
-
- var apiClient = false
- if useApiClient == 1 {
- tokenUrlString = "\(serverUrl)/api/oauth/token"
- apiClient = true
- }
-
- tokenUrlString = tokenUrlString.replacingOccurrences(of: "//api", with: "/api")
-// print("[TokenDelegate] tokenUrlString: \(tokenUrlString)")
-
- let tokenUrl = URL(string: "\(tokenUrlString)")
- guard let _ = tokenUrl else {
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] Problem constructing the URL from \(tokenUrlString)")
- completion((500, "failed"))
- return
- }
-
- let configuration = URLSessionConfiguration.ephemeral
- var request = URLRequest(url: tokenUrl!)
- request.httpMethod = "POST"
-
- let (_, minutesOld, _) = timeDiff(forWhat: "destTokenAge")
-// print("[JamfPro] \(whichServer) tokenAge: \(minutesOld) minutes")
- if !JamfProServer.validToken || (JamfProServer.base64Creds != base64creds) || (minutesOld > 25) {
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] Attempting to retrieve token from \(String(describing: tokenUrl!)) for version look-up")
-
- if apiClient {
- let clientId = JamfProServer.username
- let secret = JamfProServer.userpass
- let clientString = "grant_type=client_credentials&client_id=\(String(describing: clientId))&client_secret=\(String(describing: secret))"
-// print("[TokenDelegate] clientString: \(clientString)")
-
- let requestData = clientString.data(using: .utf8)
- request.httpBody = requestData
- configuration.httpAdditionalHeaders = ["Content-Type" : "application/x-www-form-urlencoded", "Accept" : "application/json", "User-Agent" : AppInfo.userAgentHeader]
- } else {
- configuration.httpAdditionalHeaders = ["Authorization" : "Basic \(base64creds)", "Content-Type" : "application/json", "Accept" : "application/json", "User-Agent" : AppInfo.userAgentHeader]
- }
-
-
- let session = Foundation.URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
- let task = session.dataTask(with: request as URLRequest, completionHandler: {
- (data, response, error) -> Void in
- guard let data = data else {
- print("[getToken] failed to connect")
- JamfProServer.validToken = false
- completion((0, "failed"))
- return
- }
- let dataString = String(data: data, encoding: .utf8)
- session.finishTasksAndInvalidate()
- if let httpResponse = response as? HTTPURLResponse {
- if httpSuccess.contains(httpResponse.statusCode) {
- if let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) {
-// print("[getToken] json: \(json)")
- if let endpointJSON = json as? [String: Any] {
- JamfProServer.validToken = true
- JamfProServer.authCreds = apiClient ? (endpointJSON["access_token"] as? String ?? "")!:(endpointJSON["token"] as? String ?? "")!
- // JamfProServer.authCreds = (endpointJSON["token"] as? String)!
- // JamfProServer.authExpires = "\(endpointJSON["expires"] ?? "")"
- JamfProServer.authType = "Bearer"
- JamfProServer.base64Creds = base64creds
-
- tokenTimeCreated = Date()
-
- // if LogLevel.debug { writeToLog.message(stringOfText: "[TokenDelegate.getToken] Retrieved token: \(token)") }
- // print("[JamfPro] result of token request: \(endpointJSON)")
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] new token created for \(serverUrl)")
-
- if JamfProServer.version == "" {
- // get Jamf Pro version - start
- self.getVersion(serverUrl: serverUrl, endpoint: "jamf-pro-version", apiData: [:], id: "", token: JamfProServer.authCreds, method: "GET") {
- (result: [String:Any]) in
- if let versionString = result["version"] as? String {
-
- if versionString != "" {
- writeToLog.message(stringOfText: "[TokenDelegate.getVersion] Jamf Pro Version: \(versionString)")
- JamfProServer.version = versionString
- let tmpArray = versionString.components(separatedBy: ".")
- if tmpArray.count > 2 {
- for i in 0...2 {
- switch i {
- case 0:
- JamfProServer.majorVersion = Int(tmpArray[i]) ?? 0
- case 1:
- JamfProServer.minorVersion = Int(tmpArray[i]) ?? 0
- case 2:
- let tmp = tmpArray[i].components(separatedBy: "-")
- JamfProServer.patchVersion = Int(tmp[0]) ?? 0
- if tmp.count > 1 {
- JamfProServer.build = tmp[1]
- }
- default:
- break
- }
- }
- if ( JamfProServer.majorVersion > 10 || (JamfProServer.majorVersion > 9 && JamfProServer.minorVersion > 34) ) {
- JamfProServer.authType = "Bearer"
- JamfProServer.validToken = true
- writeToLog.message(stringOfText: "[TokenDelegate.getVersion] \(serverUrl) set to use Bearer Token")
-
- } else {
- JamfProServer.authType = "Basic"
- JamfProServer.validToken = false
- JamfProServer.authCreds = base64creds
- writeToLog.message(stringOfText: "[TokenDelegate.getVersion] \(serverUrl) set to use Basic Authentication")
- }
-// if ( JamfProServer.majorVersion > 9 && JamfProServer.minorVersion > 34 ) && !forceBasicAuth {
-// JamfProServer.authType = "Bearer"
-// JamfProServer.validToken = true
-// writeToLog.message(stringOfText: "[TokenDelegate.getVersion] \(serverUrl) set to use Bearer Token")
-//
-// } else {
-// JamfProServer.authType = "Basic"
-// JamfProServer.validToken = false
-// JamfProServer.authCreds = base64creds
-// writeToLog.message(stringOfText: "[TokenDelegate.getVersion] \(serverUrl) set to use Basic Authentication")
-// }
-// if JamfProServer.authType == "Bearer" {
-// self.refresh(server: serverUrl, whichServer: whichServer, b64Creds: JamfProServer.base64Creds)
-// }
- completion((200, "success"))
- return
- }
- }
- } else { // if let versionString - end
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] failed to get version information from \(String(describing: serverUrl))")
- JamfProServer.validToken = false
- _ = alert.display(header: "Attention", message: "Failed to get version information from \(String(describing: serverUrl))", secondButton: "")
- completion((httpResponse.statusCode, "failed"))
- return
- }
- }
- // get Jamf Pro version - end
- } else {
- if JamfProServer.authType == "Bearer" {
- writeToLog.message(stringOfText: "[TokenDelegate.getVersion] call token refresh process for \(serverUrl)")
- self.refresh(server: serverUrl, whichServer: whichServer, b64Creds: JamfProServer.base64Creds)
- }
- completion((200, "success"))
- return
- }
- } else { // if let endpointJSON error
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] JSON error.\n\(String(describing: json))")
- JamfProServer.validToken = false
- completion((httpResponse.statusCode, "failed"))
- return
- }
- } else {
- // server down
- _ = alert.display(header: "", message: "Failed to get an expected response from \(String(describing: serverUrl)).", secondButton: "")
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] Failed to get an expected response from \(String(describing: serverUrl)). Status Code: \(httpResponse.statusCode)")
- JamfProServer.validToken = false
- completion((httpResponse.statusCode, "failed"))
- return
- }
-
- } else { // if httpResponse.statusCode <200 or >299
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] Failed to authenticate to \(serverUrl). Response error: \(httpResponse.statusCode).")
-
- _ = alert.display(header: "\(serverUrl)", message: "Failed to authenticate to \(serverUrl). \nStatus Code: \(httpResponse.statusCode)", secondButton: "")
-
- JamfProServer.validToken = false
- completion((httpResponse.statusCode, "failed"))
- return
- }
- } else {
- _ = alert.display(header: "\(serverUrl)", message: "Failed to connect. \nUnknown error, verify url and port.", secondButton: "")
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] token response error from \(serverUrl). Verify url and port.")
- JamfProServer.validToken = false
- completion((0, "failed"))
- return
- }
- })
- task.resume()
- } else {
- writeToLog.message(stringOfText: "[TokenDelegate.getToken] Use existing token from \(String(describing: tokenUrl!))")
- completion((200, "success"))
- return
- }
-
- }
- */
func getVersion(serverUrl: String, endpoint: String, apiData: [String:Any], id: String, token: String, method: String, completion: @escaping (_ returnedJSON: [String: Any]) -> Void) {
@@ -429,7 +207,7 @@ class TokenDelegate: NSObject, URLSessionDelegate {
}
}
- writeToLog.message(stringOfText: "[Jpapi.action] Attempting \(method) on \(urlString).")
+ writeToLog.message(stringOfText: ["[TokenDelegate.getVersion] Attempting \(method) on \(urlString)."])
// print("[Jpapi.action] Attempting \(method) on \(urlString).")
configuration.httpAdditionalHeaders = ["Authorization" : "Bearer \(token)", "Content-Type" : "application/json", "Accept" : "application/json", "User-Agent" : AppInfo.userAgentHeader]
@@ -454,12 +232,12 @@ class TokenDelegate: NSObject, URLSessionDelegate {
return
}
} else { // if httpResponse.statusCode <200 or >299
- writeToLog.message(stringOfText: "[TokenDelegate.getVersion] Response error: \(httpResponse.statusCode).")
+ writeToLog.message(stringOfText: ["[TokenDelegate.getVersion] Response error: \(httpResponse.statusCode)."])
completion(["JPAPI_result":"failed", "JPAPI_method":request.httpMethod ?? method, "JPAPI_response":httpResponse.statusCode, "JPAPI_server":urlString, "JPAPI_token":token])
return
}
} else {
- writeToLog.message(stringOfText: "[TokenDelegate.getVersion] GET response error. Verify url and port.")
+ writeToLog.message(stringOfText: ["[TokenDelegate.getVersion] GET response error. Verify url and port."])
completion([:])
return
}
@@ -467,25 +245,4 @@ class TokenDelegate: NSObject, URLSessionDelegate {
task.resume()
} // func getVersion - end
-
- /*
- func refresh(server: String, whichServer: String, b64Creds: String) {
- DispatchQueue.main.async { [self] in
- if runComplete {
- JamfProServer.validToken = false
- writeToLog.message(stringOfText: "[TokenDelegate.refresh] terminated token refresh")
- return
- }
- writeToLog.message(stringOfText: "[TokenDelegate.refresh] queue token refresh for \(server)")
- renewQ.async { [self] in
- sleep(refreshInterval)
- JamfProServer.validToken = false
- getToken(whichServer: whichServer, serverUrl: server, base64creds: JamfProServer.base64Creds) {
- (result: (Int, String)) in
-// print("[JamfPro.refresh] returned: \(result)")
- }
- }
- }
- }
- */
}
diff --git a/jamfStatus/UapiCall.swift b/jamfStatus/UapiCall.swift
index 8fd15f2..6bddfc7 100644
--- a/jamfStatus/UapiCall.swift
+++ b/jamfStatus/UapiCall.swift
@@ -6,7 +6,8 @@
// Copyright © 2019 Leslie Helou. All rights reserved.
//
-// get notifications from https://jamf.pro.server/uapi/notifications/alerts
+// get notifications from https://jamf.pro.server/uapi/notifications/alerts - old
+// get notifications from https://jamf.pro.server/api/v1/notifications
import Foundation
@@ -20,10 +21,10 @@ class UapiCall: NSObject, URLSessionDelegate, URLSessionDataDelegate, URLSession
let jps = defaults.string(forKey:"jamfServerUrl") ?? ""
- JamfPro().getToken(serverUrl: jps, whichServer: "source", base64creds: JamfProServer.base64Creds) {
- (returnedToken: String) in
+ TokenDelegate().getToken(serverUrl: jps, base64creds: JamfProServer.base64Creds) {
+ (authResult: (Int,String)) in
- if returnedToken != "failed" {
+ if authResult.1 == "success" {
URLCache.shared.removeAllCachedResponses()
@@ -41,7 +42,7 @@ class UapiCall: NSObject, URLSessionDelegate, URLSessionDataDelegate, URLSession
let configuration = URLSessionConfiguration.default
request.httpMethod = "GET"
- configuration.httpAdditionalHeaders = ["Authorization" : "\(JamfProServer.authType) \(JamfProServer.authCreds)", "Content-Type" : "application/json", "Accept" : "application/json", "User-Agent" : appInfo.userAgentHeader]
+ configuration.httpAdditionalHeaders = ["Authorization" : "\(JamfProServer.authType) \(JamfProServer.accessToken)", "Content-Type" : "application/json", "Accept" : "application/json", "User-Agent" : AppInfo.userAgentHeader]
let session = Foundation.URLSession(configuration: configuration, delegate: self as URLSessionDelegate, delegateQueue: OperationQueue.main)
@@ -50,7 +51,7 @@ class UapiCall: NSObject, URLSessionDelegate, URLSessionDataDelegate, URLSession
if let httpResponse = response as? HTTPURLResponse {
if httpResponse.statusCode >= 200 && httpResponse.statusCode <= 299 {
let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments)
- if let notificationsDictArray = json! as? [Dictionary] {
+ if let notificationsDictArray = json! as? [[String: Any]] {
completion(notificationsDictArray)
return
} else { // if let endpointJSON error
@@ -59,7 +60,7 @@ class UapiCall: NSObject, URLSessionDelegate, URLSessionDataDelegate, URLSession
return
}
} else { // if httpResponse.statusCode <200 or >299
- print("[UapiCall] get response error: \(httpResponse.statusCode)")
+ print("[UapiCall] \(endpoint) - get response error: \(httpResponse.statusCode)")
completion([])
return
}
diff --git a/jamfStatus/WriteToLog.swift b/jamfStatus/WriteToLog.swift
index 051c3c5..2d1505f 100644
--- a/jamfStatus/WriteToLog.swift
+++ b/jamfStatus/WriteToLog.swift
@@ -9,6 +9,8 @@
import Foundation
import os.log
+let writeToLog = WriteToLog()
+
class WriteToLog {
let logFileW = FileHandle(forUpdatingAtPath: Log.path! + Log.file)
diff --git a/jamfStatus/images/notifications.png b/jamfStatus/images/notifications.png
index 97d4700..7d66652 100644
Binary files a/jamfStatus/images/notifications.png and b/jamfStatus/images/notifications.png differ
diff --git a/jamfStatus/images/prefs.png b/jamfStatus/images/prefs.png
index 10efef9..cbebc81 100644
Binary files a/jamfStatus/images/prefs.png and b/jamfStatus/images/prefs.png differ