Skip to content

284 task create bootstrap scripts for thunder#292

Merged
Aravinda-HWK merged 7 commits intoLSFLK:mainfrom
Aravinda-HWK:284-task-create-bootstrap-scripts-for-thunder
Mar 11, 2026
Merged

284 task create bootstrap scripts for thunder#292
Aravinda-HWK merged 7 commits intoLSFLK:mainfrom
Aravinda-HWK:284-task-create-bootstrap-scripts-for-thunder

Conversation

@Aravinda-HWK
Copy link
Copy Markdown
Collaborator

📌 Description

Change the bootstrap scripts of the Thunder for the initial setup of the Thunder instance.


🔍 Changes Made

  • Read the admin username and password from the .env file
  • Use the develop app to the socketmap service
  • Update the Thunder version to 0.24.0

✅ Checklist (Email System)

  • Core services tested (SMTP, IMAP, mail storage, end-to-end delivery)
  • Security & compliance verified (auth via Thunder IDP, TLS, DKIM/SPF/DMARC, spam/virus filtering)
  • Configuration & deployment checked (configs generated, Docker/Compose updated)
  • Reliability confirmed (error handling, logging, monitoring)
  • Documentation & usage notes updated (README, deployment, API)

🧪 Testing Instructions


📷 Screenshots / Logs (if applicable)


⚠️ Notes for Reviewers

@Aravinda-HWK Aravinda-HWK self-assigned this Mar 11, 2026
@maneeshaxyz
Copy link
Copy Markdown
Member

/gemini review

Copy link
Copy Markdown
Member

@maneeshaxyz maneeshaxyz left a comment

Choose a reason for hiding this comment

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

LGTM.

@Aravinda-HWK Aravinda-HWK merged commit bab67ab into LSFLK:main Mar 11, 2026
6 checks passed
@Aravinda-HWK Aravinda-HWK deleted the 284-task-create-bootstrap-scripts-for-thunder branch March 11, 2026 10:29
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refines the initial setup process for a Thunder instance, making it more robust and configurable. The changes streamline the bootstrapping of essential resources like organization units, user schemas, and an admin user, while also integrating application-specific flows and themes. By externalizing admin credentials and updating the core Thunder version, the system becomes easier to deploy and maintain, ensuring a smoother initial experience.

Highlights

  • New Bootstrap Scripts: Introduced new bootstrap scripts for initial Thunder instance setup, including default organization unit, user schema, admin user, system resource server, permissions, roles, flows, themes, and i18n translations.
  • Configurable Admin Credentials: Configured the Thunder admin username and password to be read from the .env file, enhancing configurability and security.
  • Updated Application ID Reference: Updated the socketmap service to utilize the DEVELOP application for authentication, replacing the previous SAMPLE application.
  • Thunder Version Upgrade: Upgraded the Thunder version used in docker-compose.yaml from 0.22.0 to 0.24.0.
  • Improved Service Dependencies: Added depends_on conditions to the socketmap service in docker-compose.yaml to ensure thunder-setup completes and thunder starts before it.
  • Authentication Retry Logic: Implemented retry logic for the initial authentication of the socketmap service with Thunder, improving service resilience during startup.
Changelog
  • scripts/thunder/01-default-resources.sh
    • Added a new bootstrap script to create default Thunder resources, including organization units, user schemas, admin users, system resource servers, permissions, roles, default flows, application-specific flows, themes, and i18n translations.
  • scripts/thunder/02-sample-resources.sh
    • Added a new script that serves as an empty placeholder, effectively disabling sample resource creation.
  • scripts/utils/thunder-auth.sh
    • Updated comments and function names to refer to DEVELOP_APP_ID instead of SAMPLE_APP_ID.
    • Modified the thunder_get_develop_app_id function to correctly extract the DEVELOP_APP_ID from Thunder setup logs.
  • services/.env.example
    • Added THUNDER_ADMIN_USERNAME and THUNDER_ADMIN_PASSWORD variables for configuring admin credentials.
  • services/docker-compose.yaml
    • Updated the Thunder image version for thunder-db-init, thunder-setup, and thunder services to 0.24.0.
    • Mounted the new 01-default-resources.sh and 02-sample-resources.sh bootstrap scripts into the thunder-setup container.
    • Passed THUNDER_ADMIN_USERNAME and THUNDER_ADMIN_PASSWORD environment variables to the thunder-setup service.
    • Added depends_on conditions to the socketmap service to ensure thunder-setup completes and thunder starts before it.
    • Updated the comment for THUNDER_SAMPLE_APP_ID to THUNDER_DEVELOP_APP_ID in the socketmap service environment.
  • services/socketmap/internal/thunder/auth.go
    • Renamed getSampleAppIDFromThunderSetup to getDevelopAppIDFromThunderSetup and updated its internal logic to search for DEVELOP_APP_ID.
    • Modified the Authenticate function to use DEVELOP_APP_ID instead of THUNDER_SAMPLE_APP_ID from environment variables and logs.
    • Updated error messages and instructions to reflect the change from SAMPLE_APP_ID to DEVELOP_APP_ID.
  • services/socketmap/internal/thunder/types.go
    • Renamed the SampleAppID field to DevelopAppID within the Auth struct.
  • services/socketmap/main.go
    • Implemented a retry mechanism with exponential backoff for the initial authentication of the socketmap service with Thunder.
