functions/github/Assert-GitHubTeamRepoAccess.ps1
function Assert-GitHubTeamRepoAccess { [CmdletBinding()] param ( [Parameter(Mandatory=$true, ParameterSetName="ByName")] [string] $Org, [Parameter(Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="ByObject")] $Team, [Parameter(Mandatory=$true, ParameterSetName="ByName")] [string] $TeamName, [Parameter(Mandatory=$true)] [string[]] $Repositories, [Parameter(Mandatory=$true)] [ValidateSet("pull","push","admin","triage","maintain")] [string] $RepoAccess ) if ($PSCmdlet.ParameterSetName -eq "ByObject") { $Org = $Team.organization.login $TeamName = $Team.slug } else { $Team = Invoke-GitHubRestMethod -Uri "https://api.github.com/orgs/$Org/teams/$TeamName" if (!$Team) { throw "Something went wrong processing the team repository access - the team '$TeamName' could not be found" } } # Setup results data structure $teamReposAccessResults = @{ team_name = $TeamName repos_permission_added = @() repos_permission_removed = @() repos_permission_updated = @() } Write-Information "Processing team repository access for '$TeamName'" $existingRepositories = _getGitHubTeamRepos # Add the team to any repos it should have access to $reposToGrant = $Repositories | ? { $_ -notin $existingRepositories.name } foreach ($repoToGrant in $reposToGrant) { Write-Information "Granting team '$TeamName' '$RepoAccess' access to '$Org/$repoToGrant'" _addRepoPermissions $repoToGrant $teamReposAccessResults.repos_permission_added += "$Org/$repoToGrant" } # Remove any access the team should not have $reposToRevoke = $existingRepositories.name | ? { $_ -notin $Repositories } foreach ($repoToRevoke in $reposToRevoke) { Write-Information "Removing team '$TeamName' '$RepoAccess' access to '$Org/$repoToRevoke'" _removeRepoPermissions $repoToRevoke $teamReposAccessResults.repos_permission_removed += "$Org/$repoToRevoke" } # Handle where a team should have access, but currently has the wrong permissions $existingPermissionsProjection = @{} $existingRepositories | ? { $_.name -in $Repositories } | # this filters out any repos the team doesn't have explicit access to % { $existingPermissionsProjection.Add($_.name, ($_.permissions | ConvertTo-Json | ConvertFrom-Json -AsHashtable)) } # extract a key/value pair "<repoName>=<currentPermission>" (convert the PSObject from the API into a Hashtable by roundtripping via JSON) $reposToUpdate = $existingPermissionsProjection.Keys | ? { ` $_ -and -not (_hasCorrectRepoPermissions $RepoAccess $existingPermissionsProjection[$_]) # use the helper to check whether the set of permission flags returned by the API equate to the required access } foreach ($repoToUpdate in $reposToUpdate) { Write-Information "Updating team '$TeamName' '$RepoAccess' access to '$Org/$repoToUpdate'" _updateRepoPermissions $repoToUpdate $teamReposAccessResults.repos_permission_updated += "$Org/$repoToUpdate" } $teamReposAccessResults } #region Extracted functions to simplify mocking function _getGitHubTeamRepos { Invoke-GitHubRestMethod -Uri "https://api.github.com/orgs/$Org/teams/$TeamName/repos" -AllPages } function _addRepoPermissions { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $RepoName ) _invokeUpdateTeamRepoPermissions @PSBoundParameters } function _removeRepoPermissions { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $RepoName ) $resp = Invoke-GitHubRestMethod ` -Uri "https://api.github.com/orgs/$Org/teams/$TeamName/repos/$Org/$RepoName" ` -Verb "DELETE" } function _updateRepoPermissions { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $RepoName ) _invokeUpdateTeamRepoPermissions @PSBoundParameters } function _invokeUpdateTeamRepoPermissions { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $RepoName ) # $permissions = _getRepoPermissionsApiObject $RepoAccess $resp = Invoke-GitHubRestMethod -Uri "https://api.github.com/orgs/$Org/teams/$TeamName/repos/$Org/$RepoName" ` -Verb "PUT" ` -Body @{ permission = $RepoAccess } } #endregion #region Helper functions function _getRepoPermissionsApiObject { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [ValidateSet("pull","push","admin","triage","maintain")] [string] $RequiredPermission ) # # NOTE: The seemingly boolean values are set similarly to a bitmask # # Pull = pull # Push = push, pull, triage # Admin = admin, push, pull, triage, maintain # Maintain = maintain, triage, push, pull # Triage = triage, pull # Set no permissions by default $permissionsObject = [ordered]@{ pull = $false triage = $false push = $false maintain = $false admin = $false } # Use a switch statement that will process mulitple matches to # apply the required bitmask-esque combinations switch($RequiredPermission) { "admin" { $permissionsObject.admin = $true } {$_ -in "admin","maintain"} { $permissionsObject.maintain = $true } {$_ -in "admin","maintain","push"} { $permissionsObject.push = $true } {$_ -in "admin","maintain","push","triage"} { $permissionsObject.triage = $true } } # any permission will have 'pull' $permissionsObject.pull = $true return $permissionsObject } function _hasCorrectRepoPermissions { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $RequiredPermission, [Parameter(Mandatory=$true)] [hashtable] $PermissionsTable ) # A given permission maps to a set of boolean flags for each possible permission that resembles a bitmask $expectedPermissions = _getRepoPermissionsApiObject -RequiredPermission $RequiredPermission $isCorrect = $true foreach ($perm in $PermissionsTable.Keys) { if ($PermissionsTable[$perm] -ne $expectedPermissions[$perm]) { $isCorrect = $false } } return $isCorrect } #endregion |