EnterpriseTeam.ps1
|
function New-KeeperEnterpriseTeam { <# .Synopsis Create an enterprise team .PARAMETER ParentNode Parent Node name or ID .PARAMETER Team Team name #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)][string]$TeamName, [Parameter()][string] $ParentNode, [Parameter()][Switch] $RestrictView, [Parameter()][Switch] $RestrictEdit, [Parameter()][Switch] $RestrictShare ) [Enterprise]$enterprise = getEnterprise $team = New-Object Keepersecurity.Enterprise.EnterpriseTeam $team.Name = $TeamName [KeeperSecurity.Enterprise.EnterpriseNode] $parent = $null if ($ParentNode) { $parent = resolveSingleNode $ParentNode $team.ParentNodeId = $parent.Id } else { $team.ParentNodeId = $enterprise.enterpriseData.RootNode.Id } if ($RestrictView.IsPresent) { $team.RestrictView = $true } if ($RestrictEdit.IsPresent) { $team.RestrictEdit = $true } if ($RestrictShare.IsPresent) { $team.RestrictSharing = $true } $t = $enterprise.enterpriseData.CreateTeam($team).GetAwaiter().GetResult() $t } New-Alias -Name keta -Value New-KeeperEnterpriseTeam function Get-KeeperEnterpriseTeamUser { <# .Synopsis Get a list of enterprise users for team #> [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true)]$Team ) [Enterprise]$enterprise = getEnterprise $enterpriseData = $enterprise.enterpriseData $uid = $null if ($Team -is [String]) { $uids = Get-KeeperEnterpriseTeam | Where-Object { $_.Uid -ceq $Team -or $_.Name -ieq $Team } | Select-Object -Property Uid if ($uids.Length -gt 1) { Write-Error -Message "Team name `"$Team`" is not unique. Use Team UID" -ErrorAction Stop } if ($null -ne $uids.Uid) { $uid = $uids.Uid } } elseif ($null -ne $Team.Uid) { $uid = $Team.Uid } if ($uid) { $team = $null if ($enterpriseData.TryGetTeam($uid, [ref]$team)) { foreach ($userId in $enterpriseData.GetUsersForTeam($uid)) { $user = $null foreach ($userId in $enterpriseData.TryGetUserById($userId, [ref]$user)) { $user } } } else { Write-Error -Message "Team `"$uid`" not found" -ErrorAction Stop } } else { Write-Error -Message "Team `"$Team`" not found" -ErrorAction Stop } } New-Alias -Name ketu -Value Get-KeeperEnterpriseTeamUser Register-ArgumentCompleter -CommandName Get-KeeperEnterpriseTeamUser -ParameterName Team -ScriptBlock $Keeper_TeamNameCompleter function Add-KeeperEnterpriseTeamMember { <# .SYNOPSIS Adds existing enterprise users to a Keeper team. .DESCRIPTION Adds one or more users (by email) to an existing Keeper Enterprise Team. The users must already exist in the enterprise. .PARAMETER Team Team UID or Team Name. .PARAMETER Emails Array of email addresses of users to add to the team. .EXAMPLE Add-KeeperEnterpriseTeamMember -Team "Engineering" -Emails "alice@example.com", "bob@example.com" .EXAMPLE Add-KeeperEnterpriseTeamMember -Team "1P7A8XZ9K3J9H" -Emails "eve@example.com", "frank@example.com" #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Team, [Parameter(Mandatory = $true)] [string[]] $Emails ) [Enterprise]$enterprise = GetEnterprise try { $selectedTeam = Get-KeeperTeamByNameOrUid -EnterpriseData $enterprise.enterpriseData -TeamInput $Team if (-not $selectedTeam) { Write-Warning "No matching team found for input: $Team" } if ($Emails.Count -eq 0) { Write-Warning "No email addresses provided to add." return } $enterprise.enterpriseData.AddUsersToTeams( $Emails, @($selectedTeam.Uid) ).GetAwaiter().GetResult() | Out-Null Write-Output "Requested addition of $($Emails.Count) user(s) to team '$($selectedTeam.Name)'." } catch { Write-Warning "Failed to add users to team '$Team': $($_.Exception.Message)" } } function Remove-KeeperEnterpriseTeamMember { <# .SYNOPSIS Removes existing enterprise users from a Keeper team. .DESCRIPTION Removes one or more users (by email) from an existing Keeper Enterprise Team. The specified users must already exist in the enterprise and must be members of the team. .PARAMETER Team Team UID or Team Name from which the users will be removed. .PARAMETER Emails Array of email addresses of users to remove from the team. .EXAMPLE Remove-KeeperEnterpriseTeamMember -Team "Engineering" -Emails "alice@example.com", "bob@example.com" .EXAMPLE Remove-KeeperEnterpriseTeamMember -Team "1P7A8XZ9K3J9H" -Emails "eve@example.com", "frank@example.com" This command removes the specified users from the given team. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Team, [Parameter(Mandatory = $true)] [string[]] $Emails ) [Enterprise]$enterprise = GetEnterprise try { $selectedTeam = Get-KeeperTeamByNameOrUid -EnterpriseData $enterprise.enterpriseData -TeamInput $Team if (-not $selectedTeam) { Write-Warning "No matching team found for input: $Team" return } if ($Emails.Count -eq 0) { Write-Warning "No email addresses provided to remove." return } $enterprise.enterpriseData.RemoveUsersFromTeams( $Emails, @($selectedTeam.Uid) ).GetAwaiter().GetResult() | Out-Null Write-Output "Requested removal of $($Emails.Count) user(s) from team '$($selectedTeam.Name)'." } catch { Write-Warning "Failed to remove users from team '$Team': $($_.Exception.Message)" } } function Get-TeamMembersBatch { <# .SYNOPSIS Fetches team members in batches from the API. .DESCRIPTION Internal helper function that retrieves team member emails for multiple teams using parallel API calls in configurable batch sizes. #> [CmdletBinding()] [OutputType([hashtable])] param ( [Parameter(Mandatory)][KeeperSecurity.Authentication.IAuthentication]$Auth, [Parameter(Mandatory)][array]$TeamUids, [int]$BatchSize = 20 ) if ($TeamUids.Count -eq 0) { return @{} } $results = @{} for ($i = 0; $i -lt $TeamUids.Count; $i += $BatchSize) { $batch = $TeamUids[$i..([Math]::Min($i + $BatchSize - 1, $TeamUids.Count - 1))] $tasks = @{} foreach ($uid in $batch) { try { $request = New-Object Enterprise.GetTeamMemberRequest $request.TeamUid = [Google.Protobuf.ByteString]::CopyFrom( [KeeperSecurity.Utils.CryptoUtils]::Base64UrlDecode($uid)) $tasks[$uid] = $Auth.ExecuteAuthRest( "vault/get_team_members", $request, [Enterprise.GetTeamMemberResponse] ) } catch { Write-Warning "Failed to create request for team $uid : $($_.Exception.Message)" $results[$uid] = [System.Collections.Generic.List[string]]::new() } } if ($tasks.Count -eq 0) { continue } try { [System.Threading.Tasks.Task]::WhenAll($tasks.Values).GetAwaiter().GetResult() | Out-Null } catch { Write-Warning "Some team member requests failed: $($_.Exception.Message)" } foreach ($uid in $tasks.Keys) { $task = $tasks[$uid] if ($task.IsCompletedSuccessfully) { $emails = [System.Collections.Generic.List[string]]::new() if ($task.Result.EnterpriseUser) { foreach ($u in $task.Result.EnterpriseUser) { $emails.Add($u.Email) } } $results[$uid] = $emails } else { $results[$uid] = [System.Collections.Generic.List[string]]::new() } } } return $results } function Get-KeeperEnterpriseTeams { <# .SYNOPSIS Lists all Keeper Enterprise teams. .DESCRIPTION Show details for all teams you have access to within your organization. .PARAMETER ShowMembers List team members from cache (fast, may be incomplete). Alias: -v .PARAMETER ShowAllMembers List team members, fetching from server if cache is empty (slower, complete). Alias: -vv .PARAMETER All Show all teams including those from managed companies (MSP admin). Alias: -a .PARAMETER Sort Sort teams by column: company, team_uid, name (default: company) .EXAMPLE Get-KeeperEnterpriseTeams # Default sort by company Get-KeeperEnterpriseTeams -Sort name # Sort by team name Get-KeeperEnterpriseTeams -Sort team_uid # Sort by team UID Get-KeeperEnterpriseTeams -v # Show members from cache (fast) Get-KeeperEnterpriseTeams -vv # Show all members (fetches from server if needed) Get-KeeperEnterpriseTeams -a # Include teams outside primary organization (MSP admin) Get-KeeperEnterpriseTeams -vv -a # All teams (including managed companies) with complete member list #> [CmdletBinding()] param ( [Parameter()][Alias('v')][Switch] $ShowMembers, [Parameter()][Alias('vv')][Switch] $ShowAllMembers, [Parameter()][Alias('a')][Switch] $All, [Parameter()][ValidateSet('company', 'team_uid', 'name')][string] $Sort = 'company' ) if (-not $Script:Context.Auth) { Write-Error "Not connected. Please run Connect-Keeper first." -ErrorAction Stop } $includeManagedCompanyTeams = $All.IsPresent $memberMode = if ($ShowAllMembers.IsPresent) { 'full' } elseif ($ShowMembers.IsPresent) { 'cache' } else { 'none' } $showMemberInfo = $memberMode -ne 'none' [Enterprise]$enterprise = $null if ($showMemberInfo) { try { $enterprise = getEnterprise } catch { Write-Warning "Could not load enterprise data for member info: $($_.Exception.Message)" $enterprise = $null } if (-not $enterprise -or -not $enterprise.enterpriseData) { Write-Warning "Member information will not be displayed." $showMemberInfo = $false } } $results = [System.Collections.ArrayList]::new() $teamByUid = @{} try { $request = New-Object Records.GetShareObjectsRequest $response = $Script:Context.Auth.ExecuteAuthRest( "vault/get_share_objects", $request, [Records.GetShareObjectsResponse] ).GetAwaiter().GetResult() if (-not $response) { Write-Warning "Empty response from API" return } $enterpriseNames = @{} if ($response.ShareEnterpriseNames) { foreach ($ent in $response.ShareEnterpriseNames) { $enterpriseNames[$ent.EnterpriseId] = $ent.Enterprisename } } $apiTeams = if ($response.ShareTeams) { @($response.ShareTeams) } else { @() } if ($includeManagedCompanyTeams -and $response.ShareMCTeams) { $apiTeams += @($response.ShareMCTeams) } $primaryEnterpriseId = $null try { $primaryEnterpriseId = $Script:Context.Auth.AuthContext.License.EnterpriseId } catch { $primaryEnterpriseId = $null } $hasNoValidEnterpriseId = ($null -eq $primaryEnterpriseId -or $primaryEnterpriseId -le 0) $hasShareTeams = ($response.ShareTeams -and $response.ShareTeams.Count -gt 0) if ($hasNoValidEnterpriseId -and $hasShareTeams) { $primaryEnterpriseId = $response.ShareTeams[0].EnterpriseId } if (-not $includeManagedCompanyTeams -and $null -ne $primaryEnterpriseId -and $primaryEnterpriseId -gt 0) { $apiTeams = @($apiTeams | Where-Object { $_.EnterpriseId -eq $primaryEnterpriseId }) } foreach ($team in $apiTeams) { $teamUid = [KeeperSecurity.Utils.CryptoUtils]::Base64UrlEncode($team.TeamUid.ToByteArray()) if ($teamByUid.ContainsKey($teamUid)) { continue } $companyName = $enterpriseNames[$team.EnterpriseId] $members = [System.Collections.Generic.List[string]]::new() if ($showMemberInfo) { foreach ($userId in $enterprise.enterpriseData.GetUsersForTeam($teamUid)) { $user = $null if ($enterprise.enterpriseData.TryGetUserById($userId, [ref]$user)) { $members.Add($user.Email) } } } $teamByUid[$teamUid] = @{ Uid = $teamUid Name = $team.Teamname Company = $companyName Members = $members } } } catch { Write-Warning "Failed to fetch teams from API: $($_.Exception.Message)" return } $allTeams = @($teamByUid.Values) if ($memberMode -eq 'full' -and $showMemberInfo) { $teamsNeedToFetch = @($allTeams | Where-Object { $_.Members.Count -eq 0 } | ForEach-Object { $_.Uid }) if ($teamsNeedToFetch.Count -gt 0) { $fetchedMembers = Get-TeamMembersBatch -Auth $Script:Context.Auth -TeamUids $teamsNeedToFetch if ($fetchedMembers) { foreach ($team in $allTeams) { if ($team.Members.Count -eq 0 -and $fetchedMembers.ContainsKey($team.Uid)) { $team.Members = $fetchedMembers[$team.Uid] } } } } } $allTeams = @(switch ($Sort) { 'team_uid' { $allTeams | Sort-Object { if ($_.Uid) { $_.Uid.ToLower() } else { '' } } } 'name' { $allTeams | Sort-Object { if ($_.Name) { $_.Name.ToLower() } else { '' } } } default { $allTeams | Sort-Object { if ($_.Company) { $_.Company.ToLower() } else { '' } }, { if ($_.Name) { $_.Name.ToLower() } else { '' } } } }) $index = 0 foreach ($team in $allTeams) { $index++ $props = [ordered]@{ '#' = $index 'Company' = $team.Company 'Team UID' = $team.Uid 'Name' = $team.Name } if ($showMemberInfo) { $props['Member'] = if ($team.Members.Count -gt 0) { $team.Members[0] } else { '' } } [void]$results.Add([PSCustomObject]$props) if ($showMemberInfo) { for ($i = 1; $i -lt $team.Members.Count; $i++) { $memberRow = [ordered]@{ '#' = ''; 'Company' = ''; 'Team UID' = ''; 'Name' = ''; 'Member' = $team.Members[$i] } [void]$results.Add([PSCustomObject]$memberRow) } } } if ($results.Count -eq 0) { Write-Host "No teams found." return } Write-Host "`nFound $($allTeams.Count) team(s).`n" $results | Format-Table -AutoSize } New-Alias -Name list-team -Value Get-KeeperEnterpriseTeams # SIG # Begin signature block # MIInvgYJKoZIhvcNAQcCoIInrzCCJ6sCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCliQNrph/7N/Wl # F7UJj5HhDpKmt8J+3prRxgctyYBVxaCCITswggWNMIIEdaADAgECAhAOmxiO+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 # oAMCAQICEAe0P3SLJmcoVNrErUyxTt0wDQYJKoZIhvcNAQELBQAwaTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB # MTAeFw0yNTEyMzEwMDAwMDBaFw0yOTAxMDIyMzU5NTlaMIHRMRMwEQYLKwYBBAGC # NzwCAQMTAlVTMRkwFwYLKwYBBAGCNzwCAQITCERlbGF3YXJlMR0wGwYDVQQPDBRQ # cml2YXRlIE9yZ2FuaXphdGlvbjEQMA4GA1UEBRMHMzQwNzk4NTELMAkGA1UEBhMC # VVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMR0wGwYDVQQK # ExRLZWVwZXIgU2VjdXJpdHkgSW5jLjEdMBsGA1UEAxMUS2VlcGVyIFNlY3VyaXR5 # IEluYy4wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCUcNMoSVmxAi0a # vG+StFJMNFFTUIOo3HdBZ+0gqA1XpNgUx11vB1vCZrvFsD9m5oA58tdp4gZN3LmQ # aMvCl2ANUT7MilI02Hf1RWlygBzon6iE0GpU3lgRrwrk1dhtLpGsR6dbMKUUHprc # vKpXk90/VN+vhzY1uik1tCTxkDCPu/AYJg7m9+tR2KqvMuYMaMLhii66eWUAGsBC # h/uZxjkGoJF6qZ0DgFd7rW7VYljbfYSNPeZNGTDgB0J/wOsKl0mn612DTseIvAKt # 4vra/FLFukyEyStnfQ8lWYDcLLCMCjNVrzGipmT5E2iyx7Y1RZCIpNwVogp3Ixbk # Gbq5A/41YNOLLd4cFewyB2F037RevBCRsUODZEt1qBf7Jbu3DiYo1G+zTj9E0R1s # FzyijcfdsTm6X5ble+yCJeGkX5XgsyPnZpyz/FX9Fr0N9pMPGWwW2PKyHEnSytXm # 0Dxdq2P4mA4CBUxq7YoV26L2PF6QEh9BQdXTPcnLysUv7SI/a0ECAwEAAaOCAgIw # ggH+MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBRG # 4H6CH8pvNX632bsdnrda4MtJLDA9BgNVHSAENjA0MDIGBWeBDAEDMCkwJwYIKwYB # BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMC # B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25p # bmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI # QTM4NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JT # QTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUA # A4ICAQA1Wlq0WzJa3N6DgjgBU7nagIJBab1prPARXZreX1MOv9VjnS5o0CrfQLr6 # z3bmWHw7xT8dt6bcSwRixqvPJtv4q8Rvo80O3eUMvMxQzqmi7z1zf+HG+/3G4F+2 # IYegvPc8Ui151XCV9rjA8tvFWRLRMX0ZRxY1zfT027HMw0iYL20z44+Cky//FAnL # iRwoNDGiRkZiHbB9YOftPAYNMG3gm1z3zOW5RdfKPrqvMuijE+dfyLIAA6Immpzu # FMH+Wgn8NnSlot9b4YKycaqqdjd7wXDjPub/oQ7VShuCSBWj+UNOTVh0vcZGackc # H1DLVgwp2dcKlxJiQKtkHT/T6LloY6LTe6+8wkVkr8EAv1W+q/+M1a4Ao+ykFbIA # 2LBEmA9qdgoLtenAYIiEg+48SjMPgyBbVPE3bhL1vIqjEIxYCfdmi6wx33oYX7HB # +bJ7zitHw4GgtpfPV8y8QRZImKmeDOKyXjQPDmQM/Eglm/Ns0GzBkVXM8h6UI34b # WZrHz9sbLSE20m5Svmxftvw5zju+I3WsmS/stNfWlOkwU0niUgwPHaz21kjXEA5A # g+aqv26wodqZcnGOlChoWDvSJ8KKgdOFbeAYKAMp1NY7iWV315zpGH19RipCR1NH # 0ND8iIubk3WGNf2rzEfqlOi3h2ywqVkU6AKXHdO5JV4otSKKEDGCBdkwggXVAgEB # MH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYD # VQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNI # QTM4NCAyMDIxIENBMQIQB7Q/dIsmZyhU2sStTLFO3TANBglghkgBZQMEAgEFAKCB # hDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEE # AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJ # BDEiBCAAx+iM1u7+lw/0GLysg6LipVL9Ut4xt3nnDt4SnEHceTANBgkqhkiG9w0B # AQEFAASCAYCLA7WKg3ce0SrVzJ+t/0lvouixPk8RhwEAGRAgEHwpzTU4nsiuvSwd # v2na6k1n57eq/z7SShotvtR9W3RNMsSkkTbD3Blj2b3u29+Xdd7L7mZjFqysVgJf # GzBrCYedATpvfHPG99tcWTvK3TlVmn+08vs50f2VlIHncWtJL9CH0cycp+zlxWrM # dkWLg79cjE4K9MXqTAtyhiOfuCjGK7CoGz9XRdzBYyy298+uLiHcgvAZiUf6eBSE # kS8rJTT7ygg7iLTCiLtZM/xUPlEqv00aWsNk0o7OR5W6tYskpjck51xO+v9yFa0w # 6zTfZ2kcQ6NO+xAQL+CDhvaTiJ5CdppyeBpLh6uZ2e1qhpIeeUGJvoUZd5oPQ9ik # oTOxWGa4JQq0Rt2HQIR061IjhuXhQSOFV61Q7Mk1wR8fr6OEEFmOgehH4xYEdg5u # 4qNakNemSuZsUURTgYwR5dtNCqgq/u05dzEaT/1ytQqCMq0Y2eF4dbnDxf2h08zE # KEkdK6v0q2+hggMmMIIDIgYJKoZIhvcNAQkGMYIDEzCCAw8CAQEwfTBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUg # Q0ExAhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcN # AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjYwMjI1MjIzNzU0WjAv # BgkqhkiG9w0BCQQxIgQgExiVwDdMxAmPLDeyxJvU5yfhiI55vh7v1m7nrGrG4qYw # DQYJKoZIhvcNAQEBBQAEggIAJ2uzsFmZaJ/PdvNdhZequu84YcbtRGYdo1i4DL7d # 7hXldmcgVZgCjIJJ5zmqNG36/ZFKY6UAc7RK4CEoVTALazH4AIN/NYgA1LdWXQMU # gklDmCZT30yjHf4ZlGQHW3cmwLQLkWt1K1+AD3iNY4BORHAbOI8ZtJsa62U86cBn # p8dsBaeYiO7ewEUi3pJ8I/GvP+qBXsRfDM0tPMFdMjrCr5K8hCh2464uB+Elxb5V # gqyL0CTBY3iNImHLWs8Oli7wKJSuTwKfQyELDtwIuHyl8Tech4n9A2/CoIJMcGOQ # 5u/Pk4UJLhFgSumkQCMKYeWdrG+odKE4+lRu23ZENgRvhkrxB2ONaWU7h4o2Jq4G # jO6JAlMEwohXl8vZ/FwrnGfY9glfd6IvFnrkBo5PzXKfz/gt0kGkvJYV3MFE/NuN # OZXhVhpBvHMkd6x9FbclV+YBuRMFh6adtuTaIWl0n6IjgchK0O8Tn3qtnMY0sMf/ # dHpvUciQCKDB9sFJbBCzenE3YmpZ70UV2Zs+9RSotISwu1KyO2fNDyFvyPul89Sm # 7DixtZNUBr+qzWsyIm/n6k1CPMkKW5EBq6hj81wdkU07FW9SF6TAT7nh8kDCN1Ph # omzF/n1VrVaIOxNwdBYsLO27KFn6lHpR/K2QnHE/ATEHTZ0WSgbXL2RHtTQbEmsg # iKw= # SIG # End signature block |