@seek/backoffice-access

[![Slack channel](https://img.shields.io/badge/slack-%23indirect-aa6dca.svg)](https://slack.com/app_redirect?channel=C016GVBE7UK) [![backoffice-access build](https://img.shields.io/buildkite/10f05d25c6d7925964e0b5cc10e38bf34603b79cefb439bd82/master.svg?la

Usage no npm install needed!

<script type="module">
  import seekBackofficeAccess from 'https://cdn.skypack.dev/@seek/backoffice-access';
</script>

README

backoffice-access

Slack channel backoffice-access build backoffice-access-package build @seek/backoffice-access Powered by skuba

Centralised repository for Back Office access management.

Quick start

Add or remove a GitHub team member

Some SEEK staff members are granted Back Office access through GitHub teams under the SEEK-Jobs organization. This is typical for technology teams that contribute to or maintain a slice of the Back Office. For example, the SEEK-Jobs/Athena team manages budget functionality.

To add or remove Back Office permissions for an individual with access granted through GitHub teams:

  1. Find their relevant GitHub teams under org.

  2. Raise a PR to org to add or remove the individual.

  3. Rebuild the backoffice-access pipeline.

    This step may be automated in future.

Add or remove a non-GitHub user

Other SEEK staff members may be:

  • Outside of our GitHub organization

    For example, Client Support members do not use GitHub but still require Back Office access.

  • Granted Back Office access as an individual or through other logical groupings

    For example, a technology team member may need to temporarily support a business process prior to handing it over to a support team.

To add or remove Back Office permissions for a SEEK staff member that falls under one of these categories:

  1. Find their relevant groups under config/groups.

  2. Raise a PR to this repo to add or remove the individual.

Add a permission

This repo defines a unified set of granular permissions for the Back Office. If you're a technology team member who is looking to expose a new feature through the Back Office:

  • Review whether your feature exposes restricted or sensitive data.

    Your data source is required to authenticate all requests, and further authorisation is required if the given endpoint is not safe for all SEEK staff to access.

    For example, an endpoint that surfaces PII of SEEK candidates should be restricted to teams that support candidate enquiries. On the other hand, an endpoint that surfaces metadata relating to SEEK API event delivery is not sensitive and can be safely shared with all SEEK staff.

  • Review the existing config/permissions.yaml to see if the appropriate permission already exists.

To add a Back Office permission:

  1. Raise a PR to this repo to extend config/permissions.yaml.

    Make sure to follow the instructions in the PR to semantically tag your squash commit.

  2. Enforce authentication in your data source.

    This requires the SEEK Auth Sidecar to be configured via Gantry like so:

    authentication:
      serviceAudience: backoffice-graphql-server
      serviceIssuerWhitelist: [seek-ssod-ingress]
    

    See the Back Office architecture document for further details.

  3. Enforce authorisation in your data source.

    This requires backOfficeDirectory middleware to be configured like so:

    import { Zactive } from '@seek/zactive-directory';
    import { S3 } from 'aws-sdk';
    import Koa from 'koa';
    
    const directory = Zactive.koa.backOfficeDirectory({
      environment: config.environment === 'prod' ? 'prod' : 'dev',
      s3: new S3(),
    });
    
    const app = new Koa()
      .use(directory.ensureRole('PARTNER_CREATION'))
      .use(async (ctx) => {
        const { id } = await directory.getUser(ctx);
        ctx.body = `${id} has partner creation privileges`;
      });
    

    See the Zactive Directory repo for further details.

Design

Context

The Back Office graph is fronted by SSOd Ingress, which requires SEEK staff to authenticate via Okta in order to access internal resources. Endpoints behind SSOd Ingress receive an Authorization request header containing a JWT compatible with the s2sauth protocol. This bearer token must be verified to ensure that the request originated from SSOd Ingress, and it also carries additional SSOd claims that can be used to restrict access to a subset of SEEK staff members.

The Back Office graph forwards the SSOd JWT through to underlying data sources. Data sources must authenticate the token, and may use its SSOd claims to authorise sensitive operations.

A naive authorisation model may enforce a simple email address allowlist per Back Office operation. Zactive Directory builds upon this concept to support more advanced mechanisms, including role-based access control and granting permissions against GitHub org hierarchies.

While this data source-driven approach works well for standalone SSOd Ingress integrations, it doesn't scale well to the Back Office's network of data sources with distributed ownership. Onboarding or offboarding a SEEK staff member can be tedious and require coordination across multiple repositories. Visibility into the Back Office permissions of an individual is poor across the board; reviewing this ad hoc requires trawling through multiple GitHub repos, and fetching this online from the Back Office frontend requires every data source to implement a metadata endpoint to return their permissions for the authenticated user.

Overview

This repo houses a unified access control directory for use across Back Office data sources.

All Back Office permissions can now be found in a centralised place. Technology teams can easily review the existing set of permissions and possibly reuse permissions across multiple data sources. This may be useful for common objects that have additional dimensions tacked on by other teams, and to seamlessly transfer ownership of a particular slice of the Back Office. For example, some core objects may be better served by Marketplace Services rather than Indirect in future.

It's also much easier to add a new permission to the Back Office. Once this repo is updated with the permission, it will flow through to:

  1. The association JSON files in the seek-backoffice-access S3 bucket

    These are resolved mappings between individual SEEK staff email addresses and their granted permissions. Data sources using the backOfficeDirectory middleware will fetch file changes every five minutes for authorisation. The Back Office graph uses this middleware directly to expose the permissions for the authenticated user through the currentUser.permissions field. This saves every data source from having to define a metadata endpoint for their self-managed permissions.

  2. The @seek/backoffice-access npm package

    This contains a TypeScript type for the Back Office permissions. Data sources can use this directly or via backOfficeDirectory's built-in typings to avoid permission typos when implementing authorisation. The Back Office frontend uses this package along with a query to currentUser.permissions to support conditional rendering based on the authenticated user's permissions. This makes it easy to propagate through the GraphQL schema to the frontend, as each data source doesn't have to wire up its own metadata endpoint to return the available permissions for the authenticated user.

Permissions

Back Office permissions are specified in config/permissions.yaml:

query:some-object:
  - Grantee A
  - Grantee B

The Back Office supports both a dev and prod environment. By default, permissions apply across both environments. You can limit a grant to be exclusive to a particular environment like so:

query:some-object:
  - devOnly: Grantee A
  - prodOnly: Grantee B

Groups under config/groups allow you to define logical groupings of individuals and org hierarchies. For example, Group Apps consists of some developers in GitHub teams and others outside of GitHub, and this listing would be tedious to repeat against each granular permission in config/permissions.yaml. Instead, declare membership in a group.yaml file:

# config/groups/group-apps/group.yaml
members:
  - SEEK-Jobs/GroupĀ Apps
  - a@seek.com.au
  - b@seek.com.au
  - c@seek.com.au

Then reference the group in config/permissions.yaml:

query:some-object:
  - ./groups/group-apps

Groups are hierarchical:

query:some-internal-object:
  # Allow all of Group Apps
  - ./groups/group-apps

query:some-restricted-object:
  # Allow the prod-testers sub-group only
  - ./groups/group-apps/prod-testers

API

The @seek/backoffice-access package exports a handful of helpers for working with the Back Office permissions defined in this repo.

BACK_OFFICE_PERMISSIONS

An array of all Back Office permissions.

import { BACK_OFFICE_PERMISSIONS } from '@seek/backoffice-access';

BACK_OFFICE_PERMISSIONS.length;

BackOfficePermission

A string union type of all Back Office permissions.

import type { BackOfficePermission } from '@seek/backoffice-access';

const permission: BackOfficePermission = 'ADMIN_OFFBOARDING';

isBackOfficePermission

Whether a given input represents a Back Office permission.

import { isBackOfficePermission } from '@seek/backoffice-access';

isBackOfficePermission('ADMIN_OFFBOARDING');

Development

Prerequisites

  • Node.js LTS
  • Yarn 1.x
yarn install

Test

yarn test

Lint

# Fix issues
yarn format

# Check for issues
yarn lint

Package

# Compile source
yarn build

# Review bundle
npm pack

Release

This package is published to the npm registry under the private @seek npm org with Gutenberg, SEEK's central npm publishing pipeline.

It depends on this repo being hosted on SEEK-Jobs with appropriate access.

Commit messages

This package is published with semantic-release, which requires a particular commit format to manage semantic versioning. You can run the interactive yarn commit command in place of git commit to generate a compliant commit title and message. If you use the Squash and merge option on pull requests, take extra care to format the squashed commit in the GitHub UI before merging.

Releasing latest

Commits to the master branch will be released with the latest tag, which is the default used when running npm install or yarn install.

Releasing other dist-tags

semantic-release prescribes a branch-based workflow for managing distribution tags.

You can push to other branches to manage betas, maintenance updates to prior major versions, and more.

Here are some branches that semantic-release supports by default:

Git branch npm dist-tag
master latest
alpha alpha
beta beta
next next
1.x release-1.x

For more information, see the semantic-release docs on triggering a release.