Skip to content

The request process workflow tool for the RedHat SSO Dev Exchange service

License

Notifications You must be signed in to change notification settings

bcgov/sso-requests

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

  • Github: The CSS UI is a static site deployed as GitHub pages through CI/CD defined using GitHub actions. Refer to /app subdirectory for more details.
  • AWS: CSS backend is built upon AWS lambda functions protected by AWS API gateway, and supported by AWS RDS postgres database.
  • Terraform: The infrastructure for CSS backend is created and version controlled through terraform. The backed is deployed into an existing AWS zone, and uses the predefined VPC and subnets. The terraform code is located under /terraform subdirectory.
  • OCP: OpenShift environment offered through private cloud. All the keycloak instances are located in environment specific namespaces (xxxx-dev, xxxx-test, and xxxx-prod)

Workflow

The general workflow for new client creation is:

  • W1: A user authenticates with their IDIR and fills in a form for their new client/integration. The form submission sends a request to CSS backend through AWS API gateway
  • W2: The lambda function (see lambda/app) evaluates the data and after successful validation, the integration is added to a queue table in AWS RDS. If this process fails then the integration status is updated to planFailed
  • W3: The integration is picked from queue and is then used to build an openid or saml client request w/ payload depending upon the protocol and sent to keycloak via admin API for creating a new client. After successful application, the integration is deleted from queue and its status is updated to applied. If the client creation fails at keycloak, then the integration remains in queue table and its status is updated to applyFailed.
  • W4: The scheduler lambda function runs every 5 minutes and it processes all the integrations found in queue table through lambda/app. The successful integrations are removed from queue and updated with applied status.
  • W5: The data-integrity lambda function runs daily in the morning. It scans the CSS database for active integrations and lists out any missing clients from Keycloak and notifies the Pathfinder SSO team via Rocket.Chat.
  • IAC: The CSS repo folder ./terraform holds the terraform code to deploy the CSS backend and the grafana instances in AWS environment.

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:

  • Lambda: Lambda functions are used to handle backend logic on API events.
  • API Gateway: API Gateway supports serverless infrastructure, and is used to proxy API events to the lambda functions.
  • RDS: An amazon relational database that supports serverless infrastructure.
  • S3: Holds the swagger static site that can be accessed at here and also used for storing the CSS backend terraform state
  • DynamoDB: Used to store the terraform lock to streamline multiple deployments to AWS
  • CloudWatch: Stores all the logs generated by lambda functions
    • Events: Schedulers that trigger some Github actions and lambda functions

Authentication

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

Authorization

  • The user sigining into the application can request an integration for self or, could create a team and request for a team integration.
  • The user can add team members to his team and grant them either admin or member role
  • The pathfinder team members have the super-admin permissions, where they have access to all the requested integrations and teams.

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:

  • API_AUTH_SECRET: The basic secret for endpoint to remove inactive IDIR users from CSS

  • AWS_ECR_URI: AWS container registry URI

  • CHES_USERNAME: Username of CHES account

  • CHES_PASSWORD: Password of CHES account

  • KEYCLOAK_V2_DEV_URL: The URL of the keycloak dev environment.

  • KEYCLOAK_V2_TEST_URL: The URL of the keycloak test environment.

  • KEYCLOAK_V2_PROD_URL: The URL of the keycloak prod environment.

  • KEYCLOAK_V2_DEV_USERNAME: The client id for the keycloak dev environment.

  • KEYCLOAK_V2_TEST_USERNAME: The client id for the keycloak test environment.

  • KEYCLOAK_V2_PROD_USERNAME: The client id for the keycloak prod environment.

  • KEYCLOAK_V2_DEV_PASSWORD: The client secret for the keycloaks dev environment.

  • KEYCLOAK_V2_TEST_PASSWORD: The client secret for the keycloaks test environment.

  • KEYCLOAK_V2_PROD_PASSWORD: The client secret for the keycloaks prod environment.

  • GH_ACCESS_TOKEN: Access token for the github service account (used to trigger workflows in sso-terraform).

  • GH_SECRET: The secret used to authorize requests between github actions (from sso-terraform) to the API.

  • TFC_TEAM_TOKEN: The token used to run terraform cloud.

  • GRAFANA_API_TOKEN: The API token to authenticate with Grafana and pull metrics and logs data.

  • TERRAFORM_DEPLOY_ROLE_ARN: The AWS role arn required by AWS SDK in terraform github workflow to authenticate with AWS and update resources. In Addition, the following secrets are required for sso-terraform and sso-terraform-dev to work with this app:

  • GH_ACCESS_TOKEN: Access token for the github service account (used to run github API calls).

  • GH_SECRET: The secret used to authorize requests between github actions (from sso-terraform) to the API.

  • REALM_REGISTRY_API: The API URL of Realm Registry

  • VERIFY_USER_SECRET: A secret or a private key used for signing team invitation tokens

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,

  • you will need to have a local postgres database running. Ensure you have started the server with pg_ctl start

  • For the first time running the tests, the database will need to be created. Run make local_db from the root directory Note: you may need to run chmod +x ./.bin/db-setup.sh to give necessary permissions.

  • Run make server_test to run all the backend unit tests

  • Navigate to ./lambda/jest_stare directory and open the index.html to view the report that shows the code coverage

Errors

Error codes we use for the application:

  • E01: There is an application in the sso-terraform repository that cannot be applied, blocking new requests
  • E02: The user has a token in their session storage that is invalid
  • E03: Missing of invalid email address associated with your IDIR account
  • E04: Request timeout
  • E05: 503 Service Temporarily Unavailable

Reporting

AWS RDS

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

  • In the AWS console, navigate to the RDS dashboard and select DB Clusters from the resources panel.
  • Select aurora-db-postgres. From the top right actions dropdown select query.
  • You will be prompted for the db host, db name, username and password. Enter these and select connect (it may take some time to connect)
  • From the saved queries, select the one you would like to use (refer to descriptions for information)

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

  • Create a pull request from dev to main and update pull request labels to choose a specific type of release
  • release:major - will create a major release (example: v1.0.0 -> v2.0.0)
  • release:minor - will create a minor release (example: v1.0.0 -> v1.1.0)
  • release:patch - will create a patch release (example: v1.0.0 -> v1.0.1)
  • release:norelease - will not trigger any release

Setup Grafana

Image

  • Login to AWS and create a new private repository in AWS Elastic Container Registry
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

  • Create a new 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

  • Create a 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'