diff --git a/src/classes/public/App/GitHubApp.ps1 b/src/classes/public/App/GitHubApp.ps1 index 620f52af5..9356e308c 100644 --- a/src/classes/public/App/GitHubApp.ps1 +++ b/src/classes/public/App/GitHubApp.ps1 @@ -36,7 +36,7 @@ [System.Nullable[datetime]] $UpdatedAt # The permissions that the app is requesting. - [pscustomobject] $Permissions + [GitHubPermission[]] $Permissions # The events that the app is subscribing to on its target. [string[]] $Events @@ -59,7 +59,7 @@ $this.Url = $Object.html_url $this.CreatedAt = $Object.created_at $this.UpdatedAt = $Object.updated_at - $this.Permissions = $Object.permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions) $this.Events = , ($Object.events) $this.Installations = $Object.installations_count } diff --git a/src/classes/public/App/GitHubAppInstallation.ps1 b/src/classes/public/App/GitHubAppInstallation.ps1 index 96fd2831c..3fecc9140 100644 --- a/src/classes/public/App/GitHubAppInstallation.ps1 +++ b/src/classes/public/App/GitHubAppInstallation.ps1 @@ -15,7 +15,7 @@ [string] $RepositorySelection # The permissions that the app has on the target. - [pscustomobject] $Permissions + [GitHubPermission[]] $Permissions # The events that the app is subscribing to. [string[]] $Events @@ -41,6 +41,9 @@ # The URL to the target's profile based on the target type. [string] $Url + # The status indicating if the installation permissions and events match the app's configuration. + [string] $Status + GitHubAppInstallation() {} GitHubAppInstallation([PSCustomObject] $Object) { @@ -55,7 +58,24 @@ $this.Target = [GitHubOwner]::new($Object.account) $this.Type = $Object.target_type $this.RepositorySelection = $Object.repository_selection - $this.Permissions = $Object.permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions, $this.Type) + $this.Events = , ($Object.events) + $this.FilePaths = $Object.single_file_paths + $this.CreatedAt = $Object.created_at + $this.UpdatedAt = $Object.updated_at + $this.SuspendedAt = $Object.suspended_at + $this.SuspendedBy = [GitHubUser]::new($Object.suspended_by) + $this.Url = $Object.html_url + $this.Status = 'Unknown' + } + + GitHubAppInstallation([PSCustomObject] $Object, [GitHubApp] $App) { + $this.ID = $Object.id + $this.App = $App + $this.Target = [GitHubOwner]::new($Object.account) + $this.Type = $Object.target_type + $this.RepositorySelection = $Object.repository_selection + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions, $this.Type) $this.Events = , ($Object.events) $this.FilePaths = $Object.single_file_paths $this.CreatedAt = $Object.created_at @@ -63,6 +83,7 @@ $this.SuspendedAt = $Object.suspended_at $this.SuspendedBy = [GitHubUser]::new($Object.suspended_by) $this.Url = $Object.html_url + $this.UpdateStatus() } GitHubAppInstallation([PSCustomObject] $Object, [string] $Target, [string] $Type, [GitHubContext] $Context) { @@ -81,7 +102,28 @@ } $this.Type = $Type $this.RepositorySelection = $Object.repository_selection - $this.Permissions = $Object.permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions, $this.Type) + $this.Events = , ($Object.events) + $this.FilePaths = $Object.single_file_paths + $this.CreatedAt = $Object.created_at + $this.UpdatedAt = $Object.updated_at + $this.SuspendedAt = $Object.suspended_at + $this.SuspendedBy = [GitHubUser]::new($Object.suspended_by) + $this.Url = "https://$($Context.HostName)/$($Type.ToLower())s/$Target/settings/installations/$($Object.id)" + $this.Status = 'Unknown' + } + + GitHubAppInstallation([PSCustomObject] $Object, [string] $Target, [string] $Type, [GitHubContext] $Context, [GitHubApp] $App) { + $this.ID = $Object.id + $this.App = $App + $this.Target = [GitHubOwner]@{ + Name = $Target + Type = $Type + Url = "https://$($Context.HostName)/$Target" + } + $this.Type = $Type + $this.RepositorySelection = $Object.repository_selection + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions, $this.Type) $this.Events = , ($Object.events) $this.FilePaths = $Object.single_file_paths $this.CreatedAt = $Object.created_at @@ -89,5 +131,66 @@ $this.SuspendedAt = $Object.suspended_at $this.SuspendedBy = [GitHubUser]::new($Object.suspended_by) $this.Url = "https://$($Context.HostName)/$($Type.ToLower())s/$Target/settings/installations/$($Object.id)" + $this.UpdateStatus() + } + + # Updates the Status property by comparing installation permissions with app permissions + # filtered by the appropriate scope based on installation type + [void] UpdateStatus() { + if (-not $this.App -or -not $this.App.Permissions) { + $this.Status = 'Unknown' + return + } + + # Get app permissions filtered by installation type scope + $appPermissionsFiltered = switch ($this.Type) { + 'Enterprise' { + $this.App.Permissions | Where-Object { $_.Scope -eq 'Enterprise' } + } + 'Organization' { + $this.App.Permissions | Where-Object { $_.Scope -in @('Organization', 'Repository') } + } + 'User' { + $this.App.Permissions | Where-Object { $_.Scope -in @('Repository') } + } + default { + $this.App.Permissions + } + } + + # Compare permissions by creating lookup dictionaries + $appPermissionLookup = @{} + foreach ($perm in $appPermissionsFiltered) { + $appPermissionLookup[$perm.Name] = $perm.Value + } + + $installationPermissionLookup = @{} + foreach ($perm in $this.Permissions) { + $installationPermissionLookup[$perm.Name] = $perm.Value + } + + # Check if permissions match + $permissionsMatch = $true + + # Check if all app permissions exist in installation with same values + foreach ($name in $appPermissionLookup.Keys) { + if (-not $installationPermissionLookup.ContainsKey($name) -or + $installationPermissionLookup[$name] -ne $appPermissionLookup[$name]) { + $permissionsMatch = $false + break + } + } + + # Check if installation has any extra permissions not in the app + if ($permissionsMatch) { + foreach ($name in $installationPermissionLookup.Keys) { + if (-not $appPermissionLookup.ContainsKey($name)) { + $permissionsMatch = $false + break + } + } + } + + $this.Status = $permissionsMatch ? 'Ok' : 'Outdated' } } diff --git a/src/classes/public/Context/GitHubContext/GitHubAppContext.ps1 b/src/classes/public/Context/GitHubContext/GitHubAppContext.ps1 index 02cc68a67..856433681 100644 --- a/src/classes/public/Context/GitHubContext/GitHubAppContext.ps1 +++ b/src/classes/public/Context/GitHubContext/GitHubAppContext.ps1 @@ -15,7 +15,7 @@ [string] $OwnerType # The permissions that the app is requesting on the target - [pscustomobject] $Permissions + [GitHubPermission[]] $Permissions # The events that the app is subscribing to once installed [string[]] $Events @@ -47,7 +47,7 @@ $this.KeyVaultKeyReference = $Object.KeyVaultKeyReference $this.OwnerName = $Object.OwnerName $this.OwnerType = $Object.OwnerType - $this.Permissions = $Object.Permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.Permissions) $this.Events = $Object.Events } } diff --git a/src/classes/public/Context/GitHubContext/GitHubAppInstallationContext.ps1 b/src/classes/public/Context/GitHubContext/GitHubAppInstallationContext.ps1 index 2b817f3ec..93b5a7934 100644 --- a/src/classes/public/Context/GitHubContext/GitHubAppInstallationContext.ps1 +++ b/src/classes/public/Context/GitHubContext/GitHubAppInstallationContext.ps1 @@ -6,7 +6,7 @@ [System.Nullable[uint64]] $InstallationID # The permissions that the app is requesting on the target - [pscustomobject] $Permissions + [GitHubPermission[]] $Permissions # The events that the app is subscribing to once installed [string[]] $Events @@ -41,7 +41,7 @@ $this.PerPage = $Object.PerPage $this.ClientID = $Object.ClientID $this.InstallationID = $Object.InstallationID - $this.Permissions = $Object.Permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.Permissions, $Object.InstallationType) $this.Events = $Object.Events $this.InstallationType = $Object.InstallationType $this.InstallationName = $Object.InstallationName diff --git a/src/classes/public/GitHubPermission.ps1 b/src/classes/public/GitHubPermission.ps1 new file mode 100644 index 000000000..db355a3f8 --- /dev/null +++ b/src/classes/public/GitHubPermission.ps1 @@ -0,0 +1,1344 @@ +class GitHubPermissionDefinition { + # The programmatic name of the permission as returned by the GitHub API + [string] $Name + + # The human-friendly name of the permission as shown in the GitHub UI + [string] $DisplayName + + # A brief description of what access the permission grants + [string] $Description + + # A link to the relevant documentation or GitHub UI page + [uri] $URL + + # The levels of access that can be granted for this permission + [string[]] $Options + + # The type of permission (Fine-grained, Classic) + [string] $Type + + # The scope at which this permission applies (Repository, Organization, User, Enterprise) + [string] $Scope + + static [GitHubPermissionDefinition[]] $List = @( + # ------------------------------ + # Repository Fine-Grained Permission Definitions + # ------------------------------ + [GitHubPermissionDefinition]::new( + 'actions', + 'Actions', + 'Workflows, workflow runs and artifacts.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-actions', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'administration', + 'Administration', + 'Repository creation, deletion, settings, teams, and collaborators.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-administration', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'attestations', + 'Attestations', + 'Create and retrieve attestations for a repository.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-attestations', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'checks', + 'Checks', + 'Checks on code.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-checks', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'security_events', + 'Code scanning alerts', + 'View and manage code scanning alerts.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-code-scanning-alerts', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'codespaces', + 'Codespaces', + 'Create, edit, delete and list Codespaces.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-codespaces', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'codespaces_lifecycle_admin', + 'Codespaces lifecycle admin', + 'Manage the lifecycle of Codespaces, including starting and stopping.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-codespaces-lifecycle-admin', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'codespaces_metadata', + 'Codespaces metadata', + 'Access Codespaces metadata including the devcontainers and machine type.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-codespaces-metadata', + @( + 'read' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'codespaces_secrets', + 'Codespaces secrets', + 'Restrict Codespaces user secrets modifications to specific repositories.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-codespaces-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'statuses', + 'Commit statuses', + 'Commit statuses.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-commit-statuses', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'contents', + 'Contents', + 'Repository contents, commits, branches, downloads, releases, and merges.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-contents', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repository_custom_properties', + 'Custom properties', + 'Read and write repository custom properties values at the repository level, when allowed by the property.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-custom-properties', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'vulnerability_alerts', + 'Dependabot alerts', + 'Retrieve Dependabot alerts.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-dependabot-alerts', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'dependabot_secrets', + 'Dependabot secrets', + 'Manage Dependabot repository secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-dependabot-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'deployments', + 'Deployments', + 'Deployments and deployment statuses.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-deployments', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'discussions', + 'Discussions', + 'Discussions and related comments and labels.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-discussions', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'environments', + 'Environments', + 'Manage repository environments.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-environments', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'issues', + 'Issues', + 'Issues and related comments, assignees, labels, and milestones.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-issues', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'merge_queues', + 'Merge queues', + "Manage a repository's merge queues", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-merge-queues', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( #Mandatory + 'metadata', + 'Metadata', + 'Search repositories, list collaborators, and access repository metadata.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-metadata', + @( + 'read' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'packages', + 'Packages', + 'Packages published to the GitHub Package Platform.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-packages', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'pages', + 'Pages', + 'Retrieve Pages statuses, configuration, and builds, as well as create new builds.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-pages', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repository_projects', + 'Projects', + 'Manage classic projects within a repository.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-projects', + @( + 'read', + 'write', + 'admin' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'pull_requests', + 'Pull requests', + 'Pull requests and related comments, assignees, labels, milestones, and merges.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-pull-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repository_advisories', + 'Repository security advisories', + 'View and manage repository security advisories.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-repository-security-advisories', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repo_secret_scanning_dismissal_requests', + 'Secret scanning alert dismissal requests', + 'View and manage secret scanning alert dismissal requests', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-secret-scanning-alert-dismissal-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'secret_scanning_alerts', + 'Secret scanning alerts', + 'View and manage secret scanning alerts.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-secret-scanning-alerts', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'secret_scanning_bypass_requests', + 'Secret scanning push protection bypass requests', + 'Review and manage repository secret scanning push protection bypass requests.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-secret-scanning-push-protection-bypass-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'secrets', + 'Secrets', + 'Manage Actions repository secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'single_file', + 'Single file', + 'Manage just a single file.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-single-file', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'actions_variables', + 'Variables', + 'Manage Actions repository variables.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-variables', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repository_hooks', + 'Webhooks', + 'Manage the post-receive hooks for a repository.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-webhooks', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'workflows', + 'Workflows', + 'Update GitHub Action workflow files.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-workflows', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + + # ------------------------------ + # Organization Fine-Grained Permission Definitions + # ------------------------------ + [GitHubPermissionDefinition]::new( + 'organization_api_insights', + 'API Insights', + 'View statistics on how the API is being used for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-api-insights', + @( + 'read' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_administration', + 'Administration', + 'Manage access to an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-administration', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_user_blocking', + 'Blocking users', + 'View and manage users blocked by the organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-blocking-users', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_campaigns', + 'Campaigns', + 'Manage campaigns.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-campaigns', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_custom_org_roles', + 'Custom organization roles', + 'Create, edit, delete and list custom organization roles. View system organization roles.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-custom-organization-roles', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_custom_properties', + 'Custom properties', + 'Read and write repository custom properties values and administer definitions at the organization level.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-custom-properties', + @( + 'read', + 'write', + 'admin' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_custom_roles', + 'Custom repository roles', + 'Create, edit, delete and list custom repository roles.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-custom-repository-roles', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_events', + 'Events', + 'View events triggered by an activity in an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-events', + @( + 'read' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_copilot_seat_management', + 'GitHub Copilot Business', + 'Manage Copilot Business seats and settings', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-github-copilot-business', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'issue_fields', + 'Issue Fields', + 'Manage issue fields for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-issue-fields', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'issue_types', + 'Issue Types', + 'Manage issue types for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-issue-types', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_knowledge_bases', + 'Knowledge bases', + 'View and manage knowledge bases for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-knowledge-bases', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'members', + 'Members', + 'Organization members and teams.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-members', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_models', + 'Models', + 'Manage model access for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-models', + @( + 'read' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_network_configurations', + 'Network configurations', + 'View and manage hosted compute network configurations available to an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-network-configurations', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_announcement_banners', + 'Organization announcement banners', + 'View and modify announcement banners for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-announcement-banners', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_secret_scanning_bypass_requests', + 'Organization bypass requests for secret scanning', + 'Review and manage secret scanning push protection bypass requests.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-bypass-requests-for-secret-scanning', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_codespaces', + 'Organization codespaces', + 'Manage Codespaces for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-codespaces', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_codespaces_secrets', + 'Organization codespaces secrets', + 'Manage Codespaces Secrets for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-codespaces-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_codespaces_settings', + 'Organization codespaces settings', + 'Manage Codespaces settings for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-codespaces-settings', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_dependabot_secrets', + 'Organization dependabot secrets', + 'Manage Dependabot organization secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-dependabot-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_code_scanning_dismissal_requests', + 'Organization dismissal requests for code scanning', + 'Review and manage code scanning alert dismissal requests.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-dismissal-requests-for-code-scanning', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_private_registries', + 'Organization private registries', + 'Manage private registries for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-private-registries', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_personal_access_token_requests', + 'Personal access token requests', + 'Manage personal access token requests from organization members.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-personal-access-token-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_personal_access_tokens', + 'Personal access tokens', + 'View and revoke personal access tokens that have been granted access to an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-personal-access-tokens', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_plan', + 'Plan', + "View an organization's plan.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-plan', + @( + 'read' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_projects', + 'Projects', + 'Manage projects for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-projects', + @( + 'read', + 'write', + 'admin' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'secret_scanning_dismissal_requests', + 'Secret scanning alert dismissal requests', + 'Review and manage secret scanning alert dismissal requests', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-secret-scanning-alert-dismissal-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_secrets', + 'Secrets', + 'Manage Actions organization secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_self_hosted_runners', + 'Self-hosted runners', + 'View and manage Actions self-hosted runners available to an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-self-hosted-runners', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'team_discussions', + 'Team discussions', + 'Manage team discussions and related comments.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-team-discussions', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_actions_variables', + 'Variables', + 'Manage Actions organization variables.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-variables', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_hooks', + 'Webhooks', + 'Manage the post-receive hooks for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-webhooks', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + + # ------------------------------ + # User (Account) Fine-Grained Permission Definitions + # ------------------------------ + [GitHubPermissionDefinition]::new( + 'blocking', + 'Block another user', + 'View and manage users blocked by the user.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-block-another-user', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'codespaces_user_secrets', + 'Codespaces user secrets', + 'Manage Codespaces user secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-codespaces-user-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'copilot_messages', + 'Copilot Chat', + 'This application will receive your GitHub ID, your GitHub Copilot Chat session messages ' + + '(not including messages sent to another application), and timestamps of provided GitHub Copilot ' + + 'Chat session messages. This permission must be enabled for Copilot Extensions.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-copilot-chat', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'copilot_editor_context', + 'Copilot Editor Context', + 'This application will receive bits of Editor Context (e.g. currently opened file) whenever you send it a message through Copilot Chat.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-copilot-editor-context', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'emails', + 'Email addresses', + "Manage a user's email addresses.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-email-addresses', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'user_events', + 'Events', + "View events triggered by a user's activity.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-events', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'followers', + 'Followers', + "A user's followers", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-followers', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'gpg_keys', + 'GPG keys', + "View and manage a user's GPG keys.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-gpg-keys', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'gists', + 'Gists', + "Create and modify a user's gists and comments.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-gists', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'keys', + 'Git SSH keys', + 'Git SSH keys', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-git-ssh-keys', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'interaction_limits', + 'Interaction limits', + 'Interaction limits on repositories', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-interaction-limits', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'knowledge_bases', + 'Knowledge bases', + 'View knowledge bases for a user.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-knowledge-bases', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'user_models', + 'Models', + 'Allows access to GitHub Models.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-models', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'plan', + 'Plan', + "View a user's plan.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-plan', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'profile', + 'Profile', + "Manage a user's profile settings.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-profile', + @( + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'git_signing_ssh_public_keys', + 'SSH signing keys', + "View and manage a user's SSH signing keys.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-ssh-signing-keys', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'starring', + 'Starring', + 'List and manage repositories a user is starring.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-starring', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'watching', + 'Watching', + 'List and change repositories a user is subscribed to.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-watching', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + + # ------------------------------ + # Enterprise Fine-Grained Permission Definitions + # ------------------------------ + [GitHubPermissionDefinition]::new( + 'enterprise_custom_properties', + 'Custom properties', + 'View repository custom properties and administer definitions at the enterprise level.', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-custom-properties', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_custom_org_roles', + 'Enterprise custom organization roles', + 'Create, edit, delete and list custom organization roles at the enterprise level. View system organization roles.', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-custom-organization-roles', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_organization_installation_repositories', + 'Enterprise organization installation repositories', + 'Manage repository access of GitHub Apps on Enterprise-owned organizations', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-organization-installation-repositories', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_organization_installations', + 'Enterprise organization installations', + 'Manage installation of GitHub Apps on Enterprise-owned organizations', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-organization-installations', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_organizations', + 'Enterprise organizations', + 'Create and remove enterprise organizations', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-organizations', + @( + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_people', + 'Enterprise people', + 'Manage user access to the enterprise', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-people', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_sso', + 'Enterprise single sign-on', + 'View and manage enterprise single sign-on configuration', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-single-sign-on', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ) + ) + + GitHubPermissionDefinition() {} + + GitHubPermissionDefinition( + [string]$Name, + [string]$DisplayName, + [string]$Description, + [string]$URL, + [string[]]$Options, + [string]$Type, + [string]$Scope + ) { + $this.Name = $Name + $this.DisplayName = $DisplayName + $this.Description = $Description + $this.URL = [uri]$URL + $this.Options = $Options + $this.Type = $Type + $this.Scope = $Scope + } + + [string] ToString() { + return $this.Name + } +} + +class GitHubPermission : GitHubPermissionDefinition { + # The value assigned to the permission. Must be one of the options defined in the parent class. + [string] $Value + + GitHubPermission() : base() {} + + GitHubPermission([string] $Permission, [string] $Value) : base() { + $this.Name = $Permission + $this.Value = $Value + $this.DisplayName = $Permission + $this.Description = 'Unknown permission - Open issue to add metadata' + $this.URL = $null + $this.Options = @() + $this.Type = 'Unknown' + $this.Scope = 'Unknown' + } + + # Create a new list of all known permissions with null values + static [GitHubPermission[]] NewPermissionList() { + $tmpList = foreach ($def in [GitHubPermissionDefinition]::List) { + [GitHubPermission]@{ + Name = $def.Name + Value = $null + DisplayName = $def.DisplayName + Description = $def.Description + URL = $def.URL + Options = $def.Options + Type = $def.Type + Scope = $def.Scope + } + } + return $tmpList | Sort-Object Scope, DisplayName + } + + # Create a new list of permissions filtered by installation type with null values + static [GitHubPermission[]] NewPermissionList([string] $InstallationType) { + $all = [GitHubPermission]::NewPermissionList() + $returned = switch ($InstallationType) { + 'Enterprise' { $all | Where-Object { $_.Scope -eq 'Enterprise' } } + 'Organization' { $all | Where-Object { $_.Scope -in @('Organization', 'Repository') } } + 'User' { $all | Where-Object { $_.Scope -in @('Repository') } } + default { $all } + } + return $returned | Sort-Object Scope, DisplayName + } + + # Create a new list of all permissions with values from a PSCustomObject + static [GitHubPermission[]] NewPermissionList([pscustomobject] $Object) { + $all = [GitHubPermission]::NewPermissionList() + foreach ($name in $Object.PSObject.Properties.Name) { + $objectValue = $Object.$name + $knownPermission = $all | Where-Object { $_.Name -eq $name } + if ($knownPermission) { + $knownPermission.Value = $objectValue + } else { + $all += [GitHubPermission]::new($name, $objectValue) + } + } + return $all | Sort-Object Scope, DisplayName + } + + # Create a new list of permissions filtered by installation type with values from a PSCustomObject + static [GitHubPermission[]] NewPermissionList([pscustomobject] $Object, [string] $InstallationType) { + $all = [GitHubPermission]::NewPermissionList($InstallationType) + foreach ($name in $Object.PSObject.Properties.Name) { + $objectValue = $Object.$name + $knownPermission = $all | Where-Object { $_.Name -eq $name } + if ($knownPermission) { + $knownPermission.Value = $objectValue + } else { + $all += [GitHubPermission]::new($name, $objectValue) + } + } + return $all | Sort-Object Scope, DisplayName + } + + # Create a new list of permissions with values from an array of objects (import functionality) + static [GitHubPermission[]] NewPermissionList([object[]] $Objects) { + $all = [GitHubPermission]::NewPermissionList() + foreach ($obj in $Objects) { + $knownPermission = $all | Where-Object { $_.Name -eq $obj.Name } + if ($knownPermission) { + $knownPermission.Value = $obj.Value + } else { + $all += [GitHubPermission]::new($obj.Name, $obj.Value) + } + } + return $all | Sort-Object Scope, DisplayName + } + + # Create a new list of permissions filtered by installation type with values from an array of objects (import functionality) + static [GitHubPermission[]] NewPermissionList([object[]] $Objects, [string] $InstallationType) { + $all = [GitHubPermission]::NewPermissionList($InstallationType) + foreach ($obj in $Objects) { + $knownPermission = $all | Where-Object { $_.Name -eq $obj.Name } + if ($knownPermission) { + $knownPermission.Value = $obj.Value + } else { + $all += [GitHubPermission]::new($obj.Name, $obj.Value) + } + } + return $all | Sort-Object Scope, DisplayName + } +} diff --git a/src/classes/public/GitHubPermissionDefinition.ps1 b/src/classes/public/GitHubPermissionDefinition.ps1 deleted file mode 100644 index 93ff6643f..000000000 --- a/src/classes/public/GitHubPermissionDefinition.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -class GitHubPermissionDefinition { - # The programmatic name of the permission as returned by the GitHub API - [string] $Name - - # The human-friendly name of the permission as shown in the GitHub UI - [string] $DisplayName - - # A brief description of what access the permission grants - [string] $Description - - # A link to the relevant documentation or GitHub UI page - [uri] $URL - - # The levels of access that can be granted for this permission - [string[]] $Options - - # The type of permission (Fine-grained, Classic) - [string] $Type - - # The scope at which this permission applies (Repository, Organization, User, Enterprise) - [string] $Scope - - GitHubPermissionDefinition() {} - - GitHubPermissionDefinition( - [string]$Name, - [string]$DisplayName, - [string]$Description, - [string]$URL, - [string[]]$Options, - [string]$Type, - [string]$Scope - ) { - $this.Name = $Name - $this.DisplayName = $DisplayName - $this.Description = $Description - $this.URL = [uri]$URL - $this.Options = $Options - $this.Type = $Type - $this.Scope = $Scope - } - - [string] ToString() { - return $this.Name - } -} diff --git a/src/formats/GitHubAppInstallation.Format.ps1xml b/src/formats/GitHubAppInstallation.Format.ps1xml index 876cc43b7..d81a69fe6 100644 --- a/src/formats/GitHubAppInstallation.Format.ps1xml +++ b/src/formats/GitHubAppInstallation.Format.ps1xml @@ -20,6 +20,9 @@ + + + @@ -56,6 +59,9 @@ Type + + Status + CreatedAt @@ -88,6 +94,9 @@ Type + + Status + Url diff --git a/src/formats/GitHubPermission.Format.ps1xml b/src/formats/GitHubPermission.Format.ps1xml new file mode 100644 index 000000000..d917b0c3c --- /dev/null +++ b/src/formats/GitHubPermission.Format.ps1xml @@ -0,0 +1,52 @@ + + + + + GitHubPermissionTable + + GitHubPermission + + + + + + + + + + + + + + + + + + + + + Scope + + + + if ($Host.UI.SupportsVirtualTerminal -and + ($env:GITHUB_ACTIONS -ne 'true') -and $_.Url) { + $PSStyle.FormatHyperlink($_.DisplayName, $_.Url) + } else { + $_.DisplayName + } + + + + Value + + + Description + + + + + + + + diff --git a/src/functions/private/Apps/GitHub Apps/Get-GitHubAppInstallationForAuthenticatedApp.ps1 b/src/functions/private/Apps/GitHub Apps/Get-GitHubAppInstallationForAuthenticatedApp.ps1 index afbe9e03c..58f595659 100644 --- a/src/functions/private/Apps/GitHub Apps/Get-GitHubAppInstallationForAuthenticatedApp.ps1 +++ b/src/functions/private/Apps/GitHub Apps/Get-GitHubAppInstallationForAuthenticatedApp.ps1 @@ -46,9 +46,12 @@ Context = $Context } + # Get the authenticated app to compare permissions and events + $authenticatedApp = Get-GitHubAuthenticatedApp -Context $Context + Invoke-GitHubAPI @apiParams | ForEach-Object { foreach ($installation in $_.Response) { - [GitHubAppInstallation]::new($installation) + [GitHubAppInstallation]::new($installation, $authenticatedApp) } } } diff --git a/src/functions/private/Auth/Context/Set-GitHubContext.ps1 b/src/functions/private/Auth/Context/Set-GitHubContext.ps1 index c20ea4281..91ba59e6f 100644 --- a/src/functions/private/Auth/Context/Set-GitHubContext.ps1 +++ b/src/functions/private/Auth/Context/Set-GitHubContext.ps1 @@ -127,7 +127,7 @@ $contextObj['Username'] = [string]$app.Slug $contextObj['NodeID'] = [string]$app.NodeID $contextObj['DatabaseID'] = [string]$app.ID - $contextObj['Permissions'] = [PSCustomObject]$app.Permissions + $contextObj['Permissions'] = [GitHubPermission[]]$app.Permissions $contextObj['Events'] = [string[]]$app.Events $contextObj['OwnerName'] = [string]$app.Owner.Name $contextObj['OwnerType'] = [string]$app.Owner.Type diff --git a/src/functions/public/Auth/Connect-GitHubApp.ps1 b/src/functions/public/Auth/Connect-GitHubApp.ps1 index e69343465..7e208f006 100644 --- a/src/functions/public/Auth/Connect-GitHubApp.ps1 +++ b/src/functions/public/Auth/Connect-GitHubApp.ps1 @@ -134,9 +134,9 @@ HttpVersion = [string]$Context.HttpVersion PerPage = [int]$Context.PerPage ClientID = [string]$Context.ClientID - InstallationID = [string]$installation.id - Permissions = [pscustomobject]$installation.permissions - Events = [string[]]$installation.events + InstallationID = [string]$installation.ID + Permissions = [GitHubPermission[]]$installation.Permissions + Events = [string[]]$installation.Events InstallationType = [string]$installation.Type Token = [securestring]$token.Token TokenExpiresAt = [datetime]$token.ExpiresAt diff --git a/src/functions/public/Auth/Context/Switch-GitHubContext.ps1 b/src/functions/public/Auth/Context/Switch-GitHubContext.ps1 index 0a6149a66..b4f7ed876 100644 --- a/src/functions/public/Auth/Context/Switch-GitHubContext.ps1 +++ b/src/functions/public/Auth/Context/Switch-GitHubContext.ps1 @@ -16,7 +16,7 @@ param( # The context to run the command in. Used to get the details for the API call. # Can be either a string or a GitHubContext object. - [Parameter(ValueFromPipeline)] + [Parameter(Mandatory, ValueFromPipeline, Position = 0)] [object] $Context ) diff --git a/src/functions/public/Config/completers.ps1 b/src/functions/public/Config/completers.ps1 index 1165d3170..c498f0497 100644 --- a/src/functions/public/Config/completers.ps1 +++ b/src/functions/public/Config/completers.ps1 @@ -10,10 +10,37 @@ Register-ArgumentCompleter -CommandName Set-GitHubConfig -ParameterName Value -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters - if ($fakeBoundParameters.Name -eq 'CompletionMode') { - $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - @('StartsWith', 'Contains') | Where-Object { $_ -like $pattern } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } + switch ($fakeBoundParameters.Name) { + 'CompletionMode' { + @('StartsWith', 'Contains') | Where-Object { $_ -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } + 'HttpVersion' { + @('1.0', '1.1', '2.0', '3.0') | Where-Object { $_ -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } + 'EnvironmentType' { + @('Local', 'GitHubActions', 'FunctionApp', 'Unknown') | Where-Object { $_ -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } + 'ApiVersion' { + $params = @{ + Context = $fakeBoundParameters.Context + Debug = $false + Verbose = $false + } + Get-GitHubApiVersion @params | Where-Object { $_ -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } + 'DefaultContext' { + Get-GitHubContext -ListAvailable | Where-Object { $_.Name -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) + } } } } diff --git a/src/functions/public/Meta/Get-GitHubApiVersion.ps1 b/src/functions/public/Meta/Get-GitHubApiVersion.ps1 index 7a618a104..cd9ac2b0a 100644 --- a/src/functions/public/Meta/Get-GitHubApiVersion.ps1 +++ b/src/functions/public/Meta/Get-GitHubApiVersion.ps1 @@ -34,10 +34,13 @@ $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" $Context = Resolve-GitHubContext -Context $Context -Anonymous $Anonymous - Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT, Anonymous + Assert-GitHubContext -Context $Context -AuthType App, IAT, PAT, UAT, Anonymous } process { + if ($Context.AuthType -eq 'APP') { + $Context = 'Anonymous' + } $apiParams = @{ Method = 'GET' ApiEndpoint = '/versions' diff --git a/src/functions/public/Permission/Get-GitHubPermissionDefinition.ps1 b/src/functions/public/Permission/Get-GitHubPermissionDefinition.ps1 index 0019adf59..f7db75c69 100644 --- a/src/functions/public/Permission/Get-GitHubPermissionDefinition.ps1 +++ b/src/functions/public/Permission/Get-GitHubPermissionDefinition.ps1 @@ -86,7 +86,7 @@ function Get-GitHubPermissionDefinition { return $false } - $script:GitHub.Permissions | Where-Object { + [GitHubPermissionDefinition]::List | Where-Object { (& $test -Value $_.Name -Patterns $Name) -and (& $test -Value $_.DisplayName -Patterns $DisplayName) -and (& $test -Value $_.Type -Patterns $Type) -and diff --git a/src/functions/public/Permission/completers.ps1 b/src/functions/public/Permission/completers.ps1 index 4586354f0..dc0be7453 100644 --- a/src/functions/public/Permission/completers.ps1 +++ b/src/functions/public/Permission/completers.ps1 @@ -2,7 +2,7 @@ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - $script:GitHub.Permissions.Name | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + [GitHubPermissionDefinition]::List.Name | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -11,7 +11,7 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - $script:GitHub.Permissions.DisplayName | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + [GitHubPermissionDefinition]::List.DisplayName | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -20,7 +20,7 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - $script:GitHub.Permissions.Type | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + [GitHubPermissionDefinition]::List.Type | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -29,7 +29,7 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - $script:GitHub.Permissions.Scope | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + [GitHubPermissionDefinition]::List.Scope | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } diff --git a/src/variables/private/GitHub.ps1 b/src/variables/private/GitHub.ps1 index 190de6d2d..67f82414e 100644 --- a/src/variables/private/GitHub.ps1 +++ b/src/variables/private/GitHub.ps1 @@ -4,7 +4,6 @@ $script:IsLocal = -not ($script:IsGitHubActions -or $script:IsFunctionApp) $script:GitHub = [pscustomobject]@{ ContextVault = 'PSModule.GitHub' TokenPrefixPattern = '(?<=^(ghu|gho|ghs|github_pat|ghp)).*' - EnvironmentType = Get-GitHubEnvironmentType DefaultConfig = [GitHubConfig]@{ ID = 'Module' HostName = ($env:GITHUB_SERVER_URL ?? 'github.com') -replace '^https?://' @@ -24,1184 +23,4 @@ $script:GitHub = [pscustomobject]@{ Config = $null Event = $null Runner = $null - Permissions = @( - # ------------------------------ - # Repository Fine-Grained Permission Definitions - # ------------------------------ - [GitHubPermissionDefinition]::new( - 'actions', - 'Actions', - 'Workflows, workflow runs and artifacts.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-actions', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'administration', - 'Administration', - 'Repository creation, deletion, settings, teams, and collaborators.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-administration', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'attestations', - 'Attestations', - 'Create and retrieve attestations for a repository.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-attestations', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'checks', - 'Checks', - 'Checks on code.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-checks', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'security_events', - 'Code scanning alerts', - 'View and manage code scanning alerts.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-code-scanning-alerts', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'codespaces', - 'Codespaces', - 'Create, edit, delete and list Codespaces.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-codespaces', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'codespaces_lifecycle_admin', - 'Codespaces lifecycle admin', - 'Manage the lifecycle of Codespaces, including starting and stopping.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-codespaces-lifecycle-admin', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'codespaces_metadata', - 'Codespaces metadata', - 'Access Codespaces metadata including the devcontainers and machine type.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-codespaces-metadata', - @( - 'read' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'codespaces_secrets', - 'Codespaces secrets', - 'Restrict Codespaces user secrets modifications to specific repositories.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-codespaces-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'statuses', - 'Commit statuses', - 'Commit statuses.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-commit-statuses', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'contents', - 'Contents', - 'Repository contents, commits, branches, downloads, releases, and merges.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-contents', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repository_custom_properties', - 'Custom properties', - 'Read and write repository custom properties values at the repository level, when allowed by the property.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-custom-properties', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'vulnerability_alerts', - 'Dependabot alerts', - 'Retrieve Dependabot alerts.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-dependabot-alerts', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'dependabot_secrets', - 'Dependabot secrets', - 'Manage Dependabot repository secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-dependabot-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'deployments', - 'Deployments', - 'Deployments and deployment statuses.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-deployments', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'discussions', - 'Discussions', - 'Discussions and related comments and labels.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-discussions', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'environments', - 'Environments', - 'Manage repository environments.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-environments', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'issues', - 'Issues', - 'Issues and related comments, assignees, labels, and milestones.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-issues', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'merge_queues', - 'Merge queues', - "Manage a repository's merge queues", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-merge-queues', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( #Mandatory - 'metadata', - 'Metadata', - 'Search repositories, list collaborators, and access repository metadata.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-metadata', - @( - 'read' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'packages', - 'Packages', - 'Packages published to the GitHub Package Platform.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-packages', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'pages', - 'Pages', - 'Retrieve Pages statuses, configuration, and builds, as well as create new builds.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-pages', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repository_projects', - 'Projects', - 'Manage classic projects within a repository.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-projects', - @( - 'read', - 'write', - 'admin' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'pull_requests', - 'Pull requests', - 'Pull requests and related comments, assignees, labels, milestones, and merges.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-pull-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repository_advisories', - 'Repository security advisories', - 'View and manage repository security advisories.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-repository-security-advisories', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repo_secret_scanning_dismissal_requests', - 'Secret scanning alert dismissal requests', - 'View and manage secret scanning alert dismissal requests', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-secret-scanning-alert-dismissal-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'secret_scanning_alerts', - 'Secret scanning alerts', - 'View and manage secret scanning alerts.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-secret-scanning-alerts', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'secret_scanning_bypass_requests', - 'Secret scanning push protection bypass requests', - 'Review and manage repository secret scanning push protection bypass requests.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-secret-scanning-push-protection-bypass-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'secrets', - 'Secrets', - 'Manage Actions repository secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'single_file', - 'Single file', - 'Manage just a single file.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-single-file', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'actions_variables', - 'Variables', - 'Manage Actions repository variables.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-variables', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repository_hooks', - 'Webhooks', - 'Manage the post-receive hooks for a repository.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-webhooks', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'workflows', - 'Workflows', - 'Update GitHub Action workflow files.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-workflows', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - - # ------------------------------ - # Organization Fine-Grained Permission Definitions - # ------------------------------ - [GitHubPermissionDefinition]::new( - 'organization_api_insights', - 'API Insights', - 'View statistics on how the API is being used for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-api-insights', - @( - 'read' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_administration', - 'Administration', - 'Manage access to an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-administration', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_user_blocking', - 'Blocking users', - 'View and manage users blocked by the organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-blocking-users', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_campaigns', - 'Campaigns', - 'Manage campaigns.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-campaigns', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_custom_org_roles', - 'Custom organization roles', - 'Create, edit, delete and list custom organization roles. View system organization roles.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-custom-organization-roles', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_custom_properties', - 'Custom properties', - 'Read and write repository custom properties values and administer definitions at the organization level.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-custom-properties', - @( - 'read', - 'write', - 'admin' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_custom_roles', - 'Custom repository roles', - 'Create, edit, delete and list custom repository roles.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-custom-repository-roles', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_events', - 'Events', - 'View events triggered by an activity in an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-events', - @( - 'read' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_copilot_seat_management', - 'GitHub Copilot Business', - 'Manage Copilot Business seats and settings', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-github-copilot-business', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'issue_fields', - 'Issue Fields', - 'Manage issue fields for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-issue-fields', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'issue_types', - 'Issue Types', - 'Manage issue types for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-issue-types', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_knowledge_bases', - 'Knowledge bases', - 'View and manage knowledge bases for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-knowledge-bases', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'members', - 'Members', - 'Organization members and teams.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-members', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_models', - 'Models', - 'Manage model access for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-models', - @( - 'read' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_network_configurations', - 'Network configurations', - 'View and manage hosted compute network configurations available to an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-network-configurations', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_announcement_banners', - 'Organization announcement banners', - 'View and modify announcement banners for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-announcement-banners', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_secret_scanning_bypass_requests', - 'Organization bypass requests for secret scanning', - 'Review and manage secret scanning push protection bypass requests.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-bypass-requests-for-secret-scanning', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_codespaces', - 'Organization codespaces', - 'Manage Codespaces for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-codespaces', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_codespaces_secrets', - 'Organization codespaces secrets', - 'Manage Codespaces Secrets for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-codespaces-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_codespaces_settings', - 'Organization codespaces settings', - 'Manage Codespaces settings for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-codespaces-settings', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_dependabot_secrets', - 'Organization dependabot secrets', - 'Manage Dependabot organization secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-dependabot-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_code_scanning_dismissal_requests', - 'Organization dismissal requests for code scanning', - 'Review and manage code scanning alert dismissal requests.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-dismissal-requests-for-code-scanning', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_private_registries', - 'Organization private registries', - 'Manage private registries for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-private-registries', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_personal_access_token_requests', - 'Personal access token requests', - 'Manage personal access token requests from organization members.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-personal-access-token-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_personal_access_tokens', - 'Personal access tokens', - 'View and revoke personal access tokens that have been granted access to an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-personal-access-tokens', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_plan', - 'Plan', - "View an organization's plan.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-plan', - @( - 'read' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_projects', - 'Projects', - 'Manage projects for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-projects', - @( - 'read', - 'write', - 'admin' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'secret_scanning_dismissal_requests', - 'Secret scanning alert dismissal requests', - 'Review and manage secret scanning alert dismissal requests', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-secret-scanning-alert-dismissal-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_secrets', - 'Secrets', - 'Manage Actions organization secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_self_hosted_runners', - 'Self-hosted runners', - 'View and manage Actions self-hosted runners available to an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-self-hosted-runners', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'team_discussions', - 'Team discussions', - 'Manage team discussions and related comments.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-team-discussions', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_actions_variables', - 'Variables', - 'Manage Actions organization variables.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-variables', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_hooks', - 'Webhooks', - 'Manage the post-receive hooks for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-webhooks', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - - # ------------------------------ - # User (Account) Fine-Grained Permission Definitions - # ------------------------------ - [GitHubPermissionDefinition]::new( - 'blocking', - 'Block another user', - 'View and manage users blocked by the user.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-block-another-user', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'codespaces_user_secrets', - 'Codespaces user secrets', - 'Manage Codespaces user secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-codespaces-user-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'copilot_messages', - 'Copilot Chat', - 'This application will receive your GitHub ID, your GitHub Copilot Chat session messages ' + - '(not including messages sent to another application), and timestamps of provided GitHub Copilot ' + - 'Chat session messages. This permission must be enabled for Copilot Extensions.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-copilot-chat', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'copilot_editor_context', - 'Copilot Editor Context', - 'This application will receive bits of Editor Context (e.g. currently opened file) whenever you send it a message through Copilot Chat.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-copilot-editor-context', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'emails', - 'Email addresses', - "Manage a user's email addresses.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-email-addresses', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'user_events', - 'Events', - "View events triggered by a user's activity.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-events', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'followers', - 'Followers', - "A user's followers", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-followers', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'gpg_keys', - 'GPG keys', - "View and manage a user's GPG keys.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-gpg-keys', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'gists', - 'Gists', - "Create and modify a user's gists and comments.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-gists', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'keys', - 'Git SSH keys', - 'Git SSH keys', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-git-ssh-keys', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'interaction_limits', - 'Interaction limits', - 'Interaction limits on repositories', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-interaction-limits', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'knowledge_bases', - 'Knowledge bases', - 'View knowledge bases for a user.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-knowledge-bases', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'user_models', - 'Models', - 'Allows access to GitHub Models.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-models', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'plan', - 'Plan', - "View a user's plan.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-plan', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'profile', - 'Profile', - "Manage a user's profile settings.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-profile', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'git_signing_ssh_public_keys', - 'SSH signing keys', - "View and manage a user's SSH signing keys.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-ssh-signing-keys', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'starring', - 'Starring', - 'List and manage repositories a user is starring.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-starring', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'watching', - 'Watching', - 'List and change repositories a user is subscribed to.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-watching', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - - # ------------------------------ - # Enterprise Fine-Grained Permission Definitions - # ------------------------------ - [GitHubPermissionDefinition]::new( - 'enterprise_custom_properties', - 'Custom properties', - 'View repository custom properties and administer definitions at the enterprise level.', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-custom-properties', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_organization_installation_repositories', - 'Enterprise organization installation repositories', - 'Manage repository access of GitHub Apps on Enterprise-owned organizations', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-organization-installation-repositories', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_organization_installations', - 'Enterprise organization installations', - 'Manage installation of GitHub Apps on Enterprise-owned organizations', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-organization-installations', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_organizations', - 'Enterprise organizations', - 'Create and remove enterprise organizations', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-organizations', - @( - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_people', - 'Enterprise people', - 'Manage user access to the enterprise', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-people', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_sso', - 'Enterprise single sign-on', - 'View and manage enterprise single sign-on configuration', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-single-sign-on', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ) - ) } diff --git a/tests/Apps.Tests.ps1 b/tests/Apps.Tests.ps1 index 44803729b..fc36ba570 100644 --- a/tests/Apps.Tests.ps1 +++ b/tests/Apps.Tests.ps1 @@ -24,9 +24,13 @@ Describe 'Apps' { Context 'As using on ' -ForEach $authCases { BeforeAll { + $permissionsDefinitions = [GitHubPermissionDefinition]::List LogGroup 'Context' { $context = Connect-GitHubAccount @connectParams -PassThru -Silent - Write-Host ($context | Format-List | Out-String) + Write-Host "$($context | Format-List | Out-String)" + } + LogGroup 'Permissions' { + Write-Host "$($context.Permissions | Format-Table | Out-String)" } } @@ -35,198 +39,210 @@ Describe 'Apps' { Write-Host ('-' * 60) } - # Tests for APP goes here - if ($AuthType -eq 'APP') { - Context 'GitHub Apps' { - It 'Get-GitHubApp - Can get app details' { - $app = Get-GitHubApp - LogGroup 'App' { - Write-Host ($app | Format-List | Out-String) - } - $app | Should -Not -BeNullOrEmpty - $app | Should -BeOfType 'GitHubApp' - $app.ID | Should -Not -BeNullOrEmpty - $app.ClientID | Should -Not -BeNullOrEmpty - $app.Slug | Should -Not -BeNullOrEmpty - $app.NodeID | Should -Not -BeNullOrEmpty - $app.Owner | Should -BeOfType 'GitHubOwner' - $app.Name | Should -Not -BeNullOrEmpty - $app.Description | Should -Not -BeNullOrEmpty - $app.ExternalUrl | Should -Not -BeNullOrEmpty - $app.Url | Should -Not -BeNullOrEmpty - $app.CreatedAt | Should -Not -BeNullOrEmpty - $app.UpdatedAt | Should -Not -BeNullOrEmpty - $app.Permissions | Should -BeOfType 'PSCustomObject' - $app.Events | Should -BeOfType 'string' - $app.Installations | Should -Not -BeNullOrEmpty - } + It 'Get-GitHubApp - Get an app by slug' -Skip:($AuthType -eq 'APP') { + $app = Get-GitHubApp -Slug 'github-actions' + LogGroup 'App by slug' { + Write-Host ($app | Format-List | Out-String) + } + $app | Should -Not -BeNullOrEmpty + } - It 'Get-GitHubAppInstallationRequest - Can get installation requests' { - $installationRequests = Get-GitHubAppInstallationRequest - LogGroup 'Installation requests' { - Write-Host ($installationRequests | Format-List | Out-String) - } + Context 'GitHub Apps' -Skip:($AuthType -ne 'APP') { + It 'Get-GitHubApp - Can get app details' { + $app = Get-GitHubApp + LogGroup 'App' { + Write-Host ($app | Format-List | Out-String) } + $app | Should -Not -BeNullOrEmpty + $app | Should -BeOfType 'GitHubApp' + $app.ID | Should -Not -BeNullOrEmpty + $app.ClientID | Should -Not -BeNullOrEmpty + $app.Slug | Should -Not -BeNullOrEmpty + $app.NodeID | Should -Not -BeNullOrEmpty + $app.Owner | Should -BeOfType 'GitHubOwner' + $app.Name | Should -Not -BeNullOrEmpty + $app.Description | Should -Not -BeNullOrEmpty + $app.ExternalUrl | Should -Not -BeNullOrEmpty + $app.Url | Should -Not -BeNullOrEmpty + $app.CreatedAt | Should -Not -BeNullOrEmpty + $app.UpdatedAt | Should -Not -BeNullOrEmpty + $app.Permissions.Count | Should -BeGreaterThan 0 + $app.Permissions | Should -BeOfType 'GitHubPermission' + $app.Permissions.Name | Should -BeIn $permissionsDefinitions.Name + $app.Events | Should -BeOfType 'string' + $app.Installations | Should -Not -BeNullOrEmpty + } - It 'Get-GitHubAppInstallation - Can get app installations' { - $githubApp = Get-GitHubApp - $installations = Get-GitHubAppInstallation - LogGroup 'Installations' { - Write-Host ($installations | Format-List | Out-String) - } - $installations | Should -Not -BeNullOrEmpty - foreach ($installation in $installations) { - $installation | Should -BeOfType 'GitHubAppInstallation' - $installation.ID | Should -Not -BeNullOrEmpty - $installation.App | Should -BeOfType 'GitHubApp' - $installation.App.ClientID | Should -Be $githubApp.ClientID - $installation.App.AppID | Should -Not -BeNullOrEmpty - $installation.App.Slug | Should -Not -BeNullOrEmpty - $installation.Target | Should -BeOfType 'GitHubOwner' - $installation.Target | Should -Not -BeNullOrEmpty - $installation.Type | Should -BeIn @('Enterprise', 'Organization', 'User') - $installation.RepositorySelection | Should -Not -BeNullOrEmpty - $installation.Permissions | Should -BeOfType 'PSCustomObject' - $installation.Events | Should -BeOfType 'string' - $installation.CreatedAt | Should -Not -BeNullOrEmpty - $installation.UpdatedAt | Should -Not -BeNullOrEmpty - $installation.SuspendedAt | Should -BeNullOrEmpty - $installation.SuspendedBy | Should -BeOfType 'GitHubUser' - $installation.SuspendedBy | Should -BeNullOrEmpty - } + It 'Get-GitHubAppInstallationRequest - Can get installation requests' { + $installationRequests = Get-GitHubAppInstallationRequest + LogGroup 'Installation requests' { + Write-Host ($installationRequests | Format-List | Out-String) } + } - It 'Get-GitHubAppInstallation - ' { - $githubApp = Get-GitHubApp - $installation = Get-GitHubAppInstallation | Where-Object { ($_.Target.Name -eq $owner) -and ($_.Type -eq $ownerType) } - LogGroup "Installation - $ownerType" { - Write-Host ($installation | Format-List | Out-String) + It 'Get-GitHubAppInstallation - Can get app installations' { + $githubApp = Get-GitHubApp + $installations = Get-GitHubAppInstallation + $installations | Should -Not -BeNullOrEmpty + foreach ($installation in $installations) { + LogGroup "Installation - $($installation.Target.Name)" { + Write-Host "$($installation | Format-List | Out-String)" } - $installation | Should -Not -BeNullOrEmpty $installation | Should -BeOfType 'GitHubAppInstallation' $installation.ID | Should -Not -BeNullOrEmpty $installation.App | Should -BeOfType 'GitHubApp' $installation.App.ClientID | Should -Be $githubApp.ClientID - $installation.App.AppID | Should -Not -BeNullOrEmpty $installation.App.Slug | Should -Not -BeNullOrEmpty $installation.Target | Should -BeOfType 'GitHubOwner' - $installation.Target | Should -Be $owner - $installation.Type | Should -Be $ownerType + $installation.Target | Should -Not -BeNullOrEmpty + $installation.Type | Should -BeIn @('Enterprise', 'Organization', 'User') $installation.RepositorySelection | Should -Not -BeNullOrEmpty - $installation.Permissions | Should -BeOfType 'PSCustomObject' + $installation.Permissions.Count | Should -BeGreaterThan 0 + $installation.Permissions | Should -BeOfType [GitHubPermission] + $installation.Permissions.Name | Should -BeIn $permissionsDefinitions.Name $installation.Events | Should -BeOfType 'string' $installation.CreatedAt | Should -Not -BeNullOrEmpty $installation.UpdatedAt | Should -Not -BeNullOrEmpty $installation.SuspendedAt | Should -BeNullOrEmpty $installation.SuspendedBy | Should -BeOfType 'GitHubUser' $installation.SuspendedBy | Should -BeNullOrEmpty + $installation.Status | Should -Not -BeNullOrEmpty + $installation.Status | Should -BeIn @('Ok', 'Outdated') } } - Context 'Webhooks' { - It 'Get-GitHubAppWebhookConfiguration - Can get the webhook configuration' { - $webhookConfig = Get-GitHubAppWebhookConfiguration - LogGroup 'Webhook config' { - Write-Host ($webhookConfig | Format-Table | Out-String) - } - $webhookConfig | Should -Not -BeNullOrEmpty + It 'Get-GitHubAppInstallation - ' { + $githubApp = Get-GitHubApp + $installation = Get-GitHubAppInstallation | Where-Object { ($_.Target.Name -eq $owner) -and ($_.Type -eq $ownerType) } + LogGroup "Installation - $ownerType" { + Write-Host ($installation | Format-List | Out-String) } + $installation | Should -Not -BeNullOrEmpty + $installation | Should -BeOfType 'GitHubAppInstallation' + $installation.ID | Should -Not -BeNullOrEmpty + $installation.App | Should -BeOfType 'GitHubApp' + $installation.App.ClientID | Should -Be $githubApp.ClientID + $installation.App.Slug | Should -Not -BeNullOrEmpty + $installation.Target | Should -BeOfType 'GitHubOwner' + $installation.Target | Should -Be $owner + $installation.Type | Should -Be $ownerType + $installation.RepositorySelection | Should -Not -BeNullOrEmpty + $installation.Permissions.Count | Should -BeGreaterThan 0 + $installation.Permissions | Should -BeOfType [GitHubPermission] + $installation.Permissions.Name | Should -BeIn $permissionsDefinitions.Name + $installation.Events | Should -BeOfType 'string' + $installation.CreatedAt | Should -Not -BeNullOrEmpty + $installation.UpdatedAt | Should -Not -BeNullOrEmpty + $installation.SuspendedAt | Should -BeNullOrEmpty + $installation.SuspendedBy | Should -BeOfType 'GitHubUser' + $installation.SuspendedBy | Should -BeNullOrEmpty + $installation.Status | Should -Not -BeNullOrEmpty + $installation.Status | Should -BeIn @('Ok', 'Outdated') + } + } - It 'Update-GitHubAppWebhookConfiguration - Can update the webhook configuration' { - { Update-GitHubAppWebhookConfiguration -ContentType 'form' } | Should -Not -Throw - $webhookConfig = Get-GitHubAppWebhookConfiguration - LogGroup 'Webhook config - form' { - Write-Host ($webhookConfig | Format-Table | Out-String) - } - { Update-GitHubAppWebhookConfiguration -ContentType 'json' } | Should -Not -Throw - $webhookConfig = Get-GitHubAppWebhookConfiguration - LogGroup 'Webhook config - json' { - Write-Host ($webhookConfig | Format-Table | Out-String) - } + Context 'Webhooks' -Skip:($AuthType -ne 'APP') { + It 'Get-GitHubAppWebhookConfiguration - Can get the webhook configuration' { + $webhookConfig = Get-GitHubAppWebhookConfiguration + LogGroup 'Webhook config' { + Write-Host ($webhookConfig | Format-Table | Out-String) } + $webhookConfig | Should -Not -BeNullOrEmpty + } - It 'Get-GitHubAppWebhookDelivery - Can get webhook deliveries' { - $deliveries = Get-GitHubAppWebhookDelivery - LogGroup 'Deliveries' { - Write-Host ($deliveries | Format-Table | Out-String) - } - $deliveries | Should -Not -BeNullOrEmpty + It 'Update-GitHubAppWebhookConfiguration - Can update the webhook configuration' { + { Update-GitHubAppWebhookConfiguration -ContentType 'form' } | Should -Not -Throw + $webhookConfig = Get-GitHubAppWebhookConfiguration + LogGroup 'Webhook config - form' { + Write-Host ($webhookConfig | Format-Table | Out-String) } - - It 'Get-GitHubAppWebhookDelivery - Can redeliver a webhook delivery' { - $deliveries = Get-GitHubAppWebhookDelivery | Select-Object -First 1 - LogGroup 'Delivery - redeliver' { - Write-Host ($deliveries | Format-Table | Out-String) - } - { Invoke-GitHubAppWebhookReDelivery -ID $deliveries.id } | Should -Not -Throw - LogGroup 'Delivery - redeliver' { - Write-Host ($deliveries | Format-Table | Out-String) - } + { Update-GitHubAppWebhookConfiguration -ContentType 'json' } | Should -Not -Throw + $webhookConfig = Get-GitHubAppWebhookConfiguration + LogGroup 'Webhook config - json' { + Write-Host ($webhookConfig | Format-Table | Out-String) } } - Context 'Installation' { - BeforeAll { - $githubApp = Get-GitHubApp - $config = Get-GitHubConfig - $context = Connect-GitHubApp @connectAppParams -PassThru -Silent - LogGroup 'Context' { - Write-Host ($context | Format-List | Out-String) - } + It 'Get-GitHubAppWebhookDelivery - Can get webhook deliveries' { + $deliveries = Get-GitHubAppWebhookDelivery + LogGroup 'Deliveries' { + Write-Host ($deliveries | Format-Table | Out-String) } + $deliveries | Should -Not -BeNullOrEmpty + } - It 'Connect-GitHubApp - Connects as a GitHub App to ' { - $context | Should -BeOfType 'GitHubAppInstallationContext' - $context.ClientID | Should -Be $githubApp.ClientID - $context.TokenExpiresAt | Should -BeOfType [datetime] - $context.InstallationID | Should -BeOfType [uint64] - $context.InstallationID | Should -BeGreaterThan 0 - $context.Permissions | Should -BeOfType [PSCustomObject] - $context.Events | Should -BeOfType 'string' - $context.InstallationType | Should -Be $ownertype - $context.InstallationName | Should -Be $owner - $context.ID | Should -Be "$($config.HostName)/$($githubApp.Slug)/$ownertype/$owner" - $context.Name | Should -Be "$($config.HostName)/$($githubApp.Slug)/$ownertype/$owner" - $context.DisplayName | Should -Be $githubApp.Name - $context.Type | Should -Be 'Installation' - $context.HostName | Should -Be $config.HostName - $context.ApiBaseUri | Should -Be $config.ApiBaseUri - $context.ApiVersion | Should -Be $config.ApiVersion - $context.AuthType | Should -Be 'IAT' - $context.NodeID | Should -Not -BeNullOrEmpty - $context.DatabaseID | Should -Not -BeNullOrEmpty - $context.UserName | Should -Be $githubApp.Slug - $context.Token | Should -BeOfType [System.Security.SecureString] - $context.TokenType | Should -Be 'ghs' - $context.HttpVersion | Should -Be $config.HttpVersion - $context.PerPage | Should -Be $config.PerPage + It 'Get-GitHubAppWebhookDelivery - Can redeliver a webhook delivery' { + $deliveries = Get-GitHubAppWebhookDelivery | Select-Object -First 1 + LogGroup 'Delivery - redeliver' { + Write-Host ($deliveries | Format-Table | Out-String) } + { Invoke-GitHubAppWebhookReDelivery -ID $deliveries.id } | Should -Not -Throw + LogGroup 'Delivery - redeliver' { + Write-Host ($deliveries | Format-Table | Out-String) + } + } + } - It 'Connect-GitHubApp - TokenExpiresIn property should be calculated correctly' { - $context.TokenExpiresIn | Should -BeOfType [TimeSpan] - $context.TokenExpiresIn.TotalMinutes | Should -BeGreaterThan 0 - $context.TokenExpiresIn.TotalMinutes | Should -BeLessOrEqual 60 + Context 'Installation' -Skip:($AuthType -ne 'APP') { + BeforeAll { + $githubApp = Get-GitHubApp + $config = Get-GitHubConfig + $permissionsDefinitions = [GitHubPermissionDefinition]::List + $context = Connect-GitHubApp @connectAppParams -PassThru -Silent + LogGroup 'Context' { + Write-Host "$($context | Format-List | Out-String)" } + LogGroup 'Permissions' { + Write-Host "$($context.Permissions | Format-Table | Out-String)" + } + } - It 'Revoked GitHub App token should fail on API call' -Skip:($TokenType -eq 'GITHUB_TOKEN') { - $org = Get-GitHubOrganization -Name PSModule -Context $context - $org | Should -Not -BeNullOrEmpty - $context | Disconnect-GitHub + It 'Connect-GitHubApp - Connects as a GitHub App to ' { + $context | Should -BeOfType 'GitHubAppInstallationContext' + $context.ClientID | Should -Be $githubApp.ClientID + $context.TokenExpiresAt | Should -BeOfType [datetime] + $context.InstallationID | Should -BeOfType [uint64] + $context.InstallationID | Should -BeGreaterThan 0 + $context.Events | Should -BeOfType 'string' + $context.InstallationType | Should -Be $ownertype + $context.InstallationName | Should -Be $owner + $context.ID | Should -Be "$($config.HostName)/$($githubApp.Slug)/$ownertype/$owner" + $context.Name | Should -Be "$($config.HostName)/$($githubApp.Slug)/$ownertype/$owner" + $context.DisplayName | Should -Be $githubApp.Name + $context.Type | Should -Be 'Installation' + $context.HostName | Should -Be $config.HostName + $context.ApiBaseUri | Should -Be $config.ApiBaseUri + $context.ApiVersion | Should -Be $config.ApiVersion + $context.AuthType | Should -Be 'IAT' + $context.NodeID | Should -Not -BeNullOrEmpty + $context.DatabaseID | Should -Not -BeNullOrEmpty + $context.UserName | Should -Be $githubApp.Slug + $context.Token | Should -BeOfType [System.Security.SecureString] + $context.TokenType | Should -Be 'ghs' + $context.HttpVersion | Should -Be $config.HttpVersion + $context.PerPage | Should -Be $config.PerPage + $context.Permissions.Count | Should -BeGreaterThan 0 + $context.Permissions | Should -BeOfType [GitHubPermission] + $context.Permissions.Name | Should -BeIn $permissionsDefinitions.Name + $context.Events | Should -BeOfType 'string' - { - Invoke-RestMethod -Method Get -Uri "$($context.ApiBaseUri)/orgs/PSModule" -Authentication Bearer -Token $context.token - } | Should -Throw - } } - } - # Tests for IAT UAT and PAT goes here - It 'Get-GitHubApp - Get an app by slug' -Skip:($AuthType -eq 'APP') { - $app = Get-GitHubApp -Slug 'github-actions' - LogGroup 'App by slug' { - Write-Host ($app | Format-List | Out-String) + It 'Connect-GitHubApp - TokenExpiresIn property should be calculated correctly' { + $context.TokenExpiresIn | Should -BeOfType [TimeSpan] + $context.TokenExpiresIn.TotalMinutes | Should -BeGreaterThan 0 + $context.TokenExpiresIn.TotalMinutes | Should -BeLessOrEqual 60 + } + + It 'Revoked GitHub App token should fail on API call' -Skip:($TokenType -eq 'GITHUB_TOKEN') { + $org = Get-GitHubOrganization -Name PSModule -Context $context + $org | Should -Not -BeNullOrEmpty + $context | Disconnect-GitHub + + { + Invoke-RestMethod -Method Get -Uri "$($context.ApiBaseUri)/orgs/PSModule" -Authentication Bearer -Token $context.token + } | Should -Throw } - $app | Should -Not -BeNullOrEmpty } } } diff --git a/tests/GitHub.Tests.ps1 b/tests/GitHub.Tests.ps1 index 53b9e347e..9a7c69a17 100644 --- a/tests/GitHub.Tests.ps1 +++ b/tests/GitHub.Tests.ps1 @@ -124,6 +124,7 @@ Describe 'Auth' { It 'Connect-GitHubAccount - Connects to GitHub CLI on runners' { [string]::IsNullOrEmpty($(gh auth token)) | Should -Be $false } + It 'Get-GitHubViewer - Gets the logged in context' { $viewer = Get-GitHubViewer LogGroup 'Viewer' { diff --git a/tests/Permissions.Tests.ps1 b/tests/Permissions.Tests.ps1 index 043358796..99662a3d3 100644 --- a/tests/Permissions.Tests.ps1 +++ b/tests/Permissions.Tests.ps1 @@ -37,53 +37,30 @@ Describe 'Permissions' { Context 'For Apps' -Skip:($AuthType -ne 'APP') { BeforeAll { + $permissionsDefinitions = [GitHubPermissionDefinition]::List $installationContext = Connect-GitHubApp @connectAppParams -PassThru -Default -Silent LogGroup 'Context - Installation' { - Write-Host ($installationContext | Format-List | Out-String) + Write-Host "$($installationContext | Format-List | Out-String)" } - } - - It 'App context should have Permissions property populated' -Skip:($AuthType -ne 'APP') { - $installationContext.Permissions | Should -Not -BeNullOrEmpty - $installationContext.Permissions | Should -BeOfType [pscustomobject] - } - - It 'All app installation permissions should exist in permission catalog and be valid options' -Skip:($AuthType -ne 'APP') { - $catalog = Get-GitHubPermissionDefinition - $catalogNames = $catalog.Name - - # Flatten context permission hashtable/object into name/value pairs (value is access level like read/write/admin) - $granted = @() - $installationContext.Permissions.PSObject.Properties | ForEach-Object { - if ($_.Name -eq 'metadata') { return } # metadata is mandatory; still in catalog but just proceed normally - $granted += [pscustomobject]@{ Name = $_.Name; Level = [string]$_.Value } - } - - # Unknown permissions (present in context but not in catalog) - $unknown = $granted | Where-Object { $_.Name -notin $catalogNames } - if ($unknown) { - throw "Unknown permission(s) detected in app installation: $($unknown.Name -join ', ')" + LogGroup 'Permissions' { + Write-Host "$($installationContext.Permissions | Format-Table | Out-String)" } + } - # For each granted permission ensure level is one of the catalog options - foreach ($g in $granted) { - $def = $catalog | Where-Object Name -EQ $g.Name - $def | Should -Not -BeNullOrEmpty - $def.Options | Should -Contain $g.Level - } + It 'App context should have Permissions property populated' { + $installationContext.Permissions.Count | Should -BeGreaterThan 0 + $installationContext.Permissions | Should -BeOfType [GitHubPermission] + $installationContext.Permissions.Name | Should -BeIn $permissionsDefinitions.Name } - It 'Permission catalog should contain all permissions granted to the app installation' -Skip:($AuthType -ne 'APP') { - $catalog = Get-GitHubPermissionDefinition + It 'Permission catalog should contain all permissions granted to the app installation' { $missing = @() - $installationContext.Permissions.PSObject.Properties | ForEach-Object { - if ($_.Name -notin $catalog.Name) { + $installationContext.Permissions | ForEach-Object { + if ($_.Name -notin $permissionsDefinitions.Name) { $missing += $_.Name } } - if ($missing.Count -gt 0) { - throw "Missing permission definitions for: $($missing -join ', ')" - } + $missing.Count | Should -Be 0 -Because "The following permissions are missing from the catalog: $($missing -join ', ')" } } } @@ -93,7 +70,7 @@ Describe 'Permissions' { $result = Get-GitHubPermissionDefinition $result | Should -Not -BeNullOrEmpty $result | Should -BeOfType [GitHubPermissionDefinition] - ($result | Measure-Object).Count | Should -BeGreaterThan 0 + $result.Count | Should -BeGreaterThan 0 } It 'Should return only Fine-grained permissions when filtered by Type' {