This commit is contained in:
2025-09-26 23:37:11 +01:00
parent 60841eb121
commit 2f9f3fda42
11 changed files with 1203 additions and 1205 deletions
+125
View File
@@ -0,0 +1,125 @@
// Package utils provides common utility functions used across the OIDC middleware
package utils
import (
"os"
"runtime"
"strings"
)
// CreateStringMap creates a map with string keys for efficient lookups
func CreateStringMap(items []string) map[string]struct{} {
result := make(map[string]struct{})
for _, item := range items {
result[item] = struct{}{}
}
return result
}
// CreateCaseInsensitiveStringMap creates a map with lowercase keys for case-insensitive matching
func CreateCaseInsensitiveStringMap(items []string) map[string]struct{} {
result := make(map[string]struct{})
for _, item := range items {
result[strings.ToLower(item)] = struct{}{}
}
return result
}
// DeduplicateScopes removes duplicate scopes from a slice
func DeduplicateScopes(scopes []string) []string {
seen := make(map[string]bool)
result := []string{}
for _, scope := range scopes {
if !seen[scope] {
seen[scope] = true
result = append(result, scope)
}
}
return result
}
// MergeScopes combines default scopes with user-provided scopes, removing duplicates
func MergeScopes(defaultScopes, userScopes []string) []string {
if len(userScopes) == 0 {
return append([]string(nil), defaultScopes...)
}
seen := make(map[string]bool)
var result []string
for _, scope := range defaultScopes {
if !seen[scope] {
seen[scope] = true
result = append(result, scope)
}
}
for _, scope := range userScopes {
if !seen[scope] {
seen[scope] = true
result = append(result, scope)
}
}
return result
}
// IsTestMode detects if the code is running in a test environment
func IsTestMode() bool {
if os.Getenv("SUPPRESS_DIAGNOSTIC_LOGS") == "1" {
return true
}
if strings.Contains(os.Args[0], ".test") ||
strings.Contains(os.Args[0], "go_build_") ||
os.Getenv("GO_TEST") == "1" ||
runtime.Compiler == "yaegi" {
return true
}
for _, arg := range os.Args {
if strings.Contains(arg, "-test") {
return true
}
}
if runtime.Compiler == "gc" {
progName := os.Args[0]
if strings.Contains(progName, "test") ||
strings.HasSuffix(progName, ".test") ||
strings.Contains(progName, "__debug_bin") {
return true
}
}
// Only use runtime stack check as fallback when no explicit test conditions are being controlled
if os.Getenv("DISABLE_RUNTIME_STACK_CHECK") != "1" &&
os.Getenv("SUPPRESS_DIAGNOSTIC_LOGS") == "" &&
os.Getenv("GO_TEST") == "" {
// Check runtime stack for test functions only as last resort
buf := make([]byte, 2048)
n := runtime.Stack(buf, false)
stack := string(buf[:n])
if strings.Contains(stack, "testing.tRunner") ||
strings.Contains(stack, "testing.(*T)") ||
strings.Contains(stack, ".test.") {
return true
}
}
return false
}
// KeysFromMap extracts string keys from a map for logging purposes
func KeysFromMap(m map[string]struct{}) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
// BuildFullURL constructs a URL from scheme, host, and path components
func BuildFullURL(scheme, host, path string) string {
return scheme + "://" + host + path
}
+130
View File
@@ -0,0 +1,130 @@
package utils
import (
"reflect"
"testing"
)
func TestCreateStringMap(t *testing.T) {
items := []string{"apple", "banana", "cherry"}
result := CreateStringMap(items)
expected := map[string]struct{}{
"apple": {},
"banana": {},
"cherry": {},
}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestCreateCaseInsensitiveStringMap(t *testing.T) {
items := []string{"Apple", "BANANA", "Cherry"}
result := CreateCaseInsensitiveStringMap(items)
expected := map[string]struct{}{
"apple": {},
"banana": {},
"cherry": {},
}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestDeduplicateScopes(t *testing.T) {
scopes := []string{"openid", "profile", "email", "openid", "profile"}
result := DeduplicateScopes(scopes)
expected := []string{"openid", "profile", "email"}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestMergeScopes(t *testing.T) {
defaultScopes := []string{"openid", "profile"}
userScopes := []string{"email", "offline_access"}
result := MergeScopes(defaultScopes, userScopes)
expected := []string{"openid", "profile", "email", "offline_access"}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestMergeScopesWithDuplicates(t *testing.T) {
defaultScopes := []string{"openid", "profile"}
userScopes := []string{"profile", "email", "openid"}
result := MergeScopes(defaultScopes, userScopes)
expected := []string{"openid", "profile", "email"}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestMergeScopesEmptyUserScopes(t *testing.T) {
defaultScopes := []string{"openid", "profile"}
userScopes := []string{}
result := MergeScopes(defaultScopes, userScopes)
expected := []string{"openid", "profile"}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestKeysFromMap(t *testing.T) {
m := map[string]struct{}{
"key1": {},
"key2": {},
"key3": {},
}
result := KeysFromMap(m)
// Since map iteration order is not guaranteed, we need to check length and presence
if len(result) != 3 {
t.Errorf("Expected 3 keys, got %d", len(result))
}
resultMap := make(map[string]bool)
for _, key := range result {
resultMap[key] = true
}
expectedKeys := []string{"key1", "key2", "key3"}
for _, key := range expectedKeys {
if !resultMap[key] {
t.Errorf("Expected key %s not found in result", key)
}
}
}
func TestBuildFullURL(t *testing.T) {
tests := []struct {
scheme string
host string
path string
expected string
}{
{"https", "example.com", "/path", "https://example.com/path"},
{"http", "localhost:8080", "/callback", "http://localhost:8080/callback"},
{"https", "test.example.com", "/auth/callback", "https://test.example.com/auth/callback"},
}
for _, test := range tests {
result := BuildFullURL(test.scheme, test.host, test.path)
if result != test.expected {
t.Errorf("For scheme=%s, host=%s, path=%s: expected %s, got %s",
test.scheme, test.host, test.path, test.expected, result)
}
}
}