├── .browserslistrc ├── babel.config.js ├── .env.local ├── postcss.config.js ├── public ├── tux.png ├── favicon.ico ├── api │ ├── user.php │ ├── centers.php │ ├── computers.php │ ├── exemptions.php │ ├── exemption_check.php │ ├── settings.inc │ ├── includes │ │ └── jamf.inc │ └── issueexemption.php └── index.html ├── src ├── assets │ └── logo.png ├── router.js ├── main.js ├── App.vue ├── components │ ├── Footer.vue │ └── Header.vue └── views │ └── Home.vue ├── .env ├── vue.config.js ├── .gitignore ├── settings.local.inc ├── package.json ├── README.md └── Jamf Scripts ├── ExemptionRemoval.sh └── StartExemption.sh /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.env.local: -------------------------------------------------------------------------------- 1 | NODE_ENV=dev 2 | VUE_APP_API_SITE_URL=https://your.dev.server/pivexemption 3 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/tux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/therealmacjeezy/piv-exemption-portal/master/public/tux.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/therealmacjeezy/piv-exemption-portal/master/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/therealmacjeezy/piv-exemption-portal/master/src/assets/logo.png -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_API_SITE_URL=https://your.server/pivexemption 3 | BASE_URL=https://your.server/pivexemption 4 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: process.env.NODE_ENV === 'production' 3 | ? 'https://your.server/pivexemption' 4 | : '/' 5 | } 6 | -------------------------------------------------------------------------------- /public/api/user.php: -------------------------------------------------------------------------------- 1 | $v) { 14 | $centers[] = array( 15 | "value" => $k, 16 | "text" => $v['label'] 17 | ); 18 | } 19 | 20 | #print($user); 21 | print(json_encode($centers)); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivexemption", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.18.0", 11 | "bootstrap-vue": "^2.0.0-rc.14", 12 | "jquery": "^1.9.1", 13 | "vue": "^2.6.6", 14 | "vue-router": "^3.0.1" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "^3.5.0", 18 | "@vue/cli-service": "^3.5.0", 19 | "vue-template-compiler": "^2.5.21" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /public/api/computers.php: -------------------------------------------------------------------------------- 1 | user->links->computers; 21 | print(json_encode($userComputers)); 22 | } 23 | } catch (Exception $e) { 24 | print[]; 25 | } 26 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | pivexemption 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/api/exemptions.php: -------------------------------------------------------------------------------- 1 | computer_extension_attribute->input_type->popup_choices; 14 | print(json_encode(array_values(array_filter($values, function($v) { 15 | return ( 16 | strtoupper(substr($v, 0, 8)) != "TEMP SYS" && 17 | strtoupper(substr($v, 0, 3)) != "RBD" 18 | ); 19 | })))); 20 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import axios from 'axios' 5 | import BootstrapVue from 'bootstrap-vue' 6 | import 'bootstrap/dist/css/bootstrap.css' 7 | import 'bootstrap-vue/dist/bootstrap-vue.css' 8 | 9 | Vue.config.productionTip = false 10 | Vue.use(BootstrapVue) 11 | Vue.prototype.$http = axios.create({ 12 | baseURL: process.env.VUE_APP_API_SITE_URL, 13 | withCredentials: false, 14 | headers: { 15 | // 'Access-Control-Allow-Origin': '*', 16 | // 'Content-Type': 'application/json', 17 | }, 18 | }) 19 | 20 | new Vue({ 21 | router, 22 | render: h => h(App) 23 | }).$mount('#app') 24 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 41 | -------------------------------------------------------------------------------- /src/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 24 | -------------------------------------------------------------------------------- /src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /public/api/exemption_check.php: -------------------------------------------------------------------------------- 1 | $val) { 15 | if ($val['name'] === $name) { 16 | return $key; 17 | } 18 | } 19 | return null; 20 | } 21 | 22 | 23 | function EA_search_array($needle, $haystack) { 24 | 25 | if (in_array($needle, $haystack)) { 26 | return true; 27 | } 28 | 29 | foreach ($haystack as $item) { 30 | if (is_array($item) && search_array($needle, $item)) 31 | return true; 32 | } 33 | 34 | return false; 35 | 36 | } 37 | 38 | $ea_check = callAPI($center, 'GET', "computers/id/$computerID/subset/extension_attributes", false)->computer->extension_attributes; 39 | 40 | 41 | try { 42 | $ea_array = json_decode(json_encode($ea_check),true); 43 | 44 | $array_value = searchForEA("Smartcard-exempt", $ea_array); 45 | 46 | if (EA_search_array("0", $ea_array[$array_value])) { 47 | echo 'Not Exempt'; 48 | } else { 49 | echo 'Exempted'; 50 | } 51 | 52 | // print(json_encode($ea_array[$array_value])); 53 | } catch (exception $e) { 54 | print "[]"; 55 | } -------------------------------------------------------------------------------- /public/api/settings.inc: -------------------------------------------------------------------------------- 1 | array( 16 | "domain" => "your.server1.com", 17 | "creds" => $PROD_CRED, 18 | "dev" => false, 19 | "label" => "Jamf Server 1" 20 | ), 21 | "JP2" => array( 22 | "domain" => "your.server2.com", 23 | "creds" => $PROD_CRED, 24 | "dev" => false, 25 | "label" => "Jamf Server 2" 26 | ), 27 | "JP3" => array( 28 | "domain" => "your.server3.com", 29 | "creds" => $PROD_CRED, 30 | "dev" => false, 31 | "label" => "Jamf Server 3" 32 | ), 33 | "DEV" => array( 34 | "domain" => "your.dev.server.com", 35 | "creds" => $DEV_CRED, 36 | "dev" => true, 37 | "label" => "Dev Server" 38 | ), 39 | )); 40 | 41 | function getCenter(&$site) { 42 | if (DEV_MODE) { 43 | return CENTERS['DEV']; 44 | } 45 | 46 | if (strpos($site, '#') > 0) { 47 | $split = explode('#', $site); 48 | $center = CENTERS[$split[0]]; 49 | $site = $split[1]; 50 | return $center; 51 | } 52 | 53 | return CENTERS[explode('-', $site)[0]]; 54 | } -------------------------------------------------------------------------------- /public/api/includes/jamf.inc: -------------------------------------------------------------------------------- 1 | params->id; 11 | $selectedCenter = $_POST->params->selectedCenter; 12 | $reason = $_POST->params->selectedReason; 13 | 14 | # Replace the path below with a location to save the logs 15 | $LOG_PATH = 'e:\pivexemption\exemptions.log'; 16 | 17 | $center = getCenter($selectedCenter); 18 | $selectedCenter = $center[domain]; 19 | 20 | error_log("---Start New Exemption---\n", 3, $LOG_PATH); 21 | error_log("Center Selected: " . $selectedCenter . "\n", 3, $LOG_PATH); 22 | error_log("Jamf Admin: " . $user . "\n", 3, $LOG_PATH); 23 | error_log("Reason Selected: " . $reason . "\n", 3, $LOG_PATH); 24 | error_log("---End New Exemption---\n", 3, $LOG_PATH); 25 | 26 | # Replace the below variables with the ones used in your jamf pro 27 | # EA Names 28 | $PIV_EXEMPTION_EA = 'Smartcard-exempt'; 29 | $PIV_EXEMPTION_REASON = 'Smartcard-exempt - Justification Category'; 30 | $PIV_EXEMPTION_DOCUMENTATION = 'Smartcard-exempt - Reason'; 31 | 32 | $pivDocumentation = "Jamf Admin: ${user}"; 33 | 34 | # This is the data that gets used in the API call. It flips the EA from a 0 to a 1 which triggers the Exemption 35 | $pivXML = "$PIV_EXEMPTION_EAInteger1$PIV_EXEMPTION_REASONString$reason$PIV_EXEMPTION_DOCUMENTATIONString$pivDocumentation"; 36 | 37 | callAPI($center, 'PUT', "computers/id/${computerID}/subset/extension_attributes", $pivXML); 38 | -------------------------------------------------------------------------------- /Jamf Scripts/ExemptionRemoval.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################# 4 | # Enforce Timed PIV Exemption - Remove 5 | # Name: checkDate.sh 6 | # Joshua Harvey | Oct 2019 7 | # josh@macjeezy.com 8 | ################################################# 9 | 10 | if [[ -z $4 ]]; then 11 | echo "Missing Server Info" 12 | exit 1 13 | else 14 | jssURL="$4" 15 | fi 16 | 17 | udid=$(system_profiler SPHardwareDataType | grep UUID | awk '{print $3}') 18 | PIV_EXEMPTION_EA="Smartcard-exempt" 19 | PIV_EXEMPTION_REASON="Smartcard-exempt - Justification Category" 20 | PIV_EXEMPTION_DOCUMENTATION="Smartcard-exempt - Reason" 21 | 22 | 23 | function DecryptString() { 24 | # Usage: ~$ DecryptString "Encrypted String" "Salt" "Passphrase" 25 | echo "${1}" | /usr/bin/openssl enc -aes256 -d -a -A -S "${2}" -k "${3}" 26 | } 27 | 28 | apiUser=$(DecryptString "$5" '' '') 29 | apiPass=$(DecryptString "$6" '' '') 30 | 31 | 32 | echo "Starting EA Section" 33 | 34 | # curl command to update ea 35 | setEAStatus() { 36 | echo "Resetting Extension Attributes" 37 | curl -sk -u $apiUser:"$apiPass" -X "PUT" "https://$jssURL/JSSResource/computers/udid/$udid/subset/extension_attributes" \ 38 | -H "Content-Type: application/xml" \ 39 | -H "Accept: application/xml" \ 40 | -d "$PIV_EXEMPTION_EAInteger0$PIV_EXEMPTION_REASONString""$PIV_EXEMPTION_DOCUMENTATIONString""" 41 | } 42 | # Set Status 43 | setEAStatus 44 | 45 | if [[ -e "/Library/Scripts/checkDate.sh" ]]; then 46 | echo "Removing Date Check Script" 47 | rm -f "/Library/Scripts/checkDate.sh" 48 | fi 49 | 50 | if [[ -e "/Users/Shared/time.log" ]]; then 51 | echo "Removing Exemption Receipt" 52 | rm -f "/Users/Shared/time.log" 53 | fi 54 | 55 | if [[ -e "/Library/LaunchDaemons/com.jamf.exemptioncheck.plist" ]]; then 56 | echo "Stopping Launchd item" 57 | sudo launchctl stop /Library/LaunchDaemons/com.jamf.exemptioncheck.plist 58 | echo "Unloading Launchd item" 59 | sudo launchctl unload /Library/LaunchDaemons/com.jamf.exemptioncheck.plist 60 | echo "Removing Launchd item" 61 | sudo rm -f "/Library/LaunchDaemons/com.jamf.exemptioncheck.plist" 62 | fi 63 | 64 | echo "Exemption files have been unloaded and removed. The computer should now be PIV Enforced." 65 | 66 | exit 0 -------------------------------------------------------------------------------- /Jamf Scripts/StartExemption.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################# 4 | # Enforce Timed PIV Exemption 5 | # Create Exemption Check Files 6 | # Joshua Harvey | Oct 2019 7 | # josh@macjeezy.com 8 | ################################################# 9 | 10 | # Create receipt for when the exemption expires 11 | EXEMPTION_END=$(date -v +2d +"%m-%d") 12 | 13 | echo "$EXEMPTION_END" > /Users/Shared/time.log 14 | chflags hidden /Users/Shared/time.log 15 | 16 | echo "PIV Exemption End Date: $EXEMPTION_END" 17 | 18 | # Create Date Check Script 19 | echo "Creating Exemption Check Script" 20 | /bin/cat > "/Library/Scripts/checkDate.sh" << 'Check_Date' 21 | #!/bin/bash 22 | 23 | ################################################# 24 | # Enforce Timed PIV Exemption - Check 25 | # Name: checkDate.sh 26 | # Joshua Harvey | Oct 2019 27 | # josh@macjeezy.com 28 | ################################################# 29 | 30 | GET_TIME=$(cat /Users/Shared/time.log) 31 | currTime=$(date +"%m-%d") 32 | 33 | removeExemption() { 34 | echo "Starting Exemption Removal via Jamf Policy" 35 | sudo /usr/local/bin/jamf policy -trigger removeExemption 36 | } 37 | 38 | if [[ "$currTime" = "$GET_TIME" ]]; then 39 | echo "Exemption ($GET_TIME) expires today. Time to remove" 40 | removeExemption 41 | elif [[ "$currTime" > "$GET_TIME" ]]; then 42 | echo "Exemption ($GET_TIME) has expired. Time to remove" 43 | removeExemption 44 | else 45 | echo "Exemption is still valid until $GET_TIME" 46 | fi 47 | Check_Date 48 | 49 | /bin/chmod a+x /Library/Scripts/checkDate.sh 50 | /usr/bin/chflags hidden /Library/Scripts/checkDate.sh 51 | echo "Exemption Check Script Created" 52 | 53 | # Create launchd item to run each day to trigger the script. This is kicked off at 10am 54 | echo "Creating Launchd item" 55 | /bin/cat > "/Library/LaunchDaemons/com.jamf.exemptioncheck.plist" << 'Exemption_Check' 56 | 57 | 58 | 59 | 60 | Label 61 | com.jamf.exemptioncheck.plist 62 | ProgramArguments 63 | 64 | sh 65 | /Library/Scripts/checkDate.sh 66 | 67 | RunAtLoad 68 | 69 | StartCalendarInterval 70 | 71 | Hour 72 | 10 73 | 74 | StandardErrorPath 75 | /Library/Scripts/error.log 76 | 77 | 78 | Exemption_Check 79 | 80 | sudo /bin/chmod 0644 "/Library/LaunchDaemons/com.jamf.exemptioncheck.plist" 81 | sudo /bin/launchctl load "/Library/LaunchDaemons/com.jamf.exemptioncheck.plist" 82 | sudo /bin/launchctl start "/Library/LaunchDaemons/com.jamf.exemptioncheck.plist" 83 | echo "Launchd item created and loaded" 84 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 125 | 126 | 250 | 251 | 272 | --------------------------------------------------------------------------------