AuthCommands.ps1
#requires -Version 5.1 $expires = @( [KeeperSecurity.Authentication.TwoFactorDuration]::EveryLogin, [KeeperSecurity.Authentication.TwoFactorDuration]::Every30Days, [KeeperSecurity.Authentication.TwoFactorDuration]::Forever) function Test-InteractiveSession { if ($psISE) { return $true } if ($PSIsInteractive) { return $true } if ($PSPrivateMetadata.JobId) { return $false } return $Host.UI.SupportsVirtualTerminal } function twoFactorChannelToText ([KeeperSecurity.Authentication.TwoFactorChannel] $channel) { if ($channel -eq [KeeperSecurity.Authentication.TwoFactorChannel]::Authenticator) { return 'authenticator' } if ($channel -eq [KeeperSecurity.Authentication.TwoFactorChannel]::TextMessage) { return 'sms' } if ($channel -eq [KeeperSecurity.Authentication.TwoFactorChannel]::DuoSecurity) { return 'duo' } if ($channel -eq [KeeperSecurity.Authentication.TwoFactorChannel]::RSASecurID) { return 'rsa' } if ($channel -eq [KeeperSecurity.Authentication.TwoFactorChannel]::KeeperDNA) { return 'dna' } return '' } function deviceApprovalChannelToText ([KeeperSecurity.Authentication.DeviceApprovalChannel]$channel) { if ($channel -eq [KeeperSecurity.Authentication.DeviceApprovalChannel]::Email) { return 'email' } if ($channel -eq [KeeperSecurity.Authentication.DeviceApprovalChannel]::KeeperPush) { return 'keeper' } if ($channel -eq [KeeperSecurity.Authentication.DeviceApprovalChannel]::TwoFactorAuth) { return '2fa' } return '' } function twoFactorDurationToExpire ([KeeperSecurity.Authentication.TwoFactorDuration] $duration) { if ($duration -eq [KeeperSecurity.Authentication.TwoFactorDuration]::EveryLogin) { return 'now' } if ($duration -eq [KeeperSecurity.Authentication.TwoFactorDuration]::Forever) { return 'never' } return "$([int]$duration)_days" } function getStepPrompt ([KeeperSecurity.Authentication.IAuthentication] $auth) { $prompt = "`nUnsupported ($($auth.step.State.ToString()))" if ($auth.step -is [KeeperSecurity.Authentication.Sync.DeviceApprovalStep]) { $prompt = "`nDevice Approval ($(deviceApprovalChannelToText $auth.step.DefaultChannel))" } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.TwoFactorStep]) { $channelText = twoFactorChannelToText $auth.step.DefaultChannel $prompt = "`n2FA channel($($channelText)) expire[$(twoFactorDurationToExpire $auth.step.Duration)]" } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.PasswordStep]) { $prompt = "`nMaster Password" } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.SsoTokenStep]) { $prompt = "`nSSO Token" } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.SsoDataKeyStep]) { $prompt = "`nSSO Login Approval" } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.ReadyToLoginStep]) { $prompt = "`nLogin" } return $prompt } function printStepHelp ([KeeperSecurity.Authentication.IAuthentication] $auth) { $commands = @() if ($auth.step -is [KeeperSecurity.Authentication.Sync.DeviceApprovalStep]) { $channels = @() foreach ($ch in $auth.step.Channels) { $channels += deviceApprovalChannelToText $ch } if ($channels) { $commands += "channel=<$($channels -join ' | ')> to change channel." } $commands += "`"push`" to send a push to the channel" $commands += '<code> to send a code to the channel' } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.TwoFactorStep]) { $channels = @() foreach ($ch in $auth.step.Channels) { $channelText = twoFactorChannelToText $ch if ($channelText) { $channels += $channelText } } if ($channels) { $commands += "channel=<$($channels -join ' | ')> to change channel." } $channels = @() foreach ($ch in $auth.step.Channels) { $pushes = $auth.step.GetChannelPushActions($ch) if ($null -ne $pushes) { foreach ($push in $pushes) { $channels += [KeeperSecurity.Authentication.AuthUIExtensions]::GetPushActionText($push) } } } if ($channels) { $commands += "`"$($channels -join ' | ')`" to send a push/code" } $channels = @() foreach ($exp in $expires) { $channels += twoFactorDurationToExpire $exp } $commands += "expire=<$($channels -join ' | ')> to set 2fa expiration." $commands += '<code> to send a 2fa code.' } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.PasswordStep]) { $commands += '<password> to send a master password.' } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.SsoTokenStep]) { $commands += $auth.step.SsoLoginUrl $commands += '' if (-not $auth.step.LoginAsProvider) { $commands += '"password" to login using master password.' } $commands += '<sso token> paste SSO login token.' } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.SsoDataKeyStep]) { $channels = @() foreach ($ch in $auth.step.Channels) { $channels += [KeeperSecurity.Authentication.AuthUIExtensions]::SsoDataKeyShareChannelText($ch) } if ($channels) { $commands += "`"$($channels -join ' | ')`" to request login approval" } } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.ReadyToLoginStep]) { $commands += '"login <Keeper Email>" login to Keeper as user' $commands += '"login_sso <Enterprise Domain>" login to Enterprise Domain' } if ($commands) { Write-Output "`nAvailable Commands`n" foreach ($command in $commands) { Write-Output $command } Write-Output '<Enter> to resume' } } function executeStepAction ([KeeperSecurity.Authentication.IAuthentication] $auth, [string] $action) { function tryExpireToTwoFactorDuration ([string] $expire, [ref] [KeeperSecurity.Authentication.TwoFactorDuration] $duration) { $result = $true if ($expire -eq 'now') { $duration.Value = [KeeperSecurity.Authentication.TwoFactorDuration]::EveryLogin } elseif ($expire -eq 'never') { $duration.Value = [KeeperSecurity.Authentication.TwoFactorDuration]::Forever } elseif ($expire -eq '30_days') { $duration.Value = [KeeperSecurity.Authentication.TwoFactorDuration]::Every30Days } else { $duration.Value = [KeeperSecurity.Authentication.TwoFactorDuration]::EveryLogin } return $result } function tryTextToDeviceApprovalChannel ([string] $text, [ref] [KeeperSecurity.Authentication.DeviceApprovalChannel] $channel) { $result = $true if ($text -eq 'email') { $channel.Value = [KeeperSecurity.Authentication.DeviceApprovalChannel]::Email } elseif ($text -eq 'keeper') { $channel.Value = [KeeperSecurity.Authentication.DeviceApprovalChannel]::KeeperPush } elseif ($text -eq '2fa') { $channel.Value = [KeeperSecurity.Authentication.DeviceApprovalChannel]::TwoFactorAuth } else { Write-Output 'Unsupported device approval channel:', $text $result = $false } return $result } function tryTextToTwoFactorChannel ([string] $text, [ref] [KeeperSecurity.Authentication.TwoFactorChannel] $channel) { $result = $true if ($text -eq 'authenticator') { $channel.Value = [KeeperSecurity.Authentication.TwoFactorChannel]::Authenticator } elseif ($text -eq 'sms') { $channel.Value = [KeeperSecurity.Authentication.TwoFactorChannel]::TextMessage } elseif ($text -eq 'duo') { $channel.Value = [KeeperSecurity.Authentication.TwoFactorChannel]::DuoSecurity } elseif ($text -eq 'rsa') { $channel.Value = [KeeperSecurity.Authentication.TwoFactorChannel]::RSASecurID } elseif ($text -eq 'dna') { $channel.Value = [KeeperSecurity.Authentication.TwoFactorChannel]::KeeperDNA } else { Write-Output 'Unsupported 2FA channel:', $text $result = $false } return $result } if ($auth.step -is [KeeperSecurity.Authentication.Sync.DeviceApprovalStep]) { if ($action -eq 'push') { $auth.step.SendPush($auth.step.DefaultChannel).GetAwaiter().GetResult() | Out-Null } elseif ($action -match 'channel\s*=\s*(.*)') { $ch = $Matches.1 [KeeperSecurity.Authentication.DeviceApprovalChannel]$cha = $auth.step.DefaultChannel if (tryTextToDeviceApprovalChannel ($ch) ([ref]$cha)) { $auth.step.DefaultChannel = $cha } } else { Try { $auth.step.SendCode($auth.step.DefaultChannel, $action).GetAwaiter().GetResult() | Out-Null } Catch [KeeperSecurity.Authentication.KeeperApiException] { Write-Warning $_ } Catch { Write-Error $_ } } } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.TwoFactorStep]) { if ($action -match 'channel\s*=\s*(.*)') { $ch = $Matches.1 [KeeperSecurity.Authentication.TwoFactorChannel]$cha = $auth.step.DefaultChannel if (tryTextToTwoFactorChannel($ch) ([ref]$cha)) { $auth.step.DefaultChannel = $cha } } elseif ($action -match 'expire\s*=\s*(.*)') { $exp = $Matches.1 [KeeperSecurity.Authentication.TwoFactorDuration]$dur = $auth.step.Duration if (tryExpireToTwoFactorDuration($exp) ([ref]$dur)) { $auth.step.Duration = $dur } } else { foreach ($cha in $auth.step.Channels) { $pushes = $auth.step.GetChannelPushActions($cha) if ($null -ne $pushes) { foreach ($push in $pushes) { if ($action -eq [KeeperSecurity.Authentication.AuthUIExtensions]::GetPushActionText($push)) { $auth.step.SendPush($push).GetAwaiter().GetResult() | Out-Null return } } } Try { $auth.step.SendCode($auth.step.DefaultChannel, $action).GetAwaiter().GetResult() | Out-Null } Catch { Write-Error $_ } } } } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.PasswordStep]) { Try { $auth.step.VerifyPassword($action).GetAwaiter().GetResult() | Out-Null } Catch [KeeperSecurity.Authentication.KeeperAuthFailed] { Write-Warning 'Invalid password' } Catch { Write-Error $_ } } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.SsoTokenStep]) { if ($action -eq 'password') { $auth.step.LoginWithPassword().GetAwaiter().GetResult() | Out-Null } else { $auth.step.SetSsoToken($action).GetAwaiter().GetResult() | Out-Null } } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.SsoDataKeyStep]) { [KeeperSecurity.Authentication.DataKeyShareChannel]$channel = [KeeperSecurity.Authentication.DataKeyShareChannel]::KeeperPush if ([KeeperSecurity.Authentication.AuthUIExtensions]::TryParseDataKeyShareChannel($action, [ref]$channel)) { $auth.step.RequestDataKey($channel).GetAwaiter().GetResult() | Out-Null } } elseif ($auth.step -is [KeeperSecurity.Authentication.Sync.ReadyToLoginStep]) { if ($action -match '^login\s+(.*)$') { $username = $Matches.1 $auth.Login($username).GetAwaiter().GetResult() | Out-Null } elseif ($action -match '^login_sso\s+(.*)$') { $providerName = $Matches.1 $auth.LoginSso($providerName).GetAwaiter().GetResult() | Out-Null } } } function Connect-Keeper { <# .Synopsis Login to Keeper .Parameter Username User email .Parameter Password User password .Parameter NewLogin Do not use Last Login information .Parameter SsoPassword Use Master Password for SSO account .Parameter SsoProvider Login using SSO provider .Parameter Server Change default keeper server .Parameter Config Config file name #> [CmdletBinding(DefaultParameterSetName = 'regular')] Param( [Parameter(Position = 0)][string] $Username, [Parameter()] [SecureString]$Password, [Parameter()][switch] $NewLogin, [Parameter(ParameterSetName = 'sso_password')][switch] $SsoPassword, [Parameter(ParameterSetName = 'sso_provider')][switch] $SsoProvider, [Parameter()][string] $Server, [Parameter()][string] $Config ) Disconnect-Keeper -Resume | Out-Null if ($Config) { $storage = New-Object KeeperSecurity.Configuration.JsonConfigurationStorage $Config } else { $storage = New-Object KeeperSecurity.Configuration.JsonConfigurationStorage } if (-not $Server) { $Server = $storage.LastServer if ($Server) { Write-Information -MessageData "`nUsing Keeper Server: $Server`n" } else { Write-Information -MessageData "`nUsing Default Keeper Server: $([KeeperSecurity.Authentication.KeeperEndpoint]::DefaultKeeperServer)`n" } } $endpoint = New-Object KeeperSecurity.Authentication.KeeperEndpoint($storage, $Server) $endpoint.DeviceName = 'PowerShell Commander' $endpoint.ClientVersion = 'c16.11.0' $authFlow = New-Object KeeperSecurity.Authentication.Sync.AuthSync($storage, $endpoint) $authFlow.ResumeSession = $true $authFlow.AlternatePassword = $SsoPassword.IsPresent if (-not $NewLogin.IsPresent -and -not $SsoProvider.IsPresent) { if (-not $Username) { $Username = $storage.LastLogin } } $namePrompt = 'Keeper Username' if ($SsoProvider.IsPresent) { $namePrompt = 'Enterprise Domain' } if ($Username) { Write-Output "$(($namePrompt + ': ').PadLeft(21, ' ')) $Username" } elseif (Test-InteractiveSession) { while (-not $Username) { $Username = Read-Host -Prompt $namePrompt.PadLeft(20, ' ') } } else { Write-Error "Non-interactive session detected" -ErrorAction Stop } if ($SsoProvider.IsPresent) { $authFlow.LoginSso($Username).GetAwaiter().GetResult() | Out-Null } else { $passwords = @() if ($Password) { if ($Password -is [SecureString]) { $passwords += [Net.NetworkCredential]::new('', $Password).Password } elseif ($Password -is [String]) { $passwords += $Password } } $authFlow.Login($Username, $passwords).GetAwaiter().GetResult() | Out-Null } Write-Output "" while (-not $authFlow.IsCompleted) { if ($lastStep -ne $authFlow.Step.State) { printStepHelp $authFlow $lastStep = $authFlow.Step.State } $prompt = getStepPrompt $authFlow if ($authFlow.Step -is [KeeperSecurity.Authentication.Sync.PasswordStep]) { if (Test-InteractiveSession) { $securedPassword = Read-Host -Prompt $prompt -AsSecureString if ($securedPassword.Length -gt 0) { $action = [Net.NetworkCredential]::new('', $securedPassword).Password } else { $action = '' } } else { Write-Error "Non-interactive session detected" -ErrorAction Stop } } else { if (Test-InteractiveSession) { $action = Read-Host -Prompt $prompt } else { Write-Error "Non-interactive session detected" -ErrorAction Stop } } if ($action) { if ($action -eq '?') { } else { executeStepAction $authFlow $action } } } if ($authFlow.Step.State -ne [KeeperSecurity.Authentication.Sync.AuthState]::Connected) { if ($authFlow.Step -is [KeeperSecurity.Authentication.Sync.ErrorStep]) { Write-Warning $authFlow.Step.Message } return } $auth = $authFlow if ([KeeperSecurity.Authentication.AuthExtensions]::IsAuthenticated($auth)) { Write-Debug -Message "Connected to Keeper as $Username" $vault = New-Object KeeperSecurity.Vault.VaultOnline($auth) $task = $vault.SyncDown() Write-Information -MessageData 'Syncing ...' $task.GetAwaiter().GetResult() | Out-Null $vault.AutoSync = $true $Script:Context.Auth = $auth $Script:Context.Vault = $vault [KeeperSecurity.Vault.VaultData]$vaultData = $vault Write-Information -MessageData "Decrypted $($vaultData.RecordCount) record(s)" Set-KeeperLocation -Path '\' | Out-Null } } $Keeper_ConfigServerCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $prefixes = @('', 'dev.', 'qa.') $suffixes = $('.com', '.eu') $prefixes | ForEach-Object { $p = $_; $suffixes | ForEach-Object { $s = $_; "${p}keepersecurity${s}" } } | Where-Object { $_.StartsWith($wordToComplete) } } Register-ArgumentCompleter -Command Connect-Keeper -ParameterName Server -ScriptBlock $Keeper_ConfigServerCompleter New-Alias -Name kc -Value Connect-Keeper function Disconnect-Keeper { <# .Synopsis Logout from Keeper #> [CmdletBinding()] Param( [Parameter()][switch] $Resume ) $Script:Context.AvailableTeams = $null $Script:Context.AvailableUsers = $null $Script:Context.ManagedCompanyId = 0 $Script:Context.Enterprise = $null $vault = $Script:Context.Vault if ($vault) { $vault.Dispose() | Out-Null } $Script:Context.Vault = $null [KeeperSecurity.Authentication.IAuthentication] $auth = $Script:Context.Auth if ($auth) { if (-not $Resume.IsPresent) { $auth.Logout().GetAwaiter().GetResult() | Out-Null } $auth.Dispose() | Out-Null } $Script:Context.Auth = $null } New-Alias -Name kq -Value Disconnect-Keeper function Sync-Keeper { <# .Synopsis Sync down with Keeper #> [CmdletBinding()] [KeeperSecurity.Vault.VaultOnline]$vault = $Script:Context.Vault if ($vault) { $task = $vault.SyncDown() $task.GetAwaiter().GetResult() | Out-Null } else { Write-Error -Message "Not connected" -ErrorAction Stop } } New-Alias -Name ks -Value Sync-Keeper function Get-KeeperInformation { <# .Synopsis Prints account license information #> $vault = getVault [KeeperSecurity.Authentication.IAuthentication]$auth = $vault.Auth [KeeperSecurity.Authentication.AccountLicense]$license = $auth.AuthContext.License switch ($license.AccountType) { 0 { $accountType = $license.ProductTypeName } 1 { $accountType = 'Family Plan'} 2 { $accountType = 'Enterprise' } Default { $accountType = $license.ProductTypeName } } $accountType = 'Enterprise' [PSCustomObject]@{ PSTypeName = "KeeperSecurity.License.Info" User = $auth.Username Server = $auth.Endpoint.Server Admin = $auth.AuthContext.IsEnterpriseAdmin AccountType = $accountType RenewalDate = $license.ExpirationDate StorageCapacity = [int] [Math]::Truncate($license.BytesTotal / (1024 * 1024 * 1024)) StorageUsage = [int] [Math]::Truncate($license.BytesUsed * 100 / $license.BytesTotal) StorageExpires = $license.StorageExpirationDate } if ($license.AccountType -eq 2) { $enterprise = getEnterprise if ($enterprise) { $enterpriseLicense = $enterprise.enterpriseData.EnterpriseLicense $productTypeId = $enterpriseLicense.ProductTypeId if ($productTypeId -in @(2, 5)) { $tier = $enterpriseLicense.Tier if ($tier -eq 1) { $plan = 'Enterprise' } else { $plan = 'Business' } } elseif ($productTypeId -in @(9, 10)) { $distributor = $enterpriseLicense.Distributor if ($distributor -eq $true) { $plan = 'Distributor' } else { $plan = 'Managed MSP' } } elseif ($productTypeId -in @(11, 12)) { $plan = 'Keeper MSP' } elseif ($productTypeId -eq 8) { $tier = $enterpriseLicense.Tier if ($tier -eq 1) { $plan = 'Enterprise' } else { $plan = 'Business' } $plan = "MC $plan" } else { $plan = 'Unknown' } if ($productTypeId -in @(5, 10, 12)) { $plan = "$plan Trial" } $enterpriseInfo = [PSCustomObject]@{ PSTypeName = "KeeperSecurity.License.EnterpriseInfo" LicenseType = 'Enterprise' EnterpriseName = $enterprise.loader.EnterpriseName BasePlan = $plan } if ($enterpriseLicense.Paid) { $expiration = $enterpriseLicense.Expiration if ($expiration -gt 0) { $exp = [KeeperSecurity.Utils.DateTimeOffsetExtensions]::FromUnixTimeMilliseconds($expiration) $expDate = $exp.ToString('d') Add-Member -InputObject $enterpriseInfo -MemberType NoteProperty -Name 'Expires' -Value $expDate } switch ($enterpriseLicense.filePlanTypeId) { -1 { $filePlan = 'No Storage' } 0 { $filePlan = 'Trial' } 1 { $filePlan = '1GB' } 2 { $filePlan = '10GB' } 3 { $filePlan = '50GB' } 4 { $filePlan = '100GB' } 5 { $filePlan = '250GB' } 6 { $filePlan = '500GB' } 7 { $filePlan = '1TB' } 8 { $filePlan = '10TB' } Default { $filePlan = '???' } } Add-Member -InputObject $enterpriseInfo -MemberType NoteProperty -Name 'StorageCapacity' -Value $filePlan $numberOfSeats = $enterpriseLicense.NumberOfSeats if ($numberOfSeats -gt 0) { Add-Member -InputObject $enterpriseInfo -MemberType NoteProperty -Name 'TotalUsers' -Value $numberOfSeats } $seatsAllocated = $enterpriseLicense.SeatsAllocated if ($seatsAllocated -gt 0) { Add-Member -InputObject $enterpriseInfo -MemberType NoteProperty -Name 'ActiveUsers' -Value $seatsAllocated } $seatsPending = $enterpriseLicense.SeatsPending if ($seatsAllocated -gt 0) { Add-Member -InputObject $enterpriseInfo -MemberType NoteProperty -Name 'InvitedUsers' -Value $SeatsPending } } $enterpriseInfo } } } New-Alias -Name kwhoami -Value Get-KeeperInformation function compareArrays { param ($array1, $array2) if ($array1.Length -eq $array2.Length) { foreach ($i in 0..($array1.Length-1)) { if ($array1[$i] -ne $array2[$i]) { return $false } } return $true } return $false } function formatTimeout { param ($timeout) if ($timeout -gt 0) { $dayMillis = [TimeSpan]::FromDays(1).TotalMilliseconds if ($logoutTimer -gt $dayMillis) { return "$([Math]::Round($logoutTimer / $dayMillis)) day(s)" } $hourMillis = [TimeSpan]::FromHours(1).TotalMilliseconds if ($logoutTimer -gt $hourMillis) { return "$([Math]::Round($logoutTimer / $hourMillis)) hour(s)" } $minuteMillis = [TimeSpan]::FromMinutes(1).TotalMilliseconds return "$([Math]::Round($logoutTimer / $minuteMillis)) minute(s)" } } function Get-KeeperDeviceSettings { <# .SYNOPSIS Display settings of the current device #> $vault = getVault $auth = $vault.Auth $accountSummary = [KeeperSecurity.Authentication.AuthExtensions]::LoadAccountSummary($auth).GetAwaiter().GetResult() $device = $accountSummary.Devices | Where-Object { compareArrays $_.EncryptedDeviceToken $auth.DeviceToken } | Select-Object -First 1 if (-not $device) { Write-Error -Message "The current device could not be found" -ErrorAction Stop } $logoutTimer = $accountSummary.Settings.LogoutTimer if ($logoutTimer -gt 0) { $logoutTimerText = formatTimeout $logoutTimer } else { $logoutTimerText = '1 hour(s)' } $persistentLoginRestricted = $false if ($accountSummary.Enforcements.Booleans) { $plp = $accountSummary.Enforcements.Booleans | Where-Object { $_.Key -eq 'restrict_persistent_login' } | Select-Object -First 1 if ($plp) { $persistentLoginRestricted = $plp.Value } } $persistentLoginEnabled = $false if (-not $persistentLoginRestricted) { $persistentLoginEnabled = $accountSummary.Settings.PersistentLogin } $settings = [PSCustomObject]@{ PSTypeName = "KeeperSecurity.Authentication.DeviceInfo" DeviceName = $device.DeviceName PersistentLogin = $persistentLoginEnabled DataKeyPresent = $device.EncryptedDataKeyPresent IpAutoApprove = -not $accountSummary.Settings.IpDisableAutoApprove IsSsoUser = $accountSummary.Settings.SsoUser DeviceLogoutTimeout = $logoutTimerText } if ($accountSummary.Enforcements.Longs) { $enf = $accountSummary.Enforcements.Longs | Where-Object { $_.Key -eq 'logout_timer_desktop' } | Select-Object -First 1 if ($enf.Length -eq 1) { $entLogoutTimer = $enf.Value if ($entLogoutTimer -gt 0) { $entLogoutTimerText = formatTimeout $entLogoutTimer Add-Member -InputObject $settings -MemberType NoteProperty -Name 'EnterpriseLogoutTimeout' -Value $entLogoutTimerText } } } $settings } function Set-KeeperDeviceSettings { <# .SYNOPSIS Modifies the current device settings .PARAMETER NewName Modifies device name .PARAMETER Timeout Sets inactivity timeout. Format: NUMBER[h|d] default - minutes, h - hours, d - days .PARAMETER Register Register current device for Persistent Login .PARAMETER PersistentLogin Enables or disables Persistent login for account ON | OFF .PARAMETER IpAutoApprove Enables or disables Automatic Approval by IP address for account ON | OFF .EXAMPLE C:\PS> Set-KeeperDeviceSettings -NewName 'Azure' -Timeout 30d -PersistentLogin ON -Register #> [CmdletBinding()] Param ( [Parameter()][String] $NewName, [Parameter(HelpMessage='NUMBER[h|d]')][String] $Timeout, [Parameter()][Switch] $Register, [Parameter()][ValidateSet('ON', 'OFF')][String] $PersistentLogin, [Parameter()][ValidateSet('ON', 'OFF')][String] $IpAutoApprove ) $vault = getVault $auth = $vault.Auth $accountSummary = [KeeperSecurity.Authentication.AuthExtensions]::LoadAccountSummary($auth).GetAwaiter().GetResult() $device = $accountSummary.Devices | Where-Object { compareArrays $_.EncryptedDeviceToken $auth.DeviceToken } | Select-Object -First 1 if (-not $device) { Write-Error -Message "The current device could not be found" -ErrorAction Stop } $changed = $false if ($NewName) { $request = New-Object Authentication.DeviceUpdateRequest $request.ClientVersion = $auth.Endpoint.ClientVersion $request.DeviceStatus = [Authentication.DeviceStatus]::DeviceOk $request.DeviceName = $NewName $request.EncryptedDeviceToken = $device.EncryptedDeviceToken $auth.ExecuteAuthRest("authentication/update_device", $request, $null, 0).GetAwaiter().GetResult() | Out-Null Write-Information "Device name was changed to `"$NewName`"" $changed = $true } $persistentLoginRestricted = $false if ($accountSummary.Enforcements.Booleans) { $plp = $accountSummary.Enforcements.Booleans | Where-Object { $_.Key -eq 'restrict_persistent_login' } | Select-Object -First 1 if ($plp) { $persistentLoginRestricted = $plp.Value } } if ($Register.IsPresent) { if ($persistentLoginRestricted -eq $true) { Write-Error "Persistent Login feature is restricted by Enterprise Administrator" -ErrorAction Stop } $registered = [KeeperSecurity.Authentication.AuthExtensions]::RegisterDataKeyForDevice($auth, $device).GetAwaiter().GetResult() if ($registered) { Write-Information "Device is registered for Persistent Login" } $changed = $true } if ($PersistentLogin) { if ($persistentLoginRestricted -eq $true) { Write-Error "Persistent Login feature is restricted by Enterprise Administrator" -ErrorAction Stop } $value = '0' if ($PersistentLogin -eq 'ON') { $value = '1' } [KeeperSecurity.Authentication.AuthExtensions]::SetSessionParameter($auth, 'persistent_login', $value).GetAwaiter().GetResult() | Out-Null $changed = $true } if ($IpAutoApprove) { $value = '1' if ($IpAutoApprove -eq 'ON') { $value = '0' } [KeeperSecurity.Authentication.AuthExtensions]::SetSessionParameter($auth, 'ip_disable_auto_approve', $value).GetAwaiter().GetResult() | Out-Null $changed = $true } if ($Timeout) { $lastLetter = $Timeout[-1] if ($lastLetter -eq 'd') { $timeoutInt = $Timeout.Substring(0, $Timeout.Length - 1) } elseif ($lastLetter -eq 'h') { $timeoutInt = $Timeout.Substring(0, $Timeout.Length - 1) } else { $lastLetter = '' $timeoutInt = $Timeout } $minutes = $null $b = [int]::TryParse($timeoutInt, [ref]$minutes) if (-not $b) { Write-Error "Invalid timeout value `"$Timeout`". Format NUMBER[h|d]. d-days, h-hours. default minutes " -ErrorAction Stop } if ($lastLetter -eq 'h') { $minutes = $minutes * 60 } elseif ($lastLetter -eq 'd') { $minutes = $minutes * (60 * 24) } [KeeperSecurity.Authentication.AuthExtensions]::SetSessionInactivityTimeout($auth, $minutes).GetAwaiter().GetResult() | Out-Null $changed = $true } if (-not $changed) { Get-KeeperDeviceSettings } } New-Alias -Name this-device -Value Set-KeeperDeviceSettings # SIG # Begin signature block # MIIngQYJKoZIhvcNAQcCoIIncjCCJ24CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCApov4BNnT7mspu # WPF7Jg8Gm+4jgqCtV3iro81t8cqcv6CCIQQwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqG # SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMy # RGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcg # Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXH # JQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMf # UBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w # 1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRk # tFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYb # qMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUm # cJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP6 # 5x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzK # QtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo # 80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjB # Jgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXche # MBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB # /wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU # 7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoG # CCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j # c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDig # NqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v # dEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZI # hvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd # 4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiC # qBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl # /Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeC # RK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYT # gAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/ # a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37 # xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmL # NriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0 # YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJ # RyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIG # sDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw # HhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0 # ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjAN # BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zr # PYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHM # gQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8Irg # nQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyC # EUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0 # p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQa # khCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0 # XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960I # HnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2 # FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBH # X8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q2 # 7IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD # VR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1k # TN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD # AzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj # ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmww # HAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIB # ADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6j # fCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmI # moqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtf # JqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrx # oj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3 # LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx # 4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9 # Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+I # Cw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug # 0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5 # Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIGvDCCBKSgAwIBAgIQ # C65mvFq6f5WHxvnpBOMzBDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0 # ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAw # MDAwMFoXDTM1MTEyNTIzNTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERp # Z2lDZXJ0MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjE # iDtqmeOlwf0KMCBDEr4IxHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOc # Re8+CEJp+3R2O8oo76EO7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/ # GLoUb35SfWHh43rOH3bpLEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0Cha # V76Nhnj37DEYTX9ReNZ8hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8U # uKGn9966fR5X6kgXj3o5WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHw # SJ+QQRZ1fisD8UTVDSupWJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4 # EfvFrpVNnes4c16Jidj5XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzI # Xp4P0wXkgNs+CO/CacBqU0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3Jyidx # W48jwBqIJqImd93NRxvd1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizch # NULpUEoA6Vva7b1XCB+1rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJ # cv6dQ4aEKOX5AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/ # BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEE # AjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8w # HQYDVR0OBBYEFJ9XLAN3DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuG # SWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw # OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQG # CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKG # TGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJT # QTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIB # AD2tHh92mVvjOIQSR9lDkfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq # 3igpwrPvBmZdrlWBb0HvqT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcH # zBMutB6HzeledbDCzFzUy34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTV # OoJ4eTq7gj9UFAL1UruJKlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4H # v5swO+aAXxWUm3WpByXtgVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgt # d7/fvWTlCs30VAGEsshJmLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaid # RJXrI+UzB6vAlk/8a1u7cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhd # mm4bhYsVA6G2WgNFYagLDBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dH # PoWrUhftNpFC5H7QEY7MhKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDi # CLg4D+TPVgKx2EgEdeoHNHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7z # cEO1xwcdcqJsyz/JceENc2Sg8h3KeFUCS7tpFk7CrDqkMIIHSTCCBTGgAwIBAgIQ # BaOjGrg1T58olh09AgdhuDANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0 # ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTI0 # MTIzMTAwMDAwMFoXDTI1MTIzMDIzNTk1OVowgdExEzARBgsrBgEEAYI3PAIBAxMC # VVMxGTAXBgsrBgEEAYI3PAIBAhMIRGVsYXdhcmUxHTAbBgNVBA8MFFByaXZhdGUg # T3JnYW5pemF0aW9uMRAwDgYDVQQFEwczNDA3OTg1MQswCQYDVQQGEwJVUzERMA8G # A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xHTAbBgNVBAoTFEtlZXBl # ciBTZWN1cml0eSBJbmMuMR0wGwYDVQQDExRLZWVwZXIgU2VjdXJpdHkgSW5jLjCC # AaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAM7/rBevApUP+XJjlSxdyASA # AnLFQ1r4NFXPo/S0RaTv1OCahApEeSN6oy+0OwbLNlwaQeooOanMcZhh64/+fF8S # zCMHDc/Pv8aBsd1B2XIw/VT+Nawfj0NxAX1zpKPp/tPqavm6smRDMOAeOo7qLxzI # u68bS2EnqvST1367tMpxhggrVl3GYKPhdCPeNDRskwheCSxI2czR8oe7mguo2nVa # ZR5VEq4xYkMZwTuT7RN8ER4r5crOSbJFyabp79SgYP7NyKmDcYZ6XJ26AfZsEDZr # e4VhzaqO0rl8i5HBmVmDKwU0PaIoAUdyeultIaS5oe0FjcTjGtrkBl+B7TCtvN1J # RE9Tmy3spnqLyvlRhrVJdDKCGovQKKJk87BAjIoiNSmEXs0H0PbB1ZYOA6m4ce7/ # BOmUafliYWBqrWHmHixqi/ha5ZKxKlYxGlikD4p1WlMmDEBhg3RPodW1Z5eGq92Z # exMGOWsfOQp3YhTDdMOA7tjWP2XzAaebGxCeOENEpQIDAQABo4ICAjCCAf4wHwYD # VR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFOcovsKg6xAz # zjzRmmWQRpa7p47MMD0GA1UdIAQ2MDQwMgYFZ4EMAQMwKTAnBggrBgEFBQcCARYb # aHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB/wQEAwIHgDATBgNV # HSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3Js # My5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQw # OTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAy # MUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUFBzABhhhodHRwOi8v # b2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6Ly9jYWNlcnRzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI # QTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggIBALIq # AoEjkKZluMiOffwU+V+wiKkmDblKIZymyszEZot+niB6g7tRXrWkQo6gn8OG2qG6 # IO8L+o0VvwW0+V08p6gVqb0jeR9kCm7kDZk2RmzevhZDrRbZj0Q7Kb3pIeD9KEuc # RfEF0UGqgp0q7jerFXPzKtQk5kJpP65sSRV7bghIMWtq5sdHn/iGUMj+8Fd9AExq # 4kR+dyTw/6p1ZFiY7pIv4YAjjDrjkyUMSogt6ej9YGwTC8yVXJsjarLq2F+svwn8 # NlU+T03U/ZjXc/ZxDc5g3iqrl5Gm9QCaLhG2aLIrGRXN59Pcokp7JFNa6nkkWSSg # h4w01tz+xRSyiqKWAXNs2lHTD2F9ceGlz9Uw/RvPhPcl6bILqJcR6RUkzZtrKHNK # j85PBm/Kmurx0co5xRxXsXsF3tmp2r+Tt11veA9je+pyzuqE/kRQPn5hF8fIRuea # h7JVMaaHBTMbRaDcVFioGmCGHUx270yhLapA0eYXpZJv0n62QIMoX9NPcW2EcwhL # WGAV1IW+TIo/xcprAXBtXCO/mhscgInbMzesdg0uWsboiy4HfeTEzCe9ld54biUK # TJQu4wqbzkN5SGewOKTd/+c4k5w6yzuUWsk3YZpjWqsgpTlA3zU591uvMFsq0FYd # A3Py8YsVabLwTxz9d7kpBAHTPRYwDcsKNLGMPc+6MYIF0zCCBc8CAQEwfTBpMQsw # CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERp # Z2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIw # MjEgQ0ExAhAFo6MauDVPnyiWHT0CB2G4MA0GCWCGSAFlAwQCAQUAoIGEMBgGCisG # AQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJSL # LG1Nj/1ffcESJJQh5F4nFCcWBysXYMfafG+Bmd0PMA0GCSqGSIb3DQEBAQUABIIB # gIs3HC7Ew9A8tj8l7zv7Hs3BmUI7WP08lHdx8hAV1Qc/XMdTkklMid5HLUddEd59 # kucjsgYvR7SEwdrxS0nZR61ihGC9wywe5ELsjuhE2ojVhf8cP9RkIfpvt2ckrxNK # Q96Rw273HvNIYpHQWj7yanuqmw6/VOvqEKEnKZg1LeDPKANRaQ4ztooHJU5Y0lWi # oYv9YE7OdkNbhF4JGTX0V2yZj+8JeoanX4yo+7h1Ab+HT4qGWxSEYa0rlmppLw6O # 5uLv+xBgV5Pe1QLKLpBlDN6eCMXEiJdu30qODb2CRWZotJeCao3CJPiRf5GVXMub # 9gbjfuDLMggWq2ui1OfzkBUPnnLaUe0Z4cuwV69Df49Zy4g3lr4oSPgtfcpFhP7j # QHfXl375h3wD2cB4GvHDAPtFxuXzrxqyyBDP+LaSvKvs/DAlppIKIsr3aoKKePc6 # iCRmbVUYFqvBgr6F6N/cjFV4R0DWcOm3sEddmtLIa65YsaPWaeYxsGn2E7ynJVRQ # taGCAyAwggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYTAlVT # MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1 # c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAuuZrxaun+V # h8b56QTjMwQwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN # AQcBMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjcyMzI3NTRaMC8GCSqGSIb3DQEJBDEi # BCCJ42SCjU/DcC0dzQGXBqPwpaNarlwHR73vcXEMRZbU4DANBgkqhkiG9w0BAQEF # AASCAgBc0TAaJtKpKI3AYqLPlGibiBZpzSDBPmPiZSEtDtJST20/80piX4220qAX # mQ/swTIc3XV0w+6/jU+VPBbDlISx7WDJPnf5vT8GXgC4EIHLC9XmdHmU4N23IgZE # KvwHkPgVLzl7ucI5gERxEnrCFDzHXwtCV+XDTHUKBdE5mU69Sn102oqVmrj5Hahh # n4zi+tgU/C4FbN7CQvbXYOY3d5rKeLOUydAEs7QCd6biPKaUzA2qLfldsfJ1dv9D # YFoi2Ttu2kPYLJGmO7f8p64WoIiwgoleRJMqh1m0fOAMCW5md8Ef6ATs0wTNltID # BguIeU6yF1O+Bi944TxRfdfem8ylumZ57lR1hsiq8e/rDkRSJpOkRcgnJKQULlpZ # /e9oePXWot6lADrnsFRPtcMEPJGzkpGhDKQS2Kp41J+bmzzBn1RY80zVeOH7O7SG # 12+3+/tR/RplSsJ8+s7kBwZmFt6BiIv46e7cttOWICriIYekilD2irhI/dKtUJVW # jIx5VValo9BmrkF4F19m5+I5mHzSq2DwI/2EsaBtqJkWBTwq/D/2D/sLTtlAWOdU # FWC4RlbsXO7ZA7fHvUyE42/VVtqLrWQx9JoBX2c5gVTom7VdLM9UA8YT/HOnmp2/ # WbUsYvVL7oXhRH/pWPyeBu0JdIhEg8Q6gJvNNskV+QtpotbYQw== # SIG # End signature block |