abc941097c
This change adds a new virtual aggregated API that can be used by any user to echo back who they are currently authenticated as. This has general utility to end users and can be used in tests to validate if authentication was successful. Signed-off-by: Monis Khan <mok@vmware.com>
212 lines
6.1 KiB
Go
212 lines
6.1 KiB
Go
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package whoamirequest
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
|
"k8s.io/apiserver/pkg/authentication/user"
|
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
|
"k8s.io/apiserver/pkg/registry/rest"
|
|
|
|
identityapi "go.pinniped.dev/generated/latest/apis/concierge/identity"
|
|
)
|
|
|
|
func TestNew(t *testing.T) {
|
|
r := NewREST(schema.GroupResource{Group: "bears", Resource: "panda"})
|
|
require.NotNil(t, r)
|
|
require.False(t, r.NamespaceScoped())
|
|
require.Equal(t, []string{"pinniped"}, r.Categories())
|
|
require.IsType(t, &identityapi.WhoAmIRequest{}, r.New())
|
|
require.IsType(t, &identityapi.WhoAmIRequestList{}, r.NewList())
|
|
|
|
ctx := context.Background()
|
|
|
|
// check the simple invariants of our no-op list
|
|
list, err := r.List(ctx, nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, list)
|
|
require.IsType(t, &identityapi.WhoAmIRequestList{}, list)
|
|
require.Equal(t, "0", list.(*identityapi.WhoAmIRequestList).ResourceVersion)
|
|
require.NotNil(t, list.(*identityapi.WhoAmIRequestList).Items)
|
|
require.Len(t, list.(*identityapi.WhoAmIRequestList).Items, 0)
|
|
|
|
// make sure we can turn lists into tables if needed
|
|
table, err := r.ConvertToTable(ctx, list, nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, table)
|
|
require.Equal(t, "0", table.ResourceVersion)
|
|
require.Nil(t, table.Rows)
|
|
|
|
// exercise group resource - force error by passing a runtime.Object that does not have an embedded object meta
|
|
_, err = r.ConvertToTable(ctx, &metav1.APIGroup{}, nil)
|
|
require.Error(t, err, "the resource panda.bears does not support being converted to a Table")
|
|
}
|
|
|
|
func TestCreate(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
obj runtime.Object
|
|
createValidation rest.ValidateObjectFunc
|
|
options *metav1.CreateOptions
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want runtime.Object
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "wrong type",
|
|
args: args{
|
|
ctx: genericapirequest.NewContext(),
|
|
obj: &metav1.Status{},
|
|
createValidation: nil,
|
|
options: nil,
|
|
},
|
|
want: nil,
|
|
wantErr: `not a WhoAmIRequest: &v1.Status{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, ListMeta:v1.ListMeta{SelfLink:"", ResourceVersion:"", Continue:"", RemainingItemCount:(*int64)(nil)}, Status:"", Message:"", Reason:"", Details:(*v1.StatusDetails)(nil), Code:0}`,
|
|
},
|
|
{
|
|
name: "bad options",
|
|
args: args{
|
|
ctx: genericapirequest.NewContext(),
|
|
obj: &identityapi.WhoAmIRequest{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "SomeKind",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "some-name",
|
|
},
|
|
},
|
|
createValidation: nil,
|
|
options: &metav1.CreateOptions{DryRun: []string{"stuff"}},
|
|
},
|
|
want: nil,
|
|
wantErr: `SomeKind.identity.concierge.pinniped.dev "some-name" is invalid: dryRun: Unsupported value: []string{"stuff"}`,
|
|
},
|
|
{
|
|
name: "bad namespace",
|
|
args: args{
|
|
ctx: genericapirequest.WithNamespace(genericapirequest.NewContext(), "some-ns"),
|
|
obj: &identityapi.WhoAmIRequest{},
|
|
createValidation: nil,
|
|
options: nil,
|
|
},
|
|
want: nil,
|
|
wantErr: `namespace is not allowed on WhoAmIRequest: some-ns`,
|
|
},
|
|
{
|
|
// if we add fields to spec, we need additional tests to:
|
|
// - make sure admission cannot mutate it
|
|
// - the input spec fields are validated correctly
|
|
name: "create validation failure",
|
|
args: args{
|
|
ctx: genericapirequest.NewContext(),
|
|
obj: &identityapi.WhoAmIRequest{},
|
|
createValidation: func(ctx context.Context, obj runtime.Object) error {
|
|
return errors.New("some-error-here")
|
|
},
|
|
options: nil,
|
|
},
|
|
want: nil,
|
|
wantErr: `some-error-here`,
|
|
},
|
|
{
|
|
name: "no user info",
|
|
args: args{
|
|
ctx: genericapirequest.NewContext(),
|
|
obj: &identityapi.WhoAmIRequest{},
|
|
createValidation: nil,
|
|
options: nil,
|
|
},
|
|
want: nil,
|
|
wantErr: `Internal error occurred: no user info on request`,
|
|
},
|
|
{
|
|
name: "with user info, no auds",
|
|
args: args{
|
|
ctx: genericapirequest.WithUser(genericapirequest.NewContext(), &user.DefaultInfo{
|
|
Name: "bond",
|
|
UID: "007",
|
|
Groups: []string{"agents", "ops"},
|
|
Extra: map[string][]string{
|
|
"fan-of": {"pandas", "twizzlers"},
|
|
"needs": {"sleep"},
|
|
},
|
|
}),
|
|
obj: &identityapi.WhoAmIRequest{},
|
|
createValidation: nil,
|
|
options: nil,
|
|
},
|
|
want: &identityapi.WhoAmIRequest{
|
|
Status: identityapi.WhoAmIRequestStatus{
|
|
KubernetesUserInfo: identityapi.KubernetesUserInfo{
|
|
User: identityapi.UserInfo{
|
|
Username: "bond",
|
|
UID: "007",
|
|
Groups: []string{"agents", "ops"},
|
|
Extra: map[string]identityapi.ExtraValue{
|
|
"fan-of": {"pandas", "twizzlers"},
|
|
"needs": {"sleep"},
|
|
},
|
|
},
|
|
Audiences: nil,
|
|
},
|
|
},
|
|
},
|
|
wantErr: ``,
|
|
},
|
|
{
|
|
name: "with user info and auds",
|
|
args: args{
|
|
ctx: authenticator.WithAudiences(
|
|
genericapirequest.WithUser(genericapirequest.NewContext(), &user.DefaultInfo{
|
|
Name: "panda",
|
|
}),
|
|
authenticator.Audiences{"gitlab", "aws"},
|
|
),
|
|
obj: &identityapi.WhoAmIRequest{},
|
|
createValidation: nil,
|
|
options: nil,
|
|
},
|
|
want: &identityapi.WhoAmIRequest{
|
|
Status: identityapi.WhoAmIRequestStatus{
|
|
KubernetesUserInfo: identityapi.KubernetesUserInfo{
|
|
User: identityapi.UserInfo{
|
|
Username: "panda",
|
|
},
|
|
Audiences: []string{"gitlab", "aws"},
|
|
},
|
|
},
|
|
},
|
|
wantErr: ``,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := &REST{}
|
|
got, err := r.Create(tt.args.ctx, tt.args.obj, tt.args.createValidation, tt.args.options)
|
|
require.Equal(t, tt.wantErr, errString(err))
|
|
require.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func errString(err error) string {
|
|
if err == nil {
|
|
return ""
|
|
}
|
|
|
|
return err.Error()
|
|
}
|