diff --git a/docs.md b/docs.md index a83891bb..bac522aa 100644 --- a/docs.md +++ b/docs.md @@ -1,6 +1,24 @@ -# core/v1 +# cluster.cattle.io/v3 -## Namespace +## ClusterAuthToken + +### Validation Checks + +#### Invalid Fields - Create + +When a ClusterAuthToken is created, the following checks take place: + +- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`). + +#### Invalid Fields - Update + +When a ClusterAuthToken is updated, the following checks take place: + +- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`). + +# core/v1 + +## Namespace ### Validation Checks @@ -24,7 +42,7 @@ The following labels are considered relevant for PSA enforcement: - pod-security.kubernetes.io/warn - pod-security.kubernetes.io/warn-version -## Secret +## Secret ### Validation Checks @@ -43,9 +61,9 @@ places a `field.cattle.io/creatorId` annotation with the name of the user as the Checks if there are any RoleBindings owned by this secret which provide access to a role granting access to this secret. If yes, the webhook redacts the role, so that it only grants a deletion permission. -# management.cattle.io/v3 +# management.cattle.io/v3 -## ClusterProxyConfig +## ClusterProxyConfig ### Validation Checks @@ -54,7 +72,7 @@ If yes, the webhook redacts the role, so that it only grants a deletion permissi When creating a clusterproxyconfig, we check to make sure that one does not already exist for the given cluster. Only 1 clusterproxyconfig per downstream cluster is ever permitted. -## ClusterRoleTemplateBinding +## ClusterRoleTemplateBinding ### Validation Checks @@ -94,7 +112,7 @@ Users can update the following fields if they have not been set, but after they In addition, as in the create validation, both a user subject and a group subject cannot be specified. -## Feature +## Feature ### Validation Checks @@ -103,7 +121,7 @@ In addition, as in the create validation, both a user subject and a group subjec The desired value must not change on new spec unless it's equal to the `lockedValue` or `lockedValue` is nil. Due to the security impact of the `external-rules` feature flag, only users with admin permissions (`*` verbs on `*` resources in `*` APIGroups in all namespaces) can enable or disable this feature flag. -## FleetWorkspace +## FleetWorkspace ### Validation Checks @@ -118,7 +136,7 @@ When a `FleetWorkspace` is created, it will create the following resources: 2. `ClusterRole`. It will create the cluster role that has * permission only to the current workspace. 3. Two `RoleBindings` to bind the current user to fleet-admin roles and `FleetWorkspace` roles. -## GlobalRole +## GlobalRole ### Validation Checks @@ -149,7 +167,7 @@ The `globalroles.builtin` field is immutable, and new builtIn GlobalRoles cannot If `globalroles.builtin` is true then all fields are immutable except `metadata` and `newUserDefault`. If `globalroles.builtin` is true then the GlobalRole can not be deleted. -## GlobalRoleBinding +## GlobalRoleBinding ### Validation Checks @@ -182,7 +200,7 @@ All RoleTemplates which are referred to in the `inheritedClusterRoles` field mus When a GlobalRoleBinding is created an owner reference is created on the binding referring to the backing GlobalRole defined by `globalRoleName`. -## NodeDriver +## NodeDriver ### Validation Checks @@ -192,7 +210,7 @@ Note: checks only run if a node driver is being disabled or deleted This admission webhook prevents the disabling or deletion of a NodeDriver if there are any Nodes that are under management by said driver. If there are _any_ nodes that use the driver the request will be denied. -## Project +## Project ### Validation Checks @@ -221,7 +239,7 @@ Limits for any resource must not be less than requests. Adds the authz.management.cattle.io/creator-role-bindings annotation. -## ProjectRoleTemplateBinding +## ProjectRoleTemplateBinding ### Validation Checks @@ -268,7 +286,7 @@ changed: In addition, as in the create validation, both a user subject and a group subject cannot be specified. -## RoleTemplate +## RoleTemplate ### Validation Checks @@ -307,7 +325,7 @@ If `roletemplates.builtin` is true then all fields are immutable except: RoleTemplate can not be deleted if they are referenced by other RoleTemplates via `roletemplates.roleTemplateNames` or by GlobalRoles via `globalRoles.inheritedClusterRoles` -## Setting +## Setting ### Validation Checks @@ -335,7 +353,23 @@ When a Setting is updated, the following checks take place: have a status condition `AgentTlsStrictCheck` set to `True`, unless the new setting has an overriding annotation `cattle.io/force=true`. -## UserAttribute +## Token + +### Validation Checks + +#### Invalid Fields - Create + +When a Token is created, the following checks take place: + +- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`). + +#### Invalid Fields - Update + +When a Token is updated, the following checks take place: + +- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`). + +## UserAttribute ### Validation Checks @@ -355,9 +389,9 @@ When a UserAttribute is updated, the following checks take place: - If set, `disableAfter` must be zero or a positive duration (e.g. `240h`). - If set, `deleteAfter` must be zero or a positive duration (e.g. `240h`). -# provisioning.cattle.io/v1 +# provisioning.cattle.io/v1 -## Cluster +## Cluster ### Validation Checks @@ -410,9 +444,9 @@ perform no mutations. If the value is not present or not `"true"`, compare the v for each `machinePool`, to its' previous value. If the values are not identical, revert the value for the `dynamicSchemaSpec` for the specific `machinePool`, but do not reject the request. -# rbac.authorization.k8s.io/v1 +# rbac.authorization.k8s.io/v1 -## ClusterRole +## ClusterRole ### Validation Checks @@ -420,7 +454,7 @@ for each `machinePool`, to its' previous value. If the values are not identical, Users cannot update or remove the following label after it has been added: - authz.management.cattle.io/gr-owner -## ClusterRoleBinding +## ClusterRoleBinding ### Validation Checks @@ -428,7 +462,7 @@ Users cannot update or remove the following label after it has been added: Users cannot update or remove the following label after it has been added: - authz.management.cattle.io/grb-owner -## Role +## Role ### Validation Checks @@ -436,7 +470,7 @@ Users cannot update or remove the following label after it has been added: Users cannot update or remove the following label after it has been added: - authz.management.cattle.io/gr-owner -## RoleBinding +## RoleBinding ### Validation Checks diff --git a/pkg/codegen/docs.go b/pkg/codegen/docs.go index 91c116c7..a43d9d07 100644 --- a/pkg/codegen/docs.go +++ b/pkg/codegen/docs.go @@ -39,16 +39,18 @@ func generateDocs(resourcesBaseDir, outputFilePath string) (err error) { if err != nil { return err } + docFiles, err := getDocFiles(resourcesBaseDir) if err != nil { return fmt.Errorf("unable to create documentation: %w", err) } + currentGroup := "" for _, docFile := range docFiles { newGroup := docFile.group if newGroup != currentGroup { // our group has changed, output a new group header - groupFormatString := "# %s/%s \n" + groupFormatString := "# %s/%s\n" if currentGroup != "" { groupFormatString = "\n" + groupFormatString } @@ -59,10 +61,11 @@ func generateDocs(resourcesBaseDir, outputFilePath string) (err error) { currentGroup = newGroup } - _, err = fmt.Fprintf(outputFile, "\n## %s \n\n", docFile.resource) + _, err = fmt.Fprintf(outputFile, "\n## %s\n\n", docFile.resource) if err != nil { return fmt.Errorf("unable to write resource header for %s: %w", docFile.resource, err) } + scanner := bufio.NewScanner(bytes.NewReader(docFile.content)) for scanner.Scan() { line := scanner.Bytes() @@ -81,6 +84,7 @@ func generateDocs(resourcesBaseDir, outputFilePath string) (err error) { return fmt.Errorf("got an error scanning content for %s/%s.%s: %w", docFile.group, docFile.version, docFile.resource, err) } } + return nil } @@ -91,6 +95,7 @@ func getDocFiles(baseDir string) ([]docFile, error) { if err != nil { return nil, fmt.Errorf("unable to list entries in directory %s: %w", baseDir, err) } + var docFiles []docFile for _, entry := range entries { entryPath := filepath.Join(baseDir, entry.Name()) diff --git a/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/ClusterAuthToken.md b/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/ClusterAuthToken.md new file mode 100644 index 00000000..a85ccfde --- /dev/null +++ b/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/ClusterAuthToken.md @@ -0,0 +1,13 @@ +## Validation Checks + +### Invalid Fields - Create + +When a ClusterAuthToken is created, the following checks take place: + +- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`). + +### Invalid Fields - Update + +When a ClusterAuthToken is updated, the following checks take place: + +- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`). diff --git a/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/validator.go b/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/validator.go new file mode 100644 index 00000000..e84f002f --- /dev/null +++ b/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/validator.go @@ -0,0 +1,93 @@ +package clusterauthtoken + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/rancher/webhook/pkg/admission" + admissionv1 "k8s.io/api/admission/v1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/trace" +) + +var gvr = schema.GroupVersionResource{ + Group: "cluster.cattle.io", + Version: "v3", + Resource: "clusterauthtokens", +} + +// Validator validates clusterauthtokens. +type Validator struct { + admitter admitter +} + +// NewValidator returns a new Validator instance. +func NewValidator() *Validator { + return &Validator{ + admitter: admitter{}, + } +} + +// GVR returns the GroupVersionResource. +func (v *Validator) GVR() schema.GroupVersionResource { + return gvr +} + +// Operations returns list of operations handled by the validator. +func (v *Validator) Operations() []admissionregistrationv1.OperationType { + return []admissionregistrationv1.OperationType{admissionregistrationv1.Update, admissionregistrationv1.Create} +} + +// ValidatingWebhook returns the ValidatingWebhook. +func (v *Validator) ValidatingWebhook(clientConfig admissionregistrationv1.WebhookClientConfig) []admissionregistrationv1.ValidatingWebhook { + return []admissionregistrationv1.ValidatingWebhook{ + *admission.NewDefaultValidatingWebhook(v, clientConfig, admissionregistrationv1.ClusterScope, v.Operations()), + } +} + +// Admitters returns the admitter objects. +func (v *Validator) Admitters() []admission.Admitter { + return []admission.Admitter{&v.admitter} +} + +type admitter struct{} + +// Admit handles the webhook admission requests. +func (a *admitter) Admit(request *admission.Request) (*admissionv1.AdmissionResponse, error) { + listTrace := trace.New("clusterAuthTokenValidator Admit", trace.Field{Key: "user", Value: request.UserInfo.Username}) + defer listTrace.LogIfLong(admission.SlowTraceDuration) + + if request.Operation == admissionv1.Create || request.Operation == admissionv1.Update { + err := a.validateTokenFields(request) + if err != nil { + return admission.ResponseBadRequest(err.Error()), nil + } + } + + return admission.ResponseAllowed(), nil +} + +// PartialClusterAuthToken represents raw values of ClusterAuthToken fields. +type PartialClusterAuthToken struct { + LastUsedAt *string `json:"lastUsedAt"` +} + +func (a *admitter) validateTokenFields(request *admission.Request) error { + var partial PartialClusterAuthToken + + err := json.Unmarshal(request.Object.Raw, &partial) + if err != nil { + return fmt.Errorf("failed to get PartialClusterAuthToken from request: %w", err) + } + + if partial.LastUsedAt != nil { + if _, err = time.Parse(time.RFC3339, *partial.LastUsedAt); err != nil { + return field.TypeInvalid(field.NewPath("lastUsedAt"), partial.LastUsedAt, err.Error()) + } + } + + return nil +} diff --git a/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/validator_test.go b/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/validator_test.go new file mode 100644 index 00000000..0fa41fc8 --- /dev/null +++ b/pkg/resources/cluster.cattle.io/v3/clusterauthtoken/validator_test.go @@ -0,0 +1,135 @@ +package clusterauthtoken_test + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/rancher/webhook/pkg/admission" + "github.com/rancher/webhook/pkg/resources/cluster.cattle.io/v3/clusterauthtoken" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + v1 "k8s.io/api/admission/v1" + authenticationv1 "k8s.io/api/authentication/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/pointer" +) + +type ClusterAuthTokenFieldsSuite struct { + suite.Suite +} + +func TestTokenFieldsValidation(t *testing.T) { + t.Parallel() + suite.Run(t, new(ClusterAuthTokenFieldsSuite)) +} + +var ( + gvk = metav1.GroupVersionKind{Group: "cluster.cattle.io", Version: "v3", Kind: "ClusterAuthToken"} + gvr = metav1.GroupVersionResource{Group: "cluster.cattle.io", Version: "v3", Resource: "clusterauthtokens"} +) + +type tokenFieldsTest struct { + lastUsedAt *string + allowed bool +} + +func (t *tokenFieldsTest) name() string { + return pointer.StringDeref(t.lastUsedAt, "nil") +} + +func (t *tokenFieldsTest) toToken() ([]byte, error) { + return json.Marshal(clusterauthtoken.PartialClusterAuthToken{ + LastUsedAt: t.lastUsedAt, + }) +} + +var tokenFieldsTests = []tokenFieldsTest{ + { + allowed: true, + }, + { + lastUsedAt: pointer.String(time.Now().Format(time.RFC3339)), + allowed: true, + }, + { + lastUsedAt: pointer.String("2024-03-25T21:2:45Z"), // Not a valid RFC3339 time. + }, + { + lastUsedAt: pointer.String("1w"), + }, + { + lastUsedAt: pointer.String("1d"), + }, + { + lastUsedAt: pointer.String("-1h"), + }, + { + lastUsedAt: pointer.String(""), + }, +} + +func (s *ClusterAuthTokenFieldsSuite) TestValidateOnUpdate() { + s.validate(v1.Update) +} + +func (s *ClusterAuthTokenFieldsSuite) TestValidateOnCreate() { + s.validate(v1.Create) +} + +func (s *ClusterAuthTokenFieldsSuite) TestDontValidateOnDelete() { + // Make sure that Token can be deleted without enforcing validation of user token fields. + alwaysAllow := true + s.validate(v1.Delete, alwaysAllow) +} + +func (s *ClusterAuthTokenFieldsSuite) validate(op v1.Operation, allowed ...bool) { + admitter := s.setup() + + for _, test := range tokenFieldsTests { + test := test + s.Run(test.name(), func() { + t := s.T() + t.Parallel() + + objRaw, err := test.toToken() + assert.NoError(t, err, "failed to marshal PartialToken") + + resp, err := admitter.Admit(newRequest(op, objRaw)) + if assert.NoError(t, err, "Admit failed") { + wantAllowed := test.allowed + if len(allowed) > 0 { + wantAllowed = allowed[0] // Apply the override. + } + + assert.Equalf(t, wantAllowed, resp.Allowed, "expected allowed %v got %v message=%v", test.allowed, resp.Allowed, resp.Result) + } + }) + } +} + +func (s *ClusterAuthTokenFieldsSuite) setup() admission.Admitter { + validator := clusterauthtoken.NewValidator() + s.Len(validator.Admitters(), 1, "expected 1 admitter") + + return validator.Admitters()[0] +} + +func newRequest(op v1.Operation, obj []byte) *admission.Request { + return &admission.Request{ + AdmissionRequest: v1.AdmissionRequest{ + UID: "1", + Kind: gvk, + Resource: gvr, + RequestKind: &gvk, + RequestResource: &gvr, + Operation: op, + UserInfo: authenticationv1.UserInfo{Username: "foo", UID: ""}, + Object: runtime.RawExtension{Raw: obj}, + OldObject: runtime.RawExtension{Raw: []byte("{}")}, + }, + Context: context.Background(), + } +} diff --git a/pkg/resources/management.cattle.io/v3/token/Token.md b/pkg/resources/management.cattle.io/v3/token/Token.md new file mode 100644 index 00000000..09eefcb2 --- /dev/null +++ b/pkg/resources/management.cattle.io/v3/token/Token.md @@ -0,0 +1,13 @@ +## Validation Checks + +### Invalid Fields - Create + +When a Token is created, the following checks take place: + +- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`). + +### Invalid Fields - Update + +When a Token is updated, the following checks take place: + +- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`). diff --git a/pkg/resources/management.cattle.io/v3/token/validator.go b/pkg/resources/management.cattle.io/v3/token/validator.go new file mode 100644 index 00000000..b4799a36 --- /dev/null +++ b/pkg/resources/management.cattle.io/v3/token/validator.go @@ -0,0 +1,93 @@ +package token + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/rancher/webhook/pkg/admission" + admissionv1 "k8s.io/api/admission/v1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/trace" +) + +var gvr = schema.GroupVersionResource{ + Group: "management.cattle.io", + Version: "v3", + Resource: "tokens", +} + +// Validator validates tokens. +type Validator struct { + admitter admitter +} + +// NewValidator returns a new Validator instance. +func NewValidator() *Validator { + return &Validator{ + admitter: admitter{}, + } +} + +// GVR returns the GroupVersionResource. +func (v *Validator) GVR() schema.GroupVersionResource { + return gvr +} + +// Operations returns list of operations handled by the validator. +func (v *Validator) Operations() []admissionregistrationv1.OperationType { + return []admissionregistrationv1.OperationType{admissionregistrationv1.Update, admissionregistrationv1.Create} +} + +// ValidatingWebhook returns the ValidatingWebhook. +func (v *Validator) ValidatingWebhook(clientConfig admissionregistrationv1.WebhookClientConfig) []admissionregistrationv1.ValidatingWebhook { + return []admissionregistrationv1.ValidatingWebhook{ + *admission.NewDefaultValidatingWebhook(v, clientConfig, admissionregistrationv1.ClusterScope, v.Operations()), + } +} + +// Admitters returns the admitter objects. +func (v *Validator) Admitters() []admission.Admitter { + return []admission.Admitter{&v.admitter} +} + +type admitter struct{} + +// Admit handles the webhook admission requests. +func (a *admitter) Admit(request *admission.Request) (*admissionv1.AdmissionResponse, error) { + listTrace := trace.New("tokenValidator Admit", trace.Field{Key: "user", Value: request.UserInfo.Username}) + defer listTrace.LogIfLong(admission.SlowTraceDuration) + + if request.Operation == admissionv1.Create || request.Operation == admissionv1.Update { + err := a.validateTokenFields(request) + if err != nil { + return admission.ResponseBadRequest(err.Error()), nil + } + } + + return admission.ResponseAllowed(), nil +} + +// PartialToken represents raw values of Token fields. +type PartialToken struct { + LastUsedAt *string `json:"lastUsedAt"` +} + +func (a *admitter) validateTokenFields(request *admission.Request) error { + var partial PartialToken + + err := json.Unmarshal(request.Object.Raw, &partial) + if err != nil { + return fmt.Errorf("failed to get PartialToken from request: %w", err) + } + + if partial.LastUsedAt != nil { + if _, err = time.Parse(time.RFC3339, *partial.LastUsedAt); err != nil { + return field.TypeInvalid(field.NewPath("lastUsedAt"), partial.LastUsedAt, err.Error()) + } + } + + return nil +} diff --git a/pkg/resources/management.cattle.io/v3/token/validator_test.go b/pkg/resources/management.cattle.io/v3/token/validator_test.go new file mode 100644 index 00000000..c5d4b872 --- /dev/null +++ b/pkg/resources/management.cattle.io/v3/token/validator_test.go @@ -0,0 +1,135 @@ +package token_test + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/rancher/webhook/pkg/admission" + "github.com/rancher/webhook/pkg/resources/management.cattle.io/v3/token" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + v1 "k8s.io/api/admission/v1" + authenticationv1 "k8s.io/api/authentication/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/pointer" +) + +type TokenFieldsSuite struct { + suite.Suite +} + +func TestTokenFieldsValidation(t *testing.T) { + t.Parallel() + suite.Run(t, new(TokenFieldsSuite)) +} + +var ( + gvk = metav1.GroupVersionKind{Group: "management.cattle.io", Version: "v3", Kind: "Token"} + gvr = metav1.GroupVersionResource{Group: "management.cattle.io", Version: "v3", Resource: "tokens"} +) + +type tokenFieldsTest struct { + lastUsedAt *string + allowed bool +} + +func (t *tokenFieldsTest) name() string { + return pointer.StringDeref(t.lastUsedAt, "nil") +} + +func (t *tokenFieldsTest) toToken() ([]byte, error) { + return json.Marshal(token.PartialToken{ + LastUsedAt: t.lastUsedAt, + }) +} + +var tokenFieldsTests = []tokenFieldsTest{ + { + allowed: true, + }, + { + lastUsedAt: pointer.String(time.Now().Format(time.RFC3339)), + allowed: true, + }, + { + lastUsedAt: pointer.String("2024-03-25T21:2:45Z"), // Not a valid RFC3339 time. + }, + { + lastUsedAt: pointer.String("1w"), + }, + { + lastUsedAt: pointer.String("1d"), + }, + { + lastUsedAt: pointer.String("-1h"), + }, + { + lastUsedAt: pointer.String(""), + }, +} + +func (s *TokenFieldsSuite) TestValidateOnUpdate() { + s.validate(v1.Update) +} + +func (s *TokenFieldsSuite) TestValidateOnCreate() { + s.validate(v1.Create) +} + +func (s *TokenFieldsSuite) TestDontValidateOnDelete() { + // Make sure that Token can be deleted without enforcing validation of user token fields. + alwaysAllow := true + s.validate(v1.Delete, alwaysAllow) +} + +func (s *TokenFieldsSuite) validate(op v1.Operation, allowed ...bool) { + admitter := s.setup() + + for _, test := range tokenFieldsTests { + test := test + s.Run(test.name(), func() { + t := s.T() + t.Parallel() + + objRaw, err := test.toToken() + assert.NoError(t, err, "failed to marshal PartialToken") + + resp, err := admitter.Admit(newRequest(op, objRaw)) + if assert.NoError(t, err, "Admit failed") { + wantAllowed := test.allowed + if len(allowed) > 0 { + wantAllowed = allowed[0] // Apply the override. + } + + assert.Equalf(t, wantAllowed, resp.Allowed, "expected allowed %v got %v message=%v", test.allowed, resp.Allowed, resp.Result) + } + }) + } +} + +func (s *TokenFieldsSuite) setup() admission.Admitter { + validator := token.NewValidator() + s.Len(validator.Admitters(), 1, "expected 1 admitter") + + return validator.Admitters()[0] +} + +func newRequest(op v1.Operation, obj []byte) *admission.Request { + return &admission.Request{ + AdmissionRequest: v1.AdmissionRequest{ + UID: "1", + Kind: gvk, + Resource: gvr, + RequestKind: &gvk, + RequestResource: &gvr, + Operation: op, + UserInfo: authenticationv1.UserInfo{Username: "foo", UID: ""}, + Object: runtime.RawExtension{Raw: obj}, + OldObject: runtime.RawExtension{Raw: []byte("{}")}, + }, + Context: context.Background(), + } +} diff --git a/pkg/server/handlers.go b/pkg/server/handlers.go index dba61759..f53a5d3c 100644 --- a/pkg/server/handlers.go +++ b/pkg/server/handlers.go @@ -4,6 +4,7 @@ import ( "github.com/rancher/webhook/pkg/admission" "github.com/rancher/webhook/pkg/clients" "github.com/rancher/webhook/pkg/resolvers" + "github.com/rancher/webhook/pkg/resources/cluster.cattle.io/v3/clusterauthtoken" nshandler "github.com/rancher/webhook/pkg/resources/core/v1/namespace" "github.com/rancher/webhook/pkg/resources/core/v1/secret" managementCluster "github.com/rancher/webhook/pkg/resources/management.cattle.io/v3/cluster" @@ -19,6 +20,7 @@ import ( "github.com/rancher/webhook/pkg/resources/management.cattle.io/v3/projectroletemplatebinding" "github.com/rancher/webhook/pkg/resources/management.cattle.io/v3/roletemplate" "github.com/rancher/webhook/pkg/resources/management.cattle.io/v3/setting" + "github.com/rancher/webhook/pkg/resources/management.cattle.io/v3/token" "github.com/rancher/webhook/pkg/resources/management.cattle.io/v3/userattribute" provisioningCluster "github.com/rancher/webhook/pkg/resources/provisioning.cattle.io/v1/cluster" "github.com/rancher/webhook/pkg/resources/rbac.authorization.k8s.io/v1/clusterrole" @@ -55,12 +57,17 @@ func Validation(clients *clients.Clients) ([]admission.ValidatingAdmissionHandle roles := role.NewValidator() rolebindings := rolebinding.NewValidator() setting := setting.NewValidator(clients.Management.Cluster().Cache()) + token := token.NewValidator() userAttribute := userattribute.NewValidator() clusterRoles := clusterrole.NewValidator() clusterRoleBindings := clusterrolebinding.NewValidator() - handlers = append(handlers, psact, globalRoles, globalRoleBindings, prtbs, crtbs, roleTemplates, secrets, nodeDriver, projects, roles, rolebindings, clusterRoles, clusterRoleBindings, clusterProxyConfigs, userAttribute, setting) + handlers = append(handlers, psact, globalRoles, globalRoleBindings, prtbs, crtbs, roleTemplates, secrets, nodeDriver, projects, roles, rolebindings, clusterRoles, clusterRoleBindings, clusterProxyConfigs, userAttribute, setting, token) + } else { + clusterAuthTokens := clusterauthtoken.NewValidator() + handlers = append(handlers, clusterAuthTokens) } + return handlers, nil }