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_associationand the/repos/:org/:repo/collaborators/:user/permissionendpoint. pull_request.author_associationreturns 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/permissionendpoint 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_CONTRIBUTORorCONTRIBUTOR
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
pushpermission on the repository to be able to use the endpoint above.
The possible values returned by this repository are as follows:
none-:repois a private repository and the user has no permissionsread- The user hasreadortriagepermissions, or:repois a public repository and the user has no permissionswrite- The user haswriteormaintainpermissionsadmin- The user hasadminpermission
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):
yamlname: 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).
yamlsteps:- 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.