diff --git a/internal/outpost/proxyv2/application/auth.go b/internal/outpost/proxyv2/application/auth.go index a28f63e22f..2e1e821713 100644 --- a/internal/outpost/proxyv2/application/auth.go +++ b/internal/outpost/proxyv2/application/auth.go @@ -64,7 +64,7 @@ func (a *Application) getClaimsFromSession(r *http.Request) *types.Claims { // Claims are always stored as types.Claims but may be deserialized differently: // - Filesystem store (gob): preserves struct type as types.Claims - // - PostgreSQL store (JSON): deserializes as map[string]interface{} + // - PostgreSQL store (JSON): deserializes as map[string]any // Handle struct type (filesystem store) if c, ok := claims.(types.Claims); ok { @@ -72,7 +72,7 @@ func (a *Application) getClaimsFromSession(r *http.Request) *types.Claims { } // Handle map type (PostgreSQL store) - if claimsMap, ok := claims.(map[string]interface{}); ok { + if claimsMap, ok := claims.(map[string]any); ok { var c types.Claims if err := mapstructure.Decode(claimsMap, &c); err != nil { return nil diff --git a/internal/outpost/proxyv2/application/auth_test.go b/internal/outpost/proxyv2/application/auth_test.go index da5744963a..3e20b44942 100644 --- a/internal/outpost/proxyv2/application/auth_test.go +++ b/internal/outpost/proxyv2/application/auth_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/gorilla/sessions" + "github.com/mitchellh/mapstructure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -27,7 +28,7 @@ func TestClaimsJSONSerialization(t *testing.T) { Entitlements: []string{"read", "write"}, Sid: "session-id-456", Proxy: &types.ProxyClaims{ - UserAttributes: map[string]interface{}{ + UserAttributes: map[string]any{ "custom_field": "custom_value", "department": "engineering", }, @@ -70,35 +71,33 @@ func TestClaimsJSONSerialization(t *testing.T) { assert.Equal(t, "engineering", parsedClaims.Proxy.UserAttributes["department"]) } -// TestClaimsMapSerialization tests that Claims stored as map[string]interface{} can be converted back +// TestClaimsMapSerialization tests that Claims stored as map[string]any can be converted back func TestClaimsMapSerialization(t *testing.T) { // Simulate how claims are stored in session as map (like from PostgreSQL JSONB) - claimsMap := map[string]interface{}{ + claimsMap := map[string]any{ "sub": "user-id-123", "exp": float64(1234567890), // json numbers become float64 "email": "test@example.com", "email_verified": true, "name": "Test User", "preferred_username": "testuser", - "groups": []interface{}{"admin", "user"}, - "entitlements": []interface{}{"read", "write"}, + "groups": []any{"admin", "user"}, + "entitlements": []any{"read", "write"}, "sid": "session-id-456", - "ak_proxy": map[string]interface{}{ - "user_attributes": map[string]interface{}{ + "ak_proxy": map[string]any{ + "user_attributes": map[string]any{ "custom_field": "custom_value", }, "backend_override": "custom-backend", "host_header": "example.com", "is_superuser": true, }, + "raw_token": "not-a-real-token", } - // Convert map to Claims using JSON marshaling (like getClaimsFromSession does) - jsonData, err := json.Marshal(claimsMap) - require.NoError(t, err) - + // Convert map to Claims using mapstructure marshaling (like getClaimsFromSession does) var claims types.Claims - err = json.Unmarshal(jsonData, &claims) + err := mapstructure.Decode(claimsMap, &claims) require.NoError(t, err) // Verify fields @@ -111,6 +110,7 @@ func TestClaimsMapSerialization(t *testing.T) { assert.Equal(t, []string{"admin", "user"}, claims.Groups) assert.Equal(t, []string{"read", "write"}, claims.Entitlements) assert.Equal(t, "session-id-456", claims.Sid) + assert.Equal(t, "not-a-real-token", claims.RawToken) // Verify proxy claims require.NotNil(t, claims.Proxy) @@ -122,7 +122,7 @@ func TestClaimsMapSerialization(t *testing.T) { // TestClaimsMinimalFields tests that Claims work with minimal required fields func TestClaimsMinimalFields(t *testing.T) { - claimsMap := map[string]interface{}{ + claimsMap := map[string]any{ "sub": "user-id-123", "exp": float64(1234567890), } @@ -144,11 +144,11 @@ func TestClaimsMinimalFields(t *testing.T) { // TestClaimsWithEmptyArrays tests that empty arrays are handled correctly func TestClaimsWithEmptyArrays(t *testing.T) { - claimsMap := map[string]interface{}{ + claimsMap := map[string]any{ "sub": "user-id-123", "exp": float64(1234567890), - "groups": []interface{}{}, - "entitlements": []interface{}{}, + "groups": []any{}, + "entitlements": []any{}, } jsonData, err := json.Marshal(claimsMap) @@ -167,7 +167,7 @@ func TestClaimsWithEmptyArrays(t *testing.T) { // TestClaimsWithNullProxyClaims tests that null proxy claims don't cause issues func TestClaimsWithNullProxyClaims(t *testing.T) { - claimsMap := map[string]interface{}{ + claimsMap := map[string]any{ "sub": "user-id-123", "exp": float64(1234567890), "ak_proxy": nil, @@ -185,18 +185,18 @@ func TestClaimsWithNullProxyClaims(t *testing.T) { } // TestGetClaimsFromSession_Success tests successful retrieval of claims from session -// uses a mock session that returns claims as map[string]interface{} to simulate +// uses a mock session that returns claims as map[string]any to simulate // how PostgreSQL storage deserializes JSONB data func TestGetClaimsFromSession_Success(t *testing.T) { // Create a custom mock store that returns claims as map store := &mockMapSessionStore{ - claimsMap: map[string]interface{}{ + claimsMap: map[string]any{ "sub": "user-id-123", "exp": float64(1234567890), "email": "test@example.com", "email_verified": true, "preferred_username": "testuser", - "groups": []interface{}{"admin", "user"}, + "groups": []any{"admin", "user"}, }, } @@ -217,9 +217,9 @@ func TestGetClaimsFromSession_Success(t *testing.T) { assert.Equal(t, []string{"admin", "user"}, claims.Groups) } -// mockMapSessionStore is a mock session store that returns claims as map[string]interface{} +// mockMapSessionStore is a mock session store that returns claims as map[string]any type mockMapSessionStore struct { - claimsMap map[string]interface{} + claimsMap map[string]any } func (m *mockMapSessionStore) Get(r *http.Request, name string) (*sessions.Session, error) { @@ -314,7 +314,7 @@ func TestClaimsRoundTrip(t *testing.T) { Entitlements: []string{"ent1", "ent2"}, Sid: "session-789", Proxy: &types.ProxyClaims{ - UserAttributes: map[string]interface{}{ + UserAttributes: map[string]any{ "attr1": "value1", "attr2": float64(42), "attr3": true, @@ -329,8 +329,8 @@ func TestClaimsRoundTrip(t *testing.T) { jsonData, err := json.Marshal(originalClaims) require.NoError(t, err) - // Step 2: Deserialize to map[string]interface{} (simulating PostgreSQL load) - var claimsMap map[string]interface{} + // Step 2: Deserialize to map[string]any (simulating PostgreSQL load) + var claimsMap map[string]any err = json.Unmarshal(jsonData, &claimsMap) require.NoError(t, err) diff --git a/internal/outpost/proxyv2/types/claims.go b/internal/outpost/proxyv2/types/claims.go index 0a497d4180..c58ee139c5 100644 --- a/internal/outpost/proxyv2/types/claims.go +++ b/internal/outpost/proxyv2/types/claims.go @@ -1,10 +1,10 @@ package types type ProxyClaims struct { - UserAttributes map[string]interface{} `json:"user_attributes"` - BackendOverride string `json:"backend_override"` - HostHeader string `json:"host_header"` - IsSuperuser bool `json:"is_superuser"` + UserAttributes map[string]any `json:"user_attributes" mapstructure:"user_attributes"` + BackendOverride string `json:"backend_override" mapstructure:"backend_override"` + HostHeader string `json:"host_header" mapstructure:"host_header"` + IsSuperuser bool `json:"is_superuser" mapstructure:"is_superuser"` } type Claims struct { @@ -19,5 +19,5 @@ type Claims struct { Sid string `json:"sid" mapstructure:"sid"` Proxy *ProxyClaims `json:"ak_proxy" mapstructure:"ak_proxy"` - RawToken string `mapstructure:"-"` + RawToken string `json:"raw_token" mapstructure:"raw_token"` }