Files
traefikoidc/custom_claims_test.go
T
lukaszraczylo e64fc7f730 Add redis support for distributed caching (#83)
* Add redis support for distributed caching

* Move towards the self-provided Redis connection pool and RESP protocol implementation.
Official redis client library won't work with yaegi.

* fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* fixup! fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.

* ... and another all nighter.

* fixup! ... and another all nighter.

* fixup! fixup! ... and another all nighter.

* fixup! fixup! fixup! ... and another all nighter.

* Resolve issue #85 by adding ability to set custom claims in JWT tokens

* Remove redundant validation in auth middleware ( issue #89 )

* Add ability to set cookie prefix for session cookies ( #87 )

* fixup! Add ability to set cookie prefix for session cookies ( #87 )

* Add ability to set cookie max age - issue #91

* Potential fix for code scanning alert no. 10: Size computation for allocation may overflow

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* fixup! Merge main into 0.8.0-redis: resolve conflicts

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-11-30 02:18:46 +00:00

365 lines
10 KiB
Go

//go:build !yaegi
package traefikoidc
import (
"testing"
)
// TestCustomClaimNames_DefaultBehavior tests backward compatibility with default claim names
func TestCustomClaimNames_DefaultBehavior(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Explicitly set defaults to test backward compatibility
ts.tOidc.roleClaimName = "roles"
ts.tOidc.groupClaimName = "groups"
// Test that when no custom claim names are configured, it uses defaults "roles" and "groups"
claims := map[string]interface{}{
"groups": []interface{}{"admin", "users"},
"roles": []interface{}{"editor", "viewer"},
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
groups, roles, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !stringSliceEqual(groups, []string{"admin", "users"}) {
t.Errorf("Expected groups [admin users], got %v", groups)
}
if !stringSliceEqual(roles, []string{"editor", "viewer"}) {
t.Errorf("Expected roles [editor viewer], got %v", roles)
}
}
// TestCustomClaimNames_Auth0Namespaced tests Auth0-style namespaced claims
func TestCustomClaimNames_Auth0Namespaced(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure custom claim names for Auth0
ts.tOidc.roleClaimName = "https://myapp.com/roles"
ts.tOidc.groupClaimName = "https://myapp.com/groups"
// Create token with Auth0-style namespaced claims
claims := map[string]interface{}{
"https://myapp.com/groups": []interface{}{"admin", "users"},
"https://myapp.com/roles": []interface{}{"editor", "viewer"},
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
groups, roles, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !stringSliceEqual(groups, []string{"admin", "users"}) {
t.Errorf("Expected groups [admin users], got %v", groups)
}
if !stringSliceEqual(roles, []string{"editor", "viewer"}) {
t.Errorf("Expected roles [editor viewer], got %v", roles)
}
}
// TestCustomClaimNames_CustomSimpleNames tests custom simple claim names
func TestCustomClaimNames_CustomSimpleNames(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure custom simple claim names
ts.tOidc.roleClaimName = "user_roles"
ts.tOidc.groupClaimName = "user_groups"
// Create token with custom claim names
claims := map[string]interface{}{
"user_groups": []interface{}{"engineering", "product"},
"user_roles": []interface{}{"developer", "manager"},
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
groups, roles, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !stringSliceEqual(groups, []string{"engineering", "product"}) {
t.Errorf("Expected groups [engineering product], got %v", groups)
}
if !stringSliceEqual(roles, []string{"developer", "manager"}) {
t.Errorf("Expected roles [developer manager], got %v", roles)
}
}
// TestCustomClaimNames_MissingClaims tests behavior when custom claims are missing
func TestCustomClaimNames_MissingClaims(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure custom claim names
ts.tOidc.roleClaimName = "custom_roles"
ts.tOidc.groupClaimName = "custom_groups"
// Create token WITHOUT the custom claims
claims := map[string]interface{}{
"sub": "user123",
"email": "user@example.com",
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
groups, roles, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// Should return empty slices, not error
if len(groups) != 0 {
t.Errorf("Expected empty groups, got %v", groups)
}
if len(roles) != 0 {
t.Errorf("Expected empty roles, got %v", roles)
}
}
// TestCustomClaimNames_MalformedClaims tests error handling for malformed claims
func TestCustomClaimNames_MalformedRoleClaim(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure custom claim names
ts.tOidc.roleClaimName = "custom_roles"
// Create token with malformed role claim (not an array)
claims := map[string]interface{}{
"custom_roles": "this-should-be-an-array",
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
_, _, err = ts.tOidc.extractGroupsAndRoles(token)
if err == nil {
t.Error("Expected error for malformed role claim, got nil")
}
// Check error message contains the custom claim name
expectedError := "custom_roles claim is not an array"
if err.Error() != expectedError {
t.Errorf("Expected error '%s', got '%s'", expectedError, err.Error())
}
}
// TestCustomClaimNames_MalformedGroupClaim tests error handling for malformed group claims
func TestCustomClaimNames_MalformedGroupClaim(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure custom claim names
ts.tOidc.groupClaimName = "custom_groups"
// Create token with malformed group claim (not an array)
claims := map[string]interface{}{
"custom_groups": 12345, // Not an array
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
_, _, err = ts.tOidc.extractGroupsAndRoles(token)
if err == nil {
t.Error("Expected error for malformed group claim, got nil")
}
// Check error message contains the custom claim name
expectedError := "custom_groups claim is not an array"
if err.Error() != expectedError {
t.Errorf("Expected error '%s', got '%s'", expectedError, err.Error())
}
}
// TestCustomClaimNames_PartialConfiguration tests when only one claim name is customized
func TestCustomClaimNames_OnlyRoleCustomized(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure only role claim name (group uses default)
ts.tOidc.roleClaimName = "https://myapp.com/roles"
ts.tOidc.groupClaimName = "groups" // default
// Create token with mixed claim names
claims := map[string]interface{}{
"groups": []interface{}{"admin"},
"https://myapp.com/roles": []interface{}{"editor"},
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
groups, roles, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !stringSliceEqual(groups, []string{"admin"}) {
t.Errorf("Expected groups [admin], got %v", groups)
}
if !stringSliceEqual(roles, []string{"editor"}) {
t.Errorf("Expected roles [editor], got %v", roles)
}
}
// TestCustomClaimNames_OnlyGroupCustomized tests when only group claim name is customized
func TestCustomClaimNames_OnlyGroupCustomized(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure only group claim name (role uses default)
ts.tOidc.roleClaimName = "roles" // default
ts.tOidc.groupClaimName = "https://myapp.com/groups"
// Create token with mixed claim names
claims := map[string]interface{}{
"roles": []interface{}{"viewer"},
"https://myapp.com/groups": []interface{}{"users"},
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
groups, roles, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !stringSliceEqual(groups, []string{"users"}) {
t.Errorf("Expected groups [users], got %v", groups)
}
if !stringSliceEqual(roles, []string{"viewer"}) {
t.Errorf("Expected roles [viewer], got %v", roles)
}
}
// TestCustomClaimNames_EmptyArrays tests extraction with empty claim arrays
func TestCustomClaimNames_EmptyArrays(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure custom claim names
ts.tOidc.roleClaimName = "https://myapp.com/roles"
ts.tOidc.groupClaimName = "https://myapp.com/groups"
// Create token with empty arrays
claims := map[string]interface{}{
"https://myapp.com/groups": []interface{}{},
"https://myapp.com/roles": []interface{}{},
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
groups, roles, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if len(groups) != 0 {
t.Errorf("Expected empty groups, got %v", groups)
}
if len(roles) != 0 {
t.Errorf("Expected empty roles, got %v", roles)
}
}
// TestCustomClaimNames_NonStringElements tests handling of non-string elements in claim arrays
func TestCustomClaimNames_NonStringInRoleArray(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure custom claim names
ts.tOidc.roleClaimName = "custom_roles"
// Create token with mixed-type array (should skip non-string elements)
claims := map[string]interface{}{
"custom_roles": []interface{}{"role1", 12345, "role2", true},
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
_, roles, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// Should only extract string elements
if !stringSliceEqual(roles, []string{"role1", "role2"}) {
t.Errorf("Expected roles [role1 role2], got %v", roles)
}
}
// TestCustomClaimNames_NonStringInGroupArray tests handling of non-string elements in group arrays
func TestCustomClaimNames_NonStringInGroupArray(t *testing.T) {
ts := NewTestSuite(t)
ts.Setup()
// Configure custom claim names
ts.tOidc.groupClaimName = "custom_groups"
// Create token with mixed-type array (should skip non-string elements)
claims := map[string]interface{}{
"custom_groups": []interface{}{"group1", nil, "group2", 3.14},
}
token, err := createTestJWT(ts.rsaPrivateKey, "RS256", "test-key-id", claims)
if err != nil {
t.Fatalf("Failed to create test token: %v", err)
}
groups, _, err := ts.tOidc.extractGroupsAndRoles(token)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// Should only extract string elements
if !stringSliceEqual(groups, []string{"group1", "group2"}) {
t.Errorf("Expected groups [group1 group2], got %v", groups)
}
}