diff --git a/scripts/automation/Radius/Functions/Private/CertDeployment/Deploy-UserCertificate.ps1 b/scripts/automation/Radius/Functions/Private/CertDeployment/Deploy-UserCertificate.ps1 index afdec2eed..abb88d655 100644 --- a/scripts/automation/Radius/Functions/Private/CertDeployment/Deploy-UserCertificate.ps1 +++ b/scripts/automation/Radius/Functions/Private/CertDeployment/Deploy-UserCertificate.ps1 @@ -9,6 +9,10 @@ function Deploy-UserCertificate { [Parameter(HelpMessage = 'When specified, this parameter will invoke commands on systems associated to the user from the "userObject" parameter')] [bool] $forceInvokeCommands, + # when specified will generated a new command for the given user + [Parameter(HelpMessage = 'When specified, this parameter will generate new commands for the user from the "userObject" parameter')] + [bool] + $forceGenerateCommands, # prompt replace existing certificate [Parameter(HelpMessage = 'When specified, this parameter will prompt for user imput and ask if generated commands should be invoked on associated systems')] [switch] @@ -16,6 +20,23 @@ function Deploy-UserCertificate { ) begin { + $workToBeDone = [PSCustomObject]@{ + remainingMacOSDevices = $null + remainingWindowsSDevices = $null + removeQueuedCommands = $null + removeCommands = $null + forceGenerateMacOSCommands = $false + forceGenerateWindowsCommands = $false + macOSCommandID = $null + windowsCommandID = $null + commandIDsToRemove = New-Object System.Collections.ArrayList + commandQueueIDsToRemove = New-Object System.Collections.ArrayList + commandQueueIDsDuplicates = New-Object System.Collections.ArrayList + } + + $status_commandGenerated = $false + $result_deployed = $false + switch ($forceInvokeCommands) { $true { $invokeCommands = $true @@ -24,6 +45,16 @@ function Deploy-UserCertificate { $invokeCommands = $false } } + switch ($forceGenerateCommands) { + $true { + $workToBeDone.forceGenerateMacOSCommands = $true + $workToBeDone.forceGenerateWindowsCommands = $true + } + $false { + $workToBeDone.forceGenerateMacOSCommands = $false + $workToBeDone.forceGenerateWindowsCommands = $false + } + } switch ($prompt) { $true { $invokeCommandsChoice = Get-ResponsePrompt -message "Would you like to invoke commands after they've been generated?" @@ -41,21 +72,26 @@ function Deploy-UserCertificate { process { foreach ($user in $userObject) { + ## first determine if the local cert sha is the same as the cert sha in the admin console: - #### Begin removal of queued commands + existing command: - # remove commands + # Get the commands for this user: $radiusCommandsByUser = Get-CommandByUsername -username $user.username - foreach ($command in $radiusCommandsByUser) { - Remove-JcSdkCommand -Id $command.id | Out-Null + if ($radiusCommandsByUser) { + $macOSCommands = ($radiusCommandsByUser | Where-Object { $_.Name -match "MacOSX" }) + $windowsOSCommands = ($radiusCommandsByUser | Where-Object { $_.Name -match "Windows" }) } - # remove queued commands + + # Get the queued commands for the user $queuedRadiusCommandsByUser = Get-queuedCommandByUser -username $user.username - foreach ($queuedCommand in $queuedRadiusCommandsByUser) { - Clear-JCQueuedCommand -workflowId $queuedCommand.id | Out-Null + if ($queuedRadiusCommandsByUser) { + $windowsQueuedCommands = $queuedRadiusCommandsByUser | Where-Object { $_.name -match "Windows" } + $macOSQueuedCommands = $queuedRadiusCommandsByUser | Where-Object { $_.name -match "macOS" } + } else { + $windowsQueuedCommands = $null + $macOSQueuedCommands = $null } - # now clear out the user command associations from users.json - $User.commandAssociations = @() - #### End removal of queued commands + existing command + + # Get the users certificate Details: # Get certificate and zip to upload to Commands $userCertFiles = Get-ChildItem -Path "$JCScriptRoot/UserCerts" -Filter "$($user.userName)-*" # set crt and pfx filepaths @@ -65,21 +101,177 @@ function Deploy-UserCertificate { $userPfxZip = "$JCScriptRoot/UserCerts/$($user.userName)-client-signed.zip" # get certInfo for commands: $certInfo = Get-CertInfo -UserCerts -username $user.username + # Determine if the commands have matching SHA1 values: + + + if ($macOSCommands) { + # verify the certs match for macOS systems: + foreach ($maOSRadiusCommand in $macOSCommands) { + # if we want to forceGenerate new commands, add all commands to the remove list + switch ($workToBeDone.forceGenerateMacOSCommands) { + $true { + # add the command to the list to remove + $workToBeDone.commandIDsToRemove.Add($maOSRadiusCommand.Id) | Out-Null + + } + $false { + if ((-Not $workToBeDone.macOSCommandID) -AND ($maOSRadiusCommand.trigger -eq $certInfo.sha1)) { + # set the existing command IDs: + # There is a potential for this to be a bug, if duplicate commands with the same SHA1 trigger exist, this code just selects the first one and removes the second in the next iteration + $workToBeDone.macOSCommandID = ($radiusCommandsByUser | Where-Object { $_.Name -match "MacOSX" }).Id | Select-Object -First 1 + } elseif (($workToBeDone.macOSCommandID) -AND ($maOSRadiusCommand.trigger -eq $certInfo.sha1)) { + # add the duplicate command to the list to remove + $workToBeDone.commandIDsToRemove.Add($maOSRadiusCommand.Id) | Out-Null + } else { + # add the command to the list to remove + $workToBeDone.commandIDsToRemove.Add($maOSRadiusCommand.Id) | Out-Null + } + } + } + } + } else { + $workToBeDone.macOSCommandID = $null + } + if ($windowsOSCommands) { + # verify the certs match for windows systems: + foreach ($windowsOSRadiusCommands in $windowsOSCommands) { + switch ($workToBeDone.forceGenerateWindowsCommands) { + $true { + # add the command to the list to remove + $workToBeDone.commandIDsToRemove.Add($windowsOSRadiusCommands.Id) | Out-Null + } + $false { + if ((-Not $workToBeDone.windowsCommandID) -AND ($windowsOSRadiusCommands.trigger -eq $certInfo.sha1)) { + # set the existing command IDs: + # There is a potential for this to be a bug, if duplicate commands with the same SHA1 trigger exist, this code just selects the first one and removes the second in the next iteration + $workToBeDone.windowsCommandID = ($radiusCommandsByUser | Where-Object { $_.Name -match "Windows" }).Id | Select-Object -First 1 + } elseif (($workToBeDone.windowsCommandID) -AND ($windowsOSRadiusCommands.trigger -eq $certInfo.sha1)) { + # add the duplicate command to the list to remove + $workToBeDone.commandIDsToRemove.Add($windowsOSRadiusCommands.Id) | Out-Null + } else { + # add the command to the list to remove + $workToBeDone.commandIDsToRemove.Add($windowsOSRadiusCommands.Id) | Out-Null + } + } + } + } + } else { + $workToBeDone.windowsCommandID = $null + } + + + + if ($windowsQueuedCommands) { + if ($workToBeDone.windowsCommandID) { + # Get queued commands that are from a previous command; delete those + foreach ($queuedCommand in $windowsQueuedCommands) { + if ($queuedCommand.command -eq $workToBeDone.windowsCommandID) { + # TODO: nothing to do here? + $workToBeDone.commandQueueIDsDuplicates.Add($queuedCommand.Id) | Out-Null + } else { + # if the queued commands does not match the cert command, remove all these queued commands + $workToBeDone.commandQueueIDsToRemove.Add($queuedCommand.Id) | Out-Null + } + } + } else { + foreach ($queuedCommand in $windowsQueuedCommands) { + # if there are commands in queue for the user but no matching cert command, remove all the items in the queue + $workToBeDone.commandQueueIDsToRemove.Add($queuedCommand.Id) | Out-Null + } + } + } + if ($macOSQueuedCommands) { + if ($workToBeDone.windowsCommandID) { + + foreach ($queuedCommand in $macOSQueuedCommands) { + if ($queuedCommand.command -eq $workToBeDone.macOSCommandID) { + # TODO: nothing to do here? + $workToBeDone.commandQueueIDsDuplicates.Add($queuedCommand.Id) | Out-Null + } else { + # if the queued commands does not match the cert command, remove all these queued commands + $workToBeDone.commandQueueIDsToRemove.Add($queuedCommand.Id) | Out-Null + } + } + } else { + foreach ($queuedCommand in $macOSQueuedCommands) { + # if there are commands in queue for the user but no matching cert command, remove all the items in the queue + $workToBeDone.commandQueueIDsToRemove.Add($queuedCommand.Id) | Out-Null + } + } + } + + # remove the commands: + if ($workToBeDone.commandIDsToRemove) { + foreach ($commandIDToRemove in $workToBeDone.commandIDsToRemove) { + Remove-JcSdkCommand -Id $commandIDToRemove | Out-Null + } + } + # remove queued commands: + if ($workToBeDone.commandQueueIDsToRemove) { + foreach ($commandQueueIDToRemove in $workToBeDone.commandQueueIDsToRemove) { + Clear-JCQueuedCommand -workflowId $commandQueueIDToRemove | Out-Null + } + } + # remove queued commands: + if ($workToBeDone.commandQueueIDsDuplicates) { + foreach ($commandQueueIDToRemove in $workToBeDone.commandQueueIDsDuplicates) { + Clear-JCQueuedCommand -workflowId $commandQueueIDToRemove | Out-Null + } + } + ## determine the systems that need the cert + try { + $userCertHashData = $Global:JCRCertHash["$($user.certInfo.sha1)"] + + } catch { + $userCertHashData = $null + } + if ($userCertHashData) { + # set the remaining systems that don't have the cert: + # macOS + $workToBeDone.remainingMacOSDevices = $user.systemAssociations | Where-Object { ($_.systemID -notin $userCertHashData.systemId ) -And ($_.osFamily) -eq "macOS" } + # windows + $workToBeDone.remainingWindowsSDevices = $user.systemAssociations | Where-Object { ($_.systemID -notin $userCertHashData.systemId ) -And ($_.osFamily) -eq "Windows" } + + # if there's work to be done but not a valid cert command, generate the command + if (($workToBeDone.remainingMacOSDevices) -AND (-Not $workToBeDone.macOSCommandID)) { + $workToBeDone.forceGenerateMacOSCommands = $true + } + # if there's work to be done but not a valid cert command, generate the command + if (($workToBeDone.remainingWindowsSDevices) -AND (-Not $workToBeDone.windowsCommandID)) { + $workToBeDone.forceGenerateWindowsCommands = $true + } + } else { + # set the remaining devices to the association list from users.json + # macOS + $workToBeDone.remainingMacOSDevices = $user.systemAssociations | Where-Object { ($_.osFamily) -eq "macOS" } + # Windows + $workToBeDone.remainingWindowsSDevices = $user.systemAssociations | Where-Object { ($_.osFamily) -eq "Windows" } + # if there's work to be done but not a valid cert command, generate the command + if (($workToBeDone.remainingMacOSDevices) -AND (-Not $workToBeDone.macOSCommandID)) { + $workToBeDone.forceGenerateMacOSCommands = $true + } + # if there's work to be done but not a valid cert command, generate the command + if (($workToBeDone.remainingWindowsSDevices) -AND (-Not $workToBeDone.windowsCommandID)) { + $workToBeDone.forceGenerateWindowsCommands = $true + } + } + # now clear out the user command associations from users.json + $user.commandAssociations = @() - # validate the certIfo required for installing commands: If (-Not $certInfo) { Write-host "$($user.username) did not have a certificate generated" $status_commandGenerated = $false $result_deployed = $false continue } else { - # explicitly validate that the serial number + # explicitly validate that the subject header exists if (-Not $certInfo.subject) { Write-host "$($user.username) has a certificate file but no subject was found" $status_commandGenerated = $false $result_deployed = $false continue } + # explicitly validate that the serial number exists if (-Not $certInfo.serial) { Write-host "$($user.username) has a certificate file but no serial number was found" $status_commandGenerated = $false @@ -88,58 +280,48 @@ function Deploy-UserCertificate { } } - # determine certType - switch ($JCR_CERT_TYPE) { - 'EmailSAN' { - # set cert identifier to SAN email of cert - $sanID = Invoke-Expression "$JCR_OPENSSL x509 -in $($userCrt) -ext subjectAltName -noout" - $regex = 'email:(.*?)$' - $JCR_SUBJECT_HEADERSMatch = Select-String -InputObject "$($sanID)" -Pattern $regex - $certIdentifier = $JCR_SUBJECT_HEADERSMatch.matches.Groups[1].value - # in macOS search user certs by email - $macCertSearch = 'e' - } - 'EmailDN' { - # Else set cert identifier to email of cert subject - $regex = 'emailAddress=(.*?)$' - $JCR_SUBJECT_HEADERSMatch = Select-String -InputObject "$($certInfo.Subject)" -Pattern $regex - $certIdentifier = $JCR_SUBJECT_HEADERSMatch.matches.Groups[1].value - # in macOS search user certs by email - $macCertSearch = 'e' - } - 'UsernameCn' { - # if username just set cert identifier to username - $certIdentifier = $($user.userName) - # in macOS search user certs by common name (username) - $macCertSearch = 'c' + if (($workToBeDone.forcegenerateMacOSCommands) -OR ($workToBeDone.forceGenerateWindowsCommands)) { + # determine certType + switch ($JCR_CERT_TYPE) { + 'EmailSAN' { + # set cert identifier to SAN email of cert + $sanID = Invoke-Expression "$JCR_OPENSSL x509 -in $($userCrt) -ext subjectAltName -noout" + $regex = 'email:(.*?)$' + $JCR_SUBJECT_HEADERSMatch = Select-String -InputObject "$($sanID)" -Pattern $regex + $certIdentifier = $JCR_SUBJECT_HEADERSMatch.matches.Groups[1].value + # in macOS search user certs by email + $macCertSearch = 'e' + } + 'EmailDN' { + # Else set cert identifier to email of cert subject + $regex = 'emailAddress=(.*?)$' + $JCR_SUBJECT_HEADERSMatch = Select-String -InputObject "$($certInfo.Subject)" -Pattern $regex + $certIdentifier = $JCR_SUBJECT_HEADERSMatch.matches.Groups[1].value + # in macOS search user certs by email + $macCertSearch = 'e' + } + 'UsernameCn' { + # if username just set cert identifier to username + $certIdentifier = $($user.userName) + # in macOS search user certs by common name (username) + $macCertSearch = 'c' + } } + # Create the zip + Compress-Archive -Path $userPfx -DestinationPath $userPfxZip -CompressionLevel NoCompression -Force } - # Create the zip - Compress-Archive -Path $userPfx -DestinationPath $userPfxZip -CompressionLevel NoCompression -Force - # Find OS of System - $systemTypes = $user.systemAssociations.osFamily | sort | Get-Unique - foreach ($systemType in $systemTypes) { - switch ($systemType) { - 'macOS' { - # Get the macOS system ids - $systemIds = (Get-SystemsThatNeedCertWork -userData $user -osType "macOS") - if ($systemIds.count -eq 0) { - continue - } - # Check to see if previous commands exist - $Command = Get-JCCommand -name "RadiusCert-Install:$($user.userName):MacOSX" - if ($Command.Count -ge 1) { - # $confirmation = Write-Host "[status] RadiusCert-Install:$($user.userName):MacOSX command already exists, skipping..." - $status_commandGenerated = $false - continue - } - # Create new Command and upload the signed pfx - try { - $CommandBody = @{ - Name = "RadiusCert-Install:$($user.userName):MacOSX" - Command = @" + if ($workToBeDone.forcegenerateMacOSCommands) { + # Get the macOS system ids + $systemIds = (Get-SystemsThatNeedCertWork -userData $user -osType "macOS") + if ($systemIds.count -gt 0) { + + # Create new Command and upload the signed pfx + try { + $CommandBody = @{ + Name = "RadiusCert-Install:$($user.userName):MacOSX" + Command = @" unzip -o /tmp/$($user.userName)-client-signed.zip -d /tmp chmod 755 /tmp/$($user.userName)-client-signed.pfx currentUser=`$(/usr/bin/stat -f%Su /dev/console) @@ -152,413 +334,458 @@ caseMatchOrigValue=`$(shopt -p nocasematch; true) shopt -s nocasematch userCompare="$($user.localUsername)" if [[ "`$currentUser" == "`$userCompare" ]]; then - # restore case match type - `$caseMatchOrigValue - certs=`$(security find-certificate -a -$($macCertSearch) "$($certIdentifier)" -Z /Users/$($user.localUsername)/Library/Keychains/login.keychain) - regexSHA='SHA-1 hash: ([0-9A-F]{5,40})' - regexSN='"snbr"=0x([0-9A-F]{5,40})' - global_rematch() { - # Set local variables - local s=`$1 regex=`$2 - # While string matches regex expression - while [[ `$s =~ `$regex ]]; do - # Echo out the match - echo "`${BASH_REMATCH[1]}" - # Remove the string - s=`${s#*"`${BASH_REMATCH[1]}"} - done - } - # Save results - # Get Text Results - textSHA=`$(global_rematch "`$certs" "`$regexSHA") - # Set as array for SHA results - arraySHA=(`$textSHA) - # Get Text Results - textSN=`$(global_rematch "`$certs" "`$regexSN") - # Set as array for SN results - arraySN=(`$textSN) - # set import var - import=true - if [[ `${#arraySN[@]} == `${#arraySHA[@]} ]]; then - len=`${#arraySN[@]} - for (( i=0; i<`$len; i++ )); do - if [[ `$currentCertSN == `${arraySN[`$i]} ]]; then - echo "Found Cert: SN: `${arraySN[`$i]} SHA: `${arraySHA[`$i]}" - installedCertSN=`${arraySN[`$i]} - installedCertSHA=`${arraySHA[`$i]} - # if cert is installed, no need to update - import=false - else - echo "Removing previously installed radius cert:" - echo "SN: `${arraySN[`$i]} SHA: `${arraySHA[`$i]}" - security delete-certificate -Z "`${arraySHA[`$i]}" /Users/$($user.localUsername)/Library/Keychains/login.keychain - fi - done - - else - echo "array length mismatch, will not delete old certs" - fi +# restore case match type +`$caseMatchOrigValue +certs=`$(security find-certificate -a -$($macCertSearch) "$($certIdentifier)" -Z /Users/$($user.localUsername)/Library/Keychains/login.keychain) +regexSHA='SHA-1 hash: ([0-9A-F]{5,40})' +regexSN='"snbr"=0x([0-9A-F]{5,40})' +global_rematch() { + # Set local variables + local s=`$1 regex=`$2 + # While string matches regex expression + while [[ `$s =~ `$regex ]]; do + # Echo out the match + echo "`${BASH_REMATCH[1]}" + # Remove the string + s=`${s#*"`${BASH_REMATCH[1]}"} + done +} +# Save results +# Get Text Results +textSHA=`$(global_rematch "`$certs" "`$regexSHA") +# Set as array for SHA results +arraySHA=(`$textSHA) +# Get Text Results +textSN=`$(global_rematch "`$certs" "`$regexSN") +# Set as array for SN results +arraySN=(`$textSN) +# set import var +import=true +if [[ `${#arraySN[@]} == `${#arraySHA[@]} ]]; then + len=`${#arraySN[@]} + for (( i=0; i<`$len; i++ )); do + if [[ `$currentCertSN == `${arraySN[`$i]} ]]; then + echo "Found Cert: SN: `${arraySN[`$i]} SHA: `${arraySHA[`$i]}" + installedCertSN=`${arraySN[`$i]} + installedCertSHA=`${arraySHA[`$i]} + # if cert is installed, no need to update + import=false + else + echo "Removing previously installed radius cert:" + echo "SN: `${arraySN[`$i]} SHA: `${arraySHA[`$i]}" + security delete-certificate -Z "`${arraySHA[`$i]}" /Users/$($user.localUsername)/Library/Keychains/login.keychain + fi + done - if [[ `$import == true ]]; then - /bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security import /tmp/$($user.userName)-client-signed.pfx -x -k /Users/$($user.localUsername)/Library/Keychains/login.keychain -P $JCR_USER_CERT_PASS -T "/System/Library/SystemConfiguration/EAPOLController.bundle/Contents/Resources/eapolclient" - if [[ `$? -eq 0 ]]; then - echo "Import Success" - # get the SHA hash of the newly imported cert - installedCertSN=`$(/bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security find-certificate -$($macCertSearch) "$($certIdentifier)" -Z /Users/$($user.localUsername)/Library/Keychains/login.keychain | grep snbr | awk '{print `$1}' | sed 's/"snbr"=0x//g') - if [[ `$installedCertSN == `$currentCertSN ]]; then - installedCertSHA=`$(/bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security find-certificate -$($macCertSearch) "$($certIdentifier)" -Z /Users/$($user.localUsername)/Library/Keychains/login.keychain | grep SHA-1 | awk '{print `$3}') - fi +else + echo "array length mismatch, will not delete old certs" +fi - else - echo "import failed" - exit 4 +if [[ `$import == true ]]; then + /bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security import /tmp/$($user.userName)-client-signed.pfx -x -k /Users/$($user.localUsername)/Library/Keychains/login.keychain -P $JCR_USER_CERT_PASS -T "/System/Library/SystemConfiguration/EAPOLController.bundle/Contents/Resources/eapolclient" + if [[ `$? -eq 0 ]]; then + echo "Import Success" + # get the SHA hash of the newly imported cert + installedCertSN=`$(/bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security find-certificate -$($macCertSearch) "$($certIdentifier)" -Z /Users/$($user.localUsername)/Library/Keychains/login.keychain | grep snbr | awk '{print `$1}' | sed 's/"snbr"=0x//g') + if [[ `$installedCertSN == `$currentCertSN ]]; then + installedCertSHA=`$(/bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security find-certificate -$($macCertSearch) "$($certIdentifier)" -Z /Users/$($user.localUsername)/Library/Keychains/login.keychain | grep SHA-1 | awk '{print `$3}') fi + else - echo "cert already imported" + echo "import failed" + exit 4 fi +else + echo "cert already imported" +fi - # check if the cert secruity preference is set: - IFS=';' read -ra network <<< "`$JCR_NETWORKSSID" - for i in "`${network[@]}"; do - echo "begin setting network SSID: `$i" - if /bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security get-identity-preference -s "com.apple.network.eap.user.identity.wlan.ssid.`$i" -Z "`$installedCertSHA"; then - echo "it was already set" +# check if the cert secruity preference is set: +IFS=';' read -ra network <<< "`$JCR_NETWORKSSID" +for i in "`${network[@]}"; do + echo "begin setting network SSID: `$i" + if /bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security get-identity-preference -s "com.apple.network.eap.user.identity.wlan.ssid.`$i" -Z "`$installedCertSHA"; then + echo "it was already set" + else + echo "certificate not linked from SSID: `$i to certSN: `$currentCertSN, setting now" + /bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security set-identity-preference -s "com.apple.network.eap.user.identity.wlan.ssid.`$i" -Z "`$installedCertSHA" + if [[ `$? -eq 0 ]]; then + echo "SSID: `$i and certificate linked" else - echo "certificate not linked from SSID: `$i to certSN: `$currentCertSN, setting now" - /bin/launchctl asuser "`$currentUserUID" sudo -iu "`$currentUser" /usr/bin/security set-identity-preference -s "com.apple.network.eap.user.identity.wlan.ssid.`$i" -Z "`$installedCertSHA" - if [[ `$? -eq 0 ]]; then - echo "SSID: `$i and certificate linked" - else - echo "Could not associate SSID: `$i and certifiacte" - fi + echo "Could not associate SSID: `$i and certifiacte" fi - done - - # print results - echo "################## Cert Install Results ##################" - echo "Installed Cert SN: `$installedCertSN" - echo "Installed Cert SHA1: `$installedCertSHA" - echo "##########################################################" - - # Finally clean up files - if [[ -f "/tmp/$($user.userName)-client-signed.zip" ]]; then - echo "Removing Temp Zip" - rm "/tmp/$($user.userName)-client-signed.zip" - fi - if [[ -f "/tmp/$($user.userName)-client-signed.pfx" ]]; then - echo "Removing Temp Pfx" - rm "/tmp/$($user.userName)-client-signed.pfx" fi +done + +# print results +echo "################## Cert Install Results ##################" +echo "Installed Cert SN: `$installedCertSN" +echo "Installed Cert SHA1: `$installedCertSHA" +echo "##########################################################" + +# Finally clean up files +if [[ -f "/tmp/$($user.userName)-client-signed.zip" ]]; then + echo "Removing Temp Zip" + rm "/tmp/$($user.userName)-client-signed.zip" +fi +if [[ -f "/tmp/$($user.userName)-client-signed.pfx" ]]; then + echo "Removing Temp Pfx" + rm "/tmp/$($user.userName)-client-signed.pfx" +fi - # update si table - # The conf file is JSON and can be parsed using JSON.parse() in a supported language. - conf="`$(cat /opt/jc/jcagent.conf)" - regex='\"systemKey\":\"([a-zA-Z0-9_]+)\"' - if [[ `${conf} =~ `$regex ]] ; then - systemKey="`${BASH_REMATCH[1]}" - fi - # get the certUUID - regex='\"certuuid\":\"([a-zA-Z0-9_]+)\"' - if [[ `${conf} =~ `$regex ]] ; then - certUUID="`${BASH_REMATCH[1]}" - fi +# update si table +# The conf file is JSON and can be parsed using JSON.parse() in a supported language. +conf="`$(cat /opt/jc/jcagent.conf)" +regex='\"systemKey\":\"([a-zA-Z0-9_]+)\"' +if [[ `${conf} =~ `$regex ]] ; then +systemKey="`${BASH_REMATCH[1]}" +fi +# get the certUUID +regex='\"certuuid\":\"([a-zA-Z0-9_]+)\"' +if [[ `${conf} =~ `$regex ]] ; then +certUUID="`${BASH_REMATCH[1]}" +fi - # get the key /cert locations - keyLocation="/opt/jc/client.key" - certLocation="/opt/jc/client.crt" - caCertLocation="/opt/jc/ca.crt" +# get the key /cert locations +keyLocation="/opt/jc/client.key" +certLocation="/opt/jc/client.crt" +caCertLocation="/opt/jc/ca.crt" - # Get json certificate data from osquery and add missing windows certificate columns - certJson=`$(/opt/jc/bin/jcosqueryi --json "select *, '' AS sid, '' AS store, '' AS store_id, '' AS store_location, '' AS username from certificates;") +# Get json certificate data from osquery and add missing windows certificate columns +certJson=`$(/opt/jc/bin/jcosqueryi --json "select *, '' AS sid, '' AS store, '' AS store_id, '' AS store_location, '' AS username from certificates;") - # post - curl --cert `$certLocation --key `$keyLocation --cacert `$caCertLocation \ - -X POST 'https://agent.jumpcloud.com/systeminsights/snapshots/certificates' \ - -H "x-system-id: `$systemKey" \ - -H "x-ssl-client-dn: /CN=`$certUUID/O=JumpCloud" \ - -H 'Content-Type: application/json' \ - --data-raw '{ - "data": '"`$certJson"' - }' +# post +curl --cert `$certLocation --key `$keyLocation --cacert `$caCertLocation \ +-X POST 'https://agent.jumpcloud.com/systeminsights/snapshots/certificates' \ +-H "x-system-id: `$systemKey" \ +-H "x-ssl-client-dn: /CN=`$certUUID/O=JumpCloud" \ +-H 'Content-Type: application/json' \ +--data-raw '{ + "data": '"`$certJson"' +}' else - # restore case match type - `$caseMatchOrigValue - echo "Current logged in user, `$currentUser, does not match expected certificate user. Please ensure $($user.localUsername) is signed in and retry" - # Finally clean up files - if [[ -f "/tmp/$($user.userName)-client-signed.zip" ]]; then - echo "Removing Temp Zip" - rm "/tmp/$($user.userName)-client-signed.zip" - fi - if [[ -f "/tmp/$($user.userName)-client-signed.pfx" ]]; then - echo "Removing Temp Pfx" - rm "/tmp/$($user.userName)-client-signed.pfx" - fi - exit 4 +# restore case match type +`$caseMatchOrigValue +echo "Current logged in user, `$currentUser, does not match expected certificate user. Please ensure $($user.localUsername) is signed in and retry" +# Finally clean up files +if [[ -f "/tmp/$($user.userName)-client-signed.zip" ]]; then + echo "Removing Temp Zip" + rm "/tmp/$($user.userName)-client-signed.zip" +fi +if [[ -f "/tmp/$($user.userName)-client-signed.pfx" ]]; then + echo "Removing Temp Pfx" + rm "/tmp/$($user.userName)-client-signed.pfx" +fi +exit 4 fi "@ - launchType = "trigger" - User = "000000000000000000000000" - trigger = "RadiusCertInstall" - commandType = "mac" - timeout = 600 - TimeToLiveSeconds = 864000 - files = (New-JCCommandFile -certFilePath $userPfxZip -FileName "$($user.userName)-client-signed.zip" -FileDestination "/tmp/$($user.userName)-client-signed.zip") - } - $NewCommand = New-JcSdkCommand @CommandBody - - # Find newly created command and add system as target - $Command = Get-JCCommand -name "RadiusCert-Install:$($user.userName):MacOSX" - $systemIds | ForEach-Object { Set-JcSdkCommandAssociation -CommandId:("$($Command._id)") -Op 'add' -Type:('system') -Id:("$($_.systemId)") | Out-Null } - } catch { - $status_commandGenerated = $false - # throw $_ + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($certInfo.sha1)" + commandType = "mac" + timeout = 600 + TimeToLiveSeconds = 864000 + files = (New-JCCommandFile -certFilePath $userPfxZip -FileName "$($user.userName)-client-signed.zip" -FileDestination "/tmp/$($user.userName)-client-signed.zip") } + $NewCommand = New-JcSdkCommand @CommandBody - $CommandTable = [PSCustomObject]@{ - commandId = $command._id - commandName = $command.name - commandPreviouslyRun = $false - commandQueued = $false - systems = $systemIds - } + } catch { + $status_commandGenerated = $false + # throw $_ + } + # Find newly created command and add system as target + $Command = Get-JcSdkCommand -Filter @("trigger:eq:$($certInfo.sha1)", "commandType:eq:mac") + $workToBeDone.macOSCommandID = $Command.Id + + $CommandTable = [PSCustomObject]@{ + commandId = $command.Id + commandName = $command.name + commandPreviouslyRun = $false + commandQueued = $false + systems = $systemIds + } - $user.commandAssociations += $CommandTable + $user.commandAssociations += $CommandTable - # Write-Host "[status] Successfully created $($Command.name): User - $($user.userName); OS - Mac OS X" - $status_commandGenerated = $true + # Write-Host "[status] Successfully created $($Command.name): User - $($user.userName); OS - Mac OS X" + $status_commandGenerated = $true - } - 'windows' { - # Get the Windows system ids - $systemIds = (Get-SystemsThatNeedCertWork -userData $user -osType "windows") - # If there are no systemIds to process, skip generating the command: - if ($systemIds.count -eq 0) { - continue - } - # Check to see if previous commands exist - $Command = Get-JCCommand -name "RadiusCert-Install:$($user.userName):Windows" - if ($Command.Count -ge 1) { - # $confirmation = Write-Host "[status] RadiusCert-Install:$($user.userName):Windows command already exists, skipping..." - $status_commandGenerated = $false - continue - } + } + } - # Create new Command and upload the signed pfx - try { - $CommandBody = @{ - Name = "RadiusCert-Install:$($user.userName):Windows" - Command = @" + if (-Not ($workToBeDone.forceGenerateMacOSCommands) -And ($workToBeDone.macOSCommandID)) { + $systemIds = (Get-SystemsThatNeedCertWork -userData $user -osType "macOS") + + $Command = Get-JcSdkCommand -Filter @("trigger:eq:$($certInfo.sha1)", "commandType:eq:windows") + $CommandTable = [PSCustomObject]@{ + commandId = $workToBeDone.macOSCommandID + commandName = $command.name + commandPreviouslyRun = $false + commandQueued = $false + systems = $systemIds + } + + $user.commandAssociations += $CommandTable + + } + + if (($invokeCommands) -And ($workToBeDone.remainingMacOSDevices)) { + try { + $commandStart = Start-JcSdkCommand -Id $workToBeDone.macOSCommandID -SystemIds $workToBeDone.remainingMacOSDevices.systemId | Out-Null + $result_deployed = $true + } catch { + $result_deployed = $false + } + } + # set the command associations + if (($workToBeDone.remainingMacOSDevices) -And ($workToBeDone.macOSCommandID)) { + + $workToBeDone.remainingMacOSDevices | ForEach-Object { + try { + + $commandAssociation = Set-JcSdkCommandAssociation -CommandId:("$($workToBeDone.macOSCommandID)") -Op 'add' -Type:('system') -Id:("$($_.systemId)") | Out-Null + } catch { + "already exists/ couldn't add" | Out-Null + } + } + + } + if ($workToBeDone.forceGenerateWindowsCommands) { + # Get the Windows system ids + $systemIds = (Get-SystemsThatNeedCertWork -userData $user -osType "windows") + # If there are no systemIds to process, skip generating the command: + if ($systemIds.count -gt 0) { + # Create new Command and upload the signed pfx + try { + $CommandBody = @{ + Name = "RadiusCert-Install:$($user.userName):Windows" + Command = @" `$ErrorActionPreference = "Stop" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 `$PkgProvider = Get-PackageProvider If ("Nuget" -notin `$PkgProvider.Name){ - Install-PackageProvider -Name NuGet -Force +Install-PackageProvider -Name NuGet -Force } `$CurrentUser = (Get-WMIObject -ClassName Win32_ComputerSystem).Username if ( -Not [string]::isNullOrEmpty(`$CurrentUser) ){ - `$CurrentUser = `$CurrentUser.Split('\')[1] +`$CurrentUser = `$CurrentUser.Split('\')[1] } else { - `$CurrentUser = `$null +`$CurrentUser = `$null } if (`$CurrentUser -eq "$($user.localUsername)") { - if (-not(Get-InstalledModule -Name RunAsUser -errorAction "SilentlyContinue")) { - Write-Host "RunAsUser Module not installed, Installing..." - Install-Module RunAsUser -Force - Import-Module RunAsUser -Force - } else { - Write-Host "RunAsUser Module installed, importing into session..." - Import-Module RunAsUser -Force - } - # create temp new radius directory - If (Test-Path "C:\RadiusCert"){ - Write-Host "Radius Temp Cert Directory Exists" - } else { - New-Item "C:\RadiusCert" -itemType Directory - } - # expand archive as root and copy to temp location - Expand-Archive -LiteralPath C:\Windows\Temp\$($user.userName)-client-signed.zip -DestinationPath C:\RadiusCert -Force - `$password = ConvertTo-SecureString -String $JCR_USER_CERT_PASS -AsPlainText -Force - `$ScriptBlockInstall = { `$password = ConvertTo-SecureString -String $JCR_USER_CERT_PASS -AsPlainText -Force - Import-PfxCertificate -Password `$password -FilePath "C:\RadiusCert\$($user.userName)-client-signed.pfx" -CertStoreLocation Cert:\CurrentUser\My - } - `$imported = Get-PfxData -Password `$password -FilePath "C:\RadiusCert\$($user.userName)-client-signed.pfx" - # Get Current Certs As User - `$ScriptBlockCleanup = { - `$certs = Get-ChildItem Cert:\CurrentUser\My\ - - foreach (`$cert in `$certs){ - if (`$cert.subject -match "$($certIdentifier)") { - if (`$(`$cert.serialNumber) -eq "$($certInfo.serial)"){ - write-host "Found Cert:``nCert SN: `$(`$cert.serialNumber)" - } else { - write-host "Removing Cert:``nCert SN: `$(`$cert.serialNumber)" - Get-ChildItem "Cert:\CurrentUser\My\`$(`$cert.thumbprint)" | remove-item - } +if (-not(Get-InstalledModule -Name RunAsUser -errorAction "SilentlyContinue")) { + Write-Host "RunAsUser Module not installed, Installing..." + Install-Module RunAsUser -Force + Import-Module RunAsUser -Force +} else { + Write-Host "RunAsUser Module installed, importing into session..." + Import-Module RunAsUser -Force +} +# create temp new radius directory +If (Test-Path "C:\RadiusCert"){ + Write-Host "Radius Temp Cert Directory Exists" +} else { + New-Item "C:\RadiusCert" -itemType Directory +} +# expand archive as root and copy to temp location +Expand-Archive -LiteralPath C:\Windows\Temp\$($user.userName)-client-signed.zip -DestinationPath C:\RadiusCert -Force +`$password = ConvertTo-SecureString -String $JCR_USER_CERT_PASS -AsPlainText -Force +`$ScriptBlockInstall = { `$password = ConvertTo-SecureString -String $JCR_USER_CERT_PASS -AsPlainText -Force +Import-PfxCertificate -Password `$password -FilePath "C:\RadiusCert\$($user.userName)-client-signed.pfx" -CertStoreLocation Cert:\CurrentUser\My +} +`$imported = Get-PfxData -Password `$password -FilePath "C:\RadiusCert\$($user.userName)-client-signed.pfx" +# Get Current Certs As User +`$ScriptBlockCleanup = { + `$certs = Get-ChildItem Cert:\CurrentUser\My\ + + foreach (`$cert in `$certs){ + if (`$cert.subject -match "$($certIdentifier)") { + if (`$(`$cert.serialNumber) -eq "$($certInfo.serial)"){ + write-host "Found Cert:``nCert SN: `$(`$cert.serialNumber)" + } else { + write-host "Removing Cert:``nCert SN: `$(`$cert.serialNumber)" + Get-ChildItem "Cert:\CurrentUser\My\`$(`$cert.thumbprint)" | remove-item } } } - `$scriptBlockValidate = { - if (Get-ChildItem Cert:\CurrentUser\My\`$(`$imported.thumbrprint)){ - return `$true - } else { - return `$false - } - } - Write-Host "Importing Pfx Certificate for $($user.userName)" - `$certInstall = Invoke-AsCurrentUser -ScriptBlock `$ScriptBlockInstall -CaptureOutput - `$certInstall - Write-Host "Cleaning Up Previously Installed Certs for $($user.userName)" - `$certCleanup = Invoke-AsCurrentUser -ScriptBlock `$ScriptBlockCleanup -CaptureOutput - `$certCleanup - Write-Host "Validating Installed Certs for $($user.userName)" - `$certValidate = Invoke-AsCurrentUser -ScriptBlock `$scriptBlockValidate -CaptureOutput - write-host `$certValidate - - # finally clean up temp files: - If (Test-Path "C:\Windows\Temp\$($user.userName)-client-signed.zip"){ - Remove-Item "C:\Windows\Temp\$($user.userName)-client-signed.zip" - } - If (Test-Path "C:\RadiusCert\$($user.userName)-client-signed.pfx"){ - Remove-Item "C:\RadiusCert\$($user.userName)-client-signed.pfx" - } - - # Lastly validate if the cert was installed - if (`$certValidate.Trim() -eq "True"){ - Write-Host "Cert was installed" +} +`$scriptBlockValidate = { + if (Get-ChildItem Cert:\CurrentUser\My\`$(`$imported.thumbrprint)){ + return `$true } else { - Throw "Cert was not installed" + return `$false } +} +Write-Host "Importing Pfx Certificate for $($user.userName)" +`$certInstall = Invoke-AsCurrentUser -ScriptBlock `$ScriptBlockInstall -CaptureOutput +`$certInstall +Write-Host "Cleaning Up Previously Installed Certs for $($user.userName)" +`$certCleanup = Invoke-AsCurrentUser -ScriptBlock `$ScriptBlockCleanup -CaptureOutput +`$certCleanup +Write-Host "Validating Installed Certs for $($user.userName)" +`$certValidate = Invoke-AsCurrentUser -ScriptBlock `$scriptBlockValidate -CaptureOutput +write-host `$certValidate + +# finally clean up temp files: +If (Test-Path "C:\Windows\Temp\$($user.userName)-client-signed.zip"){ + Remove-Item "C:\Windows\Temp\$($user.userName)-client-signed.zip" +} +If (Test-Path "C:\RadiusCert\$($user.userName)-client-signed.pfx"){ + Remove-Item "C:\RadiusCert\$($user.userName)-client-signed.pfx" +} - # update si table - # define curl path: - `$curlPath = "C:\ProgramData\chocolatey\bin\curl.exe" - if (Test-Path -Path `$curlPath) { - - # Parse the systemKey from the cong file. - `$config = get-content 'C:\Program Files\JumpCloud\Plugins\Contrib\jcagent.conf' - `$regex = 'systemKey\":\"(\w+)\"' - `$systemKey = [regex]::Match(`$config, `$regex).Groups[1].Value - # get the certUUID - `$regex = 'certuuid\":\"(\w+)\"' - `$certUUID = [regex]::Match(`$config, `$regex).Groups[1].Value - - # Get the key/ cert location - `$keyLocation = "C:\Program Files\JumpCloud\Plugins\Contrib\client.key" - `$certLocation = "C:\Program Files\JumpCloud\Plugins\Contrib\client.crt" - `$caCertLocation = "C:\Program Files\JumpCloud\Plugins\Contrib\ca.crt" - - # Get json certificate data from osquery - `$certs = . "C:\Program Files\JumpCloud\jcosqueryi" --json "select * from certificates" - # format the data - `$certsText = "{``"data``":`$certs}" - # save the data to a temp file - `$certJsonFile = "C:\Windows\Temp\jsonFile.txt" - `$certsText | Out-File -FilePath `$certJsonFile -Force -Encoding utf8 - # submit the request - C:\ProgramData\chocolatey\bin\curl.exe --cert "`$certLocation" --key "`$keyLocation" --cacert "`$caCertLocation" --header "x-system-id: `$systemKey" --header "x-ssl-client-dn: /CN=`$certUUID/O=JumpCloud" --header "Content-type:application/json " -d @C:\Windows\Temp\jsonFile.txt --url 'https://agent.jumpcloud.com/systeminsights/snapshots/certificates' - # remove the temp file - Remove-Item -Path `$certJsonFile - } else { - Write-Host "Curl might not be installed, system insights will update certificate information on this device within an hour" - } +# Lastly validate if the cert was installed +if (`$certValidate.Trim() -eq "True"){ + Write-Host "Cert was installed" } else { - if (`$CurrentUser -eq `$null){ - Write-Host "No users are signed into the system. Please ensure $($user.userName) is signed in and retry." - } else { - Write-Host "Current logged in user, `$CurrentUser, does not match expected certificate user. Please ensure $($user.localUsername) is signed in and retry." - } - # finally clean up temp files: - If (Test-Path "C:\Windows\Temp\$($user.userName)-client-signed.zip"){ - Remove-Item "C:\Windows\Temp\$($user.userName)-client-signed.zip" - } - If (Test-Path "C:\RadiusCert\$($user.userName)-client-signed.pfx"){ - Remove-Item "C:\RadiusCert\$($user.userName)-client-signed.pfx" - } - exit 4 + Throw "Cert was not installed" } -"@ - launchType = "trigger" - trigger = "RadiusCertInstall" - commandType = "windows" - shell = "powershell" - timeout = 600 - TimeToLiveSeconds = 864000 - files = (New-JCCommandFile -certFilePath $userPfxZip -FileName "$($user.userName)-client-signed.zip" -FileDestination "C:\Windows\Temp\$($user.userName)-client-signed.zip") - } - $NewCommand = New-JcSdkCommand @CommandBody - # Find newly created command and add system as target - $Command = Get-JCCommand -name "RadiusCert-Install:$($user.userName):Windows" - $systemIds | ForEach-Object { - Set-JcSdkCommandAssociation -CommandId:("$($Command._id)") -Op 'add' -Type:('system') -Id:("$($_.systemId)") | Out-Null - } - } catch { - $status_commandGenerated = $false - # throw $_ - } +# update si table +# define curl path: +`$curlPath = "C:\ProgramData\chocolatey\bin\curl.exe" +if (Test-Path -Path `$curlPath) { - $CommandTable = [PSCustomObject]@{ - commandId = $command._id - commandName = $command.name - commandPreviouslyRun = $false - commandQueued = $false - systems = $systemIds + # Parse the systemKey from the cong file. + `$config = get-content 'C:\Program Files\JumpCloud\Plugins\Contrib\jcagent.conf' + `$regex = 'systemKey\":\"(\w+)\"' + `$systemKey = [regex]::Match(`$config, `$regex).Groups[1].Value + # get the certUUID + `$regex = 'certuuid\":\"(\w+)\"' + `$certUUID = [regex]::Match(`$config, `$regex).Groups[1].Value + + # Get the key/ cert location + `$keyLocation = "C:\Program Files\JumpCloud\Plugins\Contrib\client.key" + `$certLocation = "C:\Program Files\JumpCloud\Plugins\Contrib\client.crt" + `$caCertLocation = "C:\Program Files\JumpCloud\Plugins\Contrib\ca.crt" + + # Get json certificate data from osquery + `$certs = . "C:\Program Files\JumpCloud\jcosqueryi" --json "select * from certificates" + # format the data + `$certsText = "{``"data``":`$certs}" + # save the data to a temp file + `$certJsonFile = "C:\Windows\Temp\jsonFile.txt" + `$certsText | Out-File -FilePath `$certJsonFile -Force -Encoding utf8 + # submit the request + C:\ProgramData\chocolatey\bin\curl.exe --cert "`$certLocation" --key "`$keyLocation" --cacert "`$caCertLocation" --header "x-system-id: `$systemKey" --header "x-ssl-client-dn: /CN=`$certUUID/O=JumpCloud" --header "Content-type:application/json " -d @C:\Windows\Temp\jsonFile.txt --url 'https://agent.jumpcloud.com/systeminsights/snapshots/certificates' + # remove the temp file + Remove-Item -Path `$certJsonFile +} else { + Write-Host "Curl might not be installed, system insights will update certificate information on this device within an hour" +} +} else { +if (`$CurrentUser -eq `$null){ + Write-Host "No users are signed into the system. Please ensure $($user.userName) is signed in and retry." +} else { + Write-Host "Current logged in user, `$CurrentUser, does not match expected certificate user. Please ensure $($user.localUsername) is signed in and retry." +} +# finally clean up temp files: +If (Test-Path "C:\Windows\Temp\$($user.userName)-client-signed.zip"){ + Remove-Item "C:\Windows\Temp\$($user.userName)-client-signed.zip" +} +If (Test-Path "C:\RadiusCert\$($user.userName)-client-signed.pfx"){ + Remove-Item "C:\RadiusCert\$($user.userName)-client-signed.pfx" +} +exit 4 +} +"@ + launchType = "trigger" + trigger = "$($certInfo.sha1)" + commandType = "windows" + shell = "powershell" + timeout = 600 + TimeToLiveSeconds = 864000 + files = (New-JCCommandFile -certFilePath $userPfxZip -FileName "$($user.userName)-client-signed.zip" -FileDestination "C:\Windows\Temp\$($user.userName)-client-signed.zip") } + $NewCommand = New-JcSdkCommand @CommandBody - $user.commandAssociations += $CommandTable - # Write-Host "[status] Successfully created $($Command.name): User - $($user.userName); OS - Windows" - $status_commandGenerated = $true - - } - $null { - Write-host "$($user.username) is not associated with any systems, skipping command generation" + } catch { $status_commandGenerated = $false - $result_deployed = $false } + # Find newly created command and add system as target + $Command = Get-JcSdkCommand -Filter @("trigger:eq:$($certInfo.sha1)", "commandType:eq:windows") + $workToBeDone.windowsCommandID = $command.Id + + $CommandTable = [PSCustomObject]@{ + commandId = $workToBeDone.windowsCommandID + commandName = $command.name + commandPreviouslyRun = $false + commandQueued = $false + systems = $systemIds + } + + $user.commandAssociations += $CommandTable + # Write-Host "[status] Successfully created $($Command.name): User - $($user.userName); OS - Windows" + $status_commandGenerated = $true } } - # Update the user table with the information from the generated commands: - $userObjectFromTable, $userIndex = Get-UserFromTable -userid $user.userid - - Set-UserTable -index $userIndex -commandAssociationsObject $user.commandAssociations - switch ($invokeCommands) { - $true { - # finally update the user table to note that the command has been run, the cert has been deployed - # get the object once more: - $userObjectFromTable, $userIndex = Get-UserFromTable -userid $user.userid - # Set commandPreviouslyRun property to true if there are command associations to set - if ($userObjectFromTable.commandAssociations) { - $userObjectFromTable.commandAssociations | ForEach-Object { $_.commandPreviouslyRun = $true } - # set the deployed status to true, set the date - if (Get-Member -inputObject $userObjectFromTable.certInfo -name "deployed" -MemberType Properties) { - # if ($userObjectFromTable.certInfo.deployed) { - $userObjectFromTable.certInfo.deployed = $true - } else { - $userObjectFromTable.certInfo | Add-Member -Name 'deployed' -Type NoteProperty -Value $false - - } - if (Get-Member -inputObject $userObjectFromTable.certInfo -name "deploymentDate" -MemberType Properties) { - # if ($userObjectFromTable.certInfo.deploymentDate) { - $userObjectFromTable.certInfo.deploymentDate = (Get-Date) + if (-Not ($workToBeDone.forceGenerateWindowsCommands) -And ($workToBeDone.windowsCommandID)) { + $systemIds = (Get-SystemsThatNeedCertWork -userData $user -osType "windows") + + $Command = Get-JcSdkCommand -Filter @("trigger:eq:$($certInfo.sha1)", "commandType:eq:windows") + $CommandTable = [PSCustomObject]@{ + commandId = $workToBeDone.windowsCommandID + commandName = $command.name + commandPreviouslyRun = $false + commandQueued = $false + systems = $systemIds + } - } else { + $user.commandAssociations += $CommandTable - $userObjectFromTable.certInfo | Add-Member -Name 'deploymentDate' -Type NoteProperty -Value (Get-Date) - } - Set-UserTable -index $userIndex -commandAssociationsObject $userObjectFromTable.commandAssociations -certInfoObject $userObjectFromTable.certInfo - $invokeCommands = invoke-commandByUserId -userID $user.userId - $result_deployed = $true - } - } - $false { + } + if (($invokeCommands) -AND ($workToBeDone.remainingWindowsSDevices)) { + try { + $commandStart = Start-JcSdkCommand -Id $workToBeDone.windowsCommandID -SystemIds $workToBeDone.remainingWindowsSDevices.systemId | Out-Null + $result_deployed = $true + } catch { $result_deployed = $false } - Default { + } + # set the command associations + if (($workToBeDone.remainingWindowsSDevices) -And ($workToBeDone.windowsCommandID)) { + $workToBeDone.remainingWindowsSDevices | ForEach-Object { + try { + $commandAssociation = Set-JcSdkCommandAssociation -CommandId:("$($workToBeDone.windowsCommandID)") -Op 'add' -Type:('system') -Id:("$($_.systemId)") | Out-Null + } catch { + "already exists/ couldn't add" | Out-Null + } } + } + } + # TODO: get the userIndex only? + $userObjectFromTable, $userIndex = Get-UserFromTable -userid $user.userid + + switch ($invokeCommands) { + $true { + if ($user.commandAssociations) { + $user.commandAssociations | ForEach-Object { $_.commandPreviouslyRun = $true } + # set the deployed status to true, set the date + if (Get-Member -inputObject $user.certInfo -name "deployed" -MemberType Properties) { + # if ($userObjectFromTable.certInfo.deployed) { + $user.certInfo.deployed = $true + } else { + $user.certInfo | Add-Member -Name 'deployed' -Type NoteProperty -Value $false + + } + if (Get-Member -inputObject $user.certInfo -name "deploymentDate" -MemberType Properties) { + # if ($userObjectFromTable.certInfo.deploymentDate) { + $user.certInfo.deploymentDate = (Get-Date) + + } else { + $user.certInfo | Add-Member -Name 'deploymentDate' -Type NoteProperty -Value (Get-Date) + } + } + } + $false { + $result_deployed = $false + } + Default { + } + } @@ -566,13 +793,17 @@ if (`$CurrentUser -eq "$($user.localUsername)") { end { - #TODO: eventually add message if we fail to generate a command $resultTable = [ordered]@{ 'Username' = $user.username; 'Command Generated' = $status_commandGenerated; 'Command Deployed' = $result_deployed } + $workDone = [PSCustomObject]@{ + userIndex = $userIndex + commandAssociationsObject = $user.commandAssociations + certInfoObject = $user.certInfo + } - return $resultTable + return $resultTable, $workDone } } diff --git a/scripts/automation/Radius/Functions/Private/Commands/Get-CommandByUsername.ps1 b/scripts/automation/Radius/Functions/Private/Commands/Get-CommandByUsername.ps1 index 4dd8d882f..3e9ab9ec8 100644 --- a/scripts/automation/Radius/Functions/Private/Commands/Get-CommandByUsername.ps1 +++ b/scripts/automation/Radius/Functions/Private/Commands/Get-CommandByUsername.ps1 @@ -11,17 +11,17 @@ function Get-CommandByUsername { # define searchFilter $SearchFilter = @{ searchTerm = "RadiusCert-Install:${username}:" - fields = @('name') + fields = @('name', 'trigger', 'commandType') } } process { # Get command Results - $commandResults = Search-JcSdkCommand -SearchFilter $SearchFilter -Fields name + $commandResults = Search-JcSdkCommand -SearchFilter $SearchFilter -Fields "name trigger commandType" } end { return $commandResults } -} +} \ No newline at end of file diff --git a/scripts/automation/Radius/Functions/Public/Start-DeployUserCerts.ps1 b/scripts/automation/Radius/Functions/Public/Start-DeployUserCerts.ps1 index 8d2035766..687923576 100644 --- a/scripts/automation/Radius/Functions/Public/Start-DeployUserCerts.ps1 +++ b/scripts/automation/Radius/Functions/Public/Start-DeployUserCerts.ps1 @@ -14,7 +14,11 @@ function Start-DeployUserCerts { # Force invoke commands after generation [Parameter(HelpMessage = 'Switch to force invoke generated commands on systems', ParameterSetName = 'cli')] [switch] - $forceInvokeCommands + $forceInvokeCommands, + # Force invoke commands after generation + [Parameter(HelpMessage = 'Switch to force generate new commands on systems', ParameterSetName = 'cli')] + [switch] + $forceGenerateCommands ) # Import the users.json file and convert to PSObject @@ -28,6 +32,16 @@ function Start-DeployUserCerts { Show-DistributionMenu -CertObjectArray $userArray.certInfo -usersThatNeedCert $usersWithoutLatestCert.count -totalUserCount $userArray.count $confirmation = Read-Host "Please make a selection" + # This can be updated later if necessary but for now if using the GUI, the $forceGenerateCommands switch will always be false + # Thus the GUI will never overwrite commands unless the SHA1 value does not match the local cert SHA1 + switch ($forceGenerateCommands) { + $true { + $generateCommands = $true + } + $false { + $generateCommands = $false + } + } } 'cli' { $confirmationMap = @{ @@ -45,6 +59,14 @@ function Start-DeployUserCerts { $invokeCommands = $false } } + switch ($forceGenerateCommands) { + $true { + $generateCommands = $true + } + $false { + $generateCommands = $false + } + } } } @@ -59,10 +81,58 @@ function Start-DeployUserCerts { } } } - for ($i = 0; $i -lt $userArray.Count; $i++) { - $result = Deploy-UserCertificate -userObject $userArray[$i] -forceInvokeCommands $invokeCommands - Show-RadiusProgress -completedItems ($i + 1) -totalItems $userArray.Count -ActionText "Distributing Radius Certificates" -previousOperationResult $result + + # set thread safe variables: + $resultArray = [System.Collections.Concurrent.ConcurrentBag[object]]::new() + $workDoneArray = [System.Collections.Concurrent.ConcurrentBag[object]]::new() + + $userArray | Foreach-Object -ThrottleLimit 20 -Parallel { + # set the required variables + $JCAPIKEY = $using:JCAPIKEY + $JCORGID = $using:JCORGID + $JCScriptRoot = $using:JCScriptRoot + + # set the required global variables + $Global:JCRUsers = $using:JCRUsers + $Global:JCRSystems = $using:JCRSystems + $Global:JCRAssociations = $using:JCRAssociations + $Global:JCRRadiusMembers = $using:JCRRadiusMembers + $Global:JCRCertHash = $using:JCRCertHash + + # set the thread safe variables + $resultArray = $using:resultArray + $workDoneArray = $using:workDoneArray + + # import the private functions: + $Private = @( Get-ChildItem -Path "$JCScriptRoot/Functions/Private/*.ps1" -Recurse) + Foreach ($Import in $Private) { + Try { + . $Import.FullName + } Catch { + Write-Error -Message "Failed to import function $($Import.FullName): $_" + } + } + + # deploy user certs: + $result, $workDone = Deploy-UserCertificate -userObject $_ -forceInvokeCommands $using:invokeCommands -forceGenerateCommands $using:generateCommands + # keep track of results & work done + $resultArray.Add($result) + $WorkDoneArray.Add($workDone) + } + + # update the userTable: + foreach ($item in $workDoneArray) { + Set-UserTable -index $item.userIndex -commandAssociationsObject $item.commandAssociationsObject -certInfoObject $item.certInfoObject + } + + # print the progress: + $resultCount = $resultArray.Count + $resultItemCount = 1 + foreach ($item in $resultArray) { + Show-RadiusProgress -completedItems ($resultItemCount) -totalItems $resultArray.Count -ActionText "Distributing Radius Certificates" -previousOperationResult $item + $resultItemCount++ } + # return after an action if cli, else stay in function switch ($PSCmdlet.ParameterSetName) { 'gui' { @@ -86,9 +156,56 @@ function Start-DeployUserCerts { if (-Not $usersWithoutLatestCert) { $usersWithoutLatestCert = Get-UsersThatNeedCertWork -userData $userArray } - for ($i = 0; $i -lt $usersWithoutLatestCert.Count; $i++) { - $result = Deploy-UserCertificate -userObject $usersWithoutLatestCert[$i] -forceInvokeCommands $invokeCommands - Show-RadiusProgress -completedItems ($i + 1) -totalItems $usersWithoutLatestCert.Count -ActionText "Distributing Radius Certificates" -previousOperationResult $result + + # set thread safe variables: + $resultArray = [System.Collections.Concurrent.ConcurrentBag[object]]::new() + $workDoneArray = [System.Collections.Concurrent.ConcurrentBag[object]]::new() + # foreach user: + $usersWithoutLatestCert | Foreach-Object -ThrottleLimit 20 -Parallel { + # set the required variables + $JCAPIKEY = $using:JCAPIKEY + $JCORGID = $using:JCORGID + $JCScriptRoot = $using:JCScriptRoot + + # set the required global variables + $Global:JCRUsers = $using:JCRUsers + $Global:JCRSystems = $using:JCRSystems + $Global:JCRAssociations = $using:JCRAssociations + $Global:JCRRadiusMembers = $using:JCRRadiusMembers + $Global:JCRCertHash = $using:JCRCertHash + + # set the thread safe variables + $resultArray = $using:resultArray + $workDoneArray = $using:workDoneArray + + # import the private functions: + $Private = @( Get-ChildItem -Path "$JCScriptRoot/Functions/Private/*.ps1" -Recurse) + Foreach ($Import in $Private) { + Try { + . $Import.FullName + } Catch { + Write-Error -Message "Failed to import function $($Import.FullName): $_" + } + } + + # deploy user certs: + $result, $workDone = Deploy-UserCertificate -userObject $_ -forceInvokeCommands $using:invokeCommands -forceGenerateCommands $using:generateCommands + # keep track of results & work done + $resultArray.Add($result) + $WorkDoneArray.Add($workDone) + } + + # update the userTable: + foreach ($item in $workDoneArray) { + Set-UserTable -index $item.userIndex -commandAssociationsObject $item.commandAssociationsObject -certInfoObject $item.certInfoObject + } + + # print the progress: + $resultCount = $resultArray.Count + $resultItemCount = 1 + foreach ($item in $resultArray) { + Show-RadiusProgress -completedItems ($resultItemCount) -totalItems $resultArray.Count -ActionText "Distributing Radius Certificates" -previousOperationResult $item + $resultItemCount++ } # return after an action if cli, else stay in function switch ($PSCmdlet.ParameterSetName) { @@ -133,12 +250,16 @@ function Start-DeployUserCerts { # Process existing commands/ Generate new commands/ Deploy new Certificate switch ($PSCmdlet.ParameterSetName) { 'gui' { - $result = Deploy-UserCertificate -userObject $UserSelectionArray -prompt + $result, $workDone = Deploy-UserCertificate -userObject $UserSelectionArray -prompt } 'cli' { - $result = Deploy-UserCertificate -userObject $UserSelectionArray -forceInvokeCommands $invokeCommands + $result, $workDone = Deploy-UserCertificate -userObject $UserSelectionArray -forceInvokeCommands $invokeCommands -forceGenerateCommands $generateCommands } } + # update user json + Set-UserTable -index $workDone.userIndex -commandAssociationsObject $workDone.commandAssociationsObject -certInfoObject $workDone.certInfoObject + + # show progress Show-RadiusProgress -completedItems $UserSelectionArray.count -totalItems $UserSelectionArray.Count -ActionText "Distributing Radius Certificates" -previousOperationResult $result } # return after an action if cli, else stay in function diff --git a/scripts/automation/Radius/JumpCloud-Radius.psm1 b/scripts/automation/Radius/JumpCloud-Radius.psm1 index 92cafbf71..05a49d197 100644 --- a/scripts/automation/Radius/JumpCloud-Radius.psm1 +++ b/scripts/automation/Radius/JumpCloud-Radius.psm1 @@ -28,7 +28,7 @@ $global:JCScriptRoot = "$PSScriptRoot" # try to get the settings file, create new one if it does not exist: $global:JCRConfig = Get-JCRSettingsFile -# if the Certs / UserCerts directories do not exist, create thenm +# if the Certs / UserCerts directories do not exist, create them if (-Not (Test-Path -Path "$JCScriptRoot/Cert" -PathType Container)) { New-Item -Path "$JCScriptRoot/Cert" -ItemType Directory } diff --git a/scripts/automation/Radius/Tests/Public/Get-JCRGlobalVars.Tests.ps1 b/scripts/automation/Radius/Tests/Public/Get-JCRGlobalVars.Tests.ps1 index 15f917399..b2f93cec3 100644 --- a/scripts/automation/Radius/Tests/Public/Get-JCRGlobalVars.Tests.ps1 +++ b/scripts/automation/Radius/Tests/Public/Get-JCRGlobalVars.Tests.ps1 @@ -17,6 +17,7 @@ Describe "Get Global Variable Data Tests" -Tag "Cache" { Write-Error -Message "Failed to import function $($Import.FullName): $_" } } + Start-GenerateRootCert -certKeyPassword "TestCertificate123!@#" } Context "When no 'data' directory exists" { BeforeAll { diff --git a/scripts/automation/Radius/Tests/Public/Start-DeployUserCerts.Tests.ps1 b/scripts/automation/Radius/Tests/Public/Start-DeployUserCerts.Tests.ps1 index 4072cce96..6607a88df 100644 --- a/scripts/automation/Radius/Tests/Public/Start-DeployUserCerts.Tests.ps1 +++ b/scripts/automation/Radius/Tests/Public/Start-DeployUserCerts.Tests.ps1 @@ -314,4 +314,365 @@ Describe 'Distribute User Cert Tests' -Tag 'Distribute' { $content -replace ('\$Global:JCR_CERT_TYPE = *.+', '$Global:JCR_CERT_TYPE = "UsernameCn"') | Set-Content -Path $configPath } } + Context 'Duplicate Command / Command Result Tests' { + BeforeEach { + # create a user that has both a mac and windows association + $user = New-RandomUser -Domain "pesterRadius" | New-JCUser + $dateBefore = (Get-Date).ToString('MM/dd/yyyy HH:mm:ss') + # add user to membership group + Add-JCUserGroupMember -GroupID $Global:JCR_USER_GROUP -UserID $user.id + # get random system + $windowsSystem = Get-JCSystem -os windows | Get-Random -Count 1 + $macSystem = Get-JCSystem -os "Mac OS X" | Get-Random -Count 1 + Add-JCSystemUser -UserID $user.id -SystemID $windowsSystem.id + Add-JCSystemUser -UserID $user.id -SystemID $macSystem.id + + # update membership + Get-JCRGlobalVars -skipAssociation -force + # todo: manually update association table to account for new membership + Set-JCRAssociationHash -userId $user.id + Update-JCRUsersJson + + # Generate a user certificate for the user: + Start-GenerateUserCerts -type ByUsername -username $user.username + + # Get the SHA1 hash for the user's cert: + $certData = Get-CertInfo -userCerts -username $user.username + + } + It 'When a duplicate commands with differing trigger SHA1 hashes exists, the command with the old SHA1 hash should be removed' { + # Set a different SHA1 value to simulate an older cert command + $oldSha = "$($certData.sha1)1111" + # Mock some commands with the trigger to already exist if they do not exist + $macCommandBody = @{ + Name = "RadiusCert-Install:$($user.username):MacOSX" + Command = "sha1234" + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($oldSha)" + commandType = "mac" + timeout = 600 + TimeToLiveSeconds = 864000 + } + $newMacCommand = New-JcSdkCommand @macCommandBody + $macCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($oldSha)", "commandType:eq:mac") + + # invoke the commands manually to simulate the command queue containing items: + Start-JcSdkCommand -Id $macCmdBefore.Id -SystemIds $macSystem.id + + $windowsCommandBody = @{ + Name = "RadiusCert-Install:$($user.username):Windows" + Command = "sha1234" + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($oldSha)" + commandType = "windows" + timeout = 600 + TimeToLiveSeconds = 864000 + } + $newWindowsCommand = New-JcSdkCommand @windowsCommandBody + $windowsCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($oldSha)", "commandType:eq:windows") + + # invoke the commands manually to simulate the command queue containing items: + Start-JcSdkCommand -Id $WindowsCmdBefore.Id -SystemIds $windowsSystem.id + + # Get the queued commands: + $queuedCmdsBefore = Get-QueuedCommandByUser -username $user.username + + # Run Start Deploy User Certs by username + Start-DeployUserCerts -type ByUsername -username $user.username + + # After running, validate that the commands before execution, no longer exist + $macCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + $windowsCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + # Get the queued after: + $queuedCmdsAfter = Get-QueuedCommandByUser -username $user.username + # test that the commands should not exist: + $macCmdAfter.id | Should -Not -Contain $macCmdBefore.id + $windowsCmdAfter.id | Should -Not -Contain $windowsCmdBefore.id + $macCmdAfter | Should -Not -BeNullOrEmpty + $windowsCmdAfter | Should -Not -BeNullOrEmpty + { Get-JcSdkCommand -Id $macCmdBefore.id } | Should -Throw # in other words the command should not exist + { Get-JcSdkCommand -Id $windowsCmdBefore.id } | Should -Throw # in other words the command should not exist + # test that the queued commands should not exist: + $queuedCmdsAfter.id | Should -Not -Contain $queuedCmdsBefore.Id + $queuedCmdsAfter.id | Should -BeNullOrEmpty + # user.json should have the newID in command associations. + $allUserData = Get-UserJsonData + $testUserData = $allUserData | Where-Object { $_.username -eq $user.username } + $testUserData.commandAssociations.commandId | Should -Not -Contain $macCmdBefore.id + $testUserData.commandAssociations.commandId | Should -Not -Contain $windowsCmdBefore.id + } + It 'When a single command with the same trigger SHA1 hashes exists, a new cert command should not should be generated' { + # Mock some commands with the trigger to already exist if they do not exist + $macCommandBody = @{ + Name = "RadiusCert-Install:$($user.username):MacOSX" + Command = "sha1234" + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($certData.sha1)" + commandType = "mac" + timeout = 600 + TimeToLiveSeconds = 864000 + } + $newMacCommand = New-JcSdkCommand @macCommandBody + $macCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + + # invoke the commands manually to simulate the command queue containing items: + Start-JcSdkCommand -Id $macCmdBefore.Id -SystemIds $macSystem.id + + $windowsCommandBody = @{ + Name = "RadiusCert-Install:$($user.username):Windows" + Command = "sha1234" + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($certData.sha1)" + commandType = "windows" + timeout = 600 + TimeToLiveSeconds = 864000 + } + $newWindowsCommand = New-JcSdkCommand @windowsCommandBody + $windowsCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + + # invoke the commands manually to simulate the command queue containing items: + Start-JcSdkCommand -Id $WindowsCmdBefore.Id -SystemIds $windowsSystem.id + + # Get the queued commands: + $queuedCmdsBefore = Get-QueuedCommandByUser -username $user.username + + # Run Start Deploy User Certs by username + Start-DeployUserCerts -type ByUsername -username $user.username + + # After running, validate that the commands before execution, no longer exist + $macCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + $windowsCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + # Get the queued after: + $queuedCmdsAfter = Get-QueuedCommandByUser -username $user.username + # test that the commands should not exist: + $macCmdAfter.id | Should -Contain $macCmdBefore.id + $windowsCmdAfter.id | Should -Contain $windowsCmdBefore.id + $macCmdAfter | Should -Not -BeNullOrEmpty + $windowsCmdAfter | Should -Not -BeNullOrEmpty + { Get-JcSdkCommand -Id $macCmdBefore.id } | Should -Not -Throw # in other words the command should not exist + { Get-JcSdkCommand -Id $windowsCmdBefore.id } | Should -Not -Throw # in other words the command should not exist + # test that the queued commands should not exist: + $queuedCmdsAfter.id | Should -Not -Contain $queuedCmdsBefore.Id + $queuedCmdsAfter.id | Should -BeNullOrEmpty + # user.json should have the newID in command associations. + $allUserData = Get-UserJsonData + $testUserData = $allUserData | Where-Object { $_.username -eq $user.username } + $testUserData.commandAssociations.commandId | Should -Contain $macCmdBefore.id + $testUserData.commandAssociations.commandId | Should -Contain $windowsCmdBefore.id + + } + It 'When duplicate commands with the same trigger SHA1 hashes exists, one should be removed, a new command should not be generated' { + # Mock some commands with the trigger to already exist if they do not exist + $possibleMacIDs = New-Object System.Collections.ArrayList + $possibleWindowsIDs = New-Object System.Collections.ArrayList + $macCommandBody = @{ + Name = "RadiusCert-Install:$($user.username):MacOSX" + Command = "sha1234" + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($certData.sha1)" + commandType = "mac" + timeout = 600 + TimeToLiveSeconds = 864000 + } + $newMacCommand = New-JcSdkCommand @macCommandBody + $macCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + $possibleMacIDs.Add($macCmdBefore.id) + $macCommandBody.Command = "sha12345" + + $newMacCommand = New-JcSdkCommand @macCommandBody + $secondMacCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + $possibleMacIDs.Add($secondMacCmdBefore.id) + $secondMacCmdBefore.count | Should -BeGreaterThan 1 + + # invoke the commands manually to simulate the command queue containing items: + Start-JcSdkCommand -Id $macCmdBefore.Id -SystemIds $macSystem.id + + $windowsCommandBody = @{ + Name = "RadiusCert-Install:$($user.username):Windows" + Command = "sha1234" + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($certData.sha1)" + commandType = "windows" + timeout = 600 + TimeToLiveSeconds = 864000 + } + $newWindowsCommand = New-JcSdkCommand @windowsCommandBody + $windowsCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + $possibleWindowsIDs.Add($windowsCmdBefore.id) + $windowsCommandBody.Command = "sha12345" + + $newWindowsCommand = New-JcSdkCommand @windowsCommandBody + $secondWindowsCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + $possibleWindowsIDs.Add($secondWindowsCmdBefore.id) + $secondWindowsCmdBefore.count | Should -BeGreaterThan 1 + + # invoke the commands manually to simulate the command queue containing items: + Start-JcSdkCommand -Id $WindowsCmdBefore.Id -SystemIds $windowsSystem.id + + # Get the queued commands: + $queuedCmdsBefore = Get-QueuedCommandByUser -username $user.username + + # Run Start Deploy User Certs by username + Start-DeployUserCerts -type ByUsername -username $user.username + + # After running, validate that the commands before execution, no longer exist + $macCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + $windowsCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + # Get the queued after: + $queuedCmdsAfter = Get-QueuedCommandByUser -username $user.username + # test that one of the commands should exist: + # $macCmdBefore.id | Should -BeIn $macCmdAfter.id + $macCmdAfter.count | Should -Be 1 + $macCmdAfter.id | should -BeIn $possibleMacIDs + + # $windowsCmdBefore.id | Should -BeIn $windowsCmdAfter.id + $windowsCmdAfter.count | Should -Be 1 + $windowsCmdAfter.id | should -BeIn $possibleWindowsIDs + + # test that the queued commands should not exist: + $queuedCmdsAfter.id | Should -Not -Contain $queuedCmdsBefore.Id + $queuedCmdsAfter.id | Should -BeNullOrEmpty + # user.json should have the newID in command associations. + $allUserData = Get-UserJsonData + $testUserData = $allUserData | Where-Object { $_.username -eq $user.username } + + $testUserData.commandAssociations.commandId | Should -Contain $windowsCmdAfter.id + $testUserData.commandAssociations.commandId | Should -Contain $macCmdAfter.id + } + } + Context 'Force Generate Certificate Tests' { + BeforeEach { + # create a user that has both a mac and windows association + $user = New-RandomUser -Domain "pesterRadius" | New-JCUser + $dateBefore = (Get-Date).ToString('MM/dd/yyyy HH:mm:ss') + # add user to membership group + Add-JCUserGroupMember -GroupID $Global:JCR_USER_GROUP -UserID $user.id + # get random system + $windowsSystem = Get-JCSystem -os windows | Get-Random -Count 1 + $macSystem = Get-JCSystem -os "Mac OS X" | Get-Random -Count 1 + Add-JCSystemUser -UserID $user.id -SystemID $windowsSystem.id + Add-JCSystemUser -UserID $user.id -SystemID $macSystem.id + + # update membership + Get-JCRGlobalVars -skipAssociation -force + # todo: manually update association table to account for new membership + Set-JCRAssociationHash -userId $user.id + Update-JCRUsersJson + + # Generate a user certificate for the user: + Start-GenerateUserCerts -type ByUsername -username $user.username + + # Get the SHA1 hash for the user's cert: + $certData = Get-CertInfo -userCerts -username $user.username + + # Mock some commands with the trigger to already exist if they do not exist + $macCommandBody = @{ + Name = "RadiusCert-Install:$($user.username):MacOSX" + Command = "sha1234" + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($certData.sha1)" + commandType = "mac" + timeout = 600 + TimeToLiveSeconds = 864000 + } + $newMacCommand = New-JcSdkCommand @macCommandBody + $macCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + + # invoke the commands manually to simulate the command queue containing items: + Start-JcSdkCommand -Id $macCmdBefore.Id -SystemIds $macSystem.id + + $windowsCommandBody = @{ + Name = "RadiusCert-Install:$($user.username):Windows" + Command = "sha1234" + launchType = "trigger" + User = "000000000000000000000000" + trigger = "$($certData.sha1)" + commandType = "windows" + timeout = 600 + TimeToLiveSeconds = 864000 + } + $newWindowsCommand = New-JcSdkCommand @windowsCommandBody + $windowsCmdBefore = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + + # invoke the commands manually to simulate the command queue containing items: + Start-JcSdkCommand -Id $WindowsCmdBefore.Id -SystemIds $windowsSystem.id + + # Get the queued commands: + $queuedCmdsBefore = Get-QueuedCommandByUser -username $user.username + + } + It 'When forceGenerate switch is specified, the existing commands & queued commands should be removed' { + # Run Start Deploy User Certs by username + Start-DeployUserCerts -type ByUsername -username $user.username -forceGenerateCommands + + # After running, validate that the commands before execution, no longer exist + $macCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + $windowsCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + # Get the queued after: + $queuedCmdsAfter = Get-QueuedCommandByUser -username $user.username + # test that the commands should not exist: + $macCmdAfter.id | Should -Not -Contain $macCmdBefore.id + $windowsCmdAfter.id | Should -Not -Contain $windowsCmdBefore.id + $macCmdAfter | Should -Not -BeNullOrEmpty + $windowsCmdAfter | Should -Not -BeNullOrEmpty + { Get-JcSdkCommand -Id $macCmdBefore.id } | Should -Throw # in other words the command should not exist + { Get-JcSdkCommand -Id $windowsCmdBefore.id } | Should -Throw # in other words the command should not exist + # test that the queued commands should not exist: + $queuedCmdsAfter.id | Should -Not -Contain $queuedCmdsBefore.Id + $queuedCmdsAfter.id | Should -BeNullOrEmpty + # user.json should have the newID in command associations. + $allUserData = Get-UserJsonData + $testUserData = $allUserData | Where-Object { $_.username -eq $user.username } + $testUserData.commandAssociations.commandId | Should -Not -Contain $macCmdBefore.id + $testUserData.commandAssociations.commandId | Should -Not -Contain $windowsCmdBefore.id + } + It 'When forceGenerate & forceInvoke switches are specified, the existing commands & queued commands should be removed; new commands queues should be queued' { + # Run Start Deploy User Certs by username + Start-DeployUserCerts -type ByUsername -username $user.username -forceGenerateCommands -forceInvokeCommands + + # After running, validate that the commands before execution, no longer exist + $macCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:mac") + $windowsCmdAfter = Get-JcSdkCommand -Filter @("trigger:eq:$($certData.sha1)", "commandType:eq:windows") + # Get the queued after: + $queuedCmdsAfter = Get-QueuedCommandByUser -username $user.username + # test that the commands should not exist: + $macCmdAfter.id | Should -Not -Contain $macCmdBefore.id + $windowsCmdAfter.id | Should -Not -Contain $windowsCmdBefore.id + $macCmdAfter | Should -Not -BeNullOrEmpty + $windowsCmdAfter | Should -Not -BeNullOrEmpty + { Get-JcSdkCommand -Id $macCmdBefore.id } | Should -Throw # in other words the command should not exist + { Get-JcSdkCommand -Id $windowsCmdBefore.id } | Should -Throw # in other words the command should not exist + # test that the queued commands should not exist: + $queuedCmdsAfter.id | Should -Not -Contain $queuedCmdsBefore.Id + $queuedCmdsAfter.id | Should -Not -BeNullOrEmpty + # user.json should have the newID in command associations. + $allUserData = Get-UserJsonData + $testUserData = $allUserData | Where-Object { $_.username -eq $user.username } + $testUserData.commandAssociations.commandId | Should -Not -Contain $macCmdBefore.id + $testUserData.commandAssociations.commandId | Should -Not -Contain $windowsCmdBefore.id + } + } + context 'Deploy by all' { + It "deploys user certs by all" { + Start-DeployUserCerts -type "All" -forceGenerateCommands -forceInvokeCommands + $allUserDataBefore = Get-UserJsonData + Start-Sleep 5 + Start-DeployUserCerts -type "All" -forceGenerateCommands -forceInvokeCommands + $allUserDataAfter = Get-UserJsonData + + foreach ($user in $allUserDataBefore) { + if ($user.certInfo.deploymentDate) { + $user.certInfo.deploymentDate | Should -BeLessThan ($allUserDataAfter | Where-Object { $_.userName -eq $user.UserName }).certInfo.deploymentDate + } + } + } + + } } \ No newline at end of file diff --git a/scripts/automation/Radius/Tests/SetupRadiusOrg.ps1 b/scripts/automation/Radius/Tests/SetupRadiusOrg.ps1 index a5720e30c..19b0330e4 100644 --- a/scripts/automation/Radius/Tests/SetupRadiusOrg.ps1 +++ b/scripts/automation/Radius/Tests/SetupRadiusOrg.ps1 @@ -16,6 +16,13 @@ foreach ($user in $pesterRadiusUsers) { Write-Warning "Removing Pester Radius Users with emailDomain: *pesterRadius*" $usersToRemove = Get-JCuser -email "*pesterRadius*" | Remove-JCUser -force +# remove existing radius commands in test: +Write-Warning "Removing Pester Radius Commands" +$commandsToRemove = Get-JCCommand -Name "RadiusCert-Install:*" +foreach ($commandToRemove in $commandsToRemove) { + Remove-JCCommand -CommandID $commandToRemove._id -force | out-null +} + # Create users Write-Warning "Creating New Pester Radius Users" # user bound to mac @@ -72,4 +79,5 @@ if ($IsMacOS) { $configContent -replace ('\$Global:JCR_OPENSSL = *.+', "`$Global:JCR_OPENSSL = `"$($brewListBinary)`"") | Set-Content -Path $configPath } +$env:certKeyPassword = "TestCertificate123!@#" Import-Module "$psscriptRoot/../JumpCloud-Radius.psd1" -Force \ No newline at end of file