/
rest.go
131 lines (108 loc) · 3.98 KB
/
rest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package whoamirequest
import (
"context"
"fmt"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/authentication/authenticator"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
identityapi "go.pinniped.dev/generated/latest/apis/concierge/identity"
identityapivalidation "go.pinniped.dev/generated/latest/apis/concierge/identity/validation"
)
func NewREST(resource schema.GroupResource) *REST {
return &REST{
tableConvertor: rest.NewDefaultTableConvertor(resource),
}
}
type REST struct {
tableConvertor rest.TableConvertor
}
// Assert that our *REST implements all the optional interfaces that we expect it to implement.
var _ interface {
rest.Creater
rest.NamespaceScopedStrategy
rest.Scoper
rest.Storage
rest.CategoriesProvider
rest.Lister
} = (*REST)(nil)
func (*REST) New() runtime.Object {
return &identityapi.WhoAmIRequest{}
}
func (*REST) NewList() runtime.Object {
return &identityapi.WhoAmIRequestList{}
}
func (*REST) List(_ context.Context, _ *metainternalversion.ListOptions) (runtime.Object, error) {
return &identityapi.WhoAmIRequestList{
ListMeta: metav1.ListMeta{
ResourceVersion: "0", // this resource version means "from the API server cache"
},
Items: []identityapi.WhoAmIRequest{}, // avoid sending nil items list
}, nil
}
func (r *REST) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return r.tableConvertor.ConvertToTable(ctx, obj, tableOptions)
}
func (*REST) NamespaceScoped() bool {
return false
}
func (*REST) Categories() []string {
return []string{"pinniped"}
}
func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
whoAmIRequest, ok := obj.(*identityapi.WhoAmIRequest)
if !ok {
return nil, apierrors.NewBadRequest(fmt.Sprintf("not a WhoAmIRequest: %#v", obj))
}
if errs := identityapivalidation.ValidateWhoAmIRequest(whoAmIRequest); len(errs) > 0 {
return nil, apierrors.NewInvalid(identityapi.Kind(whoAmIRequest.Kind), whoAmIRequest.Name, errs)
}
// just a sanity check, not sure how to honor a dry run on a virtual API
if options != nil {
if len(options.DryRun) != 0 {
errs := field.ErrorList{field.NotSupported(field.NewPath("dryRun"), options.DryRun, nil)}
return nil, apierrors.NewInvalid(identityapi.Kind(whoAmIRequest.Kind), whoAmIRequest.Name, errs)
}
}
if namespace := genericapirequest.NamespaceValue(ctx); len(namespace) != 0 {
return nil, apierrors.NewBadRequest(fmt.Sprintf("namespace is not allowed on WhoAmIRequest: %v", namespace))
}
if createValidation != nil {
if err := createValidation(ctx, obj.DeepCopyObject()); err != nil {
return nil, err
}
}
userInfo, ok := genericapirequest.UserFrom(ctx)
if !ok {
return nil, apierrors.NewInternalError(fmt.Errorf("no user info on request"))
}
auds, _ := authenticator.AudiencesFrom(ctx)
out := &identityapi.WhoAmIRequest{
Status: identityapi.WhoAmIRequestStatus{
KubernetesUserInfo: identityapi.KubernetesUserInfo{
User: identityapi.UserInfo{
Username: userInfo.GetName(),
UID: userInfo.GetUID(),
Groups: userInfo.GetGroups(),
},
Audiences: auds,
},
},
}
for k, v := range userInfo.GetExtra() {
if out.Status.KubernetesUserInfo.User.Extra == nil {
out.Status.KubernetesUserInfo.User.Extra = map[string]identityapi.ExtraValue{}
}
// this assumes no one is putting secret data in the extra field
// I think this is a safe assumption since it would leak into audit logs
out.Status.KubernetesUserInfo.User.Extra[k] = v
}
return out, nil
}