From c5320e2ae7cfbb8e8db378ecafcca06a0beae656 Mon Sep 17 00:00:00 2001 From: Timur Olzhabayev Date: Thu, 14 Nov 2024 10:00:04 +0100 Subject: [PATCH 01/70] Docs: fixing angular migration link (#96431) fixing docs link --- docs/sources/developers/angular_deprecation/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/developers/angular_deprecation/_index.md b/docs/sources/developers/angular_deprecation/_index.md index 060e3931dfe..7b596b88308 100644 --- a/docs/sources/developers/angular_deprecation/_index.md +++ b/docs/sources/developers/angular_deprecation/_index.md @@ -60,7 +60,7 @@ We encourage you to locate the repository of the corresponding plugin and create ### Links -- [Migrate Angular to React](https://grafana.com/developers/plugin-tools/migration-guides/migrate-angularjs-to-react) +- [Migrate Angular to React](https://grafana.com/developers/plugin-tools/migration-guides/angular-react/) - [Build a panel plugin](https://grafana.com/tutorials/build-a-panel-plugin/) - [Build a data source plugin](https://grafana.com/tutorials/build-a-data-source-plugin/) - [List of current Angular plugins]({{< relref "./angular-plugins/" >}}) From 511524781826a0f2bfe68bccef5e75215acfeeb7 Mon Sep 17 00:00:00 2001 From: Karl Persson Date: Thu, 14 Nov 2024 10:13:59 +0100 Subject: [PATCH 02/70] Zanana: Fix list test (#96401) Fix: List operation are not guaranteed to respond in a certain order --- .../authz/zanzana/server/server_list_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/services/authz/zanzana/server/server_list_test.go b/pkg/services/authz/zanzana/server/server_list_test.go index 520616b5736..cc7ef17126c 100644 --- a/pkg/services/authz/zanzana/server/server_list_test.go +++ b/pkg/services/authz/zanzana/server/server_list_test.go @@ -52,8 +52,16 @@ func testList(t *testing.T, server *Server) { require.NoError(t, err) assert.Len(t, res.GetItems(), 0) assert.Len(t, res.GetFolders(), 2) - assert.Equal(t, res.GetFolders()[0], "1") - assert.Equal(t, res.GetFolders()[1], "3") + + first := res.GetFolders()[0] + second := res.GetFolders()[1] + + if first == "3" { + first, second = second, first + } + + assert.Equal(t, first, "1") + assert.Equal(t, second, "3") }) t.Run("user:5 should be get list all dashboards.grafana.app/dashboards in folder 1 with set relation", func(t *testing.T) { From 1911256fbb2299084f1591636848a8e300e9c109 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:15:15 +0200 Subject: [PATCH 03/70] Update dependency i18next to v23.16.5 (#96392) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7f27e129d50..98c1e4c8d16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19762,11 +19762,11 @@ __metadata: linkType: hard "i18next@npm:^23.0.0, i18next@npm:^23.11.5, i18next@npm:^23.5.1": - version: 23.15.2 - resolution: "i18next@npm:23.15.2" + version: 23.16.5 + resolution: "i18next@npm:23.16.5" dependencies: "@babel/runtime": "npm:^7.23.2" - checksum: 10/ed14a290ba8e761a06b6a6ce5e608861f367eb0806fa1b4eb77494340502ad20491748ab2b27423ba186f5602b89cbcb87565683a03443d0368cd4b182ae6ef9 + checksum: 10/842b6d35d6e0822f1c8c3c2ca8af0fec9d8d9e37a54f63cdf42c0fcc7908d980082daff6ab77ad248324d2c45a0f1fe1e40efe23ba84d950722ece36b61d647c languageName: node linkType: hard From 98caf47b2244395aaa87b9df370b82d2ae48f63b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:16:43 +0200 Subject: [PATCH 04/70] Update dependency eslint-plugin-testing-library to v6.4.0 (#96391) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 98c1e4c8d16..8a1cc69e5cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11186,7 +11186,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:^5.25.0, @typescript-eslint/utils@npm:^5.58.0": +"@typescript-eslint/utils@npm:^5.25.0, @typescript-eslint/utils@npm:^5.62.0": version: 5.62.0 resolution: "@typescript-eslint/utils@npm:5.62.0" dependencies: @@ -16963,13 +16963,13 @@ __metadata: linkType: hard "eslint-plugin-testing-library@npm:^6.3.0": - version: 6.3.0 - resolution: "eslint-plugin-testing-library@npm:6.3.0" + version: 6.4.0 + resolution: "eslint-plugin-testing-library@npm:6.4.0" dependencies: - "@typescript-eslint/utils": "npm:^5.58.0" + "@typescript-eslint/utils": "npm:^5.62.0" peerDependencies: - eslint: ^7.5.0 || ^8.0.0 - checksum: 10/192b112f84f90cc7eee28965b3e7792e8d4cda71aa29690d8180f1ae9cd0e8d6a8851ee992d37285a76750f8638c04e76f768e1885168f060ca169b72ac9ec6c + eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 + checksum: 10/b9461e6662542fbfb3c0b768e6038bbaa56e345cb0600c7f07e26ff8154166abb9f7dcdc08d565c314cd9ccf0416eca8c3ce3c235b375c5fbf3f152a81142969 languageName: node linkType: hard From 9233ad64626dcc8a57379034fb3085e260614037 Mon Sep 17 00:00:00 2001 From: Misi Date: Thu, 14 Nov 2024 10:47:25 +0100 Subject: [PATCH 05/70] Docs: Update GitLab, GitHub, Okta, Generic OAuth docs (#96386) * GitHub docs changes * Bunch of updates * More updates --- .../configure-authentication/azuread/index.md | 10 +- .../generic-oauth/index.md | 121 +++++++++--------- .../configure-authentication/github/index.md | 75 ++++++----- .../configure-authentication/gitlab/index.md | 89 +++++++------ .../configure-authentication/google/index.md | 2 +- .../keycloak/index.md | 2 +- .../configure-authentication/okta/index.md | 47 +++---- 7 files changed, 186 insertions(+), 160 deletions(-) diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md index 532d992b665..dfbea92ba5f 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md @@ -12,12 +12,12 @@ labels: - cloud - enterprise - oss -menuTitle: Azure AD OAuth2 -title: Configure Azure AD OAuth2 authentication +menuTitle: Azure AD/Entra ID OAuth +title: Configure Azure AD/Entra ID OAuth authentication weight: 800 --- -# Configure Azure AD OAuth2 authentication +# Configure Azure AD/Entra ID OAuth authentication The Azure AD authentication allows you to use a Microsoft Entra ID (formerly known as Azure Active Directory) tenant as an identity provider for Grafana. You can use Entra ID application roles to assign users and groups to Grafana roles from the Azure Portal. @@ -27,7 +27,7 @@ If Users use the same email address in Microsoft Entra ID that they use with oth ## Create the Microsoft Entra ID application -To enable the Azure AD OAuth2, register your application with Entra ID. +To enable the Azure AD/Entra ID OAuth, register your application with Entra ID. 1. Log in to [Azure Portal](https://portal.azure.com), then click **Microsoft Entra ID** in the side menu. @@ -181,7 +181,7 @@ Ensure that you have followed the steps in [Create the Microsoft Entra ID applic Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure your Azure AD OAuth2 client from within Grafana using the Grafana UI. To do this, navigate to the **Administration > Authentication > Azure AD** page and fill in the form. If you have a current configuration in the Grafana configuration file, the form will be pre-populated with those values. Otherwise the form will contain default values. +As a Grafana Admin, you can configure your Azure AD/Entra ID OAuth client from within Grafana using the Grafana UI. To do this, navigate to the **Administration > Authentication > Azure AD** page and fill in the form. If you have a current configuration in the Grafana configuration file, the form will be pre-populated with those values. Otherwise the form will contain default values. After you have filled in the form, click **Save** to save the configuration. If the save was successful, Grafana will apply the new configurations. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md index 177e24dbebd..3c3ecf906bb 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/generic-oauth/ -description: Configure generic OAuth2 authentication +description: Configure Generic OAuth authentication keywords: - grafana - configuration @@ -12,12 +12,12 @@ labels: - cloud - enterprise - oss -menuTitle: Generic OAuth2 -title: Configure generic OAuth2 authentication +menuTitle: Generic OAuth +title: Configure Generic OAuth authentication weight: 700 --- -# Configure generic OAuth2 authentication +# Configure Generic OAuth authentication {{< docs/shared lookup="auth/intro.md" source="grafana" version="" >}} @@ -31,9 +31,9 @@ Grafana provides OAuth2 integrations for the following auth providers: - [Keycloak OAuth]({{< relref "../keycloak" >}}) - [Okta OAuth]({{< relref "../okta" >}}) -If your OAuth2 provider is not listed, you can use generic OAuth2 authentication. +If your OAuth2 provider is not listed, you can use Generic OAuth authentication. -This topic describes how to configure generic OAuth2 authentication using different methods and includes [examples of setting up generic OAuth2]({{< relref "#examples-of-setting-up-generic-oauth2" >}}) with specific OAuth2 providers. +This topic describes how to configure Generic OAuth authentication using different methods and includes [examples of setting up Generic OAuth]({{< relref "#examples-of-setting-up-generic-oauth2" >}}) with specific OAuth2 providers. ## Before you begin @@ -53,7 +53,7 @@ If Users use the same email address in Azure AD that they use with other authent Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure Generic OAuth2 client from within Grafana using the Generic OAuth UI. To do this, navigate to **Administration > Authentication > Generic OAuth** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. +As a Grafana Admin, you can configure Generic OAuth client from within Grafana using the Generic OAuth UI. To do this, navigate to **Administration > Authentication > Generic OAuth** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. After you have filled in the form, click **Save** to save the configuration. If the save was successful, Grafana will apply the new configurations. @@ -98,7 +98,7 @@ Ensure that you have access to the [Grafana configuration file]({{< relref "../. ### Steps -To integrate your OAuth2 provider with Grafana using our generic OAuth2 authentication, follow these steps: +To integrate your OAuth2 provider with Grafana using our Generic OAuth authentication, follow these steps: 1. Create an OAuth2 application in your chosen OAuth2 provider. 1. Set the callback URL for your OAuth2 app to `http://:/login/generic_oauth`. @@ -114,9 +114,9 @@ To integrate your OAuth2 provider with Grafana using our generic OAuth2 authenti | `client_id`, `client_secret` | These values must match the client ID and client secret from your OAuth2 app. | | `auth_url` | The authorization endpoint of your OAuth2 provider. | | `api_url` | The user information endpoint of your OAuth2 provider. Information returned by this endpoint must be compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | - | `enabled` | Enables generic OAuth2 authentication. Set this value to `true`. | + | `enabled` | Enables Generic OAuth authentication. Set this value to `true`. | - Review the list of other generic OAuth2 [configuration options]({{< relref "#configuration-options" >}}) and complete them, as necessary. + Review the list of other Generic OAuth [configuration options]({{< relref "#configuration-options" >}}) and complete them, as necessary. 1. Optional: [Configure a refresh token]({{< relref "#configure-a-refresh-token" >}}): @@ -130,7 +130,7 @@ To integrate your OAuth2 provider with Grafana using our generic OAuth2 authenti 1. Optional: [Configure team synchronization]({{< relref "#configure-team-synchronization" >}}). 1. Restart Grafana. - You should now see a generic OAuth2 login button on the login page and be able to log in or sign up with your OAuth2 provider. + You should now see a Generic OAuth login button on the login page and be able to log in or sign up with your OAuth2 provider. ### Configure login @@ -185,7 +185,7 @@ When a user logs in using an OAuth2 provider, Grafana verifies that the access t Grafana uses a refresh token to obtain a new access token without requiring the user to log in again. If a refresh token doesn't exist, Grafana logs the user out of the system after the access token has expired. -To configure generic OAuth2 to use a refresh token, set `use_refresh_token` configuration option to `true` and perform one or both of the following steps, if required: +To configure Generic OAuth to use a refresh token, set `use_refresh_token` configuration option to `true` and perform one or both of the following steps, if required: 1. Extend the `scopes` field of `[auth.generic_oauth]` section in Grafana configuration file with additional scopes. 1. Enable the refresh token on the provider. @@ -335,7 +335,7 @@ org_mapping = org_foo:org_foo:Viewer org_bar:org_bar:Editor *:org_baz:Editor By using Team Sync, you can link your OAuth2 groups to teams within Grafana. This will automatically assign users to the appropriate teams. Teams for each user are synchronized when the user logs in. -Generic OAuth2 groups can be referenced by group ID, such as `8bab1c86-8fba-33e5-2089-1d1c80ec267d` or `myteam`. +Generic OAuth groups can be referenced by group ID, such as `8bab1c86-8fba-33e5-2089-1d1c80ec267d` or `myteam`. For information on configuring OAuth2 groups with Grafana using the `groups_attribute_path` configuration option, refer to [configuration options]({{< relref "#configuration-options" >}}). To learn more about Team Sync, refer to [Configure team sync]({{< relref "../../configure-team-sync" >}}). @@ -367,55 +367,56 @@ Payload: ## Configuration options -The following table outlines the various generic OAuth2 configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. +The following table outlines the various Generic OAuth configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). -| Setting | Required | Description | Default | -| ---------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | -| `enabled` | No | Enables generic OAuth2 authentication. | `false` | -| `name` | No | Name that refers to the generic OAuth2 authentication from the Grafana user interface. | `OAuth` | -| `icon` | No | Icon used for the generic OAuth2 authentication in the Grafana user interface. | `signin` | -| `client_id` | Yes | Client ID provided by your OAuth2 app. | | -| `client_secret` | Yes | Client secret provided by your OAuth2 app. | | -| `auth_url` | Yes | Authorization endpoint of your OAuth2 provider. | | -| `token_url` | Yes | Endpoint used to obtain the OAuth2 access token. | | -| `api_url` | Yes | Endpoint used to obtain user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | | -| `auth_style` | No | Name of the [OAuth2 AuthStyle](https://pkg.go.dev/golang.org/x/oauth2#AuthStyle) to be used when ID token is requested from OAuth2 provider. It determines how `client_id` and `client_secret` are sent to Oauth2 provider. Available values are `AutoDetect`, `InParams` and `InHeader`. | `AutoDetect` | -| `scopes` | No | List of comma- or space-separated OAuth2 scopes. | `user:email` | -| `empty_scopes` | No | Set to `true` to use an empty scope during authentication. | `false` | -| `allow_sign_up` | No | Controls Grafana user creation through the generic OAuth2 login. Only existing Grafana users can log in with Generic OAuth if set to `false`. | `true` | -| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | -| `id_token_attribute_name` | No | The name of the key used to extract the ID token from the returned OAuth2 token. | `id_token` | -| `login_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user login lookup from the user ID token. For more information on how user login is retrieved, refer to [Configure login]({{< relref "#configure-login" >}}). | | -| `name_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user name lookup from the user ID token. This name will be used as the user's display name. For more information on how user display name is retrieved, refer to [Configure display name]({{< relref "#configure-display-name" >}}). | | -| `email_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user email lookup from the user information. For more information on how user email is retrieved, refer to [Configure email address]({{< relref "#configure-email-address" >}}). | | -| `email_attribute_name` | No | Name of the key to use for user email lookup within the `attributes` map of OAuth2 ID token. For more information on how user email is retrieved, refer to [Configure email address]({{< relref "#configure-email-address" >}}). | `email:primary` | -| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | -| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `org_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no value is returned, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `org_mapping` | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `allow_assign_grafana_admin` | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | -| `groups_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for user group lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no groups are found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a string array of groups. | | -| `allowed_groups` | No | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure `groups_attribute_path`. | | -| `allowed_organizations` | No | List of comma- or space-separated organizations. The user should be a member of at least one organization to log in. | | -| `allowed_domains` | No | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | | -| `team_ids` | No | String list of team IDs. If set, the user must be a member of one of the given teams to log in. If you configure `team_ids`, you must also configure `teams_url` and `team_ids_attribute_path`. | | -| `team_ids_attribute_path` | No | The [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana team ID lookup within the results returned by the `teams_url` endpoint. | | -| `teams_url` | No | The URL used to query for team IDs. If not set, the default value is `/teams`. If you configure `teams_url`, you must also configure `team_ids_attribute_path`. | | -| `tls_skip_verify_insecure` | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | -| `tls_client_cert` | No | The path to the certificate. | | -| `tls_client_key` | No | The path to the key. | | -| `tls_client_ca` | No | The path to the trusted certificate authority list. | | -| `use_pkce` | No | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `false` | -| `use_refresh_token` | No | Set to `true` to use refresh token and check access token expiration. | `false` | +| Setting | Required | Supported on Cloud | Description | Default | +| ---------------------------- | -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| `enabled` | No | Yes | Enables Generic OAuth authentication. | `false` | +| `name` | No | Yes | Name that refers to the Generic OAuth authentication from the Grafana user interface. | `OAuth` | +| `icon` | No | Yes | Icon used for the Generic OAuth authentication in the Grafana user interface. | `signin` | +| `client_id` | Yes | Yes | Client ID provided by your OAuth2 app. | | +| `client_secret` | Yes | Yes | Client secret provided by your OAuth2 app. | | +| `auth_url` | Yes | Yes | Authorization endpoint of your OAuth2 provider. | | +| `token_url` | Yes | Yes | Endpoint used to obtain the OAuth2 access token. | | +| `api_url` | Yes | Yes | Endpoint used to obtain user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | | +| `auth_style` | No | Yes | Name of the [OAuth2 AuthStyle](https://pkg.go.dev/golang.org/x/oauth2#AuthStyle) to be used when ID token is requested from OAuth2 provider. It determines how `client_id` and `client_secret` are sent to Oauth2 provider. Available values are `AutoDetect`, `InParams` and `InHeader`. | `AutoDetect` | +| `scopes` | No | Yes | List of comma- or space-separated OAuth2 scopes. | `user:email` | +| `empty_scopes` | No | Yes | Set to `true` to use an empty scope during authentication. | `false` | +| `allow_sign_up` | No | Yes | Controls Grafana user creation through the Generic OAuth login. Only existing Grafana users can log in with Generic OAuth if set to `false`. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `id_token_attribute_name` | No | Yes | The name of the key used to extract the ID token from the returned OAuth2 token. | `id_token` | +| `login_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user login lookup from the user ID token. For more information on how user login is retrieved, refer to [Configure login]({{< relref "#configure-login" >}}). | | +| `name_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user name lookup from the user ID token. This name will be used as the user's display name. For more information on how user display name is retrieved, refer to [Configure display name]({{< relref "#configure-display-name" >}}). | | +| `email_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user email lookup from the user information. For more information on how user email is retrieved, refer to [Configure email address]({{< relref "#configure-email-address" >}}). | | +| `email_attribute_name` | No | Yes | Name of the key to use for user email lookup within the `attributes` map of OAuth2 ID token. For more information on how user email is retrieved, refer to [Configure email address]({{< relref "#configure-email-address" >}}). | `email:primary` | +| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | +| `org_attribute_path` | No | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no value is returned, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `allow_assign_grafana_admin` | No | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `groups_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for user group lookup. Grafana will first evaluate the expression using the OAuth2 ID token. If no groups are found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a string array of groups. | | +| `allowed_groups` | No | Yes | List of comma- or space-separated groups. The user should be a member of at least one group to log in. If you configure `allowed_groups`, you must also configure `groups_attribute_path`. | | +| `allowed_organizations` | No | Yes | List of comma- or space-separated organizations. The user should be a member of at least one organization to log in. | | +| `allowed_domains` | No | Yes | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | | +| `team_ids` | No | Yes | String list of team IDs. If set, the user must be a member of one of the given teams to log in. If you configure `team_ids`, you must also configure `teams_url` and `team_ids_attribute_path`. | | +| `team_ids_attribute_path` | No | Yes | The [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana team ID lookup within the results returned by the `teams_url` endpoint. | | +| `teams_url` | No | Yes | The URL used to query for team IDs. If not set, the default value is `/teams`. If you configure `teams_url`, you must also configure `team_ids_attribute_path`. | | +| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | +| `tls_client_cert` | No | No | The path to the certificate. | | +| `tls_client_key` | No | No | The path to the key. | | +| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | | +| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `false` | +| `use_refresh_token` | No | Yes | Set to `true` to use refresh token and check access token expiration. | `false` | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | -## Examples of setting up generic OAuth2 +## Examples of setting up Generic OAuth -This section includes examples of setting up generic OAuth2 integration. +This section includes examples of setting up Generic OAuth integration. ### Set up OAuth2 with Descope -To set up generic OAuth2 authentication with Descope, follow these steps: +To set up Generic OAuth authentication with Descope, follow these steps: 1. Create a Descope Project [here](https://app.descope.com/gettingStarted), and go through the Getting Started Wizard to configure your authentication. You can skip step if you already have Descope project set up. @@ -449,7 +450,7 @@ To set up generic OAuth2 authentication with Descope, follow these steps: ### Set up OAuth2 with Auth0 -To set up generic OAuth2 authentication with Auth0, follow these steps: +To set up Generic OAuth authentication with Auth0, follow these steps: 1. Create an Auth0 application using the following parameters: @@ -482,7 +483,7 @@ To set up generic OAuth2 authentication with Auth0, follow these steps: ### Set up OAuth2 with Bitbucket -To set up generic OAuth2 authentication with Bitbucket, follow these steps: +To set up Generic OAuth authentication with Bitbucket, follow these steps: 1. Navigate to **Settings > Workspace setting > OAuth consumers** in BitBucket. @@ -517,7 +518,7 @@ By default, a refresh token is included in the response for the **Authorization ### Set up OAuth2 with OneLogin -To set up generic OAuth2 authentication with OneLogin, follow these steps: +To set up Generic OAuth authentication with OneLogin, follow these steps: 1. Create a new Custom Connector in OneLogin with the following settings: @@ -553,7 +554,7 @@ To set up generic OAuth2 authentication with OneLogin, follow these steps: ### Set up OAuth2 with Dex -To set up generic OAuth2 authentication with [Dex IdP](https://dexidp.io/), follow these +To set up Generic OAuth authentication with [Dex IdP](https://dexidp.io/), follow these steps: 1. Add Grafana as a client in the Dex config YAML file: diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md index 4c820d9bbe1..3678572e0eb 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/github/ -description: Configure GitHub OAuth2 authentication +description: Configure GitHub OAuth authentication keywords: - grafana - configuration @@ -12,16 +12,16 @@ labels: - cloud - enterprise - oss -menuTitle: GitHub OAuth2 -title: Configure GitHub OAuth2 authentication +menuTitle: GitHub OAuth +title: Configure GitHub OAuth authentication weight: 900 --- -# Configure GitHub OAuth2 authentication +# Configure GitHub OAuth authentication {{< docs/shared lookup="auth/intro.md" source="grafana" version="" >}} -This topic describes how to configure GitHub OAuth2 authentication. +This topic describes how to configure GitHub OAuth authentication. {{% admonition type="note" %}} If Users use the same email address in GitHub that they use with other authentication providers (such as Grafana.com), you need to do additional configuration to ensure that the users are matched correctly. Please refer to the [Using the same email address to login with different identity providers]({{< relref "../../configure-authentication#using-the-same-email-address-to-login-with-different-identity-providers" >}}) documentation for more information. @@ -31,13 +31,23 @@ If Users use the same email address in GitHub that they use with other authentic Ensure you know how to create a GitHub OAuth app. Consult GitHub's documentation on [creating an OAuth app](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) for more information. +### Create a GitHub OAuth App + +1. Log in to your GitHub account. + In **Profile > Settings > Developer settings**, select **OAuth Apps**. +1. Click **New OAuth App**. +1. Fill out the fields, using your Grafana homepage URL when appropriate. + In the **Authorization callback URL** field, enter the following: `https:///login/github` . +1. Note your client ID. +1. Generate, then note, your client secret. + ## Configure GitHub authentication client using the Grafana UI {{% admonition type="note" %}} Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure GitHub OAuth2 client from within Grafana using the GitHub UI. To do this, navigate to **Administration > Authentication > GitHub** page and fill in the form. If you have a current configuration in the Grafana configuration file, the form will be pre-populated with those values. Otherwise the form will contain default values. +As a Grafana Admin, you can configure GitHub OAuth client from within Grafana using the GitHub UI. To do this, navigate to **Administration > Authentication > GitHub** page and fill in the form. If you have a current configuration in the Grafana configuration file, the form will be pre-populated with those values. Otherwise the form will contain default values. After you have filled in the form, click **Save** . If the save was successful, Grafana will apply the new configurations. @@ -215,30 +225,31 @@ To learn more about Team Sync, refer to [Configure team sync]({{< relref "../../ ## Configuration options -The table below describes all GitHub OAuth configuration options. Like any other Grafana configuration, you can apply these options as environment variables. +The table below describes all GitHub OAuth configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). -| Setting | Required | Description | Default | -| ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -| `enabled` | No | Whether GitHub OAuth authentication is allowed. | `false` | -| `name` | No | Name used to refer to the GitHub authentication in the Grafana user interface. | `GitHub` | -| `icon` | No | Icon used for GitHub authentication in the Grafana user interface. | `github` | -| `client_id` | Yes | Client ID provided by your GitHub OAuth app. | | -| `client_secret` | Yes | Client secret provided by your GitHub OAuth app. | | -| `auth_url` | Yes | Authorization endpoint of your GitHub OAuth provider. | `https://github.com/login/oauth/authorize` | -| `token_url` | Yes | Endpoint used to obtain GitHub OAuth access token. | `https://github.com/login/oauth/access_token` | -| `api_url` | Yes | Endpoint used to obtain GitHub user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://api.github.com/user` | -| `scopes` | No | List of comma- or space-separated GitHub OAuth scopes. | `user:email,read:org` | -| `allow_sign_up` | No | Whether to allow new Grafana user creation through GitHub login. If set to `false`, then only existing Grafana users can log in with GitHub OAuth. | `true` | -| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | -| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the user information obtained from the UserInfo endpoint. If no role is found, Grafana creates a JSON data with `groups` key that maps to GitHub teams obtained from GitHub's [`/api/user/teams`](https://docs.github.com/en/rest/teams/teams#list-teams-for-the-authenticated-user) endpoint, and evaluates the expression using this data. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | | -| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | `false` | -| `org_mapping` | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `allow_assign_grafana_admin` | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. | `false` | -| `allowed_organizations` | No | List of comma- or space-separated organizations. User must be a member of at least one organization to log in. | | -| `allowed_domains` | No | List of comma- or space-separated domains. User must belong to at least one domain to log in. | | -| `team_ids` | No | Integer list of team IDs. If set, user has to be a member of one of the given teams to log in. | | -| `tls_skip_verify_insecure` | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | -| `tls_client_cert` | No | The path to the certificate. | | -| `tls_client_key` | No | The path to the key. | | -| `tls_client_ca` | No | The path to the trusted certificate authority list. | | +| Setting | Required | Supported on Cloud | Description | Default | +| ---------------------------- | -------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| `enabled` | No | Yes | Whether GitHub OAuth authentication is allowed. | `false` | +| `name` | No | Yes | Name used to refer to the GitHub authentication in the Grafana user interface. | `GitHub` | +| `icon` | No | Yes | Icon used for GitHub authentication in the Grafana user interface. | `github` | +| `client_id` | Yes | Yes | Client ID provided by your GitHub OAuth app. | | +| `client_secret` | Yes | Yes | Client secret provided by your GitHub OAuth app. | | +| `auth_url` | Yes | Yes | Authorization endpoint of your GitHub OAuth provider. | `https://github.com/login/oauth/authorize` | +| `token_url` | Yes | Yes | Endpoint used to obtain GitHub OAuth access token. | `https://github.com/login/oauth/access_token` | +| `api_url` | Yes | Yes | Endpoint used to obtain GitHub user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://api.github.com/user` | +| `scopes` | No | Yes | List of comma- or space-separated GitHub OAuth scopes. | `user:email,read:org` | +| `allow_sign_up` | No | Yes | Whether to allow new Grafana user creation through GitHub login. If set to `false`, then only existing Grafana users can log in with GitHub OAuth. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the user information obtained from the UserInfo endpoint. If no role is found, Grafana creates a JSON data with `groups` key that maps to GitHub teams obtained from GitHub's [`/api/user/teams`](https://docs.github.com/en/rest/teams/teams#list-teams-for-the-authenticated-user) endpoint, and evaluates the expression using this data. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping](#org-roles-mapping-example). | `false` | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. | `false` | +| `allow_assign_grafana_admin` | No | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `allowed_organizations` | No | Yes | List of comma- or space-separated organizations. User must be a member of at least one organization to log in. | | +| `allowed_domains` | No | Yes | List of comma- or space-separated domains. User must belong to at least one domain to log in. | | +| `team_ids` | No | Yes | Integer list of team IDs. If set, user has to be a member of one of the given teams to log in. | | +| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | +| `tls_client_cert` | No | No | The path to the certificate. | | +| `tls_client_key` | No | No | The path to the key. | | +| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md index be61bbb796c..ae52cfef512 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/gitlab/ -description: Grafana OAuthentication Guide +description: Grafana GitLab OAuth Guide keywords: - grafana - configuration @@ -12,16 +12,16 @@ labels: - cloud - enterprise - oss -menuTitle: GitLab OAuth2 -title: Configure GitLab OAuth2 authentication +menuTitle: GitLab OAuth +title: Configure GitLab OAuth authentication weight: 1000 --- -# Configure GitLab OAuth2 authentication +# Configure GitLab OAuth authentication {{< docs/shared lookup="auth/intro.md" source="grafana" version="" >}} -This topic describes how to configure GitLab OAuth2 authentication. +This topic describes how to configure GitLab OAuth authentication. {{% admonition type="note" %}} If Users use the same email address in GitLab that they use with other authentication providers (such as Grafana.com), you need to do additional configuration to ensure that the users are matched correctly. Please refer to the [Using the same email address to login with different identity providers]({{< relref "../../configure-authentication#using-the-same-email-address-to-login-with-different-identity-providers" >}}) documentation for more information. @@ -31,13 +31,23 @@ If Users use the same email address in GitLab that they use with other authentic Ensure you know how to create a GitLab OAuth application. Consult GitLab's documentation on [creating a GitLab OAuth application](https://docs.gitlab.com/ee/integration/oauth_provider.html) for more information. +### Create a GitLab OAuth Application + +1. Log in to your GitLab account and go to **Profile > Preferences > Applications**. +1. Click **Add new application**. +1. Fill out the fields. + - In the **Redirect URI** field, enter the following: `https:///login/gitlab` and check `openid`, `email`, `profile` in the **Scopes** list. + - Leave the **Confidential** checkbox checked. +1. Click **Save application**. +1. Note your **Application ID** (this is the `Client Id`) and **Secret** (this is the `Client Secret`). + ## Configure GitLab authentication client using the Grafana UI {{% admonition type="note" %}} Available in Public Preview in Grafana 10.4 behind the `ssoSettingsApi` feature toggle. {{% /admonition %}} -As a Grafana Admin, you can configure GitLab OAuth2 client from within Grafana using the GitLab UI. To do this, navigate to **Administration > Authentication > GitLab** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. +As a Grafana Admin, you can configure GitLab OAuth client from within Grafana using the GitLab UI. To do this, navigate to **Administration > Authentication > GitLab** page and fill in the form. If you have a current configuration in the Grafana configuration file then the form will be pre-populated with those values otherwise the form will contain default values. After you have filled in the form, click **Save** to save the configuration. If the save was successful, Grafana will apply the new configurations. @@ -97,10 +107,10 @@ To configure GitLab authentication with Grafana, follow these steps: 1. Refer to the following table to update field values located in the `[auth.gitlab]` section of the Grafana configuration file: - | Field | Description | - | ---------------------------- | -------------------------------------------------------------------------------------------- | - | `client_id`, `client_secret` | These values must match the client ID and client secret from your GitLab OAuth2 application. | - | `enabled` | Enables GitLab authentication. Set this value to `true`. | + | Field | Description | + | ---------------------------- | --------------------------------------------------------------------------------------------- | + | `client_id`, `client_secret` | These values must match the `Application ID` and `Secret` from your GitLab OAuth application. | + | `enabled` | Enables GitLab authentication. Set this value to `true`. | Review the list of other GitLab [configuration options]({{< relref "#configuration-options" >}}) and complete them, as necessary. @@ -234,7 +244,9 @@ use_refresh_token = true ## Configure team synchronization -> **Note:** Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and [Grafana Cloud](/docs/grafana-cloud/). +{{% admonition type="note" %}} +Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}) and [Grafana Cloud](/docs/grafana-cloud/). +{{% /admonition %}} By using Team Sync, you can map GitLab groups to teams within Grafana. This will automatically assign users to the appropriate teams. Teams for each user are synchronized when the user logs in. @@ -247,31 +259,32 @@ To learn more about Team Sync, refer to [Configure team sync]({{< relref "../../ ## Configuration options -The table below describes all GitLab OAuth configuration options. Like any other Grafana configuration, you can apply these options as environment variables. +The table below describes all GitLab OAuth configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). -| Setting | Required | Description | Default | -| ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | -| `enabled` | Yes | Whether GitLab OAuth authentication is allowed. | `false` | -| `client_id` | Yes | Client ID provided by your GitLab OAuth app. | | -| `client_secret` | Yes | Client secret provided by your GitLab OAuth app. | | -| `auth_url` | Yes | Authorization endpoint of your GitLab OAuth provider. If you use your own instance of GitLab instead of gitlab.com, adjust `auth_url` by replacing the `gitlab.com` hostname with your own. | `https://gitlab.com/oauth/authorize` | -| `token_url` | Yes | Endpoint used to obtain GitLab OAuth access token. If you use your own instance of GitLab instead of gitlab.com, adjust `token_url` by replacing the `gitlab.com` hostname with your own. | `https://gitlab.com/oauth/token` | -| `api_url` | No | Grafana uses `/user` endpoint to obtain GitLab user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://gitlab.com/api/v4` | -| `name` | No | Name used to refer to the GitLab authentication in the Grafana user interface. | `GitLab` | -| `icon` | No | Icon used for GitLab authentication in the Grafana user interface. | `gitlab` | -| `scopes` | No | List of comma or space-separated GitLab OAuth scopes. | `openid email profile` | -| `allow_sign_up` | No | Whether to allow new Grafana user creation through GitLab login. If set to `false`, then only existing Grafana users can log in with GitLab OAuth. | `true` | -| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | -| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the GitLab OAuth token. If no role is found, Grafana creates a JSON data with `groups` key that maps to groups obtained from GitLab's `/oauth/userinfo` endpoint, and evaluates the expression using this data. Finally, if a valid role is still not found, the expression is evaluated against the user information retrieved from `api_url/users` endpoint and groups retrieved from `api_url/groups` endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | -| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana role cannot be extracted using `role_attribute_path`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `org_mapping` | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `allow_assign_grafana_admin` | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. | `false` | -| `allowed_domains` | No | List of comma or space-separated domains. User must belong to at least one domain to log in. | | -| `allowed_groups` | No | List of comma or space-separated groups. The user should be a member of at least one group to log in. | | -| `tls_skip_verify_insecure` | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | -| `tls_client_cert` | No | The path to the certificate. | | -| `tls_client_key` | No | The path to the key. | | -| `tls_client_ca` | No | The path to the trusted certificate authority list. | | -| `use_pkce` | No | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | -| `use_refresh_token` | No | Set to `true` to use refresh token and check access token expiration. The `accessTokenExpirationCheck` feature toggle should also be enabled to use refresh token. | `true` | +| Setting | Required | Supported on Cloud | Description | Default | +| ---------------------------- | -------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | +| `enabled` | Yes | Yes | Whether GitLab OAuth authentication is allowed. | `false` | +| `client_id` | Yes | Yes | Client ID provided by your GitLab OAuth app. | | +| `client_secret` | Yes | Yes | Client secret provided by your GitLab OAuth app. | | +| `auth_url` | Yes | Yes | Authorization endpoint of your GitLab OAuth provider. If you use your own instance of GitLab instead of gitlab.com, adjust `auth_url` by replacing the `gitlab.com` hostname with your own. | `https://gitlab.com/oauth/authorize` | +| `token_url` | Yes | Yes | Endpoint used to obtain GitLab OAuth access token. If you use your own instance of GitLab instead of gitlab.com, adjust `token_url` by replacing the `gitlab.com` hostname with your own. | `https://gitlab.com/oauth/token` | +| `api_url` | No | Yes | Grafana uses `/user` endpoint to obtain GitLab user information compatible with [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo). | `https://gitlab.com/api/v4` | +| `name` | No | Yes | Name used to refer to the GitLab authentication in the Grafana user interface. | `GitLab` | +| `icon` | No | Yes | Icon used for GitLab authentication in the Grafana user interface. | `gitlab` | +| `scopes` | No | Yes | List of comma or space-separated GitLab OAuth scopes. | `openid email profile` | +| `allow_sign_up` | No | Yes | Whether to allow new Grafana user creation through GitLab login. If set to `false`, then only existing Grafana users can log in with GitLab OAuth. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the GitLab OAuth token. If no role is found, Grafana creates a JSON data with `groups` key that maps to groups obtained from GitLab's `/oauth/userinfo` endpoint, and evaluates the expression using this data. Finally, if a valid role is still not found, the expression is evaluated against the user information retrieved from `api_url/users` endpoint and groups retrieved from `api_url/groups` endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana role cannot be extracted using `role_attribute_path`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. | `false` | +| `allow_assign_grafana_admin` | No | No | Set to `true` to enable automatic sync of the Grafana server administrator role. If this option is set to `true` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user the server administrator privileges and organization administrator role. If this option is set to `false` and the result of evaluating `role_attribute_path` for a user is `GrafanaAdmin`, Grafana grants the user only organization administrator role. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `allowed_domains` | No | Yes | List of comma or space-separated domains. User must belong to at least one domain to log in. | | +| `allowed_groups` | No | Yes | List of comma or space-separated groups. The user should be a member of at least one group to log in. | | +| `tls_skip_verify_insecure` | No | No | If set to `true`, the client accepts any certificate presented by the server and any host name in that certificate. _You should only use this for testing_, because this mode leaves SSL/TLS susceptible to man-in-the-middle attacks. | `false` | +| `tls_client_cert` | No | No | The path to the certificate. | | +| `tls_client_key` | No | No | The path to the key. | | +| `tls_client_ca` | No | No | The path to the trusted certificate authority list. | | +| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | +| `use_refresh_token` | No | Yes | Set to `true` to use refresh token and check access token expiration. The `accessTokenExpirationCheck` feature toggle should also be enabled to use refresh token. | `true` | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md index f80bd7a9703..fed9af7ebd9 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/google/ -description: Grafana OAuthentication Guide +description: Grafana Google OAuth Guide labels: products: - cloud diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md index ebf55c834dc..0759f30428c 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md @@ -1,7 +1,7 @@ --- aliases: - ../../../auth/keycloak/ -description: Keycloak Grafana OAuthentication Guide +description: Grafana Keycloak Guide keywords: - grafana - keycloak diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md index dba1580f535..6c3a621afeb 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md @@ -260,27 +260,28 @@ To learn more about Team Sync, refer to [Configure Team Sync]({{< relref "../../ ## Configuration options -The following table outlines the various Okta OIDC configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. +The following table outlines the various Okta OIDC configuration options. You can apply these options as environment variables, similar to any other configuration within Grafana. For more information, refer to [Override configuration with environment variables]({{< relref "../../../configure-grafana#override-configuration-with-environment-variables" >}}). -| Setting | Required | Description | Default | -| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------- | -| `enabled` | No | Enables Okta OIDC authentication. | `false` | -| `name` | No | Name that refers to the Okta OIDC authentication from the Grafana user interface. | `Okta` | -| `icon` | No | Icon used for the Okta OIDC authentication in the Grafana user interface. | `okta` | -| `client_id` | Yes | Client ID provided by your Okta OIDC app. | | -| `client_secret` | Yes | Client secret provided by your Okta OIDC app. | | -| `auth_url` | Yes | Authorization endpoint of your Okta OIDC provider. | | -| `token_url` | Yes | Endpoint used to obtain the Okta OIDC access token. | | -| `api_url` | Yes | Endpoint used to obtain user information. | | -| `scopes` | No | List of comma- or space-separated Okta OIDC scopes. | `openid profile email groups` | -| `allow_sign_up` | No | Controls Grafana user creation through the Okta OIDC login. Only existing Grafana users can log in with Okta OIDC if set to `false`. | `true` | -| `auto_login` | No | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | -| `role_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the Okta OIDC ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | -| `role_attribute_strict` | No | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | -| `org_attribute_path` | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `org_mapping` | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | -| `skip_org_role_sync` | No | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | -| `allowed_groups` | No | List of comma- or space-separated groups. The user should be a member of at least one group to log in. | | -| `allowed_domains` | No | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | | -| `use_pkce` | No | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | -| `use_refresh_token` | No | Set to `true` to use refresh token and check access token expiration. | `false` | +| Setting | Required | Supported on Cloud | Description | Default | +| ----------------------- | -------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------- | +| `enabled` | No | Yes | Enables Okta OIDC authentication. | `false` | +| `name` | No | Yes | Name that refers to the Okta OIDC authentication from the Grafana user interface. | `Okta` | +| `icon` | No | Yes | Icon used for the Okta OIDC authentication in the Grafana user interface. | `okta` | +| `client_id` | Yes | Yes | Client ID provided by your Okta OIDC app. | | +| `client_secret` | Yes | Yes | Client secret provided by your Okta OIDC app. | | +| `auth_url` | Yes | Yes | Authorization endpoint of your Okta OIDC provider. | | +| `token_url` | Yes | Yes | Endpoint used to obtain the Okta OIDC access token. | | +| `api_url` | Yes | Yes | Endpoint used to obtain user information. | | +| `scopes` | No | Yes | List of comma- or space-separated Okta OIDC scopes. | `openid profile email groups` | +| `allow_sign_up` | No | Yes | Controls Grafana user creation through the Okta OIDC login. Only existing Grafana users can log in with Okta OIDC if set to `false`. | `true` | +| `auto_login` | No | Yes | Set to `true` to enable users to bypass the login screen and automatically log in. This setting is ignored if you configure multiple auth providers to use auto-login. | `false` | +| `role_attribute_path` | No | Yes | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana role lookup. Grafana will first evaluate the expression using the Okta OIDC ID token. If no role is found, the expression will be evaluated using the user information obtained from the UserInfo endpoint. The result of the evaluation should be a valid Grafana role (`None`, `Viewer`, `Editor`, `Admin` or `GrafanaAdmin`). For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | | +| `role_attribute_strict` | No | Yes | Set to `true` to deny user login if the Grafana org role cannot be extracted using `role_attribute_path` or `org_mapping`. For more information on user role mapping, refer to [Configure role mapping]({{< relref "#configure-role-mapping" >}}). | `false` | +| `org_attribute_path` | No | No | [JMESPath](http://jmespath.org/examples.html) expression to use for Grafana org to role lookup. The result of the evaluation will be mapped to org roles based on `org_mapping`. For more information on org to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `org_mapping` | No | No | List of comma- or space-separated `::` mappings. Value can be `*` meaning "All users". Role is optional and can have the following values: `None`, `Viewer`, `Editor` or `Admin`. For more information on external organization to role mapping, refer to [Org roles mapping example](#org-roles-mapping-example). | | +| `skip_org_role_sync` | No | Yes | Set to `true` to stop automatically syncing user roles. This will allow you to set organization roles for your users from within Grafana manually. | `false` | +| `allowed_groups` | No | Yes | List of comma- or space-separated groups. The user should be a member of at least one group to log in. | | +| `allowed_domains` | No | Yes | List of comma- or space-separated domains. The user should belong to at least one domain to log in. | | +| `use_pkce` | No | Yes | Set to `true` to use [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636). Grafana uses the SHA256 based `S256` challenge method and a 128 bytes (base64url encoded) code verifier. | `true` | +| `use_refresh_token` | No | Yes | Set to `true` to use refresh token and check access token expiration. | `false` | +| `signout_redirect_url` | No | Yes | URL to redirect to after the user logs out. | | From 7a414a04a0d5099965dd4ce3708fcbb61fd338c6 Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Thu, 14 Nov 2024 09:55:15 +0000 Subject: [PATCH 06/70] Alerting: Fix permissions for timeintervals UI and improve display of contact points in notification policies (#96129) Co-authored-by: Gilles De Mey --- pkg/services/navtree/navtreeimpl/navtree.go | 2 + public/app/features/alerting/routes.tsx | 9 ++ .../alerting/unified/NotificationPolicies.tsx | 83 +++++++++---------- .../components/alert-groups/AlertGroup.tsx | 18 +--- .../contact-points/ContactPoint.tsx | 15 ++-- .../contact-points/ContactPoints.tsx | 5 ++ .../components/contact-points/permissions.ts | 2 +- .../components/contact-points/utils.ts | 6 +- .../components/mute-timings/permissions.ts | 11 +++ .../notification-policies/Policy.test.tsx | 28 ++----- .../notification-policies/Policy.tsx | 74 +++++------------ .../NotificationRouteDetailsModal.tsx | 7 +- .../alerting/unified/hooks/useAbilities.ts | 54 ++++++++---- .../components/AlertRuleListItem.tsx | 4 +- .../features/alerting/unified/utils/misc.ts | 12 +++ public/app/types/accessControl.ts | 4 + public/locales/en-US/grafana.json | 2 +- public/locales/pseudo-LOCALE/grafana.json | 2 +- 18 files changed, 172 insertions(+), 166 deletions(-) create mode 100644 public/app/features/alerting/unified/components/mute-timings/permissions.ts diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go index 2d64d9c6ade..448893bffd0 100644 --- a/pkg/services/navtree/navtreeimpl/navtree.go +++ b/pkg/services/navtree/navtreeimpl/navtree.go @@ -452,6 +452,8 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na if hasAccess(ac.EvalAny( ac.EvalPermission(ac.ActionAlertingNotificationsRead), ac.EvalPermission(ac.ActionAlertingNotificationsExternalRead), + ac.EvalPermission(ac.ActionAlertingNotificationsTimeIntervalsRead), + ac.EvalPermission(ac.ActionAlertingNotificationsTimeIntervalsWrite), )) { alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Notification policies", SubTitle: "Determine how alerts are routed to contact points", Id: "am-routes", Url: s.cfg.AppSubURL + "/alerting/routes", Icon: "sitemap"}) } diff --git a/public/app/features/alerting/routes.tsx b/public/app/features/alerting/routes.tsx index ef88f26d6c6..bb7bbe9c995 100644 --- a/public/app/features/alerting/routes.tsx +++ b/public/app/features/alerting/routes.tsx @@ -4,6 +4,10 @@ import { GrafanaRouteComponent, RouteDescriptor } from 'app/core/navigation/type import { AccessControlAction } from 'app/types'; import { PERMISSIONS_CONTACT_POINTS } from './unified/components/contact-points/permissions'; +import { + PERMISSIONS_TIME_INTERVALS_MODIFY, + PERMISSIONS_TIME_INTERVALS_READ, +} from './unified/components/mute-timings/permissions'; import { PERMISSIONS_TEMPLATES } from './unified/components/templates/permissions'; import { evaluateAccess } from './unified/utils/access-control'; @@ -33,6 +37,8 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] { roles: evaluateAccess([ AccessControlAction.AlertingNotificationsRead, AccessControlAction.AlertingNotificationsExternalRead, + ...PERMISSIONS_TIME_INTERVALS_READ, + ...PERMISSIONS_TIME_INTERVALS_MODIFY, ]), component: importAlertingComponent( () => import(/* webpackChunkName: "AlertAmRoutes" */ 'app/features/alerting/unified/NotificationPolicies') @@ -43,6 +49,7 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] { roles: evaluateAccess([ AccessControlAction.AlertingNotificationsWrite, AccessControlAction.AlertingNotificationsExternalWrite, + ...PERMISSIONS_TIME_INTERVALS_MODIFY, ]), component: importAlertingComponent( () => @@ -56,6 +63,8 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] { roles: evaluateAccess([ AccessControlAction.AlertingNotificationsWrite, AccessControlAction.AlertingNotificationsExternalWrite, + ...PERMISSIONS_TIME_INTERVALS_READ, + ...PERMISSIONS_TIME_INTERVALS_MODIFY, ]), component: importAlertingComponent( () => diff --git a/public/app/features/alerting/unified/NotificationPolicies.tsx b/public/app/features/alerting/unified/NotificationPolicies.tsx index beb1b908abe..2d9dbcd2ac6 100644 --- a/public/app/features/alerting/unified/NotificationPolicies.tsx +++ b/public/app/features/alerting/unified/NotificationPolicies.tsx @@ -242,54 +242,51 @@ const AmRoutes = () => { {isFetching && } - {haveError && ( - - {resultError.message || 'Unknown error.'} - - )} - {haveData && ( + {policyTreeTabActive && ( <> - {policyTreeTabActive && ( - <> - - {rootRoute && ( - - )} - {rootRoute && ( - - )} - - {addModal} - {editModal} - {deleteModal} - {alertInstancesModal} - + {haveError && ( + + {resultError.message || 'Unknown error.'} + )} - {muteTimingsTabActive && ( - + {haveData && ( + + {rootRoute && ( + + )} + {rootRoute && ( + + )} + )} + {addModal} + {editModal} + {deleteModal} + {alertInstancesModal} )} + {muteTimingsTabActive && ( + + )} ); diff --git a/public/app/features/alerting/unified/components/alert-groups/AlertGroup.tsx b/public/app/features/alerting/unified/components/alert-groups/AlertGroup.tsx index 7c5169f930b..17fc1ca5a01 100644 --- a/public/app/features/alerting/unified/components/alert-groups/AlertGroup.tsx +++ b/public/app/features/alerting/unified/components/alert-groups/AlertGroup.tsx @@ -3,9 +3,9 @@ import { useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2, Stack, TextLink } from '@grafana/ui'; -import { AlertmanagerGroup, AlertState } from 'app/plugins/datasource/alertmanager/types'; +import { AlertmanagerGroup } from 'app/plugins/datasource/alertmanager/types'; -import { createContactPointLink } from '../../utils/misc'; +import { createContactPointSearchLink } from '../../utils/misc'; import { AlertLabels } from '../AlertLabels'; import { CollapseToggle } from '../CollapseToggle'; import { MetaText } from '../MetaText'; @@ -44,7 +44,7 @@ export const AlertGroup = ({ alertManagerSourceName, group }: Props) => { Delivered to{' '} ({ flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-between', - padding: `${theme.spacing(1)} ${theme.spacing(1)} ${theme.spacing(1)} 0`, + padding: theme.spacing(1), backgroundColor: theme.colors.background.secondary, width: '100%', }), @@ -86,14 +86,4 @@ const getStyles = (theme: GrafanaTheme2) => ({ flexDirection: 'row', alignItems: 'center', }), - summary: css({}), - [AlertState.Active]: css({ - color: theme.colors.error.main, - }), - [AlertState.Suppressed]: css({ - color: theme.colors.primary.main, - }), - [AlertState.Unprocessed]: css({ - color: theme.colors.secondary.main, - }), }); diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx index 2c249b30171..c3a9c10aae2 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx @@ -47,13 +47,7 @@ export const ContactPoint = ({ contactPoint }: ContactPointProps) => { }) } /> - {receivers.length === 0 && ( -
- - No integrations configured - -
- )} + {showFullMetadata ? (
{receivers.map((receiver, index) => { @@ -176,6 +170,11 @@ export const ContactPointReceiverSummary = ({ receivers, limit }: ContactPointRe return ( + {integrationsShown.length === 0 && ( + + No integrations configured + + )} {integrationsShown.map(([type, receivers], index) => { const iconName = INTEGRATION_ICONS[type]; const receiverName = receiverTypeNames[type] ?? upperFirst(type); @@ -198,7 +197,7 @@ export const ContactPointReceiverSummary = ({ receivers, limit }: ContactPointRe {iconName && } {receiverName} - {receivers.length > 1 && receivers.length} + {receivers.length > 1 && ` (${receivers.length})`} {!isLastItem && '⋅'} diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx index 6366c421d9a..c33609b367f 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx @@ -243,6 +243,11 @@ const ContactPointsList = ({ contactPoints, search, pageSize = DEFAULT_PAGE_SIZE const searchResults = useContactPointsSearch(contactPoints, search); const { page, pageItems, numberOfPages, onPageChange } = usePagination(searchResults, 1, pageSize); + if (pageItems.length === 0) { + const emptyMessage = t('alerting.contact-points.no-contact-points-found', 'No contact points found'); + return ; + } + return ( <> {pageItems.map((contactPoint, index) => { diff --git a/public/app/features/alerting/unified/components/contact-points/permissions.ts b/public/app/features/alerting/unified/components/contact-points/permissions.ts index 30af528e6dc..48ba46a10c5 100644 --- a/public/app/features/alerting/unified/components/contact-points/permissions.ts +++ b/public/app/features/alerting/unified/components/contact-points/permissions.ts @@ -5,7 +5,7 @@ import { AccessControlAction } from 'app/types'; * * Any permission in this list will be checked for client side access to view Contact Points functionality. */ -const PERMISSIONS_CONTACT_POINTS_READ = [AccessControlAction.AlertingReceiversRead]; +export const PERMISSIONS_CONTACT_POINTS_READ = [AccessControlAction.AlertingReceiversRead]; /** * List of granular permissions that allow modifying contact points diff --git a/public/app/features/alerting/unified/components/contact-points/utils.ts b/public/app/features/alerting/unified/components/contact-points/utils.ts index 5f6deb9c0b0..c02bfabfb1a 100644 --- a/public/app/features/alerting/unified/components/contact-points/utils.ts +++ b/public/app/features/alerting/unified/components/contact-points/utils.ts @@ -129,7 +129,7 @@ export function enhanceContactPointsWithMetadata({ const receivers = extractReceivers(contactPoint); const statusForReceiver = status.find((status) => status.name === contactPoint.name); - const id = 'id' in contactPoint && contactPoint.id ? contactPoint.id : contactPoint.name; + const id = getContactPointIdentifier(contactPoint); return { ...contactPoint, @@ -157,6 +157,10 @@ export function enhanceContactPointsWithMetadata({ return enhanced.sort((a, b) => a.name.localeCompare(b.name)); } +function getContactPointIdentifier(contactPoint: Receiver): string { + return 'id' in contactPoint && contactPoint.id ? contactPoint.id : contactPoint.name; +} + export function isAutoGeneratedPolicy(route: Route) { const simplifiedRoutingToggleEnabled = config.featureToggles.alertingSimplifiedRouting ?? false; if (!simplifiedRoutingToggleEnabled) { diff --git a/public/app/features/alerting/unified/components/mute-timings/permissions.ts b/public/app/features/alerting/unified/components/mute-timings/permissions.ts new file mode 100644 index 00000000000..dc895c100c8 --- /dev/null +++ b/public/app/features/alerting/unified/components/mute-timings/permissions.ts @@ -0,0 +1,11 @@ +import { AccessControlAction } from 'app/types'; + +/** + * List of granular permissions that allow viewing contact points + */ +export const PERMISSIONS_TIME_INTERVALS_READ = [AccessControlAction.AlertingTimeIntervalsRead]; + +/** + * List of granular permissions that allow modifying time intervals + */ +export const PERMISSIONS_TIME_INTERVALS_MODIFY = [AccessControlAction.AlertingTimeIntervalsWrite]; diff --git a/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx b/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx index 2985c7e20a6..19afef0f8e0 100644 --- a/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx @@ -15,7 +15,7 @@ import { import { ReceiversState } from 'app/types/alerting'; import { useAlertmanagerAbilities } from '../../hooks/useAbilities'; -import { mockAlertGroup, mockAlertmanagerAlert, mockReceiversState } from '../../mocks'; +import { mockReceiversState } from '../../mocks'; import { AlertmanagerProvider } from '../../state/AlertmanagerContext'; import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; @@ -57,7 +57,8 @@ describe('Policy', () => { renderPolicy( { renderPolicy( { renderPolicy( { renderPolicy( { renderPolicy( { routes: [{ id: '1', object_matchers: [['foo', eq, 'bar']] }], }; - const matchingGroups: AlertmanagerGroup[] = [ - mockAlertGroup({ - labels: {}, - alerts: [mockAlertmanagerAlert({ labels: { foo: 'bar' } }), mockAlertmanagerAlert({ labels: { foo: 'bar' } })], - }), - mockAlertGroup({ - labels: {}, - alerts: [mockAlertmanagerAlert({ labels: { bar: 'baz' } })], - }), - ]; - renderPolicy( { renderPolicy( void; @@ -79,6 +77,7 @@ interface PolicyComponentProps { formatter?: MatcherFormatter ) => void; isAutoGenerated?: boolean; + isDefaultPolicy?: boolean; } const Policy = (props: PolicyComponentProps) => { @@ -87,10 +86,8 @@ const Policy = (props: PolicyComponentProps) => { contactPointsState, readOnly = false, provisioned = false, - alertGroups = [], alertManagerSourceName, currentRoute, - routeTree, inheritedProperties, routesMatchingFilters = { filtersApplied: false, @@ -102,12 +99,11 @@ const Policy = (props: PolicyComponentProps) => { onDeletePolicy, onShowAlertInstances, isAutoGenerated = false, + isDefaultPolicy = false, } = props; const styles = useStyles2(getStyles); - const isDefaultPolicy = currentRoute === routeTree; - const contactPoint = currentRoute.receiver; const continueMatching = currentRoute.continue ?? false; @@ -353,7 +349,6 @@ const Policy = (props: PolicyComponentProps) => { return ( { onDeletePolicy={onDeletePolicy} onShowAlertInstances={onShowAlertInstances} alertManagerSourceName={alertManagerSourceName} - alertGroups={alertGroups} routesMatchingFilters={routesMatchingFilters} matchingInstancesPreview={matchingInstancesPreview} isAutoGenerated={isThisChildAutoGenerated} @@ -844,7 +838,6 @@ interface ContactPointDetailsProps { receivers: Receiver[]; } -// @TODO make this work for cloud AMs too const ContactPointsHoverDetails: FC = ({ alertManagerSourceName, contactPoint, @@ -852,67 +845,40 @@ const ContactPointsHoverDetails: FC = ({ }) => { const details = receivers.find((receiver) => receiver.name === contactPoint); if (!details) { + // If we can't find details, then it's possible (likely) that the user doesn't have access to this + // contact point, so we don't try and link to it return ( - + {contactPoint} - + ); } const integrations = details.grafana_managed_receiver_configs; - if (!integrations) { - return ( - - {contactPoint} - - ); - } - const groupedIntegrations = groupBy(details.grafana_managed_receiver_configs, (config) => config.type); + const contactPointLink = + 'id' in details && details.id + ? createContactPointLink(details.id, alertManagerSourceName) + : createContactPointSearchLink(details.name, alertManagerSourceName); return ( -
- Contact Point -
{contactPoint} } key={uniqueId()} content={ - - {/* use "label" to indicate how many of that type we have in the contact point */} - {Object.entries(groupedIntegrations).map(([type, integrations]) => ( - + + + } > - + {contactPoint}
diff --git a/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx b/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx index 42e0bc67804..8d3dfdae77a 100644 --- a/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx @@ -9,7 +9,7 @@ import { AlertmanagerAction } from '../../../hooks/useAbilities'; import { AlertmanagerProvider } from '../../../state/AlertmanagerContext'; import { getAmMatcherFormatter } from '../../../utils/alertmanager'; import { MatcherFormatter } from '../../../utils/matchers'; -import { makeAMLink } from '../../../utils/misc'; +import { createContactPointSearchLink } from '../../../utils/misc'; import { Authorize } from '../../Authorize'; import { Matchers } from '../../notification-policies/Matchers'; @@ -98,10 +98,7 @@ export function NotificationRouteDetailsModal({ { hasConfigurationAPI, notificationsPermissions.create, // TODO: Move this into the permissions config and generalise that code to allow for an array of permissions - isGrafanaFlavoredAlertmanager ? AccessControlAction.AlertingReceiversCreate : null + ...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingReceiversCreate] : []) ), [AlertmanagerAction.ViewContactPoint]: toAbility( AlwaysSupported, notificationsPermissions.read, - isGrafanaFlavoredAlertmanager ? AccessControlAction.AlertingReceiversRead : null + ...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_CONTACT_POINTS_READ : []) ), [AlertmanagerAction.UpdateContactPoint]: toAbility( hasConfigurationAPI, notificationsPermissions.update, - isGrafanaFlavoredAlertmanager ? AccessControlAction.AlertingReceiversWrite : null + ...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingReceiversWrite] : []) ), [AlertmanagerAction.DeleteContactPoint]: toAbility( hasConfigurationAPI, notificationsPermissions.delete, - isGrafanaFlavoredAlertmanager ? AccessControlAction.AlertingReceiversWrite : null + ...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingReceiversWrite] : []) ), // At the time of writing, only Grafana flavored alertmanager supports exporting, // and if a user can view the contact point, then they can also export it @@ -254,17 +259,17 @@ export function useAllAlertmanagerAbilities(): Abilities { [AlertmanagerAction.CreateNotificationTemplate]: toAbility( hasConfigurationAPI, notificationsPermissions.create, - isGrafanaFlavoredAlertmanager ? AccessControlAction.AlertingTemplatesWrite : null + ...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingTemplatesWrite] : []) ), [AlertmanagerAction.ViewNotificationTemplate]: toAbility( AlwaysSupported, notificationsPermissions.read, - isGrafanaFlavoredAlertmanager ? AccessControlAction.AlertingTemplatesRead : null + ...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingTemplatesRead] : []) ), [AlertmanagerAction.UpdateNotificationTemplate]: toAbility( hasConfigurationAPI, notificationsPermissions.update, - isGrafanaFlavoredAlertmanager ? AccessControlAction.AlertingTemplatesWrite : null + ...(isGrafanaFlavoredAlertmanager ? [AccessControlAction.AlertingTemplatesWrite] : []) ), [AlertmanagerAction.DeleteNotificationTemplate]: toAbility(hasConfigurationAPI, notificationsPermissions.delete), // -- notification policies -- @@ -287,11 +292,27 @@ export function useAllAlertmanagerAbilities(): Abilities { [AlertmanagerAction.ViewSilence]: toAbility(AlwaysSupported, instancePermissions.read), [AlertmanagerAction.UpdateSilence]: toAbility(AlwaysSupported, instancePermissions.update), [AlertmanagerAction.PreviewSilencedInstances]: toAbility(AlwaysSupported, instancePermissions.read), - // -- mute timtings -- - [AlertmanagerAction.CreateMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.create), - [AlertmanagerAction.ViewMuteTiming]: toAbility(AlwaysSupported, notificationsPermissions.read), - [AlertmanagerAction.UpdateMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.update), - [AlertmanagerAction.DeleteMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.delete), + // -- mute timings -- + [AlertmanagerAction.CreateMuteTiming]: toAbility( + hasConfigurationAPI, + notificationsPermissions.create, + ...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_TIME_INTERVALS_MODIFY : []) + ), + [AlertmanagerAction.ViewMuteTiming]: toAbility( + AlwaysSupported, + notificationsPermissions.read, + ...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_TIME_INTERVALS_READ : []) + ), + [AlertmanagerAction.UpdateMuteTiming]: toAbility( + hasConfigurationAPI, + notificationsPermissions.update, + ...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_TIME_INTERVALS_MODIFY : []) + ), + [AlertmanagerAction.DeleteMuteTiming]: toAbility( + hasConfigurationAPI, + notificationsPermissions.delete, + ...(isGrafanaFlavoredAlertmanager ? PERMISSIONS_TIME_INTERVALS_MODIFY : []) + ), [AlertmanagerAction.ExportMuteTimings]: toAbility(isGrafanaFlavoredAlertmanager, notificationsPermissions.read), }; @@ -355,7 +376,8 @@ function useCanSilence(rule: CombinedRule): [boolean, boolean] { } // just a convenient function -const toAbility = (supported: boolean, ...actions: Array): Ability => [ - supported, - actions.some((action) => action && ctx.hasPermission(action)), -]; +const toAbility = ( + supported: boolean, + /** If user has any of these permissions, then they are allowed to perform the action */ + ...actions: AccessControlAction[] +): Ability => [supported, actions.some((action) => action && ctx.hasPermission(action))]; diff --git a/public/app/features/alerting/unified/rule-list/components/AlertRuleListItem.tsx b/public/app/features/alerting/unified/rule-list/components/AlertRuleListItem.tsx index 3e89460e872..741a62fbe35 100644 --- a/public/app/features/alerting/unified/rule-list/components/AlertRuleListItem.tsx +++ b/public/app/features/alerting/unified/rule-list/components/AlertRuleListItem.tsx @@ -15,7 +15,7 @@ import { ProvisioningBadge } from '../../components/Provisioning'; import { PluginOriginBadge } from '../../plugins/PluginOriginBadge'; import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; import { labelsSize } from '../../utils/labels'; -import { createContactPointLink } from '../../utils/misc'; +import { createContactPointSearchLink } from '../../utils/misc'; import { RulePluginOrigin } from '../../utils/rules'; import { ListItem } from './ListItem'; @@ -106,7 +106,7 @@ export const AlertRuleListItem = (props: AlertRuleListItemProps) => { Delivered to{' '} only firing notifications", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index bc936363a98..df94a7497f3 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -137,7 +137,6 @@ "export-all": "Ēχpőřŧ äľľ", "view": "Vįęŵ" }, - "contact-point": "Cőʼnŧäčŧ Pőįʼnŧ", "contact-points": { "create": "Cřęäŧę čőʼnŧäčŧ pőįʼnŧ", "custom-template-value": "Cūşŧőm ŧęmpľäŧę väľūę", @@ -155,6 +154,7 @@ }, "last-delivery-attempt": "Ŀäşŧ đęľįvęřy äŧŧęmpŧ", "last-delivery-failed": "Ŀäşŧ đęľįvęřy äŧŧęmpŧ ƒäįľęđ", + "no-contact-points-found": "Ńő čőʼnŧäčŧ pőįʼnŧş ƒőūʼnđ", "no-delivery-attempts": "Ńő đęľįvęřy äŧŧęmpŧş", "no-integrations": "Ńő įʼnŧęģřäŧįőʼnş čőʼnƒįģūřęđ", "only-firing": "Đęľįvęřįʼnģ <1>őʼnľy ƒįřįʼnģ ʼnőŧįƒįčäŧįőʼnş", From a8ab82c80fc6fcd87f041190831b3b97bbc06843 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:01:43 +0100 Subject: [PATCH 07/70] Combobox: Documentation (#96307) * Initial Combobobox docs * Add test docs * Add docs to props * Add ComboboxOption docs * Apply suggestions from code review Co-authored-by: Ashley Harrison --------- Co-authored-by: Ashley Harrison --- .betterer.results | 3 - .../src/components/Combobox/Combobox.mdx | 101 ++++++++++++++++++ .../components/Combobox/Combobox.story.tsx | 6 ++ .../src/components/Combobox/Combobox.tsx | 12 +++ 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 packages/grafana-ui/src/components/Combobox/Combobox.mdx diff --git a/.betterer.results b/.betterer.results index 5819e039e20..3d31f01d4ef 100644 --- a/.betterer.results +++ b/.betterer.results @@ -5720,9 +5720,6 @@ exports[`no undocumented stories`] = { "packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], - "packages/grafana-ui/src/components/Combobox/Combobox.story.tsx:5381": [ - [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] - ], "packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.mdx b/packages/grafana-ui/src/components/Combobox/Combobox.mdx new file mode 100644 index 00000000000..7c93072b57a --- /dev/null +++ b/packages/grafana-ui/src/components/Combobox/Combobox.mdx @@ -0,0 +1,101 @@ +import { Meta, Preview, ArgTypes } from '@storybook/blocks'; + +import { Combobox } from './Combobox'; + + + +## Usage + +**Do** + +- Use in inline query editors +- Use when you require async calls from a select input + +**Don't** + +- Use the async functionality, when all items are only loaded on the initial load +- Use when fewer than 4 items are needed, as a `RadioButtonGroup` may be more suitable (not for inline use cases) +- Use this component if you need custom option styling + +## ComboboxOption + +The `ComboboxOption` currently supports 3 properties: + +- `label` - The text that is visible in the menu. +- `value` (required) - The value that is selected. +- `description` - A longer description that describes the choice. + +If no `label` is given, `value` will be used as a display value. + +## Sizing + +The recommended way to set the width is by sizing the container element. This is so it may reflect a similar size as other inputs in the context. + +If that is not possible, the width can be set directly on the component, by setting a number, which is a multiple of `8px`. + +For inline usage, such as in query editors, it may be useful to size the input based on the content. Set `width="auto"` to achieve this. In this case, it is also recommended to set `maxWidth` and `minWidth`. + +## Async Usage + +The `options` prop can accept an async function: + +- When the menu opens, the `options` function is called with `''`, to load all options. +- When the user types, the `options` function is called with the current input value. + +Note: The calls are debounced. Old calls are invalidated when a new call is made. + +## Unit testing + +Writing unit tests with Combobox requires mocking the `getBoundingClientRect` method because of [the virtual list library](https://github.com/TanStack/virtual/issues/29#issuecomment-657519522) + +This code sets up the mocking before all tests: + +```js +beforeAll(() => { + const mockGetBoundingClientRect = jest.fn(() => ({ + width: 120, + height: 120, + top: 0, + left: 0, + bottom: 0, + right: 0, + })); + + Object.defineProperty(Element.prototype, 'getBoundingClientRect', { + value: mockGetBoundingClientRect, + }); +}); +``` + +### Selecting an option + +To select an option, you can use any `*ByRole` methods, as Combobox has proper roles for accessibility. + +#### Selecting option by clicking + +```js +render(); + +const input = screen.getByRole('combobox'); +await userEvent.click(input); + +const item = await screen.findByRole('option', { name: 'Option 1' }); +await userEvent.click(item); +expect(screen.getByDisplayValue('Option 1')).toBeInTheDocument(); +``` + +#### Selecting option by typing + +```js +render(); + +const input = screen.getByRole('combobox'); +await userEvent.type(input, 'Option 3'); +await userEvent.keyboard('{ArrowDown}{Enter}'); + +expect(screen.getByDisplayValue('Option 3')).toBeInTheDocument(); +``` + +## Props + + diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx index 967b7537b8d..1b63c36bec1 100644 --- a/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx +++ b/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx @@ -11,12 +11,18 @@ import { Field } from '../Forms/Field'; import { AsyncSelect, Select } from '../Select/Select'; import { Combobox, ComboboxOption } from './Combobox'; +import mdx from './Combobox.mdx'; type PropsAndCustomArgs = ComponentProps & { numberOfOptions: number }; const meta: Meta = { title: 'Forms/Combobox', component: Combobox, + parameters: { + docs: { + page: mdx, + }, + }, args: { loading: undefined, invalid: undefined, diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.tsx index eb8112ba6be..a0086614259 100644 --- a/packages/grafana-ui/src/components/Combobox/Combobox.tsx +++ b/packages/grafana-ui/src/components/Combobox/Combobox.tsx @@ -27,7 +27,13 @@ export type ComboboxOption = { // then the onChange handler emits ComboboxOption with the label as non-undefined. interface ComboboxBaseProps extends Omit { + /** + * An `X` appears in the UI, which clears the input and sets the value to `null`. Do not use if you have no `null` case. + */ isClearable?: boolean; + /** + * Allows the user to set a value which is not in the list of options. + */ createCustomValue?: boolean; options: Array> | ((inputValue: string) => Promise>>); onChange: (option: ComboboxOption | null) => void; @@ -45,7 +51,13 @@ interface ComboboxBaseProps type AutoSizeConditionals = | { width: 'auto'; + /** + * Needs to be set when width is 'auto' to prevent the input from shrinking too much + */ minWidth: number; + /** + * Recommended to set when width is 'auto' to prevent the input from growing too much. + */ maxWidth?: number; } | { From 14d868a791b1d15ce3fa2a80cc7b2c0587c9219b Mon Sep 17 00:00:00 2001 From: brendamuir <100768211+brendamuir@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:34:46 +0100 Subject: [PATCH 08/70] Alerting docs: adds cloud alert rule limits (#96441) --- .../alerting/alerting-rules/create-grafana-managed-rule.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md index 9933d75ed6f..4637db17de0 100644 --- a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md +++ b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md @@ -85,6 +85,9 @@ Multiple alert instances can be created as a result of one alert rule (also know {{% admonition type="note" %}} For Grafana Cloud Free Forever, you can create up to 100 free Grafana-managed alert rules with each alert rule having a maximum of 1000 alert instances. + +For all paid tiers (Cloud Pro and Advanced), there is a soft limit of 2000 alert rules and unlimited alert instances. To increase the limit, open a support ticket from the [Cloud portal](https://grafana.com/docs/grafana-cloud/account-management/support/). + {{% /admonition %}} Grafana-managed alert rules can only be edited or deleted by users with Edit permissions for the folder storing the rules. From 64047be78c2d67379975b7040c4e0a489568f5f9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:54:34 +0000 Subject: [PATCH 09/70] Update dependency knip to v5.37.0 (#96435) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8a1cc69e5cd..d5a21a2972e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21419,12 +21419,12 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^2.3.3": - version: 2.3.3 - resolution: "jiti@npm:2.3.3" +"jiti@npm:^2.4.0": + version: 2.4.0 + resolution: "jiti@npm:2.4.0" bin: jiti: lib/jiti-cli.mjs - checksum: 10/21d1e89d909101c702769537e75ad87b433f980bc6938a6f64a90d1fbe7cb1510a0d4b82d4020b3093b47cebff5568af5e39d883e26aa4413564ced43b8cfd84 + checksum: 10/10aa999a4f9bccc82b1dab9ebaf4484a8770450883c1bf7fafc07f8fca1e417fd8e7731e651337d1060c9e2ff3f97362dcdfd27e86d1f385db97f4adf7b5a21d languageName: node linkType: hard @@ -21891,18 +21891,18 @@ __metadata: linkType: hard "knip@npm:^5.10.0": - version: 5.33.3 - resolution: "knip@npm:5.33.3" + version: 5.37.0 + resolution: "knip@npm:5.37.0" dependencies: "@nodelib/fs.walk": "npm:1.2.8" "@snyk/github-codeowners": "npm:1.1.0" easy-table: "npm:1.2.0" enhanced-resolve: "npm:^5.17.1" fast-glob: "npm:^3.3.2" - jiti: "npm:^2.3.3" + jiti: "npm:^2.4.0" js-yaml: "npm:^4.1.0" minimist: "npm:^1.2.8" - picocolors: "npm:^1.0.0" + picocolors: "npm:^1.1.0" picomatch: "npm:^4.0.1" pretty-ms: "npm:^9.0.0" smol-toml: "npm:^1.3.0" @@ -21916,7 +21916,7 @@ __metadata: bin: knip: bin/knip.js knip-bun: bin/knip-bun.js - checksum: 10/d6227a43666ce9732fe6b797a0c348bc46f59603826766c2049f44682ce96c23f0fb85b659d243dd36f98098a83a4e500d1044f3114a32564a39cf92dce8e853 + checksum: 10/f3b789d22fe9711ee01eb7c87d28836d5d1616a5d050ad0ce8a4366ca50a10cdaf42b72078edaa2f9b80d248468b4f925c9f654174652e4aed6268c9bf68a7bf languageName: node linkType: hard From bd0ee50e7cc9b80d122925319ff2711c3a9ee3a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:55:17 +0000 Subject: [PATCH 10/70] Update dependency msw to v2.6.4 (#96436) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 109 +++++++++++++++++++++++---------------------------- 2 files changed, 51 insertions(+), 60 deletions(-) diff --git a/package.json b/package.json index 5a422a6f0be..aaf5b1ce72a 100644 --- a/package.json +++ b/package.json @@ -208,7 +208,7 @@ "knip": "^5.10.0", "lerna": "8.1.8", "mini-css-extract-plugin": "2.9.2", - "msw": "2.4.13", + "msw": "2.6.4", "mutationobserver-shim": "0.3.7", "ngtemplate-loader": "2.1.0", "node-notifier": "10.0.1", diff --git a/yarn.lock b/yarn.lock index d5a21a2972e..59c8a7cfdce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1712,12 +1712,12 @@ __metadata: languageName: node linkType: hard -"@bundled-es-modules/cookie@npm:^2.0.0": - version: 2.0.0 - resolution: "@bundled-es-modules/cookie@npm:2.0.0" +"@bundled-es-modules/cookie@npm:^2.0.1": + version: 2.0.1 + resolution: "@bundled-es-modules/cookie@npm:2.0.1" dependencies: - cookie: "npm:^0.5.0" - checksum: 10/c8ef02aa5d3f6c786cfa407e1c93b4af29c600eb09990973f47a7a49e4771c1bec37c8f8e567638bb9cbc41f4e38d065ff1d8eaf9bf91f0c3613a6d60bc82c8c + cookie: "npm:^0.7.2" + checksum: 10/0038a5e82c41bfcd722afedabeb6961a5f15747b3681d7f4b61e35eb1e33130039e10ee9250dc9c9e4d3915ce1aeee717c0fb92225111574f0a030411abc0987 languageName: node linkType: hard @@ -4403,49 +4403,48 @@ __metadata: languageName: node linkType: hard -"@inquirer/confirm@npm:^4.0.0": - version: 4.0.1 - resolution: "@inquirer/confirm@npm:4.0.1" +"@inquirer/confirm@npm:^5.0.0": + version: 5.0.2 + resolution: "@inquirer/confirm@npm:5.0.2" dependencies: - "@inquirer/core": "npm:^9.2.1" - "@inquirer/type": "npm:^2.0.0" - checksum: 10/9f80834fcba308b047f4953abee4ff3837693400402797bd160dee64c019a63d9d9d866bbbf03d56f4aae8013c27991a0d405765120fe49aea2a57103af53426 + "@inquirer/core": "npm:^10.1.0" + "@inquirer/type": "npm:^3.0.1" + peerDependencies: + "@types/node": ">=18" + checksum: 10/4e775b80b689adeb0b2852ed79b368ef23a82fe3d5f580a562f4af7cdf002a19e0ec1b3b95acc6d49427a72c0fcb5b6548e0cdcafe2f0d3f3d6a923e04aabd0c languageName: node linkType: hard -"@inquirer/core@npm:^9.2.1": - version: 9.2.1 - resolution: "@inquirer/core@npm:9.2.1" +"@inquirer/core@npm:^10.1.0": + version: 10.1.0 + resolution: "@inquirer/core@npm:10.1.0" dependencies: - "@inquirer/figures": "npm:^1.0.6" - "@inquirer/type": "npm:^2.0.0" - "@types/mute-stream": "npm:^0.0.4" - "@types/node": "npm:^22.5.5" - "@types/wrap-ansi": "npm:^3.0.0" + "@inquirer/figures": "npm:^1.0.8" + "@inquirer/type": "npm:^3.0.1" ansi-escapes: "npm:^4.3.2" cli-width: "npm:^4.1.0" - mute-stream: "npm:^1.0.0" + mute-stream: "npm:^2.0.0" signal-exit: "npm:^4.1.0" strip-ansi: "npm:^6.0.1" wrap-ansi: "npm:^6.2.0" yoctocolors-cjs: "npm:^2.1.2" - checksum: 10/bf35e46e70add8ffa9e9d4ae6b528ac660484afca082bca31af95ce8a145a2f8c8d0d07cc7a8627771452e68ade9849c9c9c450a004133ed10ac2d6730900452 + checksum: 10/5d097d0484c1b758f788b792d29395199bdc84af3e8cd4d9273e31de2c5202839b6edf299056956044ba7fb097c4cee7b5c0288e094a380c045082b044f9946e languageName: node linkType: hard -"@inquirer/figures@npm:^1.0.6": - version: 1.0.7 - resolution: "@inquirer/figures@npm:1.0.7" - checksum: 10/ce896860de9d822a7c2a212667bcfd0f04cf2ce86d9a2411cc9c077bb59cd61732cb5f72ac66e88d52912466eec433f005bf8a25efa658f41e1a32f3977080bd +"@inquirer/figures@npm:^1.0.8": + version: 1.0.8 + resolution: "@inquirer/figures@npm:1.0.8" + checksum: 10/0e5e4fbb15e799e818c598fcc3558ef076daf78662149711b046723fd6316381e95f7d5573d6ef0062095ad22c6ac98833033f0948df5c722932107a567fd9c3 languageName: node linkType: hard -"@inquirer/type@npm:^2.0.0": - version: 2.0.0 - resolution: "@inquirer/type@npm:2.0.0" - dependencies: - mute-stream: "npm:^1.0.0" - checksum: 10/e85f359866c28cce06272d2d51cc17788a5c9de9fda7f181c27775dd26821de0dacbc947b521cfe2009cd2965ec54696799035ef3a25a9a5794e47d8e8bdf794 +"@inquirer/type@npm:^3.0.1": + version: 3.0.1 + resolution: "@inquirer/type@npm:3.0.1" + peerDependencies: + "@types/node": ">=18" + checksum: 10/af412f1e7541d43554b02199ae71a2039a1bff5dc51ceefd87de9ece55b199682733b28810fb4b6cb3ed4a159af4cc4a26d4bb29c58dd127e7d9dbda0797d8e7 languageName: node linkType: hard @@ -10415,15 +10414,6 @@ __metadata: languageName: node linkType: hard -"@types/mute-stream@npm:^0.0.4": - version: 0.0.4 - resolution: "@types/mute-stream@npm:0.0.4" - dependencies: - "@types/node": "npm:*" - checksum: 10/af8d83ad7b68ea05d9357985daf81b6c9b73af4feacb2f5c2693c7fd3e13e5135ef1bd083ce8d5bdc8e97acd28563b61bb32dec4e4508a8067fcd31b8a098632 - languageName: node - linkType: hard - "@types/node-forge@npm:^1, @types/node-forge@npm:^1.3.0": version: 1.3.11 resolution: "@types/node-forge@npm:1.3.11" @@ -10433,7 +10423,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=13.7.0, @types/node@npm:^22.5.5": +"@types/node@npm:*, @types/node@npm:>=13.7.0": version: 22.7.8 resolution: "@types/node@npm:22.7.8" dependencies: @@ -10968,13 +10958,6 @@ __metadata: languageName: node linkType: hard -"@types/wrap-ansi@npm:^3.0.0": - version: 3.0.0 - resolution: "@types/wrap-ansi@npm:3.0.0" - checksum: 10/8aa644946ca4e859668c36b8e2bcf2ac4bdee59dac760414730ea57be8a93ae9166ebd40a088f2ab714843aaea2a2a67f0e6e6ec11cfc9c8701b2466ca1c4089 - languageName: node - linkType: hard - "@types/ws@npm:^8.5.10": version: 8.5.10 resolution: "@types/ws@npm:8.5.10" @@ -14149,10 +14132,10 @@ __metadata: languageName: node linkType: hard -"cookie@npm:^0.5.0": - version: 0.5.0 - resolution: "cookie@npm:0.5.0" - checksum: 10/aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380 +"cookie@npm:^0.7.2": + version: 0.7.2 + resolution: "cookie@npm:0.7.2" + checksum: 10/24b286c556420d4ba4e9bc09120c9d3db7d28ace2bd0f8ccee82422ce42322f73c8312441271e5eefafbead725980e5996cc02766dbb89a90ac7f5636ede608f languageName: node linkType: hard @@ -18919,7 +18902,7 @@ __metadata: moment-timezone: "npm:0.5.46" monaco-editor: "npm:0.34.1" moveable: "npm:0.53.0" - msw: "npm:2.4.13" + msw: "npm:2.6.4" mutationobserver-shim: "npm:0.3.7" nanoid: "npm:^5.0.4" ngtemplate-loader: "npm:2.1.0" @@ -23935,15 +23918,16 @@ __metadata: languageName: node linkType: hard -"msw@npm:2.4.13": - version: 2.4.13 - resolution: "msw@npm:2.4.13" +"msw@npm:2.6.4": + version: 2.6.4 + resolution: "msw@npm:2.6.4" dependencies: - "@bundled-es-modules/cookie": "npm:^2.0.0" + "@bundled-es-modules/cookie": "npm:^2.0.1" "@bundled-es-modules/statuses": "npm:^1.0.1" "@bundled-es-modules/tough-cookie": "npm:^0.1.6" - "@inquirer/confirm": "npm:^4.0.0" + "@inquirer/confirm": "npm:^5.0.0" "@mswjs/interceptors": "npm:^0.36.5" + "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/until": "npm:^2.1.0" "@types/cookie": "npm:^0.6.0" "@types/statuses": "npm:^2.0.4" @@ -23963,7 +23947,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 10/db48a8897f6ee97457c227aed47460d27a4add8200bd576d323edae32fcd863bb746c2b6fa2aba4f3c4956ab18c97f366dbf78a4dc122c7d442a5202757dab7a + checksum: 10/8420b389e4f223a1a21c2114fab6f4aa6a088de85f8c266f51dd9e44c4579cebb571a52f2acdda67a2849979d2694bf62286c9b5e4a79851c1bda166aa87abad languageName: node linkType: hard @@ -24013,6 +23997,13 @@ __metadata: languageName: node linkType: hard +"mute-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "mute-stream@npm:2.0.0" + checksum: 10/d2e4fd2f5aa342b89b98134a8d899d8ef9b0a6d69274c4af9df46faa2d97aeb1f2ce83d867880d6de63643c52386579b99139801e24e7526c3b9b0a6d1e18d6c + languageName: node + linkType: hard + "nan@npm:^2.14.0, nan@npm:^2.17.0, nan@npm:^2.18.0": version: 2.20.0 resolution: "nan@npm:2.20.0" From 1d4b336f26816de74a6aedc18208d3e63661251c Mon Sep 17 00:00:00 2001 From: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com> Date: Thu, 14 Nov 2024 06:28:41 -0500 Subject: [PATCH 11/70] Docs: update docs for singleTopNav (#95663) * updated kiosk behaviour * Removed TV mode and updated Normal mode * Updated kiosk mode and other small edits * Removed dashboard controls from playlist controls section and reformatted * Added playlist controls behaviour in kiosk mode * Updated Use dashboards image and descriptions --- .../create-manage-playlists/index.md | 41 +++++++++---------- .../dashboards/use-dashboards/index.md | 14 ++++--- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/docs/sources/dashboards/create-manage-playlists/index.md b/docs/sources/dashboards/create-manage-playlists/index.md index cae7a433784..20dba1c7cc6 100644 --- a/docs/sources/dashboards/create-manage-playlists/index.md +++ b/docs/sources/dashboards/create-manage-playlists/index.md @@ -24,7 +24,7 @@ A _playlist_ is a list of dashboards that are displayed in a sequence. You might Grafana automatically scales dashboards to any resolution, which makes them perfect for big screens. -You can access the Playlist feature from Grafana's side menu, in the Dashboards submenu. +You can access the **Playlist** feature from Grafana's side menu, in the Dashboards submenu. {{< admonition type="note" >}} You must have at least Editor role permissions to create and manage playlists. @@ -41,12 +41,12 @@ Use the information in this section to access playlists. Start and control the d ### Start a playlist -You can start a playlist in six different view modes. View modes determine how the menus and navigation bar appear on the dashboards as well as how panels are sized. +You can start a playlist in four different view modes. View modes determine how the menus and navigation bar appear on the dashboards as well as how panels are sized. 1. Click **Dashboards** in the main menu. 1. Click **Playlists**. 1. Find the desired playlist and click **Start playlist**. -1. In the dialog box that opens, select one of the [six playlist modes](#playlist-modes) available. +1. In the dialog box that opens, select one of the [four playlist modes](#playlist-modes) available. 1. Disable any dashboard controls that you don't want displayed while the list plays; these controls are enabled and visible by default. Select from: - **Time and refresh** @@ -55,31 +55,28 @@ You can start a playlist in six different view modes. View modes determine how t 1. Click **Start \**. -The playlist displays each dashboard for the time specified in the **Interval** field, set when creating or editing a playlist. After a playlist starts, you can [control](#control-a-playlist) it using the navbar at the top of your screen. +The playlist displays each dashboard for the time specified in the **Interval** field, set when creating or editing a playlist. After a playlist starts, you can [start or stop it](#control-a-playlist) it using the controls at the top of your screen. ### Playlist modes -| Mode | Description | -| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Normal mode |
  • The side menu remains visible.
  • The navbar and dashboard controls appear at the top of the screen.
| -| Normal mode (with auto fit panels) |
  • The side menu remains visible.
  • The navbar and dashboard controls appear at the top of the screen.
  • Dashboard panels automatically adjust to optimize space on screen.
| -| TV mode |
  • The side menu is hidden or removed.
  • The navbar and dashboard controls appear at the top of the screen.
  • Enabled automatically after one minute of user inactivity.
  • Enable it manually using the `d v` sequence shortcut, or by appending the parameter `?inactive` to the dashboard URL.
  • Disable it with any mouse movement or keyboard action.
| -| TV mode (with auto fit panels) |
  • The side menu is hidden or removed.
  • The navbar and dashboard controls appear at the top of the screen.
  • Dashboard panels automatically adjust to optimize space on screen.