#!/usr/bin/env bash set -euo pipefail # Uncomment the next line for debugging # set -x # A Bash script to test endpoints using cookie-based session authentication with OTP verification. # All operations are performed on a namespace accessible to the authenticated user. # This script: # 1. Authenticates a user by: # - Sending a login request with an email address. # - Prompting for the OTP code to complete verification. # 2. Checks for existing namespaces; if none, creates a new one. # 3. Performs the following actions within the namespace: # - Uploads media. # - Edits and deletes the media. # - Creates a temporary agent. # - Deletes the temporary agent. # 4. Uses cookie-based session authentication for all requests. # 5. Follows the routes specified in the provided OpenAPI specification. # Usage: # ./test_otp_cookie_agent.sh [email] /path/to/mediafile.jpg # Prerequisites: # - Replace 'email' with the user's email address. # - 'curl' and 'python' must be installed. # - Adjust BASE_URL as needed. # Variables BASE_URL="${BASE_URL:-http://localhost:6544}" EMAIL="${1:-}" MEDIA_FILE="${2:-}" if [[ -z "$EMAIL" || -z "$MEDIA_FILE" ]]; then echo "Usage: $0 [email] /path/to/mediafile.jpg" exit 1 fi # Temporary file to store cookies COOKIE_JAR=$(mktemp) # Cleanup function to remove temporary files cleanup() { rm -f "$COOKIE_JAR" } trap cleanup EXIT echo "=== Starting Authentication Process ===" # Step 1: Send login request with email to receive OTP code echo "Sending login request for email: $EMAIL" LOGIN_RESPONSE=$(curl -s -i \ -X POST \ -c "$COOKIE_JAR" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "email=$EMAIL" \ "$BASE_URL/auth/login") # Check if the login request was successful if echo "$LOGIN_RESPONSE" | grep -q "302 Found"; then echo "Login request accepted. An OTP code has been sent to your email." else echo "Failed to initiate login process." exit 1 fi # Step 2: Prompt for OTP code read -p "Enter the OTP code received via email: " code echo "Verifying OTP code..." # Step 3: Send OTP code to complete verification VERIFY_RESPONSE=$(curl -s -i \ -X POST \ -b "$COOKIE_JAR" \ -c "$COOKIE_JAR" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "code=$code" \ "$BASE_URL/auth/verify") # Check if the verification was successful if echo "$VERIFY_RESPONSE" | grep -q "302 Found"; then echo "OTP verification successful. Authenticated." else echo "OTP verification failed. Please check the code and try again." exit 1 fi # Extract the session cookie SESSION_COOKIE=$(grep -E 'session' "$COOKIE_JAR" | tail -n 1) if [[ -z "$SESSION_COOKIE" ]]; then echo "Failed to retrieve session cookie." exit 1 fi echo "Session cookie retrieved." echo "" echo "=== Retrieving User Profile and Namespaces ===" # Step 4: Access user profile to retrieve owned namespaces PROFILE_RESPONSE=$(curl -s \ -b "$COOKIE_JAR" \ "$BASE_URL/auth/profile") # Check if access to profile was successful if [[ -z "$PROFILE_RESPONSE" ]]; then echo "Failed to retrieve user profile." exit 1 fi echo "Retrieved user profile." # Extract namespace_short_ids from the profile page # Exclude the 'create' link and any duplicates NAMESPACE_SHORT_IDS=$(echo "$PROFILE_RESPONSE" | grep -oP 'href="[^"]+/namespace/\K[^/"]+(?=/manage")' | sort | uniq) if [[ -z "$NAMESPACE_SHORT_IDS" ]]; then echo "No existing namespaces found. Creating a new namespace..." # Create a new namespace NEW_NAMESPACE_NAME="TestNamespace$(date +%s)" CREATE_NAMESPACE_RESPONSE=$(curl -s -i \ -X POST \ -b "$COOKIE_JAR" \ -F "name=$NEW_NAMESPACE_NAME" \ -F "is_public=on" \ "$BASE_URL/namespace/create") if echo "$CREATE_NAMESPACE_RESPONSE" | grep -q "302 Found"; then echo "Namespace '$NEW_NAMESPACE_NAME' created successfully." # Extract the new namespace_short_id from the 'Location' header NEW_NAMESPACE_LOCATION=$(echo "$CREATE_NAMESPACE_RESPONSE" | grep -Fi Location | tail -n 1 | tr -d '\r\n') NAMESPACE_SHORT_ID=$(echo "$NEW_NAMESPACE_LOCATION" | awk -F '/' '{print $(NF-1)}') if [[ -z "$NAMESPACE_SHORT_ID" ]]; then echo "Failed to extract new namespace_short_id from response." exit 1 fi else echo "Failed to create a new namespace." exit 1 fi else # Use the first existing namespace NAMESPACE_SHORT_ID=$(echo "$NAMESPACE_SHORT_IDS" | head -n1) echo "Namespace Short ID: $NAMESPACE_SHORT_ID" fi echo "" echo "=== Uploading Media (POST /namespace/$NAMESPACE_SHORT_ID/media/upload) ===" UPLOAD_RESPONSE=$(curl -s -i \ -X POST \ -b "$COOKIE_JAR" \ -F "media_file=@${MEDIA_FILE}" \ -F "title=SampleMediaByUser" \ -F "is_public=on" \ "$BASE_URL/namespace/$NAMESPACE_SHORT_ID/media/upload") echo "$UPLOAD_RESPONSE" echo "" echo "Parsing 'Location' header to get the media short ID..." MEDIA_SHORT_ID=$(echo "$UPLOAD_RESPONSE" | grep -Fi Location | tail -n 1 | awk -F '/' '{print $(NF-1)}' | tr -d '\r\n') if [[ -z "$MEDIA_SHORT_ID" ]]; then echo "Failed to obtain media short ID. Exiting." exit 1 fi echo "Media Short ID: $MEDIA_SHORT_ID" echo "" echo "=== Editing Media (POST /namespace/$NAMESPACE_SHORT_ID/media/$MEDIA_SHORT_ID/edit) ===" EDIT_RESPONSE=$(curl -s -i \ -X POST \ -b "$COOKIE_JAR" \ -F "title=UpdatedByUser" \ "$BASE_URL/namespace/$NAMESPACE_SHORT_ID/media/$MEDIA_SHORT_ID/edit") echo "$EDIT_RESPONSE" if echo "$EDIT_RESPONSE" | grep -q "403 Forbidden"; then echo "Error: Failed to edit media. You may not have permission." exit 1 fi echo "Media title updated." echo "" echo "=== Deleting Media (POST /namespace/$NAMESPACE_SHORT_ID/media/$MEDIA_SHORT_ID/delete) ===" DELETE_RESPONSE=$(curl -s -i \ -X POST \ -b "$COOKIE_JAR" \ "$BASE_URL/namespace/$NAMESPACE_SHORT_ID/media/$MEDIA_SHORT_ID/delete") echo "$DELETE_RESPONSE" if echo "$DELETE_RESPONSE" | grep -q "403 Forbidden"; then echo "Error: Failed to delete media. You may not have permission." exit 1 fi echo "Media deleted." echo "" echo "=== Generating Temporary Agent (POST /namespace/$NAMESPACE_SHORT_ID/generate_agent_jwt) ===" AGENT_NAME="TempAgent$(date +%s)" GENERATE_AGENT_RESPONSE=$(curl -s \ -X POST \ -b "$COOKIE_JAR" \ -F "agent_name=$AGENT_NAME" \ -F "agent_role=editor" \ "$BASE_URL/namespace/$NAMESPACE_SHORT_ID/generate_agent_jwt") # Extract the agent JWT token from the HTML response AGENT_JWT=$(echo "$GENERATE_AGENT_RESPONSE" | grep -oP '(?<=
)[^<]*(?=
)') if [[ -z "$AGENT_JWT" ]]; then echo "Failed to extract agent JWT token from response." exit 1 fi echo "Temporary agent '$AGENT_NAME' has been created." echo "" echo "Extracting agent ID from agent JWT token..." # Function to URL-safe base64 decode urlsafe_base64_decode() { local input="$1" local remainder=$(( ${#input} % 4 )) if [ $remainder -eq 2 ]; then input="${input}==" elif [ $remainder -eq 3 ]; then input="${input}=" elif [ $remainder -eq 1 ]; then input="${input}=" fi input=$(echo "$input" | tr '_-' '/+') echo "$input" | base64 --decode 2>/dev/null || { echo "Error: Failed to decode base64 input." exit 1 } } # Extract the payload from the agent JWT AGENT_PAYLOAD_BASE64=$(echo "$AGENT_JWT" | cut -d "." -f2) if [[ -z "$AGENT_PAYLOAD_BASE64" ]]; then echo "Failed to extract payload from agent JWT token. Exiting." exit 1 fi # Decode the payload AGENT_PAYLOAD_JSON=$(urlsafe_base64_decode "$AGENT_PAYLOAD_BASE64") if [[ -z "$AGENT_PAYLOAD_JSON" ]]; then echo "Failed to decode agent JWT payload. Exiting." exit 1 fi # Extract the agent_id from the payload AGENT_ID=$(python -c "import sys, json; print(json.loads(sys.stdin.read()).get('agent_id', ''))" <<< "$AGENT_PAYLOAD_JSON") if [[ -z "$AGENT_ID" ]]; then echo "Failed to extract agent_id from agent JWT payload. Exiting." exit 1 fi echo "Agent ID: $AGENT_ID" echo "" echo "=== Deleting Temporary Agent '$AGENT_NAME' ===" # Revoke (delete) the temporary agent DELETE_AGENT_RESPONSE=$(curl -s -i \ -X POST \ -b "$COOKIE_JAR" \ -F "agent_id=$AGENT_ID" \ "$BASE_URL/namespace/$NAMESPACE_SHORT_ID/revoke_agent") echo "$DELETE_AGENT_RESPONSE" if echo "$DELETE_AGENT_RESPONSE" | grep -q "403 Forbidden"; then echo "Error: Failed to delete temporary agent '$AGENT_NAME'. You may not have permission." exit 1 fi echo "Temporary agent '$AGENT_NAME' has been deleted." echo "" echo "Test complete. All actions performed successfully using cookie-based session authentication on the specified namespace."