- Create QUICK_REFERENCE.md for a concise guide on setting up temporal authorization. - Add README_AUTHORIZATION.md detailing the implementation steps and common issues. - Introduce REVERSE_PROXY_APPROACH.md as an alternative method for authorization using a reverse proxy. - Implement Dockerfile for building a custom Temporal server with authorization features. - Add main.go to initialize the custom Temporal server with JWT authorization. - Create example-keycloak-mapper.json for mapping Keycloak groups to Temporal permissions. - Add development.yaml for configuring the Temporal server with JWT settings. - Implement test-authorization.sh script to verify JWT token claims and Temporal server access. - Include go.mod for managing Go dependencies in the custom server. - Document troubleshooting steps and customization options in README.md.
245 lines
7.4 KiB
Bash
Executable File
245 lines
7.4 KiB
Bash
Executable File
#!/bin/bash
|
||
# Test script to verify Temporal authorization setup
|
||
|
||
set -e
|
||
|
||
echo "=================================="
|
||
echo "Temporal Authorization Test Script"
|
||
echo "=================================="
|
||
echo ""
|
||
|
||
# Colors for output
|
||
GREEN='\033[0;32m'
|
||
RED='\033[0;31m'
|
||
YELLOW='\033[1;33m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Function to print colored output
|
||
print_success() {
|
||
echo -e "${GREEN}✓ $1${NC}"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}✗ $1${NC}"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}⚠ $1${NC}"
|
||
}
|
||
|
||
print_info() {
|
||
echo "ℹ $1"
|
||
}
|
||
|
||
# Check if required tools are installed
|
||
echo "Checking prerequisites..."
|
||
command -v jq >/dev/null 2>&1 || { print_error "jq is not installed. Install with: apt-get install jq"; exit 1; }
|
||
command -v curl >/dev/null 2>&1 || { print_error "curl is not installed."; exit 1; }
|
||
print_success "Prerequisites OK"
|
||
echo ""
|
||
|
||
# Configuration
|
||
read -p "Enter Keycloak URL (e.g., https://keycloak.example.com): " KEYCLOAK_URL
|
||
read -p "Enter Realm name: " REALM
|
||
read -p "Enter Client ID (e.g., temporal-ui): " CLIENT_ID
|
||
read -sp "Enter Client Secret: " CLIENT_SECRET
|
||
echo ""
|
||
read -p "Enter test username: " USERNAME
|
||
read -sp "Enter test password: " PASSWORD
|
||
echo ""
|
||
echo ""
|
||
|
||
# Get token from Keycloak
|
||
print_info "Requesting token from Keycloak..."
|
||
TOKEN_ENDPOINT="${KEYCLOAK_URL}/realms/${REALM}/protocol/openid-connect/token"
|
||
|
||
RESPONSE=$(curl -s -X POST "$TOKEN_ENDPOINT" \
|
||
-d "client_id=${CLIENT_ID}" \
|
||
-d "client_secret=${CLIENT_SECRET}" \
|
||
-d "grant_type=password" \
|
||
-d "username=${USERNAME}" \
|
||
-d "password=${PASSWORD}")
|
||
|
||
if [ $? -ne 0 ]; then
|
||
print_error "Failed to connect to Keycloak"
|
||
exit 1
|
||
fi
|
||
|
||
# Check for errors
|
||
if echo "$RESPONSE" | jq -e '.error' >/dev/null 2>&1; then
|
||
print_error "Authentication failed:"
|
||
echo "$RESPONSE" | jq '.error_description'
|
||
exit 1
|
||
fi
|
||
|
||
# Extract access token
|
||
ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r '.access_token')
|
||
|
||
if [ "$ACCESS_TOKEN" == "null" ] || [ -z "$ACCESS_TOKEN" ]; then
|
||
print_error "Failed to get access token"
|
||
echo "$RESPONSE"
|
||
exit 1
|
||
fi
|
||
|
||
print_success "Successfully obtained access token"
|
||
echo ""
|
||
|
||
# Decode and display JWT
|
||
print_info "Decoding JWT token..."
|
||
echo ""
|
||
|
||
# Split token into header, payload, signature
|
||
IFS='.' read -ra TOKEN_PARTS <<< "$ACCESS_TOKEN"
|
||
|
||
# Decode header
|
||
HEADER=$(echo "${TOKEN_PARTS[0]}" | base64 -d 2>/dev/null || echo "${TOKEN_PARTS[0]}" | base64 -d -i 2>/dev/null)
|
||
echo "JWT Header:"
|
||
echo "$HEADER" | jq '.' 2>/dev/null || echo "$HEADER"
|
||
echo ""
|
||
|
||
# Decode payload
|
||
PAYLOAD=$(echo "${TOKEN_PARTS[1]}" | base64 -d 2>/dev/null || echo "${TOKEN_PARTS[1]}" | base64 -d -i 2>/dev/null)
|
||
echo "JWT Payload:"
|
||
echo "$PAYLOAD" | jq '.' 2>/dev/null || echo "$PAYLOAD"
|
||
echo ""
|
||
|
||
# Extract relevant claims
|
||
print_info "Checking for Temporal authorization claims..."
|
||
echo ""
|
||
|
||
# Check for permissions claim
|
||
PERMISSIONS=$(echo "$PAYLOAD" | jq -r '.permissions // empty' 2>/dev/null)
|
||
if [ ! -z "$PERMISSIONS" ] && [ "$PERMISSIONS" != "null" ]; then
|
||
print_success "Found 'permissions' claim"
|
||
echo "Permissions:"
|
||
echo "$PAYLOAD" | jq '.permissions'
|
||
echo ""
|
||
else
|
||
print_warning "No 'permissions' claim found in JWT"
|
||
echo "Temporal requires a 'permissions' array in the JWT token."
|
||
echo "Each permission should be in the format: 'namespace:role'"
|
||
echo ""
|
||
fi
|
||
|
||
# Check for groups claim
|
||
GROUPS=$(echo "$PAYLOAD" | jq -r '.groups // empty' 2>/dev/null)
|
||
if [ ! -z "$GROUPS" ] && [ "$GROUPS" != "null" ]; then
|
||
print_info "Found 'groups' claim:"
|
||
echo "$PAYLOAD" | jq '.groups'
|
||
echo ""
|
||
else
|
||
print_warning "No 'groups' claim found"
|
||
echo ""
|
||
fi
|
||
|
||
# Check for roles claim
|
||
ROLES=$(echo "$PAYLOAD" | jq -r '.roles // empty' 2>/dev/null)
|
||
if [ ! -z "$ROLES" ] && [ "$ROLES" != "null" ]; then
|
||
print_info "Found 'roles' claim:"
|
||
echo "$PAYLOAD" | jq '.roles'
|
||
echo ""
|
||
else
|
||
print_warning "No 'roles' claim found"
|
||
echo ""
|
||
fi
|
||
|
||
# Check token expiration
|
||
EXP=$(echo "$PAYLOAD" | jq -r '.exp // empty' 2>/dev/null)
|
||
if [ ! -z "$EXP" ]; then
|
||
CURRENT_TIME=$(date +%s)
|
||
TIME_LEFT=$((EXP - CURRENT_TIME))
|
||
if [ $TIME_LEFT -gt 0 ]; then
|
||
print_success "Token is valid (expires in $((TIME_LEFT / 60)) minutes)"
|
||
else
|
||
print_error "Token has expired!"
|
||
fi
|
||
echo ""
|
||
fi
|
||
|
||
# Check issuer
|
||
ISSUER=$(echo "$PAYLOAD" | jq -r '.iss // empty' 2>/dev/null)
|
||
if [ ! -z "$ISSUER" ]; then
|
||
print_info "Token issuer: $ISSUER"
|
||
echo ""
|
||
fi
|
||
|
||
# Verify JWKS endpoint is accessible
|
||
print_info "Verifying JWKS endpoint..."
|
||
JWKS_URL="${ISSUER}/protocol/openid-connect/certs"
|
||
JWKS_RESPONSE=$(curl -s "$JWKS_URL")
|
||
if echo "$JWKS_RESPONSE" | jq -e '.keys' >/dev/null 2>&1; then
|
||
KEY_COUNT=$(echo "$JWKS_RESPONSE" | jq '.keys | length')
|
||
print_success "JWKS endpoint accessible ($KEY_COUNT keys available)"
|
||
echo "JWKS URL: $JWKS_URL"
|
||
else
|
||
print_error "JWKS endpoint not accessible or invalid response"
|
||
echo "URL: $JWKS_URL"
|
||
echo "Response: $JWKS_RESPONSE"
|
||
fi
|
||
echo ""
|
||
|
||
# Test Temporal Server connection (optional)
|
||
read -p "Do you want to test connection to Temporal Server? (y/n): " TEST_TEMPORAL
|
||
if [ "$TEST_TEMPORAL" == "y" ]; then
|
||
read -p "Enter Temporal Server address (e.g., localhost:7233): " TEMPORAL_ADDR
|
||
|
||
print_info "Testing Temporal Server connection..."
|
||
|
||
# Try to list namespaces with the token
|
||
# Note: This requires grpcurl to be installed
|
||
if command -v grpcurl >/dev/null 2>&1; then
|
||
RESULT=$(grpcurl -plaintext \
|
||
-H "authorization: Bearer $ACCESS_TOKEN" \
|
||
-d '{"pageSize": 10}' \
|
||
"$TEMPORAL_ADDR" \
|
||
temporal.api.workflowservice.v1.WorkflowService/ListNamespaces 2>&1)
|
||
|
||
if echo "$RESULT" | grep -q "PermissionDenied"; then
|
||
print_warning "Server rejected request (PermissionDenied)"
|
||
echo "This likely means authorization is working but the user lacks permissions."
|
||
echo "Check that the 'permissions' claim in the JWT matches your Temporal namespaces."
|
||
elif echo "$RESULT" | grep -q "namespaces"; then
|
||
print_success "Successfully connected to Temporal Server!"
|
||
echo "User has access to namespaces."
|
||
else
|
||
print_error "Unexpected response from Temporal Server:"
|
||
echo "$RESULT"
|
||
fi
|
||
else
|
||
print_warning "grpcurl not installed, skipping Temporal Server test"
|
||
echo "Install grpcurl to test server connection: https://github.com/fullstorydev/grpcurl"
|
||
fi
|
||
fi
|
||
echo ""
|
||
|
||
# Summary
|
||
echo "=================================="
|
||
echo "Summary"
|
||
echo "=================================="
|
||
echo ""
|
||
|
||
if [ ! -z "$PERMISSIONS" ] && [ "$PERMISSIONS" != "null" ]; then
|
||
print_success "JWT token contains 'permissions' claim - ready for Temporal authorization!"
|
||
echo ""
|
||
echo "Next steps:"
|
||
echo "1. Verify the permissions match your Temporal namespace names"
|
||
echo "2. Deploy the custom Temporal server with authorization enabled"
|
||
echo "3. Test access with different user groups"
|
||
else
|
||
print_warning "JWT token does NOT contain 'permissions' claim"
|
||
echo ""
|
||
echo "Next steps:"
|
||
echo "1. Configure Keycloak protocol mapper to add 'permissions' claim"
|
||
echo "2. Map user groups/roles to permissions in format: 'namespace:role'"
|
||
echo "3. Re-run this test script to verify"
|
||
echo ""
|
||
echo "See: custom-server/example-keycloak-mapper.json for an example"
|
||
fi
|
||
|
||
echo ""
|
||
echo "=================================="
|
||
echo "Full JWT Token (for debugging):"
|
||
echo "=================================="
|
||
echo "$ACCESS_TOKEN"
|
||
echo ""
|