mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
2 Commits
feat/plugi
...
jl/enterpr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a4138957c | ||
|
|
51ab4f3d73 |
85
enterprise/.env.example
Normal file
85
enterprise/.env.example
Normal file
@@ -0,0 +1,85 @@
|
||||
# Copy this file to .env and fill in the blanks.
|
||||
# Variables with defaults can be left as-is for local development.
|
||||
|
||||
# =============================================================================
|
||||
# Keycloak (Docker Compose)
|
||||
# =============================================================================
|
||||
KEYCLOAK_ADMIN_PASSWORD=admin
|
||||
KEYCLOAK_REALM_NAME=allhands
|
||||
KEYCLOAK_PROVIDER_NAME=github
|
||||
KEYCLOAK_CLIENT_ID=
|
||||
KEYCLOAK_CLIENT_SECRET=
|
||||
KEYCLOAK_SMTP_PASSWORD=
|
||||
|
||||
# URL used by the backend to reach Keycloak.
|
||||
# Use http://localhost:8080 when running the backend outside Docker Compose.
|
||||
KEYCLOAK_SERVER_URL=<auth_ngrok_url>
|
||||
KEYCLOAK_SERVER_URL_EXT=<auth_ngrok_url>
|
||||
|
||||
# Host values substituted into the Keycloak realm template.
|
||||
WEB_HOST=<app_ngrok_url>
|
||||
AUTH_WEB_HOST=<auth_ngrok_url>
|
||||
|
||||
# =============================================================================
|
||||
# GitHub OAuth App
|
||||
# =============================================================================
|
||||
GITHUB_APP_CLIENT_ID=
|
||||
GITHUB_APP_CLIENT_SECRET=
|
||||
GITHUB_APP_WEBHOOK_SECRET=
|
||||
GITHUB_APP_PRIVATE_KEY=
|
||||
GITHUB_APP_ID=
|
||||
|
||||
# =============================================================================
|
||||
# GitLab OAuth App
|
||||
# =============================================================================
|
||||
GITLAB_APP_CLIENT_ID=
|
||||
GITLAB_APP_CLIENT_SECRET=
|
||||
|
||||
# =============================================================================
|
||||
# Bitbucket OAuth App
|
||||
# =============================================================================
|
||||
BITBUCKET_APP_CLIENT_ID=
|
||||
BITBUCKET_APP_CLIENT_SECRET=
|
||||
|
||||
# =============================================================================
|
||||
# Bitbucket Data Center OAuth App
|
||||
# =============================================================================
|
||||
BITBUCKET_DATA_CENTER_HOST=
|
||||
BITBUCKET_DATA_CENTER_CLIENT_ID=
|
||||
BITBUCKET_DATA_CENTER_CLIENT_SECRET=
|
||||
|
||||
# =============================================================================
|
||||
# Database (Docker Compose uses these defaults)
|
||||
# =============================================================================
|
||||
DB_HOST=localhost
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_DB=openhands
|
||||
|
||||
# =============================================================================
|
||||
# Redis (Docker Compose starts redis on the default port)
|
||||
# =============================================================================
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
|
||||
# =============================================================================
|
||||
# LLM Proxy
|
||||
# =============================================================================
|
||||
LITE_LLM_API_URL=https://llm-proxy.eval.all-hands.dev
|
||||
LITE_LLM_API_KEY=
|
||||
LITELLM_DEFAULT_MODEL=litellm_proxy/claude-opus-4-5-20251101
|
||||
|
||||
# =============================================================================
|
||||
# Backend app config
|
||||
# =============================================================================
|
||||
OPENHANDS_CONFIG_CLS=server.config.SaaSServerConfig
|
||||
OPENHANDS_GITHUB_SERVICE_CLS=integrations.github.github_service.SaaSGitHubService
|
||||
OPENHANDS_GITLAB_SERVICE_CLS=integrations.gitlab.gitlab_service.SaaSGitLabService
|
||||
OPENHANDS_BITBUCKET_SERVICE_CLS=integrations.bitbucket.bitbucket_service.SaaSBitBucketService
|
||||
OPENHANDS_BITBUCKET_DATA_CENTER_SERVICE_CLS=integrations.bitbucket_data_center.bitbucket_dc_service.SaaSBitbucketDCService
|
||||
OPENHANDS_CONVERSATION_VALIDATOR_CLS=storage.saas_conversation_validator.SaasConversationValidator
|
||||
SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:main-nikolaik
|
||||
POSTHOG_CLIENT_KEY=test
|
||||
ENABLE_PROACTIVE_CONVERSATION_STARTERS=true
|
||||
MAX_CONCURRENT_CONVERSATIONS=10
|
||||
LOCAL_DEPLOYMENT=true
|
||||
77
enterprise/docker-compose.yml
Normal file
77
enterprise/docker-compose.yml
Normal file
@@ -0,0 +1,77 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: openhands
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
redis:
|
||||
image: redis:7
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:26.3.0
|
||||
ports:
|
||||
- "8180:8080"
|
||||
- "9000:9000"
|
||||
environment:
|
||||
KEYCLOAK_ADMIN: tmpadmin
|
||||
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
|
||||
KC_HEALTH_ENABLED: "true"
|
||||
KC_HOSTNAME: ${AUTH_WEB_HOST:-localhost}
|
||||
KC_PROXY_HEADERS: xforwarded
|
||||
command: start-dev
|
||||
|
||||
keycloak-config:
|
||||
image: alpine:3.19
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
apk add --no-cache curl jq gettext && sh /app/keycloak-config.sh
|
||||
volumes:
|
||||
- ./keycloak-config.sh:/app/keycloak-config.sh:ro
|
||||
- ./allhands-realm-github-provider.json.tmpl:/app/allhands-realm-github-provider.json.tmpl:ro
|
||||
environment:
|
||||
KEYCLOAK_SERVER_URL: http://keycloak:8080
|
||||
KEYCLOAK_MANAGEMENT_URL: http://keycloak:9000
|
||||
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
|
||||
KEYCLOAK_REALM_NAME: ${KEYCLOAK_REALM_NAME:-}
|
||||
KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID:-}
|
||||
KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET:-}
|
||||
KC_FEATURES: token-exchange,admin-fine-grained-authz
|
||||
WEB_HOST: ${WEB_HOST:-}
|
||||
AUTH_WEB_HOST: ${AUTH_WEB_HOST:-}
|
||||
GITHUB_APP_CLIENT_ID: ${GITHUB_APP_CLIENT_ID:-}
|
||||
GITHUB_APP_CLIENT_SECRET: ${GITHUB_APP_CLIENT_SECRET:-}
|
||||
GITLAB_APP_CLIENT_ID: ${GITLAB_APP_CLIENT_ID:-}
|
||||
GITLAB_APP_CLIENT_SECRET: ${GITLAB_APP_CLIENT_SECRET:-}
|
||||
BITBUCKET_APP_CLIENT_ID: ${BITBUCKET_APP_CLIENT_ID:-}
|
||||
BITBUCKET_APP_CLIENT_SECRET: ${BITBUCKET_APP_CLIENT_SECRET:-}
|
||||
BITBUCKET_DATA_CENTER_HOST: ${BITBUCKET_DATA_CENTER_HOST:-localhost}
|
||||
BITBUCKET_DATA_CENTER_CLIENT_ID: ${BITBUCKET_DATA_CENTER_CLIENT_ID:-}
|
||||
BITBUCKET_DATA_CENTER_CLIENT_SECRET: ${BITBUCKET_DATA_CENTER_CLIENT_SECRET:-}
|
||||
KEYCLOAK_SMTP_PASSWORD: ${KEYCLOAK_SMTP_PASSWORD:-}
|
||||
GITHUB_PROXY: "0"
|
||||
restart: "no"
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
@@ -109,6 +109,9 @@ lines.append(
|
||||
lines.append(
|
||||
'OPENHANDS_BITBUCKET_SERVICE_CLS=integrations.bitbucket.bitbucket_service.SaaSBitBucketService'
|
||||
)
|
||||
lines.append(
|
||||
'OPENHANDS_BITBUCKET_DATA_CENTER_SERVICE_CLS=integrations.bitbucket_data_center.bitbucket_dc_service.SaaSBitbucketDCService'
|
||||
)
|
||||
lines.append(
|
||||
'OPENHANDS_CONVERSATION_VALIDATOR_CLS=storage.saas_conversation_validator.SaasConversationValidator'
|
||||
)
|
||||
|
||||
90
enterprise/keycloak-config.sh
Executable file
90
enterprise/keycloak-config.sh
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
keycloak_api_call() {
|
||||
COMMAND=$1
|
||||
export RESPONSE=$(eval $COMMAND)
|
||||
ERROR=$(echo "$RESPONSE" | jq -r 'try if type == "array" then (.[0].error // .[0].errorMessage) else (.error // .errorMessage) end')
|
||||
if [ -n "$ERROR" ] && [ "null" != "$ERROR" ]; then
|
||||
echo "Error from Keycloak: $RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Waiting for Keycloak to be ready..."
|
||||
until curl --output /dev/null --silent --fail "${KEYCLOAK_MANAGEMENT_URL:-$KEYCLOAK_SERVER_URL}/health/ready"; do
|
||||
echo '.'
|
||||
sleep 5
|
||||
done
|
||||
|
||||
ACCESS_TOKEN=$(curl -s -X POST "$KEYCLOAK_SERVER_URL/realms/master/protocol/openid-connect/token" -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=admin-cli" -d "grant_type=password" -d "username=tmpadmin" -d "password=$KEYCLOAK_ADMIN_PASSWORD" | jq -r '.access_token')
|
||||
if [ "$ACCESS_TOKEN" = "null" ]; then
|
||||
NEW_ACCESS_TOKEN=$(curl -s -X POST "$KEYCLOAK_SERVER_URL/realms/master/protocol/openid-connect/token" -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=admin-cli" -d "grant_type=password" -d "username=admin" -d "password=$KEYCLOAK_ADMIN_PASSWORD" | jq -r '.access_token')
|
||||
if [ "$NEW_ACCESS_TOKEN" = "null" ]; then
|
||||
echo "Couldn't login using either the \"admin\" or \"tmpadmin\" accounts."
|
||||
exit 1;
|
||||
fi
|
||||
ACCESS_TOKEN=$NEW_ACCESS_TOKEN
|
||||
fi
|
||||
|
||||
keycloak_api_call "curl -s \"$KEYCLOAK_SERVER_URL/admin/realms/master/users?username=admin&exact=true\" -H \"Authorization: Bearer $ACCESS_TOKEN\""
|
||||
if [ "[]" = "$RESPONSE" ]; then
|
||||
echo "Creating new admin user..."
|
||||
keycloak_api_call "curl -s -X POST \"$KEYCLOAK_SERVER_URL/admin/realms/master/users\" \
|
||||
-H \"Authorization: Bearer $ACCESS_TOKEN\" \
|
||||
-H \"Content-Type: application/json\" \
|
||||
-d \"{
|
||||
\\\"username\\\": \\\"admin\\\",
|
||||
\\\"enabled\\\": true,
|
||||
\\\"emailVerified\\\": true,
|
||||
\\\"firstName\\\": \\\"Keycloak\\\",
|
||||
\\\"lastName\\\": \\\"Admin\\\",
|
||||
\\\"email\\\": \\\"admin@all-hands.dev\\\",
|
||||
\\\"credentials\\\": [{
|
||||
\\\"type\\\": \\\"password\\\",
|
||||
\\\"value\\\": \\\"$KEYCLOAK_ADMIN_PASSWORD\\\",
|
||||
\\\"temporary\\\": false
|
||||
}]
|
||||
}\""
|
||||
|
||||
keycloak_api_call "curl -s \"$KEYCLOAK_SERVER_URL/admin/realms/master/users?username=admin&exact=true\" -H \"Authorization: Bearer $ACCESS_TOKEN\""
|
||||
ADMIN_ID=$(echo "$RESPONSE" | jq -r '.[0].id')
|
||||
|
||||
keycloak_api_call "curl -s \"$KEYCLOAK_SERVER_URL/admin/realms/master/roles/admin\" -H \"Authorization: Bearer $ACCESS_TOKEN\" -H \"Content-Type: application/json\""
|
||||
ADMIN_ROLE=$(echo "$RESPONSE" | sed 's/"/\\"/g')
|
||||
|
||||
keycloak_api_call "curl -s \"$KEYCLOAK_SERVER_URL/admin/realms/master/roles/create-realm\" -H \"Authorization: Bearer $ACCESS_TOKEN\" -H \"Content-Type: application/json\""
|
||||
CREATE_ROLE=$(echo "$RESPONSE" | sed 's/"/\\"/g')
|
||||
|
||||
keycloak_api_call "curl -s -X POST \"$KEYCLOAK_SERVER_URL/admin/realms/master/users/$ADMIN_ID/role-mappings/realm\" \
|
||||
-H \"Authorization: Bearer $ACCESS_TOKEN\" \
|
||||
-H \"Content-Type: application/json\" \
|
||||
-d \"[$ADMIN_ROLE]\""
|
||||
keycloak_api_call "curl -s -X POST \"$KEYCLOAK_SERVER_URL/admin/realms/master/users/$ADMIN_ID/role-mappings/realm\" \
|
||||
-H \"Authorization: Bearer $ACCESS_TOKEN\" \
|
||||
-H \"Content-Type: application/json\" \
|
||||
-d \"[$CREATE_ROLE]\""
|
||||
|
||||
keycloak_api_call "curl -s -X POST \"$KEYCLOAK_SERVER_URL/realms/master/protocol/openid-connect/token\" -H \"Content-Type: application/x-www-form-urlencoded\" -d \"client_id=admin-cli\" -d \"grant_type=password\" -d \"username=admin\" -d \"password=$KEYCLOAK_ADMIN_PASSWORD\""
|
||||
ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r '.access_token')
|
||||
|
||||
keycloak_api_call "curl -s \"$KEYCLOAK_SERVER_URL/admin/realms/master/users?username=tmpadmin&exact=true\" -H \"Authorization: Bearer $ACCESS_TOKEN\""
|
||||
TMPADMIN_ID=$(echo "$RESPONSE" | jq -r '.[0].id')
|
||||
keycloak_api_call "curl -s -X DELETE \"$KEYCLOAK_SERVER_URL/admin/realms/master/users/$TMPADMIN_ID\" -H \"Authorization: Bearer $ACCESS_TOKEN\""
|
||||
|
||||
echo "Created new user \"admin\". The password is in KEYCLOAK_ADMIN_PASSWORD."
|
||||
else
|
||||
echo "User already exists: $RESPONSE"
|
||||
fi
|
||||
|
||||
ERROR_MESSAGE=$(curl -s "$KEYCLOAK_SERVER_URL/admin/realms/$KEYCLOAK_REALM_NAME" -H "Authorization: Bearer $ACCESS_TOKEN" | jq -r '.error')
|
||||
export GITHUB_BASE_URL=""
|
||||
if [ "$GITHUB_PROXY" = "1" ]; then
|
||||
export GITHUB_BASE_URL="https://${INGRESS_HOST:-localhost:3000}/github-proxy/test-auth-feat"
|
||||
fi
|
||||
if [ "$ERROR_MESSAGE" = "Realm not found." ]; then
|
||||
echo "Creating allhands realm..."
|
||||
envsubst '$WEB_HOST,$AUTH_WEB_HOST,$KEYCLOAK_REALM_NAME,$KEYCLOAK_PROVIDER_NAME,$KEYCLOAK_CLIENT_ID,$KEYCLOAK_CLIENT_SECRET,$GITHUB_APP_CLIENT_ID,$GITHUB_APP_CLIENT_SECRET,$GITLAB_APP_CLIENT_ID,$GITLAB_APP_CLIENT_SECRET,$BITBUCKET_APP_CLIENT_ID,$BITBUCKET_APP_CLIENT_SECRET,$GITHUB_BASE_URL,$KEYCLOAK_SMTP_PASSWORD,$BITBUCKET_DATA_CENTER_HOST,$BITBUCKET_DATA_CENTER_CLIENT_ID,$BITBUCKET_DATA_CENTER_CLIENT_SECRET' < /app/allhands-realm-github-provider.json.tmpl > /tmp/allhands-realm-github-provider.json
|
||||
keycloak_api_call "jq -c '.' /tmp/allhands-realm-github-provider.json | curl -s -X POST \"$KEYCLOAK_SERVER_URL/admin/realms\" -H \"Authorization: Bearer $ACCESS_TOKEN\" -H \"Content-Type: application/json\" -d @-"
|
||||
echo "Created allhands realm."
|
||||
fi
|
||||
10
enterprise/ngrok.yml
Normal file
10
enterprise/ngrok.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
version: "3"
|
||||
tunnels:
|
||||
openhands:
|
||||
proto: http
|
||||
addr: 3030
|
||||
domain: your-name.ngrok.io
|
||||
keycloak:
|
||||
proto: http
|
||||
addr: 8180
|
||||
domain: auth.your-name.ngrok.io
|
||||
Reference in New Issue
Block a user