mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-19 14:08:47 -04:00
Merge branch 'main' into aanand-sqlalchemy-version-update
This commit is contained in:
@@ -141,11 +141,10 @@ Terraform installations. This is great, but you still need to install Terraform
|
|||||||
itself, which can be done with this command:
|
itself, which can be done with this command:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
tfenv install latest:^1.4.0
|
tfenv install "latest:^1.7"
|
||||||
|
tfenv use 1.7.x # x = the patch version installed
|
||||||
```
|
```
|
||||||
|
|
||||||
_NOTE: This project currently uses the latest `1.4.x release of Terraform._
|
|
||||||
|
|
||||||
#### Python Installation
|
#### Python Installation
|
||||||
|
|
||||||
Now we're going to install a tool to help us manage Python versions and
|
Now we're going to install a tool to help us manage Python versions and
|
||||||
|
|||||||
@@ -20,13 +20,14 @@
|
|||||||
"Notify.gov makes it easy to keep people updated by helping you send text messages.",
|
"Notify.gov makes it easy to keep people updated by helping you send text messages.",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"Click this link to create an account on Notify.gov:",
|
|
||||||
"",
|
|
||||||
"[Join Service](((url)))",
|
"[Join Service](((url)))",
|
||||||
"If you’re new to Notify.gov you will first be directed to Login.gov create an account with us.",
|
"If you’re new to Notify.gov you will first be directed to Login.gov create an account with us.",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"This invitation will stop working at midnight tomorrow. This is to keep ((service_name)) secure."
|
"This invitation will stop working at midnight tomorrow. This is to keep ((service_name)) secure.",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"Notify.gov uses Login.gov to allow you to sign in safely. Login.gov is a secure sign in service to help you access participating government agencies' digital touchpoints."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -89,14 +89,6 @@ def invite_user_to_org(organization_id):
|
|||||||
ex=1800,
|
ex=1800,
|
||||||
)
|
)
|
||||||
|
|
||||||
# This is for the login.gov path, note 24 hour expiry to match
|
|
||||||
# The expiration of invitations.
|
|
||||||
redis_key = f"organization-invite-{invited_org_user.email_address}"
|
|
||||||
redis_store.set(
|
|
||||||
redis_key,
|
|
||||||
organization_id,
|
|
||||||
ex=3600 * 24,
|
|
||||||
)
|
|
||||||
send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY)
|
send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY)
|
||||||
|
|
||||||
return jsonify(data=invited_org_user.serialize()), 201
|
return jsonify(data=invited_org_user.serialize()), 201
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import base64
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -48,9 +49,25 @@ def _create_service_invite(invited_user, invite_link_host):
|
|||||||
current_app.config["SECRET_KEY"],
|
current_app.config["SECRET_KEY"],
|
||||||
current_app.config["DANGEROUS_SALT"],
|
current_app.config["DANGEROUS_SALT"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# The raw permissions are in the form "a,b,c,d"
|
||||||
|
# but need to be in the form ["a", "b", "c", "d"]
|
||||||
|
data = {}
|
||||||
|
permissions = invited_user.permissions
|
||||||
|
permissions = permissions.split(",")
|
||||||
|
data["from_user_id"] = (str(invited_user.from_user.id))
|
||||||
|
data["service_id"] = str(invited_user.service.id)
|
||||||
|
data["permissions"] = permissions
|
||||||
|
data["folder_permissions"] = invited_user.folder_permissions
|
||||||
|
data["invited_user_id"] = str(invited_user.id)
|
||||||
|
data["invited_user_email"] = invited_user.email_address
|
||||||
|
|
||||||
url = os.environ["LOGIN_DOT_GOV_REGISTRATION_URL"]
|
url = os.environ["LOGIN_DOT_GOV_REGISTRATION_URL"]
|
||||||
url = url.replace("NONCE", token)
|
url = url.replace("NONCE", token)
|
||||||
url = url.replace("STATE", token)
|
|
||||||
|
user_data_url_safe = get_user_data_url_safe(data)
|
||||||
|
|
||||||
|
url = url.replace("STATE", user_data_url_safe)
|
||||||
|
|
||||||
personalisation = {
|
personalisation = {
|
||||||
"user_name": invited_user.from_user.name,
|
"user_name": invited_user.from_user.name,
|
||||||
@@ -75,32 +92,6 @@ def _create_service_invite(invited_user, invite_link_host):
|
|||||||
json.dumps(personalisation),
|
json.dumps(personalisation),
|
||||||
ex=1800,
|
ex=1800,
|
||||||
)
|
)
|
||||||
# The raw permissions are in the form "a,b,c,d"
|
|
||||||
# but need to be in the form ["a", "b", "c", "d"]
|
|
||||||
data = {}
|
|
||||||
permissions = invited_user.permissions
|
|
||||||
permissions = permissions.split(",")
|
|
||||||
permission_list = []
|
|
||||||
for permission in permissions:
|
|
||||||
permission_list.append(f"{permission}")
|
|
||||||
data["from_user_id"] = (str(invited_user.from_user.id),)
|
|
||||||
data["service_id"] = str(invited_user.service.id)
|
|
||||||
data["permissions"] = permission_list
|
|
||||||
data["folder_permissions"] = invited_user.folder_permissions
|
|
||||||
|
|
||||||
# This is for the login.gov service invite on the
|
|
||||||
# "Set Up Your Profile" path.
|
|
||||||
redis_key = f"service-invite-{invited_user.email_address}"
|
|
||||||
redis_store.raw_set(
|
|
||||||
redis_key,
|
|
||||||
json.dumps(data),
|
|
||||||
ex=3600 * 24,
|
|
||||||
)
|
|
||||||
# TODO REMOVE DEBUG
|
|
||||||
print(hilite(f"Save this data {data} with this redis_key {redis_key}"))
|
|
||||||
did_we_save_it = redis_store.raw_get(redis_key)
|
|
||||||
print(hilite(f"Did we save the data successfully? {did_we_save_it}"))
|
|
||||||
# END DEBUG
|
|
||||||
send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY)
|
send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY)
|
||||||
|
|
||||||
|
|
||||||
@@ -223,3 +214,9 @@ def validate_service_invitation_token(token):
|
|||||||
|
|
||||||
invited_user = get_invited_user_by_id(invited_user_id)
|
invited_user = get_invited_user_by_id(invited_user_id)
|
||||||
return jsonify(data=invited_user_schema.dump(invited_user)), 200
|
return jsonify(data=invited_user_schema.dump(invited_user)), 200
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_data_url_safe(data):
|
||||||
|
data = json.dumps(data)
|
||||||
|
data = base64.b64encode(data.encode("utf8"))
|
||||||
|
return data.decode("utf8")
|
||||||
|
|||||||
972
poetry.lock
generated
972
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -34,21 +34,21 @@ flask-redis = "==0.4.0"
|
|||||||
flask-sqlalchemy = "==3.1.1"
|
flask-sqlalchemy = "==3.1.1"
|
||||||
gunicorn = {version = "==22.0.0", extras = ["eventlet"]}
|
gunicorn = {version = "==22.0.0", extras = ["eventlet"]}
|
||||||
iso8601 = "==2.1.0"
|
iso8601 = "==2.1.0"
|
||||||
jsonschema = {version = "==4.21.1", extras = ["format"]}
|
jsonschema = {version = "==4.22.0", extras = ["format"]}
|
||||||
lxml = "==5.2.1"
|
lxml = "==5.2.1"
|
||||||
marshmallow = "==3.21.1"
|
marshmallow = "==3.21.2"
|
||||||
marshmallow-sqlalchemy = "==1.0.0"
|
marshmallow-sqlalchemy = "==1.0.0"
|
||||||
newrelic = "*"
|
newrelic = "*"
|
||||||
notifications-python-client = "==9.0.0"
|
notifications-python-client = "==9.0.0"
|
||||||
notifications-utils = {git = "https://github.com/GSA/notifications-utils.git",rev = "d20efc2"}
|
notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"}
|
||||||
oscrypto = "==1.3.0"
|
oscrypto = "==1.3.0"
|
||||||
packaging = "==23.2"
|
packaging = "==23.2"
|
||||||
poetry-dotenv-plugin = "==0.2.0"
|
poetry-dotenv-plugin = "==0.2.0"
|
||||||
psycopg2-binary = "==2.9.9"
|
psycopg2-binary = "==2.9.9"
|
||||||
pyjwt = "==2.8.0"
|
pyjwt = "==2.8.0"
|
||||||
python-dotenv = "==1.0.0"
|
python-dotenv = "==1.0.0"
|
||||||
sqlalchemy = "==2.0.27"
|
sqlalchemy = "==2.0.29"
|
||||||
werkzeug = "^3.0.2"
|
werkzeug = "^3.0.3"
|
||||||
faker = "^24.4.0"
|
faker = "^24.4.0"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -89,17 +89,19 @@ These steps assume shared [Terraform state credentials](#terraform-state-credent
|
|||||||
|
|
||||||
1. Run `cf spaces` and, from the output, copy the space name for the environment you are working in, such as `notify-sandbox`.
|
1. Run `cf spaces` and, from the output, copy the space name for the environment you are working in, such as `notify-sandbox`.
|
||||||
|
|
||||||
1. Next you will set up a SpaceDeployer. Prepare to fill in these values:
|
1. Next you will set up a SpaceDeployer service account instance. This is something like a stub user account, just for deployment. Note these two values which you will use both to create and destroy the account:
|
||||||
* `<SPACE_NAME>` will be the string you copied from the prior step
|
1. `<SPACE_NAME>` will be the string you copied from the prior step
|
||||||
* `<ACCOUNT_NAME>` can be anything, although we recommend something that communicates the purpose of the deployer. For example: "circleci-deployer" for the credentials CircleCI uses to deploy the application, or "sandbox-<your_name>" for credentials to run terraform manually.
|
1. `<ACCOUNT_NAME>` can be anything, although we recommend something that communicates the purpose of the deployer. For example: "circleci-deployer" for the credentials CircleCI uses to deploy the application, or "sandbox-<your_name>" for credentials to run terraform manually.
|
||||||
|
|
||||||
Put those two values into this command:
|
Put those two values into this command:
|
||||||
```bash
|
```bash
|
||||||
./create_service_account.sh -s <SPACE_NAME> -u <ACCOUNT_NAME> > secrets.auto.tfvars
|
../create_service_account.sh -s <SPACE_NAME> -u <ACCOUNT_NAME> > secrets.auto.tfvars
|
||||||
```
|
```
|
||||||
|
|
||||||
The script will output the `username` (as `cf_user`) and `password` (as `cf_password`) for your `<ACCOUNT_NAME>`. The [cloud.gov service account documentation](https://cloud.gov/docs/services/cloud-gov-service-account/) has more information.
|
The script will output the `username` (as `cf_user`) and `password` (as `cf_password`) for your `<ACCOUNT_NAME>`. The [cloud.gov service account documentation](https://cloud.gov/docs/services/cloud-gov-service-account/) has more information.
|
||||||
|
|
||||||
|
Some resources you might work on require a SpaceDeployer account with higher permissions. Add the `-m` flag to the command to get this.
|
||||||
|
|
||||||
The command uses the redirection operator (`>`) to write that output to the `secrets.auto.tfvars` file. Terraform will find the username and password there, and use them as input variables.
|
The command uses the redirection operator (`>`) to write that output to the `secrets.auto.tfvars` file. Terraform will find the username and password there, and use them as input variables.
|
||||||
|
|
||||||
1. While still in an environment directory, initialize Terraform:
|
1. While still in an environment directory, initialize Terraform:
|
||||||
@@ -137,6 +139,8 @@ These steps assume shared [Terraform state credentials](#terraform-state-credent
|
|||||||
./destroy_service_account.sh -s <SPACE_NAME> -u <ACCOUNT_NAME>
|
./destroy_service_account.sh -s <SPACE_NAME> -u <ACCOUNT_NAME>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
List `cf services` if you are unsure which space deployer service instances still exist
|
||||||
|
|
||||||
Optionally, you can also `rm secrets.auto.tfvars`
|
Optionally, you can also `rm secrets.auto.tfvars`
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
@@ -195,3 +199,18 @@ You need to re-authenticate with the Cloud Foundry CLI
|
|||||||
cf login -a api.fr.cloud.gov --sso
|
cf login -a api.fr.cloud.gov --sso
|
||||||
```
|
```
|
||||||
You may also need to log in again to the Cloud.gov website.
|
You may also need to log in again to the Cloud.gov website.
|
||||||
|
|
||||||
|
### CF account not authorized
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: You are not authorized to perform the requested action
|
||||||
|
```
|
||||||
|
This error indicates that the Cloud Foundry user account (or service account) needs OrgManager permissions to take the action.
|
||||||
|
* When you create a SpaceDeployer service account, use the `-m` flag when running the `./create_service_account.sh` script
|
||||||
|
* Your own CF user may may also require OrgManager permissions to run the script
|
||||||
|
|
||||||
|
### Services limit
|
||||||
|
```
|
||||||
|
You have exceeded your organization's services limit.
|
||||||
|
```
|
||||||
|
Too many Cloud Foundry services have been created without being destroyed. Perhaps Terraform developers have forgotten to delete their SpaceDeployers after they finish with them. List `cf services` to see.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ locals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module "s3" {
|
module "s3" {
|
||||||
source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1"
|
source = "github.com/18f/terraform-cloudgov//s3?ref=v0.9.1"
|
||||||
|
|
||||||
cf_org_name = "gsa-tts-benefits-studio"
|
cf_org_name = "gsa-tts-benefits-studio"
|
||||||
cf_space_name = "notify-management"
|
cf_space_name = "notify-management"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ module "database" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module "redis" {
|
module "redis" {
|
||||||
source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1"
|
source = "github.com/18f/terraform-cloudgov//redis?ref=v0.9.1"
|
||||||
|
|
||||||
cf_org_name = local.cf_org_name
|
cf_org_name = local.cf_org_name
|
||||||
cf_space_name = local.cf_space_name
|
cf_space_name = local.cf_space_name
|
||||||
@@ -27,7 +27,7 @@ module "redis" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module "csv_upload_bucket" {
|
module "csv_upload_bucket" {
|
||||||
source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1"
|
source = "github.com/18f/terraform-cloudgov//s3?ref=v0.9.1"
|
||||||
|
|
||||||
cf_org_name = local.cf_org_name
|
cf_org_name = local.cf_org_name
|
||||||
cf_space_name = local.cf_space_name
|
cf_space_name = local.cf_space_name
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
terraform {
|
terraform {
|
||||||
required_version = "~> 1.0"
|
required_version = "~> 1.7"
|
||||||
required_providers {
|
required_providers {
|
||||||
cloudfoundry = {
|
cloudfoundry = {
|
||||||
source = "cloudfoundry-community/cloudfoundry"
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
version = "0.53.0"
|
version = "0.53.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user