Sharing.ps1
#requires -Version 5.1 function Show-KeeperRecordShare { <# .Synopsis Shows a record sharing information .Parameter Record Record UID or any object containing property Uid #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true, ValueFromPipeline=$true)]$Records ) Begin { [KeeperSecurity.Vault.VaultOnline]$vault = getVault [string[]]$recordUids = @() } Process { foreach ($r in $Records) { $uid = $null if ($r -is [String]) { $uid = $r } elseif ($null -ne $r.Uid) { $uid = $r.Uid } if ($uid) { [KeeperSecurity.Vault.KeeperRecord] $rec = $null if (-not $vault.TryGetKeeperRecord($uid, [ref]$rec)) { $entries = Get-KeeperChildItem -Filter $uid -ObjectType Record if ($entries.Uid) { $vault.TryGetRecord($entries[0].Uid, [ref]$rec) | Out-Null } } if ($rec) { $recordUids += $rec.Uid } else { Write-Error -Message "Cannot find a Keeper record: $r" -ErrorAction SilentlyContinue } } } } End { $vault.GetSharesForRecords($recordUids).GetAwaiter().GetResult() } } New-Alias -Name kshrsh -Value Show-KeeperRecordShare function Grant-KeeperRecordAccess { <# .Synopsis Shares a record with user .Parameter Record Record UID or any object containing property Uid .Parameter User User email .Parameter CanEdit Grant edit permission .Parameter CanShare Grant re-share permission #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)]$Record, [Parameter(Mandatory = $true)]$User, [Parameter()][switch]$CanEdit, [Parameter()][switch]$CanShare ) [KeeperSecurity.Vault.VaultOnline]$vault = getVault if ($Record -is [Array]) { if ($Record.Count -ne 1) { Write-Error -Message 'Only one record is expected' -ErrorAction Stop } $Record = $Record[0] } $uid = $null if ($Record -is [String]) { $uid = $Record } elseif ($null -ne $Record.Uid) { $uid = $Record.Uid } if ($uid) { [KeeperSecurity.Vault.KeeperRecord] $rec = $null if (-not $vault.TryGetKeeperRecord($uid, [ref]$rec)) { $entries = Get-KeeperChildItem -Filter $uid -ObjectType Record if ($entries.Uid) { $vault.TryGetRecord($entries[0].Uid, [ref]$rec) | Out-Null } } if ($rec) { try { $vault.ShareRecordWithUser($rec.Uid, $User, $CanShare.IsPresent, $CanEdit.IsPresent).GetAwaiter().GetResult() | Out-Null Write-Output "Record `"$($rec.Title)`" was shared with $($User)" } catch [KeeperSecurity.Vault.NoActiveShareWithUserException] { Write-Output $_ $prompt = "Do you want to send share invitation request to `"$($User)`"? (Yes/No)" $answer = Read-Host -Prompt $prompt if ($answer -in 'yes', 'y') { $vault.SendShareInvitationRequest($User).GetAwaiter().GetResult() | Out-Null Write-Output("Invitation has been sent to $($User)`nPlease repeat this command when your invitation is accepted."); } } } else { Write-Error -Message "Cannot find a Keeper record: $Record" } } } New-Alias -Name kshr -Value Grant-KeeperRecordAccess function Revoke-KeeperRecordAccess { <# .Synopsis Shares a record with user .Parameter Record Record UID or any object containg record UID .Parameter User User email #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true)]$Record, [Parameter(Mandatory = $true)]$User ) [KeeperSecurity.Vault.VaultOnline]$vault = getVault if ($Record -is [Array]) { if ($Record.Count -ne 1) { Write-Error -Message 'Only one record is expected' return } $Record = $Record[0] } $uid = $null if ($Record -is [String]) { $uid = $Record } elseif ($null -ne $Record.Uid) { $uid = $Record.Uid } $found = $false if ($uid) { [KeeperSecurity.Vault.KeeperRecord] $rec = $null if (-not $vault.TryGetKeeperRecord($uid, [ref]$rec)) { $entries = Get-KeeperChildItem -Filter $uid -ObjectType Record if ($entries.Uid) { $vault.TryGetRecord($entries[0].Uid, [ref]$rec) | Out-Null } } if ($rec) { $found = $true $vault.RevokeShareFromUser($rec.Uid, $User).GetAwaiter().GetResult() | Out-Null Write-Output "Record `"$($rec.Title)`" share has been removed from $($username)" } } if (-not $found) { Write-Error -Message "Cannot find a Keeper record: $Record" } } New-Alias -Name kushr -Value Revoke-KeeperRecordAccess function Grant-KeeperSharedFolderAccess { <# .Synopsis Adds a user or team to a shared foler .Parameter SharedFolder Shared Folder UID, name or any object containing property Uid .Parameter User User email .Parameter Team Team Name or UID .Parameter ManageRecords Grant Manage Records permission .Parameter ManageUsers Grant Manage Users permission #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)]$SharedFolder, [Parameter(Mandatory = $true, ParameterSetName='user')]$User, [Parameter(Mandatory = $true, ParameterSetName='team')]$Team, [Parameter()][switch]$ManageRecords, [Parameter()][switch]$ManageUsers ) [KeeperSecurity.Vault.VaultOnline]$private:vault = getVault if ($SharedFolder -is [Array]) { if ($SharedFolder.Count -ne 1) { Write-Error -Message 'Only one shared folder is expected' return } $SharedFolder = $SharedFolder[0] } $uid = $null if ($SharedFolder -is [String]) { $uid = $SharedFolder } elseif ($null -ne $Record.Uid) { $uid = $SharedFolder.Uid } if (-not $uid) { Write-Error -Message "Cannot find Shared Folder: $SharedFolder" -ErrorAction Stop } [KeeperSecurity.Vault.SharedFolder] $sf = $null if (-not $vault.TryGetSharedFolder($uid, [ref]$sf)) { $sf = $vault.SharedFolders | Where-Object { $_.Name -eq $uid } | Select-Object -First 1 } if (-not $sf) { Write-Error -Message "Cannot find Shared Folder: $SharedFolder" -ErrorAction Stop } if ($User) { $userType = [KeeperSecurity.Vault.UserType]::User $userId = ([MailAddress]$User).Address $userName = $userId if (-not $userId) { return } } elseif ($Team) { $userType = [KeeperSecurity.Vault.UserType]::Team [KeeperSecurity.Vault.TeamInfo]$teamInfo = $null if ($vault.TryGetTeam($Team, [ref]$teamInfo)) { $userId = $teamInfo.TeamUid $userName = $teamInfo.Name } else { $teamInfo = $vault.Teams | Where-Object { $_.Name -eq $Team } | Select-Object -First 1 if ($teamInfo) { $userId = $teamInfo.TeamUid $userName = $teamInfo.Name } } if (-not $userId) { ensureAvalableLoaded $teamInfo = $Script:Context.AvailableTeams | Where-Object { $_.TeamUid -ceq $Team -or $_.Name -eq $Team } | Select-Object -First 1 if ($teamInfo) { $userId = $teamInfo.TeamUid $userName = $teamInfo.Name } } if (-not $userId) { Write-Error -Message "Cannot find team: $Team" -ErrorAction Stop } } try { $options = New-Object KeeperSecurity.Vault.SharedFolderUserOptions $options.ManageRecords = $ManageRecords.IsPresent $options.ManageUsers = $ManageUsers.IsPresent $vault.PutUserToSharedFolder($sf.Uid, $userId, $userType, $options).GetAwaiter().GetResult() | Out-Null Write-Output "${userType} `"$($userName)`" has been added to shared folder `"$($sf.Name)`"" } catch [KeeperSecurity.Vault.NoActiveShareWithUserException] { Write-Output $_ $prompt = "Do you want to send share invitation request to `"$($User)`"? (Yes/No)" $answer = Read-Host -Prompt $prompt if ($answer -in 'yes', 'y') { $vault.SendShareInvitationRequest($User).GetAwaiter().GetResult() | Out-Null Write-Output("Invitation has been sent to `"$($User)`"`nPlease repeat this command when your invitation is accepted."); } } } function Revoke-KeeperSharedFolderAccess { <# .Synopsis Removes record share from user .Parameter SharedFolder Shared Folder UID, name or any object containing property Uid .Parameter User User email .Parameter Team Team Name or UID #> [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)]$SharedFolder, [Parameter(Mandatory = $true, ParameterSetName='user')]$User, [Parameter(Mandatory = $true, ParameterSetName='team')]$Team ) [KeeperSecurity.Vault.VaultOnline]$private:vault = getVault if ($SharedFolder -is [Array]) { if ($SharedFolder.Count -ne 1) { Write-Error -Message 'Only one shared folder is expected' return } $SharedFolder = $SharedFolder[0] } $uid = $null if ($SharedFolder -is [String]) { $uid = $SharedFolder } elseif ($null -ne $Record.Uid) { $uid = $SharedFolder.Uid } if (-not $uid) { Write-Error -Message "Cannot find Shared Folder: $SharedFolder" -ErrorAction Stop } [KeeperSecurity.Vault.SharedFolder] $sf = $null if (-not $vault.TryGetSharedFolder($uid, [ref]$sf)) { $sf = $vault.SharedFolders | Where-Object { $_.Name -eq $uid } | Select-Object -First 1 } if (-not $sf) { Write-Error -Message "Cannot find Shared Folder: $SharedFolder" -ErrorAction Stop } if ($User) { $userType = [KeeperSecurity.Vault.UserType]::User $userId = ([MailAddress]$User).Address $userName = $userId if (-not $userId) { return } } elseif ($Team) { $userType = [KeeperSecurity.Vault.UserType]::Team [KeeperSecurity.Vault.TeamInfo]$teamInfo = $null if ($vault.TryGetTeam($Team, [ref]$teamInfo)) { $userId = $teamInfo.TeamUid $userName = $teamInfo.Name } else { $teamInfo = $vault.Teams | Where-Object { $_.Name -eq $Team } | Select-Object -First 1 if ($teamInfo) { $userId = $teamInfo.TeamUid $userName = $teamInfo.Name } } if (-not $userId) { ensureAvalableLoaded $teamInfo = $Script:Context.AvailableTeams | Where-Object { $_.TeamUid -ceq $Team -or $_.Name -eq $Team } | Select-Object -First 1 if ($teamInfo) { $userId = $teamInfo.TeamUid $userName = $teamInfo.Name } } if (-not $userId) { Write-Error -Message "Cannot find team: $Team" -ErrorAction Stop } } $vault.RemoveUserFromSharedFolder($sf.Uid, $userId, $userType).GetAwaiter().GetResult() | Out-Null Write-Output "${userType} `"$($userName)`" has been removed from shared folder `"$($sf.Name)`"" } function ensureAvalableLoaded { $vault = $Script:Context.Vault if (-not $vault) { return } if ($null -ne $Script:Context.AvailableTeams) { return } $Script:Context.AvailableTeams = @() $Script:Context.AvailableUsers = @() $teamTask = $vault.GetTeamsForShare() $userTask = $vault.GetUsersForShare() [System.Threading.Tasks.Task[]]$tasks = $teamTask, $userTask [System.Threading.Tasks.Task]::WaitAll($tasks) | Out-Null $Script:Context.AvailableTeams += $teamTask.GetAwaiter().GetResult() $userInfo = $userTask.GetAwaiter().GetResult() $users = @() $users += $userInfo.SharesWith $users += $userInfo.SharesFrom $users += $userInfo.GroupUsers $Script:Context.AvailableUsers += ($users | Sort-Object | Get-Unique) } $Keeper_TeamCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) ensureAvalableLoaded if (-not $Script:Context.AvailableTeams) { return $null } $result = @() $toComplete = $wordToComplete if ($toComplete.Length -ge 1) { if ($toComplete[0] -eq '''') { $toComplete = $toComplete.Substring(1, $toComplete.Length - 1) $toComplete = $toComplete -replace '''''', '''' } if ($toComplete[0] -eq '"') { $toComplete = $toComplete.Substring(1, $toComplete.Length - 1) $toComplete = $toComplete -replace '""', '"' $toComplete = $toComplete -replace '`"', '"' } } $toComplete += '*' foreach ($team in $Script:Context.AvailableTeams) { if ($team.Name -like $toComplete) { $name = $team.Name if ($name -match ' ') { $name = $name -replace '''', '''''' $name = '''' + $name + '''' } $result += $name } } if ($result.Count -gt 0) { return $result } else { return $null } } $Keeper_UserCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) ensureAvalableLoaded if (-not $Script:Context.AvailableUsers) { return $null } $result = @() $toComplete = $wordToComplete if ($toComplete.Length -ge 1) { if ($toComplete[0] -eq '''') { $toComplete = $toComplete.Substring(1, $toComplete.Length - 1) $toComplete = $toComplete -replace '''''', '''' } if ($toComplete[0] -eq '"') { $toComplete = $toComplete.Substring(1, $toComplete.Length - 1) $toComplete = $toComplete -replace '""', '"' $toComplete = $toComplete -replace '`"', '"' } } $toComplete += '*' foreach ($user in $Script:Context.AvailableUsers) { if ($user -like $toComplete) { $result += $user } } if ($result.Count -gt 0) { return $result } else { return $null } } Register-ArgumentCompleter -CommandName Grant-KeeperSharedFolderAccess -ParameterName Team -ScriptBlock $Keeper_TeamCompleter Register-ArgumentCompleter -CommandName Grant-KeeperSharedFolderAccess -ParameterName User -ScriptBlock $Keeper_UserCompleter Register-ArgumentCompleter -CommandName Grant-KeeperSharedFolderAccess -ParameterName SharedFolder -ScriptBlock $Keeper_SharedFolderCompleter New-Alias -Name kshf -Value Grant-KeeperSharedFolderAccess Register-ArgumentCompleter -CommandName Revoke-KeeperSharedFolderAccess -ParameterName Team -ScriptBlock $Keeper_TeamCompleter Register-ArgumentCompleter -CommandName Revoke-KeeperSharedFolderAccess -ParameterName User -ScriptBlock $Keeper_UserCompleter Register-ArgumentCompleter -CommandName Revoke-KeeperSharedFolderAccess -ParameterName SharedFolder -ScriptBlock $Keeper_SharedFolderCompleter New-Alias -Name kushf -Value Revoke-KeeperSharedFolderAccess function Get-KeeperAvailableTeam { <# .Synopsis Get Keeper Available Teams .Parameter Uid Team UID .Parameter Filter Return matching teams only #> [CmdletBinding()] [OutputType([KeeperSecurity.Vault.TeamInfo[]])] Param ( [string] $Uid, [string] $Filter ) ensureAvalableLoaded $teams = $Script:Context.AvailableTeams if ($Uid) { $teams | Where-Object { $_.TeamUid -ceq $Uid } | Select-Object -First 1 } else { foreach ($team in $teams) { if ($Filter) { $match = $($team.Uid, $team.Name) | Select-String $Filter | Select-Object -First 1 if (-not $match) { continue } } $team } } } New-Alias -Name kat -Value Get-KeeperAvailableTeam # SIG # Begin signature block # MIIR1wYJKoZIhvcNAQcCoIIRyDCCEcQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUP4rPOPg1WpIS61hic4tYNGkG # UV+ggg4jMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B # AQwFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVk # IFJvb3QgRzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg # Q0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5 # WRuxiEL1M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJP # DqFX/IiZwZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXz # ENOLsvsI8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bq # HPNlaJGiTUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTC # fMjqGzLmysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaD # G7dqZy3SvUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urO # kfW+0/tvk2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7AD # K5GyNnm+960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4 # R+Z1MI3sMJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlN # Wdt4z4FKPkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0I # U0F8WD1Hs/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYB # Af8CAQAwHQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaA # FOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK # BggrBgEFBQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v # Y3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4 # oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv # b3RHNC5jcmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcN # AQEMBQADggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcT # Ep6QRJ9L/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WT # auPrINHVUHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9 # ntSZz0rdKOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np37 # 5SFTWsPK6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0 # HKKlS43Nb3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL # 6TEa/y4ZXDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+1 # 6oh7cGvmoLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8 # M4+uKIw8y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrF # hsP2JjMMB0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy # 1lKQ/a+FSCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIHazCC # BVOgAwIBAgIQAnNTGQOIer82vZ1cJyDJDjANBgkqhkiG9w0BAQsFADBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg # Q0ExMB4XDTIyMDIwMjAwMDAwMFoXDTI1MDIwMTIzNTk1OVowcDELMAkGA1UEBhMC # VVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMR0wGwYDVQQK # ExRLZWVwZXIgU2VjdXJpdHkgSW5jLjEdMBsGA1UEAxMUS2VlcGVyIFNlY3VyaXR5 # IEluYy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDNgTqmksdjUyKF # 5zWkDyghf0PLWJWdzG0TX2j8B4J55xwt+B17zd4Xc3n0dvmSVAyPQANeN+mP1chf # 4LTRn9h4jWb8Jsfn+JzyRhj/gYINYvBnpRpqoM0z7QC9Ebwj5T61Cogm9EKGcrG+ # Ujh+Z7pTqfSUrHD8NMXhDL/UpVn+w0Pb4qg7o7AH2o94n7u/qTlMGZCs+VCAvhNr # wPABxvFY07YGb9t5/IZlPE8vG3p1vw2SbgREgFWSEQFj6X2CIhSrbiFCW/766/Mq # EX6qm+RyF71fD4d3yShg39guaE9o+TBl1MqVCje4bK/wGoNxCho0I6Z1fBBKloyp # vlx3gPpU7tJJ+KpuIiel9R9dGQuscqKzehPtbRc9Abr9ThN/HrLg1sFFVMdn2oMR # 63QCUdz+B1NuS7Ap8Ti7XvAPJHzEuQDcdMcRbkIfllJVqrb9UXEFwOPzvRU2KrcQ # 42Jlnn4T+WenPx5Nr3o/o08WLhLTicEK1OacEowyRLBmih4Gxpdk3fUAVCEkdvmq # TSydQpl1Bk8V88dxCkB1wMZyFYLNcddBL4kUbwjso/z6f2TtfAVYs/iIRWqs7Xqt # 4F2BBqobOGMymwg6VgVjjzDIgJCZSbjpq2IoVTci5vli6vxgSoZ01fccSaKa4Izm # B7DbobIkIjLgPqpnCkqlHuJj5hQ9twIDAQABo4ICBjCCAgIwHwYDVR0jBBgwFoAU # aDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFCZd3/KEdT2t5WTIFb3TUaM4 # sTikMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0f # BIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU # cnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGg # T4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k # ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZn # gQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BT # MIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln # aWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIx # Q0ExLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAGyDM3Cbxq # Auhr8O2xwOoCSVKmFkXqicwlrugwLW44Y4WX+imvTrGfjj2S99k/4D5H8DgtW/u8 # tOxcCoehTOCIEwP5TLrieHppsqAR4jaJRcdAHOWiJ1bmwQBv/cBU9vaelL0oXxxf # TwD9oDaQNuyq6p+nIJMqbKv33b8AWGe3zq4JwblaFjRDL5lUDNhPx3g/pm7JhnbX # 7QTKydAJvpbuP5cqUH1GEeVMjc5vEELtGNy/fy7Ekm4dndX4IZcFXW5L0Lx8cReB # hIZwA+pzdzTWQYvfxgRMb/j2uY+Tkb6Wz2x9BBS1UXiP2qrs3rhQv8DZRkUSqnko # YD4uJP8gk8BXcIXIThgEF2YCq2hBiwna5Ijbwkmjn1lWwGv15SznTOTnrVApJqB1 # tB2s2ovUNV4CyKDPVr+9/CS6IQJfEZeHYcYLsIga2q5NZCrqZAasBfCwALVkALos # DIWhs33vYLfETMSuk5Hd5JC+hLjVM3ZJwslvnc/wec2r0GNAiZ3a1aweC7NYuzRz # 29Mi/eR/4ylmCltyZqYJ1JcC/g6eY2Q0xkdWc8P0yHfQ/3fe7+AKXXKNjfv858GW # lg1Ck2lvwPdLqJWqj1FwJPiGRCB+WulPe0csTyWnf+ed45TXx69tZ6BZr0Xr2jXu # ybBdJtg0NN0a62xxWrmX42CgsrzHzRm7OzGCAx4wggMaAgEBMH0waTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB # MQIQAnNTGQOIer82vZ1cJyDJDjAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEK # MAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3 # AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUg+75CY+3HtU/v027 # NFmckH+E9AUwDQYJKoZIhvcNAQEBBQAEggIAOA31qq9bnxVxrjKVroKcyYQodEsY # cuKpgPqi/b0tCXhG4paa+cqekz1FCMQ72pWCsbNRk5zex5CGrgqOiWhhWsw0VM9U # LdRs6TGzB0pjTe1AXHFelWb6nqWqefm7KOXhFWzLWP/nP6LkbjaHA0lQ4a08ztcr # azIVMcYsAk997Z7KC5awn/cT+eY724nejllx/dLti2gCcDWhFEEEV7zJUXG/ugRM # BNg8W82c/xwGCYXdIjjNLCZnzF5RHx8JP84RjjbBAJLprsOnRmra/xjNlyixWpu/ # yf4udwl62+4/tiVfoZCBbY/VFQJEqYAIUftfpcBFu2uxYCKCdc0cYQANYuYwXBB7 # SCYfIdUDC76JEv01aT8KgPZF21Gm32GUakaVNvrTIcMWbWEpMB5kIMzuuEXiadnL # 7oSP1l9gajczzWhXBocizzTpn5eqZ2hcZd44egcvaGqgiup0UscRHlKH8PAyCjaD # XD8CQsFs9EBUPzrCbBuhWoDxOU8sxJTlF7ZnHBLUYXCqSamcZheYEMhdbcFF1Vbk # 9avnNxyUBwKCga7EGfMpdO6b+dQ+fpelHrzyQgGzo++0YeG5euSMm8O+53vc9k6z # uv2M39DqV1ZwHsHzlIr8fZ+0GwEZbF8S5c0bhtFCGMiOFGN2+BIMYwZRO23wZycv # A41U4tBjRhnn/8M= # SIG # End signature block |