graphqlgithub-actionsgithub-apigithub-graphqlgithub-organizations

Check if user is member of a github team using GraphQL


I'm trying to write a GitHub workflow that uses a github-script in which a GraphQL query determines if a user is part of an organization's team.

Using the GitHub GraphQL Explorer I've created this example query:

query check_if_user_is_team_member($user_login: String!, $org: String!, $team_slug: String!) {
  organization(login: $org) {
    team(slug: $team_slug) {
      members(query: $user_login) {
        totalCount
      }
    }
  }
}

When I execute this in GraphQL Explorer with these variables:

{
  "user_login": "kwk",
  "org": "fedora-llvm-team",
  "team_slug": "llvm-toolset-engineers"
}

then I get this result:

{
  "data": {
    "organization": {
      "team": {
        "members": {
          "totalCount": 1
        }
      }
    }
  }
}

In theory one only needs to check the totalCount for 1 or 0, right?

In my head I thought that this must be easily portable to a github workflow like this:

name: "Example"

on:
  issue_comment:
    types: created

jobs:
  check-team-membership:
    runs-on: ubuntu-latest
    steps:
      - name: Check if commenter is member of the required team.
        uses: actions/github-script@v7
        with:
          script: |
            const query = `query check_if_user_is_team_member($user_login: String!, $org: String!, $team_slug: String!) {
              organization(login: $org) {
                team(slug: $team_slug) {
                  members(query: $user_login) {
                    totalCount
                  }
                }
              }
            }`;
            const variables = {
              "user_login": "${{github.event.comment.user.login}}",
              "org": "${{github.repository_owner}}",
              // NOTE: The team must be visible, or this won't work!
              "team_slug": "llvm-toolset-engineers"
            }
            const result = await github.graphql(query, variables)
            console.log(result)

            if(result['organization']['team']['members']['totalCount'] != 1) {
              core.setFailed("User is not allowed to use the /retest command");
            }

I see this output which looks like the concept is not working as expected:

{ organization: { team: null } }

The variables are also correctly replaced:

  const variables = {
    "user_login": "kwk",
    "org": "fedora-llvm-team",
    "team_slug": "llvm-toolset-engineers"
  }

The error that I see when the workflow runs is this one:

 Error: Unhandled error: TypeError: Cannot read properties of null (reading 'members')

I can only guess but do I have to use a special token other than the GITHUB_TOKEN that is always available in a workflow run?

EDIT:

I've tried running the query from my local computer using gh api graphql and it didn't work as well but this time the error message was much more verbose:

$ gh api graphql -f query='query {
  organization(login: "fedora-llvm-team") {
    team(slug: "llvm-toolset-engineers") {
      members(query: "kwk") {
        totalCount
      }
    }
  }
}'
{
  "data": {
    "organization": {
      "team": null
    }
  },
  "errors": [
    {
      "type": "FORBIDDEN",
      "path": [
        "organization",
        "team"
      ],
      "extensions": {
        "saml_failure": false
      },
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ],
      "message": "Resource not accessible by personal access token"
    }
  ]
}
gh: Resource not accessible by personal access token

After that I've created a fined-grained GitHub access token with just these permissions:

permission of token

When I set GH_TOKEN or GITHUB_TOKEN to this token on my local computer I can successfully query the members:

gh api graphql -f query='query {
  organization(login: "fedora-llvm-team") {
    team(slug: "llvm-toolset-engineers") {
      members(query: "kwk") {
        totalCount
      }
    }
  }
}'
{
  "data": {
    "organization": {
      "team": {
        "members": {
          "totalCount": 1
        }
      }
    }
  }
}

Solution

  • It looks like I was on the right track after all. The wrong permissions where causing the workflow to fail.

    As it turns out, the github-script documentation has a section on a parameter called github-token which needs to be passed. Remember, secrets are not inherited by GitHub Actions but you have to pass them manually through input variables.

    Here's the necessary change I had to make to my workflow file:

    --- .github/workflows/old.yml   2024-04-22 15:58:42.430341249 +0200
    +++ .github/workflows/new.yml   2024-04-22 15:59:33.513273930 +0200
    @@ -11,6 +11,7 @@
           - name: Check if commenter is member of the required team.
             uses: actions/github-script@v7
             with:
    +          github-token: ${{ secrets.MY_TOKEN_WITH_READ_ACCESS_TO_MEMBERS_PERMISSION }}
               script: |
                 const query = `query check_if_user_is_team_member($user_login: String!, $org: String!, $team_slug: String!) {
                   organization(login: $org) {
    

    In essence, I needed to create a personal access token with permission to read members of the organization:

    create PAT

    Then I copied that token and added it as a Repository Secret named MY_TOKEN_WITH_READ_ACCESS_TO_MEMBERS_PERMISSION to my repository. Here's an example link to do just that: https://github.com/MYORG/MYREPO/settings/secrets/actions/new.

    I hope this helps.