You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							278 lines
						
					
					
						
							9.5 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							278 lines
						
					
					
						
							9.5 KiB
						
					
					
				
								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
							 | 
						|
								}
							 |