aboutsummaryrefslogtreecommitdiff
path: root/weed/iam/sts/session_policy_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/iam/sts/session_policy_test.go')
-rw-r--r--weed/iam/sts/session_policy_test.go278
1 files changed, 278 insertions, 0 deletions
diff --git a/weed/iam/sts/session_policy_test.go b/weed/iam/sts/session_policy_test.go
new file mode 100644
index 000000000..6f94169ec
--- /dev/null
+++ b/weed/iam/sts/session_policy_test.go
@@ -0,0 +1,278 @@
+package sts
+
+import (
+ "context"
+ "testing"
+ "time"
+
+ "github.com/golang-jwt/jwt/v5"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// createSessionPolicyTestJWT creates a test JWT token for session policy tests
+func createSessionPolicyTestJWT(t *testing.T, issuer, subject string) string {
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
+ "iss": issuer,
+ "sub": subject,
+ "aud": "test-client",
+ "exp": time.Now().Add(time.Hour).Unix(),
+ "iat": time.Now().Unix(),
+ })
+
+ tokenString, err := token.SignedString([]byte("test-signing-key"))
+ require.NoError(t, err)
+ return tokenString
+}
+
+// TestAssumeRoleWithWebIdentity_SessionPolicy tests the handling of the Policy field
+// in AssumeRoleWithWebIdentityRequest to ensure users are properly informed that
+// session policies are not currently supported
+func TestAssumeRoleWithWebIdentity_SessionPolicy(t *testing.T) {
+ service := setupTestSTSService(t)
+
+ t.Run("should_reject_request_with_session_policy", func(t *testing.T) {
+ ctx := context.Background()
+
+ // Create a request with a session policy
+ sessionPolicy := `{
+ "Version": "2012-10-17",
+ "Statement": [{
+ "Effect": "Allow",
+ "Action": "s3:GetObject",
+ "Resource": "arn:aws:s3:::example-bucket/*"
+ }]
+ }`
+
+ testToken := createSessionPolicyTestJWT(t, "test-issuer", "test-user")
+
+ request := &AssumeRoleWithWebIdentityRequest{
+ RoleArn: "arn:seaweed:iam::role/TestRole",
+ WebIdentityToken: testToken,
+ RoleSessionName: "test-session",
+ DurationSeconds: nil, // Use default
+ Policy: &sessionPolicy, // ← Session policy provided
+ }
+
+ // Should return an error indicating session policies are not supported
+ response, err := service.AssumeRoleWithWebIdentity(ctx, request)
+
+ // Verify the error
+ assert.Error(t, err)
+ assert.Nil(t, response)
+ assert.Contains(t, err.Error(), "session policies are not currently supported")
+ assert.Contains(t, err.Error(), "Policy parameter must be omitted")
+ })
+
+ t.Run("should_succeed_without_session_policy", func(t *testing.T) {
+ ctx := context.Background()
+ testToken := createSessionPolicyTestJWT(t, "test-issuer", "test-user")
+
+ request := &AssumeRoleWithWebIdentityRequest{
+ RoleArn: "arn:seaweed:iam::role/TestRole",
+ WebIdentityToken: testToken,
+ RoleSessionName: "test-session",
+ DurationSeconds: nil, // Use default
+ Policy: nil, // ← No session policy
+ }
+
+ // Should succeed without session policy
+ response, err := service.AssumeRoleWithWebIdentity(ctx, request)
+
+ // Verify success
+ require.NoError(t, err)
+ require.NotNil(t, response)
+ assert.NotNil(t, response.Credentials)
+ assert.NotEmpty(t, response.Credentials.AccessKeyId)
+ assert.NotEmpty(t, response.Credentials.SecretAccessKey)
+ assert.NotEmpty(t, response.Credentials.SessionToken)
+ })
+
+ t.Run("should_succeed_with_empty_policy_pointer", func(t *testing.T) {
+ ctx := context.Background()
+ testToken := createSessionPolicyTestJWT(t, "test-issuer", "test-user")
+
+ request := &AssumeRoleWithWebIdentityRequest{
+ RoleArn: "arn:seaweed:iam::role/TestRole",
+ WebIdentityToken: testToken,
+ RoleSessionName: "test-session",
+ Policy: nil, // ← Explicitly nil
+ }
+
+ // Should succeed with nil policy pointer
+ response, err := service.AssumeRoleWithWebIdentity(ctx, request)
+
+ require.NoError(t, err)
+ require.NotNil(t, response)
+ assert.NotNil(t, response.Credentials)
+ })
+
+ t.Run("should_reject_empty_string_policy", func(t *testing.T) {
+ ctx := context.Background()
+
+ emptyPolicy := "" // Empty string, but still a non-nil pointer
+
+ request := &AssumeRoleWithWebIdentityRequest{
+ RoleArn: "arn:seaweed:iam::role/TestRole",
+ WebIdentityToken: createSessionPolicyTestJWT(t, "test-issuer", "test-user"),
+ RoleSessionName: "test-session",
+ Policy: &emptyPolicy, // ← Non-nil pointer to empty string
+ }
+
+ // Should still reject because pointer is not nil
+ response, err := service.AssumeRoleWithWebIdentity(ctx, request)
+
+ assert.Error(t, err)
+ assert.Nil(t, response)
+ assert.Contains(t, err.Error(), "session policies are not currently supported")
+ })
+}
+
+// TestAssumeRoleWithWebIdentity_SessionPolicy_ErrorMessage tests that the error message
+// is clear and helps users understand what they need to do
+func TestAssumeRoleWithWebIdentity_SessionPolicy_ErrorMessage(t *testing.T) {
+ service := setupTestSTSService(t)
+
+ ctx := context.Background()
+ complexPolicy := `{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "AllowS3Access",
+ "Effect": "Allow",
+ "Action": [
+ "s3:GetObject",
+ "s3:PutObject"
+ ],
+ "Resource": [
+ "arn:aws:s3:::my-bucket/*",
+ "arn:aws:s3:::my-bucket"
+ ],
+ "Condition": {
+ "StringEquals": {
+ "s3:prefix": ["documents/", "images/"]
+ }
+ }
+ }
+ ]
+ }`
+
+ testToken := createSessionPolicyTestJWT(t, "test-issuer", "test-user")
+
+ request := &AssumeRoleWithWebIdentityRequest{
+ RoleArn: "arn:seaweed:iam::role/TestRole",
+ WebIdentityToken: testToken,
+ RoleSessionName: "test-session-with-complex-policy",
+ Policy: &complexPolicy,
+ }
+
+ response, err := service.AssumeRoleWithWebIdentity(ctx, request)
+
+ // Verify error details
+ require.Error(t, err)
+ assert.Nil(t, response)
+
+ errorMsg := err.Error()
+
+ // The error should be clear and actionable
+ assert.Contains(t, errorMsg, "session policies are not currently supported",
+ "Error should explain that session policies aren't supported")
+ assert.Contains(t, errorMsg, "Policy parameter must be omitted",
+ "Error should specify what action the user needs to take")
+
+ // Should NOT contain internal implementation details
+ assert.NotContains(t, errorMsg, "nil pointer",
+ "Error should not expose internal implementation details")
+ assert.NotContains(t, errorMsg, "struct field",
+ "Error should not expose internal struct details")
+}
+
+// Test edge case scenarios for the Policy field handling
+func TestAssumeRoleWithWebIdentity_SessionPolicy_EdgeCases(t *testing.T) {
+ service := setupTestSTSService(t)
+
+ t.Run("malformed_json_policy_still_rejected", func(t *testing.T) {
+ ctx := context.Background()
+ malformedPolicy := `{"Version": "2012-10-17", "Statement": [` // Incomplete JSON
+
+ request := &AssumeRoleWithWebIdentityRequest{
+ RoleArn: "arn:seaweed:iam::role/TestRole",
+ WebIdentityToken: createSessionPolicyTestJWT(t, "test-issuer", "test-user"),
+ RoleSessionName: "test-session",
+ Policy: &malformedPolicy,
+ }
+
+ // Should reject before even parsing the policy JSON
+ response, err := service.AssumeRoleWithWebIdentity(ctx, request)
+
+ assert.Error(t, err)
+ assert.Nil(t, response)
+ assert.Contains(t, err.Error(), "session policies are not currently supported")
+ })
+
+ t.Run("policy_with_whitespace_still_rejected", func(t *testing.T) {
+ ctx := context.Background()
+ whitespacePolicy := " \t\n " // Only whitespace
+
+ request := &AssumeRoleWithWebIdentityRequest{
+ RoleArn: "arn:seaweed:iam::role/TestRole",
+ WebIdentityToken: createSessionPolicyTestJWT(t, "test-issuer", "test-user"),
+ RoleSessionName: "test-session",
+ Policy: &whitespacePolicy,
+ }
+
+ // Should reject any non-nil policy, even whitespace
+ response, err := service.AssumeRoleWithWebIdentity(ctx, request)
+
+ assert.Error(t, err)
+ assert.Nil(t, response)
+ assert.Contains(t, err.Error(), "session policies are not currently supported")
+ })
+}
+
+// TestAssumeRoleWithWebIdentity_PolicyFieldDocumentation verifies that the struct
+// field is properly documented to help developers understand the limitation
+func TestAssumeRoleWithWebIdentity_PolicyFieldDocumentation(t *testing.T) {
+ // This test documents the current behavior and ensures the struct field
+ // exists with proper typing
+ request := &AssumeRoleWithWebIdentityRequest{}
+
+ // Verify the Policy field exists and has the correct type
+ assert.IsType(t, (*string)(nil), request.Policy,
+ "Policy field should be *string type for optional JSON policy")
+
+ // Verify initial value is nil (no policy by default)
+ assert.Nil(t, request.Policy,
+ "Policy field should default to nil (no session policy)")
+
+ // Test that we can set it to a string pointer (even though it will be rejected)
+ policyValue := `{"Version": "2012-10-17"}`
+ request.Policy = &policyValue
+ assert.NotNil(t, request.Policy, "Should be able to assign policy value")
+ assert.Equal(t, policyValue, *request.Policy, "Policy value should be preserved")
+}
+
+// TestAssumeRoleWithCredentials_NoSessionPolicySupport verifies that
+// AssumeRoleWithCredentialsRequest doesn't have a Policy field, which is correct
+// since credential-based role assumption typically doesn't support session policies
+func TestAssumeRoleWithCredentials_NoSessionPolicySupport(t *testing.T) {
+ // Verify that AssumeRoleWithCredentialsRequest doesn't have a Policy field
+ // This is the expected behavior since session policies are typically only
+ // supported with web identity (OIDC/SAML) flows in AWS STS
+ request := &AssumeRoleWithCredentialsRequest{
+ RoleArn: "arn:seaweed:iam::role/TestRole",
+ Username: "testuser",
+ Password: "testpass",
+ RoleSessionName: "test-session",
+ ProviderName: "ldap",
+ }
+
+ // The struct should compile and work without a Policy field
+ assert.NotNil(t, request)
+ assert.Equal(t, "arn:seaweed:iam::role/TestRole", request.RoleArn)
+ assert.Equal(t, "testuser", request.Username)
+
+ // This documents that credential-based assume role does NOT support session policies
+ // which matches AWS STS behavior where session policies are primarily for
+ // web identity (OIDC/SAML) and federation scenarios
+}