mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-06-05 22:44:17 +00:00
e64fc7f730
* 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>
365 lines
10 KiB
Go
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)
|
|
}
|
|
}
|