Files
pinniped/internal/idtransform/identity_transformations_test.go
Ryan Richard 29e939db7f Upgrade the linter to golangci-lint@v1.55.1
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.
2023-11-02 09:54:16 -07:00

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())
}