Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cmdct-3754] Setup cognito and login #5

Merged
merged 4 commits into from
Jul 8, 2024
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
16 changes: 15 additions & 1 deletion .env.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,18 @@ VPC_ID=local-nonsense
VPC_SUBNET_A=local-nonsense
VPC_SUBNET_B=local-nonsense
VPC_SUBNET_C=local-nonsense
BROKER_STRINGS=local-nonsense
BROKER_STRINGS=local-nonsense

# needed for e2e tests
CYPRESS_ADMIN_USER_EMAIL=op://mdct_devs/hcbs_secrets/CYPRESS_ADMIN_USER_EMAIL
CYPRESS_ADMIN_USER_PASSWORD=op://mdct_devs/hcbs_secrets/CYPRESS_ADMIN_USER_PASSWORD # pragma: allowlist secret
CYPRESS_STATE_USER_EMAIL=op://mdct_devs/hcbs_secrets/CYPRESS_STATE_USER_EMAIL
CYPRESS_STATE_USER_PASSWORD=op://mdct_devs/hcbs_secrets/CYPRESS_STATE_USER_PASSWORD # pragma: allowlist secret

# db:seed
SEED_ADMIN_USER_EMAIL=op://mdct_devs/hcbs_secrets/SEED_ADMIN_USER_EMAIL
SEED_ADMIN_USER_PASSWORD=op://mdct_devs/hcbs_secrets/SEED_ADMIN_USER_PASSWORD # pragma: allowlist secret
SEED_STATE_USER_EMAIL=op://mdct_devs/hcbs_secrets/SEED_STATE_USER_EMAIL
SEED_STATE_USER_PASSWORD=op://mdct_devs/hcbs_secrets/SEED_STATE_USER_PASSWORD # pragma: allowlist secret
SEED_STATE=op://mdct_devs/hcbs_secrets/SEED_STATE
SEED_STATE_NAME=op://mdct_devs/hcbs_secrets/SEED_STATE_NAME
24 changes: 11 additions & 13 deletions serverless-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,26 @@ services:
# SarFormBucketName: ${database.SarFormBucketName}
# SarReportTableName: ${database.SarReportTableName}
# SarReportTableStreamArn: ${database.SarReportTableStreamArn}
# AttachmentsBucketName: ${uploads.AttachmentsBucketName}

# wave 3: depends on many
# ui-auth:
# path: services/ui-auth
# params:
# AttachmentsBucketArn: ${uploads.AttachmentsBucketArn}
# ApiGatewayRestApiName: ${app-api.ApiGatewayRestApiName}
# ApplicationEndpointUrl: ${ui.ApplicationEndpointUrl}
ui-auth:
path: services/ui-auth
params:
# ApiGatewayRestApiName: ${app-api.ApiGatewayRestApiName}
ApplicationEndpointUrl: ${ui.ApplicationEndpointUrl}

# wave 4: depends on most
ui-src:
path: services/ui-src
params:
# ApiRegion: ${app-api.Region}
# ApiGatewayRestApiUrl: ${app-api.ApiGatewayRestApiUrl}
# CognitoRegion: ${ui-auth.Region}
# IdentityPoolId: ${ui-auth.IdentityPoolId}
# UserPoolId: ${ui-auth.UserPoolId}
# UserPoolClientId: ${ui-auth.UserPoolClientId}
# UserPoolClientDomain: ${ui-auth.UserPoolClientDomain}
# IdentityProvider: ${ui-auth.IdentityProvider}
CognitoRegion: ${ui-auth.Region}
IdentityPoolId: ${ui-auth.IdentityPoolId}
UserPoolId: ${ui-auth.UserPoolId}
UserPoolClientId: ${ui-auth.UserPoolClientId}
UserPoolClientDomain: ${ui-auth.UserPoolClientDomain}
IdentityProvider: ${ui-auth.IdentityProvider}
S3BucketName: ${ui.S3BucketName}
CloudFrontDistributionId: ${ui.CloudFrontDistributionId}
ApplicationEndpointUrl: ${ui.ApplicationEndpointUrl}
6 changes: 6 additions & 0 deletions services/ui-auth/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# package directories
node_modules
jspm_packages

# Serverless directories
.serverless
32 changes: 32 additions & 0 deletions services/ui-auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# ui-auth

## Configuration - AWS Systems Manager Parameter Store (SSM)

The following values are used to configure the deployment of this service (see below for more background and context).
| Parameter | Required? | Accepts a default? | Accepts a branch override? | Purpose |
| --- | :---: | :---: | :---: | --- |
| .../iam/path | N | Y | Y | Specifies the [IAM Path](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-friendly-names) at which all IAM objects should be created. The default value is "/". The path variable in IAM is used for grouping related users and groups in a unique namespace, usually for organizational purposes.|
| .../iam/permissionsBoundaryPolicy | N | Y | Y | Specifies the [IAM Permissions Boundary](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) that should be attached to all IAM objects. A permissions boundary is an advanced feature for using a managed policy to set the maximum permissions that an identity-based policy can grant to an IAM entity. If set, this parmeter should contain the full ARN to the policy.|
| sesSourceEmailAddress | N | Y | Y | The email address with which the apllication sends the email. This email address must be verified in SES.|
| okta_provider_type | N | N | Y | The OKTA provider type, accepts "SAML" or "OIDC"|
| okta_metadata_url | N | N | Y | Used by SAML provider, The Metadata url for Okta, gotten from Okta's admin console from the app.|
| okta_oidc_client_id | N | N | Y | Used by OIDC provider, The client id for Okta, gotten from Okta's admin console from the app.|
| okta_oidc_client_secret | N | N | Y | Used by OIDC provider, The client secret for Okta gotten from Okta's admin console from the app.|
| okta_oidc_issuer | N | N | Y | Used by OIDC provider, The OIDC Issuer for Okta, gotten from Okta's admin console from the app.|

This project uses [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html), often referred to as simply SSM, to inject environment specific, project specific, and/or sensitive information into the deployment.
In short, SSM is an AWS service that allows users to store (optionally) encrypted strings in a directory like hierarchy. For example, "/my/first/ssm/param" is a valid path for a parameter. Access to this service and even individual paramters is granted via AWS IAM.

An example of environment specific information is the id of a VPC into which we want to deploy. This VPC id should not be checked in to git, as it may vary from environment to environment, so we would use SSM to store the id information and use the [Serverless Framework's SSM lookup capability](https://www.serverless.com/framework/docs/providers/aws/guide/variables/#reference-variables-using-the-ssm-parameter-store) to fetcn the information at deploy time.

This project has also implemented a pattern for specifying defaults for variables, while allowing for branch (environment specific overrides). That pattern looks like this:

```
sesSourceEmailAddress: ${ssm:/configuration/${self:custom.stage}/sesSourceEmailAddress~true, ssm:/configuration/default/sesSourceEmailAddress~true}
```

The above syntax says "look for an ssm parameter at /configuration/<branch name>/sesSourceEmailAddress; if there isn't one, look for a parameter at /configuration/default/sesSourceEmailAddress". With this logic, we can specify a generic value for this variable that would apply to all environments deployed to a given account, but if we wish to set a different value for a specific environment (branch), we can create a parameter at the branch specific path and it will take precedence.

In the above tabular documentation, you will see columns for "Accepts default?" and "Accepts a branch override?". These columns relate to the above convention of searching for a branch specific override but falling back to a default parameter. It's important to note if a parameter can accept a default or can accept an override, because not all can do both. For example, a parameter used to specify Okta App information cannot be set as a default, because Okta can only support one environment (branch) at a time; so, okta_metadata_url is a good example of a parameter that can only be specified on a branch by branch basis, and never as a default.

In the above documentation, you will also see the Parameter value denoted as ".../iam/path", for example. This notation is meant to represent the core of the parameter's expected path. The "..." prefix is meant to be a placeholder for either "/configuration/default" (in the case of a default value) or "/configuration/myfavoritebranch" (in the case of specifying a branch specific override for the myfavoritebranch branch.
48 changes: 48 additions & 0 deletions services/ui-auth/handlers/createUsers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as cognitolib from "../libs/cognito-lib";
const userPoolId = process.env.userPoolId;
const users = require("../libs/users.json");

async function myHandler(_event, _context, _callback) {
for (var i = 0; i < users.length; i++) {
var poolData = {
UserPoolId: userPoolId,
Username: users[i].username,
MessageAction: "SUPPRESS",
UserAttributes: users[i].attributes,
};
var passwordData = {
Password: process.env.bootstrapUsersPassword,
UserPoolId: userPoolId,
Username: users[i].username,
Permanent: true,
};
var attributeData = {
Username: users[i].username,
UserPoolId: userPoolId,
UserAttributes: users[i].attributes,
};

try {
// This may error if the user already exists
await cognitolib.createUser(poolData);
} catch {
/* swallow this exception and continue */
}

try {
//userCreate must set a temp password first, calling setPassword to set the password configured in SSM for consistent dev login
await cognitolib.setPassword(passwordData);
} catch {
/* swallow this exception and continue */
}

try {
//if user exists and attributes are updated in this file updateUserAttributes is needed to update the attributes
await cognitolib.updateUserAttributes(attributeData);
} catch {
/* swallow this exception and continue */
}
}
}

exports.handler = myHandler;
29 changes: 29 additions & 0 deletions services/ui-auth/libs/cognito-lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
CognitoIdentityProviderClient,
AdminCreateUserCommand,
AdminSetUserPasswordCommand,
AdminUpdateUserAttributesCommand,
} from "@aws-sdk/client-cognito-identity-provider";

const COGNITO_CLIENT = new CognitoIdentityProviderClient({
apiVersion: "2016-04-19",
region: "us-east-1",
logger: {
debug: console.debug, // eslint-disable-line no-console
info: console.info, // eslint-disable-line no-console
warn: console.warn, // eslint-disable-line no-console
error: console.error, // eslint-disable-line no-console
},
});

export async function createUser(params) {
await COGNITO_CLIENT.send(new AdminCreateUserCommand(params));
}

export async function setPassword(params) {
await COGNITO_CLIENT.send(new AdminSetUserPasswordCommand(params));
}

export async function updateUserAttributes(params) {
await COGNITO_CLIENT.send(new AdminUpdateUserAttributesCommand(params));
}
Loading
Loading