Check permissions in a GitHub Actions workflow
When working with GitHub Actions, you may want to check what relationship the person performing an action has to a repo before running a workflow. Public documentation on collaborators is scarce, so here’s what I’ve been able to work out so far.
- There are two ways to check relationships:
pull_request.author_association
and the/repos/:org/:repo/collaborators/:user/permission
endpoint. pull_request.author_association
returns an item from CommentAuthorAssociation, which is one of the following:COLLABORATOR
: Author has been invited to collaborate on the repository.CONTRIBUTOR
: Author has previously committed to the repository.FIRST_TIMER
: Author has not previously committed to GitHub.FIRST_TIME_CONTRIBUTOR
: Author has not previously committed to the repository.MANNEQUIN
: Author is a placeholder for an unclaimed user.MEMBER
: Author is a member of the organization that owns the repository.NONE
: Author has no association with the repository.OWNER
: Author is the owner of the repository.
- The
/repos/:org/:repo/collaborators/:user/permission
endpoint returns the permissions that the provided user has on a repository. This is one of:read
,write
,admin
Author Association
The author association field is available in the default pull_request
payload, which makes it useful if you don’t want to make an additional API call to figure out if an actor should be allowed to perform an action or not.
When interacting with an organization's repo, the pull_request.author_association
value is different depending on if the actor is an external collaborator, an org member, or neither.
If the actor is neither an org member nor an external collaborator their author_association
will be one of the following:
FIRST_TIMER
: The actor has never contributed to any repository on GitHub beforeFIRST_TIME_CONTRIBUTOR
: The actor has never contributed to this repository beforeCONTRIBUTOR
: The actor has previously contributed to the repository
If the actor has been added as an external collaborator, their author_association
will be COLLABORATOR
, no matter which set of permissions they’ve been given. If they have read
permissions, they’re a COLLABORATOR
. If they have admin
permissions, they’re still a COLLABORATOR
.
Finally, if the actor is a member of the organization that owns the repository their author_association
will be MEMBER
. This is true whether they are an organization member or owner. In addition, their author_association
will always be MEMBER
regardless of the permissions they have on the repository. If they have read
permissions, they’re a MEMBER
. If they have admin
permissions, they’re still a MEMBER
.
The only other state that you might be interested in is OWNER
. This state is not possible with organization owned repositories. It is only returned when a repository is owned by a specific user.
That leaves us with 2 unused states:
MANNEQUIN
- Mannequins are created when source code and metadata are migrated from one source control platform to GitHub. If the user was not properly mapped to a GitHub account at the time of migration a placeholder mannequin is created. If you’re interested in seeing amannequin
, check out this issue.NONE
- I’m not sure how to trigger this as any actor is implicitly aFIRST_TIMER
,FIRST_TIME_CONTRIBUTOR
orCONTRIBUTOR
Finally, let’s talk about deleted users. Any deleted user actions are attributed to ghost
, which is a GitHub owned account that “takes the place of user accounts that have been deleted 👻”
User Permissions
If you’re looking for more granularity than the data provided in author_association
you can use the /repos/:org/:repo/collaborators/:user/permission
endpoint to fetch which permission the actor has for this repo.
Note: You’ll need
push
permission on the repository to be able to use the endpoint above.
The possible values returned by this repository are as follows:
none
-:repo
is a private repository and the user has no permissionsread
- The user hasread
ortriage
permissions, or:repo
is a public repository and the user has no permissionswrite
- The user haswrite
ormaintain
permissionsadmin
- The user hasadmin
permission
The permission level provided is the same no matter how the permission is granted. The actor may be added as an external collaborator or to an organization team. As long as they have permission to the repository somehow, it will be returned via the endpoint above.
If you’d like to check a user’s permission in a workflow before performing a step, I recommend the Has Permission action.
Here’s an example from their README (with a small change to use github.token
rather than a secret):
yaml
name: Action Sample Workflow# Run workflow when a new pull request is openedon: [pull_request]jobs:check_user_permission:runs-on: ubuntu-latestname: A job to check user's permission levelsteps:# Check for write permission- name: Check user permissionid: checkuses: scherermichael-oss/action-has-permission@masterwith:required-permission: writeenv:GITHUB_TOKEN: $# Use the output from the `check` step- name: Run only if user has sufficient permissionsif: steps.check.outputs.has-permissionrun: echo "Congratulations! Your permissions to access the repository are sufficient."- name: Run only if user has NOT sufficient permissionsif: "! steps.check.outputs.has-permission"run: echo "Sorry! Your permissions are insufficient."
When to use each option
So, which should we use? It depends what you want to check.
pull_request.author_association
should be used when you want to check the relationship between the person performing an action and the repository itself. It’s useful when you want to auto-approve workflows for people that have contributed in the past (CONTRIBUTOR
) or for everyone in your organization (MEMBER
).
yaml
steps:- name: Check accessif: ${{ github.event.pull_request.author_association != 'CONTRIBUTOR' && github.event.pull_request.author_association != 'MEMBER' }}run: |echo "Event not triggered by a collaborator."exit 1
If you need to check for specific permissions, the /repos/:org/:repo/collaborators/:user/permission
endpoint is the option to choose. This is useful when you work for an organization where only specific teams have write
access to a repository and should be allowed to perform specific tasks.
If you’re not sure which to use, I’d recommend using the permission endpoint (via scherermichael-oss/action-has-permission
) as it allows for more granular access controls than pull_request.author_association
.