Skip to content
1,329 changes: 1,329 additions & 0 deletions scripts/thunder/01-default-resources.sh

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions scripts/thunder/02-sample-resources.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
echo "Overwriting the default sample resources creation script with empty content..."
35 changes: 18 additions & 17 deletions scripts/utils/thunder-auth.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# Usage:
# source "$(dirname "$0")/../utils/thunder-auth.sh"
# thunder_authenticate "$THUNDER_HOST" "$THUNDER_PORT"
# # Now you can use: $SAMPLE_APP_ID, $BEARER_TOKEN
# # Now you can use: $DEVELOP_APP_ID, $BEARER_TOKEN
#
# thunder_get_org_unit "$THUNDER_HOST" "$THUNDER_PORT" "$BEARER_TOKEN" "silver"
# # Now you can use: $ORG_UNIT_ID
Expand All @@ -24,19 +24,20 @@ RED="\033[0;31m"
NC="\033[0m" # No Color

# ============================================
# Function: Extract Sample App ID from Thunder setup logs
# Function: Extract DEVELOP App ID from Thunder setup logs
# ============================================
thunder_get_sample_app_id() {
local sample_app_id
sample_app_id=$(docker logs thunder-setup 2>&1 | grep 'Sample App ID:' | head -n1 | grep -o '[a-f0-9-]\{36\}')
thunder_get_develop_app_id() {
local develop_app_id
# Look for DEVELOP_APP_ID in logs (handles both "DEVELOP_APP_ID:" and "[INFO] DEVELOP_APP_ID:" formats)
develop_app_id=$(docker logs thunder-setup 2>&1 | grep -i 'DEVELOP_APP_ID' | head -n1 | grep -o '[a-f0-9-]\{36\}')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regex used to extract the DEVELOP_APP_ID is a bit loose ([a-f0-9-]\{36\}). For better robustness and consistency with the Go code in this PR, I recommend using a more specific UUID regex.

Suggested change
develop_app_id=$(docker logs thunder-setup 2>&1 | grep -i 'DEVELOP_APP_ID' | head -n1 | grep -o '[a-f0-9-]\{36\}')
develop_app_id=$(docker logs thunder-setup 2>&1 | grep -i 'DEVELOP_APP_ID' | head -n1 | grep -o '[a-f0-9]\{8\}-[a-f0-9]\{4\}-[a-f0-9]\{4\}-[a-f0-9]\{4\}-[a-f0-9]\{12\}')


if [ -z "$sample_app_id" ]; then
echo -e "${RED}βœ— Failed to extract Sample App ID from Thunder setup logs${NC}" >&2
if [ -z "$develop_app_id" ]; then
echo -e "${RED}βœ— Failed to extract DEVELOP_APP_ID from Thunder setup logs${NC}" >&2
echo "Please ensure Thunder setup container has completed successfully." >&2
return 1
fi

echo "$sample_app_id"
echo "$develop_app_id"
return 0
}

Expand All @@ -49,7 +50,7 @@ thunder_get_sample_app_id() {
# Returns:
# 0 on success, 1 on failure
# Exports:
# SAMPLE_APP_ID - The application ID extracted from logs
# DEVELOP_APP_ID - The application ID extracted from logs
# BEARER_TOKEN - The authentication token
# ============================================
thunder_authenticate() {
Expand All @@ -63,24 +64,24 @@ thunder_authenticate() {

echo -e "${YELLOW}Authenticating with Thunder...${NC}"

# Step 1: Extract Sample App ID
echo " - Extracting Sample App ID from Thunder setup logs..."
SAMPLE_APP_ID=$(thunder_get_sample_app_id)
# Step 1: Extract DEVELOP App ID
echo " - Extracting DEVELOP_APP_ID from Thunder setup logs..."
DEVELOP_APP_ID=$(thunder_get_develop_app_id)

if [ $? -ne 0 ] || [ -z "$SAMPLE_APP_ID" ]; then
echo -e "${RED}βœ— Failed to extract Sample App ID${NC}" >&2
if [ $? -ne 0 ] || [ -z "$DEVELOP_APP_ID" ]; then
echo -e "${RED}βœ— Failed to extract DEVELOP_APP_ID${NC}" >&2
return 1
fi

echo -e "${GREEN} βœ“ Sample App ID extracted: $SAMPLE_APP_ID${NC}"
echo -e "${GREEN} βœ“ DEVELOP_APP_ID extracted: $DEVELOP_APP_ID${NC}"

# Step 2: Start authentication flow (get flowId)
echo " - Starting authentication flow..."
local start_response
start_response=$(curl -s -w "\n%{http_code}" -X POST \
"https://${thunder_host}:${thunder_port}/flow/execute" \
-H "Content-Type: application/json" \
-d "{\"applicationId\":\"${SAMPLE_APP_ID}\",\"flowType\":\"AUTHENTICATION\"}")
-d "{\"applicationId\":\"${DEVELOP_APP_ID}\",\"flowType\":\"AUTHENTICATION\"}")

local start_body
local start_status
Expand Down Expand Up @@ -132,7 +133,7 @@ thunder_authenticate() {
echo -e "${GREEN} βœ“ Authentication successful${NC}"

# Export variables for use in calling script
export SAMPLE_APP_ID
export DEVELOP_APP_ID
export FLOW_ID
export BEARER_TOKEN

Expand Down
4 changes: 4 additions & 0 deletions services/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Rspamd web UI password
RSPAMD_PASSWORD=your_secure_password

# Thunder admin credentials (used during initial setup)
THUNDER_ADMIN_USERNAME=admin
THUNDER_ADMIN_PASSWORD=admin

# Public IP of this mail server
MAIL_SERVER_IP=

Expand Down
18 changes: 14 additions & 4 deletions services/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,16 @@ services:
environment:
- SOCKETMAP_HOST=0.0.0.0
- SOCKETMAP_PORT=9100
# THUNDER_SAMPLE_APP_ID not required - auto-extracted from thunder-setup logs
# THUNDER_DEVELOP_APP_ID not required - auto-extracted from thunder-setup logs
- THUNDER_HOST=thunder-server
- THUNDER_PORT=8090
- CACHE_TTL_SECONDS=300
- TOKEN_REFRESH_SECONDS=3300
depends_on:
thunder-setup:
condition: service_completed_successfully
thunder:
condition: service_started
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "9100"]
interval: 10s
Expand Down Expand Up @@ -175,7 +180,7 @@ services:

# Initialize database from the image
thunder-db-init:
image: ghcr.io/asgardeo/thunder:0.22.0
image: ghcr.io/asgardeo/thunder:0.24.0
container_name: thunder-db-init
command: sh -c "cp -r /opt/thunder/repository/database/* /data/"
volumes:
Expand All @@ -184,20 +189,25 @@ services:

# Run setup once with the shared database
thunder-setup:
image: ghcr.io/asgardeo/thunder:0.22.0
image: ghcr.io/asgardeo/thunder:0.24.0
container_name: thunder-setup
command: ./setup.sh
volumes:
- thunder-db:/opt/thunder/repository/database
- ./silver-config/thunder/deployment.yaml:/opt/thunder/repository/conf/deployment.yaml:ro
- ./../scripts/thunder/01-default-resources.sh:/opt/thunder/bootstrap/01-default-resources.sh
- ./../scripts/thunder/02-sample-resources.sh:/opt/thunder/bootstrap/02-sample-resources.sh
environment:
- THUNDER_ADMIN_USERNAME=${THUNDER_ADMIN_USERNAME:-admin}
- THUNDER_ADMIN_PASSWORD=${THUNDER_ADMIN_PASSWORD:-admin}
depends_on:
thunder-db-init:
condition: service_completed_successfully
restart: "no"

# Run Thunder server with the shared database
thunder:
image: ghcr.io/asgardeo/thunder:0.22.0
image: ghcr.io/asgardeo/thunder:0.24.0
container_name: thunder-server
depends_on:
thunder-setup:
Expand Down
42 changes: 22 additions & 20 deletions services/socketmap/internal/thunder/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ var (
thunderAuthMutex sync.RWMutex
)

// getSampleAppIDFromThunderSetup extracts Sample App ID from thunder-setup container
func getSampleAppIDFromThunderSetup() (string, error) {
log.Printf(" β”‚ Extracting Sample App ID from thunder-setup container...")
// getDevelopAppIDFromThunderSetup extracts DEVELOP App ID from thunder-setup container
func getDevelopAppIDFromThunderSetup() (string, error) {
log.Printf(" β”‚ Extracting DEVELOP_APP_ID from thunder-setup container...")

// Execute: docker logs thunder-setup 2>&1 | grep 'Sample App ID:' | head -n1
// Execute: docker logs thunder-setup 2>&1 | grep 'DEVELOP_APP_ID:' | head -n1
cmd := exec.Command("docker", "logs", "thunder-setup")
output, err := cmd.CombinedOutput()
if err != nil {
Expand All @@ -38,52 +38,54 @@ func getSampleAppIDFromThunderSetup() (string, error) {
log.Printf(" β”‚ - Running inside a container without Docker socket")
}

// Search for "Sample App ID:" in logs (case-insensitive)
// Search for "DEVELOP_APP_ID:" in logs
// Log format: [INFO] DEVELOP_APP_ID: 019cdc47-3537-74ee-951e-3f50e48786ab
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "Sample App ID:") {
// Look for line containing DEVELOP_APP_ID (case-insensitive)
if strings.Contains(line, "DEVELOP_APP_ID") || strings.Contains(line, "develop_app_id") {
// Extract UUID pattern: [a-f0-9-]{36}
re := regexp.MustCompile(`[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`)
match := re.FindString(line)
if match != "" {
log.Printf(" β”‚ βœ“ Sample App ID extracted: %s", match)
log.Printf(" β”‚ βœ“ DEVELOP_APP_ID extracted: %s", match)
return match, nil
}
}
}

return "", fmt.Errorf("Sample App ID not found in thunder-setup logs")
return "", fmt.Errorf("DEVELOP_APP_ID not found in thunder-setup logs")
}

// Authenticate performs the full authentication flow with Thunder IDP
func Authenticate(host, port string, tokenRefreshSeconds int) (*Auth, error) {
log.Printf(" β”Œβ”€ Thunder Authentication ─────────")

// Step 1: Get Sample App ID
sampleAppID := os.Getenv("THUNDER_SAMPLE_APP_ID")
// Step 1: Get DEVELOP App ID
developAppID := os.Getenv("THUNDER_DEVELOP_APP_ID")

if sampleAppID != "" {
log.Printf(" β”‚ Using Sample App ID from environment variable")
log.Printf(" β”‚ Sample App ID: %s", sampleAppID)
if developAppID != "" {
log.Printf(" β”‚ Using DEVELOP App ID from environment variable")
log.Printf(" β”‚ DEVELOP_APP_ID: %s", developAppID)
} else {
log.Printf(" β”‚ THUNDER_SAMPLE_APP_ID not set")
log.Printf(" β”‚ THUNDER_DEVELOP_APP_ID not set")
log.Printf(" β”‚ Attempting to extract from thunder-setup container logs...")

var err error
sampleAppID, err = getSampleAppIDFromThunderSetup()
developAppID, err = getDevelopAppIDFromThunderSetup()
if err != nil {
log.Printf(" β”‚ βœ— Failed to extract Sample App ID: %v", err)
log.Printf(" β”‚ βœ— Failed to extract DEVELOP_APP_ID: %v", err)
log.Printf(" β”‚")
log.Printf(" β”‚ Please ensure Thunder setup container has completed successfully")
log.Printf(" β”‚")
log.Printf(" β”‚ To fix this issue:")
log.Printf(" β”‚ 1. Check thunder-setup logs: docker logs thunder-setup")
log.Printf(" β”‚ 2. Extract App ID manually and set environment:")
log.Printf(" β”‚ export THUNDER_SAMPLE_APP_ID=$(docker logs thunder-setup 2>&1 | grep 'Sample App ID:' | grep -o '[a-f0-9-]\\{36\\}')")
log.Printf(" β”‚ export THUNDER_DEVELOP_APP_ID=$(docker logs thunder-setup 2>&1 | grep 'DEVELOP_APP_ID:' | grep -o '[a-f0-9-]\\{36\\}')")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The example command in the error log for manually extracting the DEVELOP_APP_ID can be improved. Using grep -i makes it case-insensitive, and a more specific regex for the UUID makes it more robust. This aligns it with the logic in the Go code and the accompanying shell scripts.

Suggested change
log.Printf(" β”‚ export THUNDER_DEVELOP_APP_ID=$(docker logs thunder-setup 2>&1 | grep 'DEVELOP_APP_ID:' | grep -o '[a-f0-9-]\\{36\\}')")
log.Printf(" β”‚ export THUNDER_DEVELOP_APP_ID=$(docker logs thunder-setup 2>&1 | grep -i 'DEVELOP_APP_ID' | grep -o '[a-f0-9]\\{8\\}-[a-f0-9]\\{4\\}-[a-f0-9]\\{4\\}-[a-f0-9]\\{4\\}-[a-f0-9]\\{12\\}')")

log.Printf(" β”‚ 3. Or if running in Docker, mount the Docker socket:")
log.Printf(" β”‚ volumes: ['/var/run/docker.sock:/var/run/docker.sock']")
log.Printf(" └───────────────────────────────────")
return nil, fmt.Errorf("failed to get Sample App ID: %w", err)
return nil, fmt.Errorf("failed to get DEVELOP App ID: %w", err)
}
}

Expand All @@ -93,7 +95,7 @@ func Authenticate(host, port string, tokenRefreshSeconds int) (*Auth, error) {
// Step 2: Start authentication flow
log.Printf(" β”‚ Starting authentication flow...")
flowPayload := map[string]interface{}{
"applicationId": sampleAppID,
"applicationId": developAppID,
"flowType": "AUTHENTICATION",
}
flowData, err := json.Marshal(flowPayload)
Expand Down Expand Up @@ -164,7 +166,7 @@ func Authenticate(host, port string, tokenRefreshSeconds int) (*Auth, error) {
log.Printf(" └───────────────────────────────────")

auth := &Auth{
SampleAppID: sampleAppID,
DevelopAppID: developAppID,
FlowID: flowResp.FlowID,
BearerToken: authResp.Assertion,
ExpiresAt: time.Now().Add(time.Duration(tokenRefreshSeconds) * time.Second),
Expand Down
2 changes: 1 addition & 1 deletion services/socketmap/internal/thunder/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

// Auth holds Thunder authentication state
type Auth struct {
SampleAppID string
DevelopAppID string
FlowID string
BearerToken string
ExpiresAt time.Time
Expand Down
33 changes: 27 additions & 6 deletions services/socketmap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,37 @@ func main() {
// Load configuration
cfg := config.Load()

// Authenticate with Thunder at startup
// Authenticate with Thunder at startup with retry logic
log.Println("β”Œβ”€ Thunder Authentication ─────────")
auth, err := thunder.Authenticate(cfg.ThunderHost, cfg.ThunderPort, cfg.TokenRefreshSeconds)

var auth *thunder.Auth
var err error
maxRetries := 5
retryDelay := 2 * time.Second

for attempt := 1; attempt <= maxRetries; attempt++ {
if attempt > 1 {
log.Printf("β”‚ Retry attempt %d/%d (waiting %v)...", attempt, maxRetries, retryDelay)
time.Sleep(retryDelay)
retryDelay *= 2 // Exponential backoff
}

auth, err = thunder.Authenticate(cfg.ThunderHost, cfg.ThunderPort, cfg.TokenRefreshSeconds)
if err == nil {
thunder.SetAuth(auth)
log.Println("└───────────────────────────────────")
break
}

if attempt < maxRetries {
log.Printf("β”‚ ⚠ Authentication attempt %d failed: %v", attempt, err)
}
}

if err != nil {
log.Printf("β”‚ ⚠ Initial authentication failed: %v", err)
log.Printf("β”‚ ⚠ Initial authentication failed after %d attempts: %v", maxRetries, err)
log.Printf("β”‚ Service will attempt to authenticate on first request")
log.Println("└───────────────────────────────────")
} else {
thunder.SetAuth(auth)
log.Println("└───────────────────────────────────")
}
log.Println("")

Expand Down
Loading