Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ Scripts a person runs from a workstation with their own login (for example an ac

### Offboarding ([`offboard/`](offboard))

- [`offboard-audit.sh`](offboard/offboard-audit.sh): read-only report of where a departed person still has GitHub or OpenShift access or ownership.
- [`offboard.sh`](offboard/offboard.sh): runs both audits, one block per person (`login` or `login=othername`).
- [`offboard-github.sh`](offboard/offboard-github.sh): read-only report of GitHub access and ownership for one or more logins.
- [`offboard-openshift.sh`](offboard/offboard-openshift.sh): read-only report of OpenShift RoleBindings whose user subject contains a given name.

## Checks

Expand Down
93 changes: 55 additions & 38 deletions offboard/README.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,91 @@
# Offboarding

## `offboard-audit.sh`
`offboard.sh` runs both reports, one block per person. The two scripts underneath share no calls.

Read-only report of every place a departed person still has access or ownership. It uses your own `gh` login, and your own `oc` login when one is active. It never changes anything and never prints tokens.
```bash
./offboard/offboard.sh
# asks for people

./offboard/offboard.sh \
gpascucci=greg.pascucci \
ianliuwk1019 \
franTarkenton \
DBAJohnL \
Mitchiavelli \
MCatherine1994 \
thermcampos \
rmcampos
```

Names joined with `=` belong to one person. Each name is searched for as written. An OpenShift subject matches when it contains the name. No suffix is added. A name that is a valid GitHub login is also sent to the GitHub audit. If `oc` is not logged in, the GitHub blocks are still printed and OpenShift is skipped.

## `offboard-github.sh`

GitHub access and ownership, using your own `gh` login.

```bash
./offboard/offboard-audit.sh [options] <github-username> [more usernames]
./offboard/offboard-github.sh [options] <github-username> [more usernames]

# Default organizations and repositories
./offboard/offboard-audit.sh example-user
./offboard/offboard-github.sh example-user

# JSON, one organization, two repositories
./offboard/offboard-audit.sh --json --org bcgov \
./offboard/offboard-github.sh --json --org bcgov \
--repo bcgov/example-repo --repo bcgov/another-repo example-user

# Repository list from a file (one OWNER/NAME per line, # comments allowed)
./offboard/offboard-audit.sh --repo-file repos.txt example-user

# Also match an IDIR name in OpenShift RoleBindings
oc login ...
./offboard/offboard-audit.sh --idir EXAMPLEIDIR example-user
./offboard/offboard-github.sh --repo-file repos.txt \
ianliuwk1019 franTarkenton DBAJohnL Mitchiavelli gpascucci MCatherine1994 thermcampos rmcampos
```

| Option | Meaning |
| --- | --- |
| `--org ORG` | Organization to check (repeatable). Default: `OFFBOARD_ORGS` (space- or comma-separated), else `bcgov bcgov-c bcgov-nr`. |
| `--repo OWNER/NAME` | Repository for the per-repo checks (repeatable). |
| `--repo-file FILE` | File with one `OWNER/NAME` per line. |
| `--idir NAME` | Also match `NAME` and `NAME@idir` in OpenShift RoleBindings. Single username only. |
| `--json` | JSON output instead of text. |

Exit codes: `0` nothing found, `1` access found, `2` usage or dependency error, `3` a GitHub API call failed.
A login GitHub does not have is printed under that name and again under `Skipped, no GitHub account`. It is not sent to GitHub. The other logins still run. Exit codes: `0` nothing found, `1` access found, `2` usage or dependency error, `3` a GitHub API call failed.

Login, organization, team, and CODEOWNERS comparisons are case-insensitive.

## Checks
The repository list, collaborator lists, and CODEOWNERS files are fetched once and matched against every login. Organization members are one list per organization. Teams are one GraphQL call per organization. CODEOWNERS code search is one query per live login.

| Check | Source |
| --- | --- |
| Organization membership | `GET /orgs/{org}/members/{user}` for each organization |
| Teams | Teams in each organization that you can see and that list the user |
| Organization membership | `GET /orgs/{org}/members`, then a local match |
| Teams | One GraphQL call per organization for every live login, then a local match |
| Repository access | Collaborator permission on each target repository, marked direct or through a team or organization role |
| CODEOWNERS (checked repositories) | `@user` entries in the target repositories' CODEOWNERS file (`.github/`, root or `docs/`), comments ignored |
| Environment required reviewers | Deployment environments in the target repositories that list the user, or one of the user's teams, as a required reviewer |
| CODEOWNERS (code search) | Code search for `@user` in CODEOWNERS files across the organizations |
| Assigned | Open issues and pull requests assigned to the user |
| Review requested | Open pull requests waiting on the user's review |
| OpenShift RoleBindings | Only when `oc whoami` succeeds: RoleBindings in the namespaces listed by `oc projects` whose `User` subjects are `user`, `user@github`, or the `--idir` name. Otherwise a skip note is printed. |
| CODEOWNERS | `@user` entries in the target repositories' CODEOWNERS file (`.github/`, root or `docs/`), comments ignored |
| CODEOWNERS (code search) | CODEOWNERS files across the organizations that mention the login, including repositories you do not admin |
| Environment required reviewers | People listed on a repository environment protection rule. A team on that rule is not listed here. |

The target repositories are those given with `--repo` or `--repo-file`. Without either, they are the repositories in the configured organizations where you have admin (`gh api user/repos` with `permissions.admin`).

## Requirements
Requires `gh` (scopes `repo` and `read:org`) and `jq`.

- `gh`, logged in with the `repo` and `read:org` scopes (`gh auth status` lists them; add with `gh auth refresh -s read:org`)
- `jq`
- Optional: `oc`, logged in (`oc whoami`)
The per-repo checks make 3 to 4 API calls per repository, about 2 seconds per repository. With around 200 admin repositories a run takes several minutes, whatever the length of the login list. `--repo` or `--repo-file` narrows it. Progress goes to stderr, the report to stdout.

## Speed
Limits: only what your login can see; repository access needs push access; code search is one query per live login (10 requests a minute) and only matches when `@user` is in the returned text fragment; a login on more than 100 teams is noted and the rest of that login's teams are not listed.

The per-repo checks make 3 to 4 API calls per repository, about 2 seconds per repository. With around 200 admin repositories a run takes about 7 minutes; `--repo` or `--repo-file` narrows it. The other checks take a few seconds per user. Progress goes to stderr, the report to stdout.
## `offboard-openshift.sh`

## Limits
RoleBindings on the cluster your `oc` login points at. Run this on the machine where that login exists.

- Only what your login can see is reported: teams you cannot see, and repositories you cannot read, are not covered.
- Repository access needs push access to the repository. Repositories given with `--repo` that you cannot push to get a note instead of a result.
- Code search covers default branches of indexed repositories, and matches only when the `@user` entry is in the returned text fragment. It is limited to 10 requests a minute; the script waits when the limit is reached.
- Issue and pull request search returns at most 1,000 results per query.
- OpenShift covers the cluster your `oc` login points at, and namespaces where you can read RoleBindings; unreadable namespaces are counted in a note. Group memberships are not expanded.
```bash
oc login ...
./offboard/offboard-openshift.sh --name thermcampos --name rmcampos --name greg.pascucci
```

## Tests
| Option | Meaning |
| --- | --- |
| `--name STRING` | Name to search for (repeatable). A User subject matches when it contains the name. |
| `--json` | JSON output instead of text. |

`tests/offboard-audit.bats` runs the script against stubbed `gh` and `oc` commands in `tests/stubs/`:
Matching ignores case. No suffix is added. Each name is its own section, and the detail line shows the subject string that matched. RoleBindings are read once per namespace. Exit codes match the GitHub script, except `3` means an `oc` call failed. `oc` not logged in is a usage error.

Requires `oc` logged in, and `jq`.

Limits: namespaces you can read; unreadable namespaces are counted in a note. Group subjects are not read.

## Tests

```bash
bats offboard/tests
Expand Down
Loading
Loading