mirror of
https://github.com/grafana/grafana.git
synced 2024-12-22 15:13:38 -06:00
Schema: Add schema for role+access policies (#68047)
This commit is contained in:
parent
3af95bebe1
commit
e7da2a179e
@ -0,0 +1,126 @@
|
||||
---
|
||||
keywords:
|
||||
- grafana
|
||||
- schema
|
||||
title: AccessPolicy kind
|
||||
---
|
||||
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
|
||||
|
||||
## AccessPolicy
|
||||
|
||||
#### Maturity: [merged](../../../maturity/#merged)
|
||||
#### Version: 0.0
|
||||
|
||||
Access rules for a scope+role. NOTE there is a unique constraint on role+scope
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|------------|---------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `metadata` | [object](#metadata) | **Yes** | | metadata contains embedded CommonMetadata and can be extended with custom string fields<br/>TODO: use CommonMetadata instead of redefining here; currently needs to be defined here<br/>without external reference as using the CommonMetadata reference breaks thema codegen. |
|
||||
| `spec` | [object](#spec) | **Yes** | | |
|
||||
| `status` | [object](#status) | **Yes** | | |
|
||||
|
||||
### Metadata
|
||||
|
||||
metadata contains embedded CommonMetadata and can be extended with custom string fields
|
||||
TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
|
||||
without external reference as using the CommonMetadata reference breaks thema codegen.
|
||||
|
||||
It extends [_kubeObjectMetadata](#_kubeobjectmetadata).
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------------|------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `createdBy` | string | **Yes** | | |
|
||||
| `creationTimestamp` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `extraFields` | [object](#extrafields) | **Yes** | | extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata |
|
||||
| `finalizers` | string[] | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `labels` | map[string]string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `resourceVersion` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `uid` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `updateTimestamp` | string | **Yes** | | |
|
||||
| `updatedBy` | string | **Yes** | | |
|
||||
| `deletionTimestamp` | string | No | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
|
||||
### _kubeObjectMetadata
|
||||
|
||||
_kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
As it is also intended to be generic enough to function with any API Server.
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------------|-------------------|----------|---------|-------------|
|
||||
| `creationTimestamp` | string | **Yes** | | |
|
||||
| `finalizers` | string[] | **Yes** | | |
|
||||
| `labels` | map[string]string | **Yes** | | |
|
||||
| `resourceVersion` | string | **Yes** | | |
|
||||
| `uid` | string | **Yes** | | |
|
||||
| `deletionTimestamp` | string | No | | |
|
||||
|
||||
### ExtraFields
|
||||
|
||||
extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
### Spec
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|-----------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `role` | [RoleRef](#roleref) | **Yes** | | |
|
||||
| `rules` | [AccessRule](#accessrule)[] | **Yes** | | The set of rules to apply. Note that * is required to modify<br/>access policy rules, and that "none" will reject all actions |
|
||||
| `scope` | [ResourceRef](#resourceref) | **Yes** | | |
|
||||
|
||||
### AccessRule
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|--------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `kind` | string | **Yes** | | The kind this rule applies to (dashboars, alert, etc) |
|
||||
| `verb` | string | **Yes** | | READ, WRITE, CREATE, DELETE, ...<br/>should move to k8s style verbs like: "get", "list", "watch", "create", "update", "patch", "delete" |
|
||||
| `target` | string | No | | Specific sub-elements like "alert.rules" or "dashboard.permissions"???? |
|
||||
|
||||
### ResourceRef
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|--------|----------|---------|-------------|
|
||||
| `kind` | string | **Yes** | | |
|
||||
| `name` | string | **Yes** | | |
|
||||
|
||||
### RoleRef
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|--------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `kind` | string | **Yes** | | Policies can apply to roles, teams, or users<br/>Applying policies to individual users is supported, but discouraged<br/>Possible values are: `Role`, `BuiltinRole`, `Team`, `User`. |
|
||||
| `name` | string | **Yes** | | |
|
||||
| `xname` | string | **Yes** | | |
|
||||
|
||||
### Status
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|--------------------|------------------------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `additionalFields` | [object](#additionalfields) | No | | additionalFields is reserved for future use |
|
||||
| `operatorStates` | map[string][status.#OperatorState](#status.#operatorstate) | No | | operatorStates is a map of operator ID to operator state evaluations.<br/>Any operator which consumes this kind SHOULD add its state evaluation information to this field. |
|
||||
|
||||
### AdditionalFields
|
||||
|
||||
additionalFields is reserved for future use
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
### Status.#OperatorState
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|--------------------|--------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `lastEvaluation` | string | **Yes** | | lastEvaluation is the ResourceVersion last evaluated |
|
||||
| `state` | string | **Yes** | | state describes the state of the lastEvaluation.<br/>It is limited to three possible states for machine evaluation.<br/>Possible values are: `success`, `in_progress`, `failed`. |
|
||||
| `descriptiveState` | string | No | | descriptiveState is an optional more descriptive state field which has no requirements on format |
|
||||
| `details` | [object](#details) | No | | details contains any extra information that is operator-specific |
|
||||
|
||||
### Details
|
||||
|
||||
details contains any extra information that is operator-specific
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
|
105
docs/sources/developers/kinds/core/role/schema-reference.md
Normal file
105
docs/sources/developers/kinds/core/role/schema-reference.md
Normal file
@ -0,0 +1,105 @@
|
||||
---
|
||||
keywords:
|
||||
- grafana
|
||||
- schema
|
||||
title: Role kind
|
||||
---
|
||||
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
|
||||
|
||||
## Role
|
||||
|
||||
#### Maturity: [merged](../../../maturity/#merged)
|
||||
#### Version: 0.0
|
||||
|
||||
Roles represent a set of users+teams that should share similar access
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|------------|---------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `metadata` | [object](#metadata) | **Yes** | | metadata contains embedded CommonMetadata and can be extended with custom string fields<br/>TODO: use CommonMetadata instead of redefining here; currently needs to be defined here<br/>without external reference as using the CommonMetadata reference breaks thema codegen. |
|
||||
| `spec` | [object](#spec) | **Yes** | | |
|
||||
| `status` | [object](#status) | **Yes** | | |
|
||||
|
||||
### Metadata
|
||||
|
||||
metadata contains embedded CommonMetadata and can be extended with custom string fields
|
||||
TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
|
||||
without external reference as using the CommonMetadata reference breaks thema codegen.
|
||||
|
||||
It extends [_kubeObjectMetadata](#_kubeobjectmetadata).
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------------|------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `createdBy` | string | **Yes** | | |
|
||||
| `creationTimestamp` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `extraFields` | [object](#extrafields) | **Yes** | | extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata |
|
||||
| `finalizers` | string[] | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `labels` | map[string]string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `resourceVersion` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `uid` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `updateTimestamp` | string | **Yes** | | |
|
||||
| `updatedBy` | string | **Yes** | | |
|
||||
| `deletionTimestamp` | string | No | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
|
||||
### _kubeObjectMetadata
|
||||
|
||||
_kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
As it is also intended to be generic enough to function with any API Server.
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------------|-------------------|----------|---------|-------------|
|
||||
| `creationTimestamp` | string | **Yes** | | |
|
||||
| `finalizers` | string[] | **Yes** | | |
|
||||
| `labels` | map[string]string | **Yes** | | |
|
||||
| `resourceVersion` | string | **Yes** | | |
|
||||
| `uid` | string | **Yes** | | |
|
||||
| `deletionTimestamp` | string | No | | |
|
||||
|
||||
### ExtraFields
|
||||
|
||||
extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
### Spec
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------|---------|----------|---------|-----------------------------------------------------------|
|
||||
| `hidden` | boolean | **Yes** | | Do not show this role |
|
||||
| `name` | string | **Yes** | | The role identifier `managed:builtins:editor:permissions` |
|
||||
| `description` | string | No | | Role description |
|
||||
| `displayName` | string | No | | Optional display |
|
||||
| `groupName` | string | No | | Name of the team. |
|
||||
|
||||
### Status
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|--------------------|------------------------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `additionalFields` | [object](#additionalfields) | No | | additionalFields is reserved for future use |
|
||||
| `operatorStates` | map[string][status.#OperatorState](#status.#operatorstate) | No | | operatorStates is a map of operator ID to operator state evaluations.<br/>Any operator which consumes this kind SHOULD add its state evaluation information to this field. |
|
||||
|
||||
### AdditionalFields
|
||||
|
||||
additionalFields is reserved for future use
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
### Status.#OperatorState
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|--------------------|--------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `lastEvaluation` | string | **Yes** | | lastEvaluation is the ResourceVersion last evaluated |
|
||||
| `state` | string | **Yes** | | state describes the state of the lastEvaluation.<br/>It is limited to three possible states for machine evaluation.<br/>Possible values are: `success`, `in_progress`, `failed`. |
|
||||
| `descriptiveState` | string | No | | descriptiveState is an optional more descriptive state field which has no requirements on format |
|
||||
| `details` | [object](#details) | No | | details contains any extra information that is operator-specific |
|
||||
|
||||
### Details
|
||||
|
||||
details contains any extra information that is operator-specific
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
|
@ -0,0 +1,131 @@
|
||||
---
|
||||
keywords:
|
||||
- grafana
|
||||
- schema
|
||||
title: RoleBinding kind
|
||||
---
|
||||
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
|
||||
|
||||
## RoleBinding
|
||||
|
||||
#### Maturity: [merged](../../../maturity/#merged)
|
||||
#### Version: 0.0
|
||||
|
||||
Role bindings links a user|team to a configured role
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|------------|---------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `metadata` | [object](#metadata) | **Yes** | | metadata contains embedded CommonMetadata and can be extended with custom string fields<br/>TODO: use CommonMetadata instead of redefining here; currently needs to be defined here<br/>without external reference as using the CommonMetadata reference breaks thema codegen. |
|
||||
| `spec` | [object](#spec) | **Yes** | | |
|
||||
| `status` | [object](#status) | **Yes** | | |
|
||||
|
||||
### Metadata
|
||||
|
||||
metadata contains embedded CommonMetadata and can be extended with custom string fields
|
||||
TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
|
||||
without external reference as using the CommonMetadata reference breaks thema codegen.
|
||||
|
||||
It extends [_kubeObjectMetadata](#_kubeobjectmetadata).
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------------|------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `createdBy` | string | **Yes** | | |
|
||||
| `creationTimestamp` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `extraFields` | [object](#extrafields) | **Yes** | | extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata |
|
||||
| `finalizers` | string[] | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `labels` | map[string]string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `resourceVersion` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `uid` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `updateTimestamp` | string | **Yes** | | |
|
||||
| `updatedBy` | string | **Yes** | | |
|
||||
| `deletionTimestamp` | string | No | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
|
||||
### _kubeObjectMetadata
|
||||
|
||||
_kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
As it is also intended to be generic enough to function with any API Server.
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------------|-------------------|----------|---------|-------------|
|
||||
| `creationTimestamp` | string | **Yes** | | |
|
||||
| `finalizers` | string[] | **Yes** | | |
|
||||
| `labels` | map[string]string | **Yes** | | |
|
||||
| `resourceVersion` | string | **Yes** | | |
|
||||
| `uid` | string | **Yes** | | |
|
||||
| `deletionTimestamp` | string | No | | |
|
||||
|
||||
### ExtraFields
|
||||
|
||||
extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
### Spec
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|-----------|-------------------------------------------|----------|---------|----------------------------|
|
||||
| `role` | [object](#role) | **Yes** | | The role we are discussing |
|
||||
| `subject` | [RoleBindingSubject](#rolebindingsubject) | **Yes** | | |
|
||||
|
||||
### RoleBindingSubject
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|--------|----------|---------|--------------------------------------|
|
||||
| `kind` | string | **Yes** | | Possible values are: `Team`, `User`. |
|
||||
| `name` | string | **Yes** | | The team/user identifier name |
|
||||
|
||||
### Role
|
||||
|
||||
The role we are discussing
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|-----------------------------------------------------------------------------------------|----------|---------|-------------|
|
||||
| `object` | Possible types are: [BuiltinRoleRef](#builtinroleref), [CustomRoleRef](#customroleref). | | |
|
||||
|
||||
### BuiltinRoleRef
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|--------|----------|---------|---------------------------------------------------|
|
||||
| `kind` | string | **Yes** | | Possible values are: `BuiltinRole`. |
|
||||
| `name` | string | **Yes** | | Possible values are: `viewer`, `editor`, `admin`. |
|
||||
|
||||
### CustomRoleRef
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|--------|----------|---------|------------------------------|
|
||||
| `kind` | string | **Yes** | | Possible values are: `Role`. |
|
||||
| `name` | string | **Yes** | | |
|
||||
|
||||
### Status
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|--------------------|------------------------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `additionalFields` | [object](#additionalfields) | No | | additionalFields is reserved for future use |
|
||||
| `operatorStates` | map[string][status.#OperatorState](#status.#operatorstate) | No | | operatorStates is a map of operator ID to operator state evaluations.<br/>Any operator which consumes this kind SHOULD add its state evaluation information to this field. |
|
||||
|
||||
### AdditionalFields
|
||||
|
||||
additionalFields is reserved for future use
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
### Status.#OperatorState
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|--------------------|--------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `lastEvaluation` | string | **Yes** | | lastEvaluation is the ResourceVersion last evaluated |
|
||||
| `state` | string | **Yes** | | state describes the state of the lastEvaluation.<br/>It is limited to three possible states for machine evaluation.<br/>Possible values are: `success`, `in_progress`, `failed`. |
|
||||
| `descriptiveState` | string | No | | descriptiveState is an optional more descriptive state field which has no requirements on format |
|
||||
| `details` | [object](#details) | No | | details contains any extra information that is operator-specific |
|
||||
|
||||
### Details
|
||||
|
||||
details contains any extra information that is operator-specific
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
|
51
kinds/accesspolicy/access_policy_kind.cue
Normal file
51
kinds/accesspolicy/access_policy_kind.cue
Normal file
@ -0,0 +1,51 @@
|
||||
package kind
|
||||
|
||||
name: "AccessPolicy"
|
||||
maturity: "merged"
|
||||
description: "Access rules for a scope+role. NOTE there is a unique constraint on role+scope"
|
||||
pluralName: "AccessPolicies"
|
||||
machineName: "accesspolicy"
|
||||
pluralMachineName: "accesspolicies"
|
||||
|
||||
lineage: schemas: [{
|
||||
version: [0, 0]
|
||||
schema: {
|
||||
spec: {
|
||||
// The scope where these policies should apply
|
||||
scope: #ResourceRef
|
||||
|
||||
// The role that must apply this policy
|
||||
role: #RoleRef
|
||||
|
||||
// The set of rules to apply. Note that * is required to modify
|
||||
// access policy rules, and that "none" will reject all actions
|
||||
rules: [...#AccessRule]
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#RoleRef: {
|
||||
// Policies can apply to roles, teams, or users
|
||||
// Applying policies to individual users is supported, but discouraged
|
||||
kind: "Role" | "BuiltinRole" | "Team" | "User"
|
||||
name: string
|
||||
xname: string // temporary
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#ResourceRef: {
|
||||
kind: string // explicit resource or folder will cascade
|
||||
name: string
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#AccessRule: {
|
||||
// The kind this rule applies to (dashboars, alert, etc)
|
||||
kind: "*" | string
|
||||
|
||||
// READ, WRITE, CREATE, DELETE, ...
|
||||
// should move to k8s style verbs like: "get", "list", "watch", "create", "update", "patch", "delete"
|
||||
verb: "*" | "none" | string
|
||||
|
||||
// Specific sub-elements like "alert.rules" or "dashboard.permissions"????
|
||||
target?: string
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
},
|
||||
]
|
25
kinds/role/role_kind.cue
Normal file
25
kinds/role/role_kind.cue
Normal file
@ -0,0 +1,25 @@
|
||||
package kind
|
||||
|
||||
name: "Role"
|
||||
maturity: "merged"
|
||||
description: "Roles represent a set of users+teams that should share similar access"
|
||||
|
||||
lineage: schemas: [{
|
||||
version: [0, 0]
|
||||
schema: {
|
||||
spec: {
|
||||
// The role identifier `managed:builtins:editor:permissions`
|
||||
name: string
|
||||
// Optional display
|
||||
displayName?: string
|
||||
// Name of the team.
|
||||
groupName?: string
|
||||
// Role description
|
||||
description?: string
|
||||
|
||||
// Do not show this role
|
||||
hidden: bool | false
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
},
|
||||
]
|
36
kinds/rolebinding/role_binding_kind.cue
Normal file
36
kinds/rolebinding/role_binding_kind.cue
Normal file
@ -0,0 +1,36 @@
|
||||
package kind
|
||||
|
||||
name: "RoleBinding"
|
||||
maturity: "merged"
|
||||
description: "Role bindings links a user|team to a configured role"
|
||||
|
||||
lineage: schemas: [{
|
||||
version: [0, 0]
|
||||
schema: {
|
||||
spec: {
|
||||
// The role we are discussing
|
||||
role: #BuiltinRoleRef | #CustomRoleRef
|
||||
|
||||
// The team or user that has the specified role
|
||||
subject: #RoleBindingSubject
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#CustomRoleRef: {
|
||||
kind: "Role"
|
||||
name: string
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#BuiltinRoleRef: {
|
||||
kind: "BuiltinRole"
|
||||
name: "viewer" | "editor" | "admin"
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#RoleBindingSubject: {
|
||||
kind: "Team" | "User"
|
||||
|
||||
// The team/user identifier name
|
||||
name: string
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
},
|
||||
]
|
@ -7,6 +7,17 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
// Raw generated types from AccessPolicy kind.
|
||||
export type {
|
||||
AccessPolicy,
|
||||
RoleRef,
|
||||
ResourceRef,
|
||||
AccessRule
|
||||
} from './raw/accesspolicy/x/accesspolicy_types.gen';
|
||||
|
||||
// Raw generated enums and default consts from accesspolicy kind.
|
||||
export { defaultAccessPolicy } from './raw/accesspolicy/x/accesspolicy_types.gen';
|
||||
|
||||
// Raw generated types from Dashboard kind.
|
||||
export type {
|
||||
AnnotationTarget,
|
||||
@ -129,6 +140,17 @@ export type {
|
||||
// Raw generated types from PublicDashboard kind.
|
||||
export type { PublicDashboard } from './raw/publicdashboard/x/publicdashboard_types.gen';
|
||||
|
||||
// Raw generated types from Role kind.
|
||||
export type { Role } from './raw/role/x/role_types.gen';
|
||||
|
||||
// Raw generated types from RoleBinding kind.
|
||||
export type {
|
||||
RoleBinding,
|
||||
CustomRoleRef,
|
||||
BuiltinRoleRef,
|
||||
RoleBindingSubject
|
||||
} from './raw/rolebinding/x/rolebinding_types.gen';
|
||||
|
||||
// Raw generated types from ServiceAccount kind.
|
||||
export type {
|
||||
ServiceAccount,
|
||||
|
@ -0,0 +1,60 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// TSResourceJenny
|
||||
// LatestMajorsOrXJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export interface RoleRef {
|
||||
/**
|
||||
* Policies can apply to roles, teams, or users
|
||||
* Applying policies to individual users is supported, but discouraged
|
||||
*/
|
||||
kind: ('Role' | 'BuiltinRole' | 'Team' | 'User');
|
||||
name: string;
|
||||
xname: string; // temporary
|
||||
}
|
||||
|
||||
export interface ResourceRef {
|
||||
kind: string; // explicit resource or folder will cascade
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface AccessRule {
|
||||
/**
|
||||
* The kind this rule applies to (dashboars, alert, etc)
|
||||
*/
|
||||
kind: ('*' | string);
|
||||
/**
|
||||
* Specific sub-elements like "alert.rules" or "dashboard.permissions"????
|
||||
*/
|
||||
target?: string;
|
||||
/**
|
||||
* READ, WRITE, CREATE, DELETE, ...
|
||||
* should move to k8s style verbs like: "get", "list", "watch", "create", "update", "patch", "delete"
|
||||
*/
|
||||
verb: ('*' | 'none' | string);
|
||||
}
|
||||
|
||||
export interface AccessPolicy {
|
||||
/**
|
||||
* The role that must apply this policy
|
||||
*/
|
||||
role: RoleRef;
|
||||
/**
|
||||
* The set of rules to apply. Note that * is required to modify
|
||||
* access policy rules, and that "none" will reject all actions
|
||||
*/
|
||||
rules: Array<AccessRule>;
|
||||
/**
|
||||
* The scope where these policies should apply
|
||||
*/
|
||||
scope: ResourceRef;
|
||||
}
|
||||
|
||||
export const defaultAccessPolicy: Partial<AccessPolicy> = {
|
||||
rules: [],
|
||||
};
|
32
packages/grafana-schema/src/raw/role/x/role_types.gen.ts
Normal file
32
packages/grafana-schema/src/raw/role/x/role_types.gen.ts
Normal file
@ -0,0 +1,32 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// TSResourceJenny
|
||||
// LatestMajorsOrXJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export interface Role {
|
||||
/**
|
||||
* Role description
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Optional display
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* Name of the team.
|
||||
*/
|
||||
groupName?: string;
|
||||
/**
|
||||
* Do not show this role
|
||||
*/
|
||||
hidden: (boolean | false);
|
||||
/**
|
||||
* The role identifier `managed:builtins:editor:permissions`
|
||||
*/
|
||||
name: string;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// TSResourceJenny
|
||||
// LatestMajorsOrXJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export interface CustomRoleRef {
|
||||
kind: 'Role';
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface BuiltinRoleRef {
|
||||
kind: 'BuiltinRole';
|
||||
name: ('viewer' | 'editor' | 'admin');
|
||||
}
|
||||
|
||||
export interface RoleBindingSubject {
|
||||
kind: ('Team' | 'User');
|
||||
/**
|
||||
* The team/user identifier name
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface RoleBinding {
|
||||
/**
|
||||
* The role we are discussing
|
||||
*/
|
||||
role: (BuiltinRoleRef | CustomRoleRef);
|
||||
/**
|
||||
* The team or user that has the specified role
|
||||
*/
|
||||
subject: RoleBindingSubject;
|
||||
}
|
40
pkg/kinds/accesspolicy/accesspolicy_gen.go
Normal file
40
pkg/kinds/accesspolicy/accesspolicy_gen.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoTypesJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package accesspolicy
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/kinds"
|
||||
)
|
||||
|
||||
// Resource is the kubernetes style representation of AccessPolicy. (TODO be better)
|
||||
type K8sResource = kinds.GrafanaResource[Spec, Status]
|
||||
|
||||
// NewResource creates a new instance of the resource with a given name (UID)
|
||||
func NewK8sResource(name string, s *Spec) K8sResource {
|
||||
return K8sResource{
|
||||
Kind: "AccessPolicy",
|
||||
APIVersion: "v0.0-alpha",
|
||||
Metadata: kinds.GrafanaResourceMetadata{
|
||||
Name: name,
|
||||
Annotations: make(map[string]string),
|
||||
Labels: make(map[string]string),
|
||||
},
|
||||
Spec: s,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Resource is the wire representation of AccessPolicy.
|
||||
// It currently will soon be merged into the k8s flavor (TODO be better)
|
||||
type Resource struct {
|
||||
Metadata Metadata `json:"metadata"`
|
||||
Spec Spec `json:"spec"`
|
||||
Status Status `json:"status"`
|
||||
}
|
79
pkg/kinds/accesspolicy/accesspolicy_kind_gen.go
Normal file
79
pkg/kinds/accesspolicy/accesspolicy_kind_gen.go
Normal file
@ -0,0 +1,79 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// CoreKindJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package accesspolicy
|
||||
|
||||
import (
|
||||
"github.com/grafana/kindsys"
|
||||
"github.com/grafana/thema"
|
||||
"github.com/grafana/thema/vmux"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
)
|
||||
|
||||
// rootrel is the relative path from the grafana repository root to the
|
||||
// directory containing the .cue files in which this kind is defined. Necessary
|
||||
// for runtime errors related to the definition and/or lineage to provide
|
||||
// a real path to the correct .cue file.
|
||||
const rootrel string = "kinds/accesspolicy"
|
||||
|
||||
// TODO standard generated docs
|
||||
type Kind struct {
|
||||
kindsys.Core
|
||||
lin thema.ConvergentLineage[*Resource]
|
||||
jcodec vmux.Codec
|
||||
valmux vmux.ValueMux[*Resource]
|
||||
}
|
||||
|
||||
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
|
||||
var _ kindsys.Core = &Kind{}
|
||||
|
||||
// TODO standard generated docs
|
||||
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
|
||||
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k := &Kind{}
|
||||
k.Core, err = kindsys.BindCore(rt, def, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get the thema.Schema that the meta says is in the current version (which
|
||||
// codegen ensures is always the latest)
|
||||
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
|
||||
tsch, err := thema.BindType(cursch, &Resource{})
|
||||
if err != nil {
|
||||
// Should be unreachable, modulo bugs in the Thema->Go code generator
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.jcodec = vmux.NewJSONCodec("accesspolicy.json")
|
||||
k.lin = tsch.ConvergentLineage()
|
||||
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
|
||||
// to the the AccessPolicy [Resource] type generated from the current schema, v0.0.
|
||||
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
|
||||
return k.lin
|
||||
}
|
||||
|
||||
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
|
||||
// at any schematized dashboard version to an instance of AccessPolicy [Resource].
|
||||
//
|
||||
// Validation and translation errors emitted from this func will identify the
|
||||
// input bytes as "dashboard.json".
|
||||
//
|
||||
// This is a thin wrapper around Thema's [vmux.ValueMux].
|
||||
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
|
||||
return k.valmux(b)
|
||||
}
|
42
pkg/kinds/accesspolicy/accesspolicy_metadata_gen.go
Normal file
42
pkg/kinds/accesspolicy/accesspolicy_metadata_gen.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package accesspolicy
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Metadata defines model for Metadata.
|
||||
type Metadata struct {
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
|
||||
// extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
ExtraFields map[string]interface{} `json:"extraFields"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Uid string `json:"uid"`
|
||||
UpdateTimestamp time.Time `json:"updateTimestamp"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
}
|
||||
|
||||
// _kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
// It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
// As it is also intended to be generic enough to function with any API Server.
|
||||
type KubeObjectMetadata struct {
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Uid string `json:"uid"`
|
||||
}
|
60
pkg/kinds/accesspolicy/accesspolicy_spec_gen.go
Normal file
60
pkg/kinds/accesspolicy/accesspolicy_spec_gen.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package accesspolicy
|
||||
|
||||
// Defines values for RoleRefKind.
|
||||
const (
|
||||
RoleRefKindBuiltinRole RoleRefKind = "BuiltinRole"
|
||||
RoleRefKindRole RoleRefKind = "Role"
|
||||
RoleRefKindTeam RoleRefKind = "Team"
|
||||
RoleRefKindUser RoleRefKind = "User"
|
||||
)
|
||||
|
||||
// AccessRule defines model for AccessRule.
|
||||
type AccessRule struct {
|
||||
// The kind this rule applies to (dashboars, alert, etc)
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// Specific sub-elements like "alert.rules" or "dashboard.permissions"????
|
||||
Target *string `json:"target,omitempty"`
|
||||
|
||||
// READ, WRITE, CREATE, DELETE, ...
|
||||
// should move to k8s style verbs like: "get", "list", "watch", "create", "update", "patch", "delete"
|
||||
Verb string `json:"verb"`
|
||||
}
|
||||
|
||||
// ResourceRef defines model for ResourceRef.
|
||||
type ResourceRef struct {
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// RoleRef defines model for RoleRef.
|
||||
type RoleRef struct {
|
||||
// Policies can apply to roles, teams, or users
|
||||
// Applying policies to individual users is supported, but discouraged
|
||||
Kind RoleRefKind `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Xname string `json:"xname"`
|
||||
}
|
||||
|
||||
// Policies can apply to roles, teams, or users
|
||||
// Applying policies to individual users is supported, but discouraged
|
||||
type RoleRefKind string
|
||||
|
||||
// Spec defines model for Spec.
|
||||
type Spec struct {
|
||||
Role RoleRef `json:"role"`
|
||||
|
||||
// The set of rules to apply. Note that * is required to modify
|
||||
// access policy rules, and that "none" will reject all actions
|
||||
Rules []AccessRule `json:"rules"`
|
||||
Scope ResourceRef `json:"scope"`
|
||||
}
|
74
pkg/kinds/accesspolicy/accesspolicy_status_gen.go
Normal file
74
pkg/kinds/accesspolicy/accesspolicy_status_gen.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package accesspolicy
|
||||
|
||||
// Defines values for OperatorStateState.
|
||||
const (
|
||||
OperatorStateStateFailed OperatorStateState = "failed"
|
||||
OperatorStateStateInProgress OperatorStateState = "in_progress"
|
||||
OperatorStateStateSuccess OperatorStateState = "success"
|
||||
)
|
||||
|
||||
// Defines values for StatusOperatorStateState.
|
||||
const (
|
||||
StatusOperatorStateStateFailed StatusOperatorStateState = "failed"
|
||||
StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress"
|
||||
StatusOperatorStateStateSuccess StatusOperatorStateState = "success"
|
||||
)
|
||||
|
||||
// OperatorState defines model for OperatorState.
|
||||
type OperatorState struct {
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State OperatorStateState `json:"state"`
|
||||
}
|
||||
|
||||
// OperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type OperatorStateState string
|
||||
|
||||
// Status defines model for Status.
|
||||
type Status struct {
|
||||
// additionalFields is reserved for future use
|
||||
AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
|
||||
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"`
|
||||
}
|
||||
|
||||
// StatusOperatorState defines model for status.#OperatorState.
|
||||
type StatusOperatorState struct {
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State StatusOperatorStateState `json:"state"`
|
||||
}
|
||||
|
||||
// StatusOperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type StatusOperatorStateState string
|
99
pkg/kinds/accesspolicy/utils.go
Normal file
99
pkg/kinds/accesspolicy/utils.go
Normal file
@ -0,0 +1,99 @@
|
||||
package accesspolicy
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
const PermissionsTarget = "permissions"
|
||||
const AllowAll = "*"
|
||||
const AllowNone = "none"
|
||||
|
||||
func ReduceRules(rules []AccessRule) []AccessRule {
|
||||
type verbs struct {
|
||||
Verb map[string][]string
|
||||
Terminal string
|
||||
}
|
||||
|
||||
kinds := make(map[string]*verbs)
|
||||
for _, rule := range rules {
|
||||
if rule.Kind == "" || rule.Verb == "" {
|
||||
continue // invalid
|
||||
}
|
||||
|
||||
// flip write permission to *
|
||||
if rule.Target != nil && *rule.Target == PermissionsTarget {
|
||||
if rule.Verb == "write" {
|
||||
rule.Verb = AllowAll
|
||||
}
|
||||
}
|
||||
kind, ok := kinds[rule.Kind]
|
||||
if !ok {
|
||||
kind = &verbs{
|
||||
Verb: make(map[string][]string),
|
||||
}
|
||||
kinds[rule.Kind] = kind
|
||||
}
|
||||
|
||||
terminal := rule.Verb == AllowAll || rule.Verb == AllowNone
|
||||
if terminal {
|
||||
if rule.Kind == AllowAll {
|
||||
return []AccessRule{rule}
|
||||
}
|
||||
kind.Terminal = rule.Verb
|
||||
} else if kind.Terminal == "" {
|
||||
targets, ok := kind.Verb[rule.Verb]
|
||||
if !ok {
|
||||
targets = []string{}
|
||||
}
|
||||
if rule.Target != nil && !contains(targets, *rule.Target) {
|
||||
targets = append(targets, *rule.Target)
|
||||
sort.Strings(targets)
|
||||
}
|
||||
kind.Verb[rule.Verb] = targets
|
||||
}
|
||||
}
|
||||
|
||||
results := make([]AccessRule, 0)
|
||||
for _, kind := range getSortedKeys(kinds) {
|
||||
verb := kinds[kind]
|
||||
if verb.Terminal != "" {
|
||||
results = append(results, AccessRule{Kind: kind, Verb: verb.Terminal})
|
||||
} else {
|
||||
for _, v := range getSortedKeys(verb.Verb) {
|
||||
targets := verb.Verb[v]
|
||||
if len(targets) == 0 {
|
||||
results = append(results, AccessRule{Kind: kind, Verb: v})
|
||||
} else {
|
||||
for _, t := range targets {
|
||||
results = append(results, AccessRule{
|
||||
Kind: kind,
|
||||
Verb: v,
|
||||
Target: util.Pointer(t),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func getSortedKeys[T any](vals map[string]T) []string {
|
||||
keys := make([]string, 0, len(vals))
|
||||
for k := range vals {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func contains[T comparable](s []T, e T) bool {
|
||||
for _, v := range s {
|
||||
if v == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
68
pkg/kinds/accesspolicy/utils_test.go
Normal file
68
pkg/kinds/accesspolicy/utils_test.go
Normal file
@ -0,0 +1,68 @@
|
||||
package accesspolicy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRuleReducer(t *testing.T) {
|
||||
t.Run("Check write pointer becomes star", func(t *testing.T) {
|
||||
rules := ReduceRules([]AccessRule{
|
||||
{Kind: "dashboard", Verb: "read"},
|
||||
{Kind: "dashboard", Verb: "write", Target: util.Pointer("permissions")},
|
||||
{Kind: "dashboard", Verb: "read"},
|
||||
})
|
||||
require.Len(t, rules, 1)
|
||||
require.Equal(t, rules[0], AccessRule{Kind: "dashboard", Verb: "*"})
|
||||
})
|
||||
|
||||
t.Run("Check sort", func(t *testing.T) {
|
||||
rules := ReduceRules([]AccessRule{
|
||||
{Kind: "x", Verb: "b"},
|
||||
{Kind: "x", Verb: "a"},
|
||||
{Kind: "x", Verb: "a"}, // ignore duplicates
|
||||
{Kind: "x", Verb: "a"}, // ignore duplicates
|
||||
{Kind: "x", Verb: "a"}, // ignore duplicates
|
||||
{Kind: "x", Verb: "a"},
|
||||
{Kind: "z", Verb: "b"},
|
||||
{Kind: "AAA", Verb: ""}, // ignore
|
||||
{Kind: "", Verb: "XXX"}, // ignore
|
||||
{Kind: "z", Verb: "a"},
|
||||
{Kind: "y", Verb: "b"},
|
||||
{Kind: "y", Verb: "a"},
|
||||
})
|
||||
out, err := json.MarshalIndent(rules, "", " ")
|
||||
fmt.Printf("%s", string(out))
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `[
|
||||
{
|
||||
"kind": "x",
|
||||
"verb": "a"
|
||||
},
|
||||
{
|
||||
"kind": "x",
|
||||
"verb": "b"
|
||||
},
|
||||
{
|
||||
"kind": "y",
|
||||
"verb": "a"
|
||||
},
|
||||
{
|
||||
"kind": "y",
|
||||
"verb": "b"
|
||||
},
|
||||
{
|
||||
"kind": "z",
|
||||
"verb": "a"
|
||||
},
|
||||
{
|
||||
"kind": "z",
|
||||
"verb": "b"
|
||||
}
|
||||
]`, string(out))
|
||||
})
|
||||
}
|
40
pkg/kinds/role/role_gen.go
Normal file
40
pkg/kinds/role/role_gen.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoTypesJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package role
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/kinds"
|
||||
)
|
||||
|
||||
// Resource is the kubernetes style representation of Role. (TODO be better)
|
||||
type K8sResource = kinds.GrafanaResource[Spec, Status]
|
||||
|
||||
// NewResource creates a new instance of the resource with a given name (UID)
|
||||
func NewK8sResource(name string, s *Spec) K8sResource {
|
||||
return K8sResource{
|
||||
Kind: "Role",
|
||||
APIVersion: "v0.0-alpha",
|
||||
Metadata: kinds.GrafanaResourceMetadata{
|
||||
Name: name,
|
||||
Annotations: make(map[string]string),
|
||||
Labels: make(map[string]string),
|
||||
},
|
||||
Spec: s,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Resource is the wire representation of Role.
|
||||
// It currently will soon be merged into the k8s flavor (TODO be better)
|
||||
type Resource struct {
|
||||
Metadata Metadata `json:"metadata"`
|
||||
Spec Spec `json:"spec"`
|
||||
Status Status `json:"status"`
|
||||
}
|
79
pkg/kinds/role/role_kind_gen.go
Normal file
79
pkg/kinds/role/role_kind_gen.go
Normal file
@ -0,0 +1,79 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// CoreKindJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package role
|
||||
|
||||
import (
|
||||
"github.com/grafana/kindsys"
|
||||
"github.com/grafana/thema"
|
||||
"github.com/grafana/thema/vmux"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
)
|
||||
|
||||
// rootrel is the relative path from the grafana repository root to the
|
||||
// directory containing the .cue files in which this kind is defined. Necessary
|
||||
// for runtime errors related to the definition and/or lineage to provide
|
||||
// a real path to the correct .cue file.
|
||||
const rootrel string = "kinds/role"
|
||||
|
||||
// TODO standard generated docs
|
||||
type Kind struct {
|
||||
kindsys.Core
|
||||
lin thema.ConvergentLineage[*Resource]
|
||||
jcodec vmux.Codec
|
||||
valmux vmux.ValueMux[*Resource]
|
||||
}
|
||||
|
||||
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
|
||||
var _ kindsys.Core = &Kind{}
|
||||
|
||||
// TODO standard generated docs
|
||||
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
|
||||
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k := &Kind{}
|
||||
k.Core, err = kindsys.BindCore(rt, def, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get the thema.Schema that the meta says is in the current version (which
|
||||
// codegen ensures is always the latest)
|
||||
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
|
||||
tsch, err := thema.BindType(cursch, &Resource{})
|
||||
if err != nil {
|
||||
// Should be unreachable, modulo bugs in the Thema->Go code generator
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.jcodec = vmux.NewJSONCodec("role.json")
|
||||
k.lin = tsch.ConvergentLineage()
|
||||
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
|
||||
// to the the Role [Resource] type generated from the current schema, v0.0.
|
||||
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
|
||||
return k.lin
|
||||
}
|
||||
|
||||
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
|
||||
// at any schematized dashboard version to an instance of Role [Resource].
|
||||
//
|
||||
// Validation and translation errors emitted from this func will identify the
|
||||
// input bytes as "dashboard.json".
|
||||
//
|
||||
// This is a thin wrapper around Thema's [vmux.ValueMux].
|
||||
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
|
||||
return k.valmux(b)
|
||||
}
|
42
pkg/kinds/role/role_metadata_gen.go
Normal file
42
pkg/kinds/role/role_metadata_gen.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package role
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Metadata defines model for Metadata.
|
||||
type Metadata struct {
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
|
||||
// extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
ExtraFields map[string]interface{} `json:"extraFields"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Uid string `json:"uid"`
|
||||
UpdateTimestamp time.Time `json:"updateTimestamp"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
}
|
||||
|
||||
// _kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
// It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
// As it is also intended to be generic enough to function with any API Server.
|
||||
type KubeObjectMetadata struct {
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Uid string `json:"uid"`
|
||||
}
|
28
pkg/kinds/role/role_spec_gen.go
Normal file
28
pkg/kinds/role/role_spec_gen.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package role
|
||||
|
||||
// Spec defines model for Spec.
|
||||
type Spec struct {
|
||||
// Role description
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Optional display
|
||||
DisplayName *string `json:"displayName,omitempty"`
|
||||
|
||||
// Name of the team.
|
||||
GroupName *string `json:"groupName,omitempty"`
|
||||
|
||||
// Do not show this role
|
||||
Hidden bool `json:"hidden"`
|
||||
|
||||
// The role identifier `managed:builtins:editor:permissions`
|
||||
Name string `json:"name"`
|
||||
}
|
74
pkg/kinds/role/role_status_gen.go
Normal file
74
pkg/kinds/role/role_status_gen.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package role
|
||||
|
||||
// Defines values for OperatorStateState.
|
||||
const (
|
||||
OperatorStateStateFailed OperatorStateState = "failed"
|
||||
OperatorStateStateInProgress OperatorStateState = "in_progress"
|
||||
OperatorStateStateSuccess OperatorStateState = "success"
|
||||
)
|
||||
|
||||
// Defines values for StatusOperatorStateState.
|
||||
const (
|
||||
StatusOperatorStateStateFailed StatusOperatorStateState = "failed"
|
||||
StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress"
|
||||
StatusOperatorStateStateSuccess StatusOperatorStateState = "success"
|
||||
)
|
||||
|
||||
// OperatorState defines model for OperatorState.
|
||||
type OperatorState struct {
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State OperatorStateState `json:"state"`
|
||||
}
|
||||
|
||||
// OperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type OperatorStateState string
|
||||
|
||||
// Status defines model for Status.
|
||||
type Status struct {
|
||||
// additionalFields is reserved for future use
|
||||
AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
|
||||
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"`
|
||||
}
|
||||
|
||||
// StatusOperatorState defines model for status.#OperatorState.
|
||||
type StatusOperatorState struct {
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State StatusOperatorStateState `json:"state"`
|
||||
}
|
||||
|
||||
// StatusOperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type StatusOperatorStateState string
|
40
pkg/kinds/rolebinding/rolebinding_gen.go
Normal file
40
pkg/kinds/rolebinding/rolebinding_gen.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoTypesJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package rolebinding
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/kinds"
|
||||
)
|
||||
|
||||
// Resource is the kubernetes style representation of RoleBinding. (TODO be better)
|
||||
type K8sResource = kinds.GrafanaResource[Spec, Status]
|
||||
|
||||
// NewResource creates a new instance of the resource with a given name (UID)
|
||||
func NewK8sResource(name string, s *Spec) K8sResource {
|
||||
return K8sResource{
|
||||
Kind: "RoleBinding",
|
||||
APIVersion: "v0.0-alpha",
|
||||
Metadata: kinds.GrafanaResourceMetadata{
|
||||
Name: name,
|
||||
Annotations: make(map[string]string),
|
||||
Labels: make(map[string]string),
|
||||
},
|
||||
Spec: s,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Resource is the wire representation of RoleBinding.
|
||||
// It currently will soon be merged into the k8s flavor (TODO be better)
|
||||
type Resource struct {
|
||||
Metadata Metadata `json:"metadata"`
|
||||
Spec Spec `json:"spec"`
|
||||
Status Status `json:"status"`
|
||||
}
|
79
pkg/kinds/rolebinding/rolebinding_kind_gen.go
Normal file
79
pkg/kinds/rolebinding/rolebinding_kind_gen.go
Normal file
@ -0,0 +1,79 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// CoreKindJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package rolebinding
|
||||
|
||||
import (
|
||||
"github.com/grafana/kindsys"
|
||||
"github.com/grafana/thema"
|
||||
"github.com/grafana/thema/vmux"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
)
|
||||
|
||||
// rootrel is the relative path from the grafana repository root to the
|
||||
// directory containing the .cue files in which this kind is defined. Necessary
|
||||
// for runtime errors related to the definition and/or lineage to provide
|
||||
// a real path to the correct .cue file.
|
||||
const rootrel string = "kinds/rolebinding"
|
||||
|
||||
// TODO standard generated docs
|
||||
type Kind struct {
|
||||
kindsys.Core
|
||||
lin thema.ConvergentLineage[*Resource]
|
||||
jcodec vmux.Codec
|
||||
valmux vmux.ValueMux[*Resource]
|
||||
}
|
||||
|
||||
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
|
||||
var _ kindsys.Core = &Kind{}
|
||||
|
||||
// TODO standard generated docs
|
||||
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
|
||||
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k := &Kind{}
|
||||
k.Core, err = kindsys.BindCore(rt, def, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get the thema.Schema that the meta says is in the current version (which
|
||||
// codegen ensures is always the latest)
|
||||
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
|
||||
tsch, err := thema.BindType(cursch, &Resource{})
|
||||
if err != nil {
|
||||
// Should be unreachable, modulo bugs in the Thema->Go code generator
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.jcodec = vmux.NewJSONCodec("rolebinding.json")
|
||||
k.lin = tsch.ConvergentLineage()
|
||||
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
|
||||
// to the the RoleBinding [Resource] type generated from the current schema, v0.0.
|
||||
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
|
||||
return k.lin
|
||||
}
|
||||
|
||||
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
|
||||
// at any schematized dashboard version to an instance of RoleBinding [Resource].
|
||||
//
|
||||
// Validation and translation errors emitted from this func will identify the
|
||||
// input bytes as "dashboard.json".
|
||||
//
|
||||
// This is a thin wrapper around Thema's [vmux.ValueMux].
|
||||
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
|
||||
return k.valmux(b)
|
||||
}
|
42
pkg/kinds/rolebinding/rolebinding_metadata_gen.go
Normal file
42
pkg/kinds/rolebinding/rolebinding_metadata_gen.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package rolebinding
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Metadata defines model for Metadata.
|
||||
type Metadata struct {
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
|
||||
// extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
ExtraFields map[string]interface{} `json:"extraFields"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Uid string `json:"uid"`
|
||||
UpdateTimestamp time.Time `json:"updateTimestamp"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
}
|
||||
|
||||
// _kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
// It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
// As it is also intended to be generic enough to function with any API Server.
|
||||
type KubeObjectMetadata struct {
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Uid string `json:"uid"`
|
||||
}
|
72
pkg/kinds/rolebinding/rolebinding_spec_gen.go
Normal file
72
pkg/kinds/rolebinding/rolebinding_spec_gen.go
Normal file
@ -0,0 +1,72 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package rolebinding
|
||||
|
||||
// Defines values for BuiltinRoleRefKind.
|
||||
const (
|
||||
BuiltinRoleRefKindBuiltinRole BuiltinRoleRefKind = "BuiltinRole"
|
||||
)
|
||||
|
||||
// Defines values for BuiltinRoleRefName.
|
||||
const (
|
||||
BuiltinRoleRefNameAdmin BuiltinRoleRefName = "admin"
|
||||
BuiltinRoleRefNameEditor BuiltinRoleRefName = "editor"
|
||||
BuiltinRoleRefNameViewer BuiltinRoleRefName = "viewer"
|
||||
)
|
||||
|
||||
// Defines values for CustomRoleRefKind.
|
||||
const (
|
||||
CustomRoleRefKindRole CustomRoleRefKind = "Role"
|
||||
)
|
||||
|
||||
// Defines values for SubjectKind.
|
||||
const (
|
||||
SubjectKindTeam SubjectKind = "Team"
|
||||
SubjectKindUser SubjectKind = "User"
|
||||
)
|
||||
|
||||
// BuiltinRoleRef defines model for BuiltinRoleRef.
|
||||
type BuiltinRoleRef struct {
|
||||
Kind BuiltinRoleRefKind `json:"kind"`
|
||||
Name BuiltinRoleRefName `json:"name"`
|
||||
}
|
||||
|
||||
// BuiltinRoleRefKind defines model for BuiltinRoleRef.Kind.
|
||||
type BuiltinRoleRefKind string
|
||||
|
||||
// BuiltinRoleRefName defines model for BuiltinRoleRef.Name.
|
||||
type BuiltinRoleRefName string
|
||||
|
||||
// CustomRoleRef defines model for CustomRoleRef.
|
||||
type CustomRoleRef struct {
|
||||
Kind CustomRoleRefKind `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// CustomRoleRefKind defines model for CustomRoleRef.Kind.
|
||||
type CustomRoleRefKind string
|
||||
|
||||
// Subject defines model for Subject.
|
||||
type Subject struct {
|
||||
Kind SubjectKind `json:"kind"`
|
||||
|
||||
// The team/user identifier name
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// SubjectKind defines model for Subject.Kind.
|
||||
type SubjectKind string
|
||||
|
||||
// Spec defines model for Spec.
|
||||
type Spec struct {
|
||||
// The role we are discussing
|
||||
Role interface{} `json:"role"`
|
||||
Subject Subject `json:"subject"`
|
||||
}
|
74
pkg/kinds/rolebinding/rolebinding_status_gen.go
Normal file
74
pkg/kinds/rolebinding/rolebinding_status_gen.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package rolebinding
|
||||
|
||||
// Defines values for OperatorStateState.
|
||||
const (
|
||||
OperatorStateStateFailed OperatorStateState = "failed"
|
||||
OperatorStateStateInProgress OperatorStateState = "in_progress"
|
||||
OperatorStateStateSuccess OperatorStateState = "success"
|
||||
)
|
||||
|
||||
// Defines values for StatusOperatorStateState.
|
||||
const (
|
||||
StatusOperatorStateStateFailed StatusOperatorStateState = "failed"
|
||||
StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress"
|
||||
StatusOperatorStateStateSuccess StatusOperatorStateState = "success"
|
||||
)
|
||||
|
||||
// OperatorState defines model for OperatorState.
|
||||
type OperatorState struct {
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State OperatorStateState `json:"state"`
|
||||
}
|
||||
|
||||
// OperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type OperatorStateState string
|
||||
|
||||
// Status defines model for Status.
|
||||
type Status struct {
|
||||
// additionalFields is reserved for future use
|
||||
AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
|
||||
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"`
|
||||
}
|
||||
|
||||
// StatusOperatorState defines model for status.#OperatorState.
|
||||
type StatusOperatorState struct {
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State StatusOperatorStateState `json:"state"`
|
||||
}
|
||||
|
||||
// StatusOperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type StatusOperatorStateState string
|
@ -12,12 +12,15 @@ package corekind
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/accesspolicy"
|
||||
"github.com/grafana/grafana/pkg/kinds/dashboard"
|
||||
"github.com/grafana/grafana/pkg/kinds/folder"
|
||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||
"github.com/grafana/grafana/pkg/kinds/playlist"
|
||||
"github.com/grafana/grafana/pkg/kinds/preferences"
|
||||
"github.com/grafana/grafana/pkg/kinds/publicdashboard"
|
||||
"github.com/grafana/grafana/pkg/kinds/role"
|
||||
"github.com/grafana/grafana/pkg/kinds/rolebinding"
|
||||
"github.com/grafana/grafana/pkg/kinds/serviceaccount"
|
||||
"github.com/grafana/grafana/pkg/kinds/team"
|
||||
"github.com/grafana/kindsys"
|
||||
@ -41,28 +44,39 @@ import (
|
||||
// kind-schematized type.
|
||||
type Base struct {
|
||||
all []kindsys.Core
|
||||
accesspolicy *accesspolicy.Kind
|
||||
dashboard *dashboard.Kind
|
||||
folder *folder.Kind
|
||||
librarypanel *librarypanel.Kind
|
||||
playlist *playlist.Kind
|
||||
preferences *preferences.Kind
|
||||
publicdashboard *publicdashboard.Kind
|
||||
role *role.Kind
|
||||
rolebinding *rolebinding.Kind
|
||||
serviceaccount *serviceaccount.Kind
|
||||
team *team.Kind
|
||||
}
|
||||
|
||||
// type guards
|
||||
var (
|
||||
_ kindsys.Core = &accesspolicy.Kind{}
|
||||
_ kindsys.Core = &dashboard.Kind{}
|
||||
_ kindsys.Core = &folder.Kind{}
|
||||
_ kindsys.Core = &librarypanel.Kind{}
|
||||
_ kindsys.Core = &playlist.Kind{}
|
||||
_ kindsys.Core = &preferences.Kind{}
|
||||
_ kindsys.Core = &publicdashboard.Kind{}
|
||||
_ kindsys.Core = &role.Kind{}
|
||||
_ kindsys.Core = &rolebinding.Kind{}
|
||||
_ kindsys.Core = &serviceaccount.Kind{}
|
||||
_ kindsys.Core = &team.Kind{}
|
||||
)
|
||||
|
||||
// AccessPolicy returns the [kindsys.Interface] implementation for the accesspolicy kind.
|
||||
func (b *Base) AccessPolicy() *accesspolicy.Kind {
|
||||
return b.accesspolicy
|
||||
}
|
||||
|
||||
// Dashboard returns the [kindsys.Interface] implementation for the dashboard kind.
|
||||
func (b *Base) Dashboard() *dashboard.Kind {
|
||||
return b.dashboard
|
||||
@ -93,6 +107,16 @@ func (b *Base) PublicDashboard() *publicdashboard.Kind {
|
||||
return b.publicdashboard
|
||||
}
|
||||
|
||||
// Role returns the [kindsys.Interface] implementation for the role kind.
|
||||
func (b *Base) Role() *role.Kind {
|
||||
return b.role
|
||||
}
|
||||
|
||||
// RoleBinding returns the [kindsys.Interface] implementation for the rolebinding kind.
|
||||
func (b *Base) RoleBinding() *rolebinding.Kind {
|
||||
return b.rolebinding
|
||||
}
|
||||
|
||||
// ServiceAccount returns the [kindsys.Interface] implementation for the serviceaccount kind.
|
||||
func (b *Base) ServiceAccount() *serviceaccount.Kind {
|
||||
return b.serviceaccount
|
||||
@ -107,6 +131,12 @@ func doNewBase(rt *thema.Runtime) *Base {
|
||||
var err error
|
||||
reg := &Base{}
|
||||
|
||||
reg.accesspolicy, err = accesspolicy.NewKind(rt)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error while initializing the accesspolicy Kind: %s", err))
|
||||
}
|
||||
reg.all = append(reg.all, reg.accesspolicy)
|
||||
|
||||
reg.dashboard, err = dashboard.NewKind(rt)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error while initializing the dashboard Kind: %s", err))
|
||||
@ -143,6 +173,18 @@ func doNewBase(rt *thema.Runtime) *Base {
|
||||
}
|
||||
reg.all = append(reg.all, reg.publicdashboard)
|
||||
|
||||
reg.role, err = role.NewKind(rt)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error while initializing the role Kind: %s", err))
|
||||
}
|
||||
reg.all = append(reg.all, reg.role)
|
||||
|
||||
reg.rolebinding, err = rolebinding.NewKind(rt)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error while initializing the rolebinding Kind: %s", err))
|
||||
}
|
||||
reg.all = append(reg.all, reg.rolebinding)
|
||||
|
||||
reg.serviceaccount, err = serviceaccount.NewKind(rt)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error while initializing the serviceaccount Kind: %s", err))
|
||||
|
@ -3,6 +3,7 @@ package database
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -32,6 +33,7 @@ type getUserPermissionsTestCase struct {
|
||||
teamPermissions []string
|
||||
builtinPermissions []string
|
||||
expected int
|
||||
policyCount int
|
||||
}
|
||||
|
||||
func TestAccessControlStore_GetUserPermissions(t *testing.T) {
|
||||
@ -44,6 +46,7 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
|
||||
teamPermissions: []string{"100", "2"},
|
||||
builtinPermissions: []string{"5", "6"},
|
||||
expected: 7,
|
||||
policyCount: 7,
|
||||
},
|
||||
{
|
||||
desc: "Should not get admin roles",
|
||||
@ -53,6 +56,7 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
|
||||
teamPermissions: []string{"100", "2"},
|
||||
builtinPermissions: []string{"5", "6"},
|
||||
expected: 5,
|
||||
policyCount: 7,
|
||||
},
|
||||
{
|
||||
desc: "Should work without org role",
|
||||
@ -62,6 +66,7 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
|
||||
teamPermissions: []string{"100", "2"},
|
||||
builtinPermissions: []string{"5", "6"},
|
||||
expected: 5,
|
||||
policyCount: 7,
|
||||
},
|
||||
{
|
||||
desc: "should only get br permissions for anonymous user",
|
||||
@ -72,6 +77,7 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
|
||||
teamPermissions: []string{"100", "2"},
|
||||
builtinPermissions: []string{"5", "6"},
|
||||
expected: 2,
|
||||
policyCount: 7,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@ -132,6 +138,17 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, permissions, tt.expected)
|
||||
|
||||
policies, err := GetAccessPolicies(context.Background(), user.OrgID, store.sql.GetSqlxSession(),
|
||||
func(ctx context.Context, orgID int64, scope string) ([]string, error) {
|
||||
return strings.Split(scope, ":"), nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, policies, tt.policyCount)
|
||||
|
||||
for idx, p := range policies {
|
||||
fmt.Printf("POLICIES[%d] %+v\n", idx, p.Spec)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
142
pkg/services/accesscontrol/database/policies.go
Normal file
142
pkg/services/accesscontrol/database/policies.go
Normal file
@ -0,0 +1,142 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/accesspolicy"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/session"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func GetAccessPolicies(ctx context.Context, orgID int64, sql *session.SessionDB, resolver accesscontrol.ScopeAttributeResolverFunc) ([]accesspolicy.Resource, error) {
|
||||
type permissionInfo struct {
|
||||
RoleUID string
|
||||
RoleName string
|
||||
Scope string
|
||||
Action string
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
info := &permissionInfo{}
|
||||
policies := make([]accesspolicy.Resource, 0)
|
||||
current := &accesspolicy.Resource{}
|
||||
prevKey := ""
|
||||
rows, err := sql.Query(ctx, `SELECT
|
||||
role.uid as role_uid,
|
||||
role.name as role_name,
|
||||
scope,
|
||||
action,
|
||||
permission.created,
|
||||
permission.updated
|
||||
FROM permission
|
||||
JOIN role ON permission.role_id = role.id
|
||||
WHERE org_id=?
|
||||
ORDER BY role.id ASC, scope ASC, action ASC`, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
created := time.Now()
|
||||
updated := time.Now()
|
||||
|
||||
for rows.Next() {
|
||||
err = rows.Scan(
|
||||
&info.RoleUID,
|
||||
&info.RoleName,
|
||||
&info.Scope,
|
||||
&info.Action,
|
||||
&info.Created,
|
||||
&info.Updated,
|
||||
)
|
||||
if err != nil {
|
||||
return policies, err
|
||||
}
|
||||
|
||||
key := info.RoleUID + "/" + info.Scope
|
||||
if key != prevKey {
|
||||
created = info.Created
|
||||
updated = info.Updated
|
||||
if len(current.Spec.Rules) > 0 {
|
||||
current.Spec.Rules = accesspolicy.ReduceRules(current.Spec.Rules)
|
||||
policies = append(policies, *current)
|
||||
}
|
||||
scope, err := resolver(ctx, orgID, info.Scope)
|
||||
if err != nil {
|
||||
return policies, err
|
||||
}
|
||||
if len(scope) != 3 {
|
||||
return policies, fmt.Errorf("expected three part scope")
|
||||
}
|
||||
|
||||
current = &accesspolicy.Resource{
|
||||
Metadata: accesspolicy.Metadata{
|
||||
CreationTimestamp: created,
|
||||
UpdateTimestamp: updated,
|
||||
},
|
||||
Spec: accesspolicy.Spec{
|
||||
Role: accesspolicy.RoleRef{
|
||||
Kind: accesspolicy.RoleRefKindRole,
|
||||
Name: info.RoleUID,
|
||||
Xname: info.RoleName,
|
||||
},
|
||||
Scope: accesspolicy.ResourceRef{
|
||||
Kind: scope[0],
|
||||
Name: scope[2],
|
||||
},
|
||||
Rules: make([]accesspolicy.AccessRule, 0),
|
||||
},
|
||||
}
|
||||
// When the value is not a UID, set the prefix to $ -- an invalid name
|
||||
if scope[1] != "uid" {
|
||||
current.Spec.Scope.Name = fmt.Sprintf("$%s:%s", scope[1], scope[2])
|
||||
}
|
||||
|
||||
// Skip role+role binding for direct users
|
||||
if strings.HasPrefix(info.RoleName, "managed:users:") {
|
||||
current.Spec.Role.Kind = accesspolicy.RoleRefKindUser
|
||||
current.Spec.Role.Name = "$TODO:" + info.RoleName
|
||||
}
|
||||
|
||||
prevKey = key
|
||||
}
|
||||
|
||||
if info.Created.Before(created) {
|
||||
created = info.Created
|
||||
current.Metadata.CreationTimestamp = created
|
||||
}
|
||||
if info.Updated.After(updated) {
|
||||
updated = info.Updated
|
||||
current.Metadata.UpdateTimestamp = updated
|
||||
}
|
||||
|
||||
action := strings.Split(info.Action, ":")
|
||||
if len(action) != 2 {
|
||||
return policies, fmt.Errorf("expected two part action")
|
||||
}
|
||||
parts := strings.SplitN(action[0], ".", 2)
|
||||
rule := accesspolicy.AccessRule{
|
||||
Verb: action[1],
|
||||
Kind: parts[0],
|
||||
}
|
||||
if len(parts) > 1 {
|
||||
rule.Target = util.Pointer(parts[1])
|
||||
}
|
||||
|
||||
// // When the scope is dashboards or teams
|
||||
// // ... hymmm ... this would imply permissions
|
||||
// if rule.Kind == current.Spec.Scope.Kind {
|
||||
// rule.Kind = "*"
|
||||
// }
|
||||
|
||||
current.Spec.Rules = append(current.Spec.Rules, rule)
|
||||
}
|
||||
if current.Spec.Scope.Name != "" {
|
||||
current.Spec.Rules = accesspolicy.ReduceRules(current.Spec.Rules)
|
||||
policies = append(policies, *current)
|
||||
}
|
||||
return policies, err
|
||||
}
|
@ -13,7 +13,7 @@ export const AppChromeUpdate = React.memo<AppChromeUpdateProps>(({ actions }: Ap
|
||||
const { chrome } = useGrafana();
|
||||
|
||||
// We use useLayoutEffect here to make sure that the chrome is updated before the page is rendered
|
||||
// This prevents flickering actions when going from one dashbaord to another for example
|
||||
// This prevents flickering actions when going from one dashboard to another for example
|
||||
useLayoutEffect(() => {
|
||||
chrome.update({ actions });
|
||||
});
|
||||
|
@ -37,7 +37,7 @@ export const Page: PageType = ({
|
||||
const pageHeaderNav = pageNav ?? navModel?.node;
|
||||
|
||||
// We use useLayoutEffect here to make sure that the chrome is updated before the page is rendered
|
||||
// This prevents flickering sectionNav when going from dashbaord to settings for example
|
||||
// This prevents flickering sectionNav when going from dashboard to settings for example
|
||||
useLayoutEffect(() => {
|
||||
if (navModel) {
|
||||
chrome.update({
|
||||
|
@ -4,7 +4,7 @@ import { FieldColorModeId, ThresholdsMode } from '@grafana/schema/src';
|
||||
import { DashboardModel } from '../state/DashboardModel';
|
||||
import { createDashboardModelFixture, createPanelJSONFixture } from '../state/__fixtures__/dashboardFixtures';
|
||||
|
||||
describe('Merge dashbaord panels', () => {
|
||||
describe('Merge dashboard panels', () => {
|
||||
describe('simple changes', () => {
|
||||
let dashboard: DashboardModel;
|
||||
let rawPanels: PanelModel[];
|
||||
|
@ -18,7 +18,7 @@ export interface GrafanaStorage {
|
||||
/** Called before save */
|
||||
getOptions: (path: string) => Promise<ItemOptions>;
|
||||
|
||||
/** Saves dashbaords */
|
||||
/** Saves dashboards */
|
||||
write: (path: string, options: WriteValueRequest) => Promise<WriteValueResponse>;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user