Skip to content

Commit

Permalink
Prepare for integration with infra template (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
rocketnova authored Jun 25, 2024
1 parent 4474a89 commit 9d6d8a4
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 36 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ If you have previously installed this template and would like to update your pro
1. Run the [update script](./template-only-bin/update-template) in your project's root directory and pass in the branch, commit hash, or release that you want to update to, followed by the name of your application directory (e.g. `app-rails`).
```bash
curl https://raw.githubusercontent.com/navapbc/template-application-rails/main/template-only-bin/download-and-install-template | bash -s -- <commit_hash> <app_name>
curl https://raw.githubusercontent.com/navapbc/template-application-rails/main/template-only-bin/update-template | bash -s -- <commit_hash> <app_name>
```
This script will:
Expand Down
3 changes: 3 additions & 0 deletions app-rails/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ db-up: ## Run just the database container
db-migrate: ## Run database migrations
$(RAILS_RUN_CMD) db:migrate

db-rollback: ## Rollback a database migration
$(RAILS_RUN_CMD) db:rollback

db-test-prepare: ## Prepare the test database
$(RAILS_RUN_CMD) db:test:prepare

Expand Down
26 changes: 13 additions & 13 deletions app-rails/app/adapters/auth/cognito_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def initialize(client: Aws::CognitoIdentityProvider::Client.new)
def create_account(email, password)
begin
response = @client.sign_up(
client_id: ENV["AWS_COGNITO_CLIENT_ID"],
client_id: ENV["COGNITO_CLIENT_ID"],
secret_hash: get_secret_hash(email),
username: email,
password: password,
Expand Down Expand Up @@ -47,7 +47,7 @@ def create_account(email, password)
def forgot_password(email)
begin
response = @client.forgot_password(
client_id: ENV["AWS_COGNITO_CLIENT_ID"],
client_id: ENV["COGNITO_CLIENT_ID"],
secret_hash: get_secret_hash(email),
username: email
)
Expand All @@ -64,7 +64,7 @@ def forgot_password(email)
def confirm_forgot_password(email, code, password)
begin
@client.confirm_forgot_password(
client_id: ENV["AWS_COGNITO_CLIENT_ID"],
client_id: ENV["COGNITO_CLIENT_ID"],
secret_hash: get_secret_hash(email),
username: email,
confirmation_code: code,
Expand All @@ -84,7 +84,7 @@ def confirm_forgot_password(email, code, password)
def change_email(uid, new_email)
begin
response = @client.admin_update_user_attributes(
user_pool_id: ENV["AWS_COGNITO_USER_POOL_ID"],
user_pool_id: ENV["COGNITO_USER_POOL_ID"],
username: uid,
user_attributes: [
{
Expand Down Expand Up @@ -114,8 +114,8 @@ def change_email(uid, new_email)
def initiate_auth(email, password)
begin
response = @client.admin_initiate_auth(
user_pool_id: ENV["AWS_COGNITO_USER_POOL_ID"],
client_id: ENV["AWS_COGNITO_CLIENT_ID"],
user_pool_id: ENV["COGNITO_USER_POOL_ID"],
client_id: ENV["COGNITO_CLIENT_ID"],
auth_flow: "ADMIN_USER_PASSWORD_AUTH",
auth_parameters: {
"USERNAME" => email,
Expand Down Expand Up @@ -151,7 +151,7 @@ def initiate_auth(email, password)
def resend_verification_code(email)
begin
@client.resend_confirmation_code(
client_id: ENV["AWS_COGNITO_CLIENT_ID"],
client_id: ENV["COGNITO_CLIENT_ID"],
secret_hash: get_secret_hash(email),
username: email
)
Expand All @@ -163,8 +163,8 @@ def resend_verification_code(email)
def respond_to_auth_challenge(code, challenge = {})
begin
response = @client.admin_respond_to_auth_challenge(
client_id: ENV["AWS_COGNITO_CLIENT_ID"],
user_pool_id: ENV["AWS_COGNITO_USER_POOL_ID"],
client_id: ENV["COGNITO_CLIENT_ID"],
user_pool_id: ENV["COGNITO_USER_POOL_ID"],
challenge_name: "SOFTWARE_TOKEN_MFA",
session: challenge[:session],
challenge_responses: {
Expand All @@ -184,7 +184,7 @@ def respond_to_auth_challenge(code, challenge = {})
def verify_account(email, code)
begin
@client.confirm_sign_up(
client_id: ENV["AWS_COGNITO_CLIENT_ID"],
client_id: ENV["COGNITO_CLIENT_ID"],
secret_hash: get_secret_hash(email),
username: email,
confirmation_code: code
Expand Down Expand Up @@ -242,7 +242,7 @@ def verify_software_token(code, access_token)
def disable_software_token(uid)
begin
@client.admin_set_user_mfa_preference(
user_pool_id: ENV["AWS_COGNITO_USER_POOL_ID"],
user_pool_id: ENV["COGNITO_USER_POOL_ID"],
username: uid,
software_token_mfa_settings: {
enabled: false,
Expand All @@ -265,8 +265,8 @@ def get_auth_result(response)
end

def get_secret_hash(username)
message = username + ENV["AWS_COGNITO_CLIENT_ID"]
key = ENV["AWS_COGNITO_CLIENT_SECRET"]
message = username + ENV["COGNITO_CLIENT_ID"]
key = ENV["COGNITO_CLIENT_SECRET"]
Base64.strict_encode64(OpenSSL::HMAC.digest("sha256", key, message))
end

Expand Down
2 changes: 1 addition & 1 deletion app-rails/config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
config.cache_store = :null_store
end

config.active_storage.service = ENV["AWS_BUCKET_NAME"] ? :amazon : :local
config.active_storage.service = ENV["BUCKET_NAME"] ? :amazon : :local

config.action_mailer.delivery_method = ENV["SES_EMAIL"] ? :sesv2 : :letter_opener

Expand Down
5 changes: 5 additions & 0 deletions app-rails/config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true

# Exclude healthcheck endpoint from force SSL since healthchecks should not go through
# the reverse proxy.
# See https://api.rubyonrails.org/classes/ActionDispatch/SSL.html
config.ssl_options = { redirect: { exclude: -> request { /health/.match?(request.path) } } }

# Log to STDOUT by default
config.logger = ActiveSupport::Logger.new(STDOUT)
.tap { |logger| logger.formatter = ::Logger::Formatter.new }
Expand Down
2 changes: 1 addition & 1 deletion app-rails/config/storage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ local:

amazon:
service: S3
bucket: <%= ENV.fetch("AWS_BUCKET_NAME") { nil } %>
bucket: <%= ENV.fetch("BUCKET_NAME") { nil } %>


# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
Expand Down

This file was deleted.

3 changes: 1 addition & 2 deletions app-rails/db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions app-rails/local.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,20 @@ AWS_ACCESS_KEY_ID=<FILL ME IN>
AWS_SECRET_ACCESS_KEY=<FILL ME IN>
AWS_DEFAULT_REGION=<FILL ME IN>

############################
# File storage
############################

# Uncomment and fill in an AWS S3 bucket name to use S3 for file storage
# AWS_BUCKET_NAME=<FILL ME IN>
# BUCKET_NAME=<FILL ME IN>

############################
# Auth
############################

AWS_COGNITO_USER_POOL_ID=<FILL ME IN>
AWS_COGNITO_CLIENT_ID=<FILL ME IN>
AWS_COGNITO_CLIENT_SECRET=<FILL ME IN>
COGNITO_USER_POOL_ID=<FILL ME IN>
COGNITO_CLIENT_ID=<FILL ME IN>
COGNITO_CLIENT_SECRET=<FILL ME IN>

############################
# Database
Expand All @@ -37,4 +41,4 @@ AWS_COGNITO_CLIENT_SECRET=<FILL ME IN>
DB_HOST=127.0.0.1
DB_NAME=app_rails
DB_USER=app_rails
DB_PASSWORD=secret123
DB_PASSWORD=secret123
7 changes: 3 additions & 4 deletions docs/app-rails/technical-foundation.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ We have are using [UUIDs for primary keys](https://guides.rubyonrails.org/active

- ⚠️ Using ActiveRecord functions like `Foo.first` and `Foo.last` have unreliable results
- Generating new models or scaffolds requires passing the `--primary-key-type=uuid` flag. For instance, `make rails-generate GENERATE_COMMAND="model Foo --primary-key-type=uuid"`
- `pgcrypto` is a required dependency

#### Enums

Expand All @@ -61,9 +60,9 @@ To preview email views in the browser, visit: `/rails/mailers`

To test AWS SES email sending locally:

1. Set the `AWS_*` env var in your `.env` file
1. Set the `SES_EMAIL` env var to a verified sending identity
1. Restart the server
1. Set the "AWS services" environment variables in your `.env` file.
1. Add an `SES_EMAIL` environment variable to a verified sending identity.
1. Restart the server.

### 🎭 Authentication

Expand Down
48 changes: 48 additions & 0 deletions template-only-docs/Deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Deployment to cloud environments

## Requirements

- External service access: The application must be able to make network calls to the public internet.
- AWS Cognito: The application comes with AWS Cognito enabled by default, so AWS Cognito must be set up before deploying the application.
- Custom domain name with HTTPS support: AWS Cognito requires HTTPS for callback urls to function.
- Access to write to temporary directory: Rails needs to be able to write out temporary files.
- Environment variables: You must provide the application with the environment variables listed in [local.env.example](/app-rails/local.env.example).
- Secrets: You must provide the application with a secret `SECRET_KEY_BASE`. For more information, see the [Rails Guide on Security](https://guides.rubyonrails.org/v7.1/security.html#environmental-security).

## Deploying using the Platform Infrastructure Template

*Note: The following will be true once https://github.com/navapbc/template-infra/pull/650 is merged.*

This template can be deployed using the [Nava Platform Infrastructure Template](https://github.com/navapbc/template-infra). Using the infrastructure template will handle creating and configuring all of the resources required in AWS.

While following the [infrastructure template installation instructions](https://github.com/navapbc/template-infra?tab=readme-ov-file#installation) and [setup instructions](https://github.com/navapbc/template-infra/blob/main/infra/README.md), use the following configuration:

1. Rename `/infra/app` to `/infra/<APP_NAME>` so that it matches the directory name of your application. By default, this is `app-rails`.
1. In `/infra/<APP_NAME>/app-config/main.tf`:
1. Set `has_external_non_aws_service` to `true`.
2. Set `enable_identity_provider` to `true`.
1. In `/infra/<APP_NAME>/app-config/<ENVIRONMENT>.tf`:
1. Set the `domain_name`.
2. Set `enable_https` to `true`.
3. Set `enable_command_execution` to `true`: This is necessary temporarily until a temporary file system can be enabled. Otherwise, ECS will run with read-only root filesystem, which will cause rails to error.
1. In `/infra/<APP_NAME>/app-config/env-config/environment-variables.tf`:
1. Add an entry to `secrets`:
```terraform
SECRET_KEY_BASE = {
manage_method = "generated"
secret_store_name = "/${var.app_name}-${var.environment}/service/rails-secret-key-base"
}
```
1. In `/infra/networks/main.tf`:
1. Modify the `app_config` module to change the path to match the directory name of your application. By default, this is `app-rails`.
```terraform
module "app_config" {
source = "../<APP_NAME>/app-config"
}
```
1. Follow the infrastructure template instructions to configure [custom domains](https://github.com/navapbc/template-infra/blob/main/docs/infra/set-up-custom-domains.md) and [https support](https://github.com/navapbc/template-infra/blob/main/docs/infra/https-support.md).

## Deploying using another method

- AWS Cognito requires a lot of configuration to work correctly. See the Nava Platform infrastructure template for example configuration.
- If you are deploying using AWS ECS, but don't want to use the Platform infrastructure template, pass in environment variables and secrets using the ECS task definition. Use the `environment` key for environment variables and the `secrets` key with `valueFrom` for secrets.

0 comments on commit 9d6d8a4

Please sign in to comment.