procurement-ingest/infra/shoc-assessment-reader/README.md
Adam Moussa 13efee9682
feat(infra): add kebab WO tables and PLAT-11 cutover tooling (PLAT-11) (#172)
* feat(infra): add kebab WO tables and PLAT-11 cutover tooling

Create empty work-orders/work-order-comments under Terraform while writers
stay on PascalCase; make emitter table classification env-driven and add
same-account migrate/verify plus cutover runbook.

* style(test): ruff-format shoc emitter envelope tests
2026-08-07 13:42:35 -04:00

4.8 KiB

shoc-assessment-dynamo-reader (TEMPORARY) — TORN DOWN

Status (2026-08-07): Role deleted in seahaven-prod as part of PLAT-11 prep (./teardown-assessment-reader.sh). Do not recreate for the kebab rename. SHOC steady-state access remains procurement-api + webhook only.


Original design (historical):

Time-boxed cross-account read role in seahaven-prod (011934824531) that lets the Luby team assess the procurement-ingest DynamoDB tables directly from seahaven-external-dev (396287094661) during their initial assessment.

This is not the steady-state access path. SHOC's durable integration is the procurement-api SigV4 read API plus the workorder-shoc-emitter webhook. This role exists only so the team can eyeball raw tables while scoping that integration, and it self-expires.

Item Value
Role arn:aws:iam::011934824531:role/shoc-assessment-dynamo-reader
Trusts arn:aws:iam::396287094661:root restricted by ArnLike aws:PrincipalArn to role/aws-reserved/sso.amazonaws.com/* — human Identity Center sessions in external-dev only (no EC2/service/created roles)
Hard expiry 2026-08-23T00:00:00Z — DateLessThan on aws:CurrentTime in BOTH the trust policy (new AssumeRole calls fail) and every permissions statement (in-flight sessions lose all data access at the same instant)
Actions dynamodb:GetItem, Query, Scan, DescribeTable
Tables purchase-orders, verified-sites, pending-site-review, WorkOrders, WorkOrderComments (+ index/* on each)
KMS kms:Decrypt on seahaven-dynamodb-cmk (be5fa4cb-c546-40fe-a13d-c7bec79f5d12), kms:ViaService-pinned to DynamoDB (the three po-ingest tables are CMK-encrypted); create script validates the key ARN against SSM /seahaven/dynamodb/cmk-arn before applying
Session 1h (chained sessions from SSO cap at 1h regardless)

Deliberate design choices (re-review before changing)

  • Root trust + aws:PrincipalArn condition, no ExternalId. The assessors are humans on Identity Center sessions in external-dev; console Switch-Role cannot pass an ExternalId, and there is no service deputy in this path. The ArnLike condition on the SSO role path restricts to human sessions without pinning a specific role ARN in Principal (role principals resolve to unique IDs and silently break when Identity Center reprovisions the permission set — a string condition survives that).
  • DescribeTable added beyond the requested GetItem/Query/Scan: the DynamoDB console item explorer and SDK table bindings require it (metadata-only).
  • Not in CDK, not a table resource policy. Table resource policies would re-open the cross-account grant surface deliberately zeroed on 2026-07-23 and trip tests/test_cross_account_principal_pin.py. A standalone role keeps the grant in one deletable object; the pin test's scope (cdk/) is intact.
  • Reads are not access-logged (no CloudTrail data events on these tables) — accepted for a 30-day read-only window.

Data handling (communicate to Luby with the grant)

Table contents are Sea Haven confidential and the work-order comment bodies are free text that may contain personal data. View/query for the assessment only; no bulk export or copies outside the AWS session; delete any local extracts when the assessment ends. The governing vendor agreement applies.

Emergency revocation

Fastest kill: run ./teardown-assessment-reader.sh. A successful DeleteRole immediately invalidates all outstanding session credentials — and the script strips every inline/attached policy first, so DeleteRole cannot fail on DeleteConflict (including the AWSRevokeOlderSessions inline policy the console's "Revoke active sessions" button attaches).

Usage (Luby side)

Console: Switch Role → account 011934824531, role shoc-assessment-dynamo-reader. Direct table deep-links: https://us-east-1.console.aws.amazon.com/dynamodbv2/home?region=us-east-1#item-explorer?table=<TableName>

CLI:

aws sts assume-role \
  --role-arn arn:aws:iam::011934824531:role/shoc-assessment-dynamo-reader \
  --role-session-name luby-assessment
aws dynamodb scan --table-name WorkOrders --max-items 25   # with the returned creds

Lifecycle

  • Create/update: ./create-assessment-reader.sh (gated on GPT-4.1 cross-family review + /sh-security-review of these exact files).
  • Remove: ./teardown-assessment-reader.sh when the assessment is done; the trust expiry is the backstop, not the plan.
  • Extend: edit the DateLessThan timestamp in both trust-policy.json AND every statement of permissions-policy.json (the expiry is enforced in both layers — extending only the trust policy yields a role that assumes but cannot read), then re-run both review gates before create-assessment-reader.sh.