diff --git a/.gitignore b/.gitignore index 8b65462..b1136bc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ /config cover.out /vendor -/.vendor-new +/.vendor-new \ No newline at end of file diff --git a/apis/authorization/v1alpha1/groupversion_info.go b/apis/authorization/v1alpha1/groupversion_info.go new file mode 100644 index 0000000..29bf8a8 --- /dev/null +++ b/apis/authorization/v1alpha1/groupversion_info.go @@ -0,0 +1,44 @@ +// Copyright 2024 Upbound Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package v1alpha1 contains the SpaceObjectRoleBinding resources. +// +kubebuilder:object:generate=true +// +groupName=authorization.spaces.upbound.io +// +versionName=v1alpha1 +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "authorization.spaces.upbound.io" + Version = "v1alpha1" +) + +var ( + // SchemeGroupVersion is group version used to register these objects. + //nolint:gochecknoglobals // This is an established pattern + SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + //nolint:gochecknoglobals // This is an established pattern + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + //nolint:gochecknoglobals // This is an established pattern + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/authorization/v1alpha1/labels.go b/apis/authorization/v1alpha1/labels.go new file mode 100644 index 0000000..7621d6d --- /dev/null +++ b/apis/authorization/v1alpha1/labels.go @@ -0,0 +1,29 @@ +// Copyright 2024 Upbound Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1alpha1 + +const ( + // LabelObjectRoleBindingObjectAPIGroup is a label applied on (Space)ObjectRoleBinding, and + // contains the same value as .spec.object.apiGroup. + LabelObjectRoleBindingObjectAPIGroup = "authorization.spaces.upbound.io/object-apigroup" + + // LabelObjectRoleBindingObjectResource is a label applied on (Space)ObjectRoleBinding, and + // contains the same value as .spec.object.resource. + LabelObjectRoleBindingObjectResource = "authorization.spaces.upbound.io/object-resource" + + // LabelObjectRoleBindingObjectName is a label applied on (Space)ObjectRoleBinding, and + // contains the same value as .spec.object.name. + LabelObjectRoleBindingObjectName = "authorization.spaces.upbound.io/object-name" +) diff --git a/apis/authorization/v1alpha1/objectrolebinding_types.go b/apis/authorization/v1alpha1/objectrolebinding_types.go new file mode 100644 index 0000000..15214cc --- /dev/null +++ b/apis/authorization/v1alpha1/objectrolebinding_types.go @@ -0,0 +1,144 @@ +// Copyright 2024 Upbound Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1alpha1 + +import ( + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ObjectRoleBindingSpec is ObjectRoleBinding's spec. +type ObjectRoleBindingSpec struct { + // Object references the object to which the listed subjects should have access at varying levels. + // The object value is immutable after creation. + // +kubebuilder:validation:Required + Object Object `json:"object"` + + // Subjects should be a map type with both kind+name as a key + // +listType=map + // +listMapKey=kind + // +listMapKey=name + Subjects []SubjectBinding `json:"subjects"` +} + +// Object represents the API object for which permissions are managed. +// In a ObjectRoleBinding context, the object exists in the same namespace +// as the referring ObjectRoleBinding, or the object is the namespace that +// the ObjectRoleBinding is in. +// In a SpaceObjectRoleBinding context, the object being pointed to must be +// non-namespaced. +type Object struct { + // APIGroup defines the apiGroup of the object being pointed to. + // With some minor differences, this is essentially matched as a DNS subdomain, like how Kubernetes validates it. + // The Kubernetes legacy core group is denoted as "core". + // +kubebuilder:validation:Required + // +kubebuilder:validation:MaxLength=64 + // +kubebuilder:validation:Pattern="^[a-z][a-z0-9-]{0,61}[a-z0-9](\\.[a-z][a-z0-9-]{0,61}[a-z0-9])*$" + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="apiGroup is immutable" + // +kubebuilder:validation:XValidation:rule="self == 'core'",message="apiGroup must be 'core' for now. This will change in the future." + APIGroup string `json:"apiGroup"` + + // Resource defines the resource type (often kind in plural, e.g. + // controlplanes) being pointed to. + // With some minor differences, this is essentially matched as a DNS label, like how Kubernetes validates it. + // +kubebuilder:validation:Required + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Pattern="^[a-z][a-z0-9-]{1,61}[a-z0-9]$" + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="resource is immutable" + // +kubebuilder:validation:XValidation:rule="self == 'namespaces'",message="resource must be 'namespaces' for now. This will change in the future." + Resource string `json:"resource"` + + // Name points to the .metadata.name of the object targeted. + // Kubernetes validates this as a DNS 1123 subdomain. + // +kubebuilder:validation:Required + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:Pattern="^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$" + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable" + Name string `json:"name"` +} + +// SubjectKind Kind of subject being referenced. +type SubjectKind string + +const ( + // SubjectKindUpboundTeam refers to an Upbound team as the subject kind. + // For this kind, the name/identifier is the team UUID. + SubjectKindUpboundTeam SubjectKind = "UpboundTeam" +) + +// SubjectBinding contains a reference to the object or user identities a role +// binding applies to. +type SubjectBinding struct { + // Kind of subject being referenced. Values defined by this API group are + // for now only "UpboundTeam". + // +kubebuilder:validation:Enum=UpboundTeam + // +kubebuilder:validation:Required + // +kubebuilder:validation:XValidation:rule="self == 'UpboundTeam'",message="kind must be 'UpboundTeam' for now. This will change in the future." + Kind SubjectKind `json:"kind"` + + // Name (identifier) of the subject (of the specified kind) being referenced. + // The identifier must be 2-100 chars, [a-zA-Z0-9-], no repeating dashes, can't start/end with a dash. + // Notably, a UUID fits that format. + // +kubebuilder:validation:Required + // +kubebuilder:validation:MaxLength=100 + // +kubebuilder:validation:Pattern="^([a-zA-Z0-9]+-?)+[a-zA-Z0-9]$" + Name string `json:"name"` + + // Role this subject has on the associated Object. + // The list of valid roles is defined for each target API resource separately. + // For namespaces, valid values are "viewer", "editor", and "admin". + // The format of this is essentially a RFC 1035 label with underscores instead of dashes, minimum three characters long. + // +kubebuilder:validation:Required + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Pattern="^[a-z][a-z0-9_]{1,62}[a-z0-9]$" + Role string `json:"role"` +} + +// ObjectRoleBindingStatus is RoleBindings' status. +type ObjectRoleBindingStatus struct{} + +// +kubebuilder:object:root=true + +// A ObjectRoleBinding binds a namespaced API object to a set of subjects, at varying access levels. +// For now, there can be at most one ObjectRoleBinding pointing to each API object. +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced,categories=iam +type ObjectRoleBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +kubebuilder:validation:Required + Spec ObjectRoleBindingSpec `json:"spec"` + Status ObjectRoleBindingStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ObjectRoleBindingList contains a list of ObjectRoleBindings. +type ObjectRoleBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ObjectRoleBinding `json:"items"` +} + +// ObjectRoleBindingKind is the kind of a ObjectRoleBinding. +// +//nolint:gochecknoglobals // This is an established pattern +var ObjectRoleBindingKind = reflect.TypeOf(ObjectRoleBinding{}).Name() + +func init() { + SchemeBuilder.Register(&ObjectRoleBinding{}, &ObjectRoleBindingList{}) +} diff --git a/apis/authorization/v1alpha1/zz_generated.deepcopy.go b/apis/authorization/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000..185849c --- /dev/null +++ b/apis/authorization/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,150 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2020 The Upbound Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Object) DeepCopyInto(out *Object) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Object. +func (in *Object) DeepCopy() *Object { + if in == nil { + return nil + } + out := new(Object) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectRoleBinding) DeepCopyInto(out *ObjectRoleBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectRoleBinding. +func (in *ObjectRoleBinding) DeepCopy() *ObjectRoleBinding { + if in == nil { + return nil + } + out := new(ObjectRoleBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ObjectRoleBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectRoleBindingList) DeepCopyInto(out *ObjectRoleBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ObjectRoleBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectRoleBindingList. +func (in *ObjectRoleBindingList) DeepCopy() *ObjectRoleBindingList { + if in == nil { + return nil + } + out := new(ObjectRoleBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ObjectRoleBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectRoleBindingSpec) DeepCopyInto(out *ObjectRoleBindingSpec) { + *out = *in + out.Object = in.Object + if in.Subjects != nil { + in, out := &in.Subjects, &out.Subjects + *out = make([]SubjectBinding, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectRoleBindingSpec. +func (in *ObjectRoleBindingSpec) DeepCopy() *ObjectRoleBindingSpec { + if in == nil { + return nil + } + out := new(ObjectRoleBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectRoleBindingStatus) DeepCopyInto(out *ObjectRoleBindingStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectRoleBindingStatus. +func (in *ObjectRoleBindingStatus) DeepCopy() *ObjectRoleBindingStatus { + if in == nil { + return nil + } + out := new(ObjectRoleBindingStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubjectBinding) DeepCopyInto(out *SubjectBinding) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubjectBinding. +func (in *SubjectBinding) DeepCopy() *SubjectBinding { + if in == nil { + return nil + } + out := new(SubjectBinding) + in.DeepCopyInto(out) + return out +}