ContainerImage.Pinniped/internal/registry/whoamirequest/rest_test.go
Monis Khan abc941097c
Add WhoAmIRequest Aggregated Virtual REST API
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>
2021-02-22 20:02:41 -05:00

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