diff --git a/test/s3/iam/setup_keycloak.sh b/test/s3/iam/setup_keycloak.sh index 60073952a..c6bd8a297 100755 --- a/test/s3/iam/setup_keycloak.sh +++ b/test/s3/iam/setup_keycloak.sh @@ -77,15 +77,21 @@ create_realm() { "sslRequired": "none" }') - local response=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms" \ + local http_code=$(curl -s -w "%{http_code}" -X POST "$KEYCLOAK_URL/admin/realms" \ -H "Authorization: Bearer $token" \ -H "Content-Type: application/json" \ - -d "$payload") + -d "$payload" \ + -o /tmp/realm_response.json) - if [[ -n "$response" && "$response" != *"error"* ]]; then + local response=$(cat /tmp/realm_response.json 2>/dev/null || echo "") + + if [[ "$http_code" == "201" ]]; then echo "✅ Realm created successfully" + return 0 else - echo "⚠️ Realm creation response: $response" + echo "❌ Realm creation failed with HTTP $http_code" + echo "📋 Response: $response" + return 1 fi } @@ -110,15 +116,21 @@ create_client() { "webOrigins": ["*"] }') - local response=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/clients" \ + local http_code=$(curl -s -w "%{http_code}" -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/clients" \ -H "Authorization: Bearer $token" \ -H "Content-Type: application/json" \ - -d "$payload") + -d "$payload" \ + -o /tmp/client_response.json) + + local response=$(cat /tmp/client_response.json 2>/dev/null || echo "") - if [[ -n "$response" && "$response" != *"error"* ]]; then + if [[ "$http_code" == "201" ]]; then echo "✅ Client created successfully" + return 0 else - echo "⚠️ Client creation response: $response" + echo "❌ Client creation failed with HTTP $http_code" + echo "📋 Response: $response" + return 1 fi } @@ -138,15 +150,21 @@ create_role() { "description": $description }') - local response=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/roles" \ + local http_code=$(curl -s -w "%{http_code}" -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/roles" \ -H "Authorization: Bearer $token" \ -H "Content-Type: application/json" \ - -d "$payload") + -d "$payload" \ + -o /tmp/role_response_$role_name.json) + + local response=$(cat /tmp/role_response_$role_name.json 2>/dev/null || echo "") - if [[ -n "$response" && "$response" != *"error"* ]]; then + if [[ "$http_code" == "201" ]]; then echo "✅ Role '$role_name' created successfully" + return 0 else - echo "⚠️ Role creation response: $response" + echo "❌ Role '$role_name' creation failed with HTTP $http_code" + echo "📋 Response: $response" + return 1 fi } @@ -183,15 +201,20 @@ create_user() { }] }') - local user_response=$(curl -s -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/users" \ + local http_code=$(curl -s -w "%{http_code}" -X POST "$KEYCLOAK_URL/admin/realms/$REALM_NAME/users" \ -H "Authorization: Bearer $token" \ -H "Content-Type: application/json" \ - -d "$user_payload") + -d "$user_payload" \ + -o /tmp/user_response_$username.json) - if [[ -n "$user_response" && "$user_response" != *"error"* ]]; then + local user_response=$(cat /tmp/user_response_$username.json 2>/dev/null || echo "") + + if [[ "$http_code" == "201" ]]; then echo "✅ User '$username' created successfully" else - echo "⚠️ User creation response: $user_response" + echo "❌ User '$username' creation failed with HTTP $http_code" + echo "📋 Response: $user_response" + return 1 fi # Get user ID @@ -258,27 +281,67 @@ main() { # Create realm if it doesn't exist if ! realm_exists "$ADMIN_TOKEN"; then - create_realm "$ADMIN_TOKEN" + if ! create_realm "$ADMIN_TOKEN"; then + echo "❌ Failed to create realm $REALM_NAME" + exit 1 + fi sleep 2 + + # Wait for realm to be fully available + echo "⏳ Waiting for realm to be fully initialized..." + timeout 60 bash -c " + while ! curl -fs $KEYCLOAK_URL/realms/$REALM_NAME/.well-known/openid-configuration >/dev/null 2>&1; do + echo ' ... waiting for realm endpoint' + sleep 3 + done + " || { + echo "❌ Realm $REALM_NAME not accessible after creation" + exit 1 + } + echo "✅ Realm is now accessible" else echo "✅ Realm $REALM_NAME already exists" fi # Create client - create_client "$ADMIN_TOKEN" + if ! create_client "$ADMIN_TOKEN"; then + echo "❌ Failed to create client $CLIENT_ID" + exit 1 + fi sleep 1 # Create roles - create_role "$ADMIN_TOKEN" "s3-admin" "SeaweedFS S3 Administrator" - create_role "$ADMIN_TOKEN" "s3-read-only" "SeaweedFS S3 Read-Only User" - create_role "$ADMIN_TOKEN" "s3-write-only" "SeaweedFS S3 Write-Only User" - create_role "$ADMIN_TOKEN" "s3-read-write" "SeaweedFS S3 Read-Write User" + if ! create_role "$ADMIN_TOKEN" "s3-admin" "SeaweedFS S3 Administrator"; then + echo "❌ Failed to create s3-admin role" + exit 1 + fi + if ! create_role "$ADMIN_TOKEN" "s3-read-only" "SeaweedFS S3 Read-Only User"; then + echo "❌ Failed to create s3-read-only role" + exit 1 + fi + if ! create_role "$ADMIN_TOKEN" "s3-write-only" "SeaweedFS S3 Write-Only User"; then + echo "❌ Failed to create s3-write-only role" + exit 1 + fi + if ! create_role "$ADMIN_TOKEN" "s3-read-write" "SeaweedFS S3 Read-Write User"; then + echo "❌ Failed to create s3-read-write role" + exit 1 + fi sleep 1 # Create test users - create_user "$ADMIN_TOKEN" "admin-user" "admin123" "admin@seaweedfs.test" "Admin" "User" "s3-admin" - create_user "$ADMIN_TOKEN" "read-user" "read123" "read@seaweedfs.test" "Read" "User" "s3-read-only" - create_user "$ADMIN_TOKEN" "write-user" "write123" "write@seaweedfs.test" "Write" "User" "s3-write-only" + if ! create_user "$ADMIN_TOKEN" "admin-user" "admin123" "admin@seaweedfs.test" "Admin" "User" "s3-admin"; then + echo "❌ Failed to create admin-user" + exit 1 + fi + if ! create_user "$ADMIN_TOKEN" "read-user" "read123" "read@seaweedfs.test" "Read" "User" "s3-read-only"; then + echo "❌ Failed to create read-user" + exit 1 + fi + if ! create_user "$ADMIN_TOKEN" "write-user" "write123" "write@seaweedfs.test" "Write" "User" "s3-write-only"; then + echo "❌ Failed to create write-user" + exit 1 + fi echo "✅ Keycloak setup completed successfully!" echo "🔗 Realm: $KEYCLOAK_URL/realms/$REALM_NAME" @@ -287,6 +350,9 @@ main() { echo " - read-user (password: read123) - s3-read-only role" echo " - write-user (password: write123) - s3-write-only role" echo "🔑 Client: $CLIENT_ID (secret: $CLIENT_SECRET)" + + # Cleanup temporary files + rm -f /tmp/realm_response.json /tmp/client_response.json /tmp/role_response_*.json /tmp/user_response_*.json } # Run main function diff --git a/weed/iam/policy/policy_engine_distributed_test.go b/weed/iam/policy/policy_engine_distributed_test.go index 0098f8bd1..4792343ce 100644 --- a/weed/iam/policy/policy_engine_distributed_test.go +++ b/weed/iam/policy/policy_engine_distributed_test.go @@ -59,23 +59,23 @@ func TestDistributedPolicyEngine(t *testing.T) { } // Store policy on instance 1 - err := instance1.AddPolicy("TestPolicy", testPolicy) + err := instance1.AddPolicy("", "TestPolicy", testPolicy) require.NoError(t, err, "Should be able to store policy on instance 1") // For memory storage, each instance has separate storage // In production with filer storage, all instances would share the same policies // Verify policy exists on instance 1 - storedPolicy1, err := instance1.store.GetPolicy(ctx, "TestPolicy") + storedPolicy1, err := instance1.store.GetPolicy(ctx, "", "TestPolicy") require.NoError(t, err, "Policy should exist on instance 1") assert.Equal(t, "2012-10-17", storedPolicy1.Version) assert.Len(t, storedPolicy1.Statement, 2) // For demonstration: store same policy on other instances - err = instance2.AddPolicy("TestPolicy", testPolicy) + err = instance2.AddPolicy("", "TestPolicy", testPolicy) require.NoError(t, err, "Should be able to store policy on instance 2") - err = instance3.AddPolicy("TestPolicy", testPolicy) + err = instance3.AddPolicy("", "TestPolicy", testPolicy) require.NoError(t, err, "Should be able to store policy on instance 3") }) @@ -92,9 +92,9 @@ func TestDistributedPolicyEngine(t *testing.T) { } // Evaluate policy on all instances - result1, err1 := instance1.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) - result2, err2 := instance2.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) - result3, err3 := instance3.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) + result1, err1 := instance1.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) + result2, err2 := instance2.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) + result3, err3 := instance3.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) require.NoError(t, err1, "Evaluation should succeed on instance 1") require.NoError(t, err2, "Evaluation should succeed on instance 2") @@ -124,9 +124,9 @@ func TestDistributedPolicyEngine(t *testing.T) { } // All instances should consistently apply deny precedence - result1, err1 := instance1.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) - result2, err2 := instance2.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) - result3, err3 := instance3.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) + result1, err1 := instance1.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) + result2, err2 := instance2.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) + result3, err3 := instance3.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) require.NoError(t, err1) require.NoError(t, err2) @@ -151,9 +151,9 @@ func TestDistributedPolicyEngine(t *testing.T) { Resource: "arn:seaweed:filer::path/test", } - result1, err1 := instance1.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) - result2, err2 := instance2.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) - result3, err3 := instance3.Evaluate(ctx, evalCtx, []string{"TestPolicy"}) + result1, err1 := instance1.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) + result2, err2 := instance2.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) + result3, err3 := instance3.Evaluate(ctx, "", evalCtx, []string{"TestPolicy"}) require.NoError(t, err1) require.NoError(t, err2) @@ -201,8 +201,8 @@ func TestPolicyEngineConfigurationConsistency(t *testing.T) { Resource: "arn:seaweed:test:::resource", } - result1, _ := instance1.Evaluate(context.Background(), evalCtx, []string{}) - result2, _ := instance2.Evaluate(context.Background(), evalCtx, []string{}) + result1, _ := instance1.Evaluate(context.Background(), "", evalCtx, []string{}) + result2, _ := instance2.Evaluate(context.Background(), "", evalCtx, []string{}) // Results should be different due to different default effects assert.NotEqual(t, result1.Effect, result2.Effect, "Different default effects should produce different results") @@ -253,15 +253,15 @@ func TestPolicyStoreDistributed(t *testing.T) { } // Store policy in store1 - err := store1.StorePolicy(ctx, "TestPolicy", policy) + err := store1.StorePolicy(ctx, "", "TestPolicy", policy) require.NoError(t, err) // Policy should exist in store1 - _, err = store1.GetPolicy(ctx, "TestPolicy") + _, err = store1.GetPolicy(ctx, "", "TestPolicy") assert.NoError(t, err, "Policy should exist in store1") // Policy should NOT exist in store2 (different instance) - _, err = store2.GetPolicy(ctx, "TestPolicy") + _, err = store2.GetPolicy(ctx, "", "TestPolicy") assert.Error(t, err, "Policy should not exist in store2") assert.Contains(t, err.Error(), "not found", "Should be a not found error") }) @@ -283,7 +283,7 @@ func TestPolicyStoreDistributed(t *testing.T) { } // Evaluate with non-existent policies - result, err := engine.Evaluate(ctx, evalCtx, []string{"NonExistentPolicy1", "NonExistentPolicy2"}) + result, err := engine.Evaluate(ctx, "", evalCtx, []string{"NonExistentPolicy1", "NonExistentPolicy2"}) require.NoError(t, err, "Should not error on missing policies") // Should use default effect when no policies can be loaded @@ -355,7 +355,7 @@ func TestPolicyEvaluationPerformance(t *testing.T) { }, } - err := engine.AddPolicy(fmt.Sprintf("Policy%d", i), policy) + err := engine.AddPolicy("", fmt.Sprintf("Policy%d", i), policy) require.NoError(t, err) } @@ -374,7 +374,7 @@ func TestPolicyEvaluationPerformance(t *testing.T) { // Measure evaluation time start := time.Now() for i := 0; i < 100; i++ { - _, err := engine.Evaluate(ctx, evalCtx, policyNames) + _, err := engine.Evaluate(ctx, "", evalCtx, policyNames) require.NoError(t, err) } duration := time.Since(start) diff --git a/weed/iam/policy/policy_engine_test.go b/weed/iam/policy/policy_engine_test.go index fdece162f..4e6cd3c3a 100644 --- a/weed/iam/policy/policy_engine_test.go +++ b/weed/iam/policy/policy_engine_test.go @@ -153,7 +153,7 @@ func TestPolicyEvaluation(t *testing.T) { }, } - err := engine.AddPolicy("read-policy", readPolicy) + err := engine.AddPolicy("", "read-policy", readPolicy) require.NoError(t, err) denyPolicy := &PolicyDocument{ @@ -168,7 +168,7 @@ func TestPolicyEvaluation(t *testing.T) { }, } - err = engine.AddPolicy("deny-policy", denyPolicy) + err = engine.AddPolicy("", "deny-policy", denyPolicy) require.NoError(t, err) tests := []struct { @@ -224,7 +224,7 @@ func TestPolicyEvaluation(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := engine.Evaluate(context.Background(), tt.context, tt.policies) + result, err := engine.Evaluate(context.Background(), "", tt.context, tt.policies) assert.NoError(t, err) assert.Equal(t, tt.want, result.Effect) @@ -259,7 +259,7 @@ func TestConditionEvaluation(t *testing.T) { }, } - err := engine.AddPolicy("ip-conditional", conditionalPolicy) + err := engine.AddPolicy("", "ip-conditional", conditionalPolicy) require.NoError(t, err) tests := []struct { @@ -307,7 +307,7 @@ func TestConditionEvaluation(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := engine.Evaluate(context.Background(), tt.context, []string{"ip-conditional"}) + result, err := engine.Evaluate(context.Background(), "", tt.context, []string{"ip-conditional"}) assert.NoError(t, err) assert.Equal(t, tt.want, result.Effect)