You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							416 lines
						
					
					
						
							15 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							416 lines
						
					
					
						
							15 KiB
						
					
					
				
								#!/usr/bin/env bash
							 | 
						|
								
							 | 
						|
								set -euo pipefail
							 | 
						|
								
							 | 
						|
								# Colors
							 | 
						|
								RED='\033[0;31m'
							 | 
						|
								GREEN='\033[0;32m'
							 | 
						|
								YELLOW='\033[1;33m'
							 | 
						|
								BLUE='\033[0;34m'
							 | 
						|
								NC='\033[0m'
							 | 
						|
								
							 | 
						|
								KEYCLOAK_IMAGE="quay.io/keycloak/keycloak:26.0.7"
							 | 
						|
								CONTAINER_NAME="keycloak-iam-test"
							 | 
						|
								KEYCLOAK_PORT="8080"  # Default external port
							 | 
						|
								KEYCLOAK_INTERNAL_PORT="8080"  # Internal container port (always 8080)
							 | 
						|
								KEYCLOAK_URL="http://localhost:${KEYCLOAK_PORT}"
							 | 
						|
								
							 | 
						|
								# Realm and test fixtures expected by tests
							 | 
						|
								REALM_NAME="seaweedfs-test"
							 | 
						|
								CLIENT_ID="seaweedfs-s3"
							 | 
						|
								CLIENT_SECRET="seaweedfs-s3-secret"
							 | 
						|
								ROLE_ADMIN="s3-admin"
							 | 
						|
								ROLE_READONLY="s3-read-only"
							 | 
						|
								ROLE_WRITEONLY="s3-write-only"
							 | 
						|
								ROLE_READWRITE="s3-read-write"
							 | 
						|
								
							 | 
						|
								# User credentials (matches Docker setup script logic: removes non-alphabetic chars + "123")
							 | 
						|
								get_user_password() {
							 | 
						|
								  case "$1" in
							 | 
						|
								    "admin-user") echo "adminuser123" ;;        # "admin-user" -> "adminuser123"
							 | 
						|
								    "read-user") echo "readuser123" ;;          # "read-user" -> "readuser123"
							 | 
						|
								    "write-user") echo "writeuser123" ;;        # "write-user" -> "writeuser123"
							 | 
						|
								    "write-only-user") echo "writeonlyuser123" ;;  # "write-only-user" -> "writeonlyuser123"
							 | 
						|
								    *) echo "" ;;
							 | 
						|
								  esac
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								# List of users to create
							 | 
						|
								USERS="admin-user read-user write-user write-only-user"
							 | 
						|
								
							 | 
						|
								echo -e "${BLUE}🔧 Setting up Keycloak realm and users for SeaweedFS S3 IAM testing...${NC}"
							 | 
						|
								
							 | 
						|
								ensure_container() {
							 | 
						|
								  # Check for any existing Keycloak container and detect its port
							 | 
						|
								  local keycloak_containers=$(docker ps --format '{{.Names}}\t{{.Ports}}' | grep -E "(keycloak|quay.io/keycloak)")
							 | 
						|
								  
							 | 
						|
								  if [[ -n "$keycloak_containers" ]]; then
							 | 
						|
								    # Parse the first available Keycloak container
							 | 
						|
								    CONTAINER_NAME=$(echo "$keycloak_containers" | head -1 | awk '{print $1}')
							 | 
						|
								    
							 | 
						|
								    # Extract the external port from the port mapping using sed (compatible with older bash)
							 | 
						|
								    local port_mapping=$(echo "$keycloak_containers" | head -1 | awk '{print $2}')
							 | 
						|
								    local extracted_port=$(echo "$port_mapping" | sed -n 's/.*:\([0-9]*\)->8080.*/\1/p')
							 | 
						|
								    if [[ -n "$extracted_port" ]]; then
							 | 
						|
								      KEYCLOAK_PORT="$extracted_port"
							 | 
						|
								      KEYCLOAK_URL="http://localhost:${KEYCLOAK_PORT}"
							 | 
						|
								      echo -e "${GREEN}[OK] Using existing container '${CONTAINER_NAME}' on port ${KEYCLOAK_PORT}${NC}"
							 | 
						|
								      return 0
							 | 
						|
								    fi
							 | 
						|
								  fi
							 | 
						|
								  
							 | 
						|
								  # Fallback: check for specific container names  
							 | 
						|
								  if docker ps --format '{{.Names}}' | grep -q '^keycloak$'; then
							 | 
						|
								    CONTAINER_NAME="keycloak"
							 | 
						|
								    # Try to detect port for 'keycloak' container using docker port command
							 | 
						|
								    local ports=$(docker port keycloak 8080 2>/dev/null | head -1)
							 | 
						|
								    if [[ -n "$ports" ]]; then
							 | 
						|
								      local extracted_port=$(echo "$ports" | sed -n 's/.*:\([0-9]*\)$/\1/p')
							 | 
						|
								      if [[ -n "$extracted_port" ]]; then
							 | 
						|
								        KEYCLOAK_PORT="$extracted_port"
							 | 
						|
								        KEYCLOAK_URL="http://localhost:${KEYCLOAK_PORT}"
							 | 
						|
								      fi
							 | 
						|
								    fi
							 | 
						|
								    echo -e "${GREEN}[OK] Using existing container '${CONTAINER_NAME}' on port ${KEYCLOAK_PORT}${NC}"
							 | 
						|
								    return 0
							 | 
						|
								  fi
							 | 
						|
								  if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
							 | 
						|
								    echo -e "${GREEN}[OK] Using existing container '${CONTAINER_NAME}'${NC}"
							 | 
						|
								    return 0
							 | 
						|
								  fi
							 | 
						|
								  echo -e "${YELLOW}🐳 Starting Keycloak container (${KEYCLOAK_IMAGE})...${NC}"
							 | 
						|
								  docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
							 | 
						|
								  docker run -d --name "${CONTAINER_NAME}" -p "${KEYCLOAK_PORT}:8080" \
							 | 
						|
								    -e KEYCLOAK_ADMIN=admin \
							 | 
						|
								    -e KEYCLOAK_ADMIN_PASSWORD=admin \
							 | 
						|
								    -e KC_HTTP_ENABLED=true \
							 | 
						|
								    -e KC_HOSTNAME_STRICT=false \
							 | 
						|
								    -e KC_HOSTNAME_STRICT_HTTPS=false \
							 | 
						|
								    -e KC_HEALTH_ENABLED=true \
							 | 
						|
								    "${KEYCLOAK_IMAGE}" start-dev >/dev/null
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								wait_ready() {
							 | 
						|
								  echo -e "${YELLOW}⏳ Waiting for Keycloak to be ready...${NC}"
							 | 
						|
								  for i in $(seq 1 120); do
							 | 
						|
								    if curl -sf "${KEYCLOAK_URL}/health/ready" >/dev/null; then
							 | 
						|
								      echo -e "${GREEN}[OK] Keycloak health check passed${NC}"
							 | 
						|
								      return 0
							 | 
						|
								    fi
							 | 
						|
								    if curl -sf "${KEYCLOAK_URL}/realms/master" >/dev/null; then
							 | 
						|
								      echo -e "${GREEN}[OK] Keycloak master realm accessible${NC}"
							 | 
						|
								      return 0
							 | 
						|
								    fi
							 | 
						|
								    sleep 2
							 | 
						|
								  done
							 | 
						|
								  echo -e "${RED}[FAIL] Keycloak did not become ready in time${NC}"
							 | 
						|
								  exit 1
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								kcadm() {
							 | 
						|
								  # Always authenticate before each command to ensure context
							 | 
						|
								  # Try different admin passwords that might be used in different environments
							 | 
						|
								  # GitHub Actions uses "admin", local testing might use "admin123"
							 | 
						|
								  local admin_passwords=("admin" "admin123" "password")
							 | 
						|
								  local auth_success=false
							 | 
						|
								  
							 | 
						|
								  for pwd in "${admin_passwords[@]}"; do
							 | 
						|
								    if docker exec -i "${CONTAINER_NAME}" /opt/keycloak/bin/kcadm.sh config credentials --server "http://localhost:${KEYCLOAK_INTERNAL_PORT}" --realm master --user admin --password "$pwd" >/dev/null 2>&1; then
							 | 
						|
								      auth_success=true
							 | 
						|
								      break
							 | 
						|
								    fi
							 | 
						|
								  done
							 | 
						|
								  
							 | 
						|
								  if [[ "$auth_success" == false ]]; then
							 | 
						|
								    echo -e "${RED}[FAIL] Failed to authenticate with any known admin password${NC}"
							 | 
						|
								    return 1
							 | 
						|
								  fi
							 | 
						|
								  
							 | 
						|
								  docker exec -i "${CONTAINER_NAME}" /opt/keycloak/bin/kcadm.sh "$@"
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								admin_login() {
							 | 
						|
								  # This is now handled by each kcadm() call  
							 | 
						|
								  echo "Logging into http://localhost:${KEYCLOAK_INTERNAL_PORT} as user admin of realm master"
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								ensure_realm() {
							 | 
						|
								  if kcadm get realms | grep -q "${REALM_NAME}"; then
							 | 
						|
								    echo -e "${GREEN}[OK] Realm '${REALM_NAME}' already exists${NC}"
							 | 
						|
								  else
							 | 
						|
								    echo -e "${YELLOW}📝 Creating realm '${REALM_NAME}'...${NC}"
							 | 
						|
								    if kcadm create realms -s realm="${REALM_NAME}" -s enabled=true 2>/dev/null; then
							 | 
						|
								    echo -e "${GREEN}[OK] Realm created${NC}"
							 | 
						|
								    else
							 | 
						|
								      # Check if it exists now (might have been created by another process)
							 | 
						|
								      if kcadm get realms | grep -q "${REALM_NAME}"; then
							 | 
						|
								        echo -e "${GREEN}[OK] Realm '${REALM_NAME}' already exists (created concurrently)${NC}"
							 | 
						|
								      else
							 | 
						|
								        echo -e "${RED}[FAIL] Failed to create realm '${REALM_NAME}'${NC}"
							 | 
						|
								        return 1
							 | 
						|
								      fi
							 | 
						|
								    fi
							 | 
						|
								  fi
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								ensure_client() {
							 | 
						|
								  local id
							 | 
						|
								  id=$(kcadm get clients -r "${REALM_NAME}" -q clientId="${CLIENT_ID}" | jq -r '.[0].id // empty')
							 | 
						|
								  if [[ -n "${id}" ]]; then
							 | 
						|
								    echo -e "${GREEN}[OK] Client '${CLIENT_ID}' already exists${NC}"
							 | 
						|
								  else
							 | 
						|
								    echo -e "${YELLOW}📝 Creating client '${CLIENT_ID}'...${NC}"
							 | 
						|
								    kcadm create clients -r "${REALM_NAME}" \
							 | 
						|
								      -s clientId="${CLIENT_ID}" \
							 | 
						|
								      -s protocol=openid-connect \
							 | 
						|
								      -s publicClient=false \
							 | 
						|
								      -s serviceAccountsEnabled=true \
							 | 
						|
								      -s directAccessGrantsEnabled=true \
							 | 
						|
								      -s standardFlowEnabled=true \
							 | 
						|
								      -s implicitFlowEnabled=false \
							 | 
						|
								      -s secret="${CLIENT_SECRET}" >/dev/null
							 | 
						|
								    echo -e "${GREEN}[OK] Client created${NC}"
							 | 
						|
								  fi
							 | 
						|
								  
							 | 
						|
								  # Create and configure role mapper for the client
							 | 
						|
								  configure_role_mapper "${CLIENT_ID}"
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								ensure_role() {
							 | 
						|
								  local role="$1"
							 | 
						|
								  if kcadm get roles -r "${REALM_NAME}" | jq -r '.[].name' | grep -qx "${role}"; then
							 | 
						|
								    echo -e "${GREEN}[OK] Role '${role}' exists${NC}"
							 | 
						|
								  else
							 | 
						|
								    echo -e "${YELLOW}📝 Creating role '${role}'...${NC}"
							 | 
						|
								    kcadm create roles -r "${REALM_NAME}" -s name="${role}" >/dev/null
							 | 
						|
								  fi
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								ensure_user() {
							 | 
						|
								  local username="$1" password="$2"
							 | 
						|
								  local uid
							 | 
						|
								  uid=$(kcadm get users -r "${REALM_NAME}" -q username="${username}" | jq -r '.[0].id // empty')
							 | 
						|
								  if [[ -z "${uid}" ]]; then
							 | 
						|
								    echo -e "${YELLOW}📝 Creating user '${username}'...${NC}"
							 | 
						|
								    uid=$(kcadm create users -r "${REALM_NAME}" \
							 | 
						|
								      -s username="${username}" \
							 | 
						|
								      -s enabled=true \
							 | 
						|
								      -s email="${username}@seaweedfs.test" \
							 | 
						|
								      -s emailVerified=true \
							 | 
						|
								      -s firstName="${username}" \
							 | 
						|
								      -s lastName="User" \
							 | 
						|
								      -i)
							 | 
						|
								  else
							 | 
						|
								    echo -e "${GREEN}[OK] User '${username}' exists${NC}"
							 | 
						|
								  fi
							 | 
						|
								  echo -e "${YELLOW}🔑 Setting password for '${username}'...${NC}"
							 | 
						|
								  kcadm set-password -r "${REALM_NAME}" --userid "${uid}" --new-password "${password}" --temporary=false >/dev/null
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								assign_role() {
							 | 
						|
								  local username="$1" role="$2"
							 | 
						|
								  local uid rid
							 | 
						|
								  uid=$(kcadm get users -r "${REALM_NAME}" -q username="${username}" | jq -r '.[0].id')
							 | 
						|
								  rid=$(kcadm get roles -r "${REALM_NAME}" | jq -r ".[] | select(.name==\"${role}\") | .id")
							 | 
						|
								  # Check if role already assigned
							 | 
						|
								  if kcadm get "users/${uid}/role-mappings/realm" -r "${REALM_NAME}" | jq -r '.[].name' | grep -qx "${role}"; then
							 | 
						|
								    echo -e "${GREEN}[OK] User '${username}' already has role '${role}'${NC}"
							 | 
						|
								    return 0
							 | 
						|
								  fi
							 | 
						|
								  echo -e "${YELLOW}➕ Assigning role '${role}' to '${username}'...${NC}"
							 | 
						|
								  kcadm add-roles -r "${REALM_NAME}" --uid "${uid}" --rolename "${role}" >/dev/null
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								configure_role_mapper() {
							 | 
						|
								  echo -e "${YELLOW}🔧 Configuring role mapper for client '${CLIENT_ID}'...${NC}"
							 | 
						|
								  
							 | 
						|
								  # Get client's internal ID
							 | 
						|
								  local internal_id
							 | 
						|
								  internal_id=$(kcadm get clients -r "${REALM_NAME}" -q clientId="${CLIENT_ID}" | jq -r '.[0].id // empty')
							 | 
						|
								  
							 | 
						|
								  if [[ -z "${internal_id}" ]]; then
							 | 
						|
								    echo -e "${RED}[FAIL] Could not find client ${client_id} to configure role mapper${NC}"
							 | 
						|
								    return 1
							 | 
						|
								  fi
							 | 
						|
								  
							 | 
						|
								  # Check if a realm roles mapper already exists for this client
							 | 
						|
								  local existing_mapper
							 | 
						|
								  existing_mapper=$(kcadm get "clients/${internal_id}/protocol-mappers/models" -r "${REALM_NAME}" | jq -r '.[] | select(.name=="realm roles" and .protocolMapper=="oidc-usermodel-realm-role-mapper") | .id // empty')
							 | 
						|
								  
							 | 
						|
								  if [[ -n "${existing_mapper}" ]]; then
							 | 
						|
								    echo -e "${GREEN}[OK] Realm roles mapper already exists${NC}"
							 | 
						|
								  else
							 | 
						|
								    echo -e "${YELLOW}📝 Creating realm roles mapper...${NC}"
							 | 
						|
								    
							 | 
						|
								    # Create protocol mapper for realm roles
							 | 
						|
								    kcadm create "clients/${internal_id}/protocol-mappers/models" -r "${REALM_NAME}" \
							 | 
						|
								      -s name="realm roles" \
							 | 
						|
								      -s protocol="openid-connect" \
							 | 
						|
								      -s protocolMapper="oidc-usermodel-realm-role-mapper" \
							 | 
						|
								      -s consentRequired=false \
							 | 
						|
								      -s 'config."multivalued"=true' \
							 | 
						|
								      -s 'config."userinfo.token.claim"=true' \
							 | 
						|
								      -s 'config."id.token.claim"=true' \
							 | 
						|
								      -s 'config."access.token.claim"=true' \
							 | 
						|
								      -s 'config."claim.name"=roles' \
							 | 
						|
								      -s 'config."jsonType.label"=String' >/dev/null || {
							 | 
						|
								        echo -e "${RED}[FAIL] Failed to create realm roles mapper${NC}"
							 | 
						|
								        return 1
							 | 
						|
								      }
							 | 
						|
								    
							 | 
						|
								    echo -e "${GREEN}[OK] Realm roles mapper created${NC}"
							 | 
						|
								  fi
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								configure_audience_mapper() {
							 | 
						|
								  echo -e "${YELLOW}🔧 Configuring audience mapper for client '${CLIENT_ID}'...${NC}"
							 | 
						|
								  
							 | 
						|
								  # Get client's internal ID
							 | 
						|
								  local internal_id
							 | 
						|
								  internal_id=$(kcadm get clients -r "${REALM_NAME}" -q clientId="${CLIENT_ID}" | jq -r '.[0].id // empty')
							 | 
						|
								  
							 | 
						|
								  if [[ -z "${internal_id}" ]]; then
							 | 
						|
								    echo -e "${RED}[FAIL] Could not find client ${CLIENT_ID} to configure audience mapper${NC}"
							 | 
						|
								    return 1
							 | 
						|
								  fi
							 | 
						|
								  
							 | 
						|
								  # Check if an audience mapper already exists for this client
							 | 
						|
								  local existing_mapper
							 | 
						|
								  existing_mapper=$(kcadm get "clients/${internal_id}/protocol-mappers/models" -r "${REALM_NAME}" | jq -r '.[] | select(.name=="audience-mapper" and .protocolMapper=="oidc-audience-mapper") | .id // empty')
							 | 
						|
								  
							 | 
						|
								  if [[ -n "${existing_mapper}" ]]; then
							 | 
						|
								    echo -e "${GREEN}[OK] Audience mapper already exists${NC}"
							 | 
						|
								  else
							 | 
						|
								    echo -e "${YELLOW}📝 Creating audience mapper...${NC}"
							 | 
						|
								    
							 | 
						|
								    # Create protocol mapper for audience
							 | 
						|
								    kcadm create "clients/${internal_id}/protocol-mappers/models" -r "${REALM_NAME}" \
							 | 
						|
								      -s name="audience-mapper" \
							 | 
						|
								      -s protocol="openid-connect" \
							 | 
						|
								      -s protocolMapper="oidc-audience-mapper" \
							 | 
						|
								      -s consentRequired=false \
							 | 
						|
								      -s 'config."included.client.audience"='"${CLIENT_ID}" \
							 | 
						|
								      -s 'config."id.token.claim"=false' \
							 | 
						|
								      -s 'config."access.token.claim"=true' >/dev/null || {
							 | 
						|
								        echo -e "${RED}[FAIL] Failed to create audience mapper${NC}"
							 | 
						|
								        return 1
							 | 
						|
								      }
							 | 
						|
								    
							 | 
						|
								    echo -e "${GREEN}[OK] Audience mapper created${NC}"
							 | 
						|
								  fi
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								main() {
							 | 
						|
								  command -v docker >/dev/null || { echo -e "${RED}[FAIL] Docker is required${NC}"; exit 1; }
							 | 
						|
								  command -v jq >/dev/null || { echo -e "${RED}[FAIL] jq is required${NC}"; exit 1; }
							 | 
						|
								
							 | 
						|
								  ensure_container
							 | 
						|
								  echo "Keycloak URL: ${KEYCLOAK_URL}"
							 | 
						|
								  wait_ready
							 | 
						|
								  admin_login
							 | 
						|
								  ensure_realm
							 | 
						|
								  ensure_client
							 | 
						|
								  configure_role_mapper
							 | 
						|
								  configure_audience_mapper
							 | 
						|
								  ensure_role "${ROLE_ADMIN}"
							 | 
						|
								  ensure_role "${ROLE_READONLY}"
							 | 
						|
								  ensure_role "${ROLE_WRITEONLY}"
							 | 
						|
								  ensure_role "${ROLE_READWRITE}"
							 | 
						|
								
							 | 
						|
								  for u in $USERS; do
							 | 
						|
								    ensure_user "$u" "$(get_user_password "$u")"
							 | 
						|
								  done
							 | 
						|
								
							 | 
						|
								  assign_role admin-user  "${ROLE_ADMIN}"
							 | 
						|
								  assign_role read-user   "${ROLE_READONLY}"
							 | 
						|
								  assign_role write-user  "${ROLE_READWRITE}"
							 | 
						|
								
							 | 
						|
								  # Also create a dedicated write-only user for testing  
							 | 
						|
								  ensure_user write-only-user "$(get_user_password write-only-user)"
							 | 
						|
								  assign_role write-only-user "${ROLE_WRITEONLY}"
							 | 
						|
								  
							 | 
						|
								  # Copy the appropriate IAM configuration for this environment
							 | 
						|
								  setup_iam_config
							 | 
						|
								
							 | 
						|
								  # Validate the setup by testing authentication and role inclusion
							 | 
						|
								  echo -e "${YELLOW}🔍 Validating setup by testing admin-user authentication and role mapping...${NC}"
							 | 
						|
								  sleep 2
							 | 
						|
								  
							 | 
						|
								  local validation_result=$(curl -s -w "%{http_code}" -X POST "http://localhost:${KEYCLOAK_PORT}/realms/${REALM_NAME}/protocol/openid-connect/token" \
							 | 
						|
								            -H "Content-Type: application/x-www-form-urlencoded" \
							 | 
						|
								            -d "grant_type=password" \
							 | 
						|
								    -d "client_id=${CLIENT_ID}" \
							 | 
						|
								    -d "client_secret=${CLIENT_SECRET}" \
							 | 
						|
								    -d "username=admin-user" \
							 | 
						|
								    -d "password=adminuser123" \
							 | 
						|
								    -d "scope=openid profile email" \
							 | 
						|
								    -o /tmp/auth_test_response.json)
							 | 
						|
								  
							 | 
						|
								  if [[ "${validation_result: -3}" == "200" ]]; then
							 | 
						|
								    echo -e "${GREEN}[OK] Authentication validation successful${NC}"
							 | 
						|
								    
							 | 
						|
								    # Extract and decode JWT token to check for roles
							 | 
						|
								    local access_token=$(cat /tmp/auth_test_response.json | jq -r '.access_token // empty')
							 | 
						|
								    if [[ -n "${access_token}" ]]; then
							 | 
						|
								      # Decode JWT payload (second part) and check for roles
							 | 
						|
								      local payload=$(echo "${access_token}" | cut -d'.' -f2)
							 | 
						|
								      # Add padding if needed for base64 decode
							 | 
						|
								      while [[ $((${#payload} % 4)) -ne 0 ]]; do
							 | 
						|
								        payload="${payload}="
							 | 
						|
								      done
							 | 
						|
								      
							 | 
						|
								      local decoded=$(echo "${payload}" | base64 -d 2>/dev/null || echo "{}")
							 | 
						|
								      local roles=$(echo "${decoded}" | jq -r '.roles // empty' 2>/dev/null || echo "")
							 | 
						|
								      
							 | 
						|
								      if [[ -n "${roles}" && "${roles}" != "null" ]]; then
							 | 
						|
								        echo -e "${GREEN}[OK] JWT token includes roles: ${roles}${NC}"
							 | 
						|
								      else
							 | 
						|
								        echo -e "${YELLOW}⚠️  JWT token does not include 'roles' claim${NC}"
							 | 
						|
								        echo -e "${YELLOW}Decoded payload sample:${NC}"
							 | 
						|
								        echo "${decoded}" | jq '.' 2>/dev/null || echo "${decoded}"
							 | 
						|
								      fi
							 | 
						|
								    fi
							 | 
						|
								  else
							 | 
						|
								    echo -e "${RED}[FAIL] Authentication validation failed with HTTP ${validation_result: -3}${NC}"
							 | 
						|
								    echo -e "${YELLOW}Response body:${NC}"
							 | 
						|
								    cat /tmp/auth_test_response.json 2>/dev/null || echo "No response body"
							 | 
						|
								    echo -e "${YELLOW}This may indicate a setup issue that needs to be resolved${NC}"
							 | 
						|
								  fi
							 | 
						|
								  rm -f /tmp/auth_test_response.json
							 | 
						|
								  
							 | 
						|
								  echo -e "${GREEN}[OK] Keycloak test realm '${REALM_NAME}' configured${NC}"
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								setup_iam_config() {
							 | 
						|
								  echo -e "${BLUE}🔧 Setting up IAM configuration for detected environment${NC}"
							 | 
						|
								  
							 | 
						|
								  # Change to script directory to ensure config files are found
							 | 
						|
								  local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
							 | 
						|
								  cd "$script_dir"
							 | 
						|
								  
							 | 
						|
								  # Choose the appropriate config based on detected port
							 | 
						|
								  local config_source
							 | 
						|
								  if [[ "${KEYCLOAK_PORT}" == "8080" ]]; then
							 | 
						|
								    config_source="iam_config.github.json"
							 | 
						|
								    echo "   Using GitHub Actions configuration (port 8080)"
							 | 
						|
								  else
							 | 
						|
								    config_source="iam_config.local.json" 
							 | 
						|
								    echo "   Using local development configuration (port ${KEYCLOAK_PORT})"
							 | 
						|
								  fi
							 | 
						|
								  
							 | 
						|
								  # Verify source config exists
							 | 
						|
								  if [[ ! -f "$config_source" ]]; then
							 | 
						|
								    echo -e "${RED}[FAIL] Config file $config_source not found in $script_dir${NC}"
							 | 
						|
								    exit 1
							 | 
						|
								  fi
							 | 
						|
								  
							 | 
						|
								  # Copy the appropriate config
							 | 
						|
								  cp "$config_source" "iam_config.json"
							 | 
						|
								  
							 | 
						|
								  local detected_issuer=$(cat iam_config.json | jq -r '.providers[] | select(.name=="keycloak") | .config.issuer')
							 | 
						|
								  echo -e "${GREEN}[OK] IAM configuration set successfully${NC}"
							 | 
						|
								  echo "   - Using config: $config_source"
							 | 
						|
								  echo "   - Keycloak issuer: $detected_issuer"
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								main "$@"
							 |