Skip to content
Open
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
6 changes: 0 additions & 6 deletions .env

This file was deleted.

72 changes: 72 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Deploy MAAP STAC Auth

permissions:
id-token: write
contents: read

on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
inputs:
stage:
description: 'Deployment stage'
required: true
default: 'dev'
type: choice
options:
- dev
- test
aws_region:
description: 'AWS Region'
required: true
default: 'us-west-2'
type: choice
options:
- us-west-2
- us-east-1

jobs:
deploy:
runs-on: ubuntu-latest
env:
STAGE: ${{ inputs.stage || 'dev'}}
OWNER: ${{ github.actor }}
STAC_REGISTER_SERVICE_ID: ${{ vars.STAC_REGISTER_SERVICE_ID }}

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24

- name: Install AWS CDK CLI
run: npm install -g aws-cdk

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "latest"

- name: Install dependencies
run: uv sync

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ vars.DEPLOY_ROLE }}
aws-region: ${{ inputs.aws_region || vars.AWS_REGION }}

- name: CDK Synth
run: uv run cdk synth

- name: CDK Deploy
if: github.event_name == 'workflow_dispatch'
run: uv run cdk deploy --require-approval never --all
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Python
*.pyc
*.pyo
*.pyd
__pycache__/

# AWS CDK
cdk.out/

# Environment files
.env
.env.local
.env.*.local
16 changes: 16 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Configuration file for pre-commit (https://pre-commit.com/).
# Please run `pre-commit run --all-files` when adding or changing entries.

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.9
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.15.0
hooks:
- id: mypy
additional_dependencies:
- types-requests
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@ Note : Managing cognito users should be done via the console.

- `docker` is running
- the AWS CDK CLI is installed
- verify the configuration in `.env`.
- [`uv`](https://docs.astral.sh/uv/) is installed

Run :
- `cdk synth --all`
- `cdk deploy --all`
### Installation

```bash
uv sync
```

Locally Run Synthesis:
```bash
# Set environment variables for local testing
export STAGE=dev
export OWNER=myname
export STAC_REGISTER_SERVICE_ID=my-service-id

# Test the CDK synthesis locally
uv run cdk synth --all
```

## Cognito resources

Expand All @@ -23,8 +36,7 @@ Run :
This example script provides you with credentials based on service authentication.

```bash
python3 -m pip install -r requirements.txt
python3 scripts/service-auth-example.py
uv run scripts/service-auth-example.py
```

### Expanding
Expand Down
8 changes: 3 additions & 5 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
#!/usr/bin/env python3
import os

import aws_cdk as cdk

from config import Config
from infra.AuthStack import AuthStack
from infra.RolesStack import RolesStack
from config import Config

config = Config(_env_file=os.environ.get("ENV_FILE", ".env"))
config = Config()

app = cdk.App()
auth_stac = AuthStack(
Expand Down Expand Up @@ -57,7 +55,7 @@
"Owner": config.owner,
"Client": "NASA",
"Stack": config.stage,
}
},
)

app.synth()
31 changes: 15 additions & 16 deletions infra/AuthStack.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@
from enum import Enum
from typing import Any, Dict, Optional, Sequence


from aws_cdk import (
aws_iam as iam,
CfnOutput,
RemovalPolicy,
SecretValue,
Stack,
aws_cognito as cognito,
aws_cognito_identitypool_alpha as cognito_id_pool,
aws_iam as iam,
aws_s3 as s3,
aws_secretsmanager as secretsmanager,
aws_ssm as ssm,
CfnOutput,
custom_resources as cr,
RemovalPolicy,
SecretValue,
Stack,
)
from constructs import Construct

Expand Down Expand Up @@ -71,13 +70,12 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
)
CfnOutput(
self,
f"identitypool_client_id",
"identitypool_client_id",
export_name=f"{stack_name}-client-id",
value=auth_provider_client.user_pool_client_id,
)

def _create_userpool(self) -> cognito.UserPool:

return cognito.UserPool(
self,
"userpool",
Expand All @@ -96,7 +94,6 @@ def _create_identity_pool(
userpool: cognito.UserPool,
auth_provider_client: cognito.UserPoolClient,
) -> cognito_id_pool.IdentityPool:

userpool_provider = cognito_id_pool.UserPoolAuthenticationProvider(
user_pool=userpool,
user_pool_client=auth_provider_client,
Expand All @@ -114,7 +111,8 @@ def _create_identity_pool(
role_mappings=[
cognito_id_pool.IdentityPoolRoleMapping(
provider_url=cognito_id_pool.IdentityPoolProviderUrl.user_pool(
f"cognito-idp.{stack.region}.{stack.url_suffix}/{userpool.user_pool_id}:{auth_provider_client.user_pool_client_id}"
f"cognito-idp.{stack.region}.{stack.url_suffix}/"
f"{userpool.user_pool_id}:{auth_provider_client.user_pool_client_id}"
),
use_token=True,
mapping_key="userpool",
Expand All @@ -131,7 +129,9 @@ def _add_domain(self, userpool: cognito.UserPool) -> cognito.UserPoolDomain:

domain = userpool.add_domain(
"cognito-domain",
cognito_domain=cognito.CognitoDomainOptions(domain_prefix=stack_name.lower()),
cognito_domain=cognito.CognitoDomainOptions(
domain_prefix=stack_name.lower()
),
)

CfnOutput(
Expand All @@ -147,7 +147,6 @@ def _get_client_secret(
self,
client: cognito.UserPoolClient,
) -> str:

describe_cognito_user_pool_client = cr.AwsCustomResource(
self,
f"describe-{client.to_string()}",
Expand Down Expand Up @@ -251,7 +250,6 @@ def add_user_client(
name: Optional[str] = None,
replica_regions: Optional[Sequence[str]] = None,
) -> cognito.UserPoolClient:

client = self.userpool.add_client(
service_id,
auth_flows=cognito.AuthFlow(user_password=True),
Expand Down Expand Up @@ -310,7 +308,8 @@ def add_service_client(
"secret-parameter",
parameter_name=f"/{service_id}/secret",
string_value=secret.secret_arn,
description=f"ARN of the secret containing credentials for {service_id} service client",
description=f"ARN of the secret containing credentials for {service_id} "
"service client",
tier=ssm.ParameterTier.STANDARD,
)

Expand All @@ -332,7 +331,7 @@ def add_cognito_group(
description: str,
bucket_permissions: Dict[str, BucketPermissions],
) -> cognito.CfnUserPoolGroup:

identity_pool_id = self.identitypool.identity_pool_id
role = iam.Role(
self,
f"{group_name}_role",
Expand All @@ -341,7 +340,7 @@ def add_cognito_group(
assume_role_action="sts:AssumeRoleWithWebIdentity",
conditions={
"StringEquals": {
"cognito-identity.amazonaws.com:aud": self.identitypool.identity_pool_id
"cognito-identity.amazonaws.com:aud": identity_pool_id
}
},
),
Expand Down
62 changes: 31 additions & 31 deletions infra/RolesStack.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,54 @@
import boto3
from aws_cdk import (
aws_iam as iam,
CfnOutput,
Stack,
aws_iam as iam,
)
import boto3
from constructs import Construct

DATA_PIPELINE_LAMBDA_EXECUTION_ROLE_PATTERN = "maap-data-pipelines-*-datapipelinelambdarole*"
STAC_INGESTOR_EXECUTION_ROLE_PATTERN = 'stacingestor'
DATA_PIPELINE_LAMBDA_EXECUTION_ROLE_PATTERN = (
"maap-data-pipelines-*-datapipelinelambdarole*"
)
STAC_INGESTOR_EXECUTION_ROLE_PATTERN = "MAAP-STAC-*-pgSTAC-stacingestor*"


class RolesStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

self.create_data_access_role()

# export a cloud formation output with the data access role name and arn
CfnOutput(
self,
"data access role arn",
export_name=f"data-access-role-arn",
value=self.data_access_role.role_arn
export_name="data-access-role-arn",
value=self.data_access_role.role_arn,
)

def create_data_access_role(
self
):

def create_data_access_role(self) -> None:
"""
Creates data access role, attaches inline policy to allow access to s3 buckets, and grants assume role to data pipeline and stac ingestor roles
Creates data access role, attaches inline policy to allow access to s3 buckets,
and grants assume role to data pipeline and stac ingestor roles
"""

role = iam.Role(
self,
"data-access-role",
assumed_by=iam.ServicePrincipal("lambda.amazonaws.com")
assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"),
)



buckets = [
"nasa-maap-data-store",
"maap-user-shared-data",
"maap-ops-workspace",
"maap-data-store-test",
"nasa-maap-data-store/*",
"maap-user-shared-data/*",
"maap-ops-workspace/*",
"maap-data-store-test/*"
]
"nasa-maap-data-store",
"maap-user-shared-data",
"maap-ops-workspace",
"maap-data-store-test",
"nasa-maap-data-store/*",
"maap-user-shared-data/*",
"maap-ops-workspace/*",
"maap-data-store-test/*",
]

role.attach_inline_policy(
iam.Policy(
self,
Expand All @@ -61,7 +62,7 @@ def create_data_access_role(
],
)
)

account_id = boto3.client("sts").get_caller_identity().get("Account")

role.assume_role_policy.add_statements(
Expand All @@ -73,12 +74,11 @@ def create_data_access_role(
"StringLike": {
"aws:PrincipalArn": [
f"arn:aws:iam::{account_id}:role/{DATA_PIPELINE_LAMBDA_EXECUTION_ROLE_PATTERN}",
f"arn:aws:iam::{account_id}:role/{STAC_INGESTOR_EXECUTION_ROLE_PATTERN}"
f"arn:aws:iam::{account_id}:role/{STAC_INGESTOR_EXECUTION_ROLE_PATTERN}",
]
}
}
},
)
)

self.data_access_role = role

Loading