diff --git a/cmd/ks-apiserver/app/options/options.go b/cmd/ks-apiserver/app/options/options.go index 855558ad3..f08390a09 100644 --- a/cmd/ks-apiserver/app/options/options.go +++ b/cmd/ks-apiserver/app/options/options.go @@ -64,6 +64,7 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) { s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions) s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions) s.AuthenticationOptions.AddFlags(fss.FlagSet("authentication"), s.AuthenticationOptions) + s.AuthorizationOptions.AddFlags(fss.FlagSet("authorization"), s.AuthorizationOptions) s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions) s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube"), s.SonarQubeOptions) s.LdapOptions.AddFlags(fss.FlagSet("ldap"), s.LdapOptions) diff --git a/cmd/ks-apiserver/app/options/validation.go b/cmd/ks-apiserver/app/options/validation.go index 17b8c9fb5..b4627f1a8 100644 --- a/cmd/ks-apiserver/app/options/validation.go +++ b/cmd/ks-apiserver/app/options/validation.go @@ -15,6 +15,7 @@ func (s *ServerRunOptions) Validate() []error { errors = append(errors, s.OpenPitrixOptions.Validate()...) errors = append(errors, s.NetworkOptions.Validate()...) errors = append(errors, s.LoggingOptions.Validate()...) + errors = append(errors, s.AuthorizationOptions.Validate()...) return errors } diff --git a/config/crds/iam.kubesphere.io_rolebindings.yaml b/config/crds/iam.kubesphere.io_globalrolebindings.yaml similarity index 70% rename from config/crds/iam.kubesphere.io_rolebindings.yaml rename to config/crds/iam.kubesphere.io_globalrolebindings.yaml index ede871b22..1884d5a62 100644 --- a/config/crds/iam.kubesphere.io_rolebindings.yaml +++ b/config/crds/iam.kubesphere.io_globalrolebindings.yaml @@ -6,28 +6,17 @@ metadata: annotations: controller-gen.kubebuilder.io/version: (devel) creationTimestamp: null - name: rolebindings.iam.kubesphere.io + name: globalrolebindings.iam.kubesphere.io spec: - additionalPrinterColumns: - - JSONPath: .scope - name: Scope - type: string - - JSONPath: .roleRef.name - name: RoleRef - type: string - - JSONPath: .subjects[*].name - name: Subjects - type: string group: iam.kubesphere.io names: categories: - iam - kind: RoleBinding - listKind: RoleBindingList - plural: rolebindings - singular: rolebinding + kind: GlobalRoleBinding + listKind: GlobalRoleBindingList + plural: globalrolebindings + singular: globalrolebinding scope: Cluster - subresources: {} validation: openAPIV3Schema: description: RoleBinding is the Schema for the rolebindings API @@ -43,10 +32,11 @@ spec: submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: + description: Standard object's metadata. type: object roleRef: - description: RoleRef contains information that points to the role being - used + description: RoleRef can only reference a ClusterRole in the global namespace. + If the RoleRef cannot be resolved, the Authorizer must return an error. properties: apiGroup: description: APIGroup is the group for the resource being referenced @@ -62,15 +52,17 @@ spec: - kind - name type: object - scope: - type: string subjects: - description: Subjects holds references to the users the role applies to. + description: Subjects holds references to the objects the role applies to. items: - description: or a value for non-objects such as user and group names. + description: Subject contains a reference to the object or user identities + a role binding applies to. This can either hold a direct API object + reference, or a value for non-objects such as user and group names. properties: apiGroup: description: APIGroup holds the API group of the referenced subject. + Defaults to "" for ServiceAccount subjects. Defaults to "rbac.authorization.k8s.io" + for User and Group subjects. type: string kind: description: Kind of object being referenced. Values defined by this @@ -81,15 +73,18 @@ spec: name: description: Name of the object being referenced. type: string + namespace: + description: Namespace of the referenced object. If the object kind + is non-namespace, such as "User" or "Group", and this value is not + empty the Authorizer should report an error. + type: string required: - - apiGroup - kind - name type: object type: array required: - roleRef - - scope type: object version: v1alpha2 versions: diff --git a/config/crds/iam.kubesphere.io_globalroles.yaml b/config/crds/iam.kubesphere.io_globalroles.yaml new file mode 100644 index 000000000..480516a60 --- /dev/null +++ b/config/crds/iam.kubesphere.io_globalroles.yaml @@ -0,0 +1,156 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: globalroles.iam.kubesphere.io +spec: + group: iam.kubesphere.io + names: + categories: + - iam + kind: GlobalRole + listKind: GlobalRoleList + plural: globalroles + singular: globalrole + scope: Cluster + validation: + openAPIV3Schema: + properties: + aggregationRule: + description: AggregationRule is an optional field that describes how to + build the Rules for this GlobalRole. If AggregationRule is set, then the + Rules are controller managed and direct changes to Rules will be stomped + by the controller. + properties: + roleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will + be used to find ClusterRoles and create the rules. If any of the selectors + match, then the ClusterRole's permissions will be added + items: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + 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 + required: + - key + - operator + type: object + type: array + 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 + type: array + type: object + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: Standard object's metadata. + type: object + rules: + description: Rules holds all the PolicyRules for this ClusterRole + items: + description: PolicyRule holds information that describes a policy rule, + but does not contain information about who the rule applies to or which + namespace the rule applies to. + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the + resources. If multiple API groups are specified, any action requested + against one of the enumerated resources in any API group will be + allowed. + items: + type: string + type: array + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user + should have access to. *s are allowed, but only as the full, final + step in the path Since non-resource URLs are not namespaced, this + field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. + Rules can either apply to API resources (such as "pods" or "secrets") + or non-resource URL paths (such as "/api"), but not both. + items: + type: string + type: array + resourceNames: + description: ResourceNames is an optional white list of names that + the rule applies to. An empty set means that everything is allowed. + items: + type: string + type: array + resources: + description: Resources is a list of resources this rule applies to. ResourceAll + represents all resources. + items: + type: string + type: array + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds + and AttributeRestrictions contained in this rule. VerbAll represents + all kinds. + items: + type: string + type: array + required: + - verbs + type: object + type: array + required: + - rules + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_policyrules.yaml b/config/crds/iam.kubesphere.io_policyrules.yaml deleted file mode 100644 index c0eb6ed9c..000000000 --- a/config/crds/iam.kubesphere.io_policyrules.yaml +++ /dev/null @@ -1,58 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: (devel) - creationTimestamp: null - name: policyrules.iam.kubesphere.io -spec: - additionalPrinterColumns: - - JSONPath: .scope - name: Scope - type: string - group: iam.kubesphere.io - names: - categories: - - iam - kind: PolicyRule - listKind: PolicyRuleList - plural: policyrules - singular: policyrule - scope: Cluster - subresources: {} - validation: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - rego: - type: string - scope: - type: string - required: - - rego - - scope - type: object - version: v1alpha2 - versions: - - name: v1alpha2 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_roles.yaml b/config/crds/iam.kubesphere.io_roles.yaml deleted file mode 100644 index 91b01e128..000000000 --- a/config/crds/iam.kubesphere.io_roles.yaml +++ /dev/null @@ -1,87 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: (devel) - creationTimestamp: null - name: roles.iam.kubesphere.io -spec: - additionalPrinterColumns: - - JSONPath: .target.scope - name: Scope - type: string - - JSONPath: .target.name - name: Target - type: string - group: iam.kubesphere.io - names: - categories: - - iam - kind: Role - listKind: RoleList - plural: roles - singular: role - scope: Cluster - subresources: {} - validation: - openAPIV3Schema: - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - rules: - items: - description: RuleRef contains information that points to the role being - used - properties: - apiGroup: - description: APIGroup is the group for the resource being referenced - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - apiGroup - - kind - - name - type: object - type: array - target: - properties: - name: - type: string - scope: - type: string - required: - - name - - scope - type: object - required: - - rules - - target - type: object - version: v1alpha2 - versions: - - name: v1alpha2 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_users.yaml b/config/crds/iam.kubesphere.io_users.yaml index eec35e012..ce60e7e35 100644 --- a/config/crds/iam.kubesphere.io_users.yaml +++ b/config/crds/iam.kubesphere.io_users.yaml @@ -40,6 +40,7 @@ spec: submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: + description: Standard object's metadata. type: object spec: description: UserSpec defines the desired state of User @@ -66,6 +67,7 @@ spec: description: The preferred written or spoken language for the user. type: string password: + description: password will be encrypted by mutating admission webhook type: string required: - email diff --git a/config/crds/iam.kubesphere.io_workspacerolebindings.yaml b/config/crds/iam.kubesphere.io_workspacerolebindings.yaml new file mode 100644 index 000000000..c06e75f2f --- /dev/null +++ b/config/crds/iam.kubesphere.io_workspacerolebindings.yaml @@ -0,0 +1,103 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: workspacerolebindings.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .metadata.labels.kubesphere\.io/workspace + name: Workspace + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: WorkspaceRoleBinding + listKind: WorkspaceRoleBindingList + plural: workspacerolebindings + singular: workspacerolebinding + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: RoleBinding is the Schema for the rolebindings API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + roleRef: + description: RoleRef can only reference a ClusterRole in the global namespace. + If the RoleRef cannot be resolved, the Authorizer must return an error. + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - apiGroup + - kind + - name + type: object + subjects: + description: Subjects holds references to the objects the role applies to. + items: + description: Subject contains a reference to the object or user identities + a role binding applies to. This can either hold a direct API object + reference, or a value for non-objects such as user and group names. + properties: + apiGroup: + description: APIGroup holds the API group of the referenced subject. + Defaults to "" for ServiceAccount subjects. Defaults to "rbac.authorization.k8s.io" + for User and Group subjects. + type: string + kind: + description: Kind of object being referenced. Values defined by this + API group are "User", "Group", and "ServiceAccount". If the Authorizer + does not recognized the kind value, the Authorizer should report + an error. + type: string + name: + description: Name of the object being referenced. + type: string + namespace: + description: Namespace of the referenced object. If the object kind + is non-namespace, such as "User" or "Group", and this value is not + empty the Authorizer should report an error. + type: string + required: + - kind + - name + type: object + type: array + required: + - roleRef + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_workspaceroles.yaml b/config/crds/iam.kubesphere.io_workspaceroles.yaml new file mode 100644 index 000000000..09d8b0b7d --- /dev/null +++ b/config/crds/iam.kubesphere.io_workspaceroles.yaml @@ -0,0 +1,164 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: workspaceroles.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .metadata.labels.kubesphere\.io/workspace + name: Workspace + type: string + - JSONPath: .metadata.labels.kubesphere\.io/alias-name + name: Alias + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: WorkspaceRole + listKind: WorkspaceRoleList + plural: workspaceroles + singular: workspacerole + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + properties: + aggregationRule: + description: AggregationRule is an optional field that describes how to + build the Rules for this WorkspaceRole. If AggregationRule is set, then + the Rules are controller managed and direct changes to Rules will be stomped + by the controller. + properties: + roleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will + be used to find ClusterRoles and create the rules. If any of the selectors + match, then the ClusterRole's permissions will be added + items: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + 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 + required: + - key + - operator + type: object + type: array + 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 + type: array + type: object + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: Standard object's metadata. + type: object + rules: + description: Rules holds all the PolicyRules for this ClusterRole + items: + description: PolicyRule holds information that describes a policy rule, + but does not contain information about who the rule applies to or which + namespace the rule applies to. + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the + resources. If multiple API groups are specified, any action requested + against one of the enumerated resources in any API group will be + allowed. + items: + type: string + type: array + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user + should have access to. *s are allowed, but only as the full, final + step in the path Since non-resource URLs are not namespaced, this + field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. + Rules can either apply to API resources (such as "pods" or "secrets") + or non-resource URL paths (such as "/api"), but not both. + items: + type: string + type: array + resourceNames: + description: ResourceNames is an optional white list of names that + the rule applies to. An empty set means that everything is allowed. + items: + type: string + type: array + resources: + description: Resources is a list of resources this rule applies to. ResourceAll + represents all resources. + items: + type: string + type: array + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds + and AttributeRestrictions contained in this rule. VerbAll represents + all kinds. + items: + type: string + type: array + required: + - verbs + type: object + type: array + required: + - rules + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/samples/iam_v1alpha2_globalrole.yaml b/config/samples/iam_v1alpha2_globalrole.yaml new file mode 100644 index 000000000..268374d06 --- /dev/null +++ b/config/samples/iam_v1alpha2_globalrole.yaml @@ -0,0 +1,14 @@ +apiVersion: iam.kubesphere.io/v1alpha2 +kind: GlobalRole +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: global-admin +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' + diff --git a/config/samples/iam_v1alpha2_rolebinding.yaml b/config/samples/iam_v1alpha2_globalrolebinding.yaml similarity index 64% rename from config/samples/iam_v1alpha2_rolebinding.yaml rename to config/samples/iam_v1alpha2_globalrolebinding.yaml index 3caf4fa87..20197d15a 100644 --- a/config/samples/iam_v1alpha2_rolebinding.yaml +++ b/config/samples/iam_v1alpha2_globalrolebinding.yaml @@ -1,14 +1,14 @@ apiVersion: iam.kubesphere.io/v1alpha2 -kind: RoleBinding +kind: GlobalRoleBinding metadata: labels: controller-tools.k8s.io: "1.0" - name: cluster-admin -scope: Global + iam.kubesphere.io/single-user-bind: admin + name: admin roleRef: apiGroup: iam.kubesphere.io/v1alpha2 - kind: Role - name: cluster-admin + kind: GlobalRole + name: global-admin subjects: - apiGroup: iam.kubesphere.io/v1alpha2 kind: User diff --git a/config/samples/iam_v1alpha2_policyrule.yaml b/config/samples/iam_v1alpha2_policyrule.yaml deleted file mode 100644 index d22ad3e5b..000000000 --- a/config/samples/iam_v1alpha2_policyrule.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: iam.kubesphere.io/v1alpha2 -kind: PolicyRule -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: always-allow -scope: Global -rego: 'package authz\ndefault allow = true' - ---- - -apiVersion: iam.kubesphere.io/v1alpha2 -kind: PolicyRule -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: always-deny -scope: Global -rego: | - package authz - default allow = false - ---- - -apiVersion: iam.kubesphere.io/v1alpha2 -kind: PolicyRule -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: cluster-manage -scope: Global -rego: | - package authz - default allow = false - allow { - input.Resource == 'clusters' - } - ---- - -apiVersion: iam.kubesphere.io/v1alpha2 -kind: PolicyRule -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: some-namespace-manage -scope: Namespace -rego: | - package authz - default allow = false - allow { - input.Resource == 'clusters' - } - - diff --git a/config/samples/iam_v1alpha2_role.yaml b/config/samples/iam_v1alpha2_role.yaml deleted file mode 100644 index a85cad08d..000000000 --- a/config/samples/iam_v1alpha2_role.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: iam.kubesphere.io/v1alpha2 -kind: Role -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: cluster-admin -target: - scope: Global - name: '' -rules: - - apiGroup: iam.kubesphere.io/v1alpha2 - kind: PolicyRule - name: always-allow - ---- - -apiVersion: iam.kubesphere.io/v1alpha2 -kind: Role -metadata: - labels: - controller-tools.k8s.io: "1.0" - name: anonymous -target: - scope: Global - name: '' -rules: - - apiGroup: iam.kubesphere.io/v1alpha2 - kind: PolicyRule - name: always-deny - diff --git a/go.mod b/go.mod index 27d2cccda..1d242ff55 100644 --- a/go.mod +++ b/go.mod @@ -83,7 +83,7 @@ require ( github.com/syndtr/goleveldb v1.0.0 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 - golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 + golang.org/x/net v0.0.0-20190923162816-aa69164e4478 google.golang.org/grpc v1.23.1 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/go-playground/validator.v9 v9.29.1 // indirect @@ -142,9 +142,7 @@ replace ( github.com/bitly/go-simplejson => github.com/bitly/go-simplejson v0.5.0 github.com/blang/semver => github.com/blang/semver v3.5.0+incompatible github.com/bmizerany/assert => github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 - github.com/cenkalti/backoff => github.com/cenkalti/backoff v2.2.1+incompatible github.com/cespare/xxhash => github.com/cespare/xxhash v1.1.0 - github.com/cheekybits/genny => github.com/cheekybits/genny v1.0.0 github.com/client9/misspell => github.com/client9/misspell v0.3.4 github.com/coreos/bbolt => github.com/coreos/bbolt v1.3.3 github.com/coreos/etcd => github.com/coreos/etcd v3.3.17+incompatible @@ -157,14 +155,12 @@ replace ( github.com/deckarep/golang-set => github.com/deckarep/golang-set v1.7.1 github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289 github.com/dgrijalva/jwt-go => github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/dgryski/go-sip13 => github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 github.com/docker/distribution => github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 github.com/docker/go-connections => github.com/docker/go-connections v0.3.0 github.com/docker/go-units => github.com/docker/go-units v0.3.3 github.com/docker/spdystream => github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c github.com/docopt/docopt-go => github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 - github.com/dustin/go-humanize => github.com/dustin/go-humanize v1.0.0 github.com/elastic/go-elasticsearch/v5 => github.com/elastic/go-elasticsearch/v5 v5.6.1 github.com/elastic/go-elasticsearch/v6 => github.com/elastic/go-elasticsearch/v6 v6.8.2 github.com/elastic/go-elasticsearch/v7 => github.com/elastic/go-elasticsearch/v7 v7.3.0 @@ -183,7 +179,6 @@ replace ( github.com/ghodss/yaml => github.com/ghodss/yaml v1.0.0 github.com/gliderlabs/ssh => github.com/gliderlabs/ssh v0.1.1 github.com/globalsign/mgo => github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 - github.com/go-acme/lego => github.com/go-acme/lego v2.5.0+incompatible github.com/go-kit/kit => github.com/go-kit/kit v0.8.0 github.com/go-ldap/ldap => github.com/go-ldap/ldap v3.0.3+incompatible github.com/go-logfmt/logfmt => github.com/go-logfmt/logfmt v0.4.0 @@ -233,7 +228,6 @@ replace ( github.com/grpc-ecosystem/go-grpc-middleware => github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 github.com/grpc-ecosystem/go-grpc-prometheus => github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.9.6 - github.com/hashicorp/go-syslog => github.com/hashicorp/go-syslog v1.0.0 github.com/hashicorp/go-version => github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/golang-lru => github.com/hashicorp/golang-lru v0.5.3 github.com/hashicorp/hcl => github.com/hashicorp/hcl v1.0.0 @@ -242,7 +236,6 @@ replace ( github.com/inconshreveable/mousetrap => github.com/inconshreveable/mousetrap v1.0.0 github.com/jbenet/go-context => github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jessevdk/go-flags => github.com/jessevdk/go-flags v1.4.0 - github.com/jimstudt/http-authentication => github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a github.com/jinzhu/gorm => github.com/jinzhu/gorm v1.9.2 github.com/jinzhu/inflection => github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a github.com/jinzhu/now => github.com/jinzhu/now v1.0.0 @@ -258,7 +251,6 @@ replace ( github.com/kiali/kiali => github.com/kubesphere/kiali v0.15.1-0.20191210080139-edbbad1ef779 github.com/kisielk/errcheck => github.com/kisielk/errcheck v1.2.0 github.com/kisielk/gotool => github.com/kisielk/gotool v1.0.0 - github.com/klauspost/cpuid => github.com/klauspost/cpuid v1.2.1 github.com/koding/multiconfig => github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 github.com/konsorten/go-windows-terminal-sequences => github.com/konsorten/go-windows-terminal-sequences v1.0.2 github.com/kr/logfmt => github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 @@ -266,23 +258,16 @@ replace ( github.com/kr/pty => github.com/kr/pty v1.1.5 github.com/kr/text => github.com/kr/text v0.1.0 github.com/kubernetes-sigs/application => github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4 - github.com/kubesphere/s2ioperator => github.com/kubesphere/s2ioperator v0.0.14 github.com/kubesphere/sonargo => github.com/kubesphere/sonargo v0.0.2 - github.com/kylelemons/godebug => github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 github.com/leodido/go-urn => github.com/leodido/go-urn v1.1.0 github.com/lib/pq => github.com/lib/pq v1.2.0 - github.com/lucas-clemente/quic-go => github.com/lucas-clemente/quic-go v0.11.1 github.com/magiconair/properties => github.com/magiconair/properties v1.8.0 github.com/mailru/easyjson => github.com/mailru/easyjson v0.7.0 - github.com/marten-seemann/qtls => github.com/marten-seemann/qtls v0.2.3 github.com/mattn/go-colorable => github.com/mattn/go-colorable v0.1.2 github.com/mattn/go-isatty => github.com/mattn/go-isatty v0.0.8 github.com/mattn/go-runewidth => github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39 github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.11.0 github.com/matttproud/golang_protobuf_extensions => github.com/matttproud/golang_protobuf_extensions v1.0.1 - github.com/mholt/caddy => github.com/mholt/caddy v1.0.0 - github.com/mholt/certmagic => github.com/mholt/certmagic v0.5.1 - github.com/miekg/dns => github.com/miekg/dns v1.1.9 github.com/mitchellh/go-homedir => github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure => github.com/mitchellh/mapstructure v1.1.2 github.com/mna/pigeon => github.com/mna/pigeon v0.0.0-20180808201053-bb0192cfc2ae @@ -292,9 +277,6 @@ replace ( github.com/munnerz/goautoneg => github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d github.com/mwitkow/go-conntrack => github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 github.com/mxk/go-flowrate => github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f - github.com/naoina/go-stringutil => github.com/naoina/go-stringutil v0.1.0 - github.com/naoina/toml => github.com/naoina/toml v0.1.1 - github.com/oklog/ulid => github.com/oklog/ulid v1.3.1 github.com/olekukonko/tablewriter => github.com/olekukonko/tablewriter v0.0.1 github.com/onsi/ginkgo => github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega => github.com/onsi/gomega v1.5.0 @@ -302,7 +284,6 @@ replace ( github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.1 github.com/openshift/api => github.com/openshift/api v0.0.0-20180801171038-322a19404e37 - github.com/openshift/build-machinery-go => github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160 github.com/opentracing/opentracing-go => github.com/opentracing/opentracing-go v1.1.0 github.com/pborman/uuid => github.com/pborman/uuid v1.2.0 github.com/pelletier/go-buffruneio => github.com/pelletier/go-buffruneio v0.2.0 @@ -323,7 +304,6 @@ replace ( github.com/prometheus/common => github.com/prometheus/common v0.4.0 github.com/prometheus/procfs => github.com/prometheus/procfs v0.0.2 github.com/prometheus/prometheus => github.com/prometheus/prometheus v1.8.2 - github.com/prometheus/tsdb => github.com/prometheus/tsdb v0.7.1 github.com/rcrowley/go-metrics => github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a github.com/remyoudompheng/bigfft => github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446 github.com/rogpeppe/fastuuid => github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af @@ -389,7 +369,6 @@ replace ( gopkg.in/go-playground/assert.v1 => gopkg.in/go-playground/assert.v1 v1.2.1 gopkg.in/go-playground/validator.v9 => gopkg.in/go-playground/validator.v9 v9.29.1 gopkg.in/inf.v0 => gopkg.in/inf.v0 v0.9.1 - gopkg.in/mcuadros/go-syslog.v2 => gopkg.in/mcuadros/go-syslog.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 => gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/resty.v1 => gopkg.in/resty.v1 v1.12.0 gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.3.1 diff --git a/pkg/apis/iam/v1alpha2/register.go b/pkg/apis/iam/v1alpha2/register.go index 69c539720..ddb23db84 100644 --- a/pkg/apis/iam/v1alpha2/register.go +++ b/pkg/apis/iam/v1alpha2/register.go @@ -51,12 +51,14 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &User{}, &UserList{}, - &Role{}, - &RoleList{}, - &RoleBinding{}, - &RoleBindingList{}, - &PolicyRule{}, - &PolicyRuleList{}, + &GlobalRole{}, + &GlobalRoleList{}, + &GlobalRoleBinding{}, + &GlobalRoleBindingList{}, + &WorkspaceRole{}, + &WorkspaceRoleList{}, + &WorkspaceRoleBinding{}, + &WorkspaceRoleBindingList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/pkg/apis/iam/v1alpha2/types.go b/pkg/apis/iam/v1alpha2/types.go index 872bac1c8..16f93a51c 100644 --- a/pkg/apis/iam/v1alpha2/types.go +++ b/pkg/apis/iam/v1alpha2/types.go @@ -17,9 +17,33 @@ limitations under the License. package v1alpha2 import ( + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const ( + ResourceKindUser = "User" + ResourcesSingularUser = "user" + ResourcesPluralUser = "users" + ResourceKindGlobalRoleBinding = "GlobalRoleBinding" + ResourcesSingularGlobalRoleBinding = "globalrolebinding" + ResourcesPluralGlobalRoleBinding = "globalrolebindings" + ResourceKindGlobalRole = "GlobalRole" + ResourcesSingularGlobalRole = "globalrole" + ResourcesPluralGlobalRole = "globalroles" + ResourceKindWorkspaceRoleBinding = "WorkspaceRoleBinding" + ResourcesSingularWorkspaceRoleBinding = "workspacerolebinding" + ResourcesPluralWorkspaceRoleBinding = "workspacerolebindings" + ResourceKindWorkspaceRole = "WorkspaceRole" + ResourcesSingularWorkspaceRole = "workspacerole" + ResourcesPluralWorkspaceRole = "workspaceroles" + RegoOverrideAnnotation = "iam.kubesphere.io/rego-override" + GlobalScope = "Global" + ClusterScope = "Cluster" + WorkspaceScope = "Workspace" + NamespaceScope = "Namespace" +) + // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -30,7 +54,9 @@ import ( // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.state" // +kubebuilder:resource:categories="iam",scope="Cluster" type User struct { - metav1.TypeMeta `json:",inline"` + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional metav1.ObjectMeta `json:"metadata,omitempty"` Spec UserSpec `json:"spec"` @@ -53,9 +79,9 @@ type UserSpec struct { // +optional DisplayName string `json:"displayName,omitempty"` // +optional - Groups []string `json:"groups,omitempty"` - EncryptedPassword string `json:"password"` - + Groups []string `json:"groups,omitempty"` + // password will be encrypted by mutating admission webhook + EncryptedPassword string `json:"password"` // Finalizers is an opaque list of values that must be empty to permanently remove object from storage. // +optional Finalizers []FinalizerName `json:"finalizers,omitempty"` @@ -102,7 +128,7 @@ type UserConditionType string // These are valid conditions of a user. const ( // UserLoginFailure contains information about user login. - UserLoginFailure UserConditionType = "UserLoginFailure" + LoginFailure UserConditionType = "LoginFailure" ) type ConditionStatus string @@ -121,6 +147,8 @@ const ( // UserList contains a list of User type UserList struct { metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional metav1.ListMeta `json:"metadata,omitempty"` Items []User `json:"items"` } @@ -129,128 +157,131 @@ type UserList struct { // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:printcolumn:name="Scope",type="string",JSONPath=".target.scope" -// +kubebuilder:printcolumn:name="Target",type="string",JSONPath=".target.name" // +kubebuilder:resource:categories="iam",scope="Cluster" -type Role struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Target Target `json:"target"` - Rules []RuleRef `json:"rules"` -} - -type Target struct { - Scope Scope `json:"scope"` - Name string `json:"name"` -} - -type Scope string - -const ( - GlobalScope Scope = "Global" - ClusterScope Scope = "Cluster" - WorkspaceScope Scope = "Workspace" - NamespaceScope Scope = "Namespace" - UserKind = "User" - PolicyRuleKind = "PolicyRule" - RoleKind = "Role" - RoleBindingKind = "RoleBinding" -) - -// RuleRef contains information that points to the role being used -type RuleRef struct { - // APIGroup is the group for the resource being referenced - APIGroup string `json:"apiGroup"` - // Kind is the type of resource being referenced - Kind string `json:"kind"` - // Name is the name of resource being referenced - Name string `json:"name"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// RoleList contains a list of Role -type RoleList struct { +type GlobalRole struct { metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Role `json:"items"` -} - -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// +kubebuilder:printcolumn:name="Scope",type="string",JSONPath=".scope" -// +kubebuilder:resource:categories="iam",scope="Cluster" -type PolicyRule struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Scope Scope `json:"scope"` - Rego string `json:"rego"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PolicyRuleList contains a list of PolicyRule -type PolicyRuleList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []PolicyRule `json:"items"` -} - -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// RoleBinding is the Schema for the rolebindings API -// +kubebuilder:printcolumn:name="Scope",type="string",JSONPath=".scope" -// +kubebuilder:printcolumn:name="RoleRef",type="string",JSONPath=".roleRef.name" -// +kubebuilder:printcolumn:name="Subjects",type="string",JSONPath=".subjects[*].name" -// +kubebuilder:resource:categories="iam",scope="Cluster" -type RoleBinding struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Scope Scope `json:"scope"` - RoleRef RoleRef `json:"roleRef"` - // Subjects holds references to the users the role applies to. + // Standard object's metadata. // +optional - Subjects []Subject `json:"subjects,omitempty"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Rules holds all the PolicyRules for this ClusterRole + Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` + + // AggregationRule is an optional field that describes how to build the Rules for this GlobalRole. + // If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be + // stomped by the controller. + AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"` } -// RoleRef contains information that points to the role being used -type RoleRef struct { - // APIGroup is the group for the resource being referenced - APIGroup string `json:"apiGroup"` - // Kind is the type of resource being referenced - Kind string `json:"kind"` - // Name is the name of resource being referenced - Name string `json:"name"` -} - -// or a value for non-objects such as user and group names. -type Subject struct { - // Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount". - // If the Authorizer does not recognized the kind value, the Authorizer should report an error. - Kind string `json:"kind"` - // APIGroup holds the API group of the referenced subject. - APIGroup string `json:"apiGroup"` - // Name of the object being referenced. - Name string `json:"name"` +// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole +type AggregationRule struct { + // ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. + // If any of the selectors match, then the ClusterRole's permissions will be added + // +optional + RoleSelectors []metav1.LabelSelector `json:"roleSelectors,omitempty" protobuf:"bytes,1,rep,name=roleSelectors"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// RoleBindingList contains a list of RoleBinding -type RoleBindingList struct { +// GlobalRoleList contains a list of GlobalRole +type GlobalRoleList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []RoleBinding `json:"items"` + Items []GlobalRole `json:"items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GlobalRoleBinding is the Schema for the globalrolebindings API +// +kubebuilder:resource:categories="iam",scope="Cluster" +type GlobalRoleBinding struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Subjects holds references to the objects the role applies to. + // +optional + Subjects []rbacv1.Subject `json:"subjects,omitempty" protobuf:"bytes,2,rep,name=subjects"` + + // RoleRef can only reference a ClusterRole in the global namespace. + // If the RoleRef cannot be resolved, the Authorizer must return an error. + RoleRef rbacv1.RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GlobalRoleBindingList contains a list of GlobalRoleBinding +type GlobalRoleBindingList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []GlobalRoleBinding `json:"items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// +kubebuilder:printcolumn:name="Workspace",type="string",JSONPath=".metadata.labels.kubesphere\\.io/workspace" +// +kubebuilder:printcolumn:name="Alias",type="string",JSONPath=".metadata.labels.kubesphere\\.io/alias-name" +// +kubebuilder:resource:categories="iam",scope="Cluster" +type WorkspaceRole struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Rules holds all the PolicyRules for this ClusterRole + Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` + // AggregationRule is an optional field that describes how to build the Rules for this WorkspaceRole. + // If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be + // stomped by the controller. + AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// WorkspaceRoleList contains a list of WorkspaceRole +type WorkspaceRoleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []WorkspaceRole `json:"items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// WorkspaceRoleBinding is the Schema for the workspacerolebindings API +// +kubebuilder:printcolumn:name="Workspace",type="string",JSONPath=".metadata.labels.kubesphere\\.io/workspace" +// +kubebuilder:resource:categories="iam",scope="Cluster" +type WorkspaceRoleBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Subjects holds references to the objects the role applies to. + // +optional + Subjects []rbacv1.Subject `json:"subjects,omitempty" protobuf:"bytes,2,rep,name=subjects"` + + // RoleRef can only reference a ClusterRole in the global namespace. + // If the RoleRef cannot be resolved, the Authorizer must return an error. + RoleRef rbacv1.RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// WorkspaceRoleBindingList contains a list of WorkspaceRoleBinding +type WorkspaceRoleBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []WorkspaceRoleBinding `json:"items"` } type UserDetail struct { *User - GlobalRole *Role `json:"globalRole"` + GlobalRole *GlobalRole `json:"globalRole"` } diff --git a/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go index ba1dbd06e..8b27972de 100644 --- a/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go @@ -16,96 +16,69 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Code generated by deepcopy-gen. DO NOT EDIT. +// Code generated by controller-gen. DO NOT EDIT. package v1alpha2 import ( + "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PolicyRule) DeepCopyInto(out *PolicyRule) { +func (in *AggregationRule) DeepCopyInto(out *AggregationRule) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyRule. -func (in *PolicyRule) DeepCopy() *PolicyRule { - if in == nil { - return nil - } - out := new(PolicyRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PolicyRule) 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 *PolicyRuleList) DeepCopyInto(out *PolicyRuleList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]PolicyRule, len(*in)) + if in.RoleSelectors != nil { + in, out := &in.RoleSelectors, &out.RoleSelectors + *out = make([]metav1.LabelSelector, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyRuleList. -func (in *PolicyRuleList) DeepCopy() *PolicyRuleList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AggregationRule. +func (in *AggregationRule) DeepCopy() *AggregationRule { if in == nil { return nil } - out := new(PolicyRuleList) + out := new(AggregationRule) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PolicyRuleList) 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 *Role) DeepCopyInto(out *Role) { +func (in *GlobalRole) DeepCopyInto(out *GlobalRole) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Target = in.Target if in.Rules != nil { in, out := &in.Rules, &out.Rules - *out = make([]RuleRef, len(*in)) - copy(*out, *in) + *out = make([]v1.PolicyRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AggregationRule != nil { + in, out := &in.AggregationRule, &out.AggregationRule + *out = new(AggregationRule) + (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Role. -func (in *Role) DeepCopy() *Role { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRole. +func (in *GlobalRole) DeepCopy() *GlobalRole { if in == nil { return nil } - out := new(Role) + out := new(GlobalRole) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Role) DeepCopyObject() runtime.Object { +func (in *GlobalRole) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -113,30 +86,30 @@ func (in *Role) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RoleBinding) DeepCopyInto(out *RoleBinding) { +func (in *GlobalRoleBinding) DeepCopyInto(out *GlobalRoleBinding) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.RoleRef = in.RoleRef if in.Subjects != nil { in, out := &in.Subjects, &out.Subjects - *out = make([]Subject, len(*in)) + *out = make([]v1.Subject, len(*in)) copy(*out, *in) } + out.RoleRef = in.RoleRef } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleBinding. -func (in *RoleBinding) DeepCopy() *RoleBinding { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleBinding. +func (in *GlobalRoleBinding) DeepCopy() *GlobalRoleBinding { if in == nil { return nil } - out := new(RoleBinding) + out := new(GlobalRoleBinding) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RoleBinding) DeepCopyObject() runtime.Object { +func (in *GlobalRoleBinding) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -144,31 +117,31 @@ func (in *RoleBinding) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RoleBindingList) DeepCopyInto(out *RoleBindingList) { +func (in *GlobalRoleBindingList) DeepCopyInto(out *GlobalRoleBindingList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]RoleBinding, len(*in)) + *out = make([]GlobalRoleBinding, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleBindingList. -func (in *RoleBindingList) DeepCopy() *RoleBindingList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleBindingList. +func (in *GlobalRoleBindingList) DeepCopy() *GlobalRoleBindingList { if in == nil { return nil } - out := new(RoleBindingList) + out := new(GlobalRoleBindingList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RoleBindingList) DeepCopyObject() runtime.Object { +func (in *GlobalRoleBindingList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -176,97 +149,37 @@ func (in *RoleBindingList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RoleList) DeepCopyInto(out *RoleList) { +func (in *GlobalRoleList) DeepCopyInto(out *GlobalRoleList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]Role, len(*in)) + *out = make([]GlobalRole, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleList. -func (in *RoleList) DeepCopy() *RoleList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleList. +func (in *GlobalRoleList) DeepCopy() *GlobalRoleList { if in == nil { return nil } - out := new(RoleList) + out := new(GlobalRoleList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RoleList) DeepCopyObject() runtime.Object { +func (in *GlobalRoleList) 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 *RoleRef) DeepCopyInto(out *RoleRef) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleRef. -func (in *RoleRef) DeepCopy() *RoleRef { - if in == nil { - return nil - } - out := new(RoleRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuleRef) DeepCopyInto(out *RuleRef) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleRef. -func (in *RuleRef) DeepCopy() *RuleRef { - if in == nil { - return nil - } - out := new(RuleRef) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Subject) DeepCopyInto(out *Subject) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subject. -func (in *Subject) DeepCopy() *Subject { - if in == nil { - return nil - } - out := new(Subject) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Target) DeepCopyInto(out *Target) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Target. -func (in *Target) DeepCopy() *Target { - if in == nil { - return nil - } - out := new(Target) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *User) DeepCopyInto(out *User) { *out = *in @@ -274,7 +187,6 @@ func (in *User) DeepCopyInto(out *User) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. @@ -299,7 +211,6 @@ func (in *User) DeepCopyObject() runtime.Object { func (in *UserCondition) DeepCopyInto(out *UserCondition) { *out = *in in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserCondition. @@ -322,7 +233,7 @@ func (in *UserDetail) DeepCopyInto(out *UserDetail) { } if in.GlobalRole != nil { in, out := &in.GlobalRole, &out.GlobalRole - *out = new(Role) + *out = new(GlobalRole) (*in).DeepCopyInto(*out) } } @@ -349,7 +260,6 @@ func (in *UserList) DeepCopyInto(out *UserList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserList. @@ -383,7 +293,6 @@ func (in *UserSpec) DeepCopyInto(out *UserSpec) { *out = make([]FinalizerName, len(*in)) copy(*out, *in) } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserSpec. @@ -406,7 +315,6 @@ func (in *UserStatus) DeepCopyInto(out *UserStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserStatus. @@ -418,3 +326,135 @@ func (in *UserStatus) DeepCopy() *UserStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceRole) DeepCopyInto(out *WorkspaceRole) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]v1.PolicyRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AggregationRule != nil { + in, out := &in.AggregationRule, &out.AggregationRule + *out = new(AggregationRule) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRole. +func (in *WorkspaceRole) DeepCopy() *WorkspaceRole { + if in == nil { + return nil + } + out := new(WorkspaceRole) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRole) 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 *WorkspaceRoleBinding) DeepCopyInto(out *WorkspaceRoleBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Subjects != nil { + in, out := &in.Subjects, &out.Subjects + *out = make([]v1.Subject, len(*in)) + copy(*out, *in) + } + out.RoleRef = in.RoleRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleBinding. +func (in *WorkspaceRoleBinding) DeepCopy() *WorkspaceRoleBinding { + if in == nil { + return nil + } + out := new(WorkspaceRoleBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleBinding) 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 *WorkspaceRoleBindingList) DeepCopyInto(out *WorkspaceRoleBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WorkspaceRoleBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleBindingList. +func (in *WorkspaceRoleBindingList) DeepCopy() *WorkspaceRoleBindingList { + if in == nil { + return nil + } + out := new(WorkspaceRoleBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleBindingList) 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 *WorkspaceRoleList) DeepCopyInto(out *WorkspaceRoleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WorkspaceRole, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleList. +func (in *WorkspaceRoleList) DeepCopy() *WorkspaceRoleList { + if in == nil { + return nil + } + out := new(WorkspaceRoleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/apis/rbac/v1/evaluation_helpers.go b/pkg/apis/rbac/v1/evaluation_helpers.go new file mode 100644 index 000000000..3707760bf --- /dev/null +++ b/pkg/apis/rbac/v1/evaluation_helpers.go @@ -0,0 +1,179 @@ +/* +Copyright 2018 The Kubernetes 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. +*/ + +package v1 + +import ( + "fmt" + "strings" + + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func RoleRefGroupKind(roleRef rbacv1.RoleRef) schema.GroupKind { + return schema.GroupKind{Group: roleRef.APIGroup, Kind: roleRef.Kind} +} + +func VerbMatches(rule *rbacv1.PolicyRule, requestedVerb string) bool { + for _, ruleVerb := range rule.Verbs { + if ruleVerb == rbacv1.VerbAll { + return true + } + if ruleVerb == requestedVerb { + return true + } + } + + return false +} + +func APIGroupMatches(rule *rbacv1.PolicyRule, requestedGroup string) bool { + for _, ruleGroup := range rule.APIGroups { + if ruleGroup == rbacv1.APIGroupAll { + return true + } + if ruleGroup == requestedGroup { + return true + } + } + + return false +} + +func ResourceMatches(rule *rbacv1.PolicyRule, combinedRequestedResource, requestedSubresource string) bool { + for _, ruleResource := range rule.Resources { + // if everything is allowed, we match + if ruleResource == rbacv1.ResourceAll { + return true + } + // if we have an exact match, we match + if ruleResource == combinedRequestedResource { + return true + } + + // We can also match a */subresource. + // if there isn't a subresource, then continue + if len(requestedSubresource) == 0 { + continue + } + // if the rule isn't in the format */subresource, then we don't match, continue + if len(ruleResource) == len(requestedSubresource)+2 && + strings.HasPrefix(ruleResource, "*/") && + strings.HasSuffix(ruleResource, requestedSubresource) { + return true + + } + } + + return false +} + +func ResourceNameMatches(rule *rbacv1.PolicyRule, requestedName string) bool { + if len(rule.ResourceNames) == 0 { + return true + } + + for _, ruleName := range rule.ResourceNames { + if ruleName == requestedName { + return true + } + } + + return false +} + +func NonResourceURLMatches(rule *rbacv1.PolicyRule, requestedURL string) bool { + for _, ruleURL := range rule.NonResourceURLs { + if ruleURL == rbacv1.NonResourceAll { + return true + } + if ruleURL == requestedURL { + return true + } + if strings.HasSuffix(ruleURL, "*") && strings.HasPrefix(requestedURL, strings.TrimRight(ruleURL, "*")) { + return true + } + } + + return false +} + +// subjectsStrings returns users, groups, serviceaccounts, unknown for display purposes. +func SubjectsStrings(subjects []rbacv1.Subject) ([]string, []string, []string, []string) { + users := []string{} + groups := []string{} + sas := []string{} + others := []string{} + + for _, subject := range subjects { + switch subject.Kind { + case rbacv1.ServiceAccountKind: + sas = append(sas, fmt.Sprintf("%s/%s", subject.Namespace, subject.Name)) + + case rbacv1.UserKind: + users = append(users, subject.Name) + + case rbacv1.GroupKind: + groups = append(groups, subject.Name) + + default: + others = append(others, fmt.Sprintf("%s/%s/%s", subject.Kind, subject.Namespace, subject.Name)) + } + } + + return users, groups, sas, others +} + +func String(r rbacv1.PolicyRule) string { + return "PolicyRule" + CompactString(r) +} + +// CompactString exposes a compact string representation for use in escalation error messages +func CompactString(r rbacv1.PolicyRule) string { + formatStringParts := []string{} + formatArgs := []interface{}{} + if len(r.APIGroups) > 0 { + formatStringParts = append(formatStringParts, "APIGroups:%q") + formatArgs = append(formatArgs, r.APIGroups) + } + if len(r.Resources) > 0 { + formatStringParts = append(formatStringParts, "Resources:%q") + formatArgs = append(formatArgs, r.Resources) + } + if len(r.NonResourceURLs) > 0 { + formatStringParts = append(formatStringParts, "NonResourceURLs:%q") + formatArgs = append(formatArgs, r.NonResourceURLs) + } + if len(r.ResourceNames) > 0 { + formatStringParts = append(formatStringParts, "ResourceNames:%q") + formatArgs = append(formatArgs, r.ResourceNames) + } + if len(r.Verbs) > 0 { + formatStringParts = append(formatStringParts, "Verbs:%q") + formatArgs = append(formatArgs, r.Verbs) + } + formatString := "{" + strings.Join(formatStringParts, ", ") + "}" + return fmt.Sprintf(formatString, formatArgs...) +} + +type SortableRuleSlice []rbacv1.PolicyRule + +func (s SortableRuleSlice) Len() int { return len(s) } +func (s SortableRuleSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortableRuleSlice) Less(i, j int) bool { + return strings.Compare(s[i].String(), s[j].String()) < 0 +} diff --git a/pkg/apis/tenant/v1alpha1/workspace_types.go b/pkg/apis/tenant/v1alpha1/workspace_types.go index 876e87d5e..b4a5fb4ff 100644 --- a/pkg/apis/tenant/v1alpha1/workspace_types.go +++ b/pkg/apis/tenant/v1alpha1/workspace_types.go @@ -26,6 +26,7 @@ const ( ResourceKindWorkspace = "Workspace" ResourceSingularWorkspace = "workspace" ResourcePluralWorkspace = "workspaces" + WorkspaceLabel = "kubesphere.io/workspace" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index ea44d915b..70d160705 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -11,13 +11,18 @@ import ( unionauth "k8s.io/apiserver/pkg/authentication/request/union" "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" "k8s.io/klog" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic" "kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/jwttoken" "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/anonymous" "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/basictoken" "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/bearertoken" "kubesphere.io/kubesphere/pkg/apiserver/authentication/token" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" "kubesphere.io/kubesphere/pkg/apiserver/authorization/path" unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union" apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config" @@ -27,7 +32,7 @@ import ( "kubesphere.io/kubesphere/pkg/informers" configv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/config/v1alpha2" devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" + iamapi "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2" @@ -118,6 +123,7 @@ func (s *APIServer) PrepareRun() error { s.container.Filter(logRequestAndResponse) s.container.Router(restful.CurlyRouter{}) s.container.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) { + klog.Error(panicReason) logStackOnRecover(panicReason, httpWriter) }) @@ -145,9 +151,8 @@ func (s *APIServer) installKubeSphereAPIs() { urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory)) urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory)) urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.KubernetesClient.Config())) - urlruntime.Must(iamv1alpha2.AddToContainer(s.container, im.NewOperator(s.KubernetesClient.KubeSphere(), - s.InformerFactory.KubeSphereSharedInformerFactory()), - am.NewAMOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory()), + urlruntime.Must(iamapi.AddToContainer(s.container, im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory), + am.NewAMOperator(s.InformerFactory), s.Config.AuthenticationOptions)) urlruntime.Must(oauth.AddToContainer(s.container, token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient), s.Config.AuthenticationOptions)) urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container)) @@ -183,24 +188,43 @@ func (s *APIServer) buildHandlerChain() { requestInfoResolver := &request.RequestInfoFactory{ APIPrefixes: sets.NewString("api", "apis", "kapis", "kapi"), GrouplessAPIPrefixes: sets.NewString("api", "kapi"), + GlobalResources: []schema.GroupResource{ + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralUser}, + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralGlobalRole}, + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralGlobalRoleBinding}, + {Group: tenantv1alpha1.SchemeGroupVersion.Group, Resource: tenantv1alpha1.ResourcePluralWorkspace}, + {Group: clusterv1alpha1.SchemeGroupVersion.Group, Resource: clusterv1alpha1.ResourcesPluralCluster}, + {Group: clusterv1alpha1.SchemeGroupVersion.Group, Resource: clusterv1alpha1.ResourcesPluralAgent}, + }, } handler := s.Server.Handler handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{}) - clusterDispatcher := dispatch.NewClusterDispatch(s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Agents().Lister(), s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister()) + clusterDispatcher := dispatch.NewClusterDispatch(s.InformerFactory.KubeSphereSharedInformerFactory().Cluster(). + V1alpha1().Agents().Lister(), s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister()) handler = filters.WithMultipleClusterDispatcher(handler, clusterDispatcher) - excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*"} - pathAuthorizer, _ := path.NewAuthorizer(excludedPaths) + var authorizers authorizer.Authorizer + + switch s.Config.AuthorizationOptions.Mode { + case authorizationoptions.AlwaysAllow: + authorizers = authorizerfactory.NewAlwaysAllowAuthorizer() + case authorizationoptions.AlwaysDeny: + authorizers = authorizerfactory.NewAlwaysDenyAuthorizer() + default: + fallthrough + case authorizationoptions.RBAC: + excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*"} + pathAuthorizer, _ := path.NewAuthorizer(excludedPaths) + authorizers = unionauthorizer.New(pathAuthorizer, authorizerfactory.NewOPAAuthorizer(am.NewAMOperator(s.InformerFactory)), authorizerfactory.NewRBACAuthorizer(am.NewAMOperator(s.InformerFactory))) + } - // union authorizers are ordered, don't change the order here - authorizers := unionauthorizer.New(pathAuthorizer, authorizerfactory.NewOPAAuthorizer(am.NewAMOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory()))) handler = filters.WithAuthorization(handler, authorizers) // authenticators are unordered authn := unionauth.New(anonymous.NewAuthenticator(), - basictoken.New(basic.NewBasicAuthenticator(im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory()))), + basictoken.New(basic.NewBasicAuthenticator(im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory))), bearertoken.New(jwttoken.NewTokenAuthenticator(token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient)))) handler = filters.WithAuthentication(handler, authn) handler = filters.WithRequestInfo(handler, requestInfoResolver) @@ -281,9 +305,10 @@ func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error { ksGVRs := []schema.GroupVersionResource{ {Group: "tenant.kubesphere.io", Version: "v1alpha1", Resource: "workspaces"}, {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "users"}, - {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "roles"}, - {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "rolebindings"}, - {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "policyrules"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalroles"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalrolebindings"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspaceroles"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspacerolebindings"}, } devopsGVRs := []schema.GroupVersionResource{ diff --git a/pkg/apiserver/authorization/authorizer/interfaces.go b/pkg/apiserver/authorization/authorizer/interfaces.go index 3c8771328..af67af2b5 100644 --- a/pkg/apiserver/authorization/authorizer/interfaces.go +++ b/pkg/apiserver/authorization/authorizer/interfaces.go @@ -68,6 +68,9 @@ type Attributes interface { // and false for non-resource endpoints like /api, /healthz IsResourceRequest() bool + // GetResourceScope returns the scope of the resource requested, if a request is for a REST object. + GetResourceScope() string + // GetPath returns the path of the request GetPath() string } @@ -111,6 +114,7 @@ type AttributesRecord struct { KubernetesRequest bool ResourceRequest bool Path string + ResourceScope string } func (a AttributesRecord) GetUser() user.Info { @@ -169,6 +173,10 @@ func (a AttributesRecord) GetPath() string { return a.Path } +func (a AttributesRecord) GetResourceScope() string { + return a.ResourceScope +} + type Decision int const ( diff --git a/pkg/apiserver/authorization/authorizerfactory/builtin.go b/pkg/apiserver/authorization/authorizerfactory/builtin.go new file mode 100644 index 000000000..6d2f4d650 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/builtin.go @@ -0,0 +1,49 @@ +/* + * + * Copyright 2020 The KubeSphere 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. + * / + */ + +package authorizerfactory + +import ( + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" +) + +// alwaysAllowAuthorizer is an implementation of authorizer.Attributes +// which always says yes to an authorization request. +// It is useful in tests and when using kubernetes in an open manner. +type alwaysAllowAuthorizer struct{} + +func (alwaysAllowAuthorizer) Authorize(authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { + return authorizer.DecisionAllow, "", nil +} + +func NewAlwaysAllowAuthorizer() *alwaysAllowAuthorizer { + return new(alwaysAllowAuthorizer) +} + +// alwaysDenyAuthorizer is an implementation of authorizer.Attributes +// which always says no to an authorization request. +// It is useful in unit tests to force an operation to be forbidden. +type alwaysDenyAuthorizer struct{} + +func (alwaysDenyAuthorizer) Authorize(a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) { + return authorizer.DecisionNoOpinion, "Everything is forbidden.", nil +} + +func NewAlwaysDenyAuthorizer() *alwaysDenyAuthorizer { + return new(alwaysDenyAuthorizer) +} diff --git a/pkg/apiserver/authorization/authorizerfactory/builtin_test.go b/pkg/apiserver/authorization/authorizerfactory/builtin_test.go new file mode 100644 index 000000000..67cd5595e --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/builtin_test.go @@ -0,0 +1,38 @@ +/* + * + * Copyright 2020 The KubeSphere 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. + * / + */ + +package authorizerfactory + +import ( + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "testing" +) + +func TestNewAlwaysAllowAuthorizer(t *testing.T) { + aaa := NewAlwaysAllowAuthorizer() + if decision, _, _ := aaa.Authorize(nil); decision != authorizer.DecisionAllow { + t.Errorf("AlwaysAllowAuthorizer.Authorize did not authorize successfully.") + } +} + +func TestNewAlwaysDenyAuthorizer(t *testing.T) { + ada := NewAlwaysDenyAuthorizer() + if decision, _, _ := ada.Authorize(nil); decision == authorizer.DecisionAllow { + t.Errorf("AlwaysDenyAuthorizer.Authorize returned nil instead of error.") + } +} diff --git a/pkg/apiserver/authorization/authorizerfactory/opa.go b/pkg/apiserver/authorization/authorizerfactory/opa.go index 513934456..07b407d3a 100644 --- a/pkg/apiserver/authorization/authorizerfactory/opa.go +++ b/pkg/apiserver/authorization/authorizerfactory/opa.go @@ -21,6 +21,7 @@ package authorizerfactory import ( "context" "github.com/open-policy-agent/opa/rego" + rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/klog" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" @@ -33,125 +34,140 @@ type opaAuthorizer struct { } const ( - permissionUndefined = "permission undefined" - defaultRegoQuery = "data.authz.allow" + defaultRegoQuery = "data.authz.allow" ) // Make decision by request attributes func (o *opaAuthorizer) Authorize(attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { - // Make decisions based on the authorization policy of different levels of roles // Error returned when an internal error occurs // Reason must be returned when access is denied - globalRole, err := o.am.GetRoleOfUserInTargetScope(iamv1alpha2.GlobalScope, "", attr.GetUser().GetName()) + globalRole, err := o.am.GetGlobalRoleOfUser(attr.GetUser().GetName()) + if err != nil { if errors.IsNotFound(err) { - return authorizer.DecisionDeny, err.Error(), nil + return authorizer.DecisionNoOpinion, "", nil } - return authorizer.DecisionDeny, "", err + return authorizer.DecisionNoOpinion, "", err } - // check global role policy rules + // check global policy rules if authorized, reason, err = o.makeDecision(globalRole, attr); authorized == authorizer.DecisionAllow { return authorized, reason, nil } - // it's not in cluster resource, permission denied - if attr.GetCluster() == "" { - return authorizer.DecisionDeny, permissionUndefined, nil + // it's global resource, permission denied + if attr.GetResourceScope() == iamv1alpha2.GlobalScope { + return authorizer.DecisionNoOpinion, "", nil } - clusterRole, err := o.am.GetRoleOfUserInTargetScope(iamv1alpha2.ClusterScope, attr.GetCluster(), attr.GetUser().GetName()) - if err != nil { - if errors.IsNotFound(err) { - return authorizer.DecisionDeny, err.Error(), nil + if attr.GetResourceScope() == iamv1alpha2.WorkspaceScope { + workspaceRole, err := o.am.GetWorkspaceRoleOfUser(attr.GetUser().GetName(), attr.GetWorkspace()) + if err != nil { + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + return authorizer.DecisionNoOpinion, "", err } - return authorizer.DecisionDeny, "", err + + // check workspace role policy rules + if authorized, reason, err := o.makeDecision(workspaceRole, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, err + } else if err != nil { + return authorizer.DecisionNoOpinion, "", err + } + + return authorizer.DecisionNoOpinion, "", nil + } + + if attr.GetResourceScope() == iamv1alpha2.NamespaceScope { + role, err := o.am.GetNamespaceRoleOfUser(attr.GetUser().GetName(), attr.GetNamespace()) + if err != nil { + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + return authorizer.DecisionNoOpinion, "", err + } + // check namespace role policy rules + if authorized, reason, err := o.makeDecision(role, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, err + } else if err != nil { + return authorizer.DecisionNoOpinion, "", err + } + + return authorizer.DecisionNoOpinion, "", nil + } + + clusterRole, err := o.am.GetClusterRoleOfUser(attr.GetUser().GetName(), attr.GetCluster()) + + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + + if err != nil { + return authorizer.DecisionNoOpinion, "", err } // check cluster role policy rules if authorized, reason, err := o.makeDecision(clusterRole, attr); authorized == authorizer.DecisionAllow { return authorized, reason, nil } else if err != nil { - return authorizer.DecisionDeny, "", err + return authorizer.DecisionNoOpinion, "", err } - // it's not in cluster resource, permission denied - if attr.GetWorkspace() == "" && attr.GetNamespace() == "" { - return authorizer.DecisionDeny, permissionUndefined, nil - } - - workspaceRole, err := o.am.GetRoleOfUserInTargetScope(iamv1alpha2.WorkspaceScope, attr.GetWorkspace(), attr.GetUser().GetName()) - if err != nil { - if errors.IsNotFound(err) { - return authorizer.DecisionDeny, err.Error(), nil - } - return authorizer.DecisionDeny, "", err - } - - // check workspace role policy rules - if authorized, reason, err := o.makeDecision(workspaceRole, attr); authorized == authorizer.DecisionAllow { - return authorized, reason, err - } else if err != nil { - return authorizer.DecisionDeny, "", err - } - - // it's not in workspace resource, permission denied - if attr.GetNamespace() == "" { - return authorizer.DecisionDeny, permissionUndefined, nil - } - - namespaceRole, err := o.am.GetRoleOfUserInTargetScope(iamv1alpha2.NamespaceScope, attr.GetNamespace(), attr.GetUser().GetName()) - if err != nil { - if errors.IsNotFound(err) { - return authorizer.DecisionDeny, err.Error(), nil - } - return authorizer.DecisionDeny, "", err - } - // check namespace role policy rules - if authorized, reason, err := o.makeDecision(namespaceRole, attr); authorized == authorizer.DecisionAllow { - return authorized, reason, err - } else if err != nil { - return authorizer.DecisionDeny, "", err - } - - return authorizer.DecisionDeny, permissionUndefined, nil + return authorizer.DecisionNoOpinion, "", nil } // Make decision base on role -func (o *opaAuthorizer) makeDecision(role *iamv1alpha2.Role, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { +func (o *opaAuthorizer) makeDecision(role interface{}, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { - for _, ruleRef := range role.Rules { - rule, err := o.am.GetPolicyRule(ruleRef.Name) - if err != nil { - if errors.IsNotFound(err) { - continue - } - return authorizer.DecisionDeny, "", err + regoPolicy := "" + + // override + if globalRole, ok := role.(*iamv1alpha2.GlobalRole); ok { + if overrideRego, ok := globalRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego } - // Call the rego.New function to create an object that can be prepared or evaluated - // After constructing a new rego.Rego object you can call PrepareForEval() to obtain an executable query - query, err := rego.New(rego.Query(defaultRegoQuery), rego.Module("authz.rego", rule.Rego)).PrepareForEval(context.Background()) - - if err != nil { - klog.Errorf("rule syntax error:%s", err) - continue + } else if workspaceRole, ok := role.(*iamv1alpha2.WorkspaceRole); ok { + if overrideRego, ok := workspaceRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego } - - // The policy decision is contained in the results returned by the Eval() call. You can inspect the decision and handle it accordingly. - results, err := query.Eval(context.Background(), rego.EvalInput(a)) - - if err != nil { - klog.Errorf("rule syntax error:%s", err) - continue + } else if clusterRole, ok := role.(*rbacv1.ClusterRole); ok { + if overrideRego, ok := clusterRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego } - - if len(results) > 0 && results[0].Expressions[0].Value == true { - return authorizer.DecisionAllow, "", nil + } else if role, ok := role.(*rbacv1.Role); ok { + if overrideRego, ok := role.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego } } - return authorizer.DecisionDeny, permissionUndefined, nil + if regoPolicy == "" { + return authorizer.DecisionNoOpinion, "", nil + } + + // Call the rego.New function to create an object that can be prepared or evaluated + // After constructing a new rego.Rego object you can call PrepareForEval() to obtain an executable query + query, err := rego.New(rego.Query(defaultRegoQuery), rego.Module("authz.rego", regoPolicy)).PrepareForEval(context.Background()) + + if err != nil { + klog.Errorf("syntax error:%s,refer: %s+v", err, role) + return authorizer.DecisionNoOpinion, "", err + } + + // The policy decision is contained in the results returned by the Eval() call. You can inspect the decision and handle it accordingly. + results, err := query.Eval(context.Background(), rego.EvalInput(a)) + + if err != nil { + klog.Errorf("syntax error:%s,refer: %s+v", err, role) + return authorizer.DecisionNoOpinion, "", err + } + + if len(results) > 0 && results[0].Expressions[0].Value == true { + return authorizer.DecisionAllow, "", nil + } + + return authorizer.DecisionNoOpinion, "", nil } func NewOPAAuthorizer(am am.AccessManagementInterface) *opaAuthorizer { diff --git a/pkg/apiserver/authorization/authorizerfactory/opa_test.go b/pkg/apiserver/authorization/authorizerfactory/opa_test.go index 13bbe74e8..c7206df99 100644 --- a/pkg/apiserver/authorization/authorizerfactory/opa_test.go +++ b/pkg/apiserver/authorization/authorizerfactory/opa_test.go @@ -20,209 +20,22 @@ package authorizerfactory import ( "fmt" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/authentication/user" + fakek8s "k8s.io/client-go/kubernetes/fake" iamvealpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" - "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + factory "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/iam/am" "testing" ) -func prepare() (am.AccessManagementInterface, error) { - rules := []*iamvealpha2.PolicyRule{ - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.PolicyRuleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "always-allow", - }, - Rego: "package authz\ndefault allow = true", - }, { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.PolicyRuleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "always-deny", - }, - Rego: "package authz\ndefault allow = false", - }, { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.PolicyRuleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "manage-cluster1-resources", - }, - Rego: `package authz -default allow = false -allow { - resources_in_cluster1 -} -resources_in_cluster1 { - input.Cluster == "cluster1" -}`, - }, - } - - roles := []*iamvealpha2.Role{ - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "global-admin", - }, - Target: iamvealpha2.Target{ - Scope: iamvealpha2.GlobalScope, - Name: "", - }, - Rules: []iamvealpha2.RuleRef{ - { - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.PolicyRuleKind, - Name: "always-allow", - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "anonymous", - }, - Target: iamvealpha2.Target{ - Scope: iamvealpha2.GlobalScope, - Name: "", - }, - Rules: []iamvealpha2.RuleRef{ - { - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.PolicyRuleKind, - Name: "always-deny", - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "cluster1-admin", - }, - Target: iamvealpha2.Target{ - Scope: iamvealpha2.GlobalScope, - Name: "", - }, - Rules: []iamvealpha2.RuleRef{ - { - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.PolicyRuleKind, - Name: "manage-cluster1-resources", - }, - }, - }, - } - - roleBindings := []*iamvealpha2.RoleBinding{ - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleBindingKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "global-admin", - }, - Scope: iamvealpha2.GlobalScope, - RoleRef: iamvealpha2.RoleRef{ - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.RoleKind, - Name: "global-admin", - }, - Subjects: []iamvealpha2.Subject{ - { - Kind: iamvealpha2.UserKind, - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Name: "admin", - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleBindingKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "anonymous", - }, - Scope: iamvealpha2.GlobalScope, - RoleRef: iamvealpha2.RoleRef{ - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.RoleKind, - Name: "anonymous", - }, - Subjects: []iamvealpha2.Subject{ - { - Kind: iamvealpha2.UserKind, - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Name: user.Anonymous, - }, - }, - }, - { - TypeMeta: metav1.TypeMeta{ - Kind: iamvealpha2.RoleBindingKind, - APIVersion: iamvealpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: "cluster1-admin", - }, - Scope: iamvealpha2.GlobalScope, - RoleRef: iamvealpha2.RoleRef{ - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Kind: iamvealpha2.RoleKind, - Name: "cluster1-admin", - }, - Subjects: []iamvealpha2.Subject{ - { - Kind: iamvealpha2.UserKind, - APIGroup: iamvealpha2.SchemeGroupVersion.String(), - Name: "tom", - }, - }, - }, - } - - ksClient := fake.NewSimpleClientset() - informerFactory := externalversions.NewSharedInformerFactory(ksClient, 0) - - for _, rule := range rules { - err := informerFactory.Iam().V1alpha2().PolicyRules().Informer().GetIndexer().Add(rule) - if err != nil { - return nil, fmt.Errorf("add rule:%s", err) - } - } - for _, role := range roles { - err := informerFactory.Iam().V1alpha2().Roles().Informer().GetIndexer().Add(role) - if err != nil { - return nil, fmt.Errorf("add role:%s", err) - } - } - for _, roleBinding := range roleBindings { - err := informerFactory.Iam().V1alpha2().RoleBindings().Informer().GetIndexer().Add(roleBinding) - if err != nil { - return nil, fmt.Errorf("add role binding:%s", err) - } - } - - operator := am.NewAMOperator(ksClient, informerFactory) - - return operator, nil -} - func TestGlobalRole(t *testing.T) { operator, err := prepare() + if err != nil { t.Fatal(err) } @@ -244,14 +57,8 @@ func TestGlobalRole(t *testing.T) { Extra: nil, }, Verb: "list", - Cluster: "", - Workspace: "", - Namespace: "", - APIGroup: "", APIVersion: "v1", Resource: "nodes", - Subresource: "", - Name: "", KubernetesRequest: true, ResourceRequest: true, Path: "/api/v1/nodes", @@ -268,19 +75,13 @@ func TestGlobalRole(t *testing.T) { Extra: nil, }, Verb: "list", - Cluster: "", - Workspace: "", - Namespace: "", - APIGroup: "", APIVersion: "v1", Resource: "nodes", - Subresource: "", - Name: "", KubernetesRequest: true, ResourceRequest: true, Path: "/api/v1/nodes", }, - expectedDecision: authorizer.DecisionDeny, + expectedDecision: authorizer.DecisionNoOpinion, }, { name: "tom can list nodes in cluster1", request: authorizer.AttributesRecord{ @@ -289,13 +90,8 @@ func TestGlobalRole(t *testing.T) { }, Verb: "list", Cluster: "cluster1", - Workspace: "", - Namespace: "", - APIGroup: "", APIVersion: "v1", Resource: "nodes", - Subresource: "", - Name: "", KubernetesRequest: true, ResourceRequest: true, Path: "/api/v1/clusters/cluster1/nodes", @@ -310,18 +106,13 @@ func TestGlobalRole(t *testing.T) { }, Verb: "list", Cluster: "cluster2", - Workspace: "", - Namespace: "", - APIGroup: "", APIVersion: "v1", Resource: "nodes", - Subresource: "", - Name: "", KubernetesRequest: true, ResourceRequest: true, Path: "/api/v1/clusters/cluster2/nodes", }, - expectedDecision: authorizer.DecisionDeny, + expectedDecision: authorizer.DecisionNoOpinion, }, } @@ -335,3 +126,127 @@ func TestGlobalRole(t *testing.T) { } } } + +func prepare() (am.AccessManagementInterface, error) { + globalRoles := []*iamvealpha2.GlobalRole{ + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "global-admin", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: "package authz\ndefault allow = true"}, + }, + }, { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "anonymous", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: "package authz\ndefault allow = false"}, + }, + }, { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster1-admin", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: `package authz +default allow = false +allow { + resources_in_cluster1 +} +resources_in_cluster1 { + input.Cluster == "cluster1" +}`}, + }, + }, + } + + roleBindings := []*iamvealpha2.GlobalRoleBinding{ + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "global-admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "global-admin", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: "admin", + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "anonymous", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "anonymous", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: user.Anonymous, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster1-admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "cluster1-admin", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: "tom", + }, + }, + }, + } + + ksClient := fake.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + factory := factory.NewInformerFactories(k8sClient, ksClient, nil, nil) + for _, role := range globalRoles { + err := factory.KubeSphereSharedInformerFactory().Iam().V1alpha2().GlobalRoles().Informer().GetIndexer().Add(role) + if err != nil { + return nil, fmt.Errorf("add role:%s", err) + } + } + + for _, roleBinding := range roleBindings { + err := factory.KubeSphereSharedInformerFactory().Iam().V1alpha2().GlobalRoleBindings().Informer().GetIndexer().Add(roleBinding) + if err != nil { + return nil, fmt.Errorf("add role binding:%s", err) + } + } + + operator := am.NewAMOperator(factory) + + return operator, nil +} diff --git a/pkg/apiserver/authorization/authorizerfactory/rbac.go b/pkg/apiserver/authorization/authorizerfactory/rbac.go new file mode 100644 index 000000000..d54199879 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/rbac.go @@ -0,0 +1,384 @@ +/* + * + * Copyright 2020 The KubeSphere 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. + * / + */ + +package authorizerfactory + +import ( + "bytes" + "fmt" + "k8s.io/apiserver/pkg/authentication/serviceaccount" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + + "k8s.io/klog" + + rbacv1 "k8s.io/api/rbac/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apiserver/pkg/authentication/user" + rbacv1helpers "kubesphere.io/kubesphere/pkg/apis/rbac/v1" +) + +type RBACAuthorizer struct { + am am.AccessManagementInterface +} + +// authorizingVisitor short-circuits once allowed, and collects any resolution errors encountered +type authorizingVisitor struct { + requestAttributes authorizer.Attributes + + allowed bool + reason string + errors []error +} + +func (v *authorizingVisitor) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool { + if rule != nil && ruleAllows(v.requestAttributes, rule) { + v.allowed = true + v.reason = fmt.Sprintf("RBAC: allowed by %s", source.String()) + return false + } + if err != nil { + v.errors = append(v.errors, err) + } + return true +} + +type ruleAccumulator struct { + rules []rbacv1.PolicyRule + errors []error +} + +func (r *ruleAccumulator) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool { + if rule != nil { + r.rules = append(r.rules, *rule) + } + if err != nil { + r.errors = append(r.errors, err) + } + return true +} + +func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) { + ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes} + + r.visitRulesFor(requestAttributes, ruleCheckingVisitor.visit) + + if ruleCheckingVisitor.allowed { + return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil + } + + // Build a detailed log of the denial. + // Make the whole block conditional so we don't do a lot of string-building we won't use. + if klog.V(4) { + var operation string + if requestAttributes.IsResourceRequest() { + b := &bytes.Buffer{} + b.WriteString(`"`) + b.WriteString(requestAttributes.GetVerb()) + b.WriteString(`" resource "`) + b.WriteString(requestAttributes.GetResource()) + if len(requestAttributes.GetAPIGroup()) > 0 { + b.WriteString(`.`) + b.WriteString(requestAttributes.GetAPIGroup()) + } + if len(requestAttributes.GetSubresource()) > 0 { + b.WriteString(`/`) + b.WriteString(requestAttributes.GetSubresource()) + } + b.WriteString(`"`) + if len(requestAttributes.GetName()) > 0 { + b.WriteString(` named "`) + b.WriteString(requestAttributes.GetName()) + b.WriteString(`"`) + } + operation = b.String() + } else { + operation = fmt.Sprintf("%q nonResourceURL %q", requestAttributes.GetVerb(), requestAttributes.GetPath()) + } + + var scope string + if ns := requestAttributes.GetNamespace(); len(ns) > 0 { + scope = fmt.Sprintf("in namespace %q", ns) + } else if ws := requestAttributes.GetWorkspace(); len(ws) > 0 { + scope = fmt.Sprintf("in workspace %q", ws) + } else if cluster := requestAttributes.GetWorkspace(); len(cluster) > 0 { + scope = fmt.Sprintf("in cluster %q", cluster) + } else { + scope = "global-wide" + } + + klog.Infof("RBAC: no rules authorize user %q with groups %q to %s %s", requestAttributes.GetUser().GetName(), requestAttributes.GetUser().GetGroups(), operation, scope) + } + + reason := "" + if len(ruleCheckingVisitor.errors) > 0 { + reason = fmt.Sprintf("RBAC: %v", utilerrors.NewAggregate(ruleCheckingVisitor.errors)) + } + return authorizer.DecisionNoOpinion, reason, nil +} + +func NewRBACAuthorizer(am am.AccessManagementInterface) *RBACAuthorizer { + return &RBACAuthorizer{am: am} +} + +func ruleAllows(requestAttributes authorizer.Attributes, rule *rbacv1.PolicyRule) bool { + if requestAttributes.IsResourceRequest() { + combinedResource := requestAttributes.GetResource() + if len(requestAttributes.GetSubresource()) > 0 { + combinedResource = requestAttributes.GetResource() + "/" + requestAttributes.GetSubresource() + } + + return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) && + rbacv1helpers.APIGroupMatches(rule, requestAttributes.GetAPIGroup()) && + rbacv1helpers.ResourceMatches(rule, combinedResource, requestAttributes.GetSubresource()) && + rbacv1helpers.ResourceNameMatches(rule, requestAttributes.GetName()) + } + + return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) && + rbacv1helpers.NonResourceURLMatches(rule, requestAttributes.GetPath()) +} + +func (r *RBACAuthorizer) rulesFor(requestAttributes authorizer.Attributes) ([]rbacv1.PolicyRule, error) { + visitor := &ruleAccumulator{} + r.visitRulesFor(requestAttributes, visitor.visit) + return visitor.rules, utilerrors.NewAggregate(visitor.errors) +} + +func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool) { + if globalRoleBindings, err := r.am.ListGlobalRoleBindings(""); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &globalRoleBindingDescriber{} + for _, globalRoleBinding := range globalRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), globalRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(globalRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = globalRoleBinding + sourceDescriber.subject = &globalRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + + if requestAttributes.GetResourceScope() == iamv1alpha2.WorkspaceScope { + if workspaceRoleBindings, err := r.am.ListWorkspaceRoleBindings("", requestAttributes.GetWorkspace()); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &workspaceRoleBindingDescriber{} + for _, workspaceRoleBinding := range workspaceRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), workspaceRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(workspaceRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = workspaceRoleBinding + sourceDescriber.subject = &workspaceRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + } + + if requestAttributes.GetResourceScope() == iamv1alpha2.NamespaceScope { + if roleBindings, err := r.am.ListRoleBindings("", requestAttributes.GetNamespace()); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &roleBindingDescriber{} + for _, roleBinding := range roleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), roleBinding.Subjects, requestAttributes.GetNamespace()) + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(roleBinding.RoleRef, requestAttributes.GetNamespace()) + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = roleBinding + sourceDescriber.subject = &roleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + } + + if clusterRoleBindings, err := r.am.ListClusterRoleBindings(""); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &clusterRoleBindingDescriber{} + for _, clusterRoleBinding := range clusterRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), clusterRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = clusterRoleBinding + sourceDescriber.subject = &clusterRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } +} + +// appliesTo returns whether any of the bindingSubjects applies to the specified subject, +// and if true, the index of the first subject that applies +func appliesTo(user user.Info, bindingSubjects []rbacv1.Subject, namespace string) (int, bool) { + for i, bindingSubject := range bindingSubjects { + if appliesToUser(user, bindingSubject, namespace) { + return i, true + } + } + return 0, false +} + +func appliesToUser(user user.Info, subject rbacv1.Subject, namespace string) bool { + switch subject.Kind { + case rbacv1.UserKind: + return user.GetName() == subject.Name + + case rbacv1.GroupKind: + return sliceutil.HasString(user.GetGroups(), subject.Name) + + case rbacv1.ServiceAccountKind: + // default the namespace to namespace we're working in if its available. This allows rolebindings that reference + // SAs in th local namespace to avoid having to qualify them. + saNamespace := namespace + if len(subject.Namespace) > 0 { + saNamespace = subject.Namespace + } + if len(saNamespace) == 0 { + return false + } + // use a more efficient comparison for RBAC checking + return serviceaccount.MatchesUsername(saNamespace, subject.Name, user.GetName()) + default: + return false + } +} + +type globalRoleBindingDescriber struct { + binding *iamv1alpha2.GlobalRoleBinding + subject *rbacv1.Subject +} + +func (d *globalRoleBindingDescriber) String() string { + return fmt.Sprintf("GlobalRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type clusterRoleBindingDescriber struct { + binding *rbacv1.ClusterRoleBinding + subject *rbacv1.Subject +} + +func (d *clusterRoleBindingDescriber) String() string { + return fmt.Sprintf("ClusterRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type workspaceRoleBindingDescriber struct { + binding *iamv1alpha2.WorkspaceRoleBinding + subject *rbacv1.Subject +} + +func (d *workspaceRoleBindingDescriber) String() string { + return fmt.Sprintf("GlobalRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type roleBindingDescriber struct { + binding *rbacv1.RoleBinding + subject *rbacv1.Subject +} + +func (d *roleBindingDescriber) String() string { + return fmt.Sprintf("RoleBinding %q of %s %q to %s", + d.binding.Name+"/"+d.binding.Namespace, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, d.binding.Namespace), + ) +} + +func describeSubject(s *rbacv1.Subject, bindingNamespace string) string { + switch s.Kind { + case rbacv1.ServiceAccountKind: + if len(s.Namespace) > 0 { + return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+s.Namespace) + } + return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+bindingNamespace) + default: + return fmt.Sprintf("%s %q", s.Kind, s.Name) + } +} diff --git a/pkg/apiserver/authorization/authorizerfactory/rbac_test.go b/pkg/apiserver/authorization/authorizerfactory/rbac_test.go new file mode 100644 index 000000000..cf2ff6570 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/rbac_test.go @@ -0,0 +1,389 @@ +/* +Copyright 2016 The Kubernetes 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. +*/ + +package authorizerfactory + +import ( + "errors" + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + "hash/fnv" + "io" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + fakek8s "k8s.io/client-go/kubernetes/fake" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "sort" + "testing" + + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" +) + +// StaticRoles is a rule resolver that resolves from lists of role objects. +type StaticRoles struct { + roles []*rbacv1.Role + roleBindings []*rbacv1.RoleBinding + clusterRoles []*rbacv1.ClusterRole + clusterRoleBindings []*rbacv1.ClusterRoleBinding +} + +func (r *StaticRoles) GetRole(namespace, name string) (*rbacv1.Role, error) { + if len(namespace) == 0 { + return nil, errors.New("must provide namespace when getting role") + } + for _, role := range r.roles { + if role.Namespace == namespace && role.Name == name { + return role, nil + } + } + return nil, errors.New("role not found") +} + +func (r *StaticRoles) GetClusterRole(name string) (*rbacv1.ClusterRole, error) { + for _, clusterRole := range r.clusterRoles { + if clusterRole.Name == name { + return clusterRole, nil + } + } + return nil, errors.New("clusterrole not found") +} + +func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) { + if len(namespace) == 0 { + return nil, errors.New("must provide namespace when listing role bindings") + } + + roleBindingList := []*rbacv1.RoleBinding{} + for _, roleBinding := range r.roleBindings { + if roleBinding.Namespace != namespace { + continue + } + // TODO(ericchiang): need to implement label selectors? + roleBindingList = append(roleBindingList, roleBinding) + } + return roleBindingList, nil +} + +func (r *StaticRoles) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) { + return r.clusterRoleBindings, nil +} + +// compute a hash of a policy rule so we can sort in a deterministic order +func hashOf(p rbacv1.PolicyRule) string { + hash := fnv.New32() + writeStrings := func(slis ...[]string) { + for _, sli := range slis { + for _, s := range sli { + io.WriteString(hash, s) + } + } + } + writeStrings(p.Verbs, p.APIGroups, p.Resources, p.ResourceNames, p.NonResourceURLs) + return string(hash.Sum(nil)) +} + +// byHash sorts a set of policy rules by a hash of its fields +type byHash []rbacv1.PolicyRule + +func (b byHash) Len() int { return len(b) } +func (b byHash) Less(i, j int) bool { return hashOf(b[i]) < hashOf(b[j]) } +func (b byHash) Swap(i, j int) { b[i], b[j] = b[j], b[i] } + +func TestRBACAuthorizer(t *testing.T) { + ruleReadPods := rbacv1.PolicyRule{ + Verbs: []string{"GET", "WATCH"}, + APIGroups: []string{"v1"}, + Resources: []string{"pods"}, + } + ruleReadServices := rbacv1.PolicyRule{ + Verbs: []string{"GET", "WATCH"}, + APIGroups: []string{"v1"}, + Resources: []string{"services"}, + } + ruleWriteNodes := rbacv1.PolicyRule{ + Verbs: []string{"PUT", "CREATE", "UPDATE"}, + APIGroups: []string{"v1"}, + Resources: []string{"nodes"}, + } + ruleAdmin := rbacv1.PolicyRule{ + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + } + + staticRoles1 := StaticRoles{ + roles: []*rbacv1.Role{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1", Name: "readthings"}, + Rules: []rbacv1.PolicyRule{ruleReadPods, ruleReadServices}, + }, + }, + clusterRoles: []*rbacv1.ClusterRole{ + { + ObjectMeta: metav1.ObjectMeta{Name: "cluster-admin"}, + Rules: []rbacv1.PolicyRule{ruleAdmin}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "write-nodes"}, + Rules: []rbacv1.PolicyRule{ruleWriteNodes}, + }, + }, + roleBindings: []*rbacv1.RoleBinding{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1"}, + Subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "foobar"}, + {Kind: rbacv1.GroupKind, Name: "group1"}, + }, + RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: "readthings"}, + }, + }, + clusterRoleBindings: []*rbacv1.ClusterRoleBinding{ + { + Subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "admin"}, + {Kind: rbacv1.GroupKind, Name: "admin"}, + }, + RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "ClusterRole", Name: "cluster-admin"}, + }, + }, + } + + tests := []struct { + StaticRoles + + // For a given context, what are the rules that apply? + user user.Info + namespace string + effectiveRules []rbacv1.PolicyRule + }{ + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "namespace1", + effectiveRules: []rbacv1.PolicyRule{ruleReadPods, ruleReadServices}, + }, + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "namespace2", + effectiveRules: nil, + }, + { + StaticRoles: staticRoles1, + // Same as above but without a namespace. Only cluster rules should apply. + user: &user.DefaultInfo{Name: "foobar", Groups: []string{"admin"}}, + effectiveRules: []rbacv1.PolicyRule{ruleAdmin}, + }, + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{}, + effectiveRules: nil, + }, + } + + for i, tc := range tests { + ruleResolver, err := newMockRBACAuthorizer(&tc.StaticRoles) + + if err != nil { + t.Fatal(err) + } + + scope := iamv1alpha2.ClusterScope + + if tc.namespace != "" { + scope = iamv1alpha2.NamespaceScope + } + + rules, err := ruleResolver.rulesFor(authorizer.AttributesRecord{ + User: tc.user, + Namespace: tc.namespace, + ResourceScope: scope, + }) + + if err != nil { + t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err) + continue + } + + // Sort for deep equals + sort.Sort(byHash(rules)) + sort.Sort(byHash(tc.effectiveRules)) + + if diff := cmp.Diff(rules, tc.effectiveRules); diff != "" { + t.Errorf("case %d: %s", i, diff) + } + } +} + +func newMockRBACAuthorizer(staticRoles *StaticRoles) (*RBACAuthorizer, error) { + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + k8sInformerFactory := fakeInformerFactory.KubernetesSharedInformerFactory() + + for _, role := range staticRoles.roles { + err := k8sInformerFactory.Rbac().V1().Roles().Informer().GetIndexer().Add(role) + if err != nil { + return nil, err + } + } + + for _, roleBinding := range staticRoles.roleBindings { + err := k8sInformerFactory.Rbac().V1().RoleBindings().Informer().GetIndexer().Add(roleBinding) + if err != nil { + return nil, err + } + } + + for _, clusterRole := range staticRoles.clusterRoles { + err := k8sInformerFactory.Rbac().V1().ClusterRoles().Informer().GetIndexer().Add(clusterRole) + if err != nil { + return nil, err + } + } + + for _, clusterRoleBinding := range staticRoles.clusterRoleBindings { + err := k8sInformerFactory.Rbac().V1().ClusterRoleBindings().Informer().GetIndexer().Add(clusterRoleBinding) + if err != nil { + return nil, err + } + } + return NewRBACAuthorizer(am.NewAMOperator(fakeInformerFactory)), nil +} + +func TestAppliesTo(t *testing.T) { + tests := []struct { + subjects []rbacv1.Subject + user user.Info + namespace string + appliesTo bool + index int + testCase string + }{ + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + appliesTo: true, + index: 0, + testCase: "single subject that matches username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that matches username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam"}, + appliesTo: false, + testCase: "multiple subjects, none that match username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}, + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that match group", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}, + namespace: "namespace1", + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that match group, should ignore namespace", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + {Kind: rbacv1.ServiceAccountKind, Namespace: "kube-system", Name: "default"}, + }, + user: &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"}, + namespace: "default", + appliesTo: true, + index: 2, + testCase: "multiple subjects with a service account that matches", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "*"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "default", + appliesTo: false, + testCase: "* user subject name doesn't match all users", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.GroupKind, Name: user.AllAuthenticated}, + {Kind: rbacv1.GroupKind, Name: user.AllUnauthenticated}, + }, + user: &user.DefaultInfo{Name: "foobar", Groups: []string{user.AllAuthenticated}}, + namespace: "default", + appliesTo: true, + index: 0, + testCase: "binding to all authenticated and unauthenticated subjects matches authenticated user", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.GroupKind, Name: user.AllAuthenticated}, + {Kind: rbacv1.GroupKind, Name: user.AllUnauthenticated}, + }, + user: &user.DefaultInfo{Name: "system:anonymous", Groups: []string{user.AllUnauthenticated}}, + namespace: "default", + appliesTo: true, + index: 1, + testCase: "binding to all authenticated and unauthenticated subjects matches anonymous user", + }, + } + + for _, tc := range tests { + gotIndex, got := appliesTo(tc.user, tc.subjects, tc.namespace) + if got != tc.appliesTo { + t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got) + } + if gotIndex != tc.index { + t.Errorf("case %q want index %d, got %d", tc.testCase, tc.index, gotIndex) + } + } +} diff --git a/pkg/apiserver/authorization/options/authorization_options.go b/pkg/apiserver/authorization/options/authorization_options.go new file mode 100644 index 000000000..0a2a5e8f2 --- /dev/null +++ b/pkg/apiserver/authorization/options/authorization_options.go @@ -0,0 +1,54 @@ +/* + * + * Copyright 2020 The KubeSphere 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. + * / + */ + +package options + +import ( + "fmt" + "github.com/spf13/pflag" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" +) + +type AuthorizationOptions struct { + Mode string `json:"mode" yaml:"mode"` +} + +func NewAuthorizationOptions() *AuthorizationOptions { + return &AuthorizationOptions{Mode: RBAC} +} + +var ( + AlwaysDeny = "AlwaysDeny" + AlwaysAllow = "AlwaysAllow" + RBAC = "RBAC" +) + +func (o *AuthorizationOptions) AddFlags(fs *pflag.FlagSet, s *AuthorizationOptions) { + fs.StringVar(&o.Mode, "authorization", s.Mode, "Authorization setting, allowed values: AlwaysDeny, AlwaysAllow, RBAC.") +} + +func (o AuthorizationOptions) Validate() []error { + errs := make([]error, 0) + if !sliceutil.HasString([]string{AlwaysAllow, AlwaysDeny, RBAC}, o.Mode) { + err := fmt.Errorf("authorization mode %s not support", o.Mode) + klog.Error(err) + errs = append(errs, err) + } + return errs +} diff --git a/pkg/apiserver/config/config.go b/pkg/apiserver/config/config.go index a9c4bd48a..f0a5aad40 100644 --- a/pkg/apiserver/config/config.go +++ b/pkg/apiserver/config/config.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/spf13/viper" authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" "kubesphere.io/kubesphere/pkg/simple/client/alerting" "kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" @@ -58,18 +59,19 @@ const ( // Config defines everything needed for apiserver to deal with external services type Config struct { - DevopsOptions *jenkins.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"` - SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"` - KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` - ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` - NetworkOptions *network.Options `json:"network,omitempty" yaml:"network,omitempty" mapstructure:"network"` - LdapOptions *ldap.Options `json:"ldap,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"` - RedisOptions *cache.Options `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"` - S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"` - OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"` - MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"` - LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"` - AuthenticationOptions *authoptions.AuthenticationOptions `json:"authentication,omitempty" yaml:"authentication,omitempty" mapstructure:"authentication"` + DevopsOptions *jenkins.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"` + SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"` + KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` + ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` + NetworkOptions *network.Options `json:"network,omitempty" yaml:"network,omitempty" mapstructure:"network"` + LdapOptions *ldap.Options `json:"ldap,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"` + RedisOptions *cache.Options `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"` + S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"` + OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"` + MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"` + LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"` + AuthenticationOptions *authoptions.AuthenticationOptions `json:"authentication,omitempty" yaml:"authentication,omitempty" mapstructure:"authentication"` + AuthorizationOptions *authorizationoptions.AuthorizationOptions `json:"authorization,omitempty" yaml:"authorization,omitempty" mapstructure:"authorization"` // Options used for enabling components, not actually used now. Once we switch Alerting/Notification API to kubesphere, // we can add these options to kubesphere command lines AlertingOptions *alerting.Options `json:"alerting,omitempty" yaml:"alerting,omitempty" mapstructure:"alerting"` @@ -93,6 +95,7 @@ func New() *Config { NotificationOptions: notification.NewNotificationOptions(), LoggingOptions: elasticsearch.NewElasticSearchOptions(), AuthenticationOptions: authoptions.NewAuthenticateOptions(), + AuthorizationOptions: authorizationoptions.NewAuthorizationOptions(), } } diff --git a/pkg/apiserver/config/config_test.go b/pkg/apiserver/config/config_test.go index 82f94a944..a2dffc769 100644 --- a/pkg/apiserver/config/config_test.go +++ b/pkg/apiserver/config/config_test.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth" authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" "kubesphere.io/kubesphere/pkg/simple/client/alerting" "kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" @@ -98,6 +99,7 @@ func newTestConfig() (*Config, error) { NotificationOptions: ¬ification.Options{ Endpoint: "http://notification.kubesphere-alerting-system.svc:9200", }, + AuthorizationOptions: authorizationoptions.NewAuthorizationOptions(), AuthenticationOptions: &authoptions.AuthenticationOptions{ AuthenticateRateLimiterMaxTries: 5, AuthenticateRateLimiterDuration: 30 * time.Minute, diff --git a/pkg/apiserver/filters/authorization.go b/pkg/apiserver/filters/authorization.go index 44c9ba4db..588ce7bef 100644 --- a/pkg/apiserver/filters/authorization.go +++ b/pkg/apiserver/filters/authorization.go @@ -59,6 +59,7 @@ func getAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) } // Start with common attributes that apply to resource and non-resource requests + attribs.ResourceScope = requestInfo.ResourceScope attribs.ResourceRequest = requestInfo.IsResourceRequest attribs.Path = requestInfo.Path attribs.Verb = requestInfo.Verb diff --git a/pkg/apiserver/query/field.go b/pkg/apiserver/query/field.go index 0a1515072..3824f8868 100644 --- a/pkg/apiserver/query/field.go +++ b/pkg/apiserver/query/field.go @@ -7,7 +7,9 @@ const ( FieldName = "name" FieldUID = "uid" FieldCreationTimeStamp = "creationTimestamp" + FieldCreateTime = "createTime" FieldLastUpdateTimestamp = "lastUpdateTimestamp" + FieldUpdateTime = "updateTime" FieldLabel = "label" FieldAnnotation = "annotation" FieldNamespace = "namespace" @@ -18,6 +20,8 @@ const ( var SortableFields = []Field{ FieldCreationTimeStamp, + FieldCreateTime, + FieldUpdateTime, FieldLastUpdateTimestamp, FieldName, } diff --git a/pkg/apiserver/query/types.go b/pkg/apiserver/query/types.go index 91d13add5..2ad1ca36e 100644 --- a/pkg/apiserver/query/types.go +++ b/pkg/apiserver/query/types.go @@ -2,6 +2,8 @@ package query import ( "github.com/emicklei/go-restful" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" "strconv" ) @@ -26,7 +28,9 @@ type Query struct { Ascending bool // - Filters []Filter + Filters map[Field]Value + + LabelSelector string } type Pagination struct { @@ -47,17 +51,27 @@ func newPagination(limit int, offset int) *Pagination { } } +func (q *Query) Selector() labels.Selector { + if selector, err := labels.Parse(q.LabelSelector); err != nil { + return labels.Everything() + } else { + return selector + } +} + func (p *Pagination) GetValidPagination(total int) (startIndex, endIndex int) { + // no pagination if p.Limit == NoPagination.Limit { return 0, total } - if p.Limit < 0 || p.Offset < 0 || total == 0 { + // out of range + if p.Limit < 0 || p.Offset < 0 || p.Offset > total { return 0, 0 } - startIndex = p.Limit * p.Offset + startIndex = p.Offset endIndex = startIndex + p.Limit if endIndex > total { @@ -72,7 +86,7 @@ func New() *Query { Pagination: NoPagination, SortBy: "", Ascending: false, - Filters: []Filter{}, + Filters: map[Field]Value{}, } } @@ -84,35 +98,36 @@ type Filter struct { func ParseQueryParameter(request *restful.Request) *Query { query := New() - limit, err := strconv.Atoi(request.QueryParameter("limit")) + limit, err := strconv.Atoi(request.QueryParameter(ParameterLimit)) // equivalent to undefined, use the default value if err != nil { limit = -1 } - page, err := strconv.Atoi(request.QueryParameter("page")) + page, err := strconv.Atoi(request.QueryParameter(ParameterPage)) // equivalent to undefined, use the default value if err != nil { page = 1 } - query.Pagination = newPagination(limit, page-1) + query.Pagination = newPagination(limit, (page-1)*limit) - query.SortBy = Field(defaultString(request.QueryParameter("sortBy"), FieldCreationTimeStamp)) + query.SortBy = Field(defaultString(request.QueryParameter(ParameterOrderBy), FieldCreationTimeStamp)) - ascending, err := strconv.ParseBool(defaultString(request.QueryParameter("ascending"), "false")) + ascending, err := strconv.ParseBool(defaultString(request.QueryParameter(ParameterAscending), "false")) if err != nil { query.Ascending = false } else { query.Ascending = ascending } - for _, field := range ComparableFields { - f := request.QueryParameter(string(field)) - if len(f) != 0 { - query.Filters = append(query.Filters, Filter{ - Field: field, - Value: Value(f), - }) + query.LabelSelector = request.QueryParameter(ParameterLabelSelector) + + for key, values := range request.Request.URL.Query() { + if !sliceutil.HasString([]string{ParameterPage, ParameterLimit, ParameterOrderBy, ParameterAscending, ParameterLabelSelector}, key) { + // support multiple query condition + for _, value := range values { + query.Filters[Field(key)] = Value(value) + } } } diff --git a/pkg/apiserver/query/types_test.go b/pkg/apiserver/query/types_test.go index e473da2fd..a28017528 100644 --- a/pkg/apiserver/query/types_test.go +++ b/pkg/apiserver/query/types_test.go @@ -21,19 +21,10 @@ func TestParseQueryParameter(t *testing.T) { Pagination: newPagination(10, 0), SortBy: FieldCreationTimeStamp, Ascending: true, - Filters: []Filter{ - { - FieldName, - Value("foo"), - }, - { - FieldLabel, - Value("app.kubernetes.io/name:book"), - }, - { - FieldStatus, - Value("Running"), - }, + Filters: map[Field]Value{ + FieldLabel: Value("app.kubernetes.io/name:book"), + FieldName: Value("foo"), + FieldStatus: Value("Running"), }, }, }, @@ -44,7 +35,10 @@ func TestParseQueryParameter(t *testing.T) { Pagination: NoPagination, SortBy: FieldCreationTimeStamp, Ascending: false, - Filters: []Filter{}, + Filters: map[Field]Value{ + Field("xxxx"): Value("xxxx"), + Field("dsfsw"): Value("xxxx"), + }, }, }, } @@ -61,6 +55,7 @@ func TestParseQueryParameter(t *testing.T) { got := ParseQueryParameter(request) if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) return } diff --git a/pkg/apiserver/request/requestinfo.go b/pkg/apiserver/request/requestinfo.go index ca94487ee..c7a9baf12 100644 --- a/pkg/apiserver/request/requestinfo.go +++ b/pkg/apiserver/request/requestinfo.go @@ -6,9 +6,11 @@ import ( "k8s.io/apimachinery/pkg/api/validation/path" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" "net/http" "strings" @@ -46,11 +48,15 @@ type RequestInfo struct { // Cluster of requested resource, this is empty in single-cluster environment Cluster string + + // Scope of requested resource. + ResourceScope string } type RequestInfoFactory struct { APIPrefixes sets.String GrouplessAPIPrefixes sets.String + GlobalResources []schema.GroupResource } // NewRequestInfo returns the information from the http request. If error is not nil, RequestInfo holds the information as best it is known before the failure @@ -188,6 +194,8 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er // parsing successful, so we now know the proper value for .Parts requestInfo.Parts = currentParts + requestInfo.ResourceScope = r.resolveResourceScope(requestInfo) + // parts look like: resource/resourceName/subresource/other/stuff/we/don't/interpret switch { case len(requestInfo.Parts) >= 3 && !specialVerbsNoSubresources.Has(requestInfo.Verb): @@ -264,3 +272,21 @@ func splitPath(path string) []string { } return strings.Split(path, "/") } + +func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string { + for _, globalResource := range r.GlobalResources { + if globalResource.Group == request.APIGroup && + globalResource.Resource == request.Resource { + return iamv1alpha2.GlobalScope + } + } + if request.Namespace != "" { + return iamv1alpha2.NamespaceScope + } + + if request.Workspace != "" { + return iamv1alpha2.WorkspaceScope + } + + return iamv1alpha2.ClusterScope +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go new file mode 100644 index 000000000..5393ec2d2 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeGlobalRoles implements GlobalRoleInterface +type FakeGlobalRoles struct { + Fake *FakeIamV1alpha2 +} + +var globalrolesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalroles"} + +var globalrolesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "GlobalRole"} + +// Get takes name of the globalRole, and returns the corresponding globalRole object, and an error if there is any. +func (c *FakeGlobalRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(globalrolesResource, name), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// List takes label and field selectors, and returns the list of GlobalRoles that match those selectors. +func (c *FakeGlobalRoles) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(globalrolesResource, globalrolesKind, opts), &v1alpha2.GlobalRoleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.GlobalRoleList{ListMeta: obj.(*v1alpha2.GlobalRoleList).ListMeta} + for _, item := range obj.(*v1alpha2.GlobalRoleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested globalRoles. +func (c *FakeGlobalRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(globalrolesResource, opts)) +} + +// Create takes the representation of a globalRole and creates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *FakeGlobalRoles) Create(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(globalrolesResource, globalRole), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// Update takes the representation of a globalRole and updates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *FakeGlobalRoles) Update(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(globalrolesResource, globalRole), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// Delete takes name of the globalRole and deletes it. Returns an error if one occurs. +func (c *FakeGlobalRoles) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(globalrolesResource, name), &v1alpha2.GlobalRole{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeGlobalRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(globalrolesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.GlobalRoleList{}) + return err +} + +// Patch applies the patch and returns the patched globalRole. +func (c *FakeGlobalRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(globalrolesResource, name, pt, data, subresources...), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go new file mode 100644 index 000000000..161de682a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeGlobalRoleBindings implements GlobalRoleBindingInterface +type FakeGlobalRoleBindings struct { + Fake *FakeIamV1alpha2 +} + +var globalrolebindingsResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalrolebindings"} + +var globalrolebindingsKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "GlobalRoleBinding"} + +// Get takes name of the globalRoleBinding, and returns the corresponding globalRoleBinding object, and an error if there is any. +func (c *FakeGlobalRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(globalrolebindingsResource, name), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// List takes label and field selectors, and returns the list of GlobalRoleBindings that match those selectors. +func (c *FakeGlobalRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleBindingList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(globalrolebindingsResource, globalrolebindingsKind, opts), &v1alpha2.GlobalRoleBindingList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.GlobalRoleBindingList{ListMeta: obj.(*v1alpha2.GlobalRoleBindingList).ListMeta} + for _, item := range obj.(*v1alpha2.GlobalRoleBindingList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested globalRoleBindings. +func (c *FakeGlobalRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(globalrolebindingsResource, opts)) +} + +// Create takes the representation of a globalRoleBinding and creates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *FakeGlobalRoleBindings) Create(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(globalrolebindingsResource, globalRoleBinding), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// Update takes the representation of a globalRoleBinding and updates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *FakeGlobalRoleBindings) Update(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(globalrolebindingsResource, globalRoleBinding), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// Delete takes name of the globalRoleBinding and deletes it. Returns an error if one occurs. +func (c *FakeGlobalRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(globalrolebindingsResource, name), &v1alpha2.GlobalRoleBinding{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeGlobalRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(globalrolebindingsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.GlobalRoleBindingList{}) + return err +} + +// Patch applies the patch and returns the patched globalRoleBinding. +func (c *FakeGlobalRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(globalrolebindingsResource, name, pt, data, subresources...), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go index 7b8ccc866..ce41b4a89 100644 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go @@ -28,22 +28,26 @@ type FakeIamV1alpha2 struct { *testing.Fake } -func (c *FakeIamV1alpha2) PolicyRules() v1alpha2.PolicyRuleInterface { - return &FakePolicyRules{c} +func (c *FakeIamV1alpha2) GlobalRoles() v1alpha2.GlobalRoleInterface { + return &FakeGlobalRoles{c} } -func (c *FakeIamV1alpha2) Roles() v1alpha2.RoleInterface { - return &FakeRoles{c} -} - -func (c *FakeIamV1alpha2) RoleBindings() v1alpha2.RoleBindingInterface { - return &FakeRoleBindings{c} +func (c *FakeIamV1alpha2) GlobalRoleBindings() v1alpha2.GlobalRoleBindingInterface { + return &FakeGlobalRoleBindings{c} } func (c *FakeIamV1alpha2) Users() v1alpha2.UserInterface { return &FakeUsers{c} } +func (c *FakeIamV1alpha2) WorkspaceRoles() v1alpha2.WorkspaceRoleInterface { + return &FakeWorkspaceRoles{c} +} + +func (c *FakeIamV1alpha2) WorkspaceRoleBindings() v1alpha2.WorkspaceRoleBindingInterface { + return &FakeWorkspaceRoleBindings{c} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeIamV1alpha2) RESTClient() rest.Interface { diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_policyrule.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_policyrule.go deleted file mode 100644 index b9e63645b..000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_policyrule.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. - -package fake - -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// FakePolicyRules implements PolicyRuleInterface -type FakePolicyRules struct { - Fake *FakeIamV1alpha2 -} - -var policyrulesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "policyrules"} - -var policyrulesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "PolicyRule"} - -// Get takes name of the policyRule, and returns the corresponding policyRule object, and an error if there is any. -func (c *FakePolicyRules) Get(name string, options v1.GetOptions) (result *v1alpha2.PolicyRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(policyrulesResource, name), &v1alpha2.PolicyRule{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.PolicyRule), err -} - -// List takes label and field selectors, and returns the list of PolicyRules that match those selectors. -func (c *FakePolicyRules) List(opts v1.ListOptions) (result *v1alpha2.PolicyRuleList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(policyrulesResource, policyrulesKind, opts), &v1alpha2.PolicyRuleList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha2.PolicyRuleList{ListMeta: obj.(*v1alpha2.PolicyRuleList).ListMeta} - for _, item := range obj.(*v1alpha2.PolicyRuleList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested policyRules. -func (c *FakePolicyRules) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(policyrulesResource, opts)) -} - -// Create takes the representation of a policyRule and creates it. Returns the server's representation of the policyRule, and an error, if there is any. -func (c *FakePolicyRules) Create(policyRule *v1alpha2.PolicyRule) (result *v1alpha2.PolicyRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(policyrulesResource, policyRule), &v1alpha2.PolicyRule{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.PolicyRule), err -} - -// Update takes the representation of a policyRule and updates it. Returns the server's representation of the policyRule, and an error, if there is any. -func (c *FakePolicyRules) Update(policyRule *v1alpha2.PolicyRule) (result *v1alpha2.PolicyRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(policyrulesResource, policyRule), &v1alpha2.PolicyRule{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.PolicyRule), err -} - -// Delete takes name of the policyRule and deletes it. Returns an error if one occurs. -func (c *FakePolicyRules) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(policyrulesResource, name), &v1alpha2.PolicyRule{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePolicyRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(policyrulesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha2.PolicyRuleList{}) - return err -} - -// Patch applies the patch and returns the patched policyRule. -func (c *FakePolicyRules) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.PolicyRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(policyrulesResource, name, pt, data, subresources...), &v1alpha2.PolicyRule{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.PolicyRule), err -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_role.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_role.go deleted file mode 100644 index eef005e6d..000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_role.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. - -package fake - -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// FakeRoles implements RoleInterface -type FakeRoles struct { - Fake *FakeIamV1alpha2 -} - -var rolesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "roles"} - -var rolesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "Role"} - -// Get takes name of the role, and returns the corresponding role object, and an error if there is any. -func (c *FakeRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.Role, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(rolesResource, name), &v1alpha2.Role{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.Role), err -} - -// List takes label and field selectors, and returns the list of Roles that match those selectors. -func (c *FakeRoles) List(opts v1.ListOptions) (result *v1alpha2.RoleList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(rolesResource, rolesKind, opts), &v1alpha2.RoleList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha2.RoleList{ListMeta: obj.(*v1alpha2.RoleList).ListMeta} - for _, item := range obj.(*v1alpha2.RoleList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested roles. -func (c *FakeRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(rolesResource, opts)) -} - -// Create takes the representation of a role and creates it. Returns the server's representation of the role, and an error, if there is any. -func (c *FakeRoles) Create(role *v1alpha2.Role) (result *v1alpha2.Role, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(rolesResource, role), &v1alpha2.Role{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.Role), err -} - -// Update takes the representation of a role and updates it. Returns the server's representation of the role, and an error, if there is any. -func (c *FakeRoles) Update(role *v1alpha2.Role) (result *v1alpha2.Role, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(rolesResource, role), &v1alpha2.Role{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.Role), err -} - -// Delete takes name of the role and deletes it. Returns an error if one occurs. -func (c *FakeRoles) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(rolesResource, name), &v1alpha2.Role{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(rolesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha2.RoleList{}) - return err -} - -// Patch applies the patch and returns the patched role. -func (c *FakeRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.Role, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(rolesResource, name, pt, data, subresources...), &v1alpha2.Role{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.Role), err -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_rolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_rolebinding.go deleted file mode 100644 index 55966eb01..000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_rolebinding.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. - -package fake - -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// FakeRoleBindings implements RoleBindingInterface -type FakeRoleBindings struct { - Fake *FakeIamV1alpha2 -} - -var rolebindingsResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "rolebindings"} - -var rolebindingsKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "RoleBinding"} - -// Get takes name of the roleBinding, and returns the corresponding roleBinding object, and an error if there is any. -func (c *FakeRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.RoleBinding, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(rolebindingsResource, name), &v1alpha2.RoleBinding{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.RoleBinding), err -} - -// List takes label and field selectors, and returns the list of RoleBindings that match those selectors. -func (c *FakeRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.RoleBindingList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(rolebindingsResource, rolebindingsKind, opts), &v1alpha2.RoleBindingList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha2.RoleBindingList{ListMeta: obj.(*v1alpha2.RoleBindingList).ListMeta} - for _, item := range obj.(*v1alpha2.RoleBindingList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested roleBindings. -func (c *FakeRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(rolebindingsResource, opts)) -} - -// Create takes the representation of a roleBinding and creates it. Returns the server's representation of the roleBinding, and an error, if there is any. -func (c *FakeRoleBindings) Create(roleBinding *v1alpha2.RoleBinding) (result *v1alpha2.RoleBinding, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(rolebindingsResource, roleBinding), &v1alpha2.RoleBinding{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.RoleBinding), err -} - -// Update takes the representation of a roleBinding and updates it. Returns the server's representation of the roleBinding, and an error, if there is any. -func (c *FakeRoleBindings) Update(roleBinding *v1alpha2.RoleBinding) (result *v1alpha2.RoleBinding, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(rolebindingsResource, roleBinding), &v1alpha2.RoleBinding{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.RoleBinding), err -} - -// Delete takes name of the roleBinding and deletes it. Returns an error if one occurs. -func (c *FakeRoleBindings) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(rolebindingsResource, name), &v1alpha2.RoleBinding{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(rolebindingsResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha2.RoleBindingList{}) - return err -} - -// Patch applies the patch and returns the patched roleBinding. -func (c *FakeRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBinding, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(rolebindingsResource, name, pt, data, subresources...), &v1alpha2.RoleBinding{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha2.RoleBinding), err -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go new file mode 100644 index 000000000..b7f81ed2e --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeWorkspaceRoles implements WorkspaceRoleInterface +type FakeWorkspaceRoles struct { + Fake *FakeIamV1alpha2 +} + +var workspacerolesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspaceroles"} + +var workspacerolesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "WorkspaceRole"} + +// Get takes name of the workspaceRole, and returns the corresponding workspaceRole object, and an error if there is any. +func (c *FakeWorkspaceRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(workspacerolesResource, name), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// List takes label and field selectors, and returns the list of WorkspaceRoles that match those selectors. +func (c *FakeWorkspaceRoles) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(workspacerolesResource, workspacerolesKind, opts), &v1alpha2.WorkspaceRoleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.WorkspaceRoleList{ListMeta: obj.(*v1alpha2.WorkspaceRoleList).ListMeta} + for _, item := range obj.(*v1alpha2.WorkspaceRoleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested workspaceRoles. +func (c *FakeWorkspaceRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(workspacerolesResource, opts)) +} + +// Create takes the representation of a workspaceRole and creates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *FakeWorkspaceRoles) Create(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(workspacerolesResource, workspaceRole), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// Update takes the representation of a workspaceRole and updates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *FakeWorkspaceRoles) Update(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(workspacerolesResource, workspaceRole), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// Delete takes name of the workspaceRole and deletes it. Returns an error if one occurs. +func (c *FakeWorkspaceRoles) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(workspacerolesResource, name), &v1alpha2.WorkspaceRole{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeWorkspaceRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(workspacerolesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.WorkspaceRoleList{}) + return err +} + +// Patch applies the patch and returns the patched workspaceRole. +func (c *FakeWorkspaceRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(workspacerolesResource, name, pt, data, subresources...), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go new file mode 100644 index 000000000..96504cdcb --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeWorkspaceRoleBindings implements WorkspaceRoleBindingInterface +type FakeWorkspaceRoleBindings struct { + Fake *FakeIamV1alpha2 +} + +var workspacerolebindingsResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspacerolebindings"} + +var workspacerolebindingsKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "WorkspaceRoleBinding"} + +// Get takes name of the workspaceRoleBinding, and returns the corresponding workspaceRoleBinding object, and an error if there is any. +func (c *FakeWorkspaceRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(workspacerolebindingsResource, name), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// List takes label and field selectors, and returns the list of WorkspaceRoleBindings that match those selectors. +func (c *FakeWorkspaceRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleBindingList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(workspacerolebindingsResource, workspacerolebindingsKind, opts), &v1alpha2.WorkspaceRoleBindingList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.WorkspaceRoleBindingList{ListMeta: obj.(*v1alpha2.WorkspaceRoleBindingList).ListMeta} + for _, item := range obj.(*v1alpha2.WorkspaceRoleBindingList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested workspaceRoleBindings. +func (c *FakeWorkspaceRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(workspacerolebindingsResource, opts)) +} + +// Create takes the representation of a workspaceRoleBinding and creates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *FakeWorkspaceRoleBindings) Create(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(workspacerolebindingsResource, workspaceRoleBinding), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// Update takes the representation of a workspaceRoleBinding and updates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *FakeWorkspaceRoleBindings) Update(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(workspacerolebindingsResource, workspaceRoleBinding), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// Delete takes name of the workspaceRoleBinding and deletes it. Returns an error if one occurs. +func (c *FakeWorkspaceRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(workspacerolebindingsResource, name), &v1alpha2.WorkspaceRoleBinding{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeWorkspaceRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(workspacerolebindingsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.WorkspaceRoleBindingList{}) + return err +} + +// Patch applies the patch and returns the patched workspaceRoleBinding. +func (c *FakeWorkspaceRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(workspacerolebindingsResource, name, pt, data, subresources...), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go index cae484a26..edc5b88b5 100644 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go @@ -18,10 +18,12 @@ limitations under the License. package v1alpha2 -type PolicyRuleExpansion interface{} +type GlobalRoleExpansion interface{} -type RoleExpansion interface{} - -type RoleBindingExpansion interface{} +type GlobalRoleBindingExpansion interface{} type UserExpansion interface{} + +type WorkspaceRoleExpansion interface{} + +type WorkspaceRoleBindingExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/policyrule.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go similarity index 52% rename from pkg/client/clientset/versioned/typed/iam/v1alpha2/policyrule.go rename to pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go index 61515de5a..ae6e9eeb3 100644 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/policyrule.go +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go @@ -29,42 +29,42 @@ import ( scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" ) -// PolicyRulesGetter has a method to return a PolicyRuleInterface. +// GlobalRolesGetter has a method to return a GlobalRoleInterface. // A group's client should implement this interface. -type PolicyRulesGetter interface { - PolicyRules() PolicyRuleInterface +type GlobalRolesGetter interface { + GlobalRoles() GlobalRoleInterface } -// PolicyRuleInterface has methods to work with PolicyRule resources. -type PolicyRuleInterface interface { - Create(*v1alpha2.PolicyRule) (*v1alpha2.PolicyRule, error) - Update(*v1alpha2.PolicyRule) (*v1alpha2.PolicyRule, error) +// GlobalRoleInterface has methods to work with GlobalRole resources. +type GlobalRoleInterface interface { + Create(*v1alpha2.GlobalRole) (*v1alpha2.GlobalRole, error) + Update(*v1alpha2.GlobalRole) (*v1alpha2.GlobalRole, error) Delete(name string, options *v1.DeleteOptions) error DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha2.PolicyRule, error) - List(opts v1.ListOptions) (*v1alpha2.PolicyRuleList, error) + Get(name string, options v1.GetOptions) (*v1alpha2.GlobalRole, error) + List(opts v1.ListOptions) (*v1alpha2.GlobalRoleList, error) Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.PolicyRule, err error) - PolicyRuleExpansion + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) + GlobalRoleExpansion } -// policyRules implements PolicyRuleInterface -type policyRules struct { +// globalRoles implements GlobalRoleInterface +type globalRoles struct { client rest.Interface } -// newPolicyRules returns a PolicyRules -func newPolicyRules(c *IamV1alpha2Client) *policyRules { - return &policyRules{ +// newGlobalRoles returns a GlobalRoles +func newGlobalRoles(c *IamV1alpha2Client) *globalRoles { + return &globalRoles{ client: c.RESTClient(), } } -// Get takes name of the policyRule, and returns the corresponding policyRule object, and an error if there is any. -func (c *policyRules) Get(name string, options v1.GetOptions) (result *v1alpha2.PolicyRule, err error) { - result = &v1alpha2.PolicyRule{} +// Get takes name of the globalRole, and returns the corresponding globalRole object, and an error if there is any. +func (c *globalRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} err = c.client.Get(). - Resource("policyrules"). + Resource("globalroles"). Name(name). VersionedParams(&options, scheme.ParameterCodec). Do(). @@ -72,15 +72,15 @@ func (c *policyRules) Get(name string, options v1.GetOptions) (result *v1alpha2. return } -// List takes label and field selectors, and returns the list of PolicyRules that match those selectors. -func (c *policyRules) List(opts v1.ListOptions) (result *v1alpha2.PolicyRuleList, err error) { +// List takes label and field selectors, and returns the list of GlobalRoles that match those selectors. +func (c *globalRoles) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleList, err error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second } - result = &v1alpha2.PolicyRuleList{} + result = &v1alpha2.GlobalRoleList{} err = c.client.Get(). - Resource("policyrules"). + Resource("globalroles"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Do(). @@ -88,47 +88,47 @@ func (c *policyRules) List(opts v1.ListOptions) (result *v1alpha2.PolicyRuleList return } -// Watch returns a watch.Interface that watches the requested policyRules. -func (c *policyRules) Watch(opts v1.ListOptions) (watch.Interface, error) { +// Watch returns a watch.Interface that watches the requested globalRoles. +func (c *globalRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { var timeout time.Duration if opts.TimeoutSeconds != nil { timeout = time.Duration(*opts.TimeoutSeconds) * time.Second } opts.Watch = true return c.client.Get(). - Resource("policyrules"). + Resource("globalroles"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Watch() } -// Create takes the representation of a policyRule and creates it. Returns the server's representation of the policyRule, and an error, if there is any. -func (c *policyRules) Create(policyRule *v1alpha2.PolicyRule) (result *v1alpha2.PolicyRule, err error) { - result = &v1alpha2.PolicyRule{} +// Create takes the representation of a globalRole and creates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *globalRoles) Create(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} err = c.client.Post(). - Resource("policyrules"). - Body(policyRule). + Resource("globalroles"). + Body(globalRole). Do(). Into(result) return } -// Update takes the representation of a policyRule and updates it. Returns the server's representation of the policyRule, and an error, if there is any. -func (c *policyRules) Update(policyRule *v1alpha2.PolicyRule) (result *v1alpha2.PolicyRule, err error) { - result = &v1alpha2.PolicyRule{} +// Update takes the representation of a globalRole and updates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *globalRoles) Update(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} err = c.client.Put(). - Resource("policyrules"). - Name(policyRule.Name). - Body(policyRule). + Resource("globalroles"). + Name(globalRole.Name). + Body(globalRole). Do(). Into(result) return } -// Delete takes name of the policyRule and deletes it. Returns an error if one occurs. -func (c *policyRules) Delete(name string, options *v1.DeleteOptions) error { +// Delete takes name of the globalRole and deletes it. Returns an error if one occurs. +func (c *globalRoles) Delete(name string, options *v1.DeleteOptions) error { return c.client.Delete(). - Resource("policyrules"). + Resource("globalroles"). Name(name). Body(options). Do(). @@ -136,13 +136,13 @@ func (c *policyRules) Delete(name string, options *v1.DeleteOptions) error { } // DeleteCollection deletes a collection of objects. -func (c *policyRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { +func (c *globalRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { var timeout time.Duration if listOptions.TimeoutSeconds != nil { timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second } return c.client.Delete(). - Resource("policyrules"). + Resource("globalroles"). VersionedParams(&listOptions, scheme.ParameterCodec). Timeout(timeout). Body(options). @@ -150,11 +150,11 @@ func (c *policyRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1 Error() } -// Patch applies the patch and returns the patched policyRule. -func (c *policyRules) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.PolicyRule, err error) { - result = &v1alpha2.PolicyRule{} +// Patch applies the patch and returns the patched globalRole. +func (c *globalRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} err = c.client.Patch(pt). - Resource("policyrules"). + Resource("globalroles"). SubResource(subresources...). Name(name). Body(data). diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go new file mode 100644 index 000000000..8fdec09aa --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// GlobalRoleBindingsGetter has a method to return a GlobalRoleBindingInterface. +// A group's client should implement this interface. +type GlobalRoleBindingsGetter interface { + GlobalRoleBindings() GlobalRoleBindingInterface +} + +// GlobalRoleBindingInterface has methods to work with GlobalRoleBinding resources. +type GlobalRoleBindingInterface interface { + Create(*v1alpha2.GlobalRoleBinding) (*v1alpha2.GlobalRoleBinding, error) + Update(*v1alpha2.GlobalRoleBinding) (*v1alpha2.GlobalRoleBinding, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.GlobalRoleBinding, error) + List(opts v1.ListOptions) (*v1alpha2.GlobalRoleBindingList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) + GlobalRoleBindingExpansion +} + +// globalRoleBindings implements GlobalRoleBindingInterface +type globalRoleBindings struct { + client rest.Interface +} + +// newGlobalRoleBindings returns a GlobalRoleBindings +func newGlobalRoleBindings(c *IamV1alpha2Client) *globalRoleBindings { + return &globalRoleBindings{ + client: c.RESTClient(), + } +} + +// Get takes name of the globalRoleBinding, and returns the corresponding globalRoleBinding object, and an error if there is any. +func (c *globalRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Get(). + Resource("globalrolebindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of GlobalRoleBindings that match those selectors. +func (c *globalRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.GlobalRoleBindingList{} + err = c.client.Get(). + Resource("globalrolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested globalRoleBindings. +func (c *globalRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("globalrolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a globalRoleBinding and creates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *globalRoleBindings) Create(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Post(). + Resource("globalrolebindings"). + Body(globalRoleBinding). + Do(). + Into(result) + return +} + +// Update takes the representation of a globalRoleBinding and updates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *globalRoleBindings) Update(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Put(). + Resource("globalrolebindings"). + Name(globalRoleBinding.Name). + Body(globalRoleBinding). + Do(). + Into(result) + return +} + +// Delete takes name of the globalRoleBinding and deletes it. Returns an error if one occurs. +func (c *globalRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("globalrolebindings"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *globalRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("globalrolebindings"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched globalRoleBinding. +func (c *globalRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Patch(pt). + Resource("globalrolebindings"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go index 7bd5bafdf..be20644fb 100644 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go @@ -26,10 +26,11 @@ import ( type IamV1alpha2Interface interface { RESTClient() rest.Interface - PolicyRulesGetter - RolesGetter - RoleBindingsGetter + GlobalRolesGetter + GlobalRoleBindingsGetter UsersGetter + WorkspaceRolesGetter + WorkspaceRoleBindingsGetter } // IamV1alpha2Client is used to interact with features provided by the iam.kubesphere.io group. @@ -37,22 +38,26 @@ type IamV1alpha2Client struct { restClient rest.Interface } -func (c *IamV1alpha2Client) PolicyRules() PolicyRuleInterface { - return newPolicyRules(c) +func (c *IamV1alpha2Client) GlobalRoles() GlobalRoleInterface { + return newGlobalRoles(c) } -func (c *IamV1alpha2Client) Roles() RoleInterface { - return newRoles(c) -} - -func (c *IamV1alpha2Client) RoleBindings() RoleBindingInterface { - return newRoleBindings(c) +func (c *IamV1alpha2Client) GlobalRoleBindings() GlobalRoleBindingInterface { + return newGlobalRoleBindings(c) } func (c *IamV1alpha2Client) Users() UserInterface { return newUsers(c) } +func (c *IamV1alpha2Client) WorkspaceRoles() WorkspaceRoleInterface { + return newWorkspaceRoles(c) +} + +func (c *IamV1alpha2Client) WorkspaceRoleBindings() WorkspaceRoleBindingInterface { + return newWorkspaceRoleBindings(c) +} + // NewForConfig creates a new IamV1alpha2Client for the given config. func NewForConfig(c *rest.Config) (*IamV1alpha2Client, error) { config := *c diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/role.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/role.go deleted file mode 100644 index 35b3a84ac..000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/role.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. - -package v1alpha2 - -import ( - "time" - - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" -) - -// RolesGetter has a method to return a RoleInterface. -// A group's client should implement this interface. -type RolesGetter interface { - Roles() RoleInterface -} - -// RoleInterface has methods to work with Role resources. -type RoleInterface interface { - Create(*v1alpha2.Role) (*v1alpha2.Role, error) - Update(*v1alpha2.Role) (*v1alpha2.Role, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha2.Role, error) - List(opts v1.ListOptions) (*v1alpha2.RoleList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.Role, err error) - RoleExpansion -} - -// roles implements RoleInterface -type roles struct { - client rest.Interface -} - -// newRoles returns a Roles -func newRoles(c *IamV1alpha2Client) *roles { - return &roles{ - client: c.RESTClient(), - } -} - -// Get takes name of the role, and returns the corresponding role object, and an error if there is any. -func (c *roles) Get(name string, options v1.GetOptions) (result *v1alpha2.Role, err error) { - result = &v1alpha2.Role{} - err = c.client.Get(). - Resource("roles"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Roles that match those selectors. -func (c *roles) List(opts v1.ListOptions) (result *v1alpha2.RoleList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha2.RoleList{} - err = c.client.Get(). - Resource("roles"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested roles. -func (c *roles) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("roles"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a role and creates it. Returns the server's representation of the role, and an error, if there is any. -func (c *roles) Create(role *v1alpha2.Role) (result *v1alpha2.Role, err error) { - result = &v1alpha2.Role{} - err = c.client.Post(). - Resource("roles"). - Body(role). - Do(). - Into(result) - return -} - -// Update takes the representation of a role and updates it. Returns the server's representation of the role, and an error, if there is any. -func (c *roles) Update(role *v1alpha2.Role) (result *v1alpha2.Role, err error) { - result = &v1alpha2.Role{} - err = c.client.Put(). - Resource("roles"). - Name(role.Name). - Body(role). - Do(). - Into(result) - return -} - -// Delete takes name of the role and deletes it. Returns an error if one occurs. -func (c *roles) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("roles"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *roles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("roles"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched role. -func (c *roles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.Role, err error) { - result = &v1alpha2.Role{} - err = c.client.Patch(pt). - Resource("roles"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/rolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/rolebinding.go deleted file mode 100644 index 7dffa28a4..000000000 --- a/pkg/client/clientset/versioned/typed/iam/v1alpha2/rolebinding.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. - -package v1alpha2 - -import ( - "time" - - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" -) - -// RoleBindingsGetter has a method to return a RoleBindingInterface. -// A group's client should implement this interface. -type RoleBindingsGetter interface { - RoleBindings() RoleBindingInterface -} - -// RoleBindingInterface has methods to work with RoleBinding resources. -type RoleBindingInterface interface { - Create(*v1alpha2.RoleBinding) (*v1alpha2.RoleBinding, error) - Update(*v1alpha2.RoleBinding) (*v1alpha2.RoleBinding, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha2.RoleBinding, error) - List(opts v1.ListOptions) (*v1alpha2.RoleBindingList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBinding, err error) - RoleBindingExpansion -} - -// roleBindings implements RoleBindingInterface -type roleBindings struct { - client rest.Interface -} - -// newRoleBindings returns a RoleBindings -func newRoleBindings(c *IamV1alpha2Client) *roleBindings { - return &roleBindings{ - client: c.RESTClient(), - } -} - -// Get takes name of the roleBinding, and returns the corresponding roleBinding object, and an error if there is any. -func (c *roleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.RoleBinding, err error) { - result = &v1alpha2.RoleBinding{} - err = c.client.Get(). - Resource("rolebindings"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of RoleBindings that match those selectors. -func (c *roleBindings) List(opts v1.ListOptions) (result *v1alpha2.RoleBindingList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha2.RoleBindingList{} - err = c.client.Get(). - Resource("rolebindings"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested roleBindings. -func (c *roleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("rolebindings"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a roleBinding and creates it. Returns the server's representation of the roleBinding, and an error, if there is any. -func (c *roleBindings) Create(roleBinding *v1alpha2.RoleBinding) (result *v1alpha2.RoleBinding, err error) { - result = &v1alpha2.RoleBinding{} - err = c.client.Post(). - Resource("rolebindings"). - Body(roleBinding). - Do(). - Into(result) - return -} - -// Update takes the representation of a roleBinding and updates it. Returns the server's representation of the roleBinding, and an error, if there is any. -func (c *roleBindings) Update(roleBinding *v1alpha2.RoleBinding) (result *v1alpha2.RoleBinding, err error) { - result = &v1alpha2.RoleBinding{} - err = c.client.Put(). - Resource("rolebindings"). - Name(roleBinding.Name). - Body(roleBinding). - Do(). - Into(result) - return -} - -// Delete takes name of the roleBinding and deletes it. Returns an error if one occurs. -func (c *roleBindings) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("rolebindings"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *roleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("rolebindings"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched roleBinding. -func (c *roleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.RoleBinding, err error) { - result = &v1alpha2.RoleBinding{} - err = c.client.Patch(pt). - Resource("rolebindings"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go new file mode 100644 index 000000000..e47e4c871 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// WorkspaceRolesGetter has a method to return a WorkspaceRoleInterface. +// A group's client should implement this interface. +type WorkspaceRolesGetter interface { + WorkspaceRoles() WorkspaceRoleInterface +} + +// WorkspaceRoleInterface has methods to work with WorkspaceRole resources. +type WorkspaceRoleInterface interface { + Create(*v1alpha2.WorkspaceRole) (*v1alpha2.WorkspaceRole, error) + Update(*v1alpha2.WorkspaceRole) (*v1alpha2.WorkspaceRole, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.WorkspaceRole, error) + List(opts v1.ListOptions) (*v1alpha2.WorkspaceRoleList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) + WorkspaceRoleExpansion +} + +// workspaceRoles implements WorkspaceRoleInterface +type workspaceRoles struct { + client rest.Interface +} + +// newWorkspaceRoles returns a WorkspaceRoles +func newWorkspaceRoles(c *IamV1alpha2Client) *workspaceRoles { + return &workspaceRoles{ + client: c.RESTClient(), + } +} + +// Get takes name of the workspaceRole, and returns the corresponding workspaceRole object, and an error if there is any. +func (c *workspaceRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Get(). + Resource("workspaceroles"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of WorkspaceRoles that match those selectors. +func (c *workspaceRoles) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.WorkspaceRoleList{} + err = c.client.Get(). + Resource("workspaceroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested workspaceRoles. +func (c *workspaceRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("workspaceroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a workspaceRole and creates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *workspaceRoles) Create(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Post(). + Resource("workspaceroles"). + Body(workspaceRole). + Do(). + Into(result) + return +} + +// Update takes the representation of a workspaceRole and updates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *workspaceRoles) Update(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Put(). + Resource("workspaceroles"). + Name(workspaceRole.Name). + Body(workspaceRole). + Do(). + Into(result) + return +} + +// Delete takes name of the workspaceRole and deletes it. Returns an error if one occurs. +func (c *workspaceRoles) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("workspaceroles"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *workspaceRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("workspaceroles"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched workspaceRole. +func (c *workspaceRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Patch(pt). + Resource("workspaceroles"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 000000000..17c4406e4 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// WorkspaceRoleBindingsGetter has a method to return a WorkspaceRoleBindingInterface. +// A group's client should implement this interface. +type WorkspaceRoleBindingsGetter interface { + WorkspaceRoleBindings() WorkspaceRoleBindingInterface +} + +// WorkspaceRoleBindingInterface has methods to work with WorkspaceRoleBinding resources. +type WorkspaceRoleBindingInterface interface { + Create(*v1alpha2.WorkspaceRoleBinding) (*v1alpha2.WorkspaceRoleBinding, error) + Update(*v1alpha2.WorkspaceRoleBinding) (*v1alpha2.WorkspaceRoleBinding, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.WorkspaceRoleBinding, error) + List(opts v1.ListOptions) (*v1alpha2.WorkspaceRoleBindingList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) + WorkspaceRoleBindingExpansion +} + +// workspaceRoleBindings implements WorkspaceRoleBindingInterface +type workspaceRoleBindings struct { + client rest.Interface +} + +// newWorkspaceRoleBindings returns a WorkspaceRoleBindings +func newWorkspaceRoleBindings(c *IamV1alpha2Client) *workspaceRoleBindings { + return &workspaceRoleBindings{ + client: c.RESTClient(), + } +} + +// Get takes name of the workspaceRoleBinding, and returns the corresponding workspaceRoleBinding object, and an error if there is any. +func (c *workspaceRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Get(). + Resource("workspacerolebindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of WorkspaceRoleBindings that match those selectors. +func (c *workspaceRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.WorkspaceRoleBindingList{} + err = c.client.Get(). + Resource("workspacerolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested workspaceRoleBindings. +func (c *workspaceRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("workspacerolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a workspaceRoleBinding and creates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *workspaceRoleBindings) Create(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Post(). + Resource("workspacerolebindings"). + Body(workspaceRoleBinding). + Do(). + Into(result) + return +} + +// Update takes the representation of a workspaceRoleBinding and updates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *workspaceRoleBindings) Update(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Put(). + Resource("workspacerolebindings"). + Name(workspaceRoleBinding.Name). + Body(workspaceRoleBinding). + Do(). + Into(result) + return +} + +// Delete takes name of the workspaceRoleBinding and deletes it. Returns an error if one occurs. +func (c *workspaceRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("workspacerolebindings"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *workspaceRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("workspacerolebindings"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched workspaceRoleBinding. +func (c *workspaceRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Patch(pt). + Resource("workspacerolebindings"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 46796b88f..5ad8c8b18 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -81,14 +81,16 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha3().Pipelines().Informer()}, nil // Group=iam.kubesphere.io, Version=v1alpha2 - case v1alpha2.SchemeGroupVersion.WithResource("policyrules"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().PolicyRules().Informer()}, nil - case v1alpha2.SchemeGroupVersion.WithResource("roles"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().Roles().Informer()}, nil - case v1alpha2.SchemeGroupVersion.WithResource("rolebindings"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().RoleBindings().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("globalroles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoles().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("globalrolebindings"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoleBindings().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("users"): return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().Users().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("workspaceroles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().WorkspaceRoles().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("workspacerolebindings"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().WorkspaceRoleBindings().Informer()}, nil // Group=network.kubesphere.io, Version=v1alpha1 case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"): diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/policyrule.go b/pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go similarity index 69% rename from pkg/client/informers/externalversions/iam/v1alpha2/policyrule.go rename to pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go index c5a2fd756..c02b92031 100644 --- a/pkg/client/informers/externalversions/iam/v1alpha2/policyrule.go +++ b/pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go @@ -31,58 +31,58 @@ import ( v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" ) -// PolicyRuleInformer provides access to a shared informer and lister for -// PolicyRules. -type PolicyRuleInformer interface { +// GlobalRoleInformer provides access to a shared informer and lister for +// GlobalRoles. +type GlobalRoleInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha2.PolicyRuleLister + Lister() v1alpha2.GlobalRoleLister } -type policyRuleInformer struct { +type globalRoleInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc } -// NewPolicyRuleInformer constructs a new informer for PolicyRule type. +// NewGlobalRoleInformer constructs a new informer for GlobalRole type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewPolicyRuleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPolicyRuleInformer(client, resyncPeriod, indexers, nil) +func NewGlobalRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredGlobalRoleInformer(client, resyncPeriod, indexers, nil) } -// NewFilteredPolicyRuleInformer constructs a new informer for PolicyRule type. +// NewFilteredGlobalRoleInformer constructs a new informer for GlobalRole type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewFilteredPolicyRuleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredGlobalRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().PolicyRules().List(options) + return client.IamV1alpha2().GlobalRoles().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().PolicyRules().Watch(options) + return client.IamV1alpha2().GlobalRoles().Watch(options) }, }, - &iamv1alpha2.PolicyRule{}, + &iamv1alpha2.GlobalRole{}, resyncPeriod, indexers, ) } -func (f *policyRuleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPolicyRuleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *globalRoleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredGlobalRoleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *policyRuleInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&iamv1alpha2.PolicyRule{}, f.defaultInformer) +func (f *globalRoleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.GlobalRole{}, f.defaultInformer) } -func (f *policyRuleInformer) Lister() v1alpha2.PolicyRuleLister { - return v1alpha2.NewPolicyRuleLister(f.Informer().GetIndexer()) +func (f *globalRoleInformer) Lister() v1alpha2.GlobalRoleLister { + return v1alpha2.NewGlobalRoleLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/role.go b/pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go similarity index 55% rename from pkg/client/informers/externalversions/iam/v1alpha2/role.go rename to pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go index af36316ed..d5376608b 100644 --- a/pkg/client/informers/externalversions/iam/v1alpha2/role.go +++ b/pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go @@ -31,58 +31,58 @@ import ( v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" ) -// RoleInformer provides access to a shared informer and lister for -// Roles. -type RoleInformer interface { +// GlobalRoleBindingInformer provides access to a shared informer and lister for +// GlobalRoleBindings. +type GlobalRoleBindingInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha2.RoleLister + Lister() v1alpha2.GlobalRoleBindingLister } -type roleInformer struct { +type globalRoleBindingInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc } -// NewRoleInformer constructs a new informer for Role type. +// NewGlobalRoleBindingInformer constructs a new informer for GlobalRoleBinding type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredRoleInformer(client, resyncPeriod, indexers, nil) +func NewGlobalRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredGlobalRoleBindingInformer(client, resyncPeriod, indexers, nil) } -// NewFilteredRoleInformer constructs a new informer for Role type. +// NewFilteredGlobalRoleBindingInformer constructs a new informer for GlobalRoleBinding type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewFilteredRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredGlobalRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().Roles().List(options) + return client.IamV1alpha2().GlobalRoleBindings().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().Roles().Watch(options) + return client.IamV1alpha2().GlobalRoleBindings().Watch(options) }, }, - &iamv1alpha2.Role{}, + &iamv1alpha2.GlobalRoleBinding{}, resyncPeriod, indexers, ) } -func (f *roleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredRoleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *globalRoleBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredGlobalRoleBindingInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *roleInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&iamv1alpha2.Role{}, f.defaultInformer) +func (f *globalRoleBindingInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.GlobalRoleBinding{}, f.defaultInformer) } -func (f *roleInformer) Lister() v1alpha2.RoleLister { - return v1alpha2.NewRoleLister(f.Informer().GetIndexer()) +func (f *globalRoleBindingInformer) Lister() v1alpha2.GlobalRoleBindingLister { + return v1alpha2.NewGlobalRoleBindingLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/interface.go b/pkg/client/informers/externalversions/iam/v1alpha2/interface.go index 674febe64..f6765fd61 100644 --- a/pkg/client/informers/externalversions/iam/v1alpha2/interface.go +++ b/pkg/client/informers/externalversions/iam/v1alpha2/interface.go @@ -24,14 +24,16 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { - // PolicyRules returns a PolicyRuleInformer. - PolicyRules() PolicyRuleInformer - // Roles returns a RoleInformer. - Roles() RoleInformer - // RoleBindings returns a RoleBindingInformer. - RoleBindings() RoleBindingInformer + // GlobalRoles returns a GlobalRoleInformer. + GlobalRoles() GlobalRoleInformer + // GlobalRoleBindings returns a GlobalRoleBindingInformer. + GlobalRoleBindings() GlobalRoleBindingInformer // Users returns a UserInformer. Users() UserInformer + // WorkspaceRoles returns a WorkspaceRoleInformer. + WorkspaceRoles() WorkspaceRoleInformer + // WorkspaceRoleBindings returns a WorkspaceRoleBindingInformer. + WorkspaceRoleBindings() WorkspaceRoleBindingInformer } type version struct { @@ -45,22 +47,27 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } -// PolicyRules returns a PolicyRuleInformer. -func (v *version) PolicyRules() PolicyRuleInformer { - return &policyRuleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +// GlobalRoles returns a GlobalRoleInformer. +func (v *version) GlobalRoles() GlobalRoleInformer { + return &globalRoleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } -// Roles returns a RoleInformer. -func (v *version) Roles() RoleInformer { - return &roleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} - -// RoleBindings returns a RoleBindingInformer. -func (v *version) RoleBindings() RoleBindingInformer { - return &roleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +// GlobalRoleBindings returns a GlobalRoleBindingInformer. +func (v *version) GlobalRoleBindings() GlobalRoleBindingInformer { + return &globalRoleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } // Users returns a UserInformer. func (v *version) Users() UserInformer { return &userInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } + +// WorkspaceRoles returns a WorkspaceRoleInformer. +func (v *version) WorkspaceRoles() WorkspaceRoleInformer { + return &workspaceRoleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// WorkspaceRoleBindings returns a WorkspaceRoleBindingInformer. +func (v *version) WorkspaceRoleBindings() WorkspaceRoleBindingInformer { + return &workspaceRoleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/rolebinding.go b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go similarity index 57% rename from pkg/client/informers/externalversions/iam/v1alpha2/rolebinding.go rename to pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go index 5cc80d0d2..be8b37540 100644 --- a/pkg/client/informers/externalversions/iam/v1alpha2/rolebinding.go +++ b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go @@ -31,58 +31,58 @@ import ( v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" ) -// RoleBindingInformer provides access to a shared informer and lister for -// RoleBindings. -type RoleBindingInformer interface { +// WorkspaceRoleInformer provides access to a shared informer and lister for +// WorkspaceRoles. +type WorkspaceRoleInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha2.RoleBindingLister + Lister() v1alpha2.WorkspaceRoleLister } -type roleBindingInformer struct { +type workspaceRoleInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc } -// NewRoleBindingInformer constructs a new informer for RoleBinding type. +// NewWorkspaceRoleInformer constructs a new informer for WorkspaceRole type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredRoleBindingInformer(client, resyncPeriod, indexers, nil) +func NewWorkspaceRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleInformer(client, resyncPeriod, indexers, nil) } -// NewFilteredRoleBindingInformer constructs a new informer for RoleBinding type. +// NewFilteredWorkspaceRoleInformer constructs a new informer for WorkspaceRole type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewFilteredRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredWorkspaceRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().RoleBindings().List(options) + return client.IamV1alpha2().WorkspaceRoles().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.IamV1alpha2().RoleBindings().Watch(options) + return client.IamV1alpha2().WorkspaceRoles().Watch(options) }, }, - &iamv1alpha2.RoleBinding{}, + &iamv1alpha2.WorkspaceRole{}, resyncPeriod, indexers, ) } -func (f *roleBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredRoleBindingInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *workspaceRoleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *roleBindingInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&iamv1alpha2.RoleBinding{}, f.defaultInformer) +func (f *workspaceRoleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.WorkspaceRole{}, f.defaultInformer) } -func (f *roleBindingInformer) Lister() v1alpha2.RoleBindingLister { - return v1alpha2.NewRoleBindingLister(f.Informer().GetIndexer()) +func (f *workspaceRoleInformer) Lister() v1alpha2.WorkspaceRoleLister { + return v1alpha2.NewWorkspaceRoleLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 000000000..18b18b4d7 --- /dev/null +++ b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" +) + +// WorkspaceRoleBindingInformer provides access to a shared informer and lister for +// WorkspaceRoleBindings. +type WorkspaceRoleBindingInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.WorkspaceRoleBindingLister +} + +type workspaceRoleBindingInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewWorkspaceRoleBindingInformer constructs a new informer for WorkspaceRoleBinding type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewWorkspaceRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleBindingInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredWorkspaceRoleBindingInformer constructs a new informer for WorkspaceRoleBinding type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredWorkspaceRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().WorkspaceRoleBindings().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().WorkspaceRoleBindings().Watch(options) + }, + }, + &iamv1alpha2.WorkspaceRoleBinding{}, + resyncPeriod, + indexers, + ) +} + +func (f *workspaceRoleBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleBindingInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *workspaceRoleBindingInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.WorkspaceRoleBinding{}, f.defaultInformer) +} + +func (f *workspaceRoleBindingInformer) Lister() v1alpha2.WorkspaceRoleBindingLister { + return v1alpha2.NewWorkspaceRoleBindingLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/listers/iam/v1alpha2/expansion_generated.go b/pkg/client/listers/iam/v1alpha2/expansion_generated.go index 5262c5697..7d88b069d 100644 --- a/pkg/client/listers/iam/v1alpha2/expansion_generated.go +++ b/pkg/client/listers/iam/v1alpha2/expansion_generated.go @@ -18,18 +18,22 @@ limitations under the License. package v1alpha2 -// PolicyRuleListerExpansion allows custom methods to be added to -// PolicyRuleLister. -type PolicyRuleListerExpansion interface{} +// GlobalRoleListerExpansion allows custom methods to be added to +// GlobalRoleLister. +type GlobalRoleListerExpansion interface{} -// RoleListerExpansion allows custom methods to be added to -// RoleLister. -type RoleListerExpansion interface{} - -// RoleBindingListerExpansion allows custom methods to be added to -// RoleBindingLister. -type RoleBindingListerExpansion interface{} +// GlobalRoleBindingListerExpansion allows custom methods to be added to +// GlobalRoleBindingLister. +type GlobalRoleBindingListerExpansion interface{} // UserListerExpansion allows custom methods to be added to // UserLister. type UserListerExpansion interface{} + +// WorkspaceRoleListerExpansion allows custom methods to be added to +// WorkspaceRoleLister. +type WorkspaceRoleListerExpansion interface{} + +// WorkspaceRoleBindingListerExpansion allows custom methods to be added to +// WorkspaceRoleBindingLister. +type WorkspaceRoleBindingListerExpansion interface{} diff --git a/pkg/client/listers/iam/v1alpha2/policyrule.go b/pkg/client/listers/iam/v1alpha2/globalrole.go similarity index 51% rename from pkg/client/listers/iam/v1alpha2/policyrule.go rename to pkg/client/listers/iam/v1alpha2/globalrole.go index ec9d01ea3..b601db313 100644 --- a/pkg/client/listers/iam/v1alpha2/policyrule.go +++ b/pkg/client/listers/iam/v1alpha2/globalrole.go @@ -25,41 +25,41 @@ import ( v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" ) -// PolicyRuleLister helps list PolicyRules. -type PolicyRuleLister interface { - // List lists all PolicyRules in the indexer. - List(selector labels.Selector) (ret []*v1alpha2.PolicyRule, err error) - // Get retrieves the PolicyRule from the index for a given name. - Get(name string) (*v1alpha2.PolicyRule, error) - PolicyRuleListerExpansion +// GlobalRoleLister helps list GlobalRoles. +type GlobalRoleLister interface { + // List lists all GlobalRoles in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.GlobalRole, err error) + // Get retrieves the GlobalRole from the index for a given name. + Get(name string) (*v1alpha2.GlobalRole, error) + GlobalRoleListerExpansion } -// policyRuleLister implements the PolicyRuleLister interface. -type policyRuleLister struct { +// globalRoleLister implements the GlobalRoleLister interface. +type globalRoleLister struct { indexer cache.Indexer } -// NewPolicyRuleLister returns a new PolicyRuleLister. -func NewPolicyRuleLister(indexer cache.Indexer) PolicyRuleLister { - return &policyRuleLister{indexer: indexer} +// NewGlobalRoleLister returns a new GlobalRoleLister. +func NewGlobalRoleLister(indexer cache.Indexer) GlobalRoleLister { + return &globalRoleLister{indexer: indexer} } -// List lists all PolicyRules in the indexer. -func (s *policyRuleLister) List(selector labels.Selector) (ret []*v1alpha2.PolicyRule, err error) { +// List lists all GlobalRoles in the indexer. +func (s *globalRoleLister) List(selector labels.Selector) (ret []*v1alpha2.GlobalRole, err error) { err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha2.PolicyRule)) + ret = append(ret, m.(*v1alpha2.GlobalRole)) }) return ret, err } -// Get retrieves the PolicyRule from the index for a given name. -func (s *policyRuleLister) Get(name string) (*v1alpha2.PolicyRule, error) { +// Get retrieves the GlobalRole from the index for a given name. +func (s *globalRoleLister) Get(name string) (*v1alpha2.GlobalRole, error) { obj, exists, err := s.indexer.GetByKey(name) if err != nil { return nil, err } if !exists { - return nil, errors.NewNotFound(v1alpha2.Resource("policyrule"), name) + return nil, errors.NewNotFound(v1alpha2.Resource("globalrole"), name) } - return obj.(*v1alpha2.PolicyRule), nil + return obj.(*v1alpha2.GlobalRole), nil } diff --git a/pkg/client/listers/iam/v1alpha2/globalrolebinding.go b/pkg/client/listers/iam/v1alpha2/globalrolebinding.go new file mode 100644 index 000000000..ea549adc1 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/globalrolebinding.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// GlobalRoleBindingLister helps list GlobalRoleBindings. +type GlobalRoleBindingLister interface { + // List lists all GlobalRoleBindings in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.GlobalRoleBinding, err error) + // Get retrieves the GlobalRoleBinding from the index for a given name. + Get(name string) (*v1alpha2.GlobalRoleBinding, error) + GlobalRoleBindingListerExpansion +} + +// globalRoleBindingLister implements the GlobalRoleBindingLister interface. +type globalRoleBindingLister struct { + indexer cache.Indexer +} + +// NewGlobalRoleBindingLister returns a new GlobalRoleBindingLister. +func NewGlobalRoleBindingLister(indexer cache.Indexer) GlobalRoleBindingLister { + return &globalRoleBindingLister{indexer: indexer} +} + +// List lists all GlobalRoleBindings in the indexer. +func (s *globalRoleBindingLister) List(selector labels.Selector) (ret []*v1alpha2.GlobalRoleBinding, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.GlobalRoleBinding)) + }) + return ret, err +} + +// Get retrieves the GlobalRoleBinding from the index for a given name. +func (s *globalRoleBindingLister) Get(name string) (*v1alpha2.GlobalRoleBinding, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("globalrolebinding"), name) + } + return obj.(*v1alpha2.GlobalRoleBinding), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/role.go b/pkg/client/listers/iam/v1alpha2/role.go deleted file mode 100644 index 15dd8f053..000000000 --- a/pkg/client/listers/iam/v1alpha2/role.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. - -package v1alpha2 - -import ( - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// RoleLister helps list Roles. -type RoleLister interface { - // List lists all Roles in the indexer. - List(selector labels.Selector) (ret []*v1alpha2.Role, err error) - // Get retrieves the Role from the index for a given name. - Get(name string) (*v1alpha2.Role, error) - RoleListerExpansion -} - -// roleLister implements the RoleLister interface. -type roleLister struct { - indexer cache.Indexer -} - -// NewRoleLister returns a new RoleLister. -func NewRoleLister(indexer cache.Indexer) RoleLister { - return &roleLister{indexer: indexer} -} - -// List lists all Roles in the indexer. -func (s *roleLister) List(selector labels.Selector) (ret []*v1alpha2.Role, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha2.Role)) - }) - return ret, err -} - -// Get retrieves the Role from the index for a given name. -func (s *roleLister) Get(name string) (*v1alpha2.Role, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha2.Resource("role"), name) - } - return obj.(*v1alpha2.Role), nil -} diff --git a/pkg/client/listers/iam/v1alpha2/rolebinding.go b/pkg/client/listers/iam/v1alpha2/rolebinding.go deleted file mode 100644 index d3704c296..000000000 --- a/pkg/client/listers/iam/v1alpha2/rolebinding.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. - -package v1alpha2 - -import ( - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" -) - -// RoleBindingLister helps list RoleBindings. -type RoleBindingLister interface { - // List lists all RoleBindings in the indexer. - List(selector labels.Selector) (ret []*v1alpha2.RoleBinding, err error) - // Get retrieves the RoleBinding from the index for a given name. - Get(name string) (*v1alpha2.RoleBinding, error) - RoleBindingListerExpansion -} - -// roleBindingLister implements the RoleBindingLister interface. -type roleBindingLister struct { - indexer cache.Indexer -} - -// NewRoleBindingLister returns a new RoleBindingLister. -func NewRoleBindingLister(indexer cache.Indexer) RoleBindingLister { - return &roleBindingLister{indexer: indexer} -} - -// List lists all RoleBindings in the indexer. -func (s *roleBindingLister) List(selector labels.Selector) (ret []*v1alpha2.RoleBinding, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha2.RoleBinding)) - }) - return ret, err -} - -// Get retrieves the RoleBinding from the index for a given name. -func (s *roleBindingLister) Get(name string) (*v1alpha2.RoleBinding, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha2.Resource("rolebinding"), name) - } - return obj.(*v1alpha2.RoleBinding), nil -} diff --git a/pkg/client/listers/iam/v1alpha2/workspacerole.go b/pkg/client/listers/iam/v1alpha2/workspacerole.go new file mode 100644 index 000000000..8e9e74965 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/workspacerole.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// WorkspaceRoleLister helps list WorkspaceRoles. +type WorkspaceRoleLister interface { + // List lists all WorkspaceRoles in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRole, err error) + // Get retrieves the WorkspaceRole from the index for a given name. + Get(name string) (*v1alpha2.WorkspaceRole, error) + WorkspaceRoleListerExpansion +} + +// workspaceRoleLister implements the WorkspaceRoleLister interface. +type workspaceRoleLister struct { + indexer cache.Indexer +} + +// NewWorkspaceRoleLister returns a new WorkspaceRoleLister. +func NewWorkspaceRoleLister(indexer cache.Indexer) WorkspaceRoleLister { + return &workspaceRoleLister{indexer: indexer} +} + +// List lists all WorkspaceRoles in the indexer. +func (s *workspaceRoleLister) List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRole, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.WorkspaceRole)) + }) + return ret, err +} + +// Get retrieves the WorkspaceRole from the index for a given name. +func (s *workspaceRoleLister) Get(name string) (*v1alpha2.WorkspaceRole, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("workspacerole"), name) + } + return obj.(*v1alpha2.WorkspaceRole), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go b/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 000000000..175f17d31 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// WorkspaceRoleBindingLister helps list WorkspaceRoleBindings. +type WorkspaceRoleBindingLister interface { + // List lists all WorkspaceRoleBindings in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRoleBinding, err error) + // Get retrieves the WorkspaceRoleBinding from the index for a given name. + Get(name string) (*v1alpha2.WorkspaceRoleBinding, error) + WorkspaceRoleBindingListerExpansion +} + +// workspaceRoleBindingLister implements the WorkspaceRoleBindingLister interface. +type workspaceRoleBindingLister struct { + indexer cache.Indexer +} + +// NewWorkspaceRoleBindingLister returns a new WorkspaceRoleBindingLister. +func NewWorkspaceRoleBindingLister(indexer cache.Indexer) WorkspaceRoleBindingLister { + return &workspaceRoleBindingLister{indexer: indexer} +} + +// List lists all WorkspaceRoleBindings in the indexer. +func (s *workspaceRoleBindingLister) List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRoleBinding, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.WorkspaceRoleBinding)) + }) + return ret, err +} + +// Get retrieves the WorkspaceRoleBinding from the index for a given name. +func (s *workspaceRoleBindingLister) Get(name string) (*v1alpha2.WorkspaceRoleBinding, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("workspacerolebinding"), name) + } + return obj.(*v1alpha2.WorkspaceRoleBinding), nil +} diff --git a/pkg/kapis/iam/v1alpha2/handler.go b/pkg/kapis/iam/v1alpha2/handler.go index 37836f233..e4f3c41db 100644 --- a/pkg/kapis/iam/v1alpha2/handler.go +++ b/pkg/kapis/iam/v1alpha2/handler.go @@ -1,14 +1,20 @@ package v1alpha2 import ( + "fmt" "github.com/emicklei/go-restful" - "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/im" - "strings" + resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" ) type iamHandler struct { @@ -23,18 +29,6 @@ func newIAMHandler(im im.IdentityManagementInterface, am am.AccessManagementInte } } -func (h *iamHandler) CreateUser(req *restful.Request, resp *restful.Response) { - panic("implement me") -} - -func (h *iamHandler) DeleteUser(req *restful.Request, resp *restful.Response) { - panic("implement me") -} - -func (h *iamHandler) ModifyUser(request *restful.Request, response *restful.Response) { - panic("implement me") -} - func (h *iamHandler) DescribeUser(req *restful.Request, resp *restful.Response) { username := req.PathParameter("user") user, err := h.im.DescribeUser(username) @@ -43,108 +37,198 @@ func (h *iamHandler) DescribeUser(req *restful.Request, resp *restful.Response) return } - globalRole, err := h.am.GetRoleOfUserInTargetScope(iamv1alpha2.GlobalScope, "", username) + globalRole, err := h.am.GetGlobalRoleOfUser(username) if err != nil { api.HandleInternalError(resp, req, err) return } + result := iamv1alpha2.UserDetail{User: user, GlobalRole: globalRole} resp.WriteEntity(result) } func (h *iamHandler) ListUsers(req *restful.Request, resp *restful.Response) { - panic("implement me") -} - -func (h *iamHandler) ListUserRoles(req *restful.Request, resp *restful.Response) { - panic("implement me") -} - -func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) { - panic("implement me") -} - -func (h *iamHandler) ListRolesOfUser(req *restful.Request, resp *restful.Response) { - username := req.PathParameter("user") - - var roles []iamv1alpha2.Role - var err error - - if strings.HasSuffix(req.Request.URL.Path, "workspaceroles") { - roles, err = h.am.ListRolesOfUser(iamv1alpha2.WorkspaceScope, username) - } else if strings.HasSuffix(req.Request.URL.Path, "clusterroles") { - roles, err = h.am.ListRolesOfUser(iamv1alpha2.ClusterScope, username) - } else if strings.HasSuffix(req.Request.URL.Path, "namespaceroles") { - roles, err = h.am.ListRolesOfUser(iamv1alpha2.NamespaceScope, username) - } - + queryParam := query.ParseQueryParameter(req) + result, err := h.im.ListUsers(queryParam) if err != nil { api.HandleInternalError(resp, req, err) return } + for i, item := range result.Items { + user := item.(*iamv1alpha2.User) + user = user.DeepCopy() + role, err := h.am.GetGlobalRoleOfUser(user.Name) + if err != nil && !errors.IsNotFound(err) { + klog.Error(err) + api.HandleInternalError(resp, req, err) + return + } - result := iamv1alpha2.RoleList{ - TypeMeta: v1.TypeMeta{ - Kind: "List", - APIVersion: "v1", - }, - ListMeta: v1.ListMeta{}, - Items: roles, + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + if role != nil { + user.Annotations["iam.kubesphere.io/global-role"] = role.Name + } else { + user.Annotations["iam.kubesphere.io/global-role"] = "" + } + + result.Items[i] = user } resp.WriteEntity(result) + } + +func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) { + namespace := req.PathParameter("namespace") + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListRoles(namespace, queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + resp.WriteEntity(result) +} + func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) { - panic("implement me") + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListClusterRoles(queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + resp.WriteEntity(result) +} + +func (h *iamHandler) ListGlobalRoles(req *restful.Request, resp *restful.Response) { + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListGlobalRoles(queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + resp.WriteEntity(result) } func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response) { panic("implement me") } -// List users by namespace func (h *iamHandler) ListNamespaceUsers(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + queryParam := query.ParseQueryParameter(req) + namespace := req.PathParameter("namespace") -func (h *iamHandler) ListClusterRoleUsers(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + roleBindings, err := h.am.ListRoleBindings("", namespace) -func (h *iamHandler) ListClusterRoleRules(req *restful.Request, resp *restful.Response) { - panic("implement me") -} + if err != nil { + api.HandleInternalError(resp, req, err) + return + } -func (h *iamHandler) ListRoleRules(req *restful.Request, resp *restful.Response) { - panic("implement me") + users := make([]runtime.Object, 0) + + for _, roleBinding := range roleBindings { + for _, subject := range roleBinding.Subjects { + if subject.Kind == iamv1alpha2.ResourceKindUser { + user, err := h.im.DescribeUser(subject.Name) + + if errors.IsNotFound(err) { + klog.Errorf("orphan subject: %+v", subject) + continue + } + + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + + user = user.DeepCopy() + + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + user.Annotations["iam.kubesphere.io/role"] = roleBinding.RoleRef.Name + + users = append(users, user) + } + } + } + + result := resources.DefaultList(users, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + user := object.(*iamv1alpha2.User).ObjectMeta + return resources.DefaultObjectMetaFilter(user, filter) + }) + + resp.WriteEntity(result) } func (h *iamHandler) ListWorkspaceRoles(request *restful.Request, response *restful.Response) { - panic("implement me") -} + queryParam := query.ParseQueryParameter(request) + workspace := request.PathParameter("workspace") + queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s:%s", tenantv1alpha1.WorkspaceLabel, workspace)) -func (h *iamHandler) DescribeWorkspaceRole(request *restful.Request, response *restful.Response) { - panic("implement me") -} - -func (h *iamHandler) ListWorkspaceRoleRules(request *restful.Request, response *restful.Response) { - panic("implement me") + result, err := h.am.ListWorkspaceRoles(queryParam) + if err != nil { + api.HandleInternalError(response, request, err) + return + } + response.WriteEntity(result) } func (h *iamHandler) ListWorkspaceUsers(request *restful.Request, response *restful.Response) { - panic("implement me") -} + queryParam := query.ParseQueryParameter(request) + workspace := request.PathParameter("workspace") -func (h *iamHandler) InviteUser(request *restful.Request, response *restful.Response) { - panic("implement me") -} + roleBindings, err := h.am.ListWorkspaceRoleBindings("", workspace) -func (h *iamHandler) RemoveUser(request *restful.Request, response *restful.Response) { - panic("implement me") -} + if err != nil { + api.HandleInternalError(response, request, err) + return + } -func (h *iamHandler) DescribeWorkspaceUser(request *restful.Request, response *restful.Response) { - panic("implement me") + users := make([]runtime.Object, 0) + + for _, roleBinding := range roleBindings { + for _, subject := range roleBinding.Subjects { + if subject.Kind == iamv1alpha2.ResourceKindUser { + user, err := h.im.DescribeUser(subject.Name) + + if errors.IsNotFound(err) { + klog.Errorf("orphan subject: %+v", subject) + continue + } + + if err != nil { + api.HandleInternalError(response, request, err) + return + } + + user = user.DeepCopy() + + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + user.Annotations["iam.kubesphere.io/workspace-role"] = roleBinding.RoleRef.Name + + users = append(users, user) + } + } + } + + result := resources.DefaultList(users, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + user := object.(*iamv1alpha2.User).ObjectMeta + return resources.DefaultObjectMetaFilter(user, filter) + }) + + response.WriteEntity(result) } diff --git a/pkg/kapis/iam/v1alpha2/register.go b/pkg/kapis/iam/v1alpha2/register.go index 9a9ce7b09..eb87ca5b8 100644 --- a/pkg/kapis/iam/v1alpha2/register.go +++ b/pkg/kapis/iam/v1alpha2/register.go @@ -29,97 +29,70 @@ import ( "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/models/iam/im" - "kubesphere.io/kubesphere/pkg/server/errors" "net/http" ) -const groupName = "iam.kubesphere.io" +const ( + GroupName = "iam.kubesphere.io" +) -var GroupVersion = schema.GroupVersion{Group: groupName, Version: "v1alpha2"} +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} func AddToContainer(container *restful.Container, im im.IdentityManagementInterface, am am.AccessManagementInterface, options *authoptions.AuthenticationOptions) error { ws := runtime.NewWebService(GroupVersion) handler := newIAMHandler(im, am, options) + // global resource + ws.Route(ws.GET("/users"). + To(handler.ListUsers). + Doc("List all users."). + Param(ws.PathParameter("user", "username")). + Returns(http.StatusOK, api.StatusOK, api.ListResult{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) + // global resource ws.Route(ws.GET("/users/{user}"). To(handler.DescribeUser). Doc("Retrieve user details."). Param(ws.PathParameter("user", "username")). Returns(http.StatusOK, api.StatusOK, iamv1alpha2.UserDetail{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/users/{user}/workspaceroles"). - To(handler.ListRolesOfUser). - Doc("Retrieve user roles in workspaces."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/users/{user}/clusterroles"). - To(handler.ListRolesOfUser). - Doc("Retrieve user roles in clusters."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/users/{user}/namespaceroles"). - To(handler.ListRolesOfUser). - Doc("Retrieve user roles in namespaces."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, api.StatusOK, iamv1alpha2.RoleList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/namespaces/{namespace}/roles"). - To(handler.ListRoles). - Doc("Retrieve the roles that are assigned to the user in the specified namespace."). - Param(ws.PathParameter("namespace", "kubernetes namespace")). + // global resource + ws.Route(ws.GET("/globalroles"). + To(handler.ListGlobalRoles). + Doc("List all cluster roles."). Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/clusterroles"). To(handler.ListClusterRoles). - Doc("List all cluster roles."). + Doc("List cluster roles."). Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - // TODO merge - //ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users")) - ws.Route(ws.GET("/namespaces/{namespace}/users"). - To(handler.ListNamespaceUsers). - Doc("List all users in the specified namespace."). - Param(ws.PathParameter("namespace", "kubernetes namespace")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/clusterroles/{clusterrole}/users"). - To(handler.ListClusterRoleUsers). - Doc("List all users that are bound to the specified cluster role."). - Param(ws.PathParameter("clusterrole", "cluster role name")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/workspaces/{workspace}/roles"). - To(handler.ListWorkspaceRoles). - Doc("List all workspace roles."). - Param(ws.PathParameter("workspace", "workspace name")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - - ws.Route(ws.GET("/workspaces/{workspace}/members"). + ws.Route(ws.GET("/workspaces/{workspace}/users"). To(handler.ListWorkspaceUsers). Doc("List all members in the specified workspace."). Param(ws.PathParameter("workspace", "workspace name")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - // TODO re-design - ws.Route(ws.POST("/workspaces/{workspace}/members"). - To(handler.InviteUser). - Doc("Invite a member to the specified workspace."). + ws.Route(ws.GET("/workspaces/{workspace}/workspaceroles"). + To(handler.ListWorkspaceRoles). + Doc("List all workspace roles."). Param(ws.PathParameter("workspace", "workspace name")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.DELETE("/workspaces/{workspace}/members/{member}"). - To(handler.RemoveUser). - Doc("Remove the specified member from the workspace."). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("member", "username")). - Returns(http.StatusOK, api.StatusOK, errors.Error{}). + + ws.Route(ws.GET("/namespaces/{namespace}/users"). + To(handler.ListNamespaceUsers). + Doc("List all users in the specified namespace."). + Param(ws.PathParameter("namespace", "kubernetes namespace")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) + + ws.Route(ws.GET("/namespaces/{namespace}/roles"). + To(handler.ListRoles). + Doc("List all roles in the specified namespace."). + Param(ws.PathParameter("namespace", "namespace")). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) container.Add(ws) diff --git a/pkg/kapis/resources/v1alpha2/register.go b/pkg/kapis/resources/v1alpha2/register.go index 557e46cea..e635e0a88 100644 --- a/pkg/kapis/resources/v1alpha2/register.go +++ b/pkg/kapis/resources/v1alpha2/register.go @@ -71,7 +71,7 @@ func AddToContainer(c *restful.Container, client kubernetes.Interface, factory i Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}). Doc("Cluster level resources"). - Param(webservice.PathParameter("resources", "cluster level resource type, e.g. nodes,workspaces,storageclasses,clusterroles.")). + Param(webservice.PathParameter("resources", "cluster level resource type, e.g. nodes,workspaces,storageclasses,clusterrole.")). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). DataFormat("key=value,key~value"). diff --git a/pkg/kapis/resources/v1alpha3/handler.go b/pkg/kapis/resources/v1alpha3/handler.go index 38316c3e5..75a8f1f67 100644 --- a/pkg/kapis/resources/v1alpha3/handler.go +++ b/pkg/kapis/resources/v1alpha3/handler.go @@ -9,20 +9,20 @@ import ( "kubesphere.io/kubesphere/pkg/models/components" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" resourcev1alpha2 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource" - resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" "kubesphere.io/kubesphere/pkg/server/params" "strings" ) type Handler struct { - resourceGetterV1alpha3 *resourcev1alpha3.ResourceGetter + resourceGetterV1alpha3 *resource.ResourceGetter resourcesGetterV1alpha2 *resourcev1alpha2.ResourceGetter componentsGetter components.ComponentsGetter } func New(factory informers.InformerFactory) *Handler { return &Handler{ - resourceGetterV1alpha3: resourcev1alpha3.NewResourceGetter(factory), + resourceGetterV1alpha3: resource.NewResourceGetter(factory), resourcesGetterV1alpha2: resourcev1alpha2.NewResourceGetter(factory), componentsGetter: components.NewComponentsGetter(factory.KubernetesSharedInformerFactory()), } @@ -41,7 +41,7 @@ func (h *Handler) handleListResources(request *restful.Request, response *restfu return } - if err != resourcev1alpha3.ErrResourceNotSupported { + if err != resource.ErrResourceNotSupported { klog.Error(err) api.HandleInternalError(response, nil, err) return @@ -63,20 +63,20 @@ func (h *Handler) fallback(resourceType string, namespace string, q *query.Query orderBy := string(q.SortBy) limit, offset := q.Pagination.Limit, q.Pagination.Offset reverse := !q.Ascending - conditions := ¶ms.Conditions{} - for _, filter := range q.Filters { - switch filter.Field { + conditions := ¶ms.Conditions{Match: make(map[string]string, 0), Fuzzy: make(map[string]string, 0)} + for field, value := range q.Filters { + switch field { case query.FieldName: - conditions.Match[v1alpha2.Name] = string(filter.Value) + conditions.Match[v1alpha2.Name] = string(value) break case query.FieldCreationTimeStamp: - conditions.Match[v1alpha2.CreateTime] = string(filter.Value) + conditions.Match[v1alpha2.CreateTime] = string(value) break case query.FieldLastUpdateTimestamp: - conditions.Match[v1alpha2.UpdateTime] = string(filter.Value) + conditions.Match[v1alpha2.UpdateTime] = string(value) break case query.FieldLabel: - values := strings.SplitN(string(filter.Value), ":", 2) + values := strings.SplitN(string(value), ":", 2) if len(values) == 2 { conditions.Match[values[0]] = values[1] } else { @@ -84,7 +84,7 @@ func (h *Handler) fallback(resourceType string, namespace string, q *query.Query } break case query.FieldAnnotation: - values := strings.SplitN(string(filter.Value), ":", 2) + values := strings.SplitN(string(value), ":", 2) if len(values) == 2 { conditions.Match[v1alpha2.Annotation] = values[1] } else { @@ -92,13 +92,15 @@ func (h *Handler) fallback(resourceType string, namespace string, q *query.Query } break case query.FieldStatus: - conditions.Match[v1alpha2.Status] = string(filter.Value) + conditions.Match[v1alpha2.Status] = string(value) break case query.FieldOwnerReference: - conditions.Match[v1alpha2.Owner] = string(filter.Value) + conditions.Match[v1alpha2.Owner] = string(value) + break + default: + conditions.Match[string(field)] = string(value) break } - } result, err := h.resourcesGetterV1alpha2.ListResources(namespace, resourceType, conditions, orderBy, reverse, limit, offset) diff --git a/pkg/kapis/resources/v1alpha3/handler_test.go b/pkg/kapis/resources/v1alpha3/handler_test.go index 1fb67ebad..4fa8b2f2a 100644 --- a/pkg/kapis/resources/v1alpha3/handler_test.go +++ b/pkg/kapis/resources/v1alpha3/handler_test.go @@ -12,7 +12,7 @@ import ( "kubesphere.io/kubesphere/pkg/apiserver/query" fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" "kubesphere.io/kubesphere/pkg/informers" - resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" "testing" ) @@ -92,7 +92,7 @@ func listResources(namespace, resourceType string, query *query.Query, h *Handle return result, nil } - if err != resourcev1alpha3.ErrResourceNotSupported { + if err != resource.ErrResourceNotSupported { return nil, err } diff --git a/pkg/kapis/tenant/v1alpha2/handler.go b/pkg/kapis/tenant/v1alpha2/handler.go index ebbe12d80..96d5473eb 100644 --- a/pkg/kapis/tenant/v1alpha2/handler.go +++ b/pkg/kapis/tenant/v1alpha2/handler.go @@ -5,6 +5,7 @@ import ( "github.com/emicklei/go-restful" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/tenant" @@ -15,15 +16,16 @@ type tenantHandler struct { tenant tenant.Interface } -func newTenantHandler(k8sClient k8s.Client, factory informers.InformerFactory) *tenantHandler { +func newTenantHandler(_ k8s.Client, factory informers.InformerFactory) *tenantHandler { return &tenantHandler{ - tenant: tenant.New(k8sClient, factory), + tenant: tenant.New(factory), } } func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Response) { user, ok := request.UserFrom(req.Request.Context()) + queryParam := query.ParseQueryParameter(req) if !ok { err := errors.New("cannot obtain user info") @@ -32,7 +34,7 @@ func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Respo return } - result, err := h.tenant.ListWorkspaces(user) + result, err := h.tenant.ListWorkspaces(user, queryParam) if err != nil { api.HandleInternalError(resp, nil, err) @@ -44,6 +46,7 @@ func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Respo func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Response) { user, ok := request.UserFrom(req.Request.Context()) + queryParam := query.ParseQueryParameter(req) if !ok { err := errors.New("cannot obtain user info") @@ -54,7 +57,7 @@ func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Respo workspace := req.PathParameter("workspace") - result, err := h.tenant.ListNamespaces(user, workspace) + result, err := h.tenant.ListNamespaces(user, workspace, queryParam) if err != nil { api.HandleInternalError(resp, nil, err) diff --git a/pkg/models/iam/am/am.go b/pkg/models/iam/am/am.go index 5fbc69523..b7ddb0dfa 100644 --- a/pkg/models/iam/am/am.go +++ b/pkg/models/iam/am/am.go @@ -19,103 +19,81 @@ package am import ( "fmt" + rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + k8sinformers "k8s.io/client-go/informers" "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/informers" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" "net/http" ) type AccessManagementInterface interface { - ListRolesOfUser(scope iamv1alpha2.Scope, username string) ([]iamv1alpha2.Role, error) - GetRoleOfUserInTargetScope(scope iamv1alpha2.Scope, target string, username string) (*iamv1alpha2.Role, error) - GetPolicyRule(name string) (*iamv1alpha2.PolicyRule, error) + GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error) + GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error) + GetClusterRoleOfUser(username, cluster string) (*rbacv1.ClusterRole, error) + GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error) + ListRoles(username string, query *query.Query) (*api.ListResult, error) + ListClusterRoles(query *query.Query) (*api.ListResult, error) + ListWorkspaceRoles(query *query.Query) (*api.ListResult, error) + ListGlobalRoles(query *query.Query) (*api.ListResult, error) + + ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error) + ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error) + ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error) + ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) + + GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) } type amOperator struct { - informers informers.SharedInformerFactory - ksClient kubesphere.Interface + ksinformer ksinformers.SharedInformerFactory + k8sinformer k8sinformers.SharedInformerFactory + resourceGetter *resourcev1alpha3.ResourceGetter } -func NewAMOperator(ksClient kubesphere.Interface, informers informers.SharedInformerFactory) AccessManagementInterface { +func NewAMOperator(factory informers.InformerFactory) AccessManagementInterface { return &amOperator{ - informers: informers, - ksClient: ksClient, + ksinformer: factory.KubeSphereSharedInformerFactory(), + k8sinformer: factory.KubernetesSharedInformerFactory(), + resourceGetter: resourcev1alpha3.NewResourceGetter(factory), } } -func containsUser(subjets []iamv1alpha2.Subject, username string) bool { - for _, sub := range subjets { - if sub.Kind == iamv1alpha2.UserKind && sub.Name == username { - return true - } - } - return false -} +func (am *amOperator) GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error) { -func (am *amOperator) ListRolesOfUser(scope iamv1alpha2.Scope, username string) ([]iamv1alpha2.Role, error) { - - lister := am.informers.Iam().V1alpha2().RoleBindings().Lister() - - roleBindings, err := lister.List(labels.Everything()) + roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything()) if err != nil { klog.Error(err) return nil, err } - roleBindingsInScope := filterRoleBindingByScope(roleBindings, scope) + userRoleBindings := make([]*iamv1alpha2.GlobalRoleBinding, 0) - roles := make([]iamv1alpha2.Role, 0) - - for _, roleBinding := range roleBindingsInScope { - if containsUser(roleBinding.Subjects, username) { - role, err := am.informers. - Iam().V1alpha2().Roles().Lister().Get(roleBinding.RoleRef.Name) - - if err != nil { - if errors.IsNotFound(err) { - continue - } - return nil, err - } - - roles = append(roles, *role) + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) } } - return roles, nil -} - -func filterRoleBindingByScope(roles []*iamv1alpha2.RoleBinding, scope iamv1alpha2.Scope) []*iamv1alpha2.RoleBinding { - result := make([]*iamv1alpha2.RoleBinding, 0) - for _, role := range roles { - if role.Scope == scope { - result = append(result, role) + if len(userRoleBindings) > 0 { + role, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err } - } - return result -} - -func (am *amOperator) GetPolicyRule(name string) (*iamv1alpha2.PolicyRule, error) { - lister := am.informers.Iam().V1alpha2().PolicyRules().Lister() - return lister.Get(name) -} - -// Users can only bind one role at each level -func (am *amOperator) GetRoleOfUserInTargetScope(scope iamv1alpha2.Scope, target, username string) (*iamv1alpha2.Role, error) { - roles, err := am.ListRolesOfUser(scope, username) - if err != nil { - klog.Error(err) - return nil, err - } - for _, role := range roles { - if role.Target.Name == target { - return &role, nil + if len(roleBindings) > 1 { + klog.Warningf("conflict global role binding, username: %s", username) } + return role, nil } err = &errors.StatusError{ErrStatus: metav1.Status{ @@ -124,11 +102,274 @@ func (am *amOperator) GetRoleOfUserInTargetScope(scope iamv1alpha2.Scope, target Reason: metav1.StatusReasonNotFound, Details: &metav1.StatusDetails{ Group: iamv1alpha2.SchemeGroupVersion.Group, - Kind: iamv1alpha2.RoleBindingKind, + Kind: iamv1alpha2.ResourceKindGlobalRoleBinding, }, - Message: fmt.Sprintf("role bind not found in %s %s scope", target, scope), + Message: fmt.Sprintf("global role binding not found for %s", username), }} - klog.Error(err) return nil, err } + +func (am *amOperator) GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error) { + + roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.SelectorFromValidatedSet(labels.Set{tenantv1alpha1.WorkspaceLabel: workspace})) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*iamv1alpha2.WorkspaceRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) + } + } + + if len(userRoleBindings) > 0 { + role, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) + + if err != nil { + klog.Error(err) + return nil, err + } + + if len(roleBindings) > 1 { + klog.Warningf("conflict workspace role binding, username: %s", username) + } + + return role, nil + } + + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: iamv1alpha2.SchemeGroupVersion.Group, + Kind: iamv1alpha2.ResourceKindWorkspaceRoleBinding, + }, + Message: fmt.Sprintf("workspace role binding not found for %s", username), + }} + + return nil, err +} + +func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error) { + roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*rbacv1.RoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) + } + } + + if len(userRoleBindings) > 0 { + role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(namespace).Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err + } + if len(roleBindings) > 1 { + klog.Warningf("conflict role binding, username: %s", username) + } + return role, nil + } + + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: rbacv1.SchemeGroupVersion.Group, + Kind: "RoleBinding", + }, + Message: fmt.Sprintf("role binding not found for %s in %s", username, namespace), + }} + + return nil, err +} + +// Get federated clusterrole of user if cluster is not empty, if cluster is empty means current cluster +func (am *amOperator) GetClusterRoleOfUser(username, cluster string) (*rbacv1.ClusterRole, error) { + roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*rbacv1.ClusterRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) + } + } + + if len(userRoleBindings) > 0 { + role, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err + } + if len(roleBindings) > 1 { + klog.Warningf("conflict cluster role binding, username: %s", username) + } + return role, nil + } + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: rbacv1.SchemeGroupVersion.Group, + Kind: "ClusterRoleBinding", + }, + Message: fmt.Sprintf("cluster role binding not found for %s in %s", username, cluster), + }} + + return nil, err +} + +func (am *amOperator) ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error) { + roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*iamv1alpha2.WorkspaceRoleBinding, 0) + + for _, roleBinding := range roleBindings { + inSpecifiedWorkspace := workspace == "" || roleBinding.Labels[tenantv1alpha1.WorkspaceLabel] == workspace + if contains(roleBinding.Subjects, username) && inSpecifiedWorkspace { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error) { + + roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*rbacv1.ClusterRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error) { + roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*iamv1alpha2.GlobalRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) { + + roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + result := make([]*rbacv1.RoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func contains(subjects []rbacv1.Subject, username string) bool { + for _, subject := range subjects { + if subject.Kind == rbacv1.UserKind && (username == "" || subject.Name == username) { + return true + } + } + return false +} + +func (am *amOperator) ListRoles(namespace string, query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List("roles", namespace, query) +} + +func (am *amOperator) ListClusterRoles(query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List("clusterroles", "", query) +} + +func (am *amOperator) ListWorkspaceRoles(queryParam *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List(iamv1alpha2.ResourcesPluralWorkspaceRole, "", queryParam) +} + +func (am *amOperator) ListGlobalRoles(query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List(iamv1alpha2.ResourcesPluralGlobalRole, "", query) +} + +// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding. +func (am *amOperator) GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) { + switch roleRef.Kind { + case "Role": + role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(bindingNamespace).Get(roleRef.Name) + if err != nil { + return nil, err + } + return role.Rules, nil + + case "ClusterRole": + clusterRole, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return clusterRole.Rules, nil + case iamv1alpha2.ResourceKindGlobalRole: + globalRole, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return globalRole.Rules, nil + case iamv1alpha2.ResourceKindWorkspaceRole: + workspaceRole, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return workspaceRole.Rules, nil + default: + return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind) + } +} diff --git a/pkg/models/iam/am/rbac/evaluation_helpers.go b/pkg/models/iam/am/rbac/evaluation_helpers.go new file mode 100644 index 000000000..abe010bda --- /dev/null +++ b/pkg/models/iam/am/rbac/evaluation_helpers.go @@ -0,0 +1,179 @@ +/* +Copyright 2018 The Kubernetes 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. +*/ + +package authorizerfactory + +import ( + "fmt" + "strings" + + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func RoleRefGroupKind(roleRef rbacv1.RoleRef) schema.GroupKind { + return schema.GroupKind{Group: roleRef.APIGroup, Kind: roleRef.Kind} +} + +func VerbMatches(rule *rbacv1.PolicyRule, requestedVerb string) bool { + for _, ruleVerb := range rule.Verbs { + if ruleVerb == rbacv1.VerbAll { + return true + } + if ruleVerb == requestedVerb { + return true + } + } + + return false +} + +func APIGroupMatches(rule *rbacv1.PolicyRule, requestedGroup string) bool { + for _, ruleGroup := range rule.APIGroups { + if ruleGroup == rbacv1.APIGroupAll { + return true + } + if ruleGroup == requestedGroup { + return true + } + } + + return false +} + +func ResourceMatches(rule *rbacv1.PolicyRule, combinedRequestedResource, requestedSubresource string) bool { + for _, ruleResource := range rule.Resources { + // if everything is allowed, we match + if ruleResource == rbacv1.ResourceAll { + return true + } + // if we have an exact match, we match + if ruleResource == combinedRequestedResource { + return true + } + + // We can also match a */subresource. + // if there isn't a subresource, then continue + if len(requestedSubresource) == 0 { + continue + } + // if the rule isn't in the format */subresource, then we don't match, continue + if len(ruleResource) == len(requestedSubresource)+2 && + strings.HasPrefix(ruleResource, "*/") && + strings.HasSuffix(ruleResource, requestedSubresource) { + return true + + } + } + + return false +} + +func ResourceNameMatches(rule *rbacv1.PolicyRule, requestedName string) bool { + if len(rule.ResourceNames) == 0 { + return true + } + + for _, ruleName := range rule.ResourceNames { + if ruleName == requestedName { + return true + } + } + + return false +} + +func NonResourceURLMatches(rule *rbacv1.PolicyRule, requestedURL string) bool { + for _, ruleURL := range rule.NonResourceURLs { + if ruleURL == rbacv1.NonResourceAll { + return true + } + if ruleURL == requestedURL { + return true + } + if strings.HasSuffix(ruleURL, "*") && strings.HasPrefix(requestedURL, strings.TrimRight(ruleURL, "*")) { + return true + } + } + + return false +} + +// subjectsStrings returns users, groups, serviceaccounts, unknown for display purposes. +func SubjectsStrings(subjects []rbacv1.Subject) ([]string, []string, []string, []string) { + users := []string{} + groups := []string{} + sas := []string{} + others := []string{} + + for _, subject := range subjects { + switch subject.Kind { + case rbacv1.ServiceAccountKind: + sas = append(sas, fmt.Sprintf("%s/%s", subject.Namespace, subject.Name)) + + case rbacv1.UserKind: + users = append(users, subject.Name) + + case rbacv1.GroupKind: + groups = append(groups, subject.Name) + + default: + others = append(others, fmt.Sprintf("%s/%s/%s", subject.Kind, subject.Namespace, subject.Name)) + } + } + + return users, groups, sas, others +} + +func String(r rbacv1.PolicyRule) string { + return "PolicyRule" + CompactString(r) +} + +// CompactString exposes a compact string representation for use in escalation error messages +func CompactString(r rbacv1.PolicyRule) string { + formatStringParts := []string{} + formatArgs := []interface{}{} + if len(r.APIGroups) > 0 { + formatStringParts = append(formatStringParts, "APIGroups:%q") + formatArgs = append(formatArgs, r.APIGroups) + } + if len(r.Resources) > 0 { + formatStringParts = append(formatStringParts, "Resources:%q") + formatArgs = append(formatArgs, r.Resources) + } + if len(r.NonResourceURLs) > 0 { + formatStringParts = append(formatStringParts, "NonResourceURLs:%q") + formatArgs = append(formatArgs, r.NonResourceURLs) + } + if len(r.ResourceNames) > 0 { + formatStringParts = append(formatStringParts, "ResourceNames:%q") + formatArgs = append(formatArgs, r.ResourceNames) + } + if len(r.Verbs) > 0 { + formatStringParts = append(formatStringParts, "Verbs:%q") + formatArgs = append(formatArgs, r.Verbs) + } + formatString := "{" + strings.Join(formatStringParts, ", ") + "}" + return fmt.Sprintf(formatString, formatArgs...) +} + +type SortableRuleSlice []rbacv1.PolicyRule + +func (s SortableRuleSlice) Len() int { return len(s) } +func (s SortableRuleSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortableRuleSlice) Less(i, j int) bool { + return strings.Compare(s[i].String(), s[j].String()) < 0 +} diff --git a/pkg/models/iam/im/im.go b/pkg/models/iam/im/im.go index 66f3b6228..c1002c261 100644 --- a/pkg/models/iam/im/im.go +++ b/pkg/models/iam/im/im.go @@ -19,11 +19,20 @@ package im import ( "github.com/pkg/errors" + "golang.org/x/crypto/bcrypt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/informers" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" ) type IdentityManagementInterface interface { CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) + ListUsers(query *query.Query) (*api.ListResult, error) DeleteUser(username string) error ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) DescribeUser(username string) (*iamv1alpha2.User, error) @@ -36,3 +45,75 @@ var ( UserAlreadyExists = errors.New("user already exists") UserNotExists = errors.New("user not exists") ) + +func NewOperator(ksClient kubesphereclient.Interface, factory informers.InformerFactory) IdentityManagementInterface { + + return &defaultIMOperator{ + ksClient: ksClient, + resourceGetter: resourcev1alpha3.NewResourceGetter(factory), + } + +} + +type defaultIMOperator struct { + ksClient kubesphereclient.Interface + resourceGetter *resourcev1alpha3.ResourceGetter +} + +func (im *defaultIMOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { + return im.ksClient.IamV1alpha2().Users().Update(user) +} + +func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) { + + user, err := im.DescribeUser(username) + + if err != nil { + klog.Error(err) + return nil, err + } + if checkPasswordHash(password, user.Spec.EncryptedPassword) { + return user, nil + } + return nil, AuthFailedIncorrectPassword +} + +func (im *defaultIMOperator) ListUsers(query *query.Query) (*api.ListResult, error) { + + result, err := im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", query) + + if err != nil { + klog.Error(err) + return nil, err + } + + return result, nil +} + +func checkPasswordHash(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +func (im *defaultIMOperator) DescribeUser(username string) (*iamv1alpha2.User, error) { + user, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username) + + if err != nil { + klog.Error(err) + return nil, err + } + return user.(*iamv1alpha2.User), nil +} + +func (im *defaultIMOperator) DeleteUser(username string) error { + return im.ksClient.IamV1alpha2().Users().Delete(username, metav1.NewDeleteOptions(0)) +} + +func (im *defaultIMOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { + user, err := im.ksClient.IamV1alpha2().Users().Create(user) + if err != nil { + klog.Error(err) + return nil, err + } + return user, nil +} diff --git a/pkg/models/iam/im/im_operator.go b/pkg/models/iam/im/im_operator.go deleted file mode 100644 index 8bf7f28ec..000000000 --- a/pkg/models/iam/im/im_operator.go +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2020 The KubeSphere 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. - * / - */ - -package im - -import ( - "golang.org/x/crypto/bcrypt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" -) - -func NewOperator(ksClient kubesphereclient.Interface, informer informers.SharedInformerFactory) IdentityManagementInterface { - - return &defaultIMOperator{ - ksClient: ksClient, - informer: informer, - } - -} - -type defaultIMOperator struct { - ksClient kubesphereclient.Interface - informer informers.SharedInformerFactory -} - -func (im *defaultIMOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { - return im.ksClient.IamV1alpha2().Users().Update(user) -} - -func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) { - - user, err := im.ksClient.IamV1alpha2().Users().Get(username, metav1.GetOptions{}) - - if err != nil { - return nil, err - } - if checkPasswordHash(password, user.Spec.EncryptedPassword) { - return user, nil - } - return nil, AuthFailedIncorrectPassword -} - -func checkPasswordHash(password, hash string) bool { - err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) - return err == nil -} - -func (im *defaultIMOperator) DescribeUser(username string) (*iamv1alpha2.User, error) { - user, err := im.ksClient.IamV1alpha2().Users().Get(username, metav1.GetOptions{}) - - if err != nil { - return nil, err - } - return user, nil -} - -func (im *defaultIMOperator) DeleteUser(username string) error { - return im.ksClient.IamV1alpha2().Users().Delete(username, metav1.NewDeleteOptions(0)) -} - -func (im *defaultIMOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { - user, err := im.ksClient.IamV1alpha2().Users().Create(user) - if err != nil { - return nil, err - } - - return user, nil -} diff --git a/pkg/models/iam/im/ldap_operator.go b/pkg/models/iam/im/ldap_operator.go index 10e40250e..69724f926 100644 --- a/pkg/models/iam/im/ldap_operator.go +++ b/pkg/models/iam/im/ldap_operator.go @@ -19,7 +19,9 @@ package im import ( + "kubesphere.io/kubesphere/pkg/api" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/simple/client/ldap" ) @@ -31,7 +33,6 @@ func NewLDAPOperator(ldapClient ldap.Interface) IdentityManagementInterface { return &ldapOperator{ ldapClient: ldapClient, } - } func (im *ldapOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { @@ -78,3 +79,7 @@ func (im *ldapOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, e return user, nil } + +func (im *ldapOperator) ListUsers(query *query.Query) (*api.ListResult, error) { + panic("not implement") +} diff --git a/pkg/models/resources/v1alpha2/interface.go b/pkg/models/resources/v1alpha2/interface.go index 11f556e5d..803cefa0e 100644 --- a/pkg/models/resources/v1alpha2/interface.go +++ b/pkg/models/resources/v1alpha2/interface.go @@ -81,6 +81,13 @@ func ObjectMetaExactlyMath(key, value string, item metav1.ObjectMeta) bool { if !strings.Contains(item.Name, value) && !FuzzyMatch(item.Labels, "", value) && !FuzzyMatch(item.Annotations, "", value) { return false } + case Owner: + for _, ownerReference := range item.OwnerReferences { + if strings.Compare(string(ownerReference.UID), value) == 0 { + return true + } + } + return false default: // label not exist or value not equal if val, ok := item.Labels[key]; !ok || val != value { @@ -109,13 +116,6 @@ func ObjectMetaFuzzyMath(key, value string, item metav1.ObjectMeta) bool { if !strings.Contains(item.Labels[Chart], value) && !strings.Contains(item.Labels[Release], value) { return false } - case Owner: - for _, ownerReference := range item.OwnerReferences { - if strings.Compare(string(ownerReference.UID), value) == 0 { - return true - } - } - return false default: if !FuzzyMatch(item.Labels, key, value) { return false diff --git a/pkg/models/resources/v1alpha2/resource/resources.go b/pkg/models/resources/v1alpha2/resource/resources.go index ffdae3900..ae0a709fd 100644 --- a/pkg/models/resources/v1alpha2/resource/resources.go +++ b/pkg/models/resources/v1alpha2/resource/resources.go @@ -115,7 +115,7 @@ func (r *ResourceGetter) ListResources(namespace, resource string, conditions *p // none namespace resource if namespace != "" && sliceutil.HasString(clusterResources, resource) { - err = fmt.Errorf("namespaced resource %s not found", resource) + err = fmt.Errorf("resource %s is not supported", resource) klog.Errorln(err) return nil, err } @@ -123,7 +123,7 @@ func (r *ResourceGetter) ListResources(namespace, resource string, conditions *p if searcher, ok := r.resourcesGetters[resource]; ok { result, err = searcher.Search(namespace, conditions, orderBy, reverse) } else { - err = fmt.Errorf("namespaced resource %s not found", resource) + err = fmt.Errorf("resource %s is not supported", resource) klog.Errorln(err) return nil, err } diff --git a/pkg/models/resources/v1alpha3/application/applications.go b/pkg/models/resources/v1alpha3/application/applications.go index 433fdd9d5..c88e29fa3 100644 --- a/pkg/models/resources/v1alpha3/application/applications.go +++ b/pkg/models/resources/v1alpha3/application/applications.go @@ -20,11 +20,11 @@ package application import ( appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "time" ) type applicationsGetter struct { @@ -40,7 +40,7 @@ func (d *applicationsGetter) Get(namespace, name string) (runtime.Object, error) } func (d *applicationsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { - all, err := d.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(labels.Everything()) + all, err := d.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(query.Selector()) if err != nil { return nil, err } @@ -64,8 +64,14 @@ func (d *applicationsGetter) compare(left runtime.Object, right runtime.Object, if !ok { return false } - - return v1alpha3.DefaultObjectMetaCompare(leftApplication.ObjectMeta, rightApplication.ObjectMeta, field) + switch field { + case query.FieldUpdateTime: + fallthrough + case query.FieldLastUpdateTimestamp: + return lastUpdateTime(leftApplication).After(lastUpdateTime(rightApplication)) + default: + return v1alpha3.DefaultObjectMetaCompare(leftApplication.ObjectMeta, rightApplication.ObjectMeta, field) + } } func (d *applicationsGetter) filter(object runtime.Object, filter query.Filter) bool { @@ -76,3 +82,13 @@ func (d *applicationsGetter) filter(object runtime.Object, filter query.Filter) return v1alpha3.DefaultObjectMetaFilter(application.ObjectMeta, filter) } + +func lastUpdateTime(deployment *appv1beta1.Application) time.Time { + lut := deployment.CreationTimestamp.Time + for _, condition := range deployment.Status.Conditions { + if condition.LastUpdateTime.After(lut) { + lut = condition.LastUpdateTime.Time + } + } + return lut +} diff --git a/pkg/models/resources/v1alpha3/application/applications_test.go b/pkg/models/resources/v1alpha3/application/applications_test.go index acb6c468e..41c988e1e 100644 --- a/pkg/models/resources/v1alpha3/application/applications_test.go +++ b/pkg/models/resources/v1alpha3/application/applications_test.go @@ -59,12 +59,7 @@ func TestListApplications(t *testing.T) { }, SortBy: query.FieldName, Ascending: false, - Filters: []query.Filter{ - { - Field: query.FieldNamespace, - Value: query.Value("bar2"), - }, - }, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("bar2")}, }, api.ListResult{ Items: []interface{}{ diff --git a/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go b/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go new file mode 100644 index 000000000..239243120 --- /dev/null +++ b/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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. + +*/ +package clusterrole + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type rolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &rolesGetter{sharedInformers: sharedInformers} +} + +func (d *rolesGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Rbac().V1().ClusterRoles().Lister().Get(name) +} + +func (d *rolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Rbac().V1().ClusterRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *rolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*rbacv1.ClusterRole) + if !ok { + return false + } + + rightRole, ok := right.(*rbacv1.ClusterRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*rbacv1.ClusterRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go b/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go new file mode 100644 index 000000000..7e8f60c24 --- /dev/null +++ b/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go @@ -0,0 +1,94 @@ +package clusterrole + +import ( + "github.com/google/go-cmp/cmp" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListClusterRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Rbac().V1().ClusterRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/configmap/configmaps.go b/pkg/models/resources/v1alpha3/configmap/configmaps.go index 9b5ee74fa..76715aa2b 100644 --- a/pkg/models/resources/v1alpha3/configmap/configmaps.go +++ b/pkg/models/resources/v1alpha3/configmap/configmaps.go @@ -19,7 +19,6 @@ package configmap import ( corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/informers" "kubesphere.io/kubesphere/pkg/api" @@ -40,7 +39,7 @@ func (d *configmapsGetter) Get(namespace, name string) (runtime.Object, error) { } func (d *configmapsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { - all, err := d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(labels.Everything()) + all, err := d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(query.Selector()) if err != nil { return nil, err } diff --git a/pkg/models/resources/v1alpha3/configmap/configmaps_test.go b/pkg/models/resources/v1alpha3/configmap/configmaps_test.go index f0c27ac6e..bc99a632c 100644 --- a/pkg/models/resources/v1alpha3/configmap/configmaps_test.go +++ b/pkg/models/resources/v1alpha3/configmap/configmaps_test.go @@ -30,12 +30,7 @@ func TestListConfigMaps(t *testing.T) { }, SortBy: query.FieldName, Ascending: false, - Filters: []query.Filter{ - { - Field: query.FieldNamespace, - Value: query.Value("default"), - }, - }, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("default")}, }, &api.ListResult{ Items: []interface{}{foo3, foo2, foo1}, diff --git a/pkg/models/resources/v1alpha3/deployment/deployments.go b/pkg/models/resources/v1alpha3/deployment/deployments.go index e23bb4c4a..b3083beb8 100644 --- a/pkg/models/resources/v1alpha3/deployment/deployments.go +++ b/pkg/models/resources/v1alpha3/deployment/deployments.go @@ -26,8 +26,6 @@ import ( "strings" "time" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/api/apps/v1" ) @@ -51,7 +49,7 @@ func (d *deploymentsGetter) Get(namespace, name string) (runtime.Object, error) func (d *deploymentsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { // first retrieves all deployments within given namespace - all, err := d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).List(labels.Everything()) + all, err := d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).List(query.Selector()) if err != nil { return nil, err } @@ -77,6 +75,8 @@ func (d *deploymentsGetter) compare(left runtime.Object, right runtime.Object, f } switch field { + case query.FieldUpdateTime: + fallthrough case query.FieldLastUpdateTimestamp: return lastUpdateTime(leftDeployment).After(lastUpdateTime(rightDeployment)) default: diff --git a/pkg/models/resources/v1alpha3/deployment/deployments_test.go b/pkg/models/resources/v1alpha3/deployment/deployments_test.go index 043c64e67..caad84fad 100644 --- a/pkg/models/resources/v1alpha3/deployment/deployments_test.go +++ b/pkg/models/resources/v1alpha3/deployment/deployments_test.go @@ -30,12 +30,7 @@ func TestListDeployments(t *testing.T) { }, SortBy: query.FieldName, Ascending: false, - Filters: []query.Filter{ - { - Field: query.FieldName, - Value: query.Value("foo2"), - }, - }, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, }, &api.ListResult{ Items: []interface{}{ diff --git a/pkg/models/resources/v1alpha3/globalrole/globalroles.go b/pkg/models/resources/v1alpha3/globalrole/globalroles.go new file mode 100644 index 000000000..750a04767 --- /dev/null +++ b/pkg/models/resources/v1alpha3/globalrole/globalroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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. + +*/ +package globalrole + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type globalrolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &globalrolesGetter{sharedInformers: sharedInformers} +} + +func (d *globalrolesGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().GlobalRoles().Lister().Get(name) +} + +func (d *globalrolesGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().GlobalRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *globalrolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.GlobalRole) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.GlobalRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *globalrolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.GlobalRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go b/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go new file mode 100644 index 000000000..85e32ce29 --- /dev/null +++ b/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go @@ -0,0 +1,94 @@ +package globalrole + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListGlobalRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Iam().V1alpha2().GlobalRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/interface.go b/pkg/models/resources/v1alpha3/interface.go index 120fe0eb1..6e3040b14 100644 --- a/pkg/models/resources/v1alpha3/interface.go +++ b/pkg/models/resources/v1alpha3/interface.go @@ -22,13 +22,13 @@ type CompareFunc func(runtime.Object, runtime.Object, query.Field) bool type FilterFunc func(runtime.Object, query.Filter) bool -func DefaultList(objects []runtime.Object, query *query.Query, compareFunc CompareFunc, filterFunc FilterFunc) *api.ListResult { +func DefaultList(objects []runtime.Object, q *query.Query, compareFunc CompareFunc, filterFunc FilterFunc) *api.ListResult { // selected matched ones var filtered []runtime.Object for _, object := range objects { selected := true - for _, filter := range query.Filters { - if !filterFunc(object, filter) { + for field, value := range q.Filters { + if !filterFunc(object, query.Filter{Field: field, Value: value}) { selected = false break } @@ -41,14 +41,14 @@ func DefaultList(objects []runtime.Object, query *query.Query, compareFunc Compa // sort by sortBy field sort.Slice(filtered, func(i, j int) bool { - if !query.Ascending { - return compareFunc(filtered[i], filtered[j], query.SortBy) + if !q.Ascending { + return compareFunc(filtered[i], filtered[j], q.SortBy) } - return !compareFunc(filtered[i], filtered[j], query.SortBy) + return !compareFunc(filtered[i], filtered[j], q.SortBy) }) total := len(filtered) - start, end := query.Pagination.GetValidPagination(total) + start, end := q.Pagination.GetValidPagination(total) return &api.ListResult{ TotalItems: len(filtered), @@ -63,6 +63,8 @@ func DefaultObjectMetaCompare(left, right metav1.ObjectMeta, sortBy query.Field) case query.FieldName: return strings.Compare(left.Name, right.Name) > 0 // ?sortBy=creationTimestamp + case query.FieldCreateTime: + fallthrough case query.FieldCreationTimeStamp: // compare by name if creation timestamp is equal if left.CreationTimestamp.Equal(&right.CreationTimestamp) { @@ -104,29 +106,43 @@ func DefaultObjectMetaFilter(item metav1.ObjectMeta, filter query.Filter) bool { return false // /namespaces?page=1&limit=10&annotation=openpitrix_runtime case query.FieldAnnotation: - return containsAnyValue(item.Annotations, string(filter.Value)) + return labelMatch(item.Annotations, string(filter.Value)) // /namespaces?page=1&limit=10&label=kubesphere.io/workspace:system-workspace case query.FieldLabel: - return containsAnyValue(item.Labels, string(filter.Value)) + return labelMatch(item.Labels, string(filter.Value)) default: return false } } -// Filter format ,if the key is defined, the key must match exactly, value match according to strings.Contains. -func containsAnyValue(keyValues map[string]string, filter string) bool { - fields := strings.SplitN(filter, ":", 2) - var keyFilter, valueFilter string +// Filter format (key!?=)?value,if the key is defined, the key must match exactly, value match according to strings.Contains. +func labelMatch(labels map[string]string, filter string) bool { + fields := strings.SplitN(filter, "=", 2) + var key, value string + var opposite bool if len(fields) == 2 { - keyFilter = fields[0] - valueFilter = fields[1] - } else { - valueFilter = fields[0] - } - for key, value := range keyValues { - if (key == keyFilter || keyFilter == "") && strings.Contains(value, valueFilter) { - return true + key = fields[0] + if strings.HasSuffix(key, "!") { + key = strings.TrimSuffix(key, "!") + opposite = true } + value = fields[1] + } else { + value = fields[0] + } + for k, v := range labels { + if opposite { + if (key == "" || k == key) && !strings.Contains(v, value) { + return true + } + } else { + if (key == "" || k == key) && strings.Contains(v, value) { + return true + } + } + } + if opposite && labels[key] == "" { + return true } return false } diff --git a/pkg/models/resources/v1alpha3/namespace/namespaces.go b/pkg/models/resources/v1alpha3/namespace/namespaces.go index e70b11b10..8febda6ff 100644 --- a/pkg/models/resources/v1alpha3/namespace/namespaces.go +++ b/pkg/models/resources/v1alpha3/namespace/namespaces.go @@ -2,7 +2,6 @@ package namespace import ( v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/informers" "kubesphere.io/kubesphere/pkg/api" @@ -24,7 +23,7 @@ func (n namespaceGetter) Get(_, name string) (runtime.Object, error) { } func (n namespaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) { - ns, err := n.informers.Core().V1().Namespaces().Lister().List(labels.Everything()) + ns, err := n.informers.Core().V1().Namespaces().Lister().List(query.Selector()) if err != nil { return nil, err } diff --git a/pkg/models/resources/v1alpha3/pod/pods.go b/pkg/models/resources/v1alpha3/pod/pods.go new file mode 100644 index 000000000..bdfa845a3 --- /dev/null +++ b/pkg/models/resources/v1alpha3/pod/pods.go @@ -0,0 +1,115 @@ +/* + + Copyright 2019 The KubeSphere 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. + +*/ +package pod + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type podsGetter struct { + informer informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &podsGetter{informer: sharedInformers} +} + +func (d *podsGetter) Get(namespace, name string) (runtime.Object, error) { + return d.informer.Core().V1().Pods().Lister().Pods(namespace).Get(name) +} + +func (d *podsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + + all, err := d.informer.Core().V1().Pods().Lister().Pods(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, app := range all { + result = append(result, app) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *podsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftPod, ok := left.(*corev1.Pod) + if !ok { + return false + } + + rightPod, ok := right.(*corev1.Pod) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftPod.ObjectMeta, rightPod.ObjectMeta, field) +} + +func (d *podsGetter) filter(object runtime.Object, filter query.Filter) bool { + pod, ok := object.(*corev1.Pod) + + if !ok { + return false + } + switch filter.Field { + case "nodeName": + if pod.Spec.NodeName != string(filter.Value) { + return false + } + case "pvcName": + if !d.podBindPVC(pod, string(filter.Value)) { + return false + } + case "serviceName": + if !d.podBelongToService(pod, string(filter.Value)) { + return false + } + } + return v1alpha3.DefaultObjectMetaFilter(pod.ObjectMeta, filter) +} + +func (s *podsGetter) podBindPVC(item *corev1.Pod, pvcName string) bool { + for _, v := range item.Spec.Volumes { + if v.VolumeSource.PersistentVolumeClaim != nil && + v.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName { + return true + } + } + return false +} + +func (s *podsGetter) podBelongToService(item *corev1.Pod, serviceName string) bool { + service, err := s.informer.Core().V1().Services().Lister().Services(item.Namespace).Get(serviceName) + if err != nil { + return false + } + selector := labels.Set(service.Spec.Selector).AsSelectorPreValidated() + if selector.Empty() || !selector.Matches(labels.Set(item.Labels)) { + return false + } + return true +} diff --git a/pkg/models/resources/v1alpha3/pod/pods_test.go b/pkg/models/resources/v1alpha3/pod/pods_test.go new file mode 100644 index 000000000..d5fb59652 --- /dev/null +++ b/pkg/models/resources/v1alpha3/pod/pods_test.go @@ -0,0 +1,91 @@ +package pod + +import ( + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListPods(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "default", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("default")}, + }, + &api.ListResult{ + Items: []interface{}{foo3, foo2, foo1}, + TotalItems: len(pods), + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + got, err := getter.List(test.namespace, test.query) + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + } +} + +var ( + foo1 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "default", + }, + } + foo2 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "default", + }, + } + foo3 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo3", + Namespace: "default", + }, + } + pods = []interface{}{foo1, foo2, foo3} +) + +func prepare() v1alpha3.Interface { + + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, pod := range pods { + informer.Core().V1().Pods().Informer().GetIndexer().Add(pod) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/resource/resource.go b/pkg/models/resources/v1alpha3/resource/resource.go index 057a12281..6a9b81af6 100644 --- a/pkg/models/resources/v1alpha3/resource/resource.go +++ b/pkg/models/resources/v1alpha3/resource/resource.go @@ -1,15 +1,45 @@ +/* + * + * Copyright 2020 The KubeSphere 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. + * / + */ + package resource import ( "errors" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/application" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/clusterrole" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/configmap" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/deployment" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/globalrole" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/namespace" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/pod" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/role" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/user" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspace" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspacerole" ) var ErrResourceNotSupported = errors.New("resource is not supported") @@ -23,7 +53,15 @@ func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { getters[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}] = deployment.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}] = namespace.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}] = configmap.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}] = pod.New(factory.KubernetesSharedInformerFactory()) getters[schema.GroupVersionResource{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}] = application.New(factory.ApplicationSharedInformerFactory()) + getters[tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha1.ResourcePluralWorkspace)] = workspace.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralGlobalRole)] = globalrole.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralWorkspaceRole)] = workspacerole.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralUser)] = user.New(factory.KubeSphereSharedInformerFactory()) + getters[rbacv1.SchemeGroupVersion.WithResource("roles")] = role.New(factory.KubernetesSharedInformerFactory()) + getters[rbacv1.SchemeGroupVersion.WithResource("clusterroles")] = clusterrole.New(factory.KubernetesSharedInformerFactory()) return &ResourceGetter{ getters: getters, @@ -38,11 +76,10 @@ func (r *ResourceGetter) tryResource(resource string) v1alpha3.Interface { return v } } - return nil } -func (r *ResourceGetter) Get(resource, namespace, name string) (interface{}, error) { +func (r *ResourceGetter) Get(resource, namespace, name string) (runtime.Object, error) { getter := r.tryResource(resource) if getter == nil { return nil, ErrResourceNotSupported diff --git a/pkg/models/resources/v1alpha3/resource/resource_test.go b/pkg/models/resources/v1alpha3/resource/resource_test.go index 4ea035c14..199690958 100644 --- a/pkg/models/resources/v1alpha3/resource/resource_test.go +++ b/pkg/models/resources/v1alpha3/resource/resource_test.go @@ -55,7 +55,7 @@ func TestResourceGetter(t *testing.T) { }, SortBy: query.FieldName, Ascending: false, - Filters: []query.Filter{}, + Filters: map[query.Field]query.Value{}, }, ExpectError: nil, ExpectResponse: &api.ListResult{ diff --git a/pkg/models/resources/v1alpha3/role/roles.go b/pkg/models/resources/v1alpha3/role/roles.go new file mode 100644 index 000000000..4ebba9faa --- /dev/null +++ b/pkg/models/resources/v1alpha3/role/roles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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. + +*/ +package role + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type rolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &rolesGetter{sharedInformers: sharedInformers} +} + +func (d *rolesGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Rbac().V1().Roles().Lister().Roles(namespace).Get(name) +} + +func (d *rolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + all, err := d.sharedInformers.Rbac().V1().Roles().Lister().Roles(namespace).List(query.Selector()) + + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *rolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*rbacv1.Role) + if !ok { + return false + } + + rightRole, ok := right.(*rbacv1.Role) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*rbacv1.Role) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/role/roles_test.go b/pkg/models/resources/v1alpha3/role/roles_test.go new file mode 100644 index 000000000..7097831f9 --- /dev/null +++ b/pkg/models/resources/v1alpha3/role/roles_test.go @@ -0,0 +1,97 @@ +package role + +import ( + "github.com/google/go-cmp/cmp" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + foo2 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Rbac().V1().Roles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/user/users.go b/pkg/models/resources/v1alpha3/user/users.go new file mode 100644 index 000000000..3c74e9d45 --- /dev/null +++ b/pkg/models/resources/v1alpha3/user/users.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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. + +*/ +package user + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type usersGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &usersGetter{sharedInformers: sharedInformers} +} + +func (d *usersGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().Users().Lister().Get(name) +} + +func (d *usersGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().Users().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *usersGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.User) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.User) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *usersGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.User) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/user/users_test.go b/pkg/models/resources/v1alpha3/user/users_test.go new file mode 100644 index 000000000..6ce6e0805 --- /dev/null +++ b/pkg/models/resources/v1alpha3/user/users_test.go @@ -0,0 +1,94 @@ +package user + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListUsers(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + users = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, user := range users { + informer.Iam().V1alpha2().Users().Informer().GetIndexer().Add(user) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/workspace/workspaces.go b/pkg/models/resources/v1alpha3/workspace/workspaces.go new file mode 100644 index 000000000..539fcd817 --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspace/workspaces.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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. + +*/ +package workspace + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type workspaceGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &workspaceGetter{sharedInformers: sharedInformers} +} + +func (d *workspaceGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Tenant().V1alpha1().Workspaces().Lister().Get(name) +} + +func (d *workspaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Tenant().V1alpha1().Workspaces().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *workspaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*tenantv1alpha1.Workspace) + if !ok { + return false + } + + rightRole, ok := right.(*tenantv1alpha1.Workspace) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *workspaceGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*tenantv1alpha1.Workspace) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/workspace/workspaces_test.go b/pkg/models/resources/v1alpha3/workspace/workspaces_test.go new file mode 100644 index 000000000..3c748399d --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspace/workspaces_test.go @@ -0,0 +1,94 @@ +package workspace + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListWorkspaces(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + workspaces = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, workspace := range workspaces { + informer.Tenant().V1alpha1().Workspaces().Informer().GetIndexer().Add(workspace) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go new file mode 100644 index 000000000..71df90721 --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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. + +*/ +package workspacerole + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type workspacerolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &workspacerolesGetter{sharedInformers: sharedInformers} +} + +func (d *workspacerolesGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().Get(name) +} + +func (d *workspacerolesGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *workspacerolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.WorkspaceRole) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.WorkspaceRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *workspacerolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.WorkspaceRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go new file mode 100644 index 000000000..56b7b8187 --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go @@ -0,0 +1,94 @@ +package workspacerole + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListWorkspaceRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Iam().V1alpha2().WorkspaceRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index 5f94d3f08..339da3bc2 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -18,45 +18,45 @@ package tenant import ( + "fmt" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/iam/am" - "kubesphere.io/kubesphere/pkg/simple/client/k8s" + resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" ) type Interface interface { - ListWorkspaces(user user.Info) (*api.ListResult, error) - ListNamespaces(user user.Info, workspace string) (*api.ListResult, error) + ListWorkspaces(user user.Info, query *query.Query) (*api.ListResult, error) + ListNamespaces(user user.Info, workspace string, query *query.Query) (*api.ListResult, error) } type tenantOperator struct { - informers informers.InformerFactory - am am.AccessManagementInterface - authorizer authorizer.Authorizer + am am.AccessManagementInterface + authorizer authorizer.Authorizer + resourceGetter *resourcesv1alpha3.ResourceGetter } -func New(k8sClient k8s.Client, informers informers.InformerFactory) Interface { - amOperator := am.NewAMOperator(k8sClient.KubeSphere(), informers.KubeSphereSharedInformerFactory()) +func New(informers informers.InformerFactory) Interface { + amOperator := am.NewAMOperator(informers) opaAuthorizer := authorizerfactory.NewOPAAuthorizer(amOperator) return &tenantOperator{ - informers: informers, - am: amOperator, - authorizer: opaAuthorizer, + am: amOperator, + authorizer: opaAuthorizer, + resourceGetter: resourcesv1alpha3.NewResourceGetter(informers), } } -func (t *tenantOperator) ListWorkspaces(user user.Info) (*api.ListResult, error) { - - workspaces := make([]*tenantv1alpha1.Workspace, 0) +func (t *tenantOperator) ListWorkspaces(user user.Info, queryParam *query.Query) (*api.ListResult, error) { listWS := authorizer.AttributesRecord{ User: user, @@ -74,49 +74,56 @@ func (t *tenantOperator) ListWorkspaces(user user.Info) (*api.ListResult, error) } if decision == authorizer.DecisionAllow { - workspaces, err = t.informers.KubeSphereSharedInformerFactory(). - Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) + + result, err := t.resourceGetter.List(tenantv1alpha1.ResourcePluralWorkspace, "", queryParam) if err != nil { klog.Error(err) return nil, err } - } else { - workspaceRoles, err := t.am.ListRolesOfUser(iamv1alpha2.WorkspaceScope, user.GetName()) + + return result, nil + } + + workspaceRoleBindings, err := t.am.ListWorkspaceRoleBindings(user.GetName(), "") + + if err != nil { + klog.Error(err) + return nil, err + } + + workspaces := make([]runtime.Object, 0) + + for _, roleBinding := range workspaceRoleBindings { + + workspaceName := roleBinding.Labels[tenantv1alpha1.WorkspaceLabel] + workspace, err := t.resourceGetter.Get(tenantv1alpha1.ResourcePluralWorkspace, "", workspaceName) + + if errors.IsNotFound(err) { + klog.Warningf("workspace role: %+v found but workspace not exist", roleBinding.ObjectMeta) + continue + } + if err != nil { klog.Error(err) return nil, err } - for _, role := range workspaceRoles { - - workspace, err := t.informers.KubeSphereSharedInformerFactory(). - Tenant().V1alpha1().Workspaces().Lister().Get(role.Target.Name) - - if errors.IsNotFound(err) { - klog.Warningf("workspace role: %s found but workspace not exist", role.Target) - continue - } - - if err != nil { - klog.Error(err) - return nil, err - } - - if !containsWorkspace(workspaces, workspace) { - workspaces = append(workspaces, workspace) - } + if !contains(workspaces, workspace) { + workspaces = append(workspaces, workspace) } } - return &api.ListResult{ - TotalItems: len(workspaces), - Items: workspacesToInterfaces(workspaces), - }, nil + result := resources.DefaultList(workspaces, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*tenantv1alpha1.Workspace).ObjectMeta, right.(*tenantv1alpha1.Workspace).ObjectMeta, field) + }, func(workspace runtime.Object, filter query.Filter) bool { + return resources.DefaultObjectMetaFilter(workspace.(*tenantv1alpha1.Workspace).ObjectMeta, filter) + }) + + return result, nil } -func (t *tenantOperator) ListNamespaces(user user.Info, workspace string) (*api.ListResult, error) { - namespaces := make([]*corev1.Namespace, 0) +func (t *tenantOperator) ListNamespaces(user user.Info, workspace string, queryParam *query.Query) (*api.ListResult, error) { listNSInWS := authorizer.AttributesRecord{ User: user, @@ -135,78 +142,65 @@ func (t *tenantOperator) ListNamespaces(user user.Info, workspace string) (*api. } if decision == authorizer.DecisionAllow { - namespaces, err = t.informers.KubernetesSharedInformerFactory(). - Core().V1().Namespaces().Lister().List(labels.Everything()) - if err != nil { - klog.Error(err) - return nil, err - } - } else { - namespaceRoles, err := t.am.ListRolesOfUser(iamv1alpha2.NamespaceScope, workspace) + queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s:%s", tenantv1alpha1.WorkspaceLabel, workspace)) + + result, err := t.resourceGetter.List("namespaces", "", queryParam) if err != nil { klog.Error(err) return nil, err } - for _, role := range namespaceRoles { + return result, nil + } - namespace, err := t.informers.KubernetesSharedInformerFactory(). - Core().V1().Namespaces().Lister().Get(role.Target.Name) + roleBindings, err := t.am.ListRoleBindings(user.GetName(), "") - if errors.IsNotFound(err) { - klog.Warningf("workspace role: %s found but workspace not exist", role.Target) - continue - } + if err != nil { + klog.Error(err) + return nil, err + } - if err != nil { - klog.Error(err) - return nil, err - } + namespaces := make([]runtime.Object, 0) - if !containsNamespace(namespaces, namespace) { - namespaces = append(namespaces, namespace) - } + for _, roleBinding := range roleBindings { + namespaceName := roleBinding.Namespace + namespace, err := t.resourceGetter.Get("namespaces", "", namespaceName) + + if errors.IsNotFound(err) { + klog.Warningf("workspace role: %+v found but workspace not exist", roleBinding.ObjectMeta) + continue + } + + if err != nil { + klog.Error(err) + return nil, err + } + + if !contains(namespaces, namespace) { + namespaces = append(namespaces, namespace) } } - return &api.ListResult{ - TotalItems: len(namespaces), - Items: namespacesToInterfaces(namespaces), - }, nil + result := resources.DefaultList(namespaces, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + namespace := object.(*corev1.Namespace).ObjectMeta + if workspaceLabel, ok := namespace.Labels[tenantv1alpha1.WorkspaceLabel]; !ok || workspaceLabel != workspace { + return false + } + return resources.DefaultObjectMetaFilter(namespace, filter) + }) + + return result, nil } -func containsWorkspace(workspaces []*tenantv1alpha1.Workspace, workspace *tenantv1alpha1.Workspace) bool { - for _, item := range workspaces { - if item.Name == workspace.Name { +func contains(objects []runtime.Object, object runtime.Object) bool { + for _, item := range objects { + if item == object { return true } } return false } - -func containsNamespace(namespaces []*corev1.Namespace, namespace *corev1.Namespace) bool { - for _, item := range namespaces { - if item.Name == namespace.Name { - return true - } - } - return false -} - -func workspacesToInterfaces(workspaces []*tenantv1alpha1.Workspace) []interface{} { - ret := make([]interface{}, len(workspaces)) - for index, v := range workspaces { - ret[index] = v - } - return ret -} - -func namespacesToInterfaces(namespaces []*corev1.Namespace) []interface{} { - ret := make([]interface{}, len(namespaces)) - for index, v := range namespaces { - ret[index] = v - } - return ret -} diff --git a/vendor/modules.txt b/vendor/modules.txt index deffc100c..546f7512f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,9 +7,9 @@ github.com/Azure/go-ansiterm github.com/Azure/go-ansiterm/winterm # github.com/Azure/go-autorest/autorest v0.9.0 => github.com/Azure/go-autorest/autorest v0.9.0 github.com/Azure/go-autorest/autorest -github.com/Azure/go-autorest/autorest/azure # github.com/Azure/go-autorest/autorest/adal v0.5.0 => github.com/Azure/go-autorest/autorest/adal v0.5.0 github.com/Azure/go-autorest/autorest/adal +github.com/Azure/go-autorest/autorest/azure # github.com/Azure/go-autorest/autorest/date v0.1.0 => github.com/Azure/go-autorest/autorest/date v0.1.0 github.com/Azure/go-autorest/autorest/date # github.com/Azure/go-autorest/logger v0.1.0 => github.com/Azure/go-autorest/logger v0.1.0 @@ -166,9 +166,9 @@ github.com/elastic/go-elasticsearch/v7/estransport github.com/elastic/go-elasticsearch/v7/internal/version # github.com/emicklei/go-restful v2.9.5+incompatible => github.com/emicklei/go-restful v2.9.5+incompatible github.com/emicklei/go-restful -github.com/emicklei/go-restful/log # github.com/emicklei/go-restful-openapi v1.0.0 => github.com/emicklei/go-restful-openapi v1.0.0 github.com/emicklei/go-restful-openapi +github.com/emicklei/go-restful/log # github.com/emirpasic/gods v1.12.0 => github.com/emirpasic/gods v1.12.0 github.com/emirpasic/gods/containers github.com/emirpasic/gods/lists @@ -664,7 +664,7 @@ golang.org/x/crypto/ssh golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/knownhosts golang.org/x/crypto/ssh/terminal -# golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 => golang.org/x/net v0.0.0-20190620200207-3b0461eec859 +# golang.org/x/net v0.0.0-20190923162816-aa69164e4478 => golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/net/context golang.org/x/net/context/ctxhttp golang.org/x/net/html