bcgov / sso-requests

The request process workflow tool for the RedHat SSO Dev Exchange service
https://bcgov.github.io/sso-requests/
Apache License 2.0
3 stars 5 forks source link
bcgov-sso citz

Common Hosted Single Sign-On

Lifecycle:Stable codecov

Introduction

Common Hosted Single Sign-On (CSS) is an self-serve application developed by Pathfinder SSO Team to enable digital product development teams to request integrations with BC Government approved login options such as IDIR, BCeID and more.

Architecture

diagram

Components

Workflow

The general workflow for new client creation is:

Design

Front-end

The front end application is built with nextjs (a react framework). It is a static application hosted on github pages. A static single-page-application was chosen to deliver a fast user experience, take advantage of pre-built bcgov components, and to interact as an independant layer from the backend.

Backend

The backend uses AWS serverless technologies, which was chosen to reduce cost (with serverless, you only pay for what you use) since this application is expected to have low traffic. It uses:

Authentication

Only users with IDIR with or without MFA are allowed to login and request for an integration.

Authorization

Microsoft Graph Service

For getting details on IDIR users (e.g valid user lookups, importing into keycloak) we are using Microsoft Graph. For details on implementation see here.

Local Development Environment

To setup a local development environment, a detailed guide may be found here.

Getting Started

Required GitHub Repository Secrets

This repository requires the following secrets:

AWS

To run this app, you will need to setup your terraform infrastructure. The code in the terraform directory assumes one available VPC exists with available subnets in two zones. To set the names for your subnets the terraform variables subnet_a and subnet_b should be set.

The lambda functions are written in typescript, and to compile and bundle them you can run make lambda-all from the lambda directory. Once they are compiled, from the terraform directory you can run terraform apply to build the backend.

Tests

This repository has frontend tests using jest and react testing library, and backend tests run with jest. Tests are run in CI in the test.yml file.

To run the frontend tests, from the app directory run yarn test. If adding a snapshot test, running yarn test will add the new snapshot file for you. If you want to update a snapshot test, run yarn test -u

To run the backend unit tests,

Errors

Error codes we use for the application:

Reporting

AWS RDS

To use, you will need access to the AWS platform as well as the database credentials.

Grafana

There are two instances of Grafana, one for sandbox and the other for production environments. Both of them are deployed in AWS ECS containers with persistent storage through AWS EBS under environment specific VPCs. Grafana has read only access to the database.

Sample Queries

Some queries are id specific, update the where clause to change the request number_

-- Count of clients in draft
select count(*) from requests where status='draft';

-- Count of clients awaiting approval (note: currently auto-approve but will apply when bceid is added)
select count(*) from requests where status='submitted';

-- Count of clients completed
select count(*) from requests where status='applied';

-- (initial )time request was fulfilled (dev, test, and prod)
select events.created_at from events join requests on requests.id = events.request_id where requests.id=1 and events.event_code = 'request-apply-success';

-- get all clients. note the null are those that have not submittted request (zs)
select client_name, preferred_email from requests where archived = false;

Queries to fetch user emails

-- Integration users with a team associated with
SELECT
    r.id,
    r.client_name,
    r.service_type,
    r.team_id,
    ut.user_id,
    u.idir_email,
    u.additional_email
FROM
    requests as r
INNER JOIN users_teams as ut ON r.team_id=ut.team_id
INNER JOIN users as u ON u.id=ut.user_id
WHERE r.uses_team=TRUE
AND r.archived=FALSE
AND ut.pending=FALSE
ORDER BY r.client_name

-- Integration users with no team associated with
SELECT
    r.id,
    r.client_name,
    r.user_id,
    r.service_type,
    u.idir_email,
    u.additional_email
FROM
    requests as r
INNER JOIN users as u ON u.id=r.user_id
WHERE r.uses_team=FALSE
AND r.archived=FALSE
ORDER BY r.client_name

Release Process

Setup Grafana

Image

export AWS_ECR_URI=

aws ecr get-login-password --region ca-central-1 | docker login --username AWS --password-stdin $AWS_ECR_URI

docker pull --platform linux/amd64 grafana/grafana:9.3.2

docker tag grafana/grafana:9.3.2 $AWS_ECR_URI/bcgov-sso/grafana:9.3.2

docker push $AWS_ECR_URI/bcgov-sso/grafana:9.3.2

Secret

export GF_SECURITY_ADMIN_PASSWORD=
# login to CSS app and download the prod installation json for the SSO Dashboard integration
export GF_AUTH_GENERIC_OAUTH_CLIENT_ID=
export GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=

aws secretsmanager create-secret --name sso-grafana --description "SSO Grafana Secrets" \
--secret-string '{"GF_SECURITY_ADMIN_PASSWORD": "$GF_SECURITY_ADMIN_PASSWORD", "GF_AUTH_GENERIC_OAUTH_CLIENT_ID": "$GF_AUTH_GENERIC_OAUTH_CLIENT_ID", "GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET": "$GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET"}' --region ca-central-1

Policy

aws iam create-policy --policy-name SSOPathfinderReadGrafanaSecretInfo --policy-document ./policy.json

# navigate to secrets manager and copy the secret ARN
export GRAFANA_SECRET_ARN=

# policy.json
{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Sid": "VisualEditor0",
          "Effect": "Allow",
          "Action": "secretsmanager:GetSecretValue",
          "Resource": "$GRAFANA_SECRET_ARN"
      }
  ]
}

Readonly User

CREATE USER cssgrafana WITH PASSWORD '<secure-password>';

GRANT CONNECT ON DATABASE ssorequests TO cssgrafana;

GRANT USAGE ON SCHEMA public TO cssgrafana;

GRANT SELECT ON ALL TABLES IN SCHEMA public TO cssgrafana;

ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO cssgrafana;

SELECT * from pg_catalog.pg_roles;

SELECT * FROM pg_catalog.pg_auth_members;

# check privileges on a table
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name='requests'