{"id":23762275,"url":"https://github.com/appvia/githubusermanager","last_synced_at":"2025-09-05T06:31:50.939Z","repository":{"id":37054948,"uuid":"350533583","full_name":"appvia/githubUserManager","owner":"appvia","description":"Maintain github users from Google Workspace membership","archived":false,"fork":false,"pushed_at":"2025-06-22T15:37:15.000Z","size":1316,"stargazers_count":17,"open_issues_count":16,"forks_count":6,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-06-22T16:35:23.177Z","etag":null,"topics":["docker","github-management","google-workspace","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/appvia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-03-23T00:46:11.000Z","updated_at":"2025-05-09T04:52:51.000Z","dependencies_parsed_at":"2023-10-20T21:05:12.299Z","dependency_job_id":"0a80fb16-a719-4237-954a-23d2cd0e35dc","html_url":"https://github.com/appvia/githubUserManager","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/appvia/githubUserManager","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appvia%2FgithubUserManager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appvia%2FgithubUserManager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appvia%2FgithubUserManager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appvia%2FgithubUserManager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/appvia","download_url":"https://codeload.github.com/appvia/githubUserManager/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appvia%2FgithubUserManager/sbom","scorecard":{"id":204232,"data":{"date":"2025-08-11","repo":{"name":"github.com/appvia/githubUserManager","commit":"08ab2eded5215f0ba01cf10eb7549c57194e5f0e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":9,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Warn: One or no descriptive hints of disclosure, vulnerability, and/or timelines in security policy","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Pinned-Dependencies","score":6,"reason":"dependency not pinned by hash detected -- score normalized to 6","details":["Warn: npmCommand not pinned by hash: Dockerfile:5","Warn: npmCommand not pinned by hash: .github/workflows/ci.yml:29","Info:   6 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   3 out of   3 third-party GitHubAction dependencies pinned","Info:   1 out of   1 containerImage dependencies pinned","Info:   0 out of   2 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":5,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Warn: 'stale review dismissal' is disabled on branch 'main'","Warn: required approving review count is 1 on branch 'main'","Warn: codeowners review is not required on branch 'main'","Warn: 'last push approval' is disabled on branch 'main'","Warn: no status checks found to merge onto branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/ci.yml:13"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"14 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-h5c3-5r3r-rr8q","Warn: Project is vulnerable to: GHSA-rmvr-2pp2-xj38","Warn: Project is vulnerable to: GHSA-xx4v-prfh-6cgc","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T23:21:25.779Z","repository_id":37054948,"created_at":"2025-08-16T23:21:25.779Z","updated_at":"2025-08-16T23:21:25.779Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273722729,"owners_count":25156300,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-05T02:00:09.113Z","response_time":402,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","github-management","google-workspace","typescript"],"created_at":"2024-12-31T21:17:34.634Z","updated_at":"2025-09-05T06:31:50.363Z","avatar_url":"https://github.com/appvia.png","language":"TypeScript","readme":"# Manage github organisation membership with Google Workspace user accounts\n\n[![Known Vulnerabilities](https://snyk.io/test/github/appvia/githubUserManager/badge.svg)](https://snyk.io/test/github/appvia/githubUserManager)\n[![GitHub license](https://img.shields.io/github/license/appvia/githubUserManager)](https://github.com/appvia/githubUserManager/blob/main/LICENSE)\n[![GitHub stars](https://img.shields.io/github/stars/appvia/githubusermanager)](https://github.com/appvia/githubusermanager/stargazers)\n[![GitHub forks](https://img.shields.io/github/forks/appvia/githubusermanager)](https://github.com/appvia/githubusermanager/network)\n[![GitHub issues](https://img.shields.io/github/issues/appvia/githubusermanager)](https://github.com/appvia/githubusermanager/issues)\n[![ci](https://github.com/appvia/githubUserManager/actions/workflows/ci.yml/badge.svg)](https://github.com/appvia/githubUserManager/actions/workflows/ci.yml)\n\nManages who is in your GitHub organization based on a custom property in their Google Workspace profile, allowing for seamless JML (Joiner mover leaver) process, if allowed removing/disabling an account in the Google Workspace will remove the user from the GitHub; similarly adding a user also works the same way. If you don't want to run it in destructive mode it can be configured to exit with a non-zero exit code so that you know to go and manually make the changes.\n\nRight now this only handles the organization membership, it **does not** touch team membership, or level of membership; the main focus is to draw alert when the configuration isn't as expected, these features could be added in future.\n\n## Deployment\n\n### Collect the secrets\n\n1.  [Add a custom attribute on the users](https://support.google.com/a/answer/6208725?hl=en#zippy=%2Cadd-a-new-custom-attribute)\n\n    1. Go to https://admin.google.com/ac/customschema\n    1. Enter\n       - Category: `Accounts`\n       - Description: `Accounts held elsewhere to link in`\n       - Custom fields:\n         - name: `github`\n         - info type: `text`\n         - visibility: `Visible to the organisation`\n         - no. of values: `multi-value`\n\n1.  [Add values to custom attributes for the users](https://support.google.com/a/answer/6208725?hl=en#add_value)\n\n    1.  Go to https://admin.google.com/ac/users\n    1.  Click a user to edit them\n    1.  Click 'user information'\n    1.  Under Accounts, click `github`\n    1.  Add all the github accounts for that user\n    1.  Click Save\n\n1.  [Make a gcp project](https://console.cloud.google.com/projectcreate)\n\n    1. Enable the [Admin SDK API](https://console.cloud.google.com/apis/library/admin.googleapis.com?q=workspace%20admin\u0026id=d0a160dd-c410-4fd0-a951-c47e05309cb9)\n    1. [Create credentials](https://console.cloud.google.com/apis/credentials/wizard?project=githubusermanager)\n\n    - Which API are you using?: `Admin SDK API`\n    - Are you planning to use this API with App Engine or Compute Engine: `no`\n    - Service account name: `githubusermanager`\n    - Role: `[none]`\n    - Key type: `JSON`\n    - Click `Continue`, then confirm `CREATE WITHOUT ROLE`\n    - Edit the user, Click `Enable G Suite domain-wide delegation`\n    - Product name for the consent screen: `githubusermanager`\n\n    1. [Delegate domain-wide authority to your service account](https://developers.google.com/admin-sdk/directory/v1/guides/delegation)\n\n    - https://admin.google.com/ac/owl/domainwidedelegation\n    - Client ID: `client id from user`\n    - OAuth scopes:\n      - `https://www.googleapis.com/auth/admin.directory.user.readonly`\n\n1.  Register new GitHub App\n    1. https://github.com/settings/organizations\n    - Click `Settings` on your organization\n    - Click `Developer settings`\n    - Click `GitHub Apps`\n    - Click `New GitHub App`\n    1. Enter:\n    - GitHub App name: `Google workspace github users`\n    - Homepage URL: github.com\n    - Webhook\n      - Active `uncheck`\n    - Organization permissions\n      - Members: `Read-only`, or `Read-Write` if you want it to\n    - Where can this GitHub App be installed? `Only on this account`\n    1. Click `Generate a private key` (should download a .pem)\n    1. Click `Install App`\n    1. Click `Install`\n    1. Click `Install`\n    - take node of the url, it'll look something like: `github.com/organizations/myorg/settings/installations/15627551`, the installationId is the last number `15627551`\n\n### Run\n\n#### Github Action:\n\n```yaml\n# ./.github/workflows/org-membership.yml\nname: Github Org Membership\n\non:\n  schedule:\n    - cron: '*/5 * * * *'\njobs:\n  run:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Github Org Membership Manager\n        uses: appvia/githubUserManager@v1.0.5\n        with:\n          google-email-address: hello@example.com\n          google-credentials: ${{ secrets.GOOGLE_CREDENTIALS }}\n          add-users: 'false'\n          remove-users: 'false'\n          exit-code-on-missmatch: '1'\n          github-org: 'myorg'\n          github-app-id: 1234\n          github-installation-id: 12345\n          github-private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}\n          ignored-users: user1,user2\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          github-actor: ${{ github.actor }}\n```\n\nAdd a dependabot configuration to always get updates!\n\n```yaml\n# ./.github/dependabot.yml\nversion: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: daily\n```\n\n#### Docker\n\n1. Make an [env file](https://www.digitalocean.com/community/tutorials/how-to-read-and-set-environmental-and-shell-variables-on-linux) with the [below table](#Setup-environment-variables)\n1. `docker run --env-file .env docker.pkg.github.com/appvia/githubusermanager/githubusermanager:main`\n\n#### node/lambda/cloud run/ something else\n\n1.  Clone this repo\n1.  `npm install --production`\n1.  `npm start` (with the with the [below environment variables table](#Setup-environment-variables) set)\n\n### Setup environment variables\n\n| Environment Variable     | Description                                                                                                                     | Example           | Default |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ------- |\n| `GOOGLE_EMAIL_ADDRESS`   | Email address to assume to, needs to be an workspace admin                                                                      | `foo@example.com` | `null`  |\n| `GOOGLE_CREDENTIALS`     | Base64'd json as downloaded from the google service account creation step                                                       | `Zm9vCg==`        | `null`  |\n| `ADD_USERS`              | Set to TRUE to add users to the github organisation                                                                             | `TRUE`            | `false` |\n| `REMOVE_USERS`           | Set to TRUE to remove users from the github organisation                                                                        | `TRUE`            | `false` |\n| `EXIT_CODE_ON_MISMATCH`  | Exit code to use when there's a mismatch, useful when combined with `ADD_USERS` and `REMOVE_USERS` to be used in a dry-run mode | `1`               | `0`     |\n| `GITHUB_ORG`             | GitHub Organization                                                                                                             | `chrisnstest`     | `null`  |\n| `GITHUB_APP_ID`          | GitHub App ID                                                                                                                   | `106341`          | `null`  |\n| `GITHUB_INSTALLATION_ID` | Github App Installation ID                                                                                                      | `15627551`        | `null`  |\n| `GITHUB_PRIVATE_KEY`     | Base64'd private key as downloaded from github application registration step                                                    | `Zm9vCg==`        | `null`  |\n| `IGNORED_USERS`          | Comma separated list of user ids to totally ignore always, useful for owners of an org you don't want accidentally removed      | `owner1,owner2`   | `null`  |\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappvia%2Fgithubusermanager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fappvia%2Fgithubusermanager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappvia%2Fgithubusermanager/lists"}