mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-01-03 11:45:45 +00:00
refactor to move audit event message types to their own pkg
This commit is contained in:
committed by
Joshua Casey
parent
088556193d
commit
8cf9c59957
@@ -1,68 +0,0 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package plog
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
type AuditEventMessage string
|
||||
|
||||
const (
|
||||
AuditEventHTTPRequestReceived AuditEventMessage = "HTTP Request Received"
|
||||
AuditEventHTTPRequestCompleted AuditEventMessage = "HTTP Request Completed"
|
||||
AuditEventHTTPRequestParameters AuditEventMessage = "HTTP Request Parameters"
|
||||
AuditEventHTTPRequestCustomHeadersUsed AuditEventMessage = "HTTP Request Custom Headers Used"
|
||||
AuditEventUsingUpstreamIDP AuditEventMessage = "Using Upstream IDP"
|
||||
AuditEventAuthorizeIDFromParameters AuditEventMessage = "AuthorizeID From Parameters"
|
||||
AuditEventIdentityFromUpstreamIDP AuditEventMessage = "Identity From Upstream IDP"
|
||||
AuditEventIdentityRefreshedFromUpstreamIDP AuditEventMessage = "Identity Refreshed From Upstream IDP"
|
||||
AuditEventSessionStarted AuditEventMessage = "Session Started"
|
||||
AuditEventSessionRefreshed AuditEventMessage = "Session Refreshed"
|
||||
AuditEventAuthenticationRejectedByTransforms AuditEventMessage = "Authentication Rejected By Transforms"
|
||||
AuditEventUpstreamOIDCTokenRevoked AuditEventMessage = "Upstream OIDC Token Revoked" //nolint:gosec // this is not a credential
|
||||
AuditEventSessionGarbageCollected AuditEventMessage = "Session Garbage Collected"
|
||||
AuditEventTokenCredentialRequest AuditEventMessage = "TokenCredentialRequest" //nolint:gosec // this is not a credential
|
||||
AuditEventUpstreamAuthorizeRedirect AuditEventMessage = "Upstream Authorize Redirect"
|
||||
)
|
||||
|
||||
// SanitizeParams can be used to redact all params not included in the allowedKeys set.
|
||||
// Useful when audit logging AuditEventHTTPRequestParameters events.
|
||||
func SanitizeParams(inputParams url.Values, allowedKeys sets.Set[string]) []any {
|
||||
params := make(map[string]string)
|
||||
multiValueParams := make(url.Values)
|
||||
|
||||
transform := func(key, value string) string {
|
||||
if !allowedKeys.Has(key) {
|
||||
return "redacted"
|
||||
}
|
||||
|
||||
unescape, err := url.QueryUnescape(value)
|
||||
if err != nil {
|
||||
// ignore these errors and just use the original query parameter
|
||||
unescape = value
|
||||
}
|
||||
return unescape
|
||||
}
|
||||
|
||||
for key := range inputParams {
|
||||
for i, p := range inputParams[key] {
|
||||
transformed := transform(key, p)
|
||||
if i == 0 {
|
||||
params[key] = transformed
|
||||
}
|
||||
|
||||
if len(inputParams[key]) > 1 {
|
||||
multiValueParams[key] = append(multiValueParams[key], transformed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(multiValueParams) > 0 {
|
||||
return []any{"params", params, "multiValueParams", multiValueParams}
|
||||
}
|
||||
return []any{"params", params}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package plog
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
func TestSanitizeParams(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params url.Values
|
||||
allowedKeys sets.Set[string]
|
||||
want []any
|
||||
}{
|
||||
{
|
||||
name: "nil values",
|
||||
params: nil,
|
||||
allowedKeys: nil,
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty values",
|
||||
params: url.Values{},
|
||||
allowedKeys: nil,
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all allowed values",
|
||||
params: url.Values{"foo": []string{"a", "b", "c"}, "bar": []string{"d", "e", "f"}},
|
||||
allowedKeys: sets.New("foo", "bar"),
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{
|
||||
"bar": "d",
|
||||
"foo": "a",
|
||||
},
|
||||
"multiValueParams",
|
||||
url.Values{
|
||||
"bar": []string{"d", "e", "f"},
|
||||
"foo": []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all allowed values with single values",
|
||||
params: url.Values{"foo": []string{"a"}, "bar": []string{"d"}},
|
||||
allowedKeys: sets.New("foo", "bar"),
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{
|
||||
"foo": "a",
|
||||
"bar": "d",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some allowed values",
|
||||
params: url.Values{"foo": []string{"a", "b", "c"}, "bar": []string{"d", "e", "f"}},
|
||||
allowedKeys: sets.New("foo"),
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{
|
||||
"bar": "redacted",
|
||||
"foo": "a",
|
||||
},
|
||||
"multiValueParams",
|
||||
url.Values{
|
||||
"bar": []string{"redacted", "redacted", "redacted"},
|
||||
"foo": []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some allowed values with single values",
|
||||
params: url.Values{"foo": []string{"a"}, "bar": []string{"d"}},
|
||||
allowedKeys: sets.New("foo"),
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{
|
||||
"bar": "redacted",
|
||||
"foo": "a",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no allowed values",
|
||||
params: url.Values{"foo": []string{"a", "b", "c"}, "bar": []string{"d", "e", "f"}},
|
||||
allowedKeys: sets.New[string](),
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{
|
||||
"bar": "redacted",
|
||||
"foo": "redacted",
|
||||
},
|
||||
"multiValueParams",
|
||||
url.Values{
|
||||
"bar": {"redacted", "redacted", "redacted"},
|
||||
"foo": {"redacted", "redacted", "redacted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil allowed values",
|
||||
params: url.Values{"foo": []string{"a", "b", "c"}, "bar": []string{"d", "e", "f"}},
|
||||
allowedKeys: nil,
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{
|
||||
"bar": "redacted",
|
||||
"foo": "redacted",
|
||||
},
|
||||
"multiValueParams",
|
||||
url.Values{
|
||||
"bar": {"redacted", "redacted", "redacted"},
|
||||
"foo": {"redacted", "redacted", "redacted"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "url decodes allowed values",
|
||||
params: url.Values{
|
||||
"foo": []string{"a%3Ab", "c", "urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange"},
|
||||
"bar": []string{"d", "e", "f"},
|
||||
},
|
||||
allowedKeys: sets.New("foo"),
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{
|
||||
"bar": "redacted",
|
||||
"foo": "a:b",
|
||||
},
|
||||
"multiValueParams",
|
||||
url.Values{
|
||||
"bar": {"redacted", "redacted", "redacted"},
|
||||
"foo": {"a:b", "c", "urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignores url decode errors",
|
||||
params: url.Values{
|
||||
"bad_encoding": []string{"%.."},
|
||||
},
|
||||
allowedKeys: sets.New("bad_encoding"),
|
||||
want: []any{
|
||||
"params",
|
||||
map[string]string{
|
||||
"bad_encoding": "%..",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// This comparison should require the exact order
|
||||
require.Equal(t, test.want, SanitizeParams(test.params, test.allowedKeys))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"slices"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"go.pinniped.dev/internal/auditevent"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
)
|
||||
|
||||
@@ -61,7 +62,7 @@ type AuditLogger interface {
|
||||
// reqCtx and session may be null.
|
||||
// When possible, pass the http request's context as reqCtx, so we may read the audit ID from the context.
|
||||
// When possible, pass the fosite.Requester or fosite.Request as the session, so we can log the session ID.
|
||||
Audit(msg AuditEventMessage, reqCtx context.Context, session SessionIDGetter, keysAndValues ...any)
|
||||
Audit(msg auditevent.Message, reqCtx context.Context, session SessionIDGetter, keysAndValues ...any)
|
||||
}
|
||||
|
||||
// Logger implements the plog logging convention described above. The global functions in this package
|
||||
@@ -126,7 +127,7 @@ func (p pLogger) Error(msg string, err error, keysAndValues ...any) {
|
||||
// by their own separate configuration. This is because Audit logs should always be printed when they are desired
|
||||
// by the admin, regardless of global log level, yet the admin should also have a way to entirely disable them
|
||||
// when they want to avoid potential PII (e.g. usernames) in their pod logs.
|
||||
func (p pLogger) Audit(msg AuditEventMessage, reqCtx context.Context, session SessionIDGetter, keysAndValues ...any) {
|
||||
func (p pLogger) Audit(msg auditevent.Message, reqCtx context.Context, session SessionIDGetter, keysAndValues ...any) {
|
||||
// Always add a key/value auditEvent=true.
|
||||
keysAndValues = slices.Concat([]any{"auditEvent", true}, keysAndValues)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user