NsfFolderCommands.ps1
|
#requires -Version 5.1 function New-KeeperNSFFolder { <# .Synopsis Creates a new Keeper NSF folder. .Description Creates a new folder in Keeper NSF using the v3 API. .Parameter Name Name of the folder to create. .Parameter ParentFolderUid UID of the parent folder. If omitted, the folder is created at root level. .Parameter Color Optional color for the folder. .Parameter NoInheritPermissions If specified, the folder will not inherit permissions from its parent. #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)] [string] $Name, [Parameter()] [string] $ParentFolderUid, [Parameter()] [string] $Color, [Parameter()] [switch] $NoInheritPermissions ) try{ [KeeperSecurity.Vault.VaultOnline]$vault = getVault } catch { Write-Host "Error getting vault: $($_.Exception.Message)" -ForegroundColor Red return } $inheritPermissions = -not $NoInheritPermissions.IsPresent try { $folderUid = $vault.CreateKeeperNSFFolder($Name, $ParentFolderUid, $Color, $inheritPermissions).GetAwaiter().GetResult() Write-Host "Folder '$Name' created successfully (UID: $folderUid)." -ForegroundColor Green return $folderUid } catch { Write-Host "Error creating folder: $($_.Exception.Message)" -ForegroundColor Red } } New-Alias -Name nsf-mkdir -Value New-KeeperNSFFolder function Set-KeeperNSFFolderAccess { <# .Synopsis Grant or revoke user access to a Keeper NSF folder. .Description Changes the sharing permissions of a Keeper NSF folder using the v3 API. Supports granting access with a specified role, or revoking access entirely. .Parameter FolderUid UID of the folder to share. .Parameter Action Action to perform: 'grant' (default) or 'remove'. .Parameter Email One or more user email addresses to grant/revoke access. .Parameter Role Access role for grant action: viewer (default), share-manager, content-manager, content-share-manager, full-manager. .Parameter ExpireIn Optional. Share expiration period from now (e.g. 30d, 6mo, 1y, 24h, 30mi), integer minutes, or a TimeSpan. Same as Grant-KeeperRecordAccess. .Parameter ExpireAt Optional. Absolute share expiration as ISO datetime (e.g. 2027-01-01T00:00:00Z). #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] Param ( [Parameter(Position = 0, Mandatory = $true)] [string] $FolderUid, [Parameter()] [ValidateSet('grant', 'remove')] [string] $Action = 'grant', [Parameter(Mandatory = $true)] [string[]] $Email, [Parameter()] [ValidateSet('viewer', 'share-manager', 'content-manager', 'content-share-manager', 'full-manager')] [string] $Role = 'viewer', [Alias('expire-in')] [Parameter()] [System.Object] $ExpireIn, [Alias('expire-at')] [Parameter()] [string] $ExpireAt ) try { [KeeperSecurity.Vault.VaultOnline]$vault = getVault } catch { Write-Host "Error getting vault: $($_.Exception.Message)" -ForegroundColor Red return } [KeeperSecurity.Vault.FolderNode]$tmpFolder = $null if (-not $vault.TryGetKeeperNSFFolder($FolderUid, [ref]$tmpFolder)) { Write-Host "Error: NSF folder '$FolderUid' not found." -ForegroundColor Red return } $shareOptions = $null if ($Action -eq 'grant' -and ($ExpireIn -or $ExpireAt)) { try { $expirationDto = Get-ExpirationDate -ExpireIn $ExpireIn -ExpireAt $ExpireAt } catch { Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red return } $shareOptions = New-Object KeeperSecurity.Vault.SharedFolderUserOptions $shareOptions.Expiration = $expirationDto } foreach ($user in $Email) { try { if ($Action -eq 'grant') { [void]$vault.GrantKeeperNSFFolderAccess($FolderUid, $user, $Role, $shareOptions).GetAwaiter().GetResult() $expireMsg = if ($shareOptions -and $shareOptions.Expiration) { " (expires $($shareOptions.Expiration.LocalDateTime.ToString('g')))" } else { '' } Write-Host "Granted '$Role' access to '$user' on folder '$FolderUid'$expireMsg." -ForegroundColor Green } else { [void]$vault.RevokeKeeperNSFFolderAccess($FolderUid, $user).GetAwaiter().GetResult() Write-Host "Revoked access for '$user' from folder '$FolderUid'." -ForegroundColor Green } } catch { Write-Host "Error ${Action}ing access for '$user': $($_.Exception.Message)" -ForegroundColor Red } } } New-Alias -Name nsf-share-folder -Value Set-KeeperNSFFolderAccess function Write-KeeperNSFRemoveImpact { param( [Folder.V3.Remove.RemoveResponse]$Response, [string] $ItemLabel = 'Record' ) if ($Response.ErrorMessage) { Write-Host "Error: $($Response.ErrorMessage)" -ForegroundColor Red } foreach ($result in $Response.Results) { $recordUid = if ($result.ItemUid.Length -gt 0) { [KeeperSecurity.Utils.CryptoUtils]::Base64UrlEncode($result.ItemUid.ToByteArray()) } else { '(unknown)' } $folderUid = if ($result.FolderUid.Length -gt 0) { [KeeperSecurity.Utils.CryptoUtils]::Base64UrlEncode($result.FolderUid.ToByteArray()) } else { '' } Write-Host "" Write-Host "${ItemLabel}: $recordUid" -ForegroundColor Cyan if ($folderUid) { Write-Host " Folder context: $folderUid" } Write-Host " Status: $($result.Status)" if ($result.Error -and $result.Error.Message) { Write-Host " Error: $($result.Error.Message)" -ForegroundColor Red } if ($result.Impact) { $impact = $result.Impact Write-Host " Impact:" Write-Host " Folders: $($impact.FoldersCount)" Write-Host " Records: $($impact.RecordsCount)" Write-Host " Affected users: $($impact.AffectedUsersCount)" Write-Host " Affected teams: $($impact.AffectedTeamsCount)" if ($impact.RecordInfo) { Write-Host " Other locations: $($impact.RecordInfo.LocationsCount)" } foreach ($warning in $impact.Warnings) { Write-Host " Warning: $warning" -ForegroundColor Yellow } } } } function Set-KeeperNSFFolder { <# .Synopsis Renames or recolors a Keeper NSF folder (Keeper NSF v3 API). .Parameter Folder Folder UID or name. .Parameter Name New folder name. .Parameter Color Optional folder color, or "none" to clear. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')] Param( [Parameter(Position = 0, Mandatory = $true)] [string] $Folder, [Alias('n')] [Parameter(ParameterSetName = 'Default')] [string] $Name, [ValidateSet('none', 'red', 'orange', 'yellow', 'green', 'blue', 'gray', 'grey')] [string] $Color ) if (-not $Name -and -not $PSBoundParameters.ContainsKey('Color')) { Write-Error -Message "Specify -Name and/or -Color to update the folder." return } try { [KeeperSecurity.Vault.VaultOnline]$vault = getVault } catch { Write-Error -Message "Error getting vault: $($_.Exception.Message)" return } [KeeperSecurity.Vault.FolderNode]$folderNode = $null if (-not $vault.TryResolveKeeperNSFFolder($Folder, [ref]$folderNode)) { Write-Error -Message "Keeper NSF folder `"$Folder`" was not found. Run Sync-Keeper or nsf-list first." return } $target = "$($folderNode.Name) ($($folderNode.FolderUid))" if (-not $PSCmdlet.ShouldProcess($target, "Update Keeper NSF folder")) { return } $nameArg = if ($PSBoundParameters.ContainsKey('Name')) { $Name } else { [NullString]::Value } $colorArg = if ($PSBoundParameters.ContainsKey('Color')) { $Color } else { [NullString]::Value } try { $result = $vault.UpdateKeeperNSFFolder($folderNode.FolderUid, $nameArg, $colorArg).GetAwaiter().GetResult() [KeeperSecurity.Vault.VaultOnline]::ValidateFolderModifyResult($result) } catch { Write-Error -Message $_.Exception.Message return } $vault.SyncDown($false).GetAwaiter().GetResult() | Out-Null Write-Host "Keeper NSF folder updated." -ForegroundColor Green } New-Alias -Name nsf-rndir -Value Set-KeeperNSFFolder function Remove-KeeperNSFFolder { <# .Synopsis Removes one or more Keeper NSF folders (Keeper NSF v3 API). .Parameter Folder One or more folder UIDs or names. .Parameter Operation folder-trash (default, recoverable) or delete-permanent (irreversible). .Parameter Force Skip confirmation after preview. .Parameter DryRun Preview only; do not remove folders. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')] Param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string[]] $Folder, [Alias('o')] [ValidateSet('folder-trash', 'delete-permanent')] [string] $Operation = 'folder-trash', [Alias('f')] [switch] $Force, [switch] $DryRun ) begin { [KeeperSecurity.Vault.VaultOnline]$vault = getVault $removals = New-Object 'System.Collections.Generic.List[KeeperSecurity.Vault.KeeperNSFFolderRemoval]' $op = switch ($Operation) { 'folder-trash' { [KeeperSecurity.Vault.KeeperNSFFolderRemoveOperation]::FolderTrash } 'delete-permanent' { [KeeperSecurity.Vault.KeeperNSFFolderRemoveOperation]::DeletePermanent } } } process { foreach ($name in $Folder) { [KeeperSecurity.Vault.FolderNode]$folderNode = $null if (-not $vault.TryResolveKeeperNSFFolder($name, [ref]$folderNode)) { Write-Error -Message "Keeper NSF folder `"$name`" was not found. Run Sync-Keeper or nsf-list first." continue } $removal = New-Object KeeperSecurity.Vault.KeeperNSFFolderRemoval $removal.FolderUid = $folderNode.FolderUid $removal.Operation = $op $removals.Add($removal) } } end { if ($removals.Count -eq 0) { return } if ($Operation -eq 'delete-permanent' -and -not $Force -and -not $DryRun) { Write-Host "" Write-Host "*** WARNING ***" -ForegroundColor Red Write-Host " delete-permanent is IRREVERSIBLE." Write-Host " All sub-folders and records inside will be permanently destroyed." } Write-Host "" Write-Host "=== Keeper NSF Folder Remove Preview ===" -ForegroundColor Cyan $previewResult = $vault.RemoveKeeperNSFFolders($removals, $true).GetAwaiter().GetResult() Write-KeeperNSFRemoveImpact -Response $previewResult.PreviewResponse -ItemLabel 'Folder' try { [KeeperSecurity.Vault.VaultOnline]::ValidateRemoveResponse($previewResult.PreviewResponse, $false) } catch { Write-Error -Message $_.Exception.Message return } if ($DryRun) { Write-Host "" Write-Host "Dry run: no folders were removed." -ForegroundColor DarkYellow return } if (-not $Force) { $prompt = if ($Operation -eq 'delete-permanent') { "Are you sure you want to permanently delete the folder(s) above? This action cannot be undone. (yes/No)" } else { "Are you sure you want to remove the folder(s) above? (yes/No)" } $confirmation = Read-Host $prompt if ($confirmation -notmatch '^(y|yes)$') { Write-Host "Remove operation cancelled" return } } if ($previewResult.PreviewResponse.ConfirmationToken.IsEmpty) { Write-Error -Message "Preview did not return a confirmation token." return } Write-Host "" Write-Host "Removing folders..." -ForegroundColor Cyan $confirmResult = $vault.RemoveKeeperNSFFolders($removals, $false).GetAwaiter().GetResult() if (-not $confirmResult.Confirmed) { Write-Error -Message "Folder removal was not confirmed by the server." return } $vault.SyncDown($false).GetAwaiter().GetResult() | Out-Null Write-Host "" Write-Host "Keeper NSF folder removal completed." -ForegroundColor Green } } New-Alias -Name nsf-rmdir -Value Remove-KeeperNSFFolder # SIG # Begin signature block # MIInvgYJKoZIhvcNAQcCoIInrzCCJ6sCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC0fc3To1aTccUI # ii1Unh57/3OvCM28iPOASvfmefOgm6CCITswggWNMIIEdaADAgECAhAOmxiO+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 # twGpn1eqXijiuZQwggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0GCSqG # SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTlaMGkx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4 # RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQg # MjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C0Cit # eLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce2vnS # 1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0daE6ZM # swEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6TSXBC # Mo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoAFdE3 # /hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7OhD26j # q22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM1bL5 # OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z8ujo # 7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05huzU # tw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNYmtwm # KwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP/2NP # TLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0TAQH/ # BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYDVR0j # BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud # JQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0 # cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E # PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATANBgkq # hkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95RysQDK # r2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HLIvda # qpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5BtfQ/g+ # lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnhOE7a # brs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIhdXNS # y0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV9zeK # iwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/jwVYb # KyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYHKi8Q # xAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmCXBVm # zGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l/aCn # HwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZWeE4w # gga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH # NDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVT # MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1 # c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0Zo # dLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi # 6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNg # xVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiF # cMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJ # m/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvS # GmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1 # ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9 # MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7 # Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bG # RinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6 # X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAd # BgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJx # XWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF # BwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln # aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJo # dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy # bDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQEL # BQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxj # aaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0 # hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0 # F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnT # mpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKf # ZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzE # wlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbh # OhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOX # gpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EO # LLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wG # WqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZPubdcMIIG7TCCBNWg # AwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQG # EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0 # IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex # MB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1OVowYzELMAkGA1UEBhMCVVMx # FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBTSEEy # NTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIwMjUgMTCCAiIwDQYJKoZI # hvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q6q5gVrMrV7pvUf+GcAoB38o3 # zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPnZ8ZN+vo8dE2/pPvOx/Vj8Tch # TySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSssp3qome7MrxVyfQO9sMx6ZAWj # FDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09ldQ//nBZZREr4h/GI6Dxb2Uo # yrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98oksouTMYFOnHoRh6+86Ltc5zjP # KHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+3qU8DD+nigNJFmt6LAHvH3KS # uNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsnqcnpJeItK/DhKbPxTTuGoX7w # JNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQPdP51ho1zBp+xUIZkpSFA8vW # doUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbSLZ49kPmk8iyyizNDIXj//cOg # rY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojTdS5PWPsWeupWs7NpChUk555K # 096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoKRR7YufAkprxMiXAJQ1XCmnCf # gPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTkO/zy # Me39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTvb1NK6eQGfHrK4pBW9i/USezL # TjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsG # AQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j # b20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNy # dDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln # aUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5j # cmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEB # CwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk9iJP2zWLpQq1b4URGnwWBdEZ # D9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tshgb4O6Lgjg8K8elC4+oWCqnU/ # ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9mzskgiC3QYIUP2S3HQvHG1FDu # +WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQBHMUBaB5bdrPbF6MRYs03h4o # bEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+YkDFkxUGtMTaiLR9wjxUxu2h # ECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0cKsb+zJNEsuEB7O7/cuvTQasn # M9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY7T/lwd6UArb+BOVAkg2oOvol # /DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcbojBcxlRcGG0LIhp6GvReQGgMgY # xQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05oxYy2zRWVFjF7mcr4C34Mj3oc # CVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskKPIJgbaP5t2nGj/ULLi49xTcB # ZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjdNXOCIUjsarfNZzCCB0kwggUx # oAMCAQICEAHdzU+FVN9jCMv0HhHagNUwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB # MTAeFw0yNjA2MDUwMDAwMDBaFw0yNzA2MDQyMzU5NTlaMIHRMRMwEQYLKwYBBAGC # NzwCAQMTAlVTMRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMR0wGwYDVQQPDBRQ # cml2YXRlIE9yZ2FuaXphdGlvbjEQMA4GA1UEBRMHMzQwNzk4NTELMAkGA1UEBhMC # VVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMR0wGwYDVQQK # ExRLZWVwZXIgU2VjdXJpdHkgSW5jLjEdMBsGA1UEAxMUS2VlcGVyIFNlY3VyaXR5 # IEluYy4wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCb4DRTV0sNQsa1 # 0YRh+bliabmLOVYr6S0+BSVvRJAN3SHP6x52i1Dkpki5xVDIH06ZnnsToVrgvTv+ # QxGwsn9SAPHEZ/PIJRFxbMR4ShDaptYyL4f0u4k/3HwRzIleWE4mTUonYH8BdgLw # /F53B7wa7VTDHtxXltYTibEOwJxYCOi4Zr2FYQhjw14/CHcqS3FSMs6YYU2T56+g # w819hQM3K0YlwTNOFoIm1v7/ZZZiJGH8uGDsvy1makh1Xyyo/wN8EbQ1nbslmePT # roPm9w7WqiP/yiq+CZHiuTk9JK5bEgkWG3ns+v25cI251WidJx3SU7IZnX0OTd6/ # ZdKhprD5Gcfy5GBbJdcYw2WycQRW0PT5BEt55xRE0heufkpDaTUN6RdOuJdXbkl0 # hV91IZIuhueEMCk3h5mDTlU5gImxqj0R/TbAxjSSGTKCeuYFkQIRqytSabdrZZ48 # kW5hOIZMVDY1f4kpPJa8UeEvDZXT3vrtj36aSJrwez2uh4FMNlkCAwEAAaOCAgIw # ggH+MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBT1 # SmCYU/7Yrz1fX66Ur5nSzlSYOzA9BgNVHSAENjA0MDIGBWeBDAEDMCkwJwYIKwYB # BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMC # B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25p # bmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI # QTM4NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JT # QTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUA # A4ICAQBcavcUHNFEg872HDRq2+hRlnvaghCXv7X/6h9HSzjAQP3rt95BZty3ASqi # 2MYyGQLGdDl4DToe/WhajtEOBOYa83agW6tBvrfcKRrDrwJOMPTbwNYvn+GuiL4T # CKzXaytWiJJbrc5odc7Ecat2ZvJylpPmNainr4Q0LzzH23Gea/Mm/hIJTN4IGgrH # hrXiTIIW/ZUzrY6g8b3RZB4BA497n43wNdSqP+C3ntFw6NiGB4Z25SW4YntIxYPv # Kf37OVhF0xqxLC1sK/XxgK0EGQ6iaj8Ncpr2C5vSNZqfW2MndxOA1W67pgDpg83k # UWG+/YJeGhqOTF82/0kIzQXeI/lIqbnL/IJAJqSm/ROSpsGUKVbzk03cpTD55ZQX # WjM0fLirypBqY05T8gnh1L0fSwxr/SwJZ8OddivgyK1YOMn02nnsEG5kxBt9cMX4 # JCYABhypmAVDRvyYifEVdoFWv2gAXXW+PPRvlNa6E4aMCZrVcoKHiyeMAXOi1IC9 # mHvC2+foTSMFueq3AdnYfeKnZnAiKXKRhXcdHbQYcR2A7AIzIcqahPYr4FNEgb/E # /y/kypAkf0rMHlYl1kNqLs2Nv1UnMEHYT5YmDVLO63+1Trcw4zTZ70zuqIqeID/d # nbOlgtyG6DSRCL7f0E7kP18f4RoX5i1PkfeO4VJHsAuCeNG1qjGCBdkwggXVAgEB # MH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYD # VQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNI # QTM4NCAyMDIxIENBMQIQAd3NT4VU32MIy/QeEdqA1TANBglghkgBZQMEAgEFAKCB # hDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEE # AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJ # BDEiBCB4SwrunVkqw4WAY+axbKxYWG8pvY8x8E8c6uows5+yZDANBgkqhkiG9w0B # AQEFAASCAYB/uxv+sQbtPkHQDzxSijncDik3LqVzwMpSSstc+eBn3WihmF91t/CP # jVBMkdM+fT3RiYAGZIA4bIRqD+ZvNL6gIPZROmeMJ/+TYOGFEyLoUwtD1FVaATS4 # S74P9C/e3sXBkcVHT9MR/4KcsG7mZsnrI88ROy2sByOKAEExdzCr5XjNmcyJULT1 # UWxh1+Zo2BXVj4lWvouOS9q6scrbLO32d0PfOdczxPTy0hgPBEg1M1B70FMQw7Be # QV8KdGbS8Fa2A3Bg1Eg9F59FOyKlhCs1cmZ0cBiVGiJEQDCUw/bdI4vhjoLkYXCd # f4EvtpZ8TSYhRCh5TwE2e3xFtuPM/jnzGgSV7HxlPhQnqoRLI2iJTmcQLBrHHTxC # A4qhsqdkmT/TcRX9tVO+FKcuyB3NjHC/minKn+7RTcjytpQzHOoO6A98VRaOHE4Y # uyPwaY1wDatWUS7qL6Kb4NXYLWuO/+U6rfmA7JfA9HhAMsvuxYoOkc4K1KsR68TV # GQ1SVQbDzgWhggMmMIIDIgYJKoZIhvcNAQkGMYIDEzCCAw8CAQEwfTBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUg # Q0ExAhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcN # AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjYwNjEzMDA1NjMxWjAv # BgkqhkiG9w0BCQQxIgQgC7KKJ1twToMDv7TUaBeOK9rYwdrue3i8iCqjTwsPgi0w # DQYJKoZIhvcNAQEBBQAEggIANTdL2s+BUzg61lYmhPQJFOWqtN9QeKZvHupP2qEH # bM23tMS2Ftqy90MZJ/swaAZvwhCrTbKHLyUrhc4yxwFzDr7otrV2YJO4HIlwpwT+ # aIxhvzlIO8EFIf5tjl57cB5Hni3udob1+NRx1FI4zFxPKBjFtbv71cgAYKMVhW+6 # lVII5qvpg3yucZHme0ST7B3JGTXnDHGdHqp5Mwhx80r9/+lZQsloCpd3n3ZHVgvp # daOQBrqXRlXxvnCRVkbOVCQnsJzoJa200zzZhsbsqCssH94oO0Ijlu/KSEjXTeAl # ua9RWKEc+LBBmbvs03ZX9wT0LijY1UiJ6PkmUG6OvkD8T7k/aag2gGGpJzSESqAP # P6agKtHsIs55UIO8Q8QJtRdm5TXLe4X31RZs+LzvSXtUP+NbNpEBuqfQ/2SHesKE # ETfK+fmM04viqgpGbWcFc/sjinXwXZPgQaBeKu6gBObce1VtHJhBW6tgSWzTdFIe # 8hkn/7hSuyAMU5B3CCRj5+K296K0MdUwD8wz3nHNUKYjom/HCN8Z47QQX5hN6w4j # IR9K5W6tFsQ6oB8CIRwC3ymTwUlTqntdRTYTdTIdM+yv4xBvP5XJDa+JAeLXw6ET # Ic/kBY7ksc6ssfqGaXxLWuQu52m1DpOmLiqXycaRQ6XoF9nD+tjpvB1YFxe3om2j # 1CM= # SIG # End signature block |