@boberito
If you could have a quick look. I just want to see if I can pull mobile devices in. I do think i'm going to have to start from the beginning and build my code from scratch. I'm trying to use yours to get a better understanding of how it works.
See code below with my edits.
import Cocoa
//login class
class jamfInfo {
var server: String?
var id: String?
var name: String?
var username: String = ""
var password: String = ""
}
struct userSearch: Decodable {
let user: listOfUsers
struct listOfUsers: Decodable {
let name: String
let full_name: String
let email: String
let phone_number: String
let links: listOfmobiledevices
struct listOfmobiledevices: Decodable {
let mobiledevices: [mobiledevices]
struct mobiledevices: Decodable {
let name: String
let id: Int
}
}
}
}
struct extensionAttribute: Decodable {
let mobiledevices: mobiledevicesEA
struct mobiledevicesEA: Decodable {
let extension_attributes: [EA]
struct EA: Decodable {
let id: Int
let name: String
let value: String
}
}
}
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
@IBOutlet var userInfo: NSTextField!
@IBOutlet var tableView: NSTableView!
@IBOutlet var JamfPassword: NSSecureTextField!
@IBOutlet var jamfUser: NSTextField!
@IBOutlet var userNameField: NSTextFieldCell!
@IBOutlet var toggleButton: NSButtonCell!
var listOfmobiledevices = [String]()
var listOfmobiledevicesIDs = [Int]()
var listOfEAs = [String]()
var prefs = jamfInfo()
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
let second = segue.destinationController as! PrefController
readPlist()
prefs.username = jamfUser.stringValue
prefs.password = JamfPassword.stringValue
second.representedObject = prefs
listOfmobiledevices.removeAll()
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
readPlist()
if prefs.server != nil {
let keychainVar = try? keychainlogin(server: prefs.server!)
if keychainVar != nil {
prefs.username = keychainVar!.KCUsername
prefs.password = keychainVar!.KCPassword
jamfUser.stringValue = prefs.username
JamfPassword.stringValue = prefs.password
}
}
toggleButton.isEnabled = false
// Do any additional setup after loading the view.
}
@IBAction func preferencesMenuItemSelected(_ sender: Any) {
self.performSegue(withIdentifier: "prefSegue", sender: self)
}
@IBAction func doSomething(_ sender: Any) {
readPlist()
getTableView()
}
func numberOfRows(in tableView: NSTableView) -> Int {
return listOfmobiledevices.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
guard let vw = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) as? NSTableCellView else { return nil }
if tableColumn?.title == "Computer List" {
vw.textField?.stringValue = listOfmobiledevices[row]
} else {
var eaOutput: Data?
readPlist()
eaOutput = getJamfData(url: "(prefs.server!)JSSResource/mobiledevices/id/(listOfmobiledevicesIDs[row])/subset/ExtensionAttributes").jamfDataOutput
let EAdecoder = JSONDecoder()
let EADetails = try! EAdecoder.decode(extensionAttribute.self, from: eaOutput!)
for EAs in EADetails.mobiledevices.extension_attributes {
if EAs.id == Int(prefs.id!) {
vw.textField?.stringValue = EAs.value
listOfEAs.insert(EAs.value, at: 0)
break;
} else {
vw.textField?.stringValue = ""
listOfEAs.insert("", at: 0)
}
}
}
return vw
}
func tableViewSelectionDidChange(_ notification: Notification) {
if tableView.selectedRow != -1 {
toggleButton.isEnabled = true
} else {
toggleButton.isEnabled = false
}
}
func getJamfData(url: String) -> (jamfDataOutput: Data, jamfResponse: Int) {
let loginData = "(jamfUser.stringValue):(JamfPassword.stringValue)".data(using: String.Encoding.utf8)
let base64LoginString = loginData!.base64EncodedString()
let headers = ["Accept": "application/json",
"Authorization": "Basic (String(describing: base64LoginString))"]
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
var dataReturn: Data?
var ResponseCode: HTTPURLResponse?
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if let APIdata = data {
dataReturn = APIdata
ResponseCode = response as? HTTPURLResponse
}
dispatchGroup.leave()
})
dataTask.resume()
dispatchGroup.wait()
if dataReturn == nil {
dataReturn = "".data(using: String.Encoding.utf8)
}
var ResponseStatusCode: Int
if ResponseCode == nil {
ResponseStatusCode = 1
} else {
ResponseStatusCode = (ResponseCode?.statusCode)!
}
return(dataReturn!, ResponseStatusCode)
}
@IBAction func toggle(_ sender: Any) {
readPlist()
guard tableView.selectedRow != -1 else { return }
var action: String = ""
if listOfEAs[tableView.selectedRow] != "Enabled" {
action = "Enabled"
} else {
action = "Disabled"
}
var xmldata: String
let requestURL = "(prefs.server!)JSSResource/mobiledevices/id/(listOfmobiledevicesIDs[tableView.selectedRow])"
xmldata = "<?xml version="1.0" encoding="UTF-8" standalone="no"?><mobiledevices><extension_attributes><extension_attribute><id>" prefs.id! "</id><name>" prefs.name! "</name><type>String</type><value>" action "</value></extension_attribute></extension_attributes></mobiledevice>"
let loginData = "(jamfUser.stringValue):(JamfPassword.stringValue)".data(using: String.Encoding.utf8)
let base64LoginString = loginData!.base64EncodedString()
let postData = NSData(data: xmldata.data(using: String.Encoding.utf8)!)
let headers = ["Content-Type": "text/xml", "Authorization": "Basic (String(describing: base64LoginString))"]
let request = NSMutableURLRequest(url: NSURL(string: requestURL)! as URL,cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
dispatchGroup.leave()
}
)
dataTask.resume()
dispatchGroup.wait()
getTableView()
toggleButton.isEnabled = true
}
func errorOccured(typeOfError: String){
let alert = NSAlert()
alert.messageText = "Error"
alert.informativeText = typeOfError
alert.runModal()
}
func getTableView(){
listOfmobiledevices.removeAll()
tableView.reloadData()
let myData = getJamfData(url: "(prefs.server!)JSSResource/users/name/(userNameField.stringValue)")
let decoder = JSONDecoder()
do {
switch myData.jamfResponse {
case 401:
errorOccured(typeOfError: "Login Incorrect.")
case 400:
errorOccured(typeOfError: "Bad Request: You sent a request that this server could not understand.")
case 404:
errorOccured(typeOfError: "User not found.")
case 1:
errorOccured(typeOfError: "Could not connect to Server")
case 200:
let userdetails = try decoder.decode(userSearch.self, from: myData.jamfDataOutput)
let userInfoString = """
(userdetails.user.full_name)
(userdetails.user.email)
(userdetails.user.phone_number)
"""
let emailStringLength = userdetails.user.email.count + 1
let attributedString = NSMutableAttributedString(string: userInfoString)
attributedString.addAttribute(.link, value: "mailto: (userdetails.user.email)", range: NSRange(location: userdetails.user.full_name.count, length: emailStringLength ))
userInfo.attributedStringValue = attributedString
for entries in userdetails.user.links.mobiledevices {
listOfmobiledevicesIDs.insert(entries.id as Int, at: 0)
listOfmobiledevices.insert(entries.name as String, at: 0)
tableView.insertRows(at: IndexSet(integer: 0))
}
default:
errorOccured(typeOfError: "A connection error occured. Sorry")
//NSApplication.shared.terminate(self)
}
} catch {
errorOccured(typeOfError: "Unknown error occured. Sorry")
//NSApplication.shared.terminate(self)
}
}
func keychainlogin(server: String) throws -> (KCUsername: String, KCPassword: String) {
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
kSecAttrServer as String: server,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true,
kSecReturnData as String: true]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
guard let existingItem = item as? [String : Any],
let passwordData = existingItem[kSecValueData as String] as? Data,
let keychainpassword = String(data: passwordData, encoding: String.Encoding.utf8),
let keychainaccount = existingItem[kSecAttrAccount as String] as? String
else {
throw KeychainError.unexpectedPasswordData
}
return(keychainaccount, keychainpassword)
}
func readPlist(){
prefs.server = UserDefaults.standard.string(forKey: "jss_URL") ?? ""
prefs.name = UserDefaults.standard.string(forKey: "EA_NAME") ?? ""
prefs.id = UserDefaults.standard.string(forKey: "EA_ID") ?? ""
}
}
import Cocoa
//login class
class jamfInfo {
var server: String?
var id: String?
var name: String?
var username: String = ""
var password: String = ""
}
struct userSearch: Decodable {
let user: listOfUsers
struct listOfUsers: Decodable {
let name: String
let full_name: String
let email: String
let phone_number: String
let links: listOfmobiledevices
struct listOfmobiledevices: Decodable {
let mobiledevices: [mobiledevices]
struct mobiledevices: Decodable {
let name: String
let id: Int
}
}
}
}
struct extensionAttribute: Decodable {
let mobiledevices: mobiledevicesEA
struct mobiledevicesEA: Decodable {
let extension_attributes: [EA]
struct EA: Decodable {
let id: Int
let name: String
let value: String
}
}
}
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
@IBOutlet var userInfo: NSTextField!
@IBOutlet var tableView: NSTableView!
@IBOutlet var JamfPassword: NSSecureTextField!
@IBOutlet var jamfUser: NSTextField!
@IBOutlet var userNameField: NSTextFieldCell!
@IBOutlet var toggleButton: NSButtonCell!
var listOfmobiledevices = [String]()
var listOfmobiledevicesIDs = [Int]()
var listOfEAs = [String]()
var prefs = jamfInfo()
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
let second = segue.destinationController as! PrefController
readPlist()
prefs.username = jamfUser.stringValue
prefs.password = JamfPassword.stringValue
second.representedObject = prefs
listOfmobiledevices.removeAll()
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
readPlist()
if prefs.server != nil {
let keychainVar = try? keychainlogin(server: prefs.server!)
if keychainVar != nil {
prefs.username = keychainVar!.KCUsername
prefs.password = keychainVar!.KCPassword
jamfUser.stringValue = prefs.username
JamfPassword.stringValue = prefs.password
}
}
toggleButton.isEnabled = false
// Do any additional setup after loading the view.
}
@IBAction func preferencesMenuItemSelected(_ sender: Any) {
self.performSegue(withIdentifier: "prefSegue", sender: self)
}
@IBAction func doSomething(_ sender: Any) {
readPlist()
getTableView()
}
func numberOfRows(in tableView: NSTableView) -> Int {
return listOfmobiledevices.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
guard let vw = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) as? NSTableCellView else { return nil }
if tableColumn?.title == "Computer List" {
vw.textField?.stringValue = listOfmobiledevices[row]
} else {
var eaOutput: Data?
readPlist()
eaOutput = getJamfData(url: "(prefs.server!)JSSResource/mobiledevices/id/(listOfmobiledevicesIDs[row])/subset/ExtensionAttributes").jamfDataOutput
let EAdecoder = JSONDecoder()
let EADetails = try! EAdecoder.decode(extensionAttribute.self, from: eaOutput!)
for EAs in EADetails.mobiledevices.extension_attributes {
if EAs.id == Int(prefs.id!) {
vw.textField?.stringValue = EAs.value
listOfEAs.insert(EAs.value, at: 0)
break;
} else {
vw.textField?.stringValue = ""
listOfEAs.insert("", at: 0)
}
}
}
return vw
}
func tableViewSelectionDidChange(_ notification: Notification) {
if tableView.selectedRow != -1 {
toggleButton.isEnabled = true
} else {
toggleButton.isEnabled = false
}
}
func getJamfData(url: String) -> (jamfDataOutput: Data, jamfResponse: Int) {
let loginData = "(jamfUser.stringValue):(JamfPassword.stringValue)".data(using: String.Encoding.utf8)
let base64LoginString = loginData!.base64EncodedString()
let headers = ["Accept": "application/json",
"Authorization": "Basic (String(describing: base64LoginString))"]
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
var dataReturn: Data?
var ResponseCode: HTTPURLResponse?
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if let APIdata = data {
dataReturn = APIdata
ResponseCode = response as? HTTPURLResponse
}
dispatchGroup.leave()
})
dataTask.resume()
dispatchGroup.wait()
if dataReturn == nil {
dataReturn = "".data(using: String.Encoding.utf8)
}
var ResponseStatusCode: Int
if ResponseCode == nil {
ResponseStatusCode = 1
} else {
ResponseStatusCode = (ResponseCode?.statusCode)!
}
return(dataReturn!, ResponseStatusCode)
}
@IBAction func toggle(_ sender: Any) {
readPlist()
guard tableView.selectedRow != -1 else { return }
var action: String = ""
if listOfEAs[tableView.selectedRow] != "Enabled" {
action = "Enabled"
} else {
action = "Disabled"
}
var xmldata: String
let requestURL = "(prefs.server!)JSSResource/mobiledevices/id/(listOfmobiledevicesIDs[tableView.selectedRow])"
xmldata = "<?xml version="1.0" encoding="UTF-8" standalone="no"?><mobiledevices><extension_attributes><extension_attribute><id>" + prefs.id! + "</id><name>" + prefs.name! + "</name><type>String</type><value>" + action + "</value></extension_attribute></extension_attributes></mobiledevice>"
let loginData = "(jamfUser.stringValue):(JamfPassword.stringValue)".data(using: String.Encoding.utf8)
let base64LoginString = loginData!.base64EncodedString()
let postData = NSData(data: xmldata.data(using: String.Encoding.utf8)!)
let headers = ["Content-Type": "text/xml", "Authorization": "Basic (String(describing: base64LoginString))"]
let request = NSMutableURLRequest(url: NSURL(string: requestURL)! as URL,cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
dispatchGroup.leave()
}
)
dataTask.resume()
dispatchGroup.wait()
getTableView()
toggleButton.isEnabled = true
}
func errorOccured(typeOfError: String){
let alert = NSAlert()
alert.messageText = "Error"
alert.informativeText = typeOfError
alert.runModal()
}
func getTableView(){
listOfmobiledevices.removeAll()
tableView.reloadData()
let myData = getJamfData(url: "(prefs.server!)JSSResource/users/name/(userNameField.stringValue)")
let decoder = JSONDecoder()
do {
switch myData.jamfResponse {
case 401:
errorOccured(typeOfError: "Login Incorrect.")
case 400:
errorOccured(typeOfError: "Bad Request: You sent a request that this server could not understand.")
case 404:
errorOccured(typeOfError: "User not found.")
case 1:
errorOccured(typeOfError: "Could not connect to Server")
case 200:
let userdetails = try decoder.decode(userSearch.self, from: myData.jamfDataOutput)
let userInfoString = """
(userdetails.user.full_name)
(userdetails.user.email)
(userdetails.user.phone_number)
"""
let emailStringLength = userdetails.user.email.count + 1
let attributedString = NSMutableAttributedString(string: userInfoString)
attributedString.addAttribute(.link, value: "mailto: (userdetails.user.email)", range: NSRange(location: userdetails.user.full_name.count, length: emailStringLength ))
userInfo.attributedStringValue = attributedString
for entries in userdetails.user.links.mobiledevices {
listOfmobiledevicesIDs.insert(entries.id as Int, at: 0)
listOfmobiledevices.insert(entries.name as String, at: 0)
tableView.insertRows(at: IndexSet(integer: 0))
}
default:
errorOccured(typeOfError: "A connection error occured. Sorry")
//NSApplication.shared.terminate(self)
}
} catch {
errorOccured(typeOfError: "Unknown error occured. Sorry")
//NSApplication.shared.terminate(self)
}
}
func keychainlogin(server: String) throws -> (KCUsername: String, KCPassword: String) {
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
kSecAttrServer as String: server,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true,
kSecReturnData as String: true]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
guard let existingItem = item as? [String : Any],
let passwordData = existingItem[kSecValueData as String] as? Data,
let keychainpassword = String(data: passwordData, encoding: String.Encoding.utf8),
let keychainaccount = existingItem[kSecAttrAccount as String] as? String
else {
throw KeychainError.unexpectedPasswordData
}
return(keychainaccount, keychainpassword)
}
func readPlist(){
prefs.server = UserDefaults.standard.string(forKey: "jss_URL") ?? ""
prefs.name = UserDefaults.standard.string(forKey: "EA_NAME") ?? ""
prefs.id = UserDefaults.standard.string(forKey: "EA_ID") ?? ""
}
}