src/cmdlets/common/CertificateHelper.ps1
# Copyright 2021, Adam Edwards # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. . (import-script DisplayTypeFormatter) . (import-script ../../graphservice/ApplicationAPI) . (import-script ../../common/LocalCertificate) . (import-script ../../common/Secret) . (import-script ../../common/GraphApplicationCertificate) . (import-script CommandContext) . (import-script ../Set-GraphApplicationCertificate) ScriptClass CertificateHelper { $appId = $null $objectId = $null $applicationName = $null $certValidityTimespan = $null $certValidityStart = $null $keyLength = $null $connection = $null $app = $null function __initialize($appId, $objectId, [string] $applicationName, $certValidityTimespan, $certValidityStart, $connection, $keyLength) { $this.appId = $appid $this.applicationName = $applicationName $this.certValidityTimespan = $certValidityTimespan $this.certValidityStart = $certValidityStart $this.keyLength = $keyLength $this.connection = $connection $this.app = $null } function NewCertificate([string] $certDirectory, $certStoreLocation, [PSCredential] $certCredential, [bool] $noCertCredential, [bool] $updateApplication, [string] $certificateFilePath, [int] $keyLength) { $targetCertCredential = __GetCertFileCredential $certDirectory $certificateFilePath $certCredential $noCertCredential if ( $updateApplication ) { __SyncApplication } $certificate = __NewLocalCertificate $certStoreLocation if ( $updateApplication ) { __UpdateApplication $certificate } $exportedCertLocation = if ( $certDirectory -or $certificateFilePath) { __ExportCertificate $certificate $targetCertCredential $certDirectory $certificateFilePath } [PSCustomObject] @{ Certificate = $certificate ExportedLocation = $exportedCertLocation } } function __GetCertFileCredential([string] $certDirectory, [string] $certificateFilePath, [PSCredential] $certCredential, [bool] $noCertCredential) { $invalidPath = $certDirectory $targetDirectory = if ( $certDirectory ) { $certDirectory } elseif ( $certificateFilePath ) { $invalidPath = $certificateFilePath split-path -parent $certificateFilePath } if ( $targetDirectory ) { if ( ! (test-path -pathtype container $targetDirectory) ) { throw [ArgumentException]::new("The specified certificate output location '$invalidPath' is not a valid directory or is not contained in a valid directory") } if ( $certCredential ) { $certCredential } elseif ( ! $noCertCredential ) { $userName = if ( $env:user ) { $env:user } else { $env:username } Get-Credential -username $userName -Message "Enter password for certificate to be stored in output directory '$certDirectory'" } } } function __NewLocalCertificate($certStorelocation) { $certificate = new-so GraphApplicationCertificate $this.appId $null $this.applicationName $this.certValidityTimeSpan $this.certValidityStart $certStoreLocation $null $this.keyLength $certificate |=> Create $certificate } function __SyncApplication { if ( $this.app ) { throw 'The application has already been synchronized' } $commandContext = new-so CommandContext $this.connection $null $null $null $::.ApplicationAPI.DefaultApplicationApiVersion $appAPI = new-so ApplicationAPI $commandContext.connection $commandContext.version $app = if ( $this.AppId ) { $appAPI |=> GetApplicationByAppId $this.appId } else { $appAPI |=> GetApplicationByObjectId $this.objectId } if ( ! $this.objectId ) { $this.objectId = $app.id } elseif ( $app.id -ne $this.objectId ) { throw "The object id '$($this.objectId)' specified for the application did not match the actual application object id '$($app.id)'" } if ( ! $this.appId ) { $this.appId = $app.appId } elseif ( $app.id -ne $this.objectId ) { throw "The app id '$($this.appId)' specified for the application did not match the actual application app id '$($app.appId)'" } if ( ! $this.applicationName ) { $this.applicationName = $app.displayName } $this.app = $app } function __UpdateApplication($certificate) { if ( ! $this.app ) { throw 'The application must be queried before performing this operation' } $connectionParameter = @{} if ( $this.connection ) { @{Connection = $this.connection} } Set-GraphApplicationCertificate -AppId $this.appId -ObjectId $this.objectId -Certificate $certificate.X509Certificate } function __ExportCertificate($certificate, [PSCredential] $exportedCertCredential, [string] $certOutputDirectory, [string] $certificateFilePath ) { $certpassword = if ( $exportedCertCredential ) { $exportedCertCredential.Password } $certificate |=> Export $certOutputDirectory $certificateFilePath $certPassword } static { const CERTIFICATE_DISPLAY_TYPE 'AutoGraph.Certificate' function __initialize { __RegisterDisplayType } function CertificateInfoToDisplayableObject($friendlyName, $subject, $graphKeyId, $appId, $appObjectId, $notBefore, $notAfter, $thumbprint, $certificatePath, $exportedCertificatePath) { $::.Secret |=> ToDisplayableSecretInfo Certificate $friendlyName $subject $graphKeyId $appId $appObjectId $notBefore $notAfter $thumbprint $certificatePath $CERTIFICATE_DISPLAY_TYPE $exportedCertificatePath } function CertificateToDisplayableObject($x509Certificate, $appId, $appObjectId, $certStorePath, $keyId, $certificateFilePath) { $notAfter = [DateTimeOffset]::new($x509Certificate.notAfter) $notBefore = [DateTimeOffset]::new($x509Certificate.notBefore) $targetPath = if ( $certStorePath ) { __NormalizeCertStorePath $certStorePath } else { $certificateFilePath } CertificateInfoToDisplayableObject $x509Certificate.FriendlyName $x509Certificate.Subject $null $appId $appObjectId $notBefore $notAfter $x509Certificate.Thumbprint $targetPath $certificateFilePath } function GetConnectionCertCredential($connection, [PSCredential] $certCredential, [boolean] $promptForCertCredentialIfNeeded, [boolean] $noCertCredential) { if ( ! $noCertCredential -and ( $certCredential -or $promptForCertCredentialIfNeeded ) ) { $targetCertificatePath = $connection |=> GetCertificatePath $targetCertCredential = if ( $promptForCertCredentialIfNeeded -and $targetCertificatePath ) { $::.LocalCertificate |=> PromptForCertificateCredential $targetCertificatePath } else { $certCredential } $targetCertCredential.Password } } function __RegisterDisplayType { $typeProperties = @( 'Thumbprint' 'AppId' 'KeyId' 'FriendlyName' 'Subject' 'NotAfter' 'AppId' 'KeyId' 'CertificatePath' 'NotBefore' 'AppObjectId' ) $::.DisplayTypeFormatter |=> RegisterDisplayType $CERTIFICATE_DISPLAY_TYPE $typeProperties $true } function __NormalizeCertStorePath([string] $certStorePath) { $driveAndPath = $certStorePath -split '::' if ( $driveAndPath.length -eq 2 ) { join-path -Path 'Cert:' -ChildPath $driveAndPath[1] } else { $certStorePath } } } } $::.CertificateHelper |=> __initialize |