Ok we have two working agents now as examples
modified: .gitignore modified: README.rst modified: app.py modified: templates/login.html.j2 modified: templates/verify.html.j2 deleted: test_agent.sh new file: test_jwt_owner_agent.sh new file: test_otp_cookie_agent.sh
This commit is contained in:
parent
2a6d55b623
commit
c5cc687dcf
8 changed files with 510 additions and 151 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -4,3 +4,5 @@ env/
|
|||
__pycache__/
|
||||
cookies.txt
|
||||
data/
|
||||
|
||||
*.html
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ PyraFiles uses a passwordless login system for users:
|
|||
JWT Agent Authentication
|
||||
========================
|
||||
|
||||
Agents authenticate with PyraFiles using JWT tokens, allowing programmatic interaction:
|
||||
Agents authenticate with PyraFiles using Cookies or JWT tokens, allowing programmatic interaction:
|
||||
|
||||
1. **Generate Agent JWT**
|
||||
|
||||
|
|
|
|||
1
app.py
1
app.py
|
|
@ -229,6 +229,7 @@ def generate_jwt_token(agent):
|
|||
"agent_id": agent.id,
|
||||
"agent_name": agent.name,
|
||||
"namespace_id": agent.namespace_id,
|
||||
"namespace_short_id": agent.namespace.short_id,
|
||||
"role": agent.role,
|
||||
"token_version": agent.token_version,
|
||||
"iat": datetime.datetime.utcnow(),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
<p>Enter your email to receive a 6-digit verification code.</p>
|
||||
<form method="POST">
|
||||
<label for="email">Email:</label>
|
||||
<input type="hidden" name="csrf_token" value="{{ request.session.get_csrf_token() }}">
|
||||
<input type="email" name="email" required>
|
||||
<button type="submit">Get Verification Code</button>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
<p>A 6-digit code has been sent to your email. Please enter it below to verify your account.</p>
|
||||
<form method="POST">
|
||||
<label for="code">Verification Code:</label>
|
||||
input type="hidden" name="csrf_token" value="{{ request.session.get_csrf_token() }}">
|
||||
<input type="text" name="code" required pattern="\d{6}" minlength="6" maxlength="6" inputmode="numeric">
|
||||
<button type="submit">Verify</button>
|
||||
</form>
|
||||
|
|
|
|||
150
test_agent.sh
150
test_agent.sh
|
|
@ -1,150 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# A Bash script to demonstrate and test multiple authenticated routes
|
||||
# in the pyrafiles application (or a similar Pyramid-based app).
|
||||
#
|
||||
# This script:
|
||||
# 1. Checks if cookies.txt is older than a defined threshold (default 1 year).
|
||||
# - If too old, it deletes cookies.txt so we force re-login.
|
||||
# 2. If cookies.txt is still valid or absent, proceeds to the next steps:
|
||||
# - If cookies.txt does not exist or was removed, it does the email + OTP flow.
|
||||
# - If cookies.txt exists and is fresh, it reuses that session.
|
||||
# 3. Fetches and prints the Profile page (GET /auth/profile) as a quick test.
|
||||
# 4. Uploads a sample media file (POST /media/upload), marked public.
|
||||
# 5. Parses the server's redirect "Location" header to extract user_short_id and media_short_id.
|
||||
# 6. Views media details (GET /media/<user_short_id>/<media_short_id>/details).
|
||||
# 7. Edits the newly uploaded media (POST /media/<user_short_id>/<media_short_id>/edit).
|
||||
# 8. Deletes the media (POST /media/<user_short_id>/<media_short_id>/delete).
|
||||
# 9. Lists all public media (GET /media/list).
|
||||
# 10. Logs out (GET /auth/logout).
|
||||
#
|
||||
# The script exits on the first command error (set -e).
|
||||
# Adjust BASE_URL, EMAIL, and file paths as needed.
|
||||
#
|
||||
# Usage:
|
||||
# MAX_COOKIE_AGE=<seconds> ./test_all_routes.sh /path/to/somefile.jpg
|
||||
# By default, MAX_COOKIE_AGE=31536000 (1 year).
|
||||
# If cookies.txt is younger than that, we skip re-login. Otherwise, we do the OTP flow.
|
||||
#
|
||||
# By default, uses http://localhost:6544 and agent@example.com
|
||||
|
||||
set -e # Exit on first error
|
||||
|
||||
BASE_URL="${BASE_URL:-http://localhost:6544}"
|
||||
EMAIL="${EMAIL:-agent@example.com}"
|
||||
MEDIA_FILE="$1"
|
||||
MAX_COOKIE_AGE="${MAX_COOKIE_AGE:-31536000}" # default ~1 year in seconds
|
||||
|
||||
if [[ -z "$MEDIA_FILE" ]]; then
|
||||
echo "Usage: MAX_COOKIE_AGE=<seconds> $0 /path/to/mediafile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Checking cookies.txt freshness ==="
|
||||
|
||||
COOKIE_FRESH=0
|
||||
if [[ -f cookies.txt ]]; then
|
||||
# 'stat -c %Y' returns the file modification time on Linux
|
||||
# 'stat -f %m' does the same on BSD/macOS
|
||||
MOD_TIME=$(stat -c %Y cookies.txt 2>/dev/null || stat -f %m cookies.txt 2>/dev/null)
|
||||
NOW=$(date +%s)
|
||||
AGE=$((NOW - MOD_TIME))
|
||||
|
||||
echo "cookies.txt is $AGE seconds old, threshold is $MAX_COOKIE_AGE."
|
||||
if (( AGE < MAX_COOKIE_AGE )); then
|
||||
COOKIE_FRESH=1
|
||||
echo "Reusing existing cookies.txt (fresh enough)."
|
||||
else
|
||||
echo "cookies.txt is too old. Removing it to force re-login."
|
||||
rm -f cookies.txt
|
||||
fi
|
||||
else
|
||||
echo "No cookies.txt found. A new session will be created."
|
||||
fi
|
||||
|
||||
if [[ "$COOKIE_FRESH" -eq 0 ]]; then
|
||||
echo ""
|
||||
echo "=== Starting new session and logging in with OTP flow ==="
|
||||
curl -s -i -c cookies.txt -b cookies.txt "$BASE_URL/" >/dev/null
|
||||
|
||||
echo "Logging in with email: $EMAIL (POST /auth/login)"
|
||||
curl -s -i -c cookies.txt -b cookies.txt \
|
||||
-X POST \
|
||||
-F "email=$EMAIL" \
|
||||
"$BASE_URL/auth/login"
|
||||
|
||||
echo ""
|
||||
echo "Check your server logs or email for the 6-digit verification code."
|
||||
read -p "Enter the 6-digit code: " VERIFICATION_CODE
|
||||
|
||||
echo "Verifying code (POST /auth/verify)"
|
||||
curl -s -i -c cookies.txt -b cookies.txt \
|
||||
-X POST \
|
||||
-F "code=$VERIFICATION_CODE" \
|
||||
"$BASE_URL/auth/verify"
|
||||
|
||||
echo "Login/OTP flow complete. cookies.txt saved."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Quick check: Fetching Profile page (GET /auth/profile) ==="
|
||||
curl -i -c cookies.txt -b cookies.txt "$BASE_URL/auth/profile"
|
||||
|
||||
echo ""
|
||||
echo "=== Uploading media (POST /media/upload) as public ==="
|
||||
UPLOAD_RESPONSE=$(curl -i -c cookies.txt -b cookies.txt \
|
||||
-X POST \
|
||||
-F "title=TestUploadByAgent" \
|
||||
-F "media_file=@${MEDIA_FILE}" \
|
||||
-F "is_public=on" \
|
||||
"$BASE_URL/media/upload")
|
||||
|
||||
echo "$UPLOAD_RESPONSE"
|
||||
echo ""
|
||||
echo "Parsing 'Location' header from upload response..."
|
||||
|
||||
LOCATION_HEADER=$(echo "$UPLOAD_RESPONSE" | grep -i "^Location:" | head -n1 | sed 's/\r//g')
|
||||
LOCATION_URL="${LOCATION_HEADER#Location: }"
|
||||
LOCATION_URL=$(echo "$LOCATION_URL" | tr -d '[:space:]')
|
||||
LOCATION_PATH="${LOCATION_URL#*//*/}"
|
||||
|
||||
USER_SHORT_ID=$(echo "$LOCATION_PATH" | cut -d'/' -f2)
|
||||
MEDIA_SHORT_ID=$(echo "$LOCATION_PATH" | cut -d'/' -f3)
|
||||
|
||||
echo "user_short_id=$USER_SHORT_ID"
|
||||
echo "media_short_id=$MEDIA_SHORT_ID"
|
||||
|
||||
if [[ -z "$USER_SHORT_ID" || -z "$MEDIA_SHORT_ID" ]]; then
|
||||
echo "Failed to parse user or media short ID. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Viewing media details (GET /media/$USER_SHORT_ID/$MEDIA_SHORT_ID/details) ==="
|
||||
curl -i -c cookies.txt -b cookies.txt \
|
||||
"$BASE_URL/media/$USER_SHORT_ID/$MEDIA_SHORT_ID/details"
|
||||
|
||||
echo ""
|
||||
echo "=== Editing media title to 'RenamedByAgent' (POST /media/$USER_SHORT_ID/$MEDIA_SHORT_ID/edit) ==="
|
||||
curl -i -c cookies.txt -b cookies.txt \
|
||||
-X POST \
|
||||
-F "title=RenamedByAgent" \
|
||||
"$BASE_URL/media/$USER_SHORT_ID/$MEDIA_SHORT_ID/edit"
|
||||
|
||||
echo ""
|
||||
echo "=== Deleting the media (POST /media/$USER_SHORT_ID/$MEDIA_SHORT_ID/delete) ==="
|
||||
curl -i -c cookies.txt -b cookies.txt \
|
||||
-X POST \
|
||||
"$BASE_URL/media/$USER_SHORT_ID/$MEDIA_SHORT_ID/delete"
|
||||
|
||||
echo ""
|
||||
echo "=== Listing all public media (GET /media/list) ==="
|
||||
curl -i -c cookies.txt -b cookies.txt "$BASE_URL/media/list"
|
||||
|
||||
echo ""
|
||||
echo "=== Logging out (GET /auth/logout) ==="
|
||||
curl -i -c cookies.txt -b cookies.txt "$BASE_URL/auth/logout"
|
||||
|
||||
echo ""
|
||||
echo "Test complete. We exercised multiple routes, including login/verify, profile, "
|
||||
echo "upload/delete/edit media, and logout. If no errors appeared, all requests succeeded!"
|
||||
214
test_jwt_owner_agent.sh
Normal file
214
test_jwt_owner_agent.sh
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
# Uncomment the next line for debugging
|
||||
#set -x
|
||||
|
||||
# A Bash script to test endpoints using a JWT token with owner privileges.
|
||||
# All operations are performed on the namespace associated with the agent.
|
||||
|
||||
# This script:
|
||||
# 1. Extracts the namespace ID and namespace short ID from the JWT token.
|
||||
# 2. Performs the following actions within that namespace:
|
||||
# - Uploads media.
|
||||
# - Edits and deletes the media.
|
||||
# - Creates a temporary agent JWT.
|
||||
# - Deletes the temporary agent using the agent_id from the agent JWT.
|
||||
# 3. Does not invite a user.
|
||||
# 4. Does not require any changes to the application.
|
||||
|
||||
# Usage:
|
||||
# JWT_TOKEN=your_owner_jwt_token ./jwt_owner_example.sh /path/to/mediafile.jpg
|
||||
|
||||
# Prerequisites:
|
||||
# - Replace 'your_owner_jwt_token' with a valid JWT token with 'owner' role.
|
||||
# - `curl` and `python` must be installed.
|
||||
# - Adjust BASE_URL as needed.
|
||||
|
||||
# Variables
|
||||
BASE_URL="${BASE_URL:-http://localhost:6544}"
|
||||
JWT_TOKEN="${JWT_TOKEN:-}"
|
||||
MEDIA_FILE="${1:-}"
|
||||
|
||||
if [[ -z "$MEDIA_FILE" ]]; then
|
||||
echo "Usage: JWT_TOKEN=your_owner_jwt_token $0 /path/to/mediafile.jpg"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$JWT_TOKEN" ]]; then
|
||||
echo "Error: JWT_TOKEN environment variable is not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
}
|
||||
}
|
||||
|
||||
echo "Extracting namespace IDs from JWT token..."
|
||||
|
||||
# Extract the payload from the JWT
|
||||
PAYLOAD_BASE64=$(echo "$JWT_TOKEN" | cut -d "." -f2)
|
||||
if [[ -z "$PAYLOAD_BASE64" ]]; then
|
||||
echo "Failed to extract payload from JWT token. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Decode the payload
|
||||
PAYLOAD_JSON=$(urlsafe_base64_decode "$PAYLOAD_BASE64")
|
||||
if [[ -z "$PAYLOAD_JSON" ]]; then
|
||||
echo "Failed to decode JWT payload. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use python to parse JSON and extract the necessary fields
|
||||
NAMESPACE_ID=$(python -c "import sys, json; print(json.loads(sys.stdin.read()).get('namespace_id', ''))" <<< "$PAYLOAD_JSON")
|
||||
NAMESPACE_SHORT_ID=$(python -c "import sys, json; print(json.loads(sys.stdin.read()).get('namespace_short_id', ''))" <<< "$PAYLOAD_JSON")
|
||||
|
||||
if [[ -z "$NAMESPACE_ID" ]]; then
|
||||
echo "Namespace ID not found in JWT token. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$NAMESPACE_SHORT_ID" ]]; then
|
||||
echo "Namespace Short ID not found in JWT token. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Namespace ID: $NAMESPACE_ID"
|
||||
echo "Namespace Short ID: $NAMESPACE_SHORT_ID"
|
||||
|
||||
echo ""
|
||||
echo "=== Uploading Media (POST /namespace/$NAMESPACE_SHORT_ID/media/upload) ==="
|
||||
UPLOAD_RESPONSE=$(curl -s -i \
|
||||
-X POST \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
-F "media_file=@${MEDIA_FILE}" \
|
||||
-F "title=SampleMediaByOwner" \
|
||||
-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 \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
-F "title=UpdatedByOwner" \
|
||||
"$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 \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
"$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 JWT (POST /namespace/$NAMESPACE_SHORT_ID/generate_agent_jwt) ==="
|
||||
AGENT_NAME="TempAgent$(date +%s)"
|
||||
GENERATE_JWT_RESPONSE=$(curl -s \
|
||||
-X POST \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
-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_JWT_RESPONSE" | grep -oP '(?<=<pre>)[^<]*(?=</pre>)')
|
||||
|
||||
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..."
|
||||
|
||||
# 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' ==="
|
||||
|
||||
# Delete the temporary agent
|
||||
DELETE_AGENT_RESPONSE=$(curl -s -i \
|
||||
-X POST \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
"$BASE_URL/namespace/$NAMESPACE_SHORT_ID/agents/$AGENT_ID/delete")
|
||||
|
||||
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 a JWT token with owner privileges on the specified namespace."
|
||||
290
test_otp_cookie_agent.sh
Normal file
290
test_otp_cookie_agent.sh
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
#!/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 '(?<=<pre>)[^<]*(?=</pre>)')
|
||||
|
||||
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."
|
||||
Loading…
Add table
Add a link
Reference in a new issue