diff --git a/Scripts/bundleInstallSample.py b/Scripts/bundleInstallSample.py new file mode 100644 index 0000000..771b70f --- /dev/null +++ b/Scripts/bundleInstallSample.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python2.7 +# +# IBM 01/12/2021 +# App Installs called by the enrollment app after bundle choices +# Sample code for Open Source project. +# +# SPDX-License-Identifier: GPL-3.0-only + +import os +import plistlib +import time +import commands +import time +from AppKit import NSWorkspace +import subprocess +from subprocess import Popen, PIPE +from collections import OrderedDict +import shlex, subprocess + +############### Start of function ############### +# Function to run the installs. +def install(section, event, loggedInUser, appText, plistText, appPath): + print section, event, loggedInUser, appText, plistText, appPath + appInstall = plistText + "InstallStatus" + # Set the status of the spinner + statusstart = '1' + statusstop = '2' + + # Set the message for the end user + print "%sMessaging %s" % (section, appText) + Message = "%sMessaging" % (section) + Text = "%s" % (appText) + print appText + print Message + + sub_command = "sudo -u %s open 'macatibmenrollment:updateuistatus?%s=%s&%s=%s'" % (loggedInUser, appInstall, statusstart, Message, Text) + print sub_command + + args = shlex.split(sub_command) + ssUser = subprocess.Popen(args, stdout=PIPE) + + if "ibmmangement" in appPath: + os.system("sudo jamf policy -event %s -forceNoRecon" % event) + else: + if os.path.exists(appPath): + print "App %s already installed" % appPath + time.sleep(2) + else: + print "jamf %s" % event + os.system("sudo jamf policy -event %s -forceNoRecon" % event) + + # Updated the install completion status... 2=Success, 3=Fail + if os.path.exists(appPath): + statusstop = '2' + else: + statusstop = '3' + + print statusstop + sub_command = "sudo -u %s open 'macatibmenrollment:updateuistatus?%s=%s'" % (loggedInUser, appInstall, statusstop) + args = shlex.split(sub_command) + ssUser = subprocess.Popen(args, stdout=PIPE) + time.sleep(2) + +############### End of function ############### + +################################################################################################################################################### +################################################################################################################################################### +## Get the User home directory path. +sub_command = "/bin/ls -la /dev/console | /usr/bin/cut -d ' ' -f 4" +loggedInUser = subprocess.Popen(sub_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +loggedInUser = loggedInUser.communicate()[0] +loggedInUser = loggedInUser.strip() +print "logged in user is: %s" % loggedInUser +filePath = "/Users/%s/Library/Preferences/com.ibm.enrollment.plist" % loggedInUser +print filePath + + +# Read the plist for the Selected Bundles +sub_command = "sudo -u %s defaults read %s selectedBundles" % (loggedInUser, filePath) +args = shlex.split(sub_command) +bundles = subprocess.Popen(sub_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) +bundles = bundles.communicate()[0] +print bundles + +statusstart = '1' +statusstop = '2' + +# Example install bundle. +#For each bundle you will need to create a loop or duplicate the block below. +################# Start of Block ######################### + +# Name of bundles in the application plist shown in the Wiki docs under Bundle Selection View page. +if "connectivity" in bundles: + print "In Loop connectivity" + section = "connectivity" + sub_command = "sudo -u %s open 'macatibmenrollment:updateuistatus?connectivityBundleStatus=%s'" % (loggedInUser, statusstart) + args = shlex.split(sub_command) + ssUser = subprocess.Popen(args, stdout=PIPE) + install_event_list = {'AppText1' : ['AppInstallOrder1','AppPlistUpdateName1','AppJPSEventTrigger1', 'AppInstallVerifyPath1'], 'AppText2' : ['AppInstallOrder2','AppPlistUpdateName2','AppJPSEventTrigger2', 'AppInstallVerifyPath2']} + install_event_list = OrderedDict(sorted(install_event_list.items(), key=lambda t: t[1])) + for dict in install_event_list: + plistText = (install_event_list[dict][1]) + event = (install_event_list[dict][2]) + appPath = (install_event_list[dict][3]) + install(section, event, loggedInUser, dict, plistText, appPath) + + Message = "%sMessaging" % (section) + sub_command = "sudo -u %s open 'macatibmenrollment:updateuistatus?connectivityBundleStatus=%s&%s=Connectivity_bundle_complete'" % (loggedInUser, statusstop, Message) + args = shlex.split(sub_command) + ssUser = subprocess.Popen(args, stdout=PIPE) +#################### End of Block ###################### + +# Set the App Screen to close it +time.sleep(2) # Allow the app to update +sub_command = "sudo -u %s open 'macatibmenrollment:updateuistatus?appScreenStatus=1'" % loggedInUser +args = shlex.split(sub_command) +ssUser = subprocess.Popen(args, stdout=PIPE) + diff --git a/Scripts/enrollmentSample.py b/Scripts/enrollmentSample.py new file mode 100644 index 0000000..f0080d1 --- /dev/null +++ b/Scripts/enrollmentSample.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python2.7 +# +# IBM 03/22/2021 +# Sample code for Open Source project +# Items used in the UI during enrollment +# +# SPDX-License-Identifier: GPL-3.0-only + +import os +import plistlib +import json +import urllib2 +import base64 +import sys +import time +import subprocess +from subprocess import Popen, PIPE + +# Get the logged in user +sub_command = "/bin/ls -la /dev/console | /usr/bin/cut -d ' ' -f 4" +loggedInUser = subprocess.Popen(sub_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +loggedInUser = loggedInUser.communicate()[0] +loggedInUser = loggedInUser.strip() + +# Sample code for pulling data from the JSS with the api +# sys arguements are pulled from script parameters in the jss +''' +jss_url = sys.argv[4] +jss_url = jss_url.rstrip('/') +jss_api_user = sys.argv[5] +jss_api_passwd = sys.argv[6] + +sub_command = "system_profiler SPHardwareDataType | grep UUID | awk '" " { print $NF }'" +result = subprocess.Popen(sub_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +UDID = result.communicate()[0] +UDID = UDID.rstrip('\r\n') + +jss_sub_url = str(jss_url + '/JSSResource/computers/udid/%s/subset/general&location&extension_attributes') % UDID +request = urllib2.Request(jss_sub_url) +request.add_header('Accept', 'application/json') +request.add_header('Authorization', 'Basic ' + base64.b64encode(jss_api_user + ':' + jss_api_passwd)) +response = urllib2.urlopen(request) +apidata = json.load(response) + +# Read the xml +username = apidata['computer']['location']['username'] +jssid = apidata['computer']['general']['id'] +email = apidata['computer']['location']['email_address'] +position = apidata['computer']['location']['position'] +''' + +# The username to display in the app. You can use the username pulled from the api call above or use your on method to populate. +hrFirstName = "User name" +jpsCommSeconds = "60.0" + +# Configuration json, see https://github.com/IBM/mac-ibm-enrollment-app/wiki/Configuration-file-(.plist) for more info about the structure. +conf_json = """ +{ + "phase": 0, + "userInfo": { + "hrFirstName": "%s" + }, + "networkInfo": { + "jpsCommSeconds": "%s" + }, + "policies": { + "registrationPolicy": "reg", + "bundleInstallationPolicy": "inst", + "removeFramework": "rem" + }, + "registration": { + "pages": [ + { + "title": { + "label": "Page One" + }, + "subitile": { + "label": "Subtitle", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + }, + "fields": [ + { + "title" : { + "label": "Field Title One" + }, + "key" : "keyone", + "multipleChoiseAllowed" : false, + "showTitle" : true, + "options" : [ + { + "key": "keyOptionOne", + "label": "Key Label One" + }, + { + "key": "keyOptionTwo", + "label": "Key Label Two" + }, + { + "key": "keyOptionThree", + "label": "Key Label Three" + } + ] + }, + { + "title" : { + "label": "Field Title Two" + }, + "key" : "keyone", + "multipleChoiseAllowed" : false, + "showTitle" : true, + "options" : [ + { + "key": "keyOptionOne", + "label": "Key Label One" + }, + { + "key": "keyOptionTwo", + "label": "Key Label Two" + }, + { + "key": "keyOptionThree", + "label": "Key Label Three" + } + ] + }, + { + "title" : { + "label": "Field Title Three" + }, + "key" : "keyone", + "multipleChoiseAllowed" : false, + "showTitle" : true, + "options" : [ + { + "key": "keyOptionOne", + "label": "Key Label One" + }, + { + "key": "keyOptionTwo", + "label": "Key Label Two" + }, + { + "key": "keyOptionThree", + "label": "Key Label Three" + } + ] + },{ + "title" : { + "label": "Field Title Four" + }, + "key" : "keyone", + "multipleChoiseAllowed" : false, + "showTitle" : true, + "options" : [ + { + "key": "keyOptionOne", + "label": "Key Label One" + }, + { + "key": "keyOptionTwo", + "label": "Key Label Two" + }, + { + "key": "keyOptionThree", + "label": "Key Label Three" + } + ] + } + ], + "footer": { + "label": "Footer", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + } + }, + { + "title": { + "label": "Page Two" + }, + "subitile": { + "label": "Subtitle", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + }, + "fields": [ + { + "title" : { + "label": "Field Title One" + }, + "key" : "keyone", + "multipleChoiseAllowed" : false, + "showTitle" : true, + "options" : [ + { + "key": "keyOptionOne", + "label": "Key Label One" + }, + { + "key": "keyOptionTwo", + "label": "Key Label Two" + }, + { + "key": "keyOptionThree", + "label": "Key Label Three" + } + ] + }, + { + "title" : { + "label": "Field Title Two" + }, + "key" : "keyone", + "multipleChoiseAllowed" : false, + "showTitle" : true, + "options" : [ + { + "key": "keyOptionOne", + "label": "Key Label One" + }, + { + "key": "keyOptionTwo", + "label": "Key Label Two" + }, + { + "key": "keyOptionThree", + "label": "Key Label Three" + } + ] + }, + { + "title" : { + "label": "Field Title Three" + }, + "key" : "keyone", + "multipleChoiseAllowed" : false, + "showTitle" : true, + "options" : [ + { + "key": "keyOptionOne", + "label": "Key Label One" + }, + { + "key": "keyOptionTwo", + "label": "Key Label Two" + }, + { + "key": "keyOptionThree", + "label": "Key Label Three" + } + ] + },{ + "title" : { + "label": "Field Title Four" + }, + "key" : "keyone", + "multipleChoiseAllowed" : false, + "showTitle" : true, + "options" : [ + { + "key": "keyOptionOne", + "label": "Key Label One" + }, + { + "key": "keyOptionTwo", + "label": "Key Label Two" + }, + { + "key": "keyOptionThree", + "label": "Key Label Three" + } + ] + } + ], + "footer": { + "label": "Footer", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + } + } + ] + }, + "bundleSelectionPage": { + "title": { + "label": "Bundle Selection Page" + }, + "subitile": { + "label": "Subtitle", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + }, + "bundles": [ + { + "title": "First Bundle", + "extendedTitle": "First Bundle", + "description": "First Bundle", + "key": "firstbundle", + "icon": "firstbundle", + "status": 0, + "bundleMessaging": "", + "time": "300", + "size": "300", + "recommended": true, + "apps": [ + { + "title": "First App", + "description": "First App", + "key": "firstappfirstbundle", + "icon": "firstappfirstbundle", + "status": 0 + }, + { + "title": "Second App", + "description": "Second App", + "key": "secondappfirstbundle", + "icon": "secondappfirstbundle", + "status": 0 + }, + { + "title": "Third App", + "description": "First App", + "key": "thirdappfirstbundle", + "icon": "thirdappfirstbundle", + "status": 0 + } + ] + }, + { + "title": "Second Bundle", + "extendedTitle": "Second Bundle", + "description": "Second Bundle", + "key": "secondbundle", + "icon": "secondbundle", + "status": 0, + "bundleMessaging": "", + "time": "300", + "size": "300", + "recommended": true, + "apps": [ + { + "title": "First App", + "description": "First App", + "key": "firstappsecondbundle", + "icon": "firstappsecondbundle", + "status": 0 + }, + { + "title": "Second App", + "description": "Second App", + "key": "secondappsecondbundle", + "icon": "secondappsecondbundle", + "status": 0 + }, + { + "title": "Third App", + "description": "First App", + "key": "thirdappsecondbundle", + "icon": "thirdappsecondbundle", + "status": 0 + } + ] + }, + { + "title": "Third Bundle", + "extendedTitle": "Third Bundle", + "description": "Third Bundle", + "key": "thirdbundle", + "icon": "thirdbundle", + "status": 0, + "bundleMessaging": "", + "time": "300", + "size": "300", + "recommended": true, + "apps": [ + { + "title": "First App", + "description": "First App", + "key": "firstappthirdbundle", + "icon": "firstappthirdbundle", + "status": 0 + }, + { + "title": "Second App", + "description": "Second App", + "key": "secondappthirdbundle", + "icon": "secondappthirdbundle", + "status": 0 + }, + { + "title": "Third App", + "description": "First App", + "key": "thirdappthirdbundle", + "icon": "thirdappthirdbundle", + "status": 0 + } + ] + } + ] + }, + "bundleInstallationPage": { + "title": { + "label": "Installation page" + }, + "subitile": { + "label": "Subtitle", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + } + }, + "postRegistrationPage": { + "title": { + "label": "Post registration" + }, + "subitile": { + "label": "Subtitle", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + }, + "body": "Post registration page body", + "needsRestart": true, + "restartTimeout": 300, + "footer": { + "label": "Footer", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + } + }, + "postInstallationPage": { + "title": { + "label": "Post Installation page" + }, + "subitile": { + "label": "Subtitle", + "infoSection": { + "fields": [ + { + "label": "Label One", + "description": "Description One" + }, + { + "label": "Label Two", + "description": "Description Two" + }, + { + "label": "Label Three", + "description": "Description Three" + } + ] + } + }, + "items": [ + { + "title": "First Item", + "description": "First Item", + "iconName": "firstPostInstallationItem", + "ctaType": "url", + "ctaPayload": "https://www.ibm.com" + }, + { + "title": "Second Item", + "description": "Second Item", + "iconName": "secondPostInstallationItem", + "ctaType": "url", + "ctaPayload": "https://www.ibm.com" + },{ + "title": "Third Item", + "description": "Third Item", + "iconName": "thirdPostInstallationItem", + "ctaType": "url", + "ctaPayload": "https://www.ibm.com" + } + ,{ + "title": "Fourth Item", + "description": "Fourth Item", + "iconName": "fourthPostInstallationItem", + "ctaType": "url", + "ctaPayload": "https://www.ibm.com" + } + ], + "needsRestart": false + }, + "environment": "prod" +} +""" % (hrFirstName, jpsCommSeconds) + +# Setup for enrollment +filePath = "/Users/%s/Library/Preferences/com.ibm.enrollment.plist" % loggedInUser +if os.path.exists(filePath): + os.remove(filePath) + +# Loading the enrollment data json +json_object = json.loads(conf_json) + +# Writing the .plist configuration file +plistlib.writePlist(json_object, filePath) + diff --git a/Scripts/speedtestSample.py b/Scripts/speedtestSample.py new file mode 100644 index 0000000..49d5a26 --- /dev/null +++ b/Scripts/speedtestSample.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# Determines the time it takes to download a 10MB pkg from the distribution point for the enrollment app to calculate download times. +# Sample code for Open Source project. used examples from https://docs.python.org/3/library/urllib.request.html and +# +# SPDX-License-Identifier: GPL-3.0-only + +import sys +import time +import urllib +import ssl +import plistlib +import os +import subprocess +import commands +import datetime + +########### VARIABLES THAT NEED TO BE CHANGED PER ENVIRONMENT ########### +# url is the path of a 10mb pkg to get a sample time from. +''' +url example https://jss_distrobution_point.com/SamplePackage.pkg +If you are using Enable Remote Authentication in the JSS you may need to create a non expiring download. +''' +url="https://my10mb.pkg" +###################################################################### +################## DO NOT CHANGE BELOW THIS LINE ################## + +lastrun = datetime.datetime.today().strftime('%Y-%m-%d') +speedtest = [] +filename="/private/tmp/sample.pkg" + +# reporthook function will calculate how long it takes to download the package from the save function. +def reporthook(number, blocksize, totalSize): + global starttime + if number == 0: + starttime = time.time() + return + speed = int(int(number * blocksize) / (1024 * (time.time() - starttime))) + speedtest.append(speed) + +# Save function downloads the url specified and saves it to the file location specified in filename. +def save(url, filename): + ssl._create_default_https_context = ssl._create_unverified_context + urllib.urlretrieve(url, filename, reporthook) + +#__main__ + +# run the save function passing the url and filename. +result=save(url,filename) +result = str(round(int(sum(speedtest) / float(len(speedtest))) * float(0.000976562),2)) +print result + +# Write the results to the plist file for the enrollment app. +sub_command = "/bin/ls -la /dev/console | /usr/bin/cut -d ' ' -f 4" +loggedInUser = subprocess.Popen(sub_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +loggedInUser = loggedInUser.communicate()[0] +loggedInUser = loggedInUser.strip() + +print "logged in user is: %s" % loggedInUser +filePath = "/Users/%s/Library/Preferences/com.ibm.enrollment.plist" % loggedInUser +print filePath + +sub_command = ['sudo', '-u', loggedInUser, 'defaults', 'write', filePath, 'networkInfo', '-dict-add', 'speedTestResult', result] +ssUser = subprocess.Popen(sub_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + diff --git a/enrollment/enrollment/Model/ContextModel/RegistrationData.swift b/enrollment/enrollment/Model/ContextModel/RegistrationData.swift index f066fea..51b7bf5 100644 --- a/enrollment/enrollment/Model/ContextModel/RegistrationData.swift +++ b/enrollment/enrollment/Model/ContextModel/RegistrationData.swift @@ -16,7 +16,7 @@ public final class RegistrationData: Codable { /// True if the registration process has been completed, false if not. (Default: false). var registrationStatus: Bool? { didSet { - UserDefaults.standard.set(registrationStatus, forKey: UserDefaultHelper.Keys.registrationStatus) + UserDefaults.standard.set(self, forKey: UserDefaultHelper.Keys.registration) } } diff --git a/enrollment/enrollment/Views/Constants/UserDefaultHelper.swift b/enrollment/enrollment/Views/Constants/UserDefaultHelper.swift index b445b7e..7436fe1 100644 --- a/enrollment/enrollment/Views/Constants/UserDefaultHelper.swift +++ b/enrollment/enrollment/Views/Constants/UserDefaultHelper.swift @@ -12,9 +12,8 @@ import Foundation struct UserDefaultHelper { struct Keys { static let phase = "phase" - static let registrationStatus = "registrationStatus" - static let hrFirstName = "hrFirstName" + static let registration = "registration" static let environment = "environment" - static let selectedBundles = "SelectedBundles" + static let selectedBundles = "selectedBundles" } }