16 changed files with 966 additions and 2379 deletions
-
22test/s3/iam/Dockerfile.debug
-
252test/s3/iam/KEYCLOAK.md
-
2test/s3/iam/Makefile
-
156test/s3/iam/Makefile.docker
-
231test/s3/iam/README-Docker.md
-
22test/s3/iam/docker-compose-simple.yml
-
151test/s3/iam/docker-compose.yml
-
294test/s3/iam/iam_config.github.json
-
294test/s3/iam/iam_config.json
-
346test/s3/iam/iam_config.local.json
-
138test/s3/iam/keycloak-realm.json
-
621test/s3/iam/setup_all_tests.sh
-
377test/s3/iam/setup_keycloak.sh
-
404test/s3/iam/setup_keycloak_docker.sh
-
23weed/iam/oidc/oidc_provider.go
-
12weed/iam/providers/provider.go
@ -0,0 +1,22 @@ |
|||
# Debug version of SeaweedFS with role mapping fixes |
|||
FROM alpine:latest |
|||
|
|||
# Install required packages |
|||
RUN apk add --no-cache \ |
|||
ca-certificates \ |
|||
curl \ |
|||
wget \ |
|||
bash |
|||
|
|||
# Copy the debug weed binary |
|||
COPY weed-debug /usr/bin/weed |
|||
RUN chmod +x /usr/bin/weed |
|||
|
|||
# Create working directory |
|||
WORKDIR /data |
|||
|
|||
# Expose common ports |
|||
EXPOSE 8080 8333 8888 9333 18080 18333 18888 19333 |
|||
|
|||
# Default command |
|||
CMD ["weed"] |
@ -1,252 +0,0 @@ |
|||
# Keycloak Integration for SeaweedFS S3 IAM Tests |
|||
|
|||
This document describes the integration of [Keycloak](https://github.com/keycloak/keycloak) as a real OIDC provider for SeaweedFS S3 IAM integration tests. |
|||
|
|||
## Overview |
|||
|
|||
The integration tests support both **mock OIDC** and **real Keycloak authentication**: |
|||
|
|||
- **Mock OIDC** (default): Fast, no dependencies, generates test JWT tokens locally |
|||
- **Keycloak OIDC** (optional): Real-world authentication using Keycloak as OIDC provider |
|||
|
|||
The test framework automatically detects if Keycloak is available and switches modes accordingly. |
|||
|
|||
## Architecture |
|||
|
|||
``` |
|||
┌─────────────┐ JWT Token ┌──────────────────┐ S3 API ┌─────────────────┐ |
|||
│ Keycloak │ ────────────► │ SeaweedFS S3 │ ────────► │ SeaweedFS │ |
|||
│ OIDC │ (Bearer) │ Gateway + IAM │ │ Storage │ |
|||
│ Provider │ │ │ │ │ |
|||
└─────────────┘ └──────────────────┘ └─────────────────┘ |
|||
``` |
|||
|
|||
1. **Test** authenticates user with Keycloak using username/password |
|||
2. **Keycloak** returns JWT access token with user roles and claims |
|||
3. **Test** creates S3 client with JWT Bearer token authentication |
|||
4. **SeaweedFS S3 Gateway** validates JWT token and enforces IAM policies |
|||
5. **S3 operations** are authorized based on user roles and attached policies |
|||
|
|||
## Quick Start |
|||
|
|||
### Option 1: Docker Compose (Recommended) |
|||
|
|||
Start everything with Docker Compose including Keycloak: |
|||
|
|||
```bash |
|||
cd test/s3/iam |
|||
make docker-test |
|||
``` |
|||
|
|||
This will: |
|||
- Start Keycloak with pre-configured realm and users |
|||
- Start SeaweedFS services (master, volume, filer, S3 gateway) |
|||
- Run Keycloak integration tests |
|||
- Clean up all services |
|||
|
|||
### Option 2: Manual Setup |
|||
|
|||
1. Start Keycloak manually: |
|||
```bash |
|||
docker run -p 8080:8080 \ |
|||
-e KEYCLOAK_ADMIN=admin \ |
|||
-e KEYCLOAK_ADMIN_PASSWORD=admin123 \ |
|||
-v $(pwd)/keycloak-realm.json:/opt/keycloak/data/import/realm.json \ |
|||
quay.io/keycloak/keycloak:26.0.7 start-dev --import-realm |
|||
``` |
|||
|
|||
2. Start SeaweedFS services: |
|||
```bash |
|||
make start-services |
|||
``` |
|||
|
|||
3. Run tests with Keycloak: |
|||
```bash |
|||
export KEYCLOAK_URL="http://localhost:8080" |
|||
make test-quick |
|||
``` |
|||
|
|||
## Configuration |
|||
|
|||
### Keycloak Realm Configuration |
|||
|
|||
The test realm (`seaweedfs-test`) includes: |
|||
|
|||
**Client:** |
|||
- **Client ID**: `seaweedfs-s3` |
|||
- **Client Secret**: `seaweedfs-s3-secret` |
|||
- **Direct Access**: Enabled (for username/password authentication) |
|||
|
|||
**Roles:** |
|||
- `s3-admin`: Full S3 access |
|||
- `s3-read-only`: Read-only S3 access |
|||
- `s3-read-write`: Read-write S3 access |
|||
|
|||
**Test Users:** |
|||
- `admin-user` (password: `admin123`) → `s3-admin` role |
|||
- `read-user` (password: `read123`) → `s3-read-only` role |
|||
- `write-user` (password: `write123`) → `s3-read-write` role |
|||
|
|||
### SeaweedFS IAM Configuration |
|||
|
|||
The IAM system maps Keycloak roles to SeaweedFS IAM roles: |
|||
|
|||
```json |
|||
{ |
|||
"roles": [ |
|||
{ |
|||
"roleName": "S3AdminRole", |
|||
"trustPolicy": { |
|||
"Principal": { "Federated": "keycloak-oidc" }, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"], |
|||
"Condition": { "StringEquals": { "roles": "s3-admin" } } |
|||
}, |
|||
"attachedPolicies": ["S3AdminPolicy"] |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
## Test Structure |
|||
|
|||
### Framework Detection |
|||
|
|||
The test framework automatically detects Keycloak availability: |
|||
|
|||
```go |
|||
// Check if Keycloak is running |
|||
framework.useKeycloak = framework.isKeycloakAvailable(keycloakURL) |
|||
|
|||
if framework.useKeycloak { |
|||
// Use real Keycloak authentication |
|||
token, err = framework.getKeycloakToken(username) |
|||
} else { |
|||
// Fall back to mock JWT tokens |
|||
token, err = framework.generateSTSSessionToken(username, roleName, time.Hour) |
|||
} |
|||
``` |
|||
|
|||
### Test Categories |
|||
|
|||
**Keycloak-Specific Tests** (`TestKeycloak*`): |
|||
- `TestKeycloakAuthentication`: Real authentication flow |
|||
- `TestKeycloakRoleMapping`: Role mapping from Keycloak to S3 policies |
|||
- `TestKeycloakTokenExpiration`: JWT token lifecycle |
|||
- `TestKeycloakS3Operations`: End-to-end S3 operations with real auth |
|||
|
|||
**General Tests** (work with both modes): |
|||
- `TestS3IAMAuthentication`: Basic authentication tests |
|||
- `TestS3IAMPolicyEnforcement`: Policy enforcement tests |
|||
- All other integration tests |
|||
|
|||
## Environment Variables |
|||
|
|||
- `KEYCLOAK_URL`: Keycloak base URL (default: `http://localhost:8080`) |
|||
- `S3_ENDPOINT`: SeaweedFS S3 endpoint (default: `http://localhost:8333`) |
|||
|
|||
## Docker Services |
|||
|
|||
The Docker Compose setup includes: |
|||
|
|||
```yaml |
|||
services: |
|||
keycloak: # Keycloak OIDC provider |
|||
seaweedfs-master: # SeaweedFS master server |
|||
seaweedfs-volume: # SeaweedFS volume server |
|||
seaweedfs-filer: # SeaweedFS filer server |
|||
seaweedfs-s3: # SeaweedFS S3 gateway with IAM |
|||
``` |
|||
|
|||
All services include health checks and proper dependencies. |
|||
|
|||
## Authentication Flow |
|||
|
|||
1. **Test requests authentication**: |
|||
```go |
|||
tokenResp, err := keycloakClient.AuthenticateUser("admin-user", "admin123") |
|||
``` |
|||
|
|||
2. **Keycloak returns JWT token** with claims: |
|||
```json |
|||
{ |
|||
"sub": "user-id", |
|||
"preferred_username": "admin-user", |
|||
"roles": ["s3-admin"], |
|||
"iss": "http://keycloak:8080/realms/seaweedfs-test" |
|||
} |
|||
``` |
|||
|
|||
3. **S3 client sends Bearer token**: |
|||
```http |
|||
GET / HTTP/1.1 |
|||
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... |
|||
``` |
|||
|
|||
4. **SeaweedFS validates token** and checks policies: |
|||
- Validate JWT signature with Keycloak JWKS |
|||
- Extract roles from token claims |
|||
- Map roles to IAM roles via trust policies |
|||
- Enforce attached IAM policies for S3 operations |
|||
|
|||
## Troubleshooting |
|||
|
|||
### Keycloak Not Available |
|||
|
|||
If Keycloak is not running, tests automatically fall back to mock mode: |
|||
|
|||
``` |
|||
Using mock OIDC server for testing |
|||
``` |
|||
|
|||
### Token Validation Errors |
|||
|
|||
Check that: |
|||
- Keycloak realm configuration matches `iam_config_docker.json` |
|||
- JWT signing algorithms are compatible (RS256/HS256) |
|||
- Trust policies correctly reference the Keycloak provider |
|||
|
|||
### Service Dependencies |
|||
|
|||
Docker Compose includes health checks. Monitor with: |
|||
|
|||
```bash |
|||
make docker-logs |
|||
``` |
|||
|
|||
### Authentication Failures |
|||
|
|||
Enable debug logging: |
|||
```bash |
|||
export KEYCLOAK_URL="http://localhost:8080" |
|||
go test -v -run "TestKeycloak" ./... |
|||
``` |
|||
|
|||
## Extending the Integration |
|||
|
|||
### Adding New Roles |
|||
|
|||
1. Update `keycloak-realm.json` with new roles |
|||
2. Add corresponding IAM role in `iam_config_docker.json` |
|||
3. Create trust policy mapping the Keycloak role |
|||
4. Define appropriate IAM policies for the role |
|||
|
|||
### Adding New Test Users |
|||
|
|||
1. Add user to `keycloak-realm.json` with credentials and roles |
|||
2. Add password mapping in `getTestUserPassword()` |
|||
3. Create tests for the new user's permissions |
|||
|
|||
### Custom OIDC Providers |
|||
|
|||
The framework can be extended to support other OIDC providers by: |
|||
1. Implementing the provider in the IAM integration system |
|||
2. Adding provider configuration to IAM config |
|||
3. Updating test framework authentication methods |
|||
|
|||
## Benefits |
|||
|
|||
- **Real-world validation**: Tests against actual OIDC provider |
|||
- **Production-like environment**: Mirrors real deployment scenarios |
|||
- **Comprehensive coverage**: Role mapping, token validation, policy enforcement |
|||
- **Automatic fallback**: Works without Keycloak dependencies |
|||
- **Easy CI/CD**: Docker Compose makes automation simple |
@ -0,0 +1,156 @@ |
|||
# Makefile for SeaweedFS S3 IAM Integration Tests with Docker Compose |
|||
.PHONY: help docker-build docker-up docker-down docker-logs docker-test docker-clean docker-status docker-keycloak-setup |
|||
|
|||
# Default target |
|||
.DEFAULT_GOAL := help |
|||
|
|||
# Docker Compose configuration |
|||
COMPOSE_FILE := docker-compose.yml |
|||
PROJECT_NAME := seaweedfs-iam-test |
|||
|
|||
help: ## Show this help message |
|||
@echo "SeaweedFS S3 IAM Integration Tests - Docker Compose" |
|||
@echo "" |
|||
@echo "Available commands:" |
|||
@echo "" |
|||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) |
|||
@echo "" |
|||
@echo "Environment:" |
|||
@echo " COMPOSE_FILE: $(COMPOSE_FILE)" |
|||
@echo " PROJECT_NAME: $(PROJECT_NAME)" |
|||
|
|||
docker-build: ## Build custom SeaweedFS image if needed |
|||
@echo "🔨 Building custom SeaweedFS image with debug changes..." |
|||
@if [ -f ../../../weed-debug ]; then \ |
|||
echo "Using debug binary for Docker image..."; \ |
|||
cp ../../../weed-debug ./weed-debug; \ |
|||
docker build -t seaweedfs-debug:latest -f Dockerfile.debug .; \ |
|||
rm -f ./weed-debug; \ |
|||
else \ |
|||
echo "No debug binary found, using standard image"; \ |
|||
fi |
|||
|
|||
docker-up: ## Start all services with Docker Compose |
|||
@echo "🚀 Starting SeaweedFS S3 IAM integration environment..." |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) up -d |
|||
@echo "" |
|||
@echo "✅ Environment started! Services will be available at:" |
|||
@echo " 🔐 Keycloak: http://localhost:8080 (admin/admin)" |
|||
@echo " 🗄️ S3 API: http://localhost:8333" |
|||
@echo " 📁 Filer: http://localhost:8888" |
|||
@echo " 🎯 Master: http://localhost:9333" |
|||
@echo "" |
|||
@echo "⏳ Waiting for all services to be healthy..." |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) ps |
|||
|
|||
docker-down: ## Stop and remove all containers |
|||
@echo "🛑 Stopping SeaweedFS S3 IAM integration environment..." |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) down -v |
|||
@echo "✅ Environment stopped and cleaned up" |
|||
|
|||
docker-restart: docker-down docker-up ## Restart the entire environment |
|||
|
|||
docker-logs: ## Show logs from all services |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) logs -f |
|||
|
|||
docker-logs-s3: ## Show logs from S3 service only |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) logs -f weed-s3 |
|||
|
|||
docker-logs-keycloak: ## Show logs from Keycloak service only |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) logs -f keycloak |
|||
|
|||
docker-status: ## Check status of all services |
|||
@echo "📊 Service Status:" |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) ps |
|||
@echo "" |
|||
@echo "🏥 Health Checks:" |
|||
@docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep $(PROJECT_NAME) || true |
|||
|
|||
docker-test: ## Run integration tests against Docker environment |
|||
@echo "🧪 Running SeaweedFS S3 IAM integration tests..." |
|||
@echo "⏳ Waiting for services to be ready..." |
|||
@timeout 120 bash -c 'until curl -s http://localhost:8333 > /dev/null 2>&1; do sleep 2; done' |
|||
@timeout 120 bash -c 'until curl -s http://localhost:8080/health/ready > /dev/null 2>&1; do sleep 2; done' |
|||
@echo "✅ Services are ready" |
|||
@echo "" |
|||
@KEYCLOAK_URL=http://localhost:8080 go test -v -timeout 10m ./... |
|||
|
|||
docker-test-single: ## Run a single test (use TEST_NAME=TestName) |
|||
@if [ -z "$(TEST_NAME)" ]; then \ |
|||
echo "❌ Please specify TEST_NAME, e.g., make docker-test-single TEST_NAME=TestKeycloakAuthentication"; \ |
|||
exit 1; \ |
|||
fi |
|||
@echo "🧪 Running single test: $(TEST_NAME)" |
|||
@KEYCLOAK_URL=http://localhost:8080 go test -v -run "$(TEST_NAME)" -timeout 5m ./... |
|||
|
|||
docker-keycloak-setup: ## Manually run Keycloak setup (usually automatic) |
|||
@echo "🔧 Running Keycloak setup manually..." |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) run --rm keycloak-setup |
|||
|
|||
docker-clean: ## Clean up everything (containers, volumes, images) |
|||
@echo "🧹 Cleaning up Docker environment..." |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) down -v --remove-orphans |
|||
@docker system prune -f |
|||
@echo "✅ Cleanup complete" |
|||
|
|||
docker-shell-s3: ## Get shell access to S3 container |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) exec weed-s3 sh |
|||
|
|||
docker-shell-keycloak: ## Get shell access to Keycloak container |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) exec keycloak bash |
|||
|
|||
docker-debug: ## Show debug information |
|||
@echo "🔍 Docker Environment Debug Information" |
|||
@echo "" |
|||
@echo "📋 Docker Compose Config:" |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) config |
|||
@echo "" |
|||
@echo "📊 Container Status:" |
|||
@docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) ps |
|||
@echo "" |
|||
@echo "🌐 Network Information:" |
|||
@docker network ls | grep $(PROJECT_NAME) || echo "No networks found" |
|||
@echo "" |
|||
@echo "💾 Volume Information:" |
|||
@docker volume ls | grep $(PROJECT_NAME) || echo "No volumes found" |
|||
|
|||
# Quick test targets |
|||
docker-test-auth: ## Quick test of authentication only |
|||
@KEYCLOAK_URL=http://localhost:8080 go test -v -run "TestKeycloakAuthentication" -timeout 2m ./... |
|||
|
|||
docker-test-roles: ## Quick test of role mapping only |
|||
@KEYCLOAK_URL=http://localhost:8080 go test -v -run "TestKeycloakRoleMapping" -timeout 2m ./... |
|||
|
|||
docker-test-s3ops: ## Quick test of S3 operations only |
|||
@KEYCLOAK_URL=http://localhost:8080 go test -v -run "TestKeycloakS3Operations" -timeout 2m ./... |
|||
|
|||
# Development workflow |
|||
docker-dev: docker-down docker-up docker-test ## Complete dev workflow: down -> up -> test |
|||
|
|||
# Show service URLs for easy access |
|||
docker-urls: ## Display all service URLs |
|||
@echo "🌐 Service URLs:" |
|||
@echo "" |
|||
@echo " 🔐 Keycloak Admin: http://localhost:8080 (admin/admin)" |
|||
@echo " 🔐 Keycloak Realm: http://localhost:8080/realms/seaweedfs-test" |
|||
@echo " 📁 S3 API: http://localhost:8333" |
|||
@echo " 📂 Filer UI: http://localhost:8888" |
|||
@echo " 🎯 Master UI: http://localhost:9333" |
|||
@echo " 💾 Volume Server: http://localhost:8080" |
|||
@echo "" |
|||
@echo " 📖 Test Users:" |
|||
@echo " • admin-user (password: adminuser123) - s3-admin role" |
|||
@echo " • read-user (password: readuser123) - s3-read-only role" |
|||
@echo " • write-user (password: writeuser123) - s3-read-write role" |
|||
@echo " • write-only-user (password: writeonlyuser123) - s3-write-only role" |
|||
|
|||
# Wait targets for CI/CD |
|||
docker-wait-healthy: ## Wait for all services to be healthy |
|||
@echo "⏳ Waiting for all services to be healthy..." |
|||
@timeout 300 bash -c ' \ |
|||
while [ $$(docker-compose -p $(PROJECT_NAME) -f $(COMPOSE_FILE) ps | grep -c "healthy") -lt 4 ]; do \ |
|||
echo "Waiting for services to be healthy..."; \ |
|||
sleep 5; \ |
|||
done \ |
|||
' |
|||
@echo "✅ All services are healthy" |
@ -0,0 +1,231 @@ |
|||
# SeaweedFS S3 IAM Integration with Docker Compose |
|||
|
|||
This directory contains a complete Docker Compose setup for testing SeaweedFS S3 IAM integration with Keycloak OIDC authentication. |
|||
|
|||
## 🚀 Quick Start |
|||
|
|||
1. **Start the environment:** |
|||
```bash |
|||
make -f Makefile.docker docker-up |
|||
``` |
|||
|
|||
2. **Run the tests:** |
|||
```bash |
|||
make -f Makefile.docker docker-test |
|||
``` |
|||
|
|||
3. **Stop the environment:** |
|||
```bash |
|||
make -f Makefile.docker docker-down |
|||
``` |
|||
|
|||
## 📋 What's Included |
|||
|
|||
The Docker Compose setup includes: |
|||
|
|||
- **🔐 Keycloak** - Identity provider with OIDC support |
|||
- **🎯 SeaweedFS Master** - Metadata management |
|||
- **💾 SeaweedFS Volume** - Data storage |
|||
- **📁 SeaweedFS Filer** - File system interface |
|||
- **📊 SeaweedFS S3** - S3-compatible API with IAM integration |
|||
- **🔧 Keycloak Setup** - Automated realm and user configuration |
|||
|
|||
## 🌐 Service URLs |
|||
|
|||
After starting with `docker-up`, services are available at: |
|||
|
|||
| Service | URL | Credentials | |
|||
|---------|-----|-------------| |
|||
| 🔐 Keycloak Admin | http://localhost:8080 | admin/admin | |
|||
| 📊 S3 API | http://localhost:8333 | JWT tokens | |
|||
| 📁 Filer | http://localhost:8888 | - | |
|||
| 🎯 Master | http://localhost:9333 | - | |
|||
|
|||
## 👥 Test Users |
|||
|
|||
The setup automatically creates test users in Keycloak: |
|||
|
|||
| Username | Password | Role | Permissions | |
|||
|----------|----------|------|-------------| |
|||
| admin-user | adminuser123 | s3-admin | Full S3 access | |
|||
| read-user | readuser123 | s3-read-only | Read-only access | |
|||
| write-user | writeuser123 | s3-read-write | Read and write | |
|||
| write-only-user | writeonlyuser123 | s3-write-only | Write only | |
|||
|
|||
## 🧪 Running Tests |
|||
|
|||
### All Tests |
|||
```bash |
|||
make -f Makefile.docker docker-test |
|||
``` |
|||
|
|||
### Specific Test Categories |
|||
```bash |
|||
# Authentication tests only |
|||
make -f Makefile.docker docker-test-auth |
|||
|
|||
# Role mapping tests only |
|||
make -f Makefile.docker docker-test-roles |
|||
|
|||
# S3 operations tests only |
|||
make -f Makefile.docker docker-test-s3ops |
|||
``` |
|||
|
|||
### Single Test |
|||
```bash |
|||
make -f Makefile.docker docker-test-single TEST_NAME=TestKeycloakAuthentication |
|||
``` |
|||
|
|||
## 🔧 Development Workflow |
|||
|
|||
### Complete workflow (recommended) |
|||
```bash |
|||
make -f Makefile.docker docker-dev |
|||
``` |
|||
This runs: down → up → test |
|||
|
|||
### Manual steps |
|||
```bash |
|||
# Start services |
|||
make -f Makefile.docker docker-up |
|||
|
|||
# Watch logs |
|||
make -f Makefile.docker docker-logs |
|||
|
|||
# Check status |
|||
make -f Makefile.docker docker-status |
|||
|
|||
# Run tests |
|||
make -f Makefile.docker docker-test |
|||
|
|||
# Stop services |
|||
make -f Makefile.docker docker-down |
|||
``` |
|||
|
|||
## 🔍 Debugging |
|||
|
|||
### View logs |
|||
```bash |
|||
# All services |
|||
make -f Makefile.docker docker-logs |
|||
|
|||
# S3 service only (includes role mapping debug) |
|||
make -f Makefile.docker docker-logs-s3 |
|||
|
|||
# Keycloak only |
|||
make -f Makefile.docker docker-logs-keycloak |
|||
``` |
|||
|
|||
### Get shell access |
|||
```bash |
|||
# S3 container |
|||
make -f Makefile.docker docker-shell-s3 |
|||
|
|||
# Keycloak container |
|||
make -f Makefile.docker docker-shell-keycloak |
|||
``` |
|||
|
|||
### Debug environment |
|||
```bash |
|||
make -f Makefile.docker docker-debug |
|||
``` |
|||
|
|||
## 📁 File Structure |
|||
|
|||
``` |
|||
seaweedfs/test/s3/iam/ |
|||
├── docker-compose.yml # Main Docker Compose configuration |
|||
├── Makefile.docker # Docker-specific Makefile |
|||
├── Dockerfile.debug # Debug SeaweedFS image |
|||
├── setup_keycloak_docker.sh # Keycloak setup for containers |
|||
├── README-Docker.md # This file |
|||
├── iam_config.json # IAM configuration (auto-generated) |
|||
├── test_config.json # S3 service configuration |
|||
└── *_test.go # Go integration tests |
|||
``` |
|||
|
|||
## 🔄 Configuration |
|||
|
|||
### IAM Configuration |
|||
The `setup_keycloak_docker.sh` script automatically generates `iam_config.json` with: |
|||
|
|||
- **OIDC Provider**: Keycloak configuration with proper container networking |
|||
- **Role Mapping**: Maps Keycloak roles to SeaweedFS IAM roles |
|||
- **Policies**: Defines S3 permissions for each role |
|||
- **Trust Relationships**: Allows Keycloak users to assume SeaweedFS roles |
|||
|
|||
### Role Mapping Rules |
|||
```json |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-admin", |
|||
"role": "arn:seaweed:iam::role/KeycloakAdminRole" |
|||
} |
|||
``` |
|||
|
|||
## 🐛 Troubleshooting |
|||
|
|||
### Services not starting |
|||
```bash |
|||
# Check service status |
|||
make -f Makefile.docker docker-status |
|||
|
|||
# View logs for specific service |
|||
docker-compose -p seaweedfs-iam-test logs <service-name> |
|||
``` |
|||
|
|||
### Keycloak setup issues |
|||
```bash |
|||
# Re-run Keycloak setup manually |
|||
make -f Makefile.docker docker-keycloak-setup |
|||
|
|||
# Check Keycloak logs |
|||
make -f Makefile.docker docker-logs-keycloak |
|||
``` |
|||
|
|||
### Role mapping not working |
|||
```bash |
|||
# Check S3 logs for role mapping debug messages |
|||
make -f Makefile.docker docker-logs-s3 | grep -i "role\|claim\|mapping" |
|||
``` |
|||
|
|||
### Port conflicts |
|||
If ports are already in use, modify `docker-compose.yml`: |
|||
```yaml |
|||
ports: |
|||
- "8081:8080" # Change external port |
|||
``` |
|||
|
|||
## 🧹 Cleanup |
|||
|
|||
```bash |
|||
# Stop containers and remove volumes |
|||
make -f Makefile.docker docker-down |
|||
|
|||
# Complete cleanup (containers, volumes, images) |
|||
make -f Makefile.docker docker-clean |
|||
``` |
|||
|
|||
## 🎯 Key Features |
|||
|
|||
- **Isolated Environment**: No conflicts with local services |
|||
- **Consistent Networking**: Services communicate via Docker network |
|||
- **Automated Setup**: Keycloak realm and users created automatically |
|||
- **Debug Logging**: Verbose logging enabled for troubleshooting |
|||
- **Health Checks**: Proper service dependency management |
|||
- **Volume Persistence**: Data persists between restarts (until docker-down) |
|||
|
|||
## 🚦 CI/CD Integration |
|||
|
|||
For automated testing: |
|||
|
|||
```bash |
|||
# Wait for all services to be healthy |
|||
make -f Makefile.docker docker-wait-healthy |
|||
|
|||
# Run tests with proper cleanup |
|||
make -f Makefile.docker docker-up |
|||
make -f Makefile.docker docker-wait-healthy |
|||
make -f Makefile.docker docker-test |
|||
make -f Makefile.docker docker-down |
|||
``` |
@ -0,0 +1,22 @@ |
|||
version: '3.8' |
|||
|
|||
services: |
|||
# Keycloak Identity Provider |
|||
keycloak: |
|||
image: quay.io/keycloak/keycloak:26.0.7 |
|||
container_name: keycloak-test-simple |
|||
ports: |
|||
- "8080:8080" |
|||
environment: |
|||
KC_BOOTSTRAP_ADMIN_USERNAME: admin |
|||
KC_BOOTSTRAP_ADMIN_PASSWORD: admin |
|||
KC_HTTP_ENABLED: "true" |
|||
KC_HOSTNAME_STRICT: "false" |
|||
KC_HOSTNAME_STRICT_HTTPS: "false" |
|||
command: start-dev |
|||
networks: |
|||
- test-network |
|||
|
|||
networks: |
|||
test-network: |
|||
driver: bridge |
@ -1,121 +1,162 @@ |
|||
version: '3.8' |
|||
|
|||
services: |
|||
# Keycloak OIDC Provider |
|||
# Keycloak Identity Provider |
|||
keycloak: |
|||
image: quay.io/keycloak/keycloak:26.0.7 |
|||
container_name: keycloak-iam-test |
|||
hostname: keycloak |
|||
environment: |
|||
KEYCLOAK_ADMIN: admin |
|||
KEYCLOAK_ADMIN_PASSWORD: admin123 |
|||
KC_HTTP_PORT: 8080 |
|||
KC_HOSTNAME_STRICT: false |
|||
KC_HOSTNAME_STRICT_HTTPS: false |
|||
KC_HTTP_ENABLED: true |
|||
KC_HEALTH_ENABLED: true |
|||
KC_BOOTSTRAP_ADMIN_USERNAME: admin |
|||
KC_BOOTSTRAP_ADMIN_PASSWORD: admin |
|||
KC_HTTP_ENABLED: "true" |
|||
KC_HOSTNAME_STRICT: "false" |
|||
KC_HOSTNAME_STRICT_HTTPS: "false" |
|||
KC_HTTP_RELATIVE_PATH: / |
|||
ports: |
|||
- "8080:8080" |
|||
command: |
|||
- start-dev |
|||
- --import-realm |
|||
volumes: |
|||
- ./keycloak-realm.json:/opt/keycloak/data/import/realm.json:ro |
|||
command: start-dev |
|||
networks: |
|||
- seaweedfs-iam |
|||
healthcheck: |
|||
test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"] |
|||
interval: 30s |
|||
timeout: 10s |
|||
interval: 10s |
|||
timeout: 5s |
|||
retries: 5 |
|||
start_period: 60s |
|||
networks: |
|||
- seaweedfs-iam |
|||
|
|||
# SeaweedFS Master |
|||
seaweedfs-master: |
|||
weed-master: |
|||
image: chrislusf/seaweedfs:latest |
|||
container_name: seaweedfs-master-iam |
|||
container_name: weed-master |
|||
hostname: weed-master |
|||
ports: |
|||
- "9333:9333" |
|||
- "19333:19333" |
|||
command: master -ip=seaweedfs-master -mdir=/data -volumeSizeLimitMB=50 |
|||
command: "master -ip=weed-master -port=9333 -mdir=/data" |
|||
volumes: |
|||
- seaweedfs-master-data:/data |
|||
- master-data:/data |
|||
networks: |
|||
- seaweedfs-iam |
|||
healthcheck: |
|||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:9333/cluster/status"] |
|||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:9333/cluster/status"] |
|||
interval: 10s |
|||
timeout: 5s |
|||
retries: 3 |
|||
networks: |
|||
- seaweedfs-iam |
|||
start_period: 10s |
|||
|
|||
# SeaweedFS Volume Server |
|||
seaweedfs-volume: |
|||
weed-volume: |
|||
image: chrislusf/seaweedfs:latest |
|||
container_name: seaweedfs-volume-iam |
|||
container_name: weed-volume |
|||
hostname: weed-volume |
|||
ports: |
|||
- "8080:8080" |
|||
command: volume -ip=seaweedfs-volume -port=8080 -mserver=seaweedfs-master:9333 -dir=/data |
|||
- "18080:18080" |
|||
command: "volume -ip=weed-volume -port=8080 -dir=/data -mserver=weed-master:9333 -dataCenter=dc1 -rack=rack1" |
|||
volumes: |
|||
- seaweedfs-volume-data:/data |
|||
- volume-data:/data |
|||
networks: |
|||
- seaweedfs-iam |
|||
depends_on: |
|||
seaweedfs-master: |
|||
weed-master: |
|||
condition: service_healthy |
|||
healthcheck: |
|||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/status"] |
|||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/status"] |
|||
interval: 10s |
|||
timeout: 5s |
|||
retries: 3 |
|||
networks: |
|||
- seaweedfs-iam |
|||
start_period: 10s |
|||
|
|||
# SeaweedFS Filer |
|||
seaweedfs-filer: |
|||
weed-filer: |
|||
image: chrislusf/seaweedfs:latest |
|||
container_name: seaweedfs-filer-iam |
|||
container_name: weed-filer |
|||
hostname: weed-filer |
|||
ports: |
|||
- "8888:8888" |
|||
- "18888:18888" |
|||
command: filer -ip=seaweedfs-filer -master=seaweedfs-master:9333 |
|||
command: "filer -ip=weed-filer -port=8888 -master=weed-master:9333 -defaultStoreDir=/data" |
|||
volumes: |
|||
- filer-data:/data |
|||
networks: |
|||
- seaweedfs-iam |
|||
depends_on: |
|||
seaweedfs-master: |
|||
weed-master: |
|||
condition: service_healthy |
|||
weed-volume: |
|||
condition: service_healthy |
|||
healthcheck: |
|||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8888/"] |
|||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8888/status"] |
|||
interval: 10s |
|||
timeout: 5s |
|||
retries: 3 |
|||
networks: |
|||
- seaweedfs-iam |
|||
start_period: 10s |
|||
|
|||
# SeaweedFS S3 Gateway with IAM |
|||
seaweedfs-s3: |
|||
build: |
|||
context: ../../.. |
|||
dockerfile: test/s3/iam/Dockerfile.s3 |
|||
container_name: seaweedfs-s3-iam |
|||
# SeaweedFS S3 API with IAM |
|||
weed-s3: |
|||
image: ${SEAWEEDFS_IMAGE:-chrislusf/seaweedfs:latest} |
|||
container_name: weed-s3 |
|||
hostname: weed-s3 |
|||
ports: |
|||
- "8333:8333" |
|||
environment: |
|||
- KEYCLOAK_URL=http://keycloak:8080 |
|||
command: s3 -port=8333 -filer=seaweedfs-filer:8888 -iam.config=/etc/seaweedfs/iam_config.json |
|||
WEED_FILER: "weed-filer:8888" |
|||
WEED_IAM_CONFIG: "/config/iam_config.json" |
|||
WEED_S3_CONFIG: "/config/test_config.json" |
|||
GLOG_v: "3" |
|||
command: > |
|||
sh -c " |
|||
echo 'Waiting for dependencies...' && |
|||
sleep 10 && |
|||
echo 'Starting S3 API with IAM...' && |
|||
weed -v=3 s3 -ip=weed-s3 -port=8333 |
|||
-filer=weed-filer:8888 |
|||
-config=/config/test_config.json |
|||
-iam.config=/config/iam_config.json |
|||
" |
|||
volumes: |
|||
- ./iam_config_docker.json:/etc/seaweedfs/iam_config.json:ro |
|||
- ./iam_config.json:/config/iam_config.json:ro |
|||
- ./test_config.json:/config/test_config.json:ro |
|||
networks: |
|||
- seaweedfs-iam |
|||
depends_on: |
|||
keycloak: |
|||
weed-filer: |
|||
condition: service_healthy |
|||
seaweedfs-filer: |
|||
keycloak: |
|||
condition: service_healthy |
|||
healthcheck: |
|||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8333/"] |
|||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8333"] |
|||
interval: 10s |
|||
timeout: 5s |
|||
retries: 3 |
|||
retries: 5 |
|||
start_period: 30s |
|||
|
|||
# Keycloak Setup Service |
|||
keycloak-setup: |
|||
image: alpine/curl:latest |
|||
container_name: keycloak-setup |
|||
volumes: |
|||
- ./setup_keycloak_docker.sh:/setup.sh:ro |
|||
- .:/workspace:rw |
|||
working_dir: /workspace |
|||
networks: |
|||
- seaweedfs-iam |
|||
depends_on: |
|||
keycloak: |
|||
condition: service_healthy |
|||
command: > |
|||
sh -c " |
|||
apk add --no-cache bash jq && |
|||
chmod +x /setup.sh && |
|||
/setup.sh |
|||
" |
|||
|
|||
volumes: |
|||
seaweedfs-master-data: |
|||
seaweedfs-volume-data: |
|||
master-data: |
|||
volume-data: |
|||
filer-data: |
|||
|
|||
networks: |
|||
seaweedfs-iam: |
|||
driver: bridge |
|||
driver: bridge |
@ -1,294 +0,0 @@ |
|||
{ |
|||
"sts": { |
|||
"tokenDuration": 3600000000000, |
|||
"maxSessionLength": 43200000000000, |
|||
"issuer": "seaweedfs-sts", |
|||
"signingKey": "dGVzdC1zaWduaW5nLWtleS0zMi1jaGFyYWN0ZXJzLWxvbmc=" |
|||
}, |
|||
"providers": [ |
|||
{ |
|||
"name": "test-oidc", |
|||
"type": "mock", |
|||
"config": { |
|||
"issuer": "test-oidc-issuer", |
|||
"clientId": "test-oidc-client" |
|||
} |
|||
}, |
|||
{ |
|||
"name": "keycloak", |
|||
"type": "oidc", |
|||
"enabled": true, |
|||
"config": { |
|||
"issuer": "http://localhost:8080/realms/seaweedfs-test", |
|||
"clientId": "seaweedfs-s3", |
|||
"clientSecret": "seaweedfs-s3-secret", |
|||
"jwksUri": "http://localhost:8080/realms/seaweedfs-test/protocol/openid-connect/certs", |
|||
"userInfoUri": "http://localhost:8080/realms/seaweedfs-test/protocol/openid-connect/userinfo", |
|||
"scopes": ["openid", "profile", "email"], |
|||
"claimsMapping": { |
|||
"username": "preferred_username", |
|||
"email": "email", |
|||
"name": "name" |
|||
}, |
|||
"roleMapping": { |
|||
"rules": [ |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-admin", |
|||
"role": "arn:seaweed:iam::role/KeycloakAdminRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-read-only", |
|||
"role": "arn:seaweed:iam::role/KeycloakReadOnlyRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-write-only", |
|||
"role": "arn:seaweed:iam::role/KeycloakWriteOnlyRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-read-write", |
|||
"role": "arn:seaweed:iam::role/KeycloakReadWriteRole" |
|||
} |
|||
], |
|||
"defaultRole": "arn:seaweed:iam::role/KeycloakReadOnlyRole" |
|||
} |
|||
} |
|||
} |
|||
], |
|||
"policy": { |
|||
"defaultEffect": "Deny", |
|||
"storeType": "memory" |
|||
}, |
|||
"roles": [ |
|||
{ |
|||
"roleName": "TestAdminRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestAdminRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3AdminPolicy"], |
|||
"description": "Admin role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "TestReadOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestReadOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadOnlyPolicy"], |
|||
"description": "Read-only role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "TestWriteOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestWriteOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3WriteOnlyPolicy"], |
|||
"description": "Write-only role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakAdminRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakAdminRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3AdminPolicy"], |
|||
"description": "Admin role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakReadOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakReadOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadOnlyPolicy"], |
|||
"description": "Read-only role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakWriteOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakWriteOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3WriteOnlyPolicy"], |
|||
"description": "Write-only role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakReadWriteRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakReadWriteRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadWritePolicy"], |
|||
"description": "Read-write role for Keycloak users" |
|||
} |
|||
], |
|||
"policies": [ |
|||
{ |
|||
"name": "S3AdminPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["s3:*"], |
|||
"Resource": ["*"] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3WriteOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:*" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Deny", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadWritePolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:*" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
@ -1,294 +0,0 @@ |
|||
{ |
|||
"sts": { |
|||
"tokenDuration": 3600000000000, |
|||
"maxSessionLength": 43200000000000, |
|||
"issuer": "seaweedfs-sts", |
|||
"signingKey": "dGVzdC1zaWduaW5nLWtleS0zMi1jaGFyYWN0ZXJzLWxvbmc=" |
|||
}, |
|||
"providers": [ |
|||
{ |
|||
"name": "test-oidc", |
|||
"type": "mock", |
|||
"config": { |
|||
"issuer": "test-oidc-issuer", |
|||
"clientId": "test-oidc-client" |
|||
} |
|||
}, |
|||
{ |
|||
"name": "keycloak", |
|||
"type": "oidc", |
|||
"enabled": true, |
|||
"config": { |
|||
"issuer": "http://localhost:8080/realms/seaweedfs-test", |
|||
"clientId": "seaweedfs-s3", |
|||
"clientSecret": "seaweedfs-s3-secret", |
|||
"jwksUri": "http://localhost:8080/realms/seaweedfs-test/protocol/openid-connect/certs", |
|||
"userInfoUri": "http://localhost:8080/realms/seaweedfs-test/protocol/openid-connect/userinfo", |
|||
"scopes": ["openid", "profile", "email"], |
|||
"claimsMapping": { |
|||
"username": "preferred_username", |
|||
"email": "email", |
|||
"name": "name" |
|||
}, |
|||
"roleMapping": { |
|||
"rules": [ |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-admin", |
|||
"role": "arn:seaweed:iam::role/KeycloakAdminRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-read-only", |
|||
"role": "arn:seaweed:iam::role/KeycloakReadOnlyRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-write-only", |
|||
"role": "arn:seaweed:iam::role/KeycloakWriteOnlyRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-read-write", |
|||
"role": "arn:seaweed:iam::role/KeycloakReadWriteRole" |
|||
} |
|||
], |
|||
"defaultRole": "arn:seaweed:iam::role/KeycloakReadOnlyRole" |
|||
} |
|||
} |
|||
} |
|||
], |
|||
"policy": { |
|||
"defaultEffect": "Deny", |
|||
"storeType": "memory" |
|||
}, |
|||
"roles": [ |
|||
{ |
|||
"roleName": "TestAdminRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestAdminRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3AdminPolicy"], |
|||
"description": "Admin role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "TestReadOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestReadOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadOnlyPolicy"], |
|||
"description": "Read-only role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "TestWriteOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestWriteOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3WriteOnlyPolicy"], |
|||
"description": "Write-only role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakAdminRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakAdminRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3AdminPolicy"], |
|||
"description": "Admin role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakReadOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakReadOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadOnlyPolicy"], |
|||
"description": "Read-only role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakWriteOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakWriteOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3WriteOnlyPolicy"], |
|||
"description": "Write-only role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakReadWriteRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakReadWriteRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadWritePolicy"], |
|||
"description": "Read-write role for Keycloak users" |
|||
} |
|||
], |
|||
"policies": [ |
|||
{ |
|||
"name": "S3AdminPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["s3:*"], |
|||
"Resource": ["*"] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3WriteOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:*" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Deny", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadWritePolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:*" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
@ -1,346 +0,0 @@ |
|||
{ |
|||
"sts": { |
|||
"tokenDuration": 3600000000000, |
|||
"maxSessionLength": 43200000000000, |
|||
"issuer": "seaweedfs-sts", |
|||
"signingKey": "dGVzdC1zaWduaW5nLWtleS0zMi1jaGFyYWN0ZXJzLWxvbmc=" |
|||
}, |
|||
"providers": [ |
|||
{ |
|||
"name": "test-oidc", |
|||
"type": "mock", |
|||
"config": { |
|||
"issuer": "test-oidc-issuer", |
|||
"clientId": "test-oidc-client" |
|||
} |
|||
}, |
|||
{ |
|||
"name": "keycloak", |
|||
"type": "oidc", |
|||
"enabled": true, |
|||
"config": { |
|||
"issuer": "http://localhost:8090/realms/seaweedfs-test", |
|||
"clientId": "seaweedfs-s3", |
|||
"clientSecret": "seaweedfs-s3-secret", |
|||
"jwksUri": "http://localhost:8090/realms/seaweedfs-test/protocol/openid-connect/certs", |
|||
"userInfoUri": "http://localhost:8090/realms/seaweedfs-test/protocol/openid-connect/userinfo", |
|||
"scopes": [ |
|||
"openid", |
|||
"profile", |
|||
"email" |
|||
], |
|||
"claimsMapping": { |
|||
"username": "preferred_username", |
|||
"email": "email", |
|||
"name": "name" |
|||
}, |
|||
"roleMapping": { |
|||
"rules": [ |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-admin", |
|||
"role": "arn:seaweed:iam::role/KeycloakAdminRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-read-only", |
|||
"role": "arn:seaweed:iam::role/KeycloakReadOnlyRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-write-only", |
|||
"role": "arn:seaweed:iam::role/KeycloakWriteOnlyRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-read-write", |
|||
"role": "arn:seaweed:iam::role/KeycloakReadWriteRole" |
|||
} |
|||
], |
|||
"defaultRole": "arn:seaweed:iam::role/KeycloakReadOnlyRole" |
|||
} |
|||
} |
|||
} |
|||
], |
|||
"policy": { |
|||
"defaultEffect": "Deny", |
|||
"storeType": "memory" |
|||
}, |
|||
"roles": [ |
|||
{ |
|||
"roleName": "TestAdminRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestAdminRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": [ |
|||
"sts:AssumeRoleWithWebIdentity" |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": [ |
|||
"S3AdminPolicy" |
|||
], |
|||
"description": "Admin role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "TestReadOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestReadOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": [ |
|||
"sts:AssumeRoleWithWebIdentity" |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": [ |
|||
"S3ReadOnlyPolicy" |
|||
], |
|||
"description": "Read-only role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "TestWriteOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestWriteOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": [ |
|||
"sts:AssumeRoleWithWebIdentity" |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": [ |
|||
"S3WriteOnlyPolicy" |
|||
], |
|||
"description": "Write-only role for testing" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakAdminRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakAdminRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": [ |
|||
"sts:AssumeRoleWithWebIdentity" |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": [ |
|||
"S3AdminPolicy" |
|||
], |
|||
"description": "Admin role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakReadOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakReadOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": [ |
|||
"sts:AssumeRoleWithWebIdentity" |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": [ |
|||
"S3ReadOnlyPolicy" |
|||
], |
|||
"description": "Read-only role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakWriteOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakWriteOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": [ |
|||
"sts:AssumeRoleWithWebIdentity" |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": [ |
|||
"S3WriteOnlyPolicy" |
|||
], |
|||
"description": "Write-only role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakReadWriteRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakReadWriteRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": [ |
|||
"sts:AssumeRoleWithWebIdentity" |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": [ |
|||
"S3ReadWritePolicy" |
|||
], |
|||
"description": "Read-write role for Keycloak users" |
|||
} |
|||
], |
|||
"policies": [ |
|||
{ |
|||
"name": "S3AdminPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:*" |
|||
], |
|||
"Resource": [ |
|||
"*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"sts:ValidateSession" |
|||
], |
|||
"Resource": [ |
|||
"*" |
|||
] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"sts:ValidateSession" |
|||
], |
|||
"Resource": [ |
|||
"*" |
|||
] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3WriteOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:*" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Deny", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"sts:ValidateSession" |
|||
], |
|||
"Resource": [ |
|||
"*" |
|||
] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadWritePolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:*" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"sts:ValidateSession" |
|||
], |
|||
"Resource": [ |
|||
"*" |
|||
] |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
@ -1,138 +0,0 @@ |
|||
{ |
|||
"realm": "seaweedfs-test", |
|||
"enabled": true, |
|||
"displayName": "SeaweedFS Test Realm", |
|||
"accessTokenLifespan": 3600, |
|||
"accessTokenLifespanForImplicitFlow": 3600, |
|||
"ssoSessionIdleTimeout": 3600, |
|||
"ssoSessionMaxLifespan": 36000, |
|||
"clients": [ |
|||
{ |
|||
"clientId": "seaweedfs-s3", |
|||
"enabled": true, |
|||
"protocol": "openid-connect", |
|||
"publicClient": false, |
|||
"secret": "seaweedfs-s3-secret", |
|||
"directAccessGrantsEnabled": true, |
|||
"serviceAccountsEnabled": true, |
|||
"standardFlowEnabled": true, |
|||
"implicitFlowEnabled": false, |
|||
"redirectUris": ["*"], |
|||
"webOrigins": ["*"], |
|||
"protocolMappers": [ |
|||
{ |
|||
"name": "role-mapper", |
|||
"protocol": "openid-connect", |
|||
"protocolMapper": "oidc-usermodel-realm-role-mapper", |
|||
"config": { |
|||
"claim.name": "roles", |
|||
"jsonType.label": "String", |
|||
"multivalued": "true", |
|||
"userinfo.token.claim": "true", |
|||
"id.token.claim": "true", |
|||
"access.token.claim": "true" |
|||
} |
|||
}, |
|||
{ |
|||
"name": "username-mapper", |
|||
"protocol": "openid-connect", |
|||
"protocolMapper": "oidc-usermodel-property-mapper", |
|||
"config": { |
|||
"claim.name": "preferred_username", |
|||
"user.attribute": "username", |
|||
"jsonType.label": "String", |
|||
"userinfo.token.claim": "true", |
|||
"id.token.claim": "true", |
|||
"access.token.claim": "true" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
], |
|||
"roles": { |
|||
"realm": [ |
|||
{ |
|||
"name": "s3-admin", |
|||
"description": "S3 Administrator role with full access" |
|||
}, |
|||
{ |
|||
"name": "s3-read-only", |
|||
"description": "S3 Read-only role" |
|||
}, |
|||
{ |
|||
"name": "s3-read-write", |
|||
"description": "S3 Read-write role" |
|||
} |
|||
] |
|||
}, |
|||
"users": [ |
|||
{ |
|||
"username": "admin-user", |
|||
"enabled": true, |
|||
"firstName": "Admin", |
|||
"lastName": "User", |
|||
"email": "admin@seaweedfs.test", |
|||
"emailVerified": true, |
|||
"credentials": [ |
|||
{ |
|||
"type": "password", |
|||
"value": "admin123", |
|||
"temporary": false |
|||
} |
|||
], |
|||
"realmRoles": ["s3-admin"], |
|||
"attributes": { |
|||
"department": ["engineering"], |
|||
"location": ["datacenter-1"] |
|||
} |
|||
}, |
|||
{ |
|||
"username": "read-user", |
|||
"enabled": true, |
|||
"firstName": "Read", |
|||
"lastName": "User", |
|||
"email": "read@seaweedfs.test", |
|||
"emailVerified": true, |
|||
"credentials": [ |
|||
{ |
|||
"type": "password", |
|||
"value": "read123", |
|||
"temporary": false |
|||
} |
|||
], |
|||
"realmRoles": ["s3-read-only"], |
|||
"attributes": { |
|||
"department": ["analytics"], |
|||
"location": ["datacenter-2"] |
|||
} |
|||
}, |
|||
{ |
|||
"username": "write-user", |
|||
"enabled": true, |
|||
"firstName": "Write", |
|||
"lastName": "User", |
|||
"email": "write@seaweedfs.test", |
|||
"emailVerified": true, |
|||
"credentials": [ |
|||
{ |
|||
"type": "password", |
|||
"value": "write123", |
|||
"temporary": false |
|||
} |
|||
], |
|||
"realmRoles": ["s3-read-write"], |
|||
"attributes": { |
|||
"department": ["operations"], |
|||
"location": ["datacenter-1"] |
|||
} |
|||
} |
|||
], |
|||
"identityProviders": [], |
|||
"identityProviderMappers": [], |
|||
"requiredActions": [], |
|||
"browserFlow": "browser", |
|||
"registrationFlow": "registration", |
|||
"directGrantFlow": "direct grant", |
|||
"resetCredentialsFlow": "reset credentials", |
|||
"clientAuthenticationFlow": "clients" |
|||
} |
@ -1,621 +0,0 @@ |
|||
#!/bin/bash |
|||
|
|||
# SeaweedFS S3 IAM Complete Test Setup Script |
|||
# This script enables all previously skipped tests by setting up the required environment |
|||
|
|||
set -e |
|||
|
|||
# Colors for output |
|||
RED='\033[0;31m' |
|||
GREEN='\033[0;32m' |
|||
YELLOW='\033[1;33m' |
|||
BLUE='\033[0;34m' |
|||
NC='\033[0m' # No Color |
|||
|
|||
# Configuration |
|||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|||
SEAWEEDFS_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" |
|||
TEST_DIR="$SCRIPT_DIR" |
|||
|
|||
# Service ports |
|||
KEYCLOAK_PORT=8080 |
|||
S3_PORT=8333 |
|||
FILER_PORT=8888 |
|||
MASTER_PORT=9333 |
|||
VOLUME_PORT=8080 |
|||
|
|||
# Test configuration |
|||
export KEYCLOAK_URL="http://localhost:$KEYCLOAK_PORT" |
|||
export S3_ENDPOINT="http://localhost:$S3_PORT" |
|||
export TEST_TIMEOUT="60m" |
|||
export CGO_ENABLED=0 |
|||
|
|||
echo -e "${BLUE}🚀 SeaweedFS S3 IAM Complete Test Setup${NC}" |
|||
echo -e "${BLUE}======================================${NC}" |
|||
|
|||
# Function to check if a service is running |
|||
check_service() { |
|||
local service_name="$1" |
|||
local url="$2" |
|||
local max_attempts=30 |
|||
local attempt=1 |
|||
|
|||
echo -e "${YELLOW}⏳ Waiting for $service_name to be ready...${NC}" |
|||
|
|||
while [ $attempt -le $max_attempts ]; do |
|||
if curl -s "$url" > /dev/null 2>&1; then |
|||
echo -e "${GREEN}✅ $service_name is ready${NC}" |
|||
return 0 |
|||
fi |
|||
echo -n "." |
|||
sleep 2 |
|||
((attempt++)) |
|||
done |
|||
|
|||
echo -e "${RED}❌ $service_name failed to start after $((max_attempts * 2)) seconds${NC}" |
|||
return 1 |
|||
} |
|||
|
|||
# Function to setup Keycloak |
|||
setup_keycloak() { |
|||
echo -e "${BLUE}🔐 Setting up Keycloak for IAM integration tests...${NC}" |
|||
|
|||
# Check if Keycloak is already running |
|||
if curl -s "http://localhost:$KEYCLOAK_PORT/health/ready" > /dev/null 2>&1; then |
|||
echo -e "${GREEN}✅ Keycloak is already running${NC}" |
|||
return 0 |
|||
fi |
|||
|
|||
# Check if Docker is available |
|||
if ! command -v docker &> /dev/null; then |
|||
echo -e "${RED}❌ Docker is required for Keycloak setup${NC}" |
|||
echo -e "${YELLOW}💡 Install Docker or run Keycloak manually${NC}" |
|||
return 1 |
|||
fi |
|||
|
|||
# Start Keycloak with Docker |
|||
echo -e "${YELLOW}🐳 Starting Keycloak container...${NC}" |
|||
|
|||
# Stop any existing Keycloak container |
|||
docker stop keycloak-iam-test 2>/dev/null || true |
|||
docker rm keycloak-iam-test 2>/dev/null || true |
|||
|
|||
# Start new Keycloak container with correct environment variables for 26.0 |
|||
docker run -d \ |
|||
--name keycloak-iam-test \ |
|||
-p $KEYCLOAK_PORT:8080 \ |
|||
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \ |
|||
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \ |
|||
-e KC_HTTP_ENABLED=true \ |
|||
-e KC_HOSTNAME_STRICT=false \ |
|||
-e KC_HOSTNAME_STRICT_HTTPS=false \ |
|||
-e KC_HEALTH_ENABLED=true \ |
|||
quay.io/keycloak/keycloak:26.0 start-dev |
|||
|
|||
# Wait for Keycloak to be ready |
|||
if check_service "Keycloak" "http://localhost:$KEYCLOAK_PORT/health/ready"; then |
|||
echo -e "${GREEN}✅ Keycloak setup complete${NC}" |
|||
return 0 |
|||
else |
|||
echo -e "${RED}❌ Keycloak setup failed${NC}" |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
# Function to setup distributed environment |
|||
setup_distributed_environment() { |
|||
echo -e "${BLUE}🌐 Setting up distributed test environment...${NC}" |
|||
|
|||
# Create distributed configuration |
|||
if [ ! -f "$TEST_DIR/iam_config_distributed.json" ]; then |
|||
echo -e "${YELLOW}📝 Creating distributed IAM configuration...${NC}" |
|||
cat > "$TEST_DIR/iam_config_distributed.json" << 'EOF' |
|||
{ |
|||
"sts": { |
|||
"tokenDuration": 3600000000000, |
|||
"maxSessionLength": 43200000000000, |
|||
"issuer": "seaweedfs-sts", |
|||
"signingKey": "dGVzdC1zaWduaW5nLWtleS0zMi1jaGFyYWN0ZXJzLWxvbmc=", |
|||
"sessionStoreType": "filer", |
|||
"sessionStoreConfig": { |
|||
"filerAddress": "localhost:8888", |
|||
"basePath": "/seaweedfs/iam/sessions" |
|||
} |
|||
}, |
|||
"identityProviders": [ |
|||
{ |
|||
"name": "test-oidc", |
|||
"type": "mock", |
|||
"config": { |
|||
"issuer": "test-oidc-issuer" |
|||
} |
|||
} |
|||
], |
|||
"policy": { |
|||
"defaultEffect": "Deny", |
|||
"storeType": "filer", |
|||
"storeConfig": { |
|||
"filerAddress": "localhost:8888", |
|||
"basePath": "/seaweedfs/iam/policies" |
|||
} |
|||
}, |
|||
"roleStore": { |
|||
"storeType": "filer", |
|||
"storeConfig": { |
|||
"filerAddress": "localhost:8888", |
|||
"basePath": "/seaweedfs/iam/roles" |
|||
} |
|||
}, |
|||
"roles": [ |
|||
{ |
|||
"roleName": "TestAdminRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestAdminRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3AdminPolicy"], |
|||
"description": "Admin role for distributed testing" |
|||
}, |
|||
{ |
|||
"roleName": "TestReadOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/TestReadOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "test-oidc" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadOnlyPolicy"], |
|||
"description": "Read-only role for distributed testing" |
|||
} |
|||
], |
|||
"policies": [ |
|||
{ |
|||
"name": "S3AdminPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["s3:*"], |
|||
"Resource": ["*"] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
|||
EOF |
|||
fi |
|||
|
|||
echo -e "${GREEN}✅ Distributed environment configuration ready${NC}" |
|||
} |
|||
|
|||
# Function to create performance test runner |
|||
create_performance_test_runner() { |
|||
echo -e "${BLUE}🏁 Creating performance test runner...${NC}" |
|||
|
|||
cat > "$TEST_DIR/run_performance_tests.sh" << 'EOF' |
|||
#!/bin/bash |
|||
|
|||
# Performance Test Runner for SeaweedFS S3 IAM |
|||
|
|||
set -e |
|||
|
|||
# Colors |
|||
GREEN='\033[0;32m' |
|||
YELLOW='\033[1;33m' |
|||
NC='\033[0m' |
|||
|
|||
echo -e "${YELLOW}🏁 Running S3 IAM Performance Tests${NC}" |
|||
|
|||
# Enable performance tests |
|||
export ENABLE_PERFORMANCE_TESTS=true |
|||
export TEST_TIMEOUT=60m |
|||
|
|||
# Run benchmarks |
|||
echo -e "${YELLOW}📊 Running benchmarks...${NC}" |
|||
go test -bench=. -benchmem -timeout=$TEST_TIMEOUT ./... |
|||
|
|||
# Run performance tests |
|||
echo -e "${YELLOW}🧪 Running performance test suite...${NC}" |
|||
go test -v -timeout=$TEST_TIMEOUT -run "TestS3IAMPerformanceTests" ./... |
|||
|
|||
echo -e "${GREEN}✅ Performance tests completed${NC}" |
|||
EOF |
|||
|
|||
chmod +x "$TEST_DIR/run_performance_tests.sh" |
|||
echo -e "${GREEN}✅ Performance test runner created${NC}" |
|||
} |
|||
|
|||
# Function to create stress test runner |
|||
create_stress_test_runner() { |
|||
echo -e "${BLUE}💪 Creating stress test runner...${NC}" |
|||
|
|||
cat > "$TEST_DIR/run_stress_tests.sh" << 'EOF' |
|||
#!/bin/bash |
|||
|
|||
# Stress Test Runner for SeaweedFS S3 IAM |
|||
|
|||
set -e |
|||
|
|||
# Colors |
|||
GREEN='\033[0;32m' |
|||
YELLOW='\033[1;33m' |
|||
RED='\033[0;31m' |
|||
NC='\033[0m' |
|||
|
|||
echo -e "${YELLOW}💪 Running S3 IAM Stress Tests${NC}" |
|||
|
|||
# Enable stress tests |
|||
export ENABLE_STRESS_TESTS=true |
|||
export TEST_TIMEOUT=60m |
|||
|
|||
# Run stress tests multiple times |
|||
STRESS_ITERATIONS=5 |
|||
|
|||
echo -e "${YELLOW}🔄 Running stress tests with $STRESS_ITERATIONS iterations...${NC}" |
|||
|
|||
for i in $(seq 1 $STRESS_ITERATIONS); do |
|||
echo -e "${YELLOW}📊 Iteration $i/$STRESS_ITERATIONS${NC}" |
|||
|
|||
if ! go test -v -timeout=$TEST_TIMEOUT -run "TestS3IAMDistributedTests.*concurrent" ./... -count=1; then |
|||
echo -e "${RED}❌ Stress test failed on iteration $i${NC}" |
|||
exit 1 |
|||
fi |
|||
|
|||
# Brief pause between iterations |
|||
sleep 2 |
|||
done |
|||
|
|||
echo -e "${GREEN}✅ All stress test iterations completed successfully${NC}" |
|||
EOF |
|||
|
|||
chmod +x "$TEST_DIR/run_stress_tests.sh" |
|||
echo -e "${GREEN}✅ Stress test runner created${NC}" |
|||
} |
|||
|
|||
# Function to create versioning stress test setup |
|||
setup_versioning_stress_tests() { |
|||
echo -e "${BLUE}📚 Setting up S3 versioning stress tests...${NC}" |
|||
|
|||
# Navigate to versioning test directory |
|||
VERSIONING_DIR="$SEAWEEDFS_ROOT/test/s3/versioning" |
|||
|
|||
if [ ! -d "$VERSIONING_DIR" ]; then |
|||
echo -e "${RED}❌ Versioning test directory not found: $VERSIONING_DIR${NC}" |
|||
return 1 |
|||
fi |
|||
|
|||
# Create versioning stress test enabler |
|||
cat > "$VERSIONING_DIR/enable_stress_tests.sh" << 'EOF' |
|||
#!/bin/bash |
|||
|
|||
# Enable S3 Versioning Stress Tests |
|||
|
|||
set -e |
|||
|
|||
# Colors |
|||
GREEN='\033[0;32m' |
|||
YELLOW='\033[1;33m' |
|||
NC='\033[0m' |
|||
|
|||
echo -e "${YELLOW}📚 Enabling S3 Versioning Stress Tests${NC}" |
|||
|
|||
# Disable short mode to enable stress tests |
|||
export ENABLE_STRESS_TESTS=true |
|||
|
|||
# Run versioning stress tests |
|||
echo -e "${YELLOW}🧪 Running versioning stress tests...${NC}" |
|||
make test-versioning-stress |
|||
|
|||
echo -e "${GREEN}✅ Versioning stress tests completed${NC}" |
|||
EOF |
|||
|
|||
chmod +x "$VERSIONING_DIR/enable_stress_tests.sh" |
|||
echo -e "${GREEN}✅ Versioning stress test setup complete${NC}" |
|||
} |
|||
|
|||
# Function to create master test runner |
|||
create_master_test_runner() { |
|||
echo -e "${BLUE}🎯 Creating master test runner...${NC}" |
|||
|
|||
cat > "$TEST_DIR/run_all_tests.sh" << 'EOF' |
|||
#!/bin/bash |
|||
|
|||
# Master Test Runner - Enables and runs all previously skipped tests |
|||
|
|||
set -e |
|||
|
|||
# Colors |
|||
RED='\033[0;31m' |
|||
GREEN='\033[0;32m' |
|||
YELLOW='\033[1;33m' |
|||
BLUE='\033[0;34m' |
|||
NC='\033[0m' |
|||
|
|||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|||
|
|||
echo -e "${BLUE}🎯 SeaweedFS S3 IAM Complete Test Suite${NC}" |
|||
echo -e "${BLUE}=====================================${NC}" |
|||
|
|||
# Set environment variables to enable all tests |
|||
export ENABLE_DISTRIBUTED_TESTS=true |
|||
export ENABLE_PERFORMANCE_TESTS=true |
|||
export ENABLE_STRESS_TESTS=true |
|||
export KEYCLOAK_URL="http://localhost:8080" |
|||
export S3_ENDPOINT="http://localhost:8333" |
|||
export TEST_TIMEOUT=60m |
|||
export CGO_ENABLED=0 |
|||
|
|||
# Function to run test category |
|||
run_test_category() { |
|||
local category="$1" |
|||
local test_pattern="$2" |
|||
local description="$3" |
|||
|
|||
echo -e "${YELLOW}🧪 Running $description...${NC}" |
|||
|
|||
if go test -v -timeout=$TEST_TIMEOUT -run "$test_pattern" ./...; then |
|||
echo -e "${GREEN}✅ $description completed successfully${NC}" |
|||
return 0 |
|||
else |
|||
echo -e "${RED}❌ $description failed${NC}" |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
# Track results |
|||
TOTAL_CATEGORIES=0 |
|||
PASSED_CATEGORIES=0 |
|||
|
|||
# 1. Standard IAM Integration Tests |
|||
echo -e "\n${BLUE}1. Standard IAM Integration Tests${NC}" |
|||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1)) |
|||
if run_test_category "standard" "TestS3IAM(?!.*Distributed|.*Performance)" "Standard IAM Integration Tests"; then |
|||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1)) |
|||
fi |
|||
|
|||
# 2. Keycloak Integration Tests (if Keycloak is available) |
|||
echo -e "\n${BLUE}2. Keycloak Integration Tests${NC}" |
|||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1)) |
|||
if curl -s "http://localhost:8080/health/ready" > /dev/null 2>&1; then |
|||
if run_test_category "keycloak" "TestKeycloak" "Keycloak Integration Tests"; then |
|||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1)) |
|||
fi |
|||
else |
|||
echo -e "${YELLOW}⚠️ Keycloak not available, skipping Keycloak tests${NC}" |
|||
echo -e "${YELLOW}💡 Run './setup_all_tests.sh' to start Keycloak${NC}" |
|||
fi |
|||
|
|||
# 3. Distributed Tests |
|||
echo -e "\n${BLUE}3. Distributed IAM Tests${NC}" |
|||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1)) |
|||
if run_test_category "distributed" "TestS3IAMDistributedTests" "Distributed IAM Tests"; then |
|||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1)) |
|||
fi |
|||
|
|||
# 4. Performance Tests |
|||
echo -e "\n${BLUE}4. Performance Tests${NC}" |
|||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1)) |
|||
if run_test_category "performance" "TestS3IAMPerformanceTests" "Performance Tests"; then |
|||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1)) |
|||
fi |
|||
|
|||
# 5. Benchmarks |
|||
echo -e "\n${BLUE}5. Benchmark Tests${NC}" |
|||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1)) |
|||
if go test -bench=. -benchmem -timeout=$TEST_TIMEOUT ./...; then |
|||
echo -e "${GREEN}✅ Benchmark tests completed successfully${NC}" |
|||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1)) |
|||
else |
|||
echo -e "${RED}❌ Benchmark tests failed${NC}" |
|||
fi |
|||
|
|||
# 6. Versioning Stress Tests |
|||
echo -e "\n${BLUE}6. S3 Versioning Stress Tests${NC}" |
|||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1)) |
|||
if [ -f "../versioning/enable_stress_tests.sh" ]; then |
|||
if (cd ../versioning && ./enable_stress_tests.sh); then |
|||
echo -e "${GREEN}✅ Versioning stress tests completed successfully${NC}" |
|||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1)) |
|||
else |
|||
echo -e "${RED}❌ Versioning stress tests failed${NC}" |
|||
fi |
|||
else |
|||
echo -e "${YELLOW}⚠️ Versioning stress tests not available${NC}" |
|||
fi |
|||
|
|||
# Summary |
|||
echo -e "\n${BLUE}📊 Test Summary${NC}" |
|||
echo -e "${BLUE}===============${NC}" |
|||
echo -e "Total test categories: $TOTAL_CATEGORIES" |
|||
echo -e "Passed: ${GREEN}$PASSED_CATEGORIES${NC}" |
|||
echo -e "Failed: ${RED}$((TOTAL_CATEGORIES - PASSED_CATEGORIES))${NC}" |
|||
|
|||
if [ $PASSED_CATEGORIES -eq $TOTAL_CATEGORIES ]; then |
|||
echo -e "\n${GREEN}🎉 All test categories passed!${NC}" |
|||
exit 0 |
|||
else |
|||
echo -e "\n${RED}❌ Some test categories failed${NC}" |
|||
exit 1 |
|||
fi |
|||
EOF |
|||
|
|||
chmod +x "$TEST_DIR/run_all_tests.sh" |
|||
echo -e "${GREEN}✅ Master test runner created${NC}" |
|||
} |
|||
|
|||
# Function to update Makefile with new targets |
|||
update_makefile() { |
|||
echo -e "${BLUE}📝 Updating Makefile with new test targets...${NC}" |
|||
|
|||
# Add new targets to Makefile |
|||
cat >> "$TEST_DIR/Makefile" << 'EOF' |
|||
|
|||
# New test targets for previously skipped tests |
|||
|
|||
test-distributed: ## Run distributed IAM tests |
|||
@echo "🌐 Running distributed IAM tests..." |
|||
@export ENABLE_DISTRIBUTED_TESTS=true && go test -v -timeout $(TEST_TIMEOUT) -run "TestS3IAMDistributedTests" ./... |
|||
|
|||
test-performance: ## Run performance tests |
|||
@echo "🏁 Running performance tests..." |
|||
@export ENABLE_PERFORMANCE_TESTS=true && go test -v -timeout $(TEST_TIMEOUT) -run "TestS3IAMPerformanceTests" ./... |
|||
|
|||
test-stress: ## Run stress tests |
|||
@echo "💪 Running stress tests..." |
|||
@export ENABLE_STRESS_TESTS=true && ./run_stress_tests.sh |
|||
|
|||
test-versioning-stress: ## Run S3 versioning stress tests |
|||
@echo "📚 Running versioning stress tests..." |
|||
@cd ../versioning && ./enable_stress_tests.sh |
|||
|
|||
test-keycloak-full: docker-up ## Run complete Keycloak integration tests |
|||
@echo "🔐 Running complete Keycloak integration tests..." |
|||
@export KEYCLOAK_URL="http://localhost:8080" && \ |
|||
export S3_ENDPOINT="http://localhost:8333" && \ |
|||
sleep 15 && \ |
|||
go test -v -timeout $(TEST_TIMEOUT) -run "TestKeycloak" ./... |
|||
@make docker-down |
|||
|
|||
test-all-previously-skipped: ## Run all previously skipped tests |
|||
@echo "🎯 Running all previously skipped tests..." |
|||
@./run_all_tests.sh |
|||
|
|||
setup-all-tests: ## Setup environment for all tests (including Keycloak) |
|||
@echo "🚀 Setting up complete test environment..." |
|||
@./setup_all_tests.sh |
|||
|
|||
# Update help target |
|||
help: ## Show this help message |
|||
@echo "SeaweedFS S3 IAM Integration Tests" |
|||
@echo "" |
|||
@echo "Usage:" |
|||
@echo " make [target]" |
|||
@echo "" |
|||
@echo "Standard Targets:" |
|||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-25s %s\n", $$1, $$2}' $(MAKEFILE_LIST) | head -20 |
|||
@echo "" |
|||
@echo "New Test Targets (Previously Skipped):" |
|||
@echo " test-distributed Run distributed IAM tests" |
|||
@echo " test-performance Run performance tests" |
|||
@echo " test-stress Run stress tests" |
|||
@echo " test-versioning-stress Run S3 versioning stress tests" |
|||
@echo " test-keycloak-full Run complete Keycloak integration tests" |
|||
@echo " test-all-previously-skipped Run all previously skipped tests" |
|||
@echo " setup-all-tests Setup environment for all tests" |
|||
@echo "" |
|||
@echo "Docker Compose Targets:" |
|||
@echo " docker-test Run tests with Docker Compose including Keycloak" |
|||
@echo " docker-up Start all services with Docker Compose" |
|||
@echo " docker-down Stop all Docker Compose services" |
|||
@echo " docker-logs Show logs from all services" |
|||
|
|||
EOF |
|||
|
|||
echo -e "${GREEN}✅ Makefile updated with new targets${NC}" |
|||
} |
|||
|
|||
# Main execution |
|||
main() { |
|||
echo -e "${BLUE}🔧 Starting complete test setup...${NC}" |
|||
|
|||
# Setup distributed environment |
|||
setup_distributed_environment |
|||
|
|||
# Create test functions |
|||
|
|||
# Create test runners |
|||
create_performance_test_runner |
|||
create_stress_test_runner |
|||
create_master_test_runner |
|||
|
|||
# Setup versioning stress tests |
|||
setup_versioning_stress_tests |
|||
|
|||
# Update Makefile |
|||
update_makefile |
|||
|
|||
# Setup Keycloak (optional) |
|||
echo -e "\n${YELLOW}🔐 Setting up Keycloak...${NC}" |
|||
if setup_keycloak; then |
|||
echo -e "${GREEN}✅ Keycloak container running${NC}" |
|||
# Configure realm, client, roles, and users idempotently |
|||
if [ -f "$TEST_DIR/setup_keycloak.sh" ]; then |
|||
echo -e "${YELLOW}🧩 Configuring Keycloak realm and test users...${NC}" |
|||
bash "$TEST_DIR/setup_keycloak.sh" |
|||
fi |
|||
echo -e "${GREEN}✅ Keycloak setup successful${NC}" |
|||
else |
|||
echo -e "${YELLOW}⚠️ Keycloak setup failed or skipped${NC}" |
|||
echo -e "${YELLOW}💡 You can run Keycloak manually or skip Keycloak tests${NC}" |
|||
fi |
|||
|
|||
echo -e "\n${GREEN}🎉 Complete test setup finished!${NC}" |
|||
echo -e "\n${BLUE}📋 Available commands:${NC}" |
|||
echo -e " ${YELLOW}./run_all_tests.sh${NC} - Run all previously skipped tests" |
|||
echo -e " ${YELLOW}make test-distributed${NC} - Run distributed tests only" |
|||
echo -e " ${YELLOW}make test-performance${NC} - Run performance tests only" |
|||
echo -e " ${YELLOW}make test-stress${NC} - Run stress tests only" |
|||
echo -e " ${YELLOW}make test-keycloak-full${NC} - Run complete Keycloak tests" |
|||
echo -e " ${YELLOW}make test-versioning-stress${NC} - Run versioning stress tests" |
|||
echo -e " ${YELLOW}make help${NC} - Show all available targets" |
|||
|
|||
echo -e "\n${BLUE}🔍 Environment Status:${NC}" |
|||
echo -e " Keycloak: $(curl -s http://localhost:8080/health/ready > /dev/null 2>&1 && echo -e "${GREEN}✅ Running${NC}" || echo -e "${RED}❌ Not running${NC}")" |
|||
# For IAM-enabled S3 API, any response (including 403) indicates the service is running |
|||
echo -e " S3 API: $(curl -s http://localhost:8333 > /dev/null 2>&1 && echo -e "${GREEN}✅ Running (IAM-protected)${NC}" || echo -e "${RED}❌ Not running${NC}")" |
|||
|
|||
if ! curl -s http://localhost:8333 > /dev/null 2>&1; then |
|||
echo -e "\n${YELLOW}💡 To start SeaweedFS services:${NC}" |
|||
echo -e " ${YELLOW}make start-services wait-for-services${NC}" |
|||
fi |
|||
} |
|||
|
|||
# Run main function |
|||
main "$@" |
@ -1,377 +0,0 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
set -euo pipefail |
|||
|
|||
# Colors |
|||
RED='\033[0;31m' |
|||
GREEN='\033[0;32m' |
|||
YELLOW='\033[1;33m' |
|||
BLUE='\033[0;34m' |
|||
NC='\033[0m' |
|||
|
|||
KEYCLOAK_IMAGE="quay.io/keycloak/keycloak:26.0.7" |
|||
CONTAINER_NAME="keycloak-iam-test" |
|||
KEYCLOAK_PORT="8080" # Default external port |
|||
KEYCLOAK_INTERNAL_PORT="8080" # Internal container port (always 8080) |
|||
KEYCLOAK_URL="http://localhost:${KEYCLOAK_PORT}" |
|||
|
|||
# Realm and test fixtures expected by tests |
|||
REALM_NAME="seaweedfs-test" |
|||
CLIENT_ID="seaweedfs-s3" |
|||
CLIENT_SECRET="seaweedfs-s3-secret" |
|||
ROLE_ADMIN="s3-admin" |
|||
ROLE_READONLY="s3-read-only" |
|||
ROLE_WRITEONLY="s3-write-only" |
|||
ROLE_READWRITE="s3-read-write" |
|||
|
|||
# User credentials (compatible with older bash versions) |
|||
get_user_password() { |
|||
case "$1" in |
|||
"admin-user") echo "admin123" ;; |
|||
"read-user") echo "read123" ;; |
|||
"write-user") echo "readwrite123" ;; |
|||
"write-only-user") echo "writeonly123" ;; |
|||
*) echo "" ;; |
|||
esac |
|||
} |
|||
|
|||
# List of users to create |
|||
USERS="admin-user read-user write-user write-only-user" |
|||
|
|||
echo -e "${BLUE}🔧 Setting up Keycloak realm and users for SeaweedFS S3 IAM testing...${NC}" |
|||
|
|||
ensure_container() { |
|||
# Check for any existing Keycloak container and detect its port |
|||
local keycloak_containers=$(docker ps --format '{{.Names}}\t{{.Ports}}' | grep -E "(keycloak|quay.io/keycloak)") |
|||
|
|||
if [[ -n "$keycloak_containers" ]]; then |
|||
# Parse the first available Keycloak container |
|||
CONTAINER_NAME=$(echo "$keycloak_containers" | head -1 | awk '{print $1}') |
|||
|
|||
# Extract the external port from the port mapping using sed (compatible with older bash) |
|||
local port_mapping=$(echo "$keycloak_containers" | head -1 | awk '{print $2}') |
|||
local extracted_port=$(echo "$port_mapping" | sed -n 's/.*:\([0-9]*\)->8080.*/\1/p') |
|||
if [[ -n "$extracted_port" ]]; then |
|||
KEYCLOAK_PORT="$extracted_port" |
|||
KEYCLOAK_URL="http://localhost:${KEYCLOAK_PORT}" |
|||
echo -e "${GREEN}✅ Using existing container '${CONTAINER_NAME}' on port ${KEYCLOAK_PORT}${NC}" |
|||
return 0 |
|||
fi |
|||
fi |
|||
|
|||
# Fallback: check for specific container names |
|||
if docker ps --format '{{.Names}}' | grep -q '^keycloak$'; then |
|||
CONTAINER_NAME="keycloak" |
|||
# Try to detect port for 'keycloak' container using docker port command |
|||
local ports=$(docker port keycloak 8080 2>/dev/null | head -1) |
|||
if [[ -n "$ports" ]]; then |
|||
local extracted_port=$(echo "$ports" | sed -n 's/.*:\([0-9]*\)$/\1/p') |
|||
if [[ -n "$extracted_port" ]]; then |
|||
KEYCLOAK_PORT="$extracted_port" |
|||
KEYCLOAK_URL="http://localhost:${KEYCLOAK_PORT}" |
|||
fi |
|||
fi |
|||
echo -e "${GREEN}✅ Using existing container '${CONTAINER_NAME}' on port ${KEYCLOAK_PORT}${NC}" |
|||
return 0 |
|||
fi |
|||
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then |
|||
echo -e "${GREEN}✅ Using existing container '${CONTAINER_NAME}'${NC}" |
|||
return 0 |
|||
fi |
|||
echo -e "${YELLOW}🐳 Starting Keycloak container (${KEYCLOAK_IMAGE})...${NC}" |
|||
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true |
|||
docker run -d --name "${CONTAINER_NAME}" -p "${KEYCLOAK_PORT}:8080" \ |
|||
-e KEYCLOAK_ADMIN=admin \ |
|||
-e KEYCLOAK_ADMIN_PASSWORD=admin \ |
|||
-e KC_HTTP_ENABLED=true \ |
|||
-e KC_HOSTNAME_STRICT=false \ |
|||
-e KC_HOSTNAME_STRICT_HTTPS=false \ |
|||
-e KC_HEALTH_ENABLED=true \ |
|||
"${KEYCLOAK_IMAGE}" start-dev >/dev/null |
|||
} |
|||
|
|||
wait_ready() { |
|||
echo -e "${YELLOW}⏳ Waiting for Keycloak to be ready...${NC}" |
|||
for i in $(seq 1 120); do |
|||
if curl -sf "${KEYCLOAK_URL}/health/ready" >/dev/null; then |
|||
echo -e "${GREEN}✅ Keycloak health check passed${NC}" |
|||
return 0 |
|||
fi |
|||
if curl -sf "${KEYCLOAK_URL}/realms/master" >/dev/null; then |
|||
echo -e "${GREEN}✅ Keycloak master realm accessible${NC}" |
|||
return 0 |
|||
fi |
|||
sleep 2 |
|||
done |
|||
echo -e "${RED}❌ Keycloak did not become ready in time${NC}" |
|||
exit 1 |
|||
} |
|||
|
|||
kcadm() { |
|||
# Always authenticate before each command to ensure context |
|||
# Try different admin passwords that might be used in different environments |
|||
# GitHub Actions uses "admin", local testing might use "admin123" |
|||
local admin_passwords=("admin" "admin123" "password") |
|||
local auth_success=false |
|||
|
|||
for pwd in "${admin_passwords[@]}"; do |
|||
if docker exec -i "${CONTAINER_NAME}" /opt/keycloak/bin/kcadm.sh config credentials --server "http://localhost:${KEYCLOAK_INTERNAL_PORT}" --realm master --user admin --password "$pwd" >/dev/null 2>&1; then |
|||
auth_success=true |
|||
break |
|||
fi |
|||
done |
|||
|
|||
if [[ "$auth_success" == false ]]; then |
|||
echo -e "${RED}❌ Failed to authenticate with any known admin password${NC}" |
|||
return 1 |
|||
fi |
|||
|
|||
docker exec -i "${CONTAINER_NAME}" /opt/keycloak/bin/kcadm.sh "$@" |
|||
} |
|||
|
|||
admin_login() { |
|||
# This is now handled by each kcadm() call |
|||
echo "Logging into http://localhost:${KEYCLOAK_INTERNAL_PORT} as user admin of realm master" |
|||
} |
|||
|
|||
ensure_realm() { |
|||
if kcadm get realms | grep -q "${REALM_NAME}"; then |
|||
echo -e "${GREEN}✅ Realm '${REALM_NAME}' already exists${NC}" |
|||
else |
|||
echo -e "${YELLOW}📝 Creating realm '${REALM_NAME}'...${NC}" |
|||
if kcadm create realms -s realm="${REALM_NAME}" -s enabled=true 2>/dev/null; then |
|||
echo -e "${GREEN}✅ Realm created${NC}" |
|||
else |
|||
# Check if it exists now (might have been created by another process) |
|||
if kcadm get realms | grep -q "${REALM_NAME}"; then |
|||
echo -e "${GREEN}✅ Realm '${REALM_NAME}' already exists (created concurrently)${NC}" |
|||
else |
|||
echo -e "${RED}❌ Failed to create realm '${REALM_NAME}'${NC}" |
|||
return 1 |
|||
fi |
|||
fi |
|||
fi |
|||
} |
|||
|
|||
ensure_client() { |
|||
local id |
|||
id=$(kcadm get clients -r "${REALM_NAME}" -q clientId="${CLIENT_ID}" | jq -r '.[0].id // empty') |
|||
if [[ -n "${id}" ]]; then |
|||
echo -e "${GREEN}✅ Client '${CLIENT_ID}' already exists${NC}" |
|||
else |
|||
echo -e "${YELLOW}📝 Creating client '${CLIENT_ID}'...${NC}" |
|||
kcadm create clients -r "${REALM_NAME}" \ |
|||
-s clientId="${CLIENT_ID}" \ |
|||
-s protocol=openid-connect \ |
|||
-s publicClient=false \ |
|||
-s serviceAccountsEnabled=true \ |
|||
-s directAccessGrantsEnabled=true \ |
|||
-s standardFlowEnabled=true \ |
|||
-s implicitFlowEnabled=false \ |
|||
-s secret="${CLIENT_SECRET}" >/dev/null |
|||
echo -e "${GREEN}✅ Client created${NC}" |
|||
fi |
|||
|
|||
# Create and configure role mapper for the client |
|||
configure_role_mapper "${CLIENT_ID}" |
|||
} |
|||
|
|||
ensure_role() { |
|||
local role="$1" |
|||
if kcadm get roles -r "${REALM_NAME}" | jq -r '.[].name' | grep -qx "${role}"; then |
|||
echo -e "${GREEN}✅ Role '${role}' exists${NC}" |
|||
else |
|||
echo -e "${YELLOW}📝 Creating role '${role}'...${NC}" |
|||
kcadm create roles -r "${REALM_NAME}" -s name="${role}" >/dev/null |
|||
fi |
|||
} |
|||
|
|||
ensure_user() { |
|||
local username="$1" password="$2" |
|||
local uid |
|||
uid=$(kcadm get users -r "${REALM_NAME}" -q username="${username}" | jq -r '.[0].id // empty') |
|||
if [[ -z "${uid}" ]]; then |
|||
echo -e "${YELLOW}📝 Creating user '${username}'...${NC}" |
|||
uid=$(kcadm create users -r "${REALM_NAME}" \ |
|||
-s username="${username}" \ |
|||
-s enabled=true \ |
|||
-s email="${username}@seaweedfs.test" \ |
|||
-s emailVerified=true \ |
|||
-s firstName="${username}" \ |
|||
-s lastName="User" \ |
|||
-i) |
|||
else |
|||
echo -e "${GREEN}✅ User '${username}' exists${NC}" |
|||
fi |
|||
echo -e "${YELLOW}🔑 Setting password for '${username}'...${NC}" |
|||
kcadm set-password -r "${REALM_NAME}" --userid "${uid}" --new-password "${password}" --temporary=false >/dev/null |
|||
} |
|||
|
|||
assign_role() { |
|||
local username="$1" role="$2" |
|||
local uid rid |
|||
uid=$(kcadm get users -r "${REALM_NAME}" -q username="${username}" | jq -r '.[0].id') |
|||
rid=$(kcadm get roles -r "${REALM_NAME}" | jq -r ".[] | select(.name==\"${role}\") | .id") |
|||
# Check if role already assigned |
|||
if kcadm get "users/${uid}/role-mappings/realm" -r "${REALM_NAME}" | jq -r '.[].name' | grep -qx "${role}"; then |
|||
echo -e "${GREEN}✅ User '${username}' already has role '${role}'${NC}" |
|||
return 0 |
|||
fi |
|||
echo -e "${YELLOW}➕ Assigning role '${role}' to '${username}'...${NC}" |
|||
kcadm add-roles -r "${REALM_NAME}" --uid "${uid}" --rolename "${role}" >/dev/null |
|||
} |
|||
|
|||
configure_role_mapper() { |
|||
echo -e "${YELLOW}🔧 Configuring role mapper for client '${CLIENT_ID}'...${NC}" |
|||
|
|||
# Get client's internal ID |
|||
local internal_id |
|||
internal_id=$(kcadm get clients -r "${REALM_NAME}" -q clientId="${CLIENT_ID}" | jq -r '.[0].id // empty') |
|||
|
|||
if [[ -z "${internal_id}" ]]; then |
|||
echo -e "${RED}❌ Could not find client ${client_id} to configure role mapper${NC}" |
|||
return 1 |
|||
fi |
|||
|
|||
# Check if a realm roles mapper already exists for this client |
|||
local existing_mapper |
|||
existing_mapper=$(kcadm get "clients/${internal_id}/protocol-mappers/models" -r "${REALM_NAME}" | jq -r '.[] | select(.name=="realm roles" and .protocolMapper=="oidc-usermodel-realm-role-mapper") | .id // empty') |
|||
|
|||
if [[ -n "${existing_mapper}" ]]; then |
|||
echo -e "${GREEN}✅ Realm roles mapper already exists${NC}" |
|||
else |
|||
echo -e "${YELLOW}📝 Creating realm roles mapper...${NC}" |
|||
|
|||
# Create protocol mapper for realm roles |
|||
kcadm create "clients/${internal_id}/protocol-mappers/models" -r "${REALM_NAME}" \ |
|||
-s name="realm roles" \ |
|||
-s protocol="openid-connect" \ |
|||
-s protocolMapper="oidc-usermodel-realm-role-mapper" \ |
|||
-s consentRequired=false \ |
|||
-s 'config."multivalued"=true' \ |
|||
-s 'config."userinfo.token.claim"=true' \ |
|||
-s 'config."id.token.claim"=true' \ |
|||
-s 'config."access.token.claim"=true' \ |
|||
-s 'config."claim.name"=roles' \ |
|||
-s 'config."jsonType.label"=String' >/dev/null || { |
|||
echo -e "${RED}❌ Failed to create realm roles mapper${NC}" |
|||
return 1 |
|||
} |
|||
|
|||
echo -e "${GREEN}✅ Realm roles mapper created${NC}" |
|||
fi |
|||
} |
|||
|
|||
main() { |
|||
command -v docker >/dev/null || { echo -e "${RED}❌ Docker is required${NC}"; exit 1; } |
|||
command -v jq >/dev/null || { echo -e "${RED}❌ jq is required${NC}"; exit 1; } |
|||
|
|||
ensure_container |
|||
echo "Keycloak URL: ${KEYCLOAK_URL}" |
|||
wait_ready |
|||
admin_login |
|||
ensure_realm |
|||
ensure_client |
|||
configure_role_mapper |
|||
ensure_role "${ROLE_ADMIN}" |
|||
ensure_role "${ROLE_READONLY}" |
|||
ensure_role "${ROLE_WRITEONLY}" |
|||
ensure_role "${ROLE_READWRITE}" |
|||
|
|||
for u in $USERS; do |
|||
ensure_user "$u" "$(get_user_password "$u")" |
|||
done |
|||
|
|||
assign_role admin-user "${ROLE_ADMIN}" |
|||
assign_role read-user "${ROLE_READONLY}" |
|||
assign_role write-user "${ROLE_READWRITE}" |
|||
|
|||
# Also create a dedicated write-only user for testing |
|||
ensure_user write-only-user "$(get_user_password write-only-user)" |
|||
assign_role write-only-user "${ROLE_WRITEONLY}" |
|||
|
|||
# Copy the appropriate IAM configuration for this environment |
|||
setup_iam_config |
|||
|
|||
# Validate the setup by testing authentication and role inclusion |
|||
echo -e "${YELLOW}🔍 Validating setup by testing admin-user authentication and role mapping...${NC}" |
|||
sleep 2 |
|||
|
|||
local validation_result=$(curl -s -w "%{http_code}" -X POST "http://localhost:${KEYCLOAK_PORT}/realms/${REALM_NAME}/protocol/openid-connect/token" \ |
|||
-H "Content-Type: application/x-www-form-urlencoded" \ |
|||
-d "grant_type=password" \ |
|||
-d "client_id=${CLIENT_ID}" \ |
|||
-d "client_secret=${CLIENT_SECRET}" \ |
|||
-d "username=admin-user" \ |
|||
-d "password=admin123" \ |
|||
-d "scope=openid profile email" \ |
|||
-o /tmp/auth_test_response.json) |
|||
|
|||
if [[ "${validation_result: -3}" == "200" ]]; then |
|||
echo -e "${GREEN}✅ Authentication validation successful${NC}" |
|||
|
|||
# Extract and decode JWT token to check for roles |
|||
local access_token=$(cat /tmp/auth_test_response.json | jq -r '.access_token // empty') |
|||
if [[ -n "${access_token}" ]]; then |
|||
# Decode JWT payload (second part) and check for roles |
|||
local payload=$(echo "${access_token}" | cut -d'.' -f2) |
|||
# Add padding if needed for base64 decode |
|||
while [[ $((${#payload} % 4)) -ne 0 ]]; do |
|||
payload="${payload}=" |
|||
done |
|||
|
|||
local decoded=$(echo "${payload}" | base64 -d 2>/dev/null || echo "{}") |
|||
local roles=$(echo "${decoded}" | jq -r '.roles // empty' 2>/dev/null || echo "") |
|||
|
|||
if [[ -n "${roles}" && "${roles}" != "null" ]]; then |
|||
echo -e "${GREEN}✅ JWT token includes roles: ${roles}${NC}" |
|||
else |
|||
echo -e "${YELLOW}⚠️ JWT token does not include 'roles' claim${NC}" |
|||
echo -e "${YELLOW}Decoded payload sample:${NC}" |
|||
echo "${decoded}" | jq '.' 2>/dev/null || echo "${decoded}" |
|||
fi |
|||
fi |
|||
else |
|||
echo -e "${RED}❌ Authentication validation failed with HTTP ${validation_result: -3}${NC}" |
|||
echo -e "${YELLOW}Response body:${NC}" |
|||
cat /tmp/auth_test_response.json 2>/dev/null || echo "No response body" |
|||
echo -e "${YELLOW}This may indicate a setup issue that needs to be resolved${NC}" |
|||
fi |
|||
rm -f /tmp/auth_test_response.json |
|||
|
|||
echo -e "${GREEN}✅ Keycloak test realm '${REALM_NAME}' configured${NC}" |
|||
} |
|||
|
|||
setup_iam_config() { |
|||
echo -e "${BLUE}🔧 Setting up IAM configuration for detected environment${NC}" |
|||
|
|||
# Change to script directory to ensure config files are found |
|||
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
|||
cd "$script_dir" |
|||
|
|||
# Choose the appropriate config based on detected port |
|||
local config_source |
|||
if [[ "${KEYCLOAK_PORT}" == "8080" ]]; then |
|||
config_source="iam_config.github.json" |
|||
echo " Using GitHub Actions configuration (port 8080)" |
|||
else |
|||
config_source="iam_config.local.json" |
|||
echo " Using local development configuration (port ${KEYCLOAK_PORT})" |
|||
fi |
|||
|
|||
# Verify source config exists |
|||
if [[ ! -f "$config_source" ]]; then |
|||
echo -e "${RED}❌ Config file $config_source not found in $script_dir${NC}" |
|||
exit 1 |
|||
fi |
|||
|
|||
# Copy the appropriate config |
|||
cp "$config_source" "iam_config.json" |
|||
|
|||
local detected_issuer=$(cat iam_config.json | jq -r '.providers[] | select(.name=="keycloak") | .config.issuer') |
|||
echo -e "${GREEN}✅ IAM configuration set successfully${NC}" |
|||
echo " - Using config: $config_source" |
|||
echo " - Keycloak issuer: $detected_issuer" |
|||
} |
|||
|
|||
main "$@" |
@ -0,0 +1,404 @@ |
|||
#!/bin/bash |
|||
set -e |
|||
|
|||
# Keycloak configuration for Docker environment |
|||
KEYCLOAK_URL="http://keycloak:8080" |
|||
KEYCLOAK_ADMIN_USER="admin" |
|||
KEYCLOAK_ADMIN_PASSWORD="admin" |
|||
REALM_NAME="seaweedfs-test" |
|||
CLIENT_ID="seaweedfs-s3" |
|||
CLIENT_SECRET="seaweedfs-s3-secret" |
|||
|
|||
echo "🔧 Setting up Keycloak realm and users for SeaweedFS S3 IAM testing..." |
|||
echo "Keycloak URL: $KEYCLOAK_URL" |
|||
|
|||
# Wait for Keycloak to be ready |
|||
echo "⏳ Waiting for Keycloak to be ready..." |
|||
timeout 120 bash -c ' |
|||
until curl -f "$0/health/ready" > /dev/null 2>&1; do |
|||
echo "Waiting for Keycloak..." |
|||
sleep 5 |
|||
done |
|||
echo "✅ Keycloak health check passed" |
|||
' "$KEYCLOAK_URL" |
|||
|
|||
# Download kcadm.sh if not available |
|||
if ! command -v kcadm.sh &> /dev/null; then |
|||
echo "📥 Downloading Keycloak admin CLI..." |
|||
wget -q https://github.com/keycloak/keycloak/releases/download/26.0.7/keycloak-26.0.7.tar.gz |
|||
tar -xzf keycloak-26.0.7.tar.gz |
|||
export PATH="$PWD/keycloak-26.0.7/bin:$PATH" |
|||
fi |
|||
|
|||
# Wait a bit more for admin user initialization |
|||
echo "⏳ Waiting for admin user to be fully initialized..." |
|||
sleep 10 |
|||
|
|||
# Function to execute kcadm commands with retry and multiple password attempts |
|||
kcadm() { |
|||
local max_retries=3 |
|||
local retry_count=0 |
|||
local passwords=("admin" "admin123" "password") |
|||
|
|||
while [ $retry_count -lt $max_retries ]; do |
|||
for password in "${passwords[@]}"; do |
|||
if kcadm.sh "$@" --server "$KEYCLOAK_URL" --realm master --user "$KEYCLOAK_ADMIN_USER" --password "$password" 2>/dev/null; then |
|||
return 0 |
|||
fi |
|||
done |
|||
retry_count=$((retry_count + 1)) |
|||
echo "🔄 Retry $retry_count of $max_retries..." |
|||
sleep 5 |
|||
done |
|||
|
|||
echo "❌ Failed to execute kcadm command after $max_retries retries" |
|||
return 1 |
|||
} |
|||
|
|||
# Create realm |
|||
echo "📝 Creating realm '$REALM_NAME'..." |
|||
kcadm create realms -s realm="$REALM_NAME" -s enabled=true || echo "Realm may already exist" |
|||
echo "✅ Realm created" |
|||
|
|||
# Create OIDC client |
|||
echo "📝 Creating client '$CLIENT_ID'..." |
|||
CLIENT_UUID=$(kcadm create clients -r "$REALM_NAME" \ |
|||
-s clientId="$CLIENT_ID" \ |
|||
-s secret="$CLIENT_SECRET" \ |
|||
-s enabled=true \ |
|||
-s serviceAccountsEnabled=true \ |
|||
-s standardFlowEnabled=true \ |
|||
-s directAccessGrantsEnabled=true \ |
|||
-s 'redirectUris=["*"]' \ |
|||
-s 'webOrigins=["*"]' \ |
|||
-i 2>/dev/null || echo "existing-client") |
|||
|
|||
if [ "$CLIENT_UUID" != "existing-client" ]; then |
|||
echo "✅ Client created with ID: $CLIENT_UUID" |
|||
else |
|||
echo "✅ Using existing client" |
|||
CLIENT_UUID=$(kcadm get clients -r "$REALM_NAME" -q clientId="$CLIENT_ID" --fields id --format csv --noquotes | tail -n +2) |
|||
fi |
|||
|
|||
# Configure protocol mapper for roles |
|||
echo "🔧 Configuring role mapper for client '$CLIENT_ID'..." |
|||
MAPPER_CONFIG='{ |
|||
"protocol": "openid-connect", |
|||
"protocolMapper": "oidc-usermodel-realm-role-mapper", |
|||
"name": "realm-roles", |
|||
"config": { |
|||
"claim.name": "roles", |
|||
"jsonType.label": "String", |
|||
"multivalued": "true", |
|||
"usermodel.realmRoleMapping.rolePrefix": "" |
|||
} |
|||
}' |
|||
|
|||
kcadm create clients/"$CLIENT_UUID"/protocol-mappers/models -r "$REALM_NAME" -b "$MAPPER_CONFIG" 2>/dev/null || echo "✅ Role mapper already exists" |
|||
echo "✅ Realm roles mapper configured" |
|||
|
|||
# Create realm roles |
|||
echo "📝 Creating realm roles..." |
|||
for role in "s3-admin" "s3-read-only" "s3-write-only" "s3-read-write"; do |
|||
kcadm create roles -r "$REALM_NAME" -s name="$role" 2>/dev/null || echo "Role $role may already exist" |
|||
done |
|||
|
|||
# Create users with roles |
|||
declare -A USERS=( |
|||
["admin-user"]="s3-admin" |
|||
["read-user"]="s3-read-only" |
|||
["write-user"]="s3-read-write" |
|||
["write-only-user"]="s3-write-only" |
|||
) |
|||
|
|||
for username in "${!USERS[@]}"; do |
|||
role="${USERS[$username]}" |
|||
password="${username//[^a-zA-Z]/}123" # e.g., "admin-user" -> "adminuser123" |
|||
|
|||
echo "📝 Creating user '$username'..." |
|||
kcadm create users -r "$REALM_NAME" \ |
|||
-s username="$username" \ |
|||
-s enabled=true \ |
|||
-s firstName="Test" \ |
|||
-s lastName="User" \ |
|||
-s email="$username@test.com" 2>/dev/null || echo "User $username may already exist" |
|||
|
|||
echo "🔑 Setting password for '$username'..." |
|||
kcadm set-password -r "$REALM_NAME" --username "$username" --new-password "$password" |
|||
|
|||
echo "➕ Assigning role '$role' to '$username'..." |
|||
kcadm add-roles -r "$REALM_NAME" --uusername "$username" --rolename "$role" |
|||
done |
|||
|
|||
# Create IAM configuration for Docker environment |
|||
echo "🔧 Setting up IAM configuration for Docker environment..." |
|||
cat > iam_config.json << 'EOF' |
|||
{ |
|||
"sts": { |
|||
"tokenDuration": 3600000000000, |
|||
"maxSessionLength": 43200000000000, |
|||
"issuer": "seaweedfs-sts", |
|||
"signingKey": "dGVzdC1zaWduaW5nLWtleS0zMi1jaGFyYWN0ZXJzLWxvbmc=" |
|||
}, |
|||
"providers": [ |
|||
{ |
|||
"name": "keycloak", |
|||
"type": "oidc", |
|||
"enabled": true, |
|||
"config": { |
|||
"issuer": "http://keycloak:8080/realms/seaweedfs-test", |
|||
"clientId": "seaweedfs-s3", |
|||
"clientSecret": "seaweedfs-s3-secret", |
|||
"jwksUri": "http://keycloak:8080/realms/seaweedfs-test/protocol/openid-connect/certs", |
|||
"userInfoUri": "http://keycloak:8080/realms/seaweedfs-test/protocol/openid-connect/userinfo", |
|||
"scopes": ["openid", "profile", "email"], |
|||
"claimsMapping": { |
|||
"username": "preferred_username", |
|||
"email": "email", |
|||
"name": "name" |
|||
}, |
|||
"roleMapping": { |
|||
"rules": [ |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-admin", |
|||
"role": "arn:seaweed:iam::role/KeycloakAdminRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-read-only", |
|||
"role": "arn:seaweed:iam::role/KeycloakReadOnlyRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-write-only", |
|||
"role": "arn:seaweed:iam::role/KeycloakWriteOnlyRole" |
|||
}, |
|||
{ |
|||
"claim": "roles", |
|||
"value": "s3-read-write", |
|||
"role": "arn:seaweed:iam::role/KeycloakReadWriteRole" |
|||
} |
|||
], |
|||
"defaultRole": "arn:seaweed:iam::role/KeycloakReadOnlyRole" |
|||
} |
|||
} |
|||
} |
|||
], |
|||
"policy": { |
|||
"defaultEffect": "Deny", |
|||
"storeType": "memory" |
|||
}, |
|||
"roles": [ |
|||
{ |
|||
"roleName": "KeycloakAdminRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakAdminRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3AdminPolicy"], |
|||
"description": "Admin role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakReadOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakReadOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadOnlyPolicy"], |
|||
"description": "Read-only role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakWriteOnlyRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakWriteOnlyRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3WriteOnlyPolicy"], |
|||
"description": "Write-only role for Keycloak users" |
|||
}, |
|||
{ |
|||
"roleName": "KeycloakReadWriteRole", |
|||
"roleArn": "arn:seaweed:iam::role/KeycloakReadWriteRole", |
|||
"trustPolicy": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Principal": { |
|||
"Federated": "keycloak" |
|||
}, |
|||
"Action": ["sts:AssumeRoleWithWebIdentity"] |
|||
} |
|||
] |
|||
}, |
|||
"attachedPolicies": ["S3ReadWritePolicy"], |
|||
"description": "Read-write role for Keycloak users" |
|||
} |
|||
], |
|||
"policies": [ |
|||
{ |
|||
"name": "S3AdminPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["s3:*"], |
|||
"Resource": ["*"] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3WriteOnlyPolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["s3:*"], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Deny", |
|||
"Action": [ |
|||
"s3:GetObject", |
|||
"s3:ListBucket" |
|||
], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"name": "S3ReadWritePolicy", |
|||
"document": { |
|||
"Version": "2012-10-17", |
|||
"Statement": [ |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["s3:*"], |
|||
"Resource": [ |
|||
"arn:seaweed:s3:::*", |
|||
"arn:seaweed:s3:::*/*" |
|||
] |
|||
}, |
|||
{ |
|||
"Effect": "Allow", |
|||
"Action": ["sts:ValidateSession"], |
|||
"Resource": ["*"] |
|||
} |
|||
] |
|||
} |
|||
} |
|||
] |
|||
} |
|||
EOF |
|||
|
|||
# Validate setup by testing authentication |
|||
echo "🔍 Validating setup by testing admin-user authentication and role mapping..." |
|||
KEYCLOAK_TOKEN_URL="http://keycloak:8080/realms/$REALM_NAME/protocol/openid-connect/token" |
|||
|
|||
# Get access token for admin-user |
|||
ACCESS_TOKEN=$(curl -s -X POST "$KEYCLOAK_TOKEN_URL" \ |
|||
-H "Content-Type: application/x-www-form-urlencoded" \ |
|||
-d "grant_type=password" \ |
|||
-d "client_id=$CLIENT_ID" \ |
|||
-d "client_secret=$CLIENT_SECRET" \ |
|||
-d "username=admin-user" \ |
|||
-d "password=adminuser123" \ |
|||
-d "scope=openid profile email" | jq -r '.access_token') |
|||
|
|||
if [ "$ACCESS_TOKEN" = "null" ] || [ -z "$ACCESS_TOKEN" ]; then |
|||
echo "❌ Failed to obtain access token" |
|||
exit 1 |
|||
fi |
|||
|
|||
echo "✅ Authentication validation successful" |
|||
|
|||
# Decode and check JWT claims |
|||
PAYLOAD=$(echo "$ACCESS_TOKEN" | cut -d'.' -f2) |
|||
# Add padding for base64 decode |
|||
while [ $((${#PAYLOAD} % 4)) -ne 0 ]; do |
|||
PAYLOAD="${PAYLOAD}=" |
|||
done |
|||
|
|||
CLAIMS=$(echo "$PAYLOAD" | base64 -d 2>/dev/null | jq .) |
|||
ROLES=$(echo "$CLAIMS" | jq -r '.roles[]?') |
|||
|
|||
if [ -n "$ROLES" ]; then |
|||
echo "✅ JWT token includes roles: [$(echo "$ROLES" | tr '\n' ',' | sed 's/,$//' | sed 's/,/, /g')]" |
|||
else |
|||
echo "⚠️ No roles found in JWT token" |
|||
fi |
|||
|
|||
echo "✅ Keycloak test realm '$REALM_NAME' configured for Docker environment" |
|||
echo "🐳 Setup complete! You can now run: docker-compose up -d" |
Write
Preview
Loading…
Cancel
Save
Reference in new issue