mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-02-07 05:21:24 +00:00
The unused-parameter linter became stricter, so we adjust it to allow unused params that start with underscore. It can be nice to keep unused param names when implementing an interface sometimes, to help readers understand why it is unused in that particular implementation.
341 lines
10 KiB
Go
341 lines
10 KiB
Go
// Copyright 2023 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package idtransform
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type fakeNoopTransformer struct{}
|
|
|
|
func (a fakeNoopTransformer) Evaluate(_ctx context.Context, username string, groups []string) (*TransformationResult, error) {
|
|
return &TransformationResult{
|
|
Username: username,
|
|
Groups: groups,
|
|
AuthenticationAllowed: true,
|
|
RejectedAuthenticationMessage: "none",
|
|
}, nil
|
|
}
|
|
|
|
func (a fakeNoopTransformer) Source() interface{} {
|
|
return nil // not needed for this test
|
|
}
|
|
|
|
type fakeNilGroupTransformer struct{}
|
|
|
|
func (a fakeNilGroupTransformer) Evaluate(_ctx context.Context, username string, _groups []string) (*TransformationResult, error) {
|
|
return &TransformationResult{
|
|
Username: username,
|
|
Groups: nil,
|
|
AuthenticationAllowed: true,
|
|
RejectedAuthenticationMessage: "none",
|
|
}, nil
|
|
}
|
|
|
|
func (a fakeNilGroupTransformer) Source() interface{} {
|
|
return nil // not needed for this test
|
|
}
|
|
|
|
type fakeAppendStringTransformer struct{}
|
|
|
|
func (a fakeAppendStringTransformer) Evaluate(_ctx context.Context, username string, groups []string) (*TransformationResult, error) {
|
|
newGroups := []string{}
|
|
for _, group := range groups {
|
|
newGroups = append(newGroups, group+":transformed")
|
|
}
|
|
return &TransformationResult{
|
|
Username: username + ":transformed",
|
|
Groups: newGroups,
|
|
AuthenticationAllowed: true,
|
|
RejectedAuthenticationMessage: "none",
|
|
}, nil
|
|
}
|
|
|
|
func (a fakeAppendStringTransformer) Source() interface{} {
|
|
return nil // not needed for this test
|
|
}
|
|
|
|
type fakeDeleteUsernameAndGroupsTransformer struct{}
|
|
|
|
func (a fakeDeleteUsernameAndGroupsTransformer) Evaluate(_ctx context.Context, _username string, _groups []string) (*TransformationResult, error) {
|
|
return &TransformationResult{
|
|
Username: "",
|
|
Groups: []string{},
|
|
AuthenticationAllowed: true,
|
|
RejectedAuthenticationMessage: "none",
|
|
}, nil
|
|
}
|
|
|
|
func (a fakeDeleteUsernameAndGroupsTransformer) Source() interface{} {
|
|
return nil // not needed for this test
|
|
}
|
|
|
|
type fakeAuthenticationDisallowedTransformer struct{}
|
|
|
|
func (a fakeAuthenticationDisallowedTransformer) Evaluate(_ctx context.Context, username string, groups []string) (*TransformationResult, error) {
|
|
newGroups := []string{}
|
|
for _, group := range groups {
|
|
newGroups = append(newGroups, group+":disallowed")
|
|
}
|
|
return &TransformationResult{
|
|
Username: username + ":disallowed",
|
|
Groups: newGroups,
|
|
AuthenticationAllowed: false,
|
|
RejectedAuthenticationMessage: "no authentication is allowed",
|
|
}, nil
|
|
}
|
|
|
|
func (a fakeAuthenticationDisallowedTransformer) Source() interface{} {
|
|
return nil // not needed for this test
|
|
}
|
|
|
|
type fakeErrorTransformer struct{}
|
|
|
|
func (a fakeErrorTransformer) Evaluate(_ctx context.Context, _username string, _groups []string) (*TransformationResult, error) {
|
|
return &TransformationResult{}, errors.New("unexpected catastrophic error")
|
|
}
|
|
|
|
func (a fakeErrorTransformer) Source() interface{} {
|
|
return nil // not needed for this test
|
|
}
|
|
|
|
type fakeTransformerWithSource struct {
|
|
source string
|
|
}
|
|
|
|
func (a fakeTransformerWithSource) Evaluate(_ctx context.Context, _username string, _groups []string) (*TransformationResult, error) {
|
|
return nil, nil // not needed for this test
|
|
}
|
|
|
|
func (a fakeTransformerWithSource) Source() interface{} {
|
|
return a.source
|
|
}
|
|
|
|
func TestTransformationPipelineEvaluation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
username string
|
|
groups []string
|
|
transforms []IdentityTransformation
|
|
wantUsername string
|
|
wantGroups []string
|
|
wantAuthenticationAllowed bool
|
|
wantRejectionAuthenticationMessage string
|
|
wantError string
|
|
}{
|
|
{
|
|
name: "single transformation applied successfully",
|
|
transforms: []IdentityTransformation{
|
|
fakeAppendStringTransformer{},
|
|
},
|
|
username: "foo",
|
|
groups: []string{
|
|
"foobar",
|
|
"foobaz",
|
|
},
|
|
wantUsername: "foo:transformed",
|
|
wantGroups: []string{
|
|
"foobar:transformed",
|
|
"foobaz:transformed",
|
|
},
|
|
wantAuthenticationAllowed: true,
|
|
wantRejectionAuthenticationMessage: "none",
|
|
},
|
|
{
|
|
name: "group results are sorted and made unique",
|
|
transforms: []IdentityTransformation{
|
|
fakeAppendStringTransformer{},
|
|
},
|
|
username: "foo",
|
|
groups: []string{
|
|
"b",
|
|
"a",
|
|
"b",
|
|
"a",
|
|
"c",
|
|
"b",
|
|
},
|
|
wantUsername: "foo:transformed",
|
|
wantGroups: []string{
|
|
"a:transformed",
|
|
"b:transformed",
|
|
"c:transformed",
|
|
},
|
|
wantAuthenticationAllowed: true,
|
|
wantRejectionAuthenticationMessage: "none",
|
|
},
|
|
{
|
|
name: "multiple transformations applied successfully",
|
|
username: "foo",
|
|
groups: []string{
|
|
"foobar",
|
|
"foobaz",
|
|
},
|
|
transforms: []IdentityTransformation{
|
|
fakeAppendStringTransformer{},
|
|
fakeAppendStringTransformer{},
|
|
},
|
|
wantUsername: "foo:transformed:transformed",
|
|
wantGroups: []string{
|
|
"foobar:transformed:transformed",
|
|
"foobaz:transformed:transformed",
|
|
},
|
|
wantAuthenticationAllowed: true,
|
|
wantRejectionAuthenticationMessage: "none",
|
|
},
|
|
{
|
|
name: "single transformation results in AuthenticationAllowed:false",
|
|
username: "foo",
|
|
groups: []string{
|
|
"foobar",
|
|
},
|
|
transforms: []IdentityTransformation{
|
|
fakeAuthenticationDisallowedTransformer{},
|
|
},
|
|
wantUsername: "foo:disallowed",
|
|
wantGroups: []string{"foobar:disallowed"},
|
|
wantAuthenticationAllowed: false,
|
|
wantRejectionAuthenticationMessage: "no authentication is allowed",
|
|
},
|
|
{
|
|
name: "multiple transformations results in AuthenticationAllowed:false but earlier transforms are successful",
|
|
username: "foo",
|
|
groups: []string{
|
|
"foobar",
|
|
},
|
|
transforms: []IdentityTransformation{
|
|
fakeAppendStringTransformer{},
|
|
fakeAuthenticationDisallowedTransformer{},
|
|
// this transformation will not be run because the previous exits the pipeline
|
|
fakeAppendStringTransformer{},
|
|
},
|
|
wantUsername: "foo:transformed:disallowed",
|
|
wantGroups: []string{"foobar:transformed:disallowed"},
|
|
wantAuthenticationAllowed: false,
|
|
wantRejectionAuthenticationMessage: "no authentication is allowed",
|
|
},
|
|
{
|
|
name: "unexpected error at index",
|
|
username: "foo",
|
|
groups: []string{
|
|
"foobar",
|
|
},
|
|
transforms: []IdentityTransformation{
|
|
fakeAppendStringTransformer{},
|
|
fakeErrorTransformer{},
|
|
fakeAppendStringTransformer{},
|
|
},
|
|
wantError: "identity transformation at index 1: unexpected catastrophic error",
|
|
},
|
|
{
|
|
name: "empty username not allowed",
|
|
username: "foo",
|
|
transforms: []IdentityTransformation{
|
|
fakeDeleteUsernameAndGroupsTransformer{},
|
|
},
|
|
wantError: "identity transformation returned an empty username, which is not allowed",
|
|
},
|
|
{
|
|
name: "whitespace username not allowed",
|
|
username: " \t\n\r ",
|
|
transforms: []IdentityTransformation{
|
|
fakeNoopTransformer{},
|
|
},
|
|
wantError: "identity transformation returned an empty username, which is not allowed",
|
|
},
|
|
{
|
|
name: "identity transformation which returns an empty list of groups is allowed",
|
|
username: "foo",
|
|
groups: []string{},
|
|
transforms: []IdentityTransformation{
|
|
fakeAppendStringTransformer{},
|
|
},
|
|
wantUsername: "foo:transformed",
|
|
wantGroups: []string{},
|
|
wantAuthenticationAllowed: true,
|
|
wantRejectionAuthenticationMessage: "none",
|
|
},
|
|
{
|
|
name: "nil passed in for groups will be automatically converted to an empty list",
|
|
username: "foo",
|
|
groups: nil,
|
|
transforms: []IdentityTransformation{
|
|
fakeNoopTransformer{},
|
|
},
|
|
wantUsername: "foo",
|
|
wantGroups: []string{},
|
|
wantAuthenticationAllowed: true,
|
|
wantRejectionAuthenticationMessage: "none",
|
|
},
|
|
{
|
|
name: "any transformation returning nil for the list of groups will cause an error",
|
|
username: "foo",
|
|
groups: []string{
|
|
"these.will.be.converted.to.nil",
|
|
},
|
|
transforms: []IdentityTransformation{
|
|
fakeNilGroupTransformer{},
|
|
},
|
|
wantError: "identity transformation returned a null list of groups, which is not allowed",
|
|
},
|
|
{
|
|
name: "no transformations is allowed",
|
|
username: "foo",
|
|
groups: []string{"bar", "baz"},
|
|
transforms: []IdentityTransformation{},
|
|
wantUsername: "foo",
|
|
wantGroups: []string{"bar", "baz"},
|
|
wantAuthenticationAllowed: true,
|
|
// since no transformations run, this will be empty string
|
|
wantRejectionAuthenticationMessage: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
pipeline := NewTransformationPipeline()
|
|
|
|
for _, transform := range tt.transforms {
|
|
pipeline.AppendTransformation(transform)
|
|
}
|
|
|
|
result, err := pipeline.Evaluate(context.Background(), tt.username, tt.groups)
|
|
|
|
if tt.wantError != "" {
|
|
require.EqualError(t, err, tt.wantError)
|
|
require.Nil(t, result)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err, "got an unexpected evaluation error")
|
|
require.Equal(t, tt.wantUsername, result.Username)
|
|
require.Equal(t, tt.wantGroups, result.Groups)
|
|
require.Equal(t, tt.wantAuthenticationAllowed, result.AuthenticationAllowed)
|
|
require.Equal(t, tt.wantRejectionAuthenticationMessage, result.RejectedAuthenticationMessage)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTransformationSource(t *testing.T) {
|
|
pipeline := NewTransformationPipeline()
|
|
|
|
for _, transform := range []IdentityTransformation{
|
|
&fakeTransformerWithSource{source: "foo"},
|
|
&fakeTransformerWithSource{source: "bar"},
|
|
&fakeTransformerWithSource{source: "baz"},
|
|
} {
|
|
pipeline.AppendTransformation(transform)
|
|
}
|
|
|
|
require.Equal(t, []interface{}{"foo", "bar", "baz"}, pipeline.Source())
|
|
require.NotEqual(t, []interface{}{"foo", "something-else", "baz"}, pipeline.Source())
|
|
}
|