diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..b862318 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,54 @@ +name: Deploy on LocalStack + +on: + push: + paths-ignore: + - 'README.md' + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + sam: + name: Deploy the SAM application + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install lstk + run: | + npm install -g @localstack/lstk + lstk setup aws --force --non-interactive + + - name: Install dependencies + run: make install + + - name: Start LocalStack + env: + LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} + run: lstk start + + - name: Deploy the SAM application + run: make deploy + + - name: Run tests + run: make test + + - name: Show LocalStack logs + if: failure() + run: lstk logs | tail -200 diff --git a/README.md b/README.md index 16cdf81..c654b7f 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ To setup the infrastructure on LocalStack, run: make deploy ``` -You will be prompted to enter a name for the stack. Use the default options for the prompts and fill `Y` (`Yes`) for the confirmation prompt. The stack will be created and the output will be printed to the console. +This deploys the stack `sam-app` without prompting. Set `STACK_NAME` to use another name, for example `make deploy STACK_NAME=my-stack`. The stack outputs are printed to the console. After deploying, you can invoke the Lambda function using the following commands: @@ -54,3 +54,9 @@ make invoke-deny make invoke-unauthorized make invoke-none ``` + +To check all four cases at once, run: + +```sh +make test +``` diff --git a/makefile b/makefile index b7207a2..a720389 100644 --- a/makefile +++ b/makefile @@ -4,6 +4,7 @@ export AWS_ACCESS_KEY_ID ?= test export AWS_SECRET_ACCESS_KEY ?= test export AWS_DEFAULT_REGION=us-east-1 +STACK_NAME ?= sam-app SHELL := /bin/bash usage: ## Show this help @@ -22,11 +23,11 @@ logs: ## Save the logs in a separate file install: npm install --save ulid -deploy: venv - $(VENV_RUN) && lstk sam deploy -g +deploy: venv ## Deploy the SAM application + $(VENV_RUN) && lstk sam deploy --stack-name $(STACK_NAME) --resolve-s3 --capabilities CAPABILITY_IAM --no-confirm-changeset invoke-url: venv - @$(VENV_RUN) && lstk sam list stack-outputs --stack-name sam-app --output json | jq -r '.[0].OutputValue' + @$(VENV_RUN) && lstk sam list stack-outputs --stack-name $(STACK_NAME) --output json | jq -r '.[0].OutputValue' invoke-allow: url=$$(make invoke-url) && \ @@ -43,3 +44,8 @@ invoke-unauthorized: invoke-none: url=$$(make invoke-url) && \ curl $${url} -H "Authorization: none" + +test: ## Call the API with each token and check the authorizer's decision + STACK_NAME=$(STACK_NAME) ./test.sh + +.PHONY: usage start stop logs install deploy invoke-url invoke-allow invoke-deny invoke-unauthorized invoke-none test diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..68ccf68 --- /dev/null +++ b/test.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Call the API with each token the Lambda authorizer understands and check +# API Gateway answers the way src/authorizer.js documents it. +set -euo pipefail + +STACK_NAME=${STACK_NAME:-sam-app} + +URL=$(lstk aws cloudformation describe-stacks --stack-name "$STACK_NAME" \ + --query "Stacks[0].Outputs[?OutputKey=='APIGWEndpoint'].OutputValue" --output text) +echo "API: $URL" + +failed=0 +# expect [curl args...] +expect() { + local description=$1 status=$2 substring=$3 response actual + shift 3 + response=$(curl -sk -m 30 -w '\n%{http_code}' "$URL" "$@") + actual=${response##*$'\n'} + response=${response%$'\n'*} + if [ "$actual" = "$status" ] && [[ "$response" == *"$substring"* ]]; then + echo "PASS: $description returned $actual" + else + echo "FAIL: $description returned $actual '$response', expected $status with '$substring'" + failed=1 + fi +} + +expect "token 'allow'" 200 "hello world" -H "Authorization: allow" +expect "token 'deny'" 403 "explicit deny" -H "Authorization: deny" +expect "token 'unauthorized'" 401 "Unauthorized" -H "Authorization: unauthorized" +expect "any other token" 500 "" -H "Authorization: none" +expect "no token" 401 "Unauthorized" + +if [ "$failed" != 0 ]; then + echo "Test failed" + exit 1 +fi +echo "Test passed: the authorizer allowed and denied requests as expected"