RBAC: Make RBAC action names more consistent (#49730)

* update action names

* correctly retrieve teams for signed in user

* remove test

* undo swagger changes

* undo swagger changes pt2

* add migration from old action names to the new ones

* rename from list to read

* linting

* also update alertign actions

* fix migration
This commit is contained in:
Ieva
2022-06-02 13:14:48 +01:00
committed by GitHub
parent 24c6a73095
commit 5dbea9996b
32 changed files with 279 additions and 222 deletions

View File

@@ -79,7 +79,7 @@ Query Parameters:
| roles:read | roles:\* |
#### Example request
```http
GET /api/access-control/roles
Accept: application/json
@@ -180,13 +180,13 @@ Content-Type: application/json; charset=UTF-8
#### JSON body schema
| Field Name | Date Type | Required | Description |
| Field Name | Date Type | Required | Description |
| ----------- | ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| uid | string | No | UID of the role. If not present, the UID will be automatically created for you and returned in response. Refer to the [Custom roles]({{< relref "../../enterprise/access-control/about-rbac/#custom-roles" >}}) for more information. |
| global | boolean | No | A flag indicating if the role is global or not. If set to `false`, the default org ID of the authenticated user will be used from the request. |
| version | number | No | Version of the role. If not present, version 0 will be assigned to the role and returned in the response. Refer to the [Custom roles]({{< relref "../../enterprise/access-control/about-rbac/#custom-roles" >}}) for more information. |
| name | string | Yes | Name of the role. Refer to [Custom roles]({{< relref "../../enterprise/access-control/about-rbac/#custom-roles" >}}) for more information. |
| description | string | No | Description of the role. |
| description | string | No | Description of the role. |
| displayName | string | No | Display name of the role, visible in the UI. |
| group | string | No | The group name the role belongs to. |
| hidden | boolean | No | Specify whether the role is hidden or not. If set to `true`, then the role does not show in the role picker. It will not be listed by API endpoints unless explicitly specified. |
@@ -489,7 +489,7 @@ Query Parameters:
`permissions:type:delegate` scope ensures that users can only unassign roles which have same, or a subset of permissions which the user has.
For example, if a user does not have required permissions for creating users, they won't be able to unassign a role which will allow to do that. This is done to prevent escalation of privileges.
| Action | Scope |
| Action | Scope |
| ------------------ | ------------------------- |
| users.roles:remove | permissions:type:delegate |
@@ -537,7 +537,7 @@ Lists the permissions that a given user has.
#### Required permissions
`permissions:type:delegate` scope ensures that users can only assign or unassign roles which have same, or a subset of permissions which the user has.
`permissions:type:delegate` scope ensures that users can only assign or unassign roles which have same, or a subset of permissions which the user has.
For example, if a user does not have required permissions for creating users, they won't be able to assign or unassign a role which will allow to do that. This is done to prevent escalation of privileges.
| Action | Scope |
@@ -763,7 +763,7 @@ Query Parameters:
#### JSON body schema
| Field Name | Date Type | Required | Description |
| ------------- | --------- | -------- | -------------------------------------------------------------- |
| ------------- | --------- | -------- | -------------------------------------------------------------- |
| roleUids | list | Yes | List of role UIDs. |
| includeHidden | boolean | No | Specify whether the hidden role assignments should be updated. |

View File

@@ -380,9 +380,9 @@ Change password for a specific user.
## Logout User
`POST /api/admin/users/:id/logout`
Logout user revokes all auth tokens (devices) for the user. User of issued auth tokens (devices) will no longer be logged in
`POST /api/admin/users/:id/logout`
Logout user revokes all auth tokens (devices) for the user. User of issued auth tokens (devices) will no longer be logged in
and will be required to authenticate again upon next activity.
Only works with Basic Authentication (username and password). See [introduction](http://docs.grafana.org/http_api/admin/#admin-api) for an explanation.
@@ -413,9 +413,9 @@ Only works with Basic Authentication (username and password). See [introduction]
## Reload provisioning configurations
`POST /api/admin/provisioning/dashboards/reload`
`POST /api/admin/provisioning/datasources/reload`
`POST /api/admin/provisioning/dashboards/reload`
`POST /api/admin/provisioning/datasources/reload`
`POST /api/admin/provisioning/plugins/reload`
@@ -516,7 +516,7 @@ See note in the [introduction]({{< ref "#admin-api" >}}) for an explanation.
| Action | Scope |
| -------------------- | --------------- |
| users.authtoken:list | global.users:\* |
| users.authtoken:read | global.users:\* |
**Example Request**:
@@ -573,9 +573,9 @@ Only works with Basic Authentication (username and password). See [introduction]
See note in the [introduction]({{< ref "#admin-api" >}}) for an explanation.
| Action | Scope |
| ---------------------- | --------------- |
| users.authtoken:update | global.users:\* |
| Action | Scope |
| --------------------- | --------------- |
| users.authtoken:write | global.users:\* |
**Example Request**:

View File

@@ -71,9 +71,9 @@ Manually ask license issuer for a new token.
See note in the [introduction]({{< ref "#enterprise-license-api" >}}) for an explanation.
| Action | Scope |
| ---------------- | ----- |
| licensing:update | n/a |
| --------------- | ----- |
| licensing:write | n/a |
### Examples
**Example request:**

View File

@@ -149,9 +149,9 @@ Content-Type: application/json
```
### Delete user in current organization
`DELETE /api/org/users/:userId`
`DELETE /api/org/users/:userId`
**Required permissions**
See note in the [introduction]({{< ref "#organization-api" >}}) for an explanation.
@@ -605,9 +605,9 @@ Only works with Basic Authentication (username and password), see [introduction]
See note in the [introduction]({{< ref "#organization-api" >}}) for an explanation.
| Action | Scope |
| --------------------- | -------- |
| org.users.role:update | users:\* |
| Action | Scope |
| --------------- | -------- |
| org.users:write | users:\* |
**Example Request**:

View File

@@ -140,9 +140,9 @@ Content-Type: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
```
Requires basic authentication and that the authenticated user is a Grafana Admin.
Requires basic authentication and that the authenticated user is a Grafana Admin.
**Example Response**:
```http
@@ -241,9 +241,9 @@ Content-Type: application/json
**Example Response**:
```http
Content-Type: application/json
```
HTTP/1.1 200
Content-Type: application/json
```
## User
@@ -280,9 +280,9 @@ Content-Type: application/json
**Example Request**:
```http
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
PUT /api/user/password HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
```
@@ -318,9 +318,10 @@ Content-Type: application/json
```http
POST /api/users/7/using/2 HTTP/1.1
Authorization: Basic YWRtaW46YWRtaW4=
**Example Response**:
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json

View File

@@ -91,9 +91,9 @@ To learn more about the permissions you can grant for each resource, refer to [R
If you are a Grafana Enterprise customer, you can create custom roles to manage user permissions in a way that meets your security requirements.
Custom roles contain unique combinations of permissions _actions_ and _scopes_. An action defines the action a use can perform on a Grafana resource. For example, the `teams.roles:list` action allows a user to see a list of roles associated with each team.
Custom roles contain unique combinations of permissions _actions_ and _scopes_. An action defines the action a use can perform on a Grafana resource. For example, the `teams.roles:read` action allows a user to see a list of roles associated with each team.
A scope describes where an action can be performed. For example, the `teams:id:1` scope restricts the user's action to the team with ID `1`. When paired with the `teams.roles:list` action, this permission prohibits the user from viewing the roles for teams other than team `1`.
A scope describes where an action can be performed. For example, the `teams:id:1` scope restricts the user's action to the team with ID `1`. When paired with the `teams.roles:read` action, this permission prohibits the user from viewing the roles for teams other than team `1`.
Consider creating a custom role when fixed roles do not meet your permissions requirements.

View File

@@ -27,7 +27,7 @@ The following list contains role-based access control actions.
| `alert.instances.external:write` | `datasources:*`<br>`datasources:uid:*` | Manage alerts and silences in data sources that support alerting. |
| `alert.instances:create` | n/a | Create silences in the current organization. |
| `alert.instances:read` | n/a | Read alerts and silences in the current organization. |
| `alert.instances:update` | n/a | Update and expire silences in the current organization. |
| `alert.instances:write` | n/a | Update and expire silences in the current organization. |
| `alert.notifications.external:read` | `datasources:*`<br>`datasources:uid:*` | Read templates, contact points, notification policies, and mute timings in data sources that support alerting. |
| `alert.notifications.external:write` | `datasources:*`<br>`datasources:uid:*` | Manage templates, contact points, notification policies, and mute timings in data sources that support alerting. |
| `alert.notifications:write` | n/a | Manage templates, contact points, notification policies, and mute timings in the current organization. |
@@ -37,7 +37,7 @@ The following list contains role-based access control actions.
| `alert.rules:create` | `folders:*`<br>`folders:uid:*` | Create Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. |
| `alert.rules:delete` | `folders:*`<br>`folders:uid:*` | Delete Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. |
| `alert.rules:read` | `folders:*`<br>`folders:uid:*` | Read Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. |
| `alert.rules:update` | `folders:*`<br>`folders:uid:*` | Update Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. |
| `alert.rules:write` | `folders:*`<br>`folders:uid:*` | Update Grafana alert rules in a folder. Combine this permission with `folders:read` in a scope that includes the folder and `datasources:query` in the scope of data sources the user can query. |
| `annotations:create` | `annotations:*`<br>`annotations:type:*` | Create annotations. |
| `annotations:delete` | `annotations:*`<br>`annotations:type:*` | Delete annotations. |
| `annotations:read` | `annotations:*`<br>`annotations:type:*` | Read annotations and annotation tags. |
@@ -73,8 +73,8 @@ The following list contains role-based access control actions.
| `licensing.reports:read` | n/a | Get custom permission reports. |
| `licensing:delete` | n/a | Delete the license token. |
| `licensing:read` | n/a | Read licensing information. |
| `licensing:update` | n/a | Update the license token. |
| `org.users.role:update` | `users:*` <br> `users:id:*` | Update the organization role (`Viewer`, `Editor`, or `Admin`) of an organization. |
| `licensing:write` | n/a | Update the license token. |
| `org.users:write` | `users:*` <br> `users:id:*` | Update the organization role (`Viewer`, `Editor`, or `Admin`) of a user. |
| `org.users:add` | `users:*` | Add a user to an organization. |
| `org.users:read` | `users:*` <br> `users:id:*` | Get user profiles within an organization. |
| `org.users:remove` | `users:*` <br> `users:id:*` | Remove a user from an organization. |
@@ -87,16 +87,15 @@ The following list contains role-based access control actions.
| `orgs:read` | `orgs:*` <br> `orgs:id:*` | Read one or more organizations. |
| `orgs:write` | `orgs:*` <br> `orgs:id:*` | Update one or more organizations. |
| `provisioning:reload` | `provisioners:*` | Reload provisioning files. To find the exact scope for specific provisioner, see [Scope definitions]({{< relref "#scope-definitions" >}}). |
| `reports.admin:create` | n/a | Create reports. |
| `reports.admin:write` | `reports:*` <br> `reports:id:*` | Update reports. |
| `reports:create` | n/a | Create reports. |
| `reports:write` | `reports:*` <br> `reports:id:*` | Update reports. |
| `reports.settings:read` | n/a | Read report settings. |
| `reports.settings:write` | n/a | Update report settings. |
| `reports:delete` | `reports:*` <br> `reports:id:*` | Delete reports. |
| `reports:read` | `reports:*` | List all available reports or get a specific report. |
| `reports:send` | `reports:*` | Send a report email. |
| `roles:delete` | `permissions:type:delegate` | Delete a custom role. |
| `roles:list` | `roles:*` | List available roles without permissions. |
| `roles:read` | `roles:*` <br> `roles:uid:*` | Read a specific role with its permissions. |
| `roles:read` | `roles:*` <br> `roles:uid:*` | List roles and read a specific with its permissions. |
| `roles:write` | `permissions:type:delegate` | Create or update a custom role. |
| `roles:write` | `permissions:type:escalate` | Reset basic roles to their default permissions. |
| `server.stats:read` | n/a | Read Grafana instance statistics. |
@@ -106,23 +105,22 @@ The following list contains role-based access control actions.
| `teams.permissions:read` | `teams:*`<br>`teams:id:*` | Read members and External Group Synchronization setup for teams. |
| `teams.permissions:write` | `teams:*`<br>`teams:id:*` | Add, remove and update members and manage External Group Synchronization setup for teams. |
| `teams.roles:add` | `permissions:type:delegate` | Assign a role to a team. |
| `teams.roles:list` | `teams:*` | List roles assigned directly to a team. |
| `teams.roles:read` | `teams:*` | List roles assigned directly to a team. |
| `teams.roles:remove` | `permissions:type:delegate` | Unassign a role from a team. |
| `teams:create` | n/a | Create teams. |
| `teams:delete` | `teams:*`<br>`teams:id:*` | Delete one or more teams. |
| `teams:read` | `teams:*`<br>`teams:id:*` | Read one or more teams and team preferences. |
| `teams:write` | `teams:*`<br>`teams:id:*` | Update one or more teams and team preferences. |
| `users.authtoken:list` | `global.users:*` <br> `global.users:id:*` | List authentication tokens that are assigned to a user. |
| `users.authtoken:update` | `global.users:*` <br> `global.users:id:*` | Update authentication tokens that are assigned to a user. |
| `users.password:update` | `global.users:*` <br> `global.users:id:*` | Update a users password. |
| `users.permissions:list` | `users:*` | List permissions of a user. |
| `users.permissions:update` | `global.users:*` <br> `global.users:id:*` | Update a users organization-level permissions. |
| `users.quotas:list` | `global.users:*` <br> `global.users:id:*` | List a users quotas. |
| `users.quotas:update` | `global.users:*` <br> `global.users:id:*` | Update a users quotas. |
| `users.authtoken:read` | `global.users:*` <br> `global.users:id:*` | List authentication tokens that are assigned to a user. |
| `users.authtoken:write` | `global.users:*` <br> `global.users:id:*` | Update authentication tokens that are assigned to a user. |
| `users.password:write` | `global.users:*` <br> `global.users:id:*` | Update a users password. |
| `users.permissions:read` | `users:*` | List permissions of a user. |
| `users.permissions:write` | `global.users:*` <br> `global.users:id:*` | Update a users organization-level permissions. |
| `users.quotas:read` | `global.users:*` <br> `global.users:id:*` | List a users quotas. |
| `users.quotas:write` | `global.users:*` <br> `global.users:id:*` | Update a users quotas. |
| `users.roles:add` | `permissions:type:delegate` | Assign a role to a user. |
| `users.roles:list` | `users:*` | List roles assigned directly to a user. |
| `users.roles:read` | `users:*` | List roles assigned directly to a user. |
| `users.roles:remove` | `permissions:type:delegate` | Unassign a role from a user. |
| `users.teams:read` | `global.users:*` <br> `global.users:id:*` | Read a users teams. |
| `users:create` | n/a | Create a user. |
| `users:delete` | `global.users:*` <br> `global.users:id:*` | Delete a user. |
| `users:disable` | `global.users:*` <br> `global.users:id:*` | Disable a user. |

View File

@@ -65,7 +65,7 @@ curl --location --request GET '<grafana_url>/api/access-control/roles/qQui_LCMk'
"created": "2021-05-17T20:49:18+02:00"
},
{
"action": "org.users.role:update",
"action": "org.users:write",
"scope": "users:*",
"updated": "2021-05-17T20:49:18+02:00",
"created": "2021-05-17T20:49:18+02:00"
@@ -178,7 +178,7 @@ roles:
- name: 'fixed:org.users:writer'
global: true
permissions:
- action: 'org.users.role:update'
- action: 'org.users:write'
scope: 'users:*'
state: 'absent'
- action: 'org.users:add'
@@ -283,7 +283,7 @@ roles:
global: true
permissions:
# Permissions to remove
- action: 'teams.roles:list'
- action: 'teams.roles:read'
scope: 'teams:*'
state: 'absent'
- action: 'teams.roles:remove'

View File

@@ -173,7 +173,6 @@ roles:
| action | scope |
| -------------- | --------------------------- |
| `roles:list` | `roles:*` |
| `roles:read` | `roles:*` |
| `roles:write` | `permissions:type:delegate` |
| `roles:delete` | `permissions:type:delegate` |
@@ -204,12 +203,12 @@ roles:
- Add the following permissions to the `basic:viewer` role, using provisioning or the [RBAC HTTP API]({{< relref "../../developers/http_api/access_control.md#update-a-role" >}}):
| Action | Scope |
| ---------------------- | ------------------------------- |
| `reports.admin:create` | n/a |
| `reports.admin:write` | `reports:*` <br> `reports:id:*` |
| `reports:read` | `reports:*` |
| `reports:send` | `reports:*` |
| Action | Scope |
| ---------------- | ------------------------------- |
| `reports:create` | n/a |
| `reports:write` | `reports:*` <br> `reports:id:*` |
| `reports:read` | `reports:*` |
| `reports:send` | `reports:*` |
### Prevent a Grafana Admin from creating and inviting users

View File

@@ -24,59 +24,59 @@ The following tables list permissions associated with basic and fixed roles.
## Fixed role definitions
| Fixed role | Permissions | Description |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fixed:alerting.instances:editor` | All permissions from `fixed:alerting.instances:reader` and<br> `alert.instances:create`<br>`alert.instances:update` for organization scope <br> `alert.instances.external:write` for scope `datasources:*` | Create, update and expire all silences in the organization produced by Grafana, Mimir, and Loki.[\*](#alerting-roles) |
| `fixed:alerting.instances:reader` | `alert.instances:read` for organization scope <br> `alert.instances.external:read` for scope `datasources:*` | Read all alerts and silences in the organization produced by Grafana Alerts and Mimir and Loki alerts and silences.[\*](#alerting-roles) |
| `fixed:alerting.notifications:editor` | All permissions from `fixed:alerting.notifications:reader` and<br>`alert.notifications:write`for organization scope<br>`alert.notifications.external:read` for scope `datasources:*` | Create, update, and delete contact points, templates, mute timings and notification policies for Grafana and external Alertmanager.[\*](#alerting-roles) |
| `fixed:alerting.notifications:reader` | `alert.notifications:read` for organization scope<br>`alert.notifications.external:read` for scope `datasources:*` | Read all Grafana and Alertmanager contact points, templates, and notification policies.[\*](#alerting-roles) |
| `fixed:alerting.rules:editor` | All permissions from `fixed:alerting.rules:reader` and <br> `alert.rule:create` <br> `alert.rule:update` <br> `alert.rule:delete` for scope `folders:*` <br> `alert.rules.external:write` for scope `datasources:*` | Create, update, and delete all\* Grafana, Mimir, and Loki alert rules.[\*](#alerting-roles) |
| `fixed:alerting.rules:reader` | `alert.rule:read` for scope `folders:*` <br> `alert.rules.external:read` for scope `datasources:*` | Read all\* Grafana, Mimir, and Loki alert rules.[\*](#alerting-roles) |
| `fixed:alerting:editor` | All permissions from `fixed:alerting.rules:editor` <br>`fixed:alerting.instances:editor`<br>`fixed:alerting.notifications:editor` | Create, update, and delete Grafana, Mimir, Loki and Alertmanager alert rules\*, silences, contact points, templates, mute timings, and notification policies.[\*](#alerting-roles) |
| `fixed:alerting:reader` | All permissions from `fixed:alerting.rules:reader` <br>`fixed:alerting.instances:reader`<br>`fixed:alerting.notifications:reader` | Read-only permissions for all Grafana, Mimir, Loki and Alertmanager alert rules\*, alerts, contact points, and notification policies.[\*](#alerting-roles) |
| `fixed:annotations.dashboard:writer` | `annotations:write` <br>`annotations.create`<br> `annotations:delete` for scope `annotations:type:dashboard` | Create, update and delete dashboard annotations and annotation tags. |
| `fixed:annotations:reader` | `annotations:read` for scopes `annotations:type:*` | Read all annotations and annotation tags. |
| `fixed:annotations:writer` | All permissions from `fixed:annotations:reader` <br>`annotations:write` <br>`annotations.create`<br> `annotations:delete` for scope `annotations:type:*` | Read, create, update and delete all annotations and annotation tags. |
| `fixed:apikeys:reader` | `apikeys:read` for scope `apikeys:*` | Read all api keys. |
| `fixed:apikeys:writer` | All permissions from `fixed:apikeys:reader` and <br> `apikeys:create` <br> `apikeys:delete` for scope `apikeys:*` | Read, create, delete all api keys. |
| `fixed:dashboards.permissions:reader` | `dashboards.permissions:read` | Read all dashboard permissions. |
| `fixed:dashboards.permissions:writer` | All permissions from `fixed:dashboards.permissions:reader` and <br>`dashboards.permissions:write` | Read and update all dashboard permissions. |
| `fixed:dashboards:creator` | `dashboards:create`<br>`folders:read` | Create dashboards. |
| `fixed:dashboards:reader` | `dashboards:read` | Read all dashboards. |
| `fixed:dashboards:writer` | All permissions from `fixed:dashboards:reader` and <br>`dashboards:write`<br>`dashboards:edit`<br>`dashboards:delete`<br>`dashboards:create`<br>`dashboards.permissions:read`<br>`dashboards.permissions:write` | Read, create, update, and delete all dashboards. |
| `fixed:datasources.permissions:reader` | `datasources.permissions:read` | Read data source permissions. |
| `fixed:datasources.permissions:writer` | All permissions from `fixed:datasources.permissions:reader` and <br>`datasources.permissions:write` | Create, read, or delete permissions of a data source. |
| `fixed:datasources:explorer` | `datasources:explore` | Enable the Explore feature. Data source permissions still apply, you can only query data sources for which you have query permissions. |
| `fixed:datasources:id:reader` | `datasources.id:read` | Read the ID of a data source based on its name. |
| `fixed:datasources:reader` | `datasources:read`<br>`datasources:query` | Read and query data sources. |
| `fixed:datasources:writer` | All permissions from `fixed:datasources:reader` and <br>`datasources:create`<br>`datasources:write`<br>`datasources:delete` | Read, query, create, delete, or update a data source. |
| `fixed:folders.permissions:reader` | `folders.permissions:read` | Read all folder permissions. |
| `fixed:folders.permissions:writer` | All permissions from `fixed:folders.permissions:reader` and <br>`folders.permissions:write` | Read and update all folder permissions. |
| `fixed:folders:creator` | `folders:create` | Create folders. |
| `fixed:folders:reader` | `folders:read`<br>`dashboards:read` | Read all folders and dashboards. |
| `fixed:folders:writer` | All permissions from `fixed:dashboards:writer` and <br>`folders:read`<br>`folders:write`<br>`folders:create`<br>`folders:delete`<br>`folders.permissions:read`<br>`folders.permissions:write` | Read, create, update, and delete all folders and dashboards. |
| `fixed:ldap:reader` | `ldap.user:read`<br>`ldap.status:read` | Read the LDAP configuration and LDAP status information. |
| `fixed:ldap:writer` | All permissions from `fixed:ldap:reader` and <br>`ldap.user:sync`<br>`ldap.config:reload` | Read and update the LDAP configuration, and read LDAP status information. |
| `fixed:licensing:reader` | `licensing:read`<br>`licensing.reports:read` | Read licensing information and licensing reports. |
| `fixed:licensing:writer` | All permissions from `fixed:licensing:viewer` and <br>`licensing:update`<br>`licensing:delete` | Read licensing information and licensing reports, update and delete the license token. |
| `fixed:org.users:reader` | `org.users:read` | Read users within a single organization. |
| `fixed:org.users:writer` | All permissions from `fixed:org.users:reader` and <br>`org.users:add`<br>`org.users:remove`<br>`org.users.role:update` | Within a single organization, add a user, invite a user, read information about a user and their role, remove a user from that organization, or change the role of a user. |
| `fixed:organization:maintainer` | All permissions from `fixed:organization:reader` and <br> `orgs:write`<br>`orgs:create`<br>`orgs:delete`<br>`orgs.quotas:write` | Create, read, write, or delete an organization. Read or write its quotas. This role needs to be assigned globally. |
| `fixed:organization:reader` | `orgs:read`<br>`orgs.quotas:read` | Read an organization and its quotas. |
| `fixed:organization:writer` | All permissions from `fixed:organization:reader` and <br> `orgs:write`<br>`orgs.preferences:read`<br>`orgs.preferences:write` | Read an organization, its quotas, or its preferences. Update organization properties, or its preferences. |
| `fixed:provisioning:writer` | `provisioning:reload` | Reload provisioning. |
| `fixed:reports:reader` | `reports:read`<br>`reports:send`<br>`reports.settings:read` | Read all reports and shared report settings. |
| `fixed:reports:writer` | All permissions from `fixed:reports:reader` and <br>`reports.admin:write`<br>`reports:delete`<br>`reports.settings:write` | Create, read, update, or delete all reports and shared report settings. |
| `fixed:roles:reader` | `roles:read`<br>`roles:list`<br>`teams.roles:list`<br>`users.roles:list`<br>`users.permissions:list` | Read all access control roles, roles and permissions assigned to users, teams. |
| `fixed:roles:writer` | All permissions from `fixed:roles:reader` and <br>`roles:write`<br>`roles:delete`<br>`teams.roles:add`<br>`teams.roles:remove`<br>`users.roles:add`<br>`users.roles:remove` | Create, read, update, or delete all roles, assign or unassign roles to users, teams. |
| `fixed:roles:resetter` | `roles:write` with scope `permissions:type:escalate` | Reset basic roles to their default. |
| `fixed:settings:reader` | `settings:read` | Read Grafana instance settings. |
| `fixed:settings:writer` | All permissions from `fixed:settings:reader` and<br>`settings:write` | Read and update Grafana instance settings. |
| `fixed:stats:reader` | `server.stats:read` | Read Grafana instance statistics. |
| `fixed:teams:creator` | `teams:create`<br>`org.users:read` | Create a team and list organization users (required to manage the created team). |
| `fixed:teams:writer` | `teams:create`<br>`teams:delete`<br>`teams:read`<br>`teams:write`<br>`teams.permissions:read`<br>`teams.permissions:write` | Create, read, update and delete teams and manage team memberships. |
| `fixed:users:reader` | `users:read`<br>`users.quotas:list`<br>`users.authtoken:list`<br>`users.teams:read` | Read all users and their information, such as team memberships, authentication tokens, and quotas. |
| `fixed:users:writer` | All permissions from `fixed:users:reader` and <br>`users:write`<br>`users:create`<br>`users:delete`<br>`users:enable`<br>`users:disable`<br>`users.password:update`<br>`users.permissions:update`<br>`users:logout`<br>`users.authtoken:update`<br>`users.quotas:update` | Read and update all attributes and settings for all users in Grafana: update user information, read user information, create or enable or disable a user, make a user a Grafana administrator, sign out a user, update a users authentication token, or update quotas for all users. |
| Fixed role | Permissions | Description |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fixed:alerting.instances:editor` | All permissions from `fixed:alerting.instances:reader` and<br> `alert.instances:create`<br>`alert.instances:write` for organization scope <br> `alert.instances.external:write` for scope `datasources:*` | Create, update and expire all silences in the organization produced by Grafana, Mimir, and Loki.[\*](#alerting-roles) |
| `fixed:alerting.instances:reader` | `alert.instances:read` for organization scope <br> `alert.instances.external:read` for scope `datasources:*` | Read all alerts and silences in the organization produced by Grafana Alerts and Mimir and Loki alerts and silences.[\*](#alerting-roles) |
| `fixed:alerting.notifications:editor` | All permissions from `fixed:alerting.notifications:reader` and<br>`alert.notifications:write`for organization scope<br>`alert.notifications.external:read` for scope `datasources:*` | Create, update, and delete contact points, templates, mute timings and notification policies for Grafana and external Alertmanager.[\*](#alerting-roles) |
| `fixed:alerting.notifications:reader` | `alert.notifications:read` for organization scope<br>`alert.notifications.external:read` for scope `datasources:*` | Read all Grafana and Alertmanager contact points, templates, and notification policies.[\*](#alerting-roles) |
| `fixed:alerting.rules:editor` | All permissions from `fixed:alerting.rules:reader` and <br> `alert.rule:create` <br> `alert.rule:update` <br> `alert.rule:delete` for scope `folders:*` <br> `alert.rules.external:write` for scope `datasources:*` | Create, update, and delete all\* Grafana, Mimir, and Loki alert rules.[\*](#alerting-roles) |
| `fixed:alerting.rules:reader` | `alert.rule:read` for scope `folders:*` <br> `alert.rules.external:read` for scope `datasources:*` | Read all\* Grafana, Mimir, and Loki alert rules.[\*](#alerting-roles) |
| `fixed:alerting:editor` | All permissions from `fixed:alerting.rules:editor` <br>`fixed:alerting.instances:editor`<br>`fixed:alerting.notifications:editor` | Create, update, and delete Grafana, Mimir, Loki and Alertmanager alert rules\*, silences, contact points, templates, mute timings, and notification policies.[\*](#alerting-roles) |
| `fixed:alerting:reader` | All permissions from `fixed:alerting.rules:reader` <br>`fixed:alerting.instances:reader`<br>`fixed:alerting.notifications:reader` | Read-only permissions for all Grafana, Mimir, Loki and Alertmanager alert rules\*, alerts, contact points, and notification policies.[\*](#alerting-roles) |
| `fixed:annotations.dashboard:writer` | `annotations:write` <br>`annotations.create`<br> `annotations:delete` for scope `annotations:type:dashboard` | Create, update and delete dashboard annotations and annotation tags. |
| `fixed:annotations:reader` | `annotations:read` for scopes `annotations:type:*` | Read all annotations and annotation tags. |
| `fixed:annotations:writer` | All permissions from `fixed:annotations:reader` <br>`annotations:write` <br>`annotations.create`<br> `annotations:delete` for scope `annotations:type:*` | Read, create, update and delete all annotations and annotation tags. |
| `fixed:apikeys:reader` | `apikeys:read` for scope `apikeys:*` | Read all api keys. |
| `fixed:apikeys:writer` | All permissions from `fixed:apikeys:reader` and <br> `apikeys:create` <br> `apikeys:delete` for scope `apikeys:*` | Read, create, delete all api keys. |
| `fixed:dashboards.permissions:reader` | `dashboards.permissions:read` | Read all dashboard permissions. |
| `fixed:dashboards.permissions:writer` | All permissions from `fixed:dashboards.permissions:reader` and <br>`dashboards.permissions:write` | Read and update all dashboard permissions. |
| `fixed:dashboards:creator` | `dashboards:create`<br>`folders:read` | Create dashboards. |
| `fixed:dashboards:reader` | `dashboards:read` | Read all dashboards. |
| `fixed:dashboards:writer` | All permissions from `fixed:dashboards:reader` and <br>`dashboards:write`<br>`dashboards:edit`<br>`dashboards:delete`<br>`dashboards:create`<br>`dashboards.permissions:read`<br>`dashboards.permissions:write` | Read, create, update, and delete all dashboards. |
| `fixed:datasources.permissions:reader` | `datasources.permissions:read` | Read data source permissions. |
| `fixed:datasources.permissions:writer` | All permissions from `fixed:datasources.permissions:reader` and <br>`datasources.permissions:write` | Create, read, or delete permissions of a data source. |
| `fixed:datasources:explorer` | `datasources:explore` | Enable the Explore feature. Data source permissions still apply, you can only query data sources for which you have query permissions. |
| `fixed:datasources:id:reader` | `datasources.id:read` | Read the ID of a data source based on its name. |
| `fixed:datasources:reader` | `datasources:read`<br>`datasources:query` | Read and query data sources. |
| `fixed:datasources:writer` | All permissions from `fixed:datasources:reader` and <br>`datasources:create`<br>`datasources:write`<br>`datasources:delete` | Read, query, create, delete, or update a data source. |
| `fixed:folders.permissions:reader` | `folders.permissions:read` | Read all folder permissions. |
| `fixed:folders.permissions:writer` | All permissions from `fixed:folders.permissions:reader` and <br>`folders.permissions:write` | Read and update all folder permissions. |
| `fixed:folders:creator` | `folders:create` | Create folders. |
| `fixed:folders:reader` | `folders:read`<br>`dashboards:read` | Read all folders and dashboards. |
| `fixed:folders:writer` | All permissions from `fixed:dashboards:writer` and <br>`folders:read`<br>`folders:write`<br>`folders:create`<br>`folders:delete`<br>`folders.permissions:read`<br>`folders.permissions:write` | Read, create, update, and delete all folders and dashboards. |
| `fixed:ldap:reader` | `ldap.user:read`<br>`ldap.status:read` | Read the LDAP configuration and LDAP status information. |
| `fixed:ldap:writer` | All permissions from `fixed:ldap:reader` and <br>`ldap.user:sync`<br>`ldap.config:reload` | Read and update the LDAP configuration, and read LDAP status information. |
| `fixed:licensing:reader` | `licensing:read`<br>`licensing.reports:read` | Read licensing information and licensing reports. |
| `fixed:licensing:writer` | All permissions from `fixed:licensing:viewer` and <br>`licensing:write`<br>`licensing:delete` | Read licensing information and licensing reports, update and delete the license token. |
| `fixed:org.users:reader` | `org.users:read` | Read users within a single organization. |
| `fixed:org.users:writer` | All permissions from `fixed:org.users:reader` and <br>`org.users:add`<br>`org.users:remove`<br>`org.users:write` | Within a single organization, add a user, invite a user, read information about a user and their role, remove a user from that organization, or change the role of a user. |
| `fixed:organization:maintainer` | All permissions from `fixed:organization:reader` and <br> `orgs:write`<br>`orgs:create`<br>`orgs:delete`<br>`orgs.quotas:write` | Create, read, write, or delete an organization. Read or write its quotas. This role needs to be assigned globally. |
| `fixed:organization:reader` | `orgs:read`<br>`orgs.quotas:read` | Read an organization and its quotas. |
| `fixed:organization:writer` | All permissions from `fixed:organization:reader` and <br> `orgs:write`<br>`orgs.preferences:read`<br>`orgs.preferences:write` | Read an organization, its quotas, or its preferences. Update organization properties, or its preferences. |
| `fixed:provisioning:writer` | `provisioning:reload` | Reload provisioning. |
| `fixed:reports:reader` | `reports:read`<br>`reports:send`<br>`reports.settings:read` | Read all reports and shared report settings. |
| `fixed:reports:writer` | All permissions from `fixed:reports:reader` and <br>`reports:create`<br>`reports:write`<br>`reports:delete`<br>`reports.settings:write` | Create, read, update, or delete all reports and shared report settings. |
| `fixed:roles:reader` | `roles:read`<br>`teams.roles:read`<br>`users.roles:read`<br>`users.permissions:read` | Read all access control roles, roles and permissions assigned to users, teams. |
| `fixed:roles:writer` | All permissions from `fixed:roles:reader` and <br>`roles:write`<br>`roles:delete`<br>`teams.roles:add`<br>`teams.roles:remove`<br>`users.roles:add`<br>`users.roles:remove` | Create, read, update, or delete all roles, assign or unassign roles to users, teams. |
| `fixed:roles:resetter` | `roles:write` with scope `permissions:type:escalate` | Reset basic roles to their default. |
| `fixed:settings:reader` | `settings:read` | Read Grafana instance settings. |
| `fixed:settings:writer` | All permissions from `fixed:settings:reader` and<br>`settings:write` | Read and update Grafana instance settings. |
| `fixed:stats:reader` | `server.stats:read` | Read Grafana instance statistics. |
| `fixed:teams:creator` | `teams:create`<br>`org.users:read` | Create a team and list organization users (required to manage the created team). |
| `fixed:teams:writer` | `teams:create`<br>`teams:delete`<br>`teams:read`<br>`teams:write`<br>`teams.permissions:read`<br>`teams.permissions:write` | Create, read, update and delete teams and manage team memberships. |
| `fixed:users:reader` | `users:read`<br>`users.quotas:read`<br>`users.authtoken:read`<br>` | Read all users and their information, such as team memberships, authentication tokens, and quotas. |
| `fixed:users:writer` | All permissions from `fixed:users:reader` and <br>`users:write`<br>`users:create`<br>`users:delete`<br>`users:enable`<br>`users:disable`<br>`users.password:write`<br>`users.permissions:write`<br>`users:logout`<br>`users.authtoken:write`<br>`users.quotas:write` | Read and update all attributes and settings for all users in Grafana: update user information, read user information, create or enable or disable a user, make a user a Grafana administrator, sign out a user, update a users authentication token, or update quotas for all users. |
### Alerting roles

View File

@@ -179,7 +179,7 @@ func (hs *HTTPServer) registerRoutes() {
usersRoute.Get("/", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead)), routing.Wrap(hs.searchUsersService.SearchUsers))
usersRoute.Get("/search", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead)), routing.Wrap(hs.searchUsersService.SearchUsersWithPaging))
usersRoute.Get("/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, userIDScope)), routing.Wrap(hs.GetUserByID))
usersRoute.Get("/:id/teams", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersTeamRead, userIDScope)), routing.Wrap(hs.GetUserTeams))
usersRoute.Get("/:id/teams", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, userIDScope)), routing.Wrap(hs.GetUserTeams))
usersRoute.Get("/:id/orgs", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, userIDScope)), routing.Wrap(hs.GetUserOrgList))
// query parameters /users/lookup?loginOrEmail=admin@example.com
usersRoute.Get("/lookup", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, ac.ScopeGlobalUsersAll)), routing.Wrap(hs.GetUserByLoginOrEmail))
@@ -233,7 +233,7 @@ func (hs *HTTPServer) registerRoutes() {
orgRoute.Get("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.GetOrgUsersForCurrentOrg))
orgRoute.Get("/users/search", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.SearchOrgUsersWithPaging))
orgRoute.Post("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), quota("user"), routing.Wrap(hs.AddOrgUserToCurrentOrg))
orgRoute.Patch("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRoleUpdate, userIDScope)), routing.Wrap(hs.UpdateOrgUserForCurrentOrg))
orgRoute.Patch("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersWrite, userIDScope)), routing.Wrap(hs.UpdateOrgUserForCurrentOrg))
orgRoute.Delete("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRemove, userIDScope)), routing.Wrap(hs.RemoveOrgUserForCurrentOrg))
// invites
@@ -279,7 +279,7 @@ func (hs *HTTPServer) registerRoutes() {
orgsRoute.Delete("/", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsDelete)), routing.Wrap(hs.DeleteOrgByID))
orgsRoute.Get("/users", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.GetOrgUsers))
orgsRoute.Post("/users", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), routing.Wrap(hs.AddOrgUser))
orgsRoute.Patch("/users/:userId", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRoleUpdate, userIDScope)), routing.Wrap(hs.UpdateOrgUser))
orgsRoute.Patch("/users/:userId", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersWrite, userIDScope)), routing.Wrap(hs.UpdateOrgUser))
orgsRoute.Delete("/users/:userId", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRemove, userIDScope)), routing.Wrap(hs.RemoveOrgUser))
orgsRoute.Get("/quotas", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsQuotasRead)), routing.Wrap(hs.GetOrgQuotas))
orgsRoute.Put("/quotas/:target", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsQuotasWrite)), routing.Wrap(hs.UpdateOrgQuota))

View File

@@ -325,10 +325,10 @@ func TestGetOrgUsersAPIEndpoint_AccessControlMetadata(t *testing.T) {
enableAccessControl: true,
expectedCode: http.StatusOK,
expectedMetadata: map[string]bool{
"org.users.role:update": true,
"org.users:add": true,
"org.users:read": true,
"org.users:remove": true},
"org.users:write": true,
"org.users:add": true,
"org.users:read": true,
"org.users:remove": true},
user: testServerAdminViewer,
targetOrg: testServerAdminViewer.OrgId,
},

View File

@@ -154,7 +154,7 @@ func (hs *HTTPServer) GetSignedInUserOrgList(c *models.ReqContext) response.Resp
// GET /api/user/teams
func (hs *HTTPServer) GetSignedInUserTeamList(c *models.ReqContext) response.Response {
return hs.getUserTeamList(c.Req.Context(), c.OrgId, c.UserId)
return hs.getUserTeamList(c, c.OrgId, c.UserId)
}
// GET /api/users/:id/teams
@@ -163,13 +163,13 @@ func (hs *HTTPServer) GetUserTeams(c *models.ReqContext) response.Response {
if err != nil {
return response.Error(http.StatusBadRequest, "id is invalid", err)
}
return hs.getUserTeamList(c.Req.Context(), c.OrgId, id)
return hs.getUserTeamList(c, c.OrgId, id)
}
func (hs *HTTPServer) getUserTeamList(ctx context.Context, orgID int64, userID int64) response.Response {
query := models.GetTeamsByUserQuery{OrgId: orgID, UserId: userID}
func (hs *HTTPServer) getUserTeamList(c *models.ReqContext, orgID int64, userID int64) response.Response {
query := models.GetTeamsByUserQuery{OrgId: orgID, UserId: userID, SignedInUser: c.SignedInUser}
if err := hs.SQLStore.GetTeamsByUser(ctx, &query); err != nil {
if err := hs.SQLStore.GetTeamsByUser(c.Req.Context(), &query); err != nil {
return response.Error(500, "Failed to get user teams", err)
}

View File

@@ -62,9 +62,10 @@ type GetTeamByIdQuery struct {
const FilterIgnoreUser int64 = 0
type GetTeamsByUserQuery struct {
OrgId int64
UserId int64 `json:"userId"`
Result []*TeamDTO `json:"teams"`
OrgId int64
UserId int64 `json:"userId"`
Result []*TeamDTO `json:"teams"`
SignedInUser *SignedInUser
}
type SearchTeamsQuery struct {

View File

@@ -11,7 +11,7 @@ import (
var sqlIDAcceptList = map[string]struct{}{
"id": {},
"org_user.user_id": {},
"role.id": {},
"role.uid": {},
"t.id": {},
"team.id": {},
"u.id": {},

View File

@@ -285,32 +285,31 @@ const (
ActionAPIKeyDelete = "apikeys:delete"
// Users actions
ActionUsersRead = "users:read"
ActionUsersWrite = "users:write"
ActionUsersTeamRead = "users.teams:read"
ActionUsersRead = "users:read"
ActionUsersWrite = "users:write"
// We can ignore gosec G101 since this does not contain any credentials.
// nolint:gosec
ActionUsersAuthTokenList = "users.authtoken:list"
ActionUsersAuthTokenList = "users.authtoken:read"
// We can ignore gosec G101 since this does not contain any credentials.
// nolint:gosec
ActionUsersAuthTokenUpdate = "users.authtoken:update"
ActionUsersAuthTokenUpdate = "users.authtoken:write"
// We can ignore gosec G101 since this does not contain any credentials.
// nolint:gosec
ActionUsersPasswordUpdate = "users.password:update"
ActionUsersPasswordUpdate = "users.password:write"
ActionUsersDelete = "users:delete"
ActionUsersCreate = "users:create"
ActionUsersEnable = "users:enable"
ActionUsersDisable = "users:disable"
ActionUsersPermissionsUpdate = "users.permissions:update"
ActionUsersPermissionsUpdate = "users.permissions:write"
ActionUsersLogout = "users:logout"
ActionUsersQuotasList = "users.quotas:list"
ActionUsersQuotasUpdate = "users.quotas:update"
ActionUsersQuotasList = "users.quotas:read"
ActionUsersQuotasUpdate = "users.quotas:write"
// Org actions
ActionOrgUsersRead = "org.users:read"
ActionOrgUsersAdd = "org.users:add"
ActionOrgUsersRemove = "org.users:remove"
ActionOrgUsersRoleUpdate = "org.users.role:update"
ActionOrgUsersRead = "org.users:read"
ActionOrgUsersAdd = "org.users:add"
ActionOrgUsersRemove = "org.users:remove"
ActionOrgUsersWrite = "org.users:write"
// LDAP actions
ActionLDAPUsersRead = "ldap.user:read"
@@ -363,12 +362,12 @@ const (
// Alerting rules actions
ActionAlertingRuleCreate = "alert.rules:create"
ActionAlertingRuleRead = "alert.rules:read"
ActionAlertingRuleUpdate = "alert.rules:update"
ActionAlertingRuleUpdate = "alert.rules:write"
ActionAlertingRuleDelete = "alert.rules:delete"
// Alerting instances (+silences) actions
ActionAlertingInstanceCreate = "alert.instances:create"
ActionAlertingInstanceUpdate = "alert.instances:update"
ActionAlertingInstanceUpdate = "alert.instances:write"
ActionAlertingInstanceRead = "alert.instances:read"
// Alerting Notification policies actions

View File

@@ -47,14 +47,14 @@ var (
DisplayName: "Organization user writer",
Description: "Within a single organization, add a user, invite a user, read information about a user and their role, remove a user from that organization, or change the role of a user.",
Group: "User administration (organizational)",
Version: 3,
Version: 4,
Permissions: ConcatPermissions(orgUsersReaderRole.Permissions, []Permission{
{
Action: ActionOrgUsersAdd,
Scope: ScopeUsersAll,
},
{
Action: ActionOrgUsersRoleUpdate,
Action: ActionOrgUsersWrite,
Scope: ScopeUsersAll,
},
{
@@ -110,16 +110,12 @@ var (
DisplayName: "User reader",
Description: "Read all users and their information, such as team memberships, authentication tokens, and quotas.",
Group: "User administration (global)",
Version: 4,
Version: 6,
Permissions: []Permission{
{
Action: ActionUsersRead,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersTeamRead,
Scope: ScopeGlobalUsersAll,
},
{
Action: ActionUsersAuthTokenList,
Scope: ScopeGlobalUsersAll,
@@ -136,7 +132,7 @@ var (
DisplayName: "User writer",
Description: "Read and update all attributes and settings for all users in Grafana: update user information, read user information, create or enable or disable a user, make a user a Grafana administrator, sign out a user, update a users authentication token, or update quotas for all users.",
Group: "User administration (global)",
Version: 4,
Version: 5,
Permissions: ConcatPermissions(usersReaderRole.Permissions, []Permission{
{
Action: ActionUsersPasswordUpdate,

View File

@@ -273,7 +273,7 @@ func (g *dashboardGuardianImpl) getTeams() ([]*models.TeamDTO, error) {
return g.teams, nil
}
query := models.GetTeamsByUserQuery{OrgId: g.orgId, UserId: g.user.UserId}
query := models.GetTeamsByUserQuery{OrgId: g.orgId, UserId: g.user.UserId, SignedInUser: g.user}
err := g.store.GetTeamsByUser(g.ctx, &query)
g.teams = query.Result

View File

@@ -4,7 +4,7 @@ import "github.com/grafana/grafana/pkg/services/accesscontrol"
const (
ActionRead = "licensing:read"
ActionUpdate = "licensing:update"
ActionUpdate = "licensing:write"
ActionDelete = "licensing:delete"
ActionReportsRead = "licensing.reports:read"
)

View File

@@ -36,7 +36,7 @@ var (
DisplayName: "Rules Editor",
Description: "Can add, update, and delete rules in any Grafana folder and external providers",
Group: AlertRolesGroup,
Version: 2,
Version: 3,
Permissions: accesscontrol.ConcatPermissions(rulesReaderRole.Role.Permissions, []accesscontrol.Permission{
{
Action: accesscontrol.ActionAlertingRuleCreate,
@@ -84,7 +84,7 @@ var (
DisplayName: "Silences Editor",
Description: "Can add and update silences in Grafana and external providers",
Group: AlertRolesGroup,
Version: 1,
Version: 2,
Permissions: accesscontrol.ConcatPermissions(instancesReaderRole.Role.Permissions, []accesscontrol.Permission{
{
Action: accesscontrol.ActionAlertingInstanceCreate,

View File

@@ -209,25 +209,7 @@ func (s *ServiceAccountsStoreImpl) RetrieveServiceAccount(ctx context.Context, o
return nil
})
if err != nil {
return nil, err
}
// Get Teams of service account. Can be optimized by combining with the query above
// in refactor
getTeamQuery := models.GetTeamsByUserQuery{UserId: serviceAccountID, OrgId: orgID}
if err := s.sqlStore.GetTeamsByUser(ctx, &getTeamQuery); err != nil {
return nil, err
}
teams := make([]string, len(getTeamQuery.Result))
for i := range getTeamQuery.Result {
teams[i] = getTeamQuery.Result[i].Name
}
serviceAccount.Teams = teams
return serviceAccount, nil
return serviceAccount, err
}
func (s *ServiceAccountsStoreImpl) RetrieveServiceAccountIdByName(ctx context.Context, orgID int64, name string) (int64, error) {

View File

@@ -4,7 +4,6 @@ import (
"context"
"testing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
"github.com/grafana/grafana/pkg/services/sqlstore"
@@ -107,20 +106,3 @@ func TestStore_RetrieveServiceAccount(t *testing.T) {
})
}
}
func TestStore_RetrieveServiceAccountWithTeams(t *testing.T) {
userToCreate := tests.TestUser{Login: "servicetestwithTeam@admin", IsServiceAccount: true}
db, store := setupTestDatabase(t)
user := tests.SetupUserServiceAccount(t, db, userToCreate)
team, err := store.sqlStore.CreateTeam("serviceTeam", "serviceTeam", user.OrgId)
require.NoError(t, err)
err = store.sqlStore.AddTeamMember(user.Id, user.OrgId, team.Id, false, models.PERMISSION_VIEW)
require.NoError(t, err)
dto, err := store.RetrieveServiceAccount(context.Background(), user.OrgId, user.Id)
require.NoError(t, err)
require.Equal(t, userToCreate.Login, dto.Login)
require.Len(t, dto.Teams, 1)
require.Equal(t, "serviceTeam", dto.Teams[0])
}

View File

@@ -0,0 +1,68 @@
package accesscontrol
import (
"fmt"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"xorm.io/xorm"
)
func AddActionNameMigrator(mg *migrator.Migrator) {
mg.AddMigration("RBAC action name migrator", &actionNameMigrator{})
}
type actionNameMigrator struct {
sess *xorm.Session
migrator *migrator.Migrator
migrator.MigrationBase
}
var _ migrator.CodeMigration = new(actionNameMigrator)
func (m *actionNameMigrator) SQL(migrator.Dialect) string {
return CodeMigrationSQL
}
func (m *actionNameMigrator) Exec(sess *xorm.Session, migrator *migrator.Migrator) error {
m.sess = sess
m.migrator = migrator
return m.migrateActionNames()
}
func (m *actionNameMigrator) migrateActionNames() error {
actionNameMapping := map[string]string{
"licensing:update": "licensing:write",
"reports.admin:create": "reports:create",
"reports.admin:write": "reports:write",
"org.users.role:update": accesscontrol.ActionOrgUsersWrite,
"users.authtoken:update": accesscontrol.ActionUsersAuthTokenUpdate,
"users.password:update": accesscontrol.ActionUsersPasswordUpdate,
"users.permissions:update": accesscontrol.ActionUsersPermissionsUpdate,
"users.quotas:update": accesscontrol.ActionUsersQuotasUpdate,
"teams.roles:list": "teams.roles:read",
"users.roles:list": "users.roles:read",
"users.authtoken:list": accesscontrol.ActionUsersAuthTokenList,
"users.quotas:list": accesscontrol.ActionUsersQuotasList,
"users.permissions:list": "users.permissions:read",
"alert.instances:update": accesscontrol.ActionAlertingInstanceUpdate,
"alert.rules:update": accesscontrol.ActionAlertingRuleUpdate,
}
for oldName, newName := range actionNameMapping {
_, err := m.sess.Table(&accesscontrol.Permission{}).Where("action = ?", oldName).Update(&accesscontrol.Permission{Action: newName})
if err != nil {
return fmt.Errorf("failed to update permission table for action %s: %w", oldName, err)
}
}
actionsToDelete := []string{"users.teams:read", "roles:list"}
for _, action := range actionsToDelete {
_, err := m.sess.Table(&accesscontrol.Permission{}).Where("action = ?", action).Delete(accesscontrol.Permission{})
if err != nil {
return fmt.Errorf("failed to update permission table for action %s: %w", action, err)
}
}
return nil
}

View File

@@ -90,6 +90,7 @@ func (*OSSMigrations) AddMigration(mg *Migrator) {
accesscontrol.AddManagedPermissionsMigration(mg)
accesscontrol.AddManagedFolderAlertActionsMigration(mg)
accesscontrol.AddActionNameMigrator(mg)
}
func addMigrationLogMigrations(mg *Migrator) {

View File

@@ -310,12 +310,23 @@ func (ss *SQLStore) GetTeamsByUser(ctx context.Context, query *models.GetTeamsBy
query.Result = make([]*models.TeamDTO, 0)
var sql bytes.Buffer
var params []interface{}
params = append(params, query.OrgId, query.UserId)
sql.WriteString(getTeamSelectSQLBase([]string{}))
sql.WriteString(` INNER JOIN team_member on team.id = team_member.team_id`)
sql.WriteString(` WHERE team.org_id = ? and team_member.user_id = ?`)
err := sess.SQL(sql.String(), query.OrgId, query.UserId).Find(&query.Result)
if !ac.IsDisabled(ss.Cfg) {
acFilter, err := ac.Filter(query.SignedInUser, "team.id", "teams:id:", ac.ActionTeamsRead)
if err != nil {
return err
}
sql.WriteString(` and` + acFilter.Where)
params = append(params, acFilter.Args...)
}
err := sess.SQL(sql.String(), params...).Find(&query.Result)
return err
})
}

View File

@@ -219,7 +219,14 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) {
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
require.NoError(t, err)
query := &models.GetTeamsByUserQuery{OrgId: testOrgID, UserId: userIds[0]}
query := &models.GetTeamsByUserQuery{
OrgId: testOrgID,
UserId: userIds[0],
SignedInUser: &models.SignedInUser{
OrgId: testOrgID,
Permissions: map[int64]map[string][]string{testOrgID: {ac.ActionOrgUsersRead: {ac.ScopeUsersAll}, ac.ActionTeamsRead: {ac.ScopeTeamsAll}}},
},
}
err = sqlStore.GetTeamsByUser(context.Background(), query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 1)

View File

@@ -577,7 +577,20 @@ func (ss *SQLStore) GetSignedInUser(ctx context.Context, query *models.GetSigned
user.ExternalAuthId = ""
}
getTeamsByUserQuery := &models.GetTeamsByUserQuery{OrgId: user.OrgId, UserId: user.UserId}
// tempUser is used to retrieve the teams for the signed in user for internal use.
tempUser := &models.SignedInUser{
OrgId: user.OrgId,
Permissions: map[int64]map[string][]string{
user.OrgId: {
ac.ActionTeamsRead: {ac.ScopeTeamsAll},
},
},
}
getTeamsByUserQuery := &models.GetTeamsByUserQuery{
OrgId: user.OrgId,
UserId: user.UserId,
SignedInUser: tempUser,
}
err = ss.GetTeamsByUser(ctx, getTeamsByUserQuery)
if err != nil {
return err

View File

@@ -617,7 +617,7 @@ func TestRulerAccess(t *testing.T) {
desc: "viewer request should fail",
url: "http://viewer:viewer@%s/api/ruler/grafana/api/v1/rules/default",
expStatus: http.StatusForbidden,
expectedMessage: `You'll need additional permissions to perform this action. Permissions needed: any of alert.rules:update, alert.rules:create, alert.rules:delete`,
expectedMessage: `You'll need additional permissions to perform this action. Permissions needed: any of alert.rules:write, alert.rules:create, alert.rules:delete`,
},
{
desc: "editor request should succeed",

View File

@@ -193,7 +193,7 @@ class UnThemedOrgRow extends PureComponent<OrgRowProps> {
const { currentRole, isChangingRole } = this.state;
const styles = getOrgRowStyles(theme);
const labelClass = cx('width-16', styles.label);
const canChangeRole = contextSrv.hasPermission(AccessControlAction.OrgUsersRoleUpdate);
const canChangeRole = contextSrv.hasPermission(AccessControlAction.OrgUsersWrite);
const canRemoveFromOrg = contextSrv.hasPermission(AccessControlAction.OrgUsersRemove);
const rolePickerDisabled = isExternalUser || !canChangeRole;
@@ -328,7 +328,7 @@ export class AddToOrgModal extends PureComponent<AddToOrgModalProps, AddToOrgMod
this.props.onOrgAdd(selectedOrg!.id, role);
// add the stored userRoles also
if (contextSrv.licensedAccessControlEnabled()) {
if (contextSrv.hasPermission(AccessControlAction.OrgUsersRoleUpdate)) {
if (contextSrv.hasPermission(AccessControlAction.OrgUsersWrite)) {
if (this.state.pendingUserId) {
await updateUserRoles(this.state.pendingRoles, this.state.pendingUserId!, this.state.pendingOrgId!);
// clear pending state

View File

@@ -40,7 +40,7 @@ const ServiceAccountListItem = memo(
const displayRolePicker =
contextSrv.hasPermission(AccessControlAction.ActionRolesList) &&
contextSrv.hasPermission(AccessControlAction.ActionUserRolesList);
const enableRolePicker = contextSrv.hasPermission(AccessControlAction.OrgUsersRoleUpdate) && canUpdateRole;
const enableRolePicker = contextSrv.hasPermission(AccessControlAction.OrgUsersWrite) && canUpdateRole;
return (
<tr key={serviceAccount.id} className={cx({ [styles.disabled]: serviceAccount.isDisabled })}>

View File

@@ -94,13 +94,13 @@ const UsersTable: FC<Props> = (props) => {
onBuiltinRoleChange={(newRole) => onRoleChange(newRole, user)}
roleOptions={roleOptions}
builtInRoles={builtinRoles}
disabled={!contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersRoleUpdate, user)}
disabled={!contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersWrite, user)}
/>
) : (
<OrgRolePicker
aria-label="Role"
value={user.role}
disabled={!contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersRoleUpdate, user)}
disabled={!contextSrv.hasPermissionInMetadata(AccessControlAction.OrgUsersWrite, user)}
onChange={(newRole) => onRoleChange(newRole, user)}
/>
)}

View File

@@ -10,18 +10,17 @@ export type UserPermission = Record<string, boolean>;
export enum AccessControlAction {
UsersRead = 'users:read',
UsersWrite = 'users:write',
UsersTeamRead = 'users.teams:read',
UsersAuthTokenList = 'users.authtoken:list',
UsersAuthTokenUpdate = 'users.authtoken:update',
UsersPasswordUpdate = 'users.password:update',
UsersAuthTokenList = 'users.authtoken:read',
UsersAuthTokenUpdate = 'users.authtoken:write',
UsersPasswordUpdate = 'users.password:write',
UsersDelete = 'users:delete',
UsersCreate = 'users:create',
UsersEnable = 'users:enable',
UsersDisable = 'users:disable',
UsersPermissionsUpdate = 'users.permissions:update',
UsersPermissionsUpdate = 'users.permissions:write',
UsersLogout = 'users:logout',
UsersQuotasList = 'users.quotas:list',
UsersQuotasUpdate = 'users.quotas:update',
UsersQuotasList = 'users.quotas:read',
UsersQuotasUpdate = 'users.quotas:write',
ServiceAccountsRead = 'serviceaccounts:read',
ServiceAccountsCreate = 'serviceaccounts:create',
@@ -37,7 +36,7 @@ export enum AccessControlAction {
OrgUsersRead = 'org.users:read',
OrgUsersAdd = 'org.users:add',
OrgUsersRemove = 'org.users:remove',
OrgUsersRoleUpdate = 'org.users.role:update',
OrgUsersWrite = 'org.users:write',
LDAPUsersRead = 'ldap.user:read',
LDAPUsersSync = 'ldap.user:sync',
@@ -59,12 +58,12 @@ export enum AccessControlAction {
ActionTeamsPermissionsRead = 'teams.permissions:read',
ActionTeamsPermissionsWrite = 'teams.permissions:write',
ActionRolesList = 'roles:list',
ActionRolesList = 'roles:read',
ActionBuiltinRolesList = 'roles.builtin:list',
ActionTeamsRolesList = 'teams.roles:list',
ActionTeamsRolesList = 'teams.roles:read',
ActionTeamsRolesAdd = 'teams.roles:add',
ActionTeamsRolesRemove = 'teams.roles:remove',
ActionUserRolesList = 'users.roles:list',
ActionUserRolesList = 'users.roles:read',
DashboardsRead = 'dashboards:read',
DashboardsWrite = 'dashboards:write',
@@ -83,12 +82,12 @@ export enum AccessControlAction {
// Alerting rules
AlertingRuleCreate = 'alert.rules:create',
AlertingRuleRead = 'alert.rules:read',
AlertingRuleUpdate = 'alert.rules:update',
AlertingRuleUpdate = 'alert.rules:write',
AlertingRuleDelete = 'alert.rules:delete',
// Alerting instances (+silences)
AlertingInstanceCreate = 'alert.instances:create',
AlertingInstanceUpdate = 'alert.instances:update',
AlertingInstanceUpdate = 'alert.instances:write',
AlertingInstanceRead = 'alert.instances:read',
// Alerting Notification policies