Activity
  • No specific activity has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request introduces a comprehensive 01-default-resources.sh script to bootstrap default Thunder resources, including organization units, user schemas, an admin user, system resource servers with hierarchical permissions, admin roles, a "Develop" application, themes, and i18n translations. This replaces the previous sample resources script. Related changes include updating the thunder-auth.sh utility and the socketmap Go application to reference DEVELOP_APP_ID instead of SAMPLE_APP_ID, enhancing the log parsing for the application ID, and adding retry logic with exponential backoff for initial authentication in the socketmap service. The docker-compose.yaml file is updated to use Thunder image 0.24.0, mount the new bootstrap scripts, and pass admin credentials. Review comments suggest improving the robustness of JSON parsing for ADMIN_USER_ID and SYSTEM_RS_ID in 01-default-resources.sh by directly using more reliable parsing methods. Additionally, significant code duplication in the resource creation and flow processing sections of the bootstrap script is highlighted, with recommendations to refactor these into reusable shell functions for better maintainability.

Comment on lines +241 to +246
ADMIN_USER_ID=$(echo "$BODY" | grep -o '"id":"[^"]*","[^"]*":"[^"]*","attributes":{[^}]*"username":"'"${ADMIN_USERNAME}"'"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)

# Fallback parsing
if [[ -z "$ADMIN_USER_ID" ]]; then
ADMIN_USER_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"username":"'"${ADMIN_USERNAME}"'"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
fi
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 primary method for parsing the admin user ID on line 241 is very fragile as it depends on the specific order of JSON fields. A minor change in the API response could break it. The fallback logic on line 245 is more robust as it only relies on finding the username field within a JSON object. It would be better to use the more robust method directly.

Suggested change
ADMIN_USER_ID=$(echo "$BODY" | grep -o '"id":"[^"]*","[^"]*":"[^"]*","attributes":{[^}]*"username":"'"${ADMIN_USERNAME}"'"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
# Fallback parsing
if [[ -z "$ADMIN_USER_ID" ]]; then
ADMIN_USER_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"username":"'"${ADMIN_USERNAME}"'"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
fi
ADMIN_USER_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"username":"'"${ADMIN_USERNAME}"'"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)

Comment on lines +304 to +309
SYSTEM_RS_ID=$(echo "$BODY" | grep -o '"id":"[^"]*","[^"]*":"System"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)

# Fallback parsing
if [[ -z "$SYSTEM_RS_ID" ]]; then
SYSTEM_RS_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"identifier":"system"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
fi
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

This grep command is fragile as it assumes the name field with value System appears right after the id field. It's better to rely on the unique identifier field, as done in the fallback logic on line 308. I suggest using the more robust method directly.

Suggested change
SYSTEM_RS_ID=$(echo "$BODY" | grep -o '"id":"[^"]*","[^"]*":"System"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
# Fallback parsing
if [[ -z "$SYSTEM_RS_ID" ]]; then
SYSTEM_RS_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"identifier":"system"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
fi
SYSTEM_RS_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"identifier":"system"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)

Comment on lines +395 to +438
log_info "Creating 'ou' sub-resource under the 'system' resource..."

RESPONSE=$(thunder_api_call POST "/resource-servers/${SYSTEM_RS_ID}/resources" "{
\"name\": \"Organization Unit\",
\"description\": \"Organization unit resource\",
\"handle\": \"ou\",
\"parent\": \"${SYSTEM_RESOURCE_ID}\"
}")

HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

if [[ "$HTTP_CODE" == "201" ]] || [[ "$HTTP_CODE" == "200" ]]; then
log_success "OU resource created successfully (permission: system:ou)"
OU_RESOURCE_ID=$(echo "$BODY" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
if [[ -n "$OU_RESOURCE_ID" ]]; then
log_info "OU resource ID: $OU_RESOURCE_ID"
else
log_error "Could not extract OU resource ID from response"
exit 1
fi
elif [[ "$HTTP_CODE" == "409" ]]; then
log_warning "OU resource already exists, retrieving ID..."
RESPONSE=$(thunder_api_call GET "/resource-servers/${SYSTEM_RS_ID}/resources?parentId=${SYSTEM_RESOURCE_ID}")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

if [[ "$HTTP_CODE" == "200" ]]; then
OU_RESOURCE_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"handle":"ou"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
if [[ -n "$OU_RESOURCE_ID" ]]; then
log_success "Found OU resource ID: $OU_RESOURCE_ID"
else
log_error "Could not find OU resource in response"
exit 1
fi
else
log_error "Failed to fetch resources (HTTP $HTTP_CODE)"
exit 1
fi
else
log_error "Failed to create OU resource (HTTP $HTTP_CODE)"
echo "Response: $BODY"
exit 1
fi
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

There is significant code duplication in the creation of various resources (ou, user, userschema, group). This block of code for creating the ou resource is repeated with minor changes for other resources between lines 395 and 659. This logic could be extracted into a reusable shell function to improve maintainability and reduce code size. A similar pattern of duplication exists for creating the view action for each resource.

Comment on lines +740 to +801
# Process authentication flows
if [[ -d "$AUTH_FLOWS_DIR" ]]; then
shopt -s nullglob
AUTH_FILES=("$AUTH_FLOWS_DIR"/*.json)
shopt -u nullglob

if [[ ${#AUTH_FILES[@]} -gt 0 ]]; then
log_info "Processing authentication flows..."

# Fetch existing auth flows
RESPONSE=$(thunder_api_call GET "/flows?flowType=AUTHENTICATION&limit=200")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

# Store existing auth flows as "handle|id" pairs
EXISTING_AUTH_FLOWS=""
if [[ "$HTTP_CODE" == "200" ]]; then
while IFS= read -r line; do
FLOW_ID=$(echo "$line" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
FLOW_HANDLE=$(echo "$line" | grep -o '"handle":"[^"]*"' | cut -d'"' -f4)
if [[ -n "$FLOW_ID" ]] && [[ -n "$FLOW_HANDLE" ]]; then
EXISTING_AUTH_FLOWS="${EXISTING_AUTH_FLOWS}${FLOW_HANDLE}|${FLOW_ID}"$'\n'
log_debug "Found existing auth flow: handle=$FLOW_HANDLE (ID: $FLOW_ID)"
fi
done < <(echo "$BODY" | grep -o '{[^}]*"id":"[^"]*"[^}]*"handle":"[^"]*"[^}]*}')
fi

log_debug "Total existing auth flows found: $(echo "$EXISTING_AUTH_FLOWS" | grep -c '|' || echo 0)"

for FLOW_FILE in "$AUTH_FLOWS_DIR"/*.json; do
[[ ! -f "$FLOW_FILE" ]] && continue

FLOW_COUNT=$((FLOW_COUNT + 1))
FLOW_HANDLE=$(grep -o '"handle"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"handle"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
FLOW_NAME=$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"name"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
log_debug "Processing flow file: $FLOW_FILE with handle: $FLOW_HANDLE, name: $FLOW_NAME"

# Check if flow exists by handle
if echo "$EXISTING_AUTH_FLOWS" | grep -q "^${FLOW_HANDLE}|"; then
# Update existing flow
FLOW_ID=$(echo "$EXISTING_AUTH_FLOWS" | grep "^${FLOW_HANDLE}|" | cut -d'|' -f2)
log_info "Updating existing auth flow: $FLOW_NAME (handle: $FLOW_HANDLE)"
update_flow "$FLOW_ID" "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
fi
else
# Create new flow
create_flow "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
elif [[ $RESULT -eq 2 ]]; then
FLOW_SKIPPED=$((FLOW_SKIPPED + 1))
fi
fi
done
else
log_warning "No authentication flow files found"
fi
fi
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 logic for processing authentication, registration, and user onboarding flows is nearly identical. This block for authentication flows is repeated for registration and user onboarding flows between lines 740 and 919. You could create a function that takes the flow type (e.g., AUTHENTICATION) and the source directory as parameters to avoid this duplication. This would make the script much shorter and easier to maintain.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces bootstrap scripts for Thunder, updates configurations, and refactors related code to use the new DEVELOP application instead of a sample application. A critical aspect to address is the identification of two medium-severity vulnerabilities in scripts/thunder/01-default-resources.sh: a JSON injection vulnerability due to improper handling of custom redirect URIs and the logging of sensitive admin credentials. Beyond security, the review also focuses on improving the maintainability and robustness of the new bash scripts and ensuring consistency across the codebase, including the acceptable use of grep and sed for JSON parsing when encapsulated for maintainability.

Comment on lines +1091 to +1095
REDIRECT_URIS="${REDIRECT_URIS},\"${uri}\""
done
fi

RESPONSE=$(thunder_api_call POST "/applications" "{
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.

security-medium medium

The script constructs a JSON payload by concatenating the CUSTOM_DEVELOP_REDIRECT_URIS input without proper escaping or validation. An attacker providing a specially crafted redirect URI containing double quotes could inject arbitrary fields into the JSON payload sent to the /applications API, leading to a JSON injection vulnerability. Additionally, constructing large multi-line JSON payloads as a single string with escaped quotes and newlines is hard to read and maintain. Consider using a heredoc for better readability and maintainability, but ensure REDIRECT_URIS is properly escaped before inclusion to prevent the injection.

if [[ "$HTTP_CODE" == "201" ]] || [[ "$HTTP_CODE" == "200" ]]; then
log_success "Admin user created successfully"
log_info "Username: ${ADMIN_USERNAME}"
log_info "Password: ${ADMIN_PASSWORD}"
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.

security-medium medium

The script logs the admin password to the console/logs during the setup process. This can lead to the exposure of sensitive credentials if logs are stored or accessible by unauthorized users. It is recommended to remove this logging statement.

Comment on lines +241 to +246
ADMIN_USER_ID=$(echo "$BODY" | grep -o '"id":"[^"]*","[^"]*":"[^"]*","attributes":{[^}]*"username":"'"${ADMIN_USERNAME}"'"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)

# Fallback parsing
if [[ -z "$ADMIN_USER_ID" ]]; then
ADMIN_USER_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"username":"'"${ADMIN_USERNAME}"'"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
fi
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

This complex and repeated logic for parsing JSON to find the admin user ID is brittle and hard to maintain. A fallback is provided, but the whole block can be simplified by using a helper function.

I suggest adding the following helper function near the top of your script (e.g., after source "${SCRIPT_DIR}/common.sh"):

# Helper function to extract a value from a JSON array based on a filter.
# It splits the JSON array into lines, finds the line with the filter,
# and extracts the value of the target key.
#
# Arguments:
#   $1: JSON body (string)
#   $2: Filter pattern to find the correct JSON object (e.g., '"username":"admin"')
#   $3: The key of the value to extract (e.g., "id")
extract_json_value() {
    local body="$1"
    local filter_pattern="$2"
    local target_key="$3"
    echo "$body" | sed 's/},{/}\n{/g' | grep "$filter_pattern" | grep -o "\"$target_key\":\"[^\"]*\"" | head -1 | cut -d'\"' -f4
}

Then you can replace these lines with a single, more readable line:

ADMIN_USER_ID=$(extract_json_value "$BODY" "\"username\":\"${ADMIN_USERNAME}\"" "id")

This pattern can be reused throughout the script (e.g., for finding the system resource server ID on lines 304-309) to significantly improve readability and maintainability.

References
  1. Using grep and sed for simple JSON parsing is acceptable to avoid introducing new dependencies like jq.

Comment on lines +740 to +918
# Process authentication flows
if [[ -d "$AUTH_FLOWS_DIR" ]]; then
shopt -s nullglob
AUTH_FILES=("$AUTH_FLOWS_DIR"/*.json)
shopt -u nullglob

if [[ ${#AUTH_FILES[@]} -gt 0 ]]; then
log_info "Processing authentication flows..."

# Fetch existing auth flows
RESPONSE=$(thunder_api_call GET "/flows?flowType=AUTHENTICATION&limit=200")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

# Store existing auth flows as "handle|id" pairs
EXISTING_AUTH_FLOWS=""
if [[ "$HTTP_CODE" == "200" ]]; then
while IFS= read -r line; do
FLOW_ID=$(echo "$line" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
FLOW_HANDLE=$(echo "$line" | grep -o '"handle":"[^"]*"' | cut -d'"' -f4)
if [[ -n "$FLOW_ID" ]] && [[ -n "$FLOW_HANDLE" ]]; then
EXISTING_AUTH_FLOWS="${EXISTING_AUTH_FLOWS}${FLOW_HANDLE}|${FLOW_ID}"$'\n'
log_debug "Found existing auth flow: handle=$FLOW_HANDLE (ID: $FLOW_ID)"
fi
done < <(echo "$BODY" | grep -o '{[^}]*"id":"[^"]*"[^}]*"handle":"[^"]*"[^}]*}')
fi

log_debug "Total existing auth flows found: $(echo "$EXISTING_AUTH_FLOWS" | grep -c '|' || echo 0)"

for FLOW_FILE in "$AUTH_FLOWS_DIR"/*.json; do
[[ ! -f "$FLOW_FILE" ]] && continue

FLOW_COUNT=$((FLOW_COUNT + 1))
FLOW_HANDLE=$(grep -o '"handle"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"handle"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
FLOW_NAME=$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"name"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
log_debug "Processing flow file: $FLOW_FILE with handle: $FLOW_HANDLE, name: $FLOW_NAME"

# Check if flow exists by handle
if echo "$EXISTING_AUTH_FLOWS" | grep -q "^${FLOW_HANDLE}|"; then
# Update existing flow
FLOW_ID=$(echo "$EXISTING_AUTH_FLOWS" | grep "^${FLOW_HANDLE}|" | cut -d'|' -f2)
log_info "Updating existing auth flow: $FLOW_NAME (handle: $FLOW_HANDLE)"
update_flow "$FLOW_ID" "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
fi
else
# Create new flow
create_flow "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
elif [[ $RESULT -eq 2 ]]; then
FLOW_SKIPPED=$((FLOW_SKIPPED + 1))
fi
fi
done
else
log_warning "No authentication flow files found"
fi
fi

# Process registration flows
if [[ -d "$REG_FLOWS_DIR" ]]; then
shopt -s nullglob
REG_FILES=("$REG_FLOWS_DIR"/*.json)
shopt -u nullglob

if [[ ${#REG_FILES[@]} -gt 0 ]]; then
log_info "Processing registration flows..."

# Fetch existing registration flows
RESPONSE=$(thunder_api_call GET "/flows?flowType=REGISTRATION&limit=200")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

# Store existing registration flows as "handle|id" pairs
EXISTING_REG_FLOWS=""
if [[ "$HTTP_CODE" == "200" ]]; then
while IFS= read -r line; do
FLOW_ID=$(echo "$line" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
FLOW_HANDLE=$(echo "$line" | grep -o '"handle":"[^"]*"' | cut -d'"' -f4)
if [[ -n "$FLOW_ID" ]] && [[ -n "$FLOW_HANDLE" ]]; then
EXISTING_REG_FLOWS="${EXISTING_REG_FLOWS}${FLOW_HANDLE}|${FLOW_ID}"$'\n'
fi
done < <(echo "$BODY" | grep -o '{[^}]*"id":"[^"]*"[^}]*"handle":"[^"]*"[^}]*}')
fi

for FLOW_FILE in "$REG_FLOWS_DIR"/*.json; do
[[ ! -f "$FLOW_FILE" ]] && continue

FLOW_COUNT=$((FLOW_COUNT + 1))
FLOW_HANDLE=$(grep -o '"handle"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"handle"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
FLOW_NAME=$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"name"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')

# Check if flow exists by handle
if echo "$EXISTING_REG_FLOWS" | grep -q "^${FLOW_HANDLE}|"; then
# Update existing flow
FLOW_ID=$(echo "$EXISTING_REG_FLOWS" | grep "^${FLOW_HANDLE}|" | cut -d'|' -f2)
log_info "Updating existing registration flow: $FLOW_NAME (handle: $FLOW_HANDLE)"
update_flow "$FLOW_ID" "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
fi
else
# Create new flow
create_flow "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
elif [[ $RESULT -eq 2 ]]; then
FLOW_SKIPPED=$((FLOW_SKIPPED + 1))
fi
fi
done
else
log_warning "No registration flow files found"
fi
fi

# Process user onboarding flows
if [[ -d "$USER_ONBOARDING_FLOWS_DIR" ]]; then
shopt -s nullglob
INVITE_FILES=("$USER_ONBOARDING_FLOWS_DIR"/*.json)
shopt -u nullglob

if [[ ${#INVITE_FILES[@]} -gt 0 ]]; then
log_info "Processing user onboarding flows..."

# Fetch existing user onboarding flows
RESPONSE=$(thunder_api_call GET "/flows?flowType=USER_ONBOARDING&limit=200")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

# Store existing user onboarding flows as "handle|id" pairs
EXISTING_INVITE_FLOWS=""
if [[ "$HTTP_CODE" == "200" ]]; then
while IFS= read -r line; do
FLOW_ID=$(echo "$line" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
FLOW_HANDLE=$(echo "$line" | grep -o '"handle":"[^"]*"' | cut -d'"' -f4)
if [[ -n "$FLOW_ID" ]] && [[ -n "$FLOW_HANDLE" ]]; then
EXISTING_INVITE_FLOWS="${EXISTING_INVITE_FLOWS}${FLOW_HANDLE}|${FLOW_ID}"$'\n'
fi
done < <(echo "$BODY" | grep -o '{[^}]*"id":"[^"]*"[^}]*"handle":"[^"]*"[^}]*}')
fi

for FLOW_FILE in "$USER_ONBOARDING_FLOWS_DIR"/*.json; do
[[ ! -f "$FLOW_FILE" ]] && continue

FLOW_COUNT=$((FLOW_COUNT + 1))
FLOW_HANDLE=$(grep -o '"handle"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"handle"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
FLOW_NAME=$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"name"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')

# Check if flow exists by handle
if echo "$EXISTING_INVITE_FLOWS" | grep -q "^${FLOW_HANDLE}|"; then
# Update existing flow
FLOW_ID=$(echo "$EXISTING_INVITE_FLOWS" | grep "^${FLOW_HANDLE}|" | cut -d'|' -f2)
log_info "Updating existing user onboarding flow: $FLOW_NAME (handle: $FLOW_HANDLE)"
update_flow "$FLOW_ID" "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
fi
else
# Create new flow
create_flow "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
elif [[ $RESULT -eq 2 ]]; then
FLOW_SKIPPED=$((FLOW_SKIPPED + 1))
fi
fi
done
else
log_debug "No user onboarding flow files found"
fi
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

There is significant code duplication in the logic for processing authentication, registration, and user onboarding flows. This makes the script harder to read and maintain. This entire block of logic can be refactored into a single, reusable function.

Consider adding a function like process_flows_by_type to your script:

process_flows_by_type() {
    local flow_type="$1"
    local flow_dir="$2"

    if [[ ! -d "$flow_dir" ]]; then
        log_warning "Flow definition directory for '$flow_type' not found, skipping"
        return
    fi

    shopt -s nullglob
    local flow_files=("$flow_dir"/*.json)
    shopt -u nullglob

    if [[ ${#flow_files[@]} -eq 0 ]]; then
        log_warning "No flow files found in ${flow_dir}"
        return
    fi

    log_info "Processing ${flow_type,,} flows..."

    # Fetch existing flows
    local response
    response=$(thunder_api_call GET "/flows?flowType=${flow_type}&limit=200")
    # ... (rest of the logic from the original blocks) ...
}

You can then replace the duplicated blocks (lines 740-801, 803-859, and 862-918) with simple calls to this function:

process_flows_by_type "AUTHENTICATION" "$AUTH_FLOWS_DIR"
process_flows_by_type "REGISTRATION" "$REG_FLOWS_DIR"
process_flows_by_type "USER_ONBOARDING" "$USER_ONBOARDING_FLOWS_DIR"

This refactoring will make the script much shorter, cleaner, and easier to maintain.

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\}')

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\\}')")

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request introduces a comprehensive bootstrap script (01-default-resources.sh) to set up default Thunder resources, including organization units, user schemas, an admin user, system resource servers, permissions, roles, authentication/registration flows, a "Develop" application, themes, and i18n translations. Correspondingly, 02-sample-resources.sh is made empty, and references to SAMPLE_APP_ID are updated to DEVELOP_APP_ID across utility scripts and the socketmap service, which also gains retry logic for initial authentication. Docker Compose configurations are updated to use Thunder image 0.24.0 and integrate the new bootstrap scripts and admin credentials. Review comments highlight a high-severity JSON injection vulnerability in 01-default-resources.sh due to direct concatenation of user-provided redirect URIs into a JSON payload. Additionally, there are code duplication issues in 01-default-resources.sh related to processing different flow types and handling HTTP 400/409 errors during application creation, both of which could be refactored for improved maintainability.

Comment on lines +1083 to +1109
REDIRECT_URIS="\"${PUBLIC_URL}/develop\""
if [[ -n "$CUSTOM_DEVELOP_REDIRECT_URIS" ]]; then
log_info "Adding custom redirect URIs: $CUSTOM_DEVELOP_REDIRECT_URIS"
# Split comma-separated URIs and append to array
IFS=',' read -ra URI_ARRAY <<< "$CUSTOM_DEVELOP_REDIRECT_URIS"
for uri in "${URI_ARRAY[@]}"; do
# Trim whitespace
uri=$(echo "$uri" | xargs)
REDIRECT_URIS="${REDIRECT_URIS},\"${uri}\""
done
fi

RESPONSE=$(thunder_api_call POST "/applications" "{
\"name\": \"Develop\",
\"description\": \"Developer application for Thunder\",
\"url\": \"${PUBLIC_URL}/develop\",
\"logo_url\": \"${PUBLIC_URL}/develop/assets/images/logo-mini.svg\",
\"auth_flow_id\": \"${DEVELOP_AUTH_FLOW_ID}\",
\"registration_flow_id\": \"${DEVELOP_REG_FLOW_ID}\",
\"is_registration_flow_enabled\": false,
\"allowed_user_types\": [\"Person\"],
\"user_attributes\": [\"given_name\",\"family_name\",\"email\",\"groups\", \"name\", \"ouId\"],
\"inbound_auth_config\": [{
\"type\": \"oauth2\",
\"config\": {
\"client_id\": \"DEVELOP\",
\"redirect_uris\": [${REDIRECT_URIS}],
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.

security-high high

The script takes user input from the command line argument --develop-redirect-uris and directly concatenates it into a JSON payload sent to the Thunder API. An attacker can provide a specially crafted string to inject arbitrary fields into the JSON object, potentially leading to unauthorized configuration changes. For example, providing a value like http://example.com", "is_registration_flow_enabled": true would inject the is_registration_flow_enabled field into the JSON payload.

Comment on lines +740 to +919
# Process authentication flows
if [[ -d "$AUTH_FLOWS_DIR" ]]; then
shopt -s nullglob
AUTH_FILES=("$AUTH_FLOWS_DIR"/*.json)
shopt -u nullglob

if [[ ${#AUTH_FILES[@]} -gt 0 ]]; then
log_info "Processing authentication flows..."

# Fetch existing auth flows
RESPONSE=$(thunder_api_call GET "/flows?flowType=AUTHENTICATION&limit=200")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

# Store existing auth flows as "handle|id" pairs
EXISTING_AUTH_FLOWS=""
if [[ "$HTTP_CODE" == "200" ]]; then
while IFS= read -r line; do
FLOW_ID=$(echo "$line" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
FLOW_HANDLE=$(echo "$line" | grep -o '"handle":"[^"]*"' | cut -d'"' -f4)
if [[ -n "$FLOW_ID" ]] && [[ -n "$FLOW_HANDLE" ]]; then
EXISTING_AUTH_FLOWS="${EXISTING_AUTH_FLOWS}${FLOW_HANDLE}|${FLOW_ID}"$'\n'
log_debug "Found existing auth flow: handle=$FLOW_HANDLE (ID: $FLOW_ID)"
fi
done < <(echo "$BODY" | grep -o '{[^}]*"id":"[^"]*"[^}]*"handle":"[^"]*"[^}]*}')
fi

log_debug "Total existing auth flows found: $(echo "$EXISTING_AUTH_FLOWS" | grep -c '|' || echo 0)"

for FLOW_FILE in "$AUTH_FLOWS_DIR"/*.json; do
[[ ! -f "$FLOW_FILE" ]] && continue

FLOW_COUNT=$((FLOW_COUNT + 1))
FLOW_HANDLE=$(grep -o '"handle"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"handle"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
FLOW_NAME=$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"name"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
log_debug "Processing flow file: $FLOW_FILE with handle: $FLOW_HANDLE, name: $FLOW_NAME"

# Check if flow exists by handle
if echo "$EXISTING_AUTH_FLOWS" | grep -q "^${FLOW_HANDLE}|"; then
# Update existing flow
FLOW_ID=$(echo "$EXISTING_AUTH_FLOWS" | grep "^${FLOW_HANDLE}|" | cut -d'|' -f2)
log_info "Updating existing auth flow: $FLOW_NAME (handle: $FLOW_HANDLE)"
update_flow "$FLOW_ID" "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
fi
else
# Create new flow
create_flow "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
elif [[ $RESULT -eq 2 ]]; then
FLOW_SKIPPED=$((FLOW_SKIPPED + 1))
fi
fi
done
else
log_warning "No authentication flow files found"
fi
fi

# Process registration flows
if [[ -d "$REG_FLOWS_DIR" ]]; then
shopt -s nullglob
REG_FILES=("$REG_FLOWS_DIR"/*.json)
shopt -u nullglob

if [[ ${#REG_FILES[@]} -gt 0 ]]; then
log_info "Processing registration flows..."

# Fetch existing registration flows
RESPONSE=$(thunder_api_call GET "/flows?flowType=REGISTRATION&limit=200")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

# Store existing registration flows as "handle|id" pairs
EXISTING_REG_FLOWS=""
if [[ "$HTTP_CODE" == "200" ]]; then
while IFS= read -r line; do
FLOW_ID=$(echo "$line" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
FLOW_HANDLE=$(echo "$line" | grep -o '"handle":"[^"]*"' | cut -d'"' -f4)
if [[ -n "$FLOW_ID" ]] && [[ -n "$FLOW_HANDLE" ]]; then
EXISTING_REG_FLOWS="${EXISTING_REG_FLOWS}${FLOW_HANDLE}|${FLOW_ID}"$'\n'
fi
done < <(echo "$BODY" | grep -o '{[^}]*"id":"[^"]*"[^}]*"handle":"[^"]*"[^}]*}')
fi

for FLOW_FILE in "$REG_FLOWS_DIR"/*.json; do
[[ ! -f "$FLOW_FILE" ]] && continue

FLOW_COUNT=$((FLOW_COUNT + 1))
FLOW_HANDLE=$(grep -o '"handle"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"handle"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
FLOW_NAME=$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"name"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')

# Check if flow exists by handle
if echo "$EXISTING_REG_FLOWS" | grep -q "^${FLOW_HANDLE}|"; then
# Update existing flow
FLOW_ID=$(echo "$EXISTING_REG_FLOWS" | grep "^${FLOW_HANDLE}|" | cut -d'|' -f2)
log_info "Updating existing registration flow: $FLOW_NAME (handle: $FLOW_HANDLE)"
update_flow "$FLOW_ID" "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
fi
else
# Create new flow
create_flow "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
elif [[ $RESULT -eq 2 ]]; then
FLOW_SKIPPED=$((FLOW_SKIPPED + 1))
fi
fi
done
else
log_warning "No registration flow files found"
fi
fi

# Process user onboarding flows
if [[ -d "$USER_ONBOARDING_FLOWS_DIR" ]]; then
shopt -s nullglob
INVITE_FILES=("$USER_ONBOARDING_FLOWS_DIR"/*.json)
shopt -u nullglob

if [[ ${#INVITE_FILES[@]} -gt 0 ]]; then
log_info "Processing user onboarding flows..."

# Fetch existing user onboarding flows
RESPONSE=$(thunder_api_call GET "/flows?flowType=USER_ONBOARDING&limit=200")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

# Store existing user onboarding flows as "handle|id" pairs
EXISTING_INVITE_FLOWS=""
if [[ "$HTTP_CODE" == "200" ]]; then
while IFS= read -r line; do
FLOW_ID=$(echo "$line" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
FLOW_HANDLE=$(echo "$line" | grep -o '"handle":"[^"]*"' | cut -d'"' -f4)
if [[ -n "$FLOW_ID" ]] && [[ -n "$FLOW_HANDLE" ]]; then
EXISTING_INVITE_FLOWS="${EXISTING_INVITE_FLOWS}${FLOW_HANDLE}|${FLOW_ID}"$'\n'
fi
done < <(echo "$BODY" | grep -o '{[^}]*"id":"[^"]*"[^}]*"handle":"[^"]*"[^}]*}')
fi

for FLOW_FILE in "$USER_ONBOARDING_FLOWS_DIR"/*.json; do
[[ ! -f "$FLOW_FILE" ]] && continue

FLOW_COUNT=$((FLOW_COUNT + 1))
FLOW_HANDLE=$(grep -o '"handle"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"handle"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')
FLOW_NAME=$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$FLOW_FILE" | head -1 | sed 's/"name"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/')

# Check if flow exists by handle
if echo "$EXISTING_INVITE_FLOWS" | grep -q "^${FLOW_HANDLE}|"; then
# Update existing flow
FLOW_ID=$(echo "$EXISTING_INVITE_FLOWS" | grep "^${FLOW_HANDLE}|" | cut -d'|' -f2)
log_info "Updating existing user onboarding flow: $FLOW_NAME (handle: $FLOW_HANDLE)"
update_flow "$FLOW_ID" "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
fi
else
# Create new flow
create_flow "$FLOW_FILE"
RESULT=$?
if [[ $RESULT -eq 0 ]]; then
FLOW_SUCCESS=$((FLOW_SUCCESS + 1))
elif [[ $RESULT -eq 2 ]]; then
FLOW_SKIPPED=$((FLOW_SKIPPED + 1))
fi
fi
done
else
log_debug "No user onboarding flow files found"
fi
fi
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.

high

There is significant code duplication in the sections for processing authentication (lines 740-801), registration (803-860), and user onboarding (862-919) flows. The logic within each block is nearly identical. This makes the script harder to read and maintain, as any change to the flow processing logic would need to be manually applied in three separate places.

To improve maintainability, this repeated logic should be extracted into a reusable function. This function could take parameters like the flow type and directory path.

Comment on lines +1167 to +1186
elif [[ "$HTTP_CODE" == "400" ]] && [[ "$BODY" =~ (Application already exists|APP-1022) ]]; then
log_warning "DEVELOP application already exists, retrieving application ID..."
# Get existing DEVELOP application ID
RESPONSE=$(thunder_api_call GET "/applications")
HTTP_CODE="${RESPONSE: -3}"
BODY="${RESPONSE%???}"

if [[ "$HTTP_CODE" == "200" ]]; then
# Parse JSON to find DEVELOP application by client_id
DEVELOP_APP_ID=$(echo "$BODY" | sed 's/},{/}\n{/g' | grep '"client_id":"DEVELOP"' | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)

if [[ -n "$DEVELOP_APP_ID" ]]; then
log_success "Found DEVELOP application ID: $DEVELOP_APP_ID"
log_info "DEVELOP_APP_ID: $DEVELOP_APP_ID"
else
log_error "Could not find DEVELOP application in response"
fi
else
log_error "Failed to fetch applications (HTTP $HTTP_CODE)"
fi
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 logic inside this elif block for handling a 400 error is identical to the logic in the elif block for the 409 error (lines 1147-1166). This duplicates the code for retrieving an existing application ID.

These two conditions can be combined to avoid repetition. You can modify the condition on line 1147 and remove this duplicated block:

elif [[ "$HTTP_CODE" == "409" ]] || ([[ "$HTTP_CODE" == "400" ]] && [[ "$BODY" =~ (Application already exists|APP-1022) ]]); then
    # ... single implementation of the logic

Aravinda-HWK added a commit that referenced this pull request Mar 17, 2026
* feat(thunder): update Thunder image version to 0.24.0 and add sample resources script

* Refactor code structure for improved readability and maintainability

* feat(thunder): add default and sample resources scripts to Thunder service

* chore: update file permissions for default and sample resources scripts

* feat(thunder): update application ID references from Sample to DEVELOP in scripts and authentication flow

* feat(thunder): enhance DEVELOP_APP_ID extraction to handle case-insensitive formats in logs

* feat(socketmap): implement retry logic for Thunder authentication on startup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TASK] Create Bootstrap Scripts for Thunder

2 participants