From f5ce106e4d43b7ccf7fa0d05bfd25123bdb4b67a Mon Sep 17 00:00:00 2001 From: Karol Szwaj Date: Wed, 20 Aug 2025 15:33:57 +0200 Subject: [PATCH] add permission claims to APIServices apis Signed-off-by: Karol Szwaj On-behalf-of: @SAP karol.szwaj@sap.com --- .../crd/kube-bind.io_apiservicebindings.yaml | 223 +++++++++++++++++ ...kube-bind.io_apiserviceexportrequests.yaml | 47 ++++ .../crd/kube-bind.io_apiserviceexports.yaml | 69 +++++- .../resources/apiexport-kube-bind.io.yaml | 6 +- ...chema-apiservicebindings.kube-bind.io.yaml | 225 +++++++++++++++++- ...apiserviceexportrequests.kube-bind.io.yaml | 49 +++- ...schema-apiserviceexports.kube-bind.io.yaml | 71 +++++- .../v1alpha2/apiservicebinding_types.go | 68 +++++- .../v1alpha2/apiserviceexport_types.go | 49 +++- .../v1alpha2/apiserviceexportrequest_types.go | 9 + .../v1alpha2/zz_generated.deepcopy.go | 104 +++++++- 11 files changed, 895 insertions(+), 25 deletions(-) diff --git a/deploy/crd/kube-bind.io_apiservicebindings.yaml b/deploy/crd/kube-bind.io_apiservicebindings.yaml index c68ca4e9f..5faeaa750 100644 --- a/deploy/crd/kube-bind.io_apiservicebindings.yaml +++ b/deploy/crd/kube-bind.io_apiservicebindings.yaml @@ -227,6 +227,115 @@ spec: x-kubernetes-validations: - message: kubeconfigSecretRef is immutable rule: self == oldSelf + permissionClaims: + description: PermissionClaims records decisions about permission claims + requested by the API service provider. + items: + description: |- + ScopedPermissionClaim embeds a PermissionClaim and adds a selector to + scope down access to objects of the claimed resource. + properties: + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + identityHash: + default: "" + description: |- + This is the identity for a given APIExport that the APIResourceSchema belongs to. + The hash can be found on APIExport and APIResourceSchema's status. + It will be empty for core types. + Note that one must look this up for a particular KCP instance. + type: string + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + description: |- + PermissionClaimSelector configures scoped access to objects + of a claimed resource. + properties: + matchAll: + description: matchAll grants access to all objects of the + claimed resource. + type: boolean + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + x-kubernetes-validations: + - message: Permission claim selector is immutable + rule: self == oldSelf + - message: a selector is required. Only "matchAll" is currently + implemented + rule: (has(self.matchAll) && self.matchAll) + verbs: + description: |- + verbs is a list of supported API operation types (this includes + but is not limited to get, list, watch, create, update, patch, + delete, deletecollection, and proxy). + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + required: + - resource + - selector + - verbs + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + - identityHash + x-kubernetes-list-type: map required: - kubeconfigSecretRef type: object @@ -307,6 +416,120 @@ spec: - type type: object type: array + permissionClaims: + description: permissionClaims tracks the status of permission claims + from the provider. + items: + description: AcceptablePermissionClaim is a PermissionClaim that + records if the user accepts or rejects it. + properties: + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + identityHash: + default: "" + description: |- + This is the identity for a given APIExport that the APIResourceSchema belongs to. + The hash can be found on APIExport and APIResourceSchema's status. + It will be empty for core types. + Note that one must look this up for a particular KCP instance. + type: string + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + description: |- + PermissionClaimSelector configures scoped access to objects + of a claimed resource. + properties: + matchAll: + description: matchAll grants access to all objects of the + claimed resource. + type: boolean + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + x-kubernetes-validations: + - message: Permission claim selector is immutable + rule: self == oldSelf + - message: a selector is required. Only "matchAll" is currently + implemented + rule: (has(self.matchAll) && self.matchAll) + state: + enum: + - Accepted + - Rejected + type: string + verbs: + description: |- + verbs is a list of supported API operation types (this includes + but is not limited to get, list, watch, create, update, patch, + delete, deletecollection, and proxy). + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + required: + - resource + - selector + - state + - verbs + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + - identityHash + x-kubernetes-list-type: map providerPrettyName: description: |- providerPrettyName is the pretty name of the service provider cluster. This diff --git a/deploy/crd/kube-bind.io_apiserviceexportrequests.yaml b/deploy/crd/kube-bind.io_apiserviceexportrequests.yaml index b7aaf85c8..097560686 100644 --- a/deploy/crd/kube-bind.io_apiserviceexportrequests.yaml +++ b/deploy/crd/kube-bind.io_apiserviceexportrequests.yaml @@ -221,6 +221,53 @@ spec: x-kubernetes-validations: - message: parameters are immutable rule: self == oldSelf + permissionClaims: + items: + description: |- + PermissionClaim defines a permission that the consumer cluster requests from the provider. + This is compatible with KCP's PermissionClaim structure. + properties: + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + identityHash: + default: "" + description: |- + This is the identity for a given APIExport that the APIResourceSchema belongs to. + The hash can be found on APIExport and APIResourceSchema's status. + It will be empty for core types. + Note that one must look this up for a particular KCP instance. + type: string + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + verbs: + description: |- + verbs is a list of supported API operation types (this includes + but is not limited to get, list, watch, create, update, patch, + delete, deletecollection, and proxy). + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + required: + - resource + - verbs + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + x-kubernetes-list-type: map resources: description: resources is a list of resources that should be exported. items: diff --git a/deploy/crd/kube-bind.io_apiserviceexports.yaml b/deploy/crd/kube-bind.io_apiserviceexports.yaml index 6a0a9eed2..756ee59cf 100644 --- a/deploy/crd/kube-bind.io_apiserviceexports.yaml +++ b/deploy/crd/kube-bind.io_apiserviceexports.yaml @@ -467,13 +467,23 @@ spec: description: spec specifies the resource. properties: clusterScopedIsolation: + allOf: + - enum: + - Prefixed + - Namespaced + - None + - Cluster + - enum: + - Prefixed + - Namespaced + - None + - Cluster description: |- - ClusterScopedIsolation specifies how cluster scoped service objects are isolated between multiple consumers on the provider side. - It can be "Prefixed", "Namespaced", or "None". - enum: - - Prefixed - - Namespaced - - None + ClusterScopedIsolation defines how objects are isolated at the provider side. + If any of the spec.resources are cluster-scoped, they will automatically be cluster-scoped. + In future releases, if policies are implemented, APIServiceExportObject are used + (policy.provider.sync = object) to abstract objects coming from the consumer side - + isolation can be increased, even for cluster-scoped CRDs. type: string informerScope: description: |- @@ -490,6 +500,53 @@ spec: x-kubernetes-validations: - message: informerScope is immutable rule: self == oldSelf + permissionClaims: + items: + description: |- + PermissionClaim defines a permission that the consumer cluster requests from the provider. + This is compatible with KCP's PermissionClaim structure. + properties: + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + identityHash: + default: "" + description: |- + This is the identity for a given APIExport that the APIResourceSchema belongs to. + The hash can be found on APIExport and APIResourceSchema's status. + It will be empty for core types. + Note that one must look this up for a particular KCP instance. + type: string + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + verbs: + description: |- + verbs is a list of supported API operation types (this includes + but is not limited to get, list, watch, create, update, patch, + delete, deletecollection, and proxy). + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + required: + - resource + - verbs + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + x-kubernetes-list-type: map resources: description: resources specifies the API resources to export items: diff --git a/kcp/deploy/resources/apiexport-kube-bind.io.yaml b/kcp/deploy/resources/apiexport-kube-bind.io.yaml index e3f8463c9..611908f35 100644 --- a/kcp/deploy/resources/apiexport-kube-bind.io.yaml +++ b/kcp/deploy/resources/apiexport-kube-bind.io.yaml @@ -50,17 +50,17 @@ spec: crd: {} - group: kube-bind.io name: apiservicebindings - schema: v250809-5ed76a1.apiservicebindings.kube-bind.io + schema: v250820-3baf1a4.apiservicebindings.kube-bind.io storage: crd: {} - group: kube-bind.io name: apiserviceexportrequests - schema: v250809-5ed76a1.apiserviceexportrequests.kube-bind.io + schema: v250820-3baf1a4.apiserviceexportrequests.kube-bind.io storage: crd: {} - group: kube-bind.io name: apiserviceexports - schema: v250809-5ed76a1.apiserviceexports.kube-bind.io + schema: v250820-3baf1a4.apiserviceexports.kube-bind.io storage: crd: {} - group: kube-bind.io diff --git a/kcp/deploy/resources/apiresourceschema-apiservicebindings.kube-bind.io.yaml b/kcp/deploy/resources/apiresourceschema-apiservicebindings.kube-bind.io.yaml index 33d76eda1..bc6ce0f25 100644 --- a/kcp/deploy/resources/apiresourceschema-apiservicebindings.kube-bind.io.yaml +++ b/kcp/deploy/resources/apiresourceschema-apiservicebindings.kube-bind.io.yaml @@ -2,7 +2,7 @@ apiVersion: apis.kcp.io/v1alpha1 kind: APIResourceSchema metadata: creationTimestamp: null - name: v250809-5ed76a1.apiservicebindings.kube-bind.io + name: v250820-3baf1a4.apiservicebindings.kube-bind.io spec: conversion: strategy: None @@ -222,6 +222,115 @@ spec: x-kubernetes-validations: - message: kubeconfigSecretRef is immutable rule: self == oldSelf + permissionClaims: + description: PermissionClaims records decisions about permission claims + requested by the API service provider. + items: + description: |- + ScopedPermissionClaim embeds a PermissionClaim and adds a selector to + scope down access to objects of the claimed resource. + properties: + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + identityHash: + default: "" + description: |- + This is the identity for a given APIExport that the APIResourceSchema belongs to. + The hash can be found on APIExport and APIResourceSchema's status. + It will be empty for core types. + Note that one must look this up for a particular KCP instance. + type: string + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + description: |- + PermissionClaimSelector configures scoped access to objects + of a claimed resource. + properties: + matchAll: + description: matchAll grants access to all objects of the + claimed resource. + type: boolean + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + x-kubernetes-validations: + - message: Permission claim selector is immutable + rule: self == oldSelf + - message: a selector is required. Only "matchAll" is currently + implemented + rule: (has(self.matchAll) && self.matchAll) + verbs: + description: |- + verbs is a list of supported API operation types (this includes + but is not limited to get, list, watch, create, update, patch, + delete, deletecollection, and proxy). + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + required: + - resource + - selector + - verbs + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + - identityHash + x-kubernetes-list-type: map required: - kubeconfigSecretRef type: object @@ -300,6 +409,120 @@ spec: - type type: object type: array + permissionClaims: + description: permissionClaims tracks the status of permission claims + from the provider. + items: + description: AcceptablePermissionClaim is a PermissionClaim that records + if the user accepts or rejects it. + properties: + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + identityHash: + default: "" + description: |- + This is the identity for a given APIExport that the APIResourceSchema belongs to. + The hash can be found on APIExport and APIResourceSchema's status. + It will be empty for core types. + Note that one must look this up for a particular KCP instance. + type: string + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + selector: + description: |- + PermissionClaimSelector configures scoped access to objects + of a claimed resource. + properties: + matchAll: + description: matchAll grants access to all objects of the + claimed resource. + type: boolean + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + x-kubernetes-validations: + - message: Permission claim selector is immutable + rule: self == oldSelf + - message: a selector is required. Only "matchAll" is currently + implemented + rule: (has(self.matchAll) && self.matchAll) + state: + enum: + - Accepted + - Rejected + type: string + verbs: + description: |- + verbs is a list of supported API operation types (this includes + but is not limited to get, list, watch, create, update, patch, + delete, deletecollection, and proxy). + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + required: + - resource + - selector + - state + - verbs + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + - identityHash + x-kubernetes-list-type: map providerPrettyName: description: |- providerPrettyName is the pretty name of the service provider cluster. This diff --git a/kcp/deploy/resources/apiresourceschema-apiserviceexportrequests.kube-bind.io.yaml b/kcp/deploy/resources/apiresourceschema-apiserviceexportrequests.kube-bind.io.yaml index d1d26d448..da15d2859 100644 --- a/kcp/deploy/resources/apiresourceschema-apiserviceexportrequests.kube-bind.io.yaml +++ b/kcp/deploy/resources/apiresourceschema-apiserviceexportrequests.kube-bind.io.yaml @@ -2,7 +2,7 @@ apiVersion: apis.kcp.io/v1alpha1 kind: APIResourceSchema metadata: creationTimestamp: null - name: v250809-5ed76a1.apiserviceexportrequests.kube-bind.io + name: v250820-3baf1a4.apiserviceexportrequests.kube-bind.io spec: conversion: strategy: None @@ -217,6 +217,53 @@ spec: x-kubernetes-validations: - message: parameters are immutable rule: self == oldSelf + permissionClaims: + items: + description: |- + PermissionClaim defines a permission that the consumer cluster requests from the provider. + This is compatible with KCP's PermissionClaim structure. + properties: + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + identityHash: + default: "" + description: |- + This is the identity for a given APIExport that the APIResourceSchema belongs to. + The hash can be found on APIExport and APIResourceSchema's status. + It will be empty for core types. + Note that one must look this up for a particular KCP instance. + type: string + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + verbs: + description: |- + verbs is a list of supported API operation types (this includes + but is not limited to get, list, watch, create, update, patch, + delete, deletecollection, and proxy). + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + required: + - resource + - verbs + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + x-kubernetes-list-type: map resources: description: resources is a list of resources that should be exported. items: diff --git a/kcp/deploy/resources/apiresourceschema-apiserviceexports.kube-bind.io.yaml b/kcp/deploy/resources/apiresourceschema-apiserviceexports.kube-bind.io.yaml index 8eb68ef26..2f8de8609 100644 --- a/kcp/deploy/resources/apiresourceschema-apiserviceexports.kube-bind.io.yaml +++ b/kcp/deploy/resources/apiresourceschema-apiserviceexports.kube-bind.io.yaml @@ -2,7 +2,7 @@ apiVersion: apis.kcp.io/v1alpha1 kind: APIResourceSchema metadata: creationTimestamp: null - name: v250809-5ed76a1.apiserviceexports.kube-bind.io + name: v250820-3baf1a4.apiserviceexports.kube-bind.io spec: conversion: strategy: None @@ -464,13 +464,23 @@ spec: description: spec specifies the resource. properties: clusterScopedIsolation: + allOf: + - enum: + - Prefixed + - Namespaced + - None + - Cluster + - enum: + - Prefixed + - Namespaced + - None + - Cluster description: |- - ClusterScopedIsolation specifies how cluster scoped service objects are isolated between multiple consumers on the provider side. - It can be "Prefixed", "Namespaced", or "None". - enum: - - Prefixed - - Namespaced - - None + ClusterScopedIsolation defines how objects are isolated at the provider side. + If any of the spec.resources are cluster-scoped, they will automatically be cluster-scoped. + In future releases, if policies are implemented, APIServiceExportObject are used + (policy.provider.sync = object) to abstract objects coming from the consumer side - + isolation can be increased, even for cluster-scoped CRDs. type: string informerScope: description: |- @@ -487,6 +497,53 @@ spec: x-kubernetes-validations: - message: informerScope is immutable rule: self == oldSelf + permissionClaims: + items: + description: |- + PermissionClaim defines a permission that the consumer cluster requests from the provider. + This is compatible with KCP's PermissionClaim structure. + properties: + group: + default: "" + description: |- + group is the name of an API group. + For core groups this is the empty string '""'. + pattern: ^(|[a-z0-9]([-a-z0-9]*[a-z0-9](\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)?)$ + type: string + identityHash: + default: "" + description: |- + This is the identity for a given APIExport that the APIResourceSchema belongs to. + The hash can be found on APIExport and APIResourceSchema's status. + It will be empty for core types. + Note that one must look this up for a particular KCP instance. + type: string + resource: + description: |- + resource is the name of the resource. + Note: it is worth noting that you can not ask for permissions for resource provided by a CRD + not provided by an service binding export. + pattern: ^[a-z][-a-z0-9]*[a-z0-9]$ + type: string + verbs: + description: |- + verbs is a list of supported API operation types (this includes + but is not limited to get, list, watch, create, update, patch, + delete, deletecollection, and proxy). + items: + type: string + minItems: 1 + type: array + x-kubernetes-list-type: set + required: + - resource + - verbs + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + x-kubernetes-list-type: map resources: description: resources specifies the API resources to export items: diff --git a/sdk/apis/kubebind/v1alpha2/apiservicebinding_types.go b/sdk/apis/kubebind/v1alpha2/apiservicebinding_types.go index 97cfc0450..cc5b402ee 100644 --- a/sdk/apis/kubebind/v1alpha2/apiservicebinding_types.go +++ b/sdk/apis/kubebind/v1alpha2/apiservicebinding_types.go @@ -88,6 +88,20 @@ type APIServiceBindingSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="kubeconfigSecretRef is immutable" KubeconfigSecretRef ClusterSecretKeyRef `json:"kubeconfigSecretRef"` + + // PermissionClaims records decisions about permission claims requested by the API service provider. + // + // +optional + // +listType=map + // +listMapKey=group + // +listMapKey=resource + // +listMapKey=identityHash + PermissionClaims []ScopedPermissionClaim `json:"permissionClaims,omitempty"` +} + +// BoundSchemaReference contains a reference to a BoundAPIResourceSchema with status information. +type BoundSchemaReference struct { + GroupResource `json:",inline"` } type APIServiceBindingStatus struct { @@ -103,11 +117,59 @@ type APIServiceBindingStatus struct { // +optional // +kubebuilder:validation:MinItems=1 BoundSchemas []BoundSchemaReference `json:"boundSchemas,omitempty"` + + // permissionClaims tracks the status of permission claims from the provider. + // +optional + // +listType=map + // +listMapKey=group + // +listMapKey=resource + // +listMapKey=identityHash + PermissionClaims []AcceptablePermissionClaim `json:"permissionClaims,omitempty"` } -// BoundSchemaReference contains a reference to a BoundAPIResourceSchema with status information. -type BoundSchemaReference struct { - GroupResource `json:",inline"` +// ScopedPermissionClaim embeds a PermissionClaim and adds a selector to +// scope down access to objects of the claimed resource. +type ScopedPermissionClaim struct { + PermissionClaim `json:",inline"` + + // selector configures which objects for the claimed resource + // are made available to the APIExport owner. This field is immutable. + // Only one of matchLabels, matchExpressions or matchAll can be set. + + // +required + // +kubebuilder:validation:Required + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Permission claim selector is immutable" + Selector PermissionClaimSelector `json:"selector"` +} + +// AcceptablePermissionClaim is a PermissionClaim that records if the user accepts or rejects it. +type AcceptablePermissionClaim struct { + ScopedPermissionClaim `json:",inline"` + + // state indicates if the claim is accepted or rejected. + + // +required + // +kubebuilder:validation:Required + // +kubebuilder:validation:Enum=Accepted;Rejected + State AcceptablePermissionClaimState `json:"state"` +} + +type AcceptablePermissionClaimState string + +const ( + ClaimAccepted AcceptablePermissionClaimState = "Accepted" + ClaimRejected AcceptablePermissionClaimState = "Rejected" +) + +// PermissionClaimSelector configures scoped access to objects +// of a claimed resource. +// +// +kubebuilder:validation:XValidation:rule="(has(self.matchAll) && self.matchAll)",message="a selector is required. Only \"matchAll\" is currently implemented" +type PermissionClaimSelector struct { + metav1.LabelSelector `json:",inline"` + + // matchAll grants access to all objects of the claimed resource. + MatchAll bool `json:"matchAll,omitempty"` } // APIServiceBindingList is a list of APIServiceBindings. diff --git a/sdk/apis/kubebind/v1alpha2/apiserviceexport_types.go b/sdk/apis/kubebind/v1alpha2/apiserviceexport_types.go index 2be34e89e..1c3f453e5 100644 --- a/sdk/apis/kubebind/v1alpha2/apiserviceexport_types.go +++ b/sdk/apis/kubebind/v1alpha2/apiserviceexport_types.go @@ -90,9 +90,25 @@ type APIServiceExportSpec struct { // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="informerScope is immutable" InformerScope InformerScope `json:"informerScope"` - // ClusterScopedIsolation specifies how cluster scoped service objects are isolated between multiple consumers on the provider side. - // It can be "Prefixed", "Namespaced", or "None". + // ClusterScopedIsolation defines how objects are isolated at the provider side. + // If any of the spec.resources are cluster-scoped, they will automatically be cluster-scoped. + // In future releases, if policies are implemented, APIServiceExportObject are used + // (policy.provider.sync = object) to abstract objects coming from the consumer side - + // isolation can be increased, even for cluster-scoped CRDs. + // +optional + // +kubebuilder:validation:Enum=Prefixed;Namespaced;None;Cluster ClusterScopedIsolation Isolation `json:"clusterScopedIsolation,omitempty"` + + // permissionClaims defines non-resource (non-crd) based resource and their permission claims. + // The contract implies that the consumer must give access to specified resources. + // To limit the scope on which resource access is granted, resourceSelector[].reference is used + // to identify the object from spec.resources and path inside their object, where object names can be derived from. + + // +optional + // +listType=map + // +listMapKey=group + // +listMapKey=resource + PermissionClaims []PermissionClaim `json:"permissionClaims,omitempty"` } // APIResourceSchemaReference is a list of references to APIResourceSchemas. @@ -111,7 +127,7 @@ type APIResourceSchemaReference struct { // Isolation is an enum defining the different ways to isolate cluster scoped objects // -// +kubebuilder:validation:Enum=Prefixed;Namespaced;None +// +kubebuilder:validation:Enum=Prefixed;Namespaced;None;Cluster type Isolation string const ( @@ -123,8 +139,35 @@ const ( // Used for the case of a dedicated provider where isolation is not necessary. IsolationNone Isolation = "None" + + // Cluster-wide isolation for cluster-scoped resources. + IsolationCluster Isolation = "Cluster" ) +// PermissionClaim defines a permission that the consumer cluster requests from the provider. +// This is compatible with KCP's PermissionClaim structure. +type PermissionClaim struct { + GroupResource `json:",inline"` + + // verbs is a list of supported API operation types (this includes + // but is not limited to get, list, watch, create, update, patch, + // delete, deletecollection, and proxy). + // + // +required + // +listType=set + // +kubebuilder:validation:MinItems=1 + Verbs []string `json:"verbs"` + + // This is the identity for a given APIExport that the APIResourceSchema belongs to. + // The hash can be found on APIExport and APIResourceSchema's status. + // It will be empty for core types. + // Note that one must look this up for a particular KCP instance. + // + // +kubebuilder:default="" + // +optional + IdentityHash string `json:"identityHash,omitempty"` +} + // APIServiceExportStatus stores status information about a APIServiceExport. It // reflects the status of the CRD of the consumer cluster. type APIServiceExportStatus struct { diff --git a/sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go b/sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go index 302dff235..03748f1b6 100644 --- a/sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go +++ b/sdk/apis/kubebind/v1alpha2/apiserviceexportrequest_types.go @@ -104,6 +104,15 @@ type APIServiceExportRequestSpec struct { // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="resources are immutable" Resources []APIServiceExportRequestResource `json:"resources"` + + // permissionClaims defines the permissions that the consumer cluster requests from the provider. + // These permissions are required for the konnector to operate correctly with the exported resources. + + // +optional + // +listType=map + // +listMapKey=group + // +listMapKey=resource + PermissionClaims []PermissionClaim `json:"permissionClaims,omitempty"` } type APIServiceExportRequestResource struct { diff --git a/sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go b/sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go index 4e9a67533..0a84baad6 100644 --- a/sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go +++ b/sdk/apis/kubebind/v1alpha2/zz_generated.deepcopy.go @@ -183,7 +183,7 @@ func (in *APIServiceBinding) DeepCopyInto(out *APIServiceBinding) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) return } @@ -243,6 +243,13 @@ func (in *APIServiceBindingList) DeepCopyObject() runtime.Object { func (in *APIServiceBindingSpec) DeepCopyInto(out *APIServiceBindingSpec) { *out = *in out.KubeconfigSecretRef = in.KubeconfigSecretRef + if in.PermissionClaims != nil { + in, out := &in.PermissionClaims, &out.PermissionClaims + *out = make([]ScopedPermissionClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -271,6 +278,13 @@ func (in *APIServiceBindingStatus) DeepCopyInto(out *APIServiceBindingStatus) { *out = make([]BoundSchemaReference, len(*in)) copy(*out, *in) } + if in.PermissionClaims != nil { + in, out := &in.PermissionClaims, &out.PermissionClaims + *out = make([]AcceptablePermissionClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -471,6 +485,13 @@ func (in *APIServiceExportRequestSpec) DeepCopyInto(out *APIServiceExportRequest (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.PermissionClaims != nil { + in, out := &in.PermissionClaims, &out.PermissionClaims + *out = make([]PermissionClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -515,6 +536,13 @@ func (in *APIServiceExportSpec) DeepCopyInto(out *APIServiceExportSpec) { *out = make([]APIResourceSchemaReference, len(*in)) copy(*out, *in) } + if in.PermissionClaims != nil { + in, out := &in.PermissionClaims, &out.PermissionClaims + *out = make([]PermissionClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -650,6 +678,23 @@ func (in *APIServiceNamespaceStatus) DeepCopy() *APIServiceNamespaceStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AcceptablePermissionClaim) DeepCopyInto(out *AcceptablePermissionClaim) { + *out = *in + in.ScopedPermissionClaim.DeepCopyInto(&out.ScopedPermissionClaim) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AcceptablePermissionClaim. +func (in *AcceptablePermissionClaim) DeepCopy() *AcceptablePermissionClaim { + if in == nil { + return nil + } + out := new(AcceptablePermissionClaim) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AuthenticationMethod) DeepCopyInto(out *AuthenticationMethod) { *out = *in @@ -1125,6 +1170,63 @@ func (in *OAuth2CodeGrant) DeepCopy() *OAuth2CodeGrant { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PermissionClaim) DeepCopyInto(out *PermissionClaim) { + *out = *in + out.GroupResource = in.GroupResource + if in.Verbs != nil { + in, out := &in.Verbs, &out.Verbs + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PermissionClaim. +func (in *PermissionClaim) DeepCopy() *PermissionClaim { + if in == nil { + return nil + } + out := new(PermissionClaim) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PermissionClaimSelector) DeepCopyInto(out *PermissionClaimSelector) { + *out = *in + in.LabelSelector.DeepCopyInto(&out.LabelSelector) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PermissionClaimSelector. +func (in *PermissionClaimSelector) DeepCopy() *PermissionClaimSelector { + if in == nil { + return nil + } + out := new(PermissionClaimSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScopedPermissionClaim) DeepCopyInto(out *ScopedPermissionClaim) { + *out = *in + in.PermissionClaim.DeepCopyInto(&out.PermissionClaim) + in.Selector.DeepCopyInto(&out.Selector) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopedPermissionClaim. +func (in *ScopedPermissionClaim) DeepCopy() *ScopedPermissionClaim { + if in == nil { + return nil + } + out := new(ScopedPermissionClaim) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WebhookClientConfig) DeepCopyInto(out *WebhookClientConfig) { *out = *in