diff --git a/.betterer.results b/.betterer.results
index 5819e039e20..a5f9a3f4d3c 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -5536,35 +5536,16 @@ exports[`better eslint`] = {
[0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
- "public/app/plugins/panel/xychart/ManualEditor.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"]
- ],
- "public/app/plugins/panel/xychart/XYChartPanel.tsx:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"]
- ],
- "public/app/plugins/panel/xychart/scatter.ts:5381": [
- [0, 0, 0, "Do not use any type assertions.", "0"],
- [0, 0, 0, "Do not use any type assertions.", "1"],
- [0, 0, 0, "Do not use any type assertions.", "2"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "3"],
- [0, 0, 0, "Do not use any type assertions.", "4"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "5"],
- [0, 0, 0, "Do not use any type assertions.", "6"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "7"],
- [0, 0, 0, "Do not use any type assertions.", "8"],
- [0, 0, 0, "Unexpected any. Specify a different type.", "9"],
- [0, 0, 0, "Do not use any type assertions.", "10"]
- ],
- "public/app/plugins/panel/xychart/v2/SeriesEditor.tsx:5381": [
+ "public/app/plugins/panel/xychart/SeriesEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"]
],
- "public/app/plugins/panel/xychart/v2/migrations.ts:5381": [
+ "public/app/plugins/panel/xychart/migrations.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
- "public/app/plugins/panel/xychart/v2/scatter.ts:5381": [
+ "public/app/plugins/panel/xychart/scatter.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
@@ -5720,9 +5701,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/.github/CODEOWNERS b/.github/CODEOWNERS
index 7b6434ed0c8..351bd787bdf 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -40,7 +40,7 @@
/docs/sources/alerting/ @brendamuir
/docs/sources/dashboards/ @imatwawana
-/docs/sources/explore/ @grafana/explore-squad @lwandz13
+/docs/sources/explore/ @grafana/observability-traces-and-profiling @lwandz13
/docs/sources/panels-visualizations/ @imatwawana
/docs/sources/release-notes/ @irenerl24 @GrafanaWriter
/docs/sources/upgrade-guide/ @imatwawana
@@ -123,7 +123,7 @@
/pkg/services/apikey/ @grafana/identity-squad
/pkg/services/cleanup/ @grafana/grafana-backend-group
/pkg/services/contexthandler/ @grafana/grafana-backend-group @grafana/grafana-app-platform-squad
-/pkg/services/correlations/ @grafana/explore-squad
+/pkg/services/correlations/ @grafana/dataviz-squad
/pkg/services/dashboardimport/ @grafana/grafana-backend-group
/pkg/services/dashboards/ @grafana/grafana-app-platform-squad
/pkg/services/dashboardversion/ @grafana/grafana-backend-group
@@ -141,7 +141,7 @@
/pkg/services/provisioning/ @grafana/grafana-search-and-storage
/pkg/services/provisioning/alerting/ @grafana/alerting-backend
/pkg/services/query/ @grafana/grafana-app-platform-squad
-/pkg/services/queryhistory/ @grafana/explore-squad
+/pkg/services/queryhistory/ @grafana/observability-traces-and-profiling
/pkg/services/quota/ @grafana/grafana-search-and-storage
/pkg/services/screenshot/ @grafana/grafana-backend-group
/pkg/services/search/ @grafana/grafana-search-and-storage
@@ -163,7 +163,7 @@
/pkg/tests/apis/ @grafana/grafana-app-platform-squad
/pkg/tests/apis/query @grafana/grafana-datasources-core-services
/pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend
-/pkg/tests/api/correlations/ @grafana/explore-squad
+/pkg/tests/api/correlations/ @grafana/dataviz-squad
/pkg/tsdb/grafanads/ @grafana/grafana-backend-group
/pkg/tsdb/opentsdb/ @grafana/partner-datasources
/pkg/util/ @grafana/grafana-backend-group
@@ -418,7 +418,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/core/components/OptionsUI/ @grafana/dashboards-squad @grafana/dataviz-squad
-/public/app/core/history/ @grafana/explore-squad
+/public/app/core/history/ @grafana/observability-traces-and-profiling
/public/app/features/admin/ @grafana/identity-access-team
# Temp owners until Enterprise team takes over
@@ -433,7 +433,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/visualization/data-hover/ @grafana/dataviz-squad
/public/app/features/commandPalette/ @grafana/grafana-frontend-platform
/public/app/features/connections/ @grafana/plugins-platform-frontend
-/public/app/features/correlations/ @grafana/explore-squad
+/public/app/features/correlations/ @grafana/dataviz-squad
/public/app/features/dashboard/ @grafana/dashboards-squad
/public/app/features/dashboard/components/TransformationsEditor/ @grafana/dataviz-squad
/public/app/features/dashboard-scene/ @grafana/dashboards-squad
@@ -441,7 +441,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/datasources/ @grafana/plugins-platform-frontend
/public/app/features/dimensions/ @grafana/dataviz-squad
/public/app/features/dataframe-import/ @grafana/dataviz-squad
-/public/app/features/explore/ @grafana/explore-squad
+/public/app/features/explore/ @grafana/observability-traces-and-profiling
/public/app/features/expressions/ @grafana/observability-metrics
/public/app/features/folders/ @grafana/grafana-frontend-platform
/public/app/features/inspector/ @grafana/dashboards-squad
@@ -457,7 +457,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/playlist/ @grafana/dashboards-squad
/public/app/features/plugins/ @grafana/plugins-platform-frontend
/public/app/features/profile/ @grafana/grafana-frontend-platform
-/public/app/features/query-library/ @grafana/explore-squad
+/public/app/features/query-library/ @grafana/grafana-frontend-platform
/public/app/features/runtime/ @ryantxu
/public/app/features/query/ @grafana/dashboards-squad
/public/app/features/sandbox/ @grafana/grafana-frontend-platform
@@ -473,6 +473,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/users/ @grafana/access-squad
/public/app/features/variables/ @grafana/dashboards-squad
/public/app/features/preferences/ @grafana/grafana-frontend-platform
+/public/app/features/bookmarks/ @grafana/grafana-frontend-platform
/public/app/plugins/panel/alertlist/ @grafana/alerting-frontend
/public/app/plugins/panel/annolist/ @grafana/grafana-frontend-platform
/public/app/plugins/panel/barchart/ @grafana/dataviz-squad
@@ -630,7 +631,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/pkg/services/rendering/ @grafana/sharing-squad
# SSE - Server Side Expressions
-/pkg/expr/ @grafana/observability-metrics
+/pkg/expr/ @grafana/grafana-datasources-core-services
# Cloud middleware
/grafana-mixin/ @grafana/grafana-backend-services-squad
diff --git a/conf/defaults.ini b/conf/defaults.ini
index 9b3184ebafa..4ee34159a96 100644
--- a/conf/defaults.ini
+++ b/conf/defaults.ini
@@ -626,6 +626,11 @@ id_response_header_namespaces = user api-key service-account
# This feature currently **only supports single-organization deployments**
managed_service_accounts_enabled = false
+#################################### Passwordless Auth ###########################
+[auth.passwordless]
+enabled = false
+code_expiration = 20m
+
#################################### SSO Settings ###########################
[sso_settings]
# interval for reloading the SSO Settings from the database
@@ -1756,7 +1761,8 @@ hide_angular_deprecation =
# Comma separated list of plugin ids for which environment variables should be forwarded. Used only when feature flag pluginsSkipHostEnvVars is enabled.
forward_host_env_vars =
# Comma separated list of plugin ids to install as part of the startup process.
-preinstall = grafana-lokiexplore-app
+# By default, the following plugins will be preinstalled: "grafana-lokiexplore-app"
+preinstall =
# Controls whether preinstall plugins asynchronously (in the background) or synchronously (blocking). Useful when preinstalled plugins are used with provisioning.
preinstall_async = true
# Disables preinstall feature. It has the same effect as setting preinstall to an empty list.
diff --git a/docs/sources/administration/plugin-management/index.md b/docs/sources/administration/plugin-management/index.md
index a1f8dee6532..9e051c06b70 100644
--- a/docs/sources/administration/plugin-management/index.md
+++ b/docs/sources/administration/plugin-management/index.md
@@ -103,53 +103,45 @@ To browse for available plugins:
### Install a plugin
-To install a plugin:
+The most common way to install a plugin is through the Grafana UI, but alternative methods are also available.
1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins.
1. Browse and find a plugin.
1. Click the plugin's logo.
1. Click **Install**.
-When the update is complete, you'll see a confirmation message that the installation was successful.
+There are also additional ways to install plugins depending on your setup.
-### Update a plugin
+#### Install a plugin using Grafana CLI
-To update a plugin:
+Grafana CLI allows you to install, upgrade, and manage your Grafana plugins using a command line. For more information about Grafana CLI plugin commands, refer to [Plugin commands]({{< relref "../../cli/#plugins-commands" >}}).
-1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins.
-1. Click the **Installed** filter to show only installed plugins.
-1. Click the plugin's logo.
-1. Click **Update**.
+#### Install a plugin from a ZIP file
-When the update is complete, you'll see a confirmation message that the update was successful.
+This method is typically used for plugins not available in the Plugin Catalog or in environments without internet access.
-### Uninstall a plugin
+Download the archive containing the plugin assets, and install it by extracting the archive into the plugin directory. For example:
-To uninstall a plugin:
+```bash
+unzip my-plugin-0.2.0.zip -d YOUR_PLUGIN_DIR/my-plugin
+```
-1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins.
-1. Click the plugin's logo.
-1. Click the **Installed** filter to show only installed plugins.
-1. Click **Uninstall**.
+The path to the plugin directory is defined in the configuration file. For more information, refer to [Configuration]({{< relref "../../setup-grafana/configure-grafana/#plugins" >}}).
-When the update is complete, you'll see a confirmation message that the uninstall was successful.
+#### Install a plugin in air-gapped environment
-## Install Grafana plugins
+Plugin installation usually requires an internet connection. You can check which endpoints are used during the installation on your instance and add them to your instance’s allowlist.
-Grafana supports data source, panel, and app plugins.
+If this is not possible you can go via installing a plugin using [Grafana CLI](#install-a-plugin-using-grafana-cli) or as a [ZIP file](#install-a-plugin-from-a-zip-file).
-1. In a web browser, navigate to the [Grafana plugin catalog](https://grafana.com/plugins) and find a plugin that you want to install.
-1. Click the plugin, and then click the **Installation** tab.
+You can fetch any plugin from Grafana.com API following the download link referenced in the API.
+Here is an example based on `grafana-lokiexplore-app` plugins.
-### Install plugin on Grafana Cloud
+1. Open `https://grafana.com/api/plugins/grafana-lokiexplore-app` and look for `links` section
+1. Find a `download` url which looks something like `https://grafana.com/api/plugins/grafana-lokiexplore-app/versions/1.0.2/download`
+1. Use this URL to download the plugin ZIP file, which you can then install as described above.
-On the **Installation tab**, in the **For** field, click the name of the Grafana instance on which you want to install the plugin.
-
-Grafana Cloud handles the plugin installation automatically.
-
-If you're logged in to Grafana Cloud when you add a plugin, log out and then log back in again to use the new plugin.
-
-### Install plugins using the Grafana Helm chart
+#### Install plugins using the Grafana Helm chart
With the Grafana Helm chart, add the plugins you want to install as a list using the `plugins` field in the your values file. For more information about the configuration, refer to [the Helm chart configuration reference](https://github.com/grafana/helm-charts/tree/main/charts/grafana#configuration).
@@ -162,21 +154,29 @@ plugins:
- redis-datasource
```
-### Install plugin on local Grafana
+When the update is complete, a confirmation message will indicate the installation was successful.
-Follow the instructions on the **Install** tab. You can either install the plugin with a Grafana CLI command or by downloading and uncompressing a zip file into the Grafana plugins directory. We recommend using Grafana CLI in most instances. The zip option is available if your Grafana server doesn't have access to the internet.
+### Update a plugin
-For more information about Grafana CLI plugin commands, refer to [Plugin commands]({{< relref "../../cli/#plugins-commands" >}}).
+To update a plugin:
-#### Install a packaged plugin
+1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins.
+1. Click the **Installed** filter to show only installed plugins.
+1. Click the plugin's logo.
+1. Click **Update**.
-After the user has downloaded the archive containing the plugin assets, they can install it by extracting the archive into their plugin directory. For example:
+When the update is complete, a confirmation message will indicate the installation was successful.
-```bash
-unzip my-plugin-0.2.0.zip -d YOUR_PLUGIN_DIR/my-plugin
-```
+### Uninstall a plugin
-The path to the plugin directory is defined in the configuration file. For more information, refer to [Configuration]({{< relref "../../setup-grafana/configure-grafana/#plugins" >}}).
+To uninstall a plugin:
+
+1. In Grafana, click **Administration > Plugins and data > Plugins** in the side navigation menu to view all plugins.
+1. Click the plugin's logo.
+1. Click the **Installed** filter to show only installed plugins.
+1. Click **Uninstall**.
+
+When the update is complete, a confirmation message will indicate the installation was successful.
## Plugin signatures
diff --git a/docs/sources/administration/provisioning/index.md b/docs/sources/administration/provisioning/index.md
index 188e29124a4..34f32dbbc4f 100644
--- a/docs/sources/administration/provisioning/index.md
+++ b/docs/sources/administration/provisioning/index.md
@@ -253,17 +253,17 @@ All of these settings are optional.
The _HTTP\*_ tag denotes data sources that communicate using the HTTP protocol, including all core data source plugins except MySQL, PostgreSQL, and MS SQL.
{{< /admonition >}}
-| Name | Type | Data source | Description |
-| ----------------- | ------ | ---------------------------------- | -------------------------------------------------------- |
-| tlsCACert | string | _HTTP\*_, MySQL, PostgreSQL | CA cert for out going requests |
-| tlsClientCert | string | _HTTP\*_, MySQL, PostgreSQL | TLS Client cert for outgoing requests |
-| tlsClientKey | string | _HTTP\*_, MySQL, PostgreSQL | TLS Client key for outgoing requests |
-| password | string | _HTTP\*_, MySQL, PostgreSQL, MSSQL | password |
-| basicAuthPassword | string | _HTTP\*_ | password for basic authentication |
-| accessKey | string | Cloudwatch | Access key for connecting to Cloudwatch |
-| secretKey | string | Cloudwatch | Secret key for connecting to Cloudwatch |
-| sigV4AccessKey | string | Elasticsearch and Prometheus | SigV4 access key. Required when using keys auth provider |
-| sigV4SecretKey | string | Elasticsearch and Prometheus | SigV4 secret key. Required when using keys auth provider |
+| Name | Type | Data source | Description |
+| ----------------- | ------ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| tlsCACert | string | _HTTP\*_, MySQL, PostgreSQL | CA cert for out going requests. You can point directly to your stored cert by using an environment variable following the `$__file{path/to/ca}` format. |
+| tlsClientCert | string | _HTTP\*_, MySQL, PostgreSQL | TLS Client cert for outgoing requests. You can point directly to your stored cert by using an environment variable following the `$__file{path/to/cert}` format. |
+| tlsClientKey | string | _HTTP\*_, MySQL, PostgreSQL | TLS Client key for outgoing requests. You can point directly to your stored key by using an environment variable following the `$__file{path/to/key}` format. |
+| password | string | _HTTP\*_, MySQL, PostgreSQL, MSSQL | password |
+| basicAuthPassword | string | _HTTP\*_ | password for basic authentication |
+| accessKey | string | Cloudwatch | Access key for connecting to Cloudwatch |
+| secretKey | string | Cloudwatch | Secret key for connecting to Cloudwatch |
+| sigV4AccessKey | string | Elasticsearch and Prometheus | SigV4 access key. Required when using keys auth provider |
+| sigV4SecretKey | string | Elasticsearch and Prometheus | SigV4 secret key. Required when using keys auth provider |
#### Custom HTTP headers for data sources
diff --git a/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md b/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md
index 9ede2a53bd0..9aab2bfca61 100644
--- a/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md
+++ b/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md
@@ -100,6 +100,8 @@ The following list contains role-based access control actions.
| `folders:delete` |
`folders:*`
`folders:uid:*`
| Delete one or more folders and their subfolders. |
| `folders:read` |
`folders:*`
`folders:uid:*`
| Read one or more folders and their subfolders. |
| `folders:write` |
`folders:*`
`folders:uid:*`
| Update one or more folders and their subfolders. |
+| `groupsync.mappings:read` | None | List group attribute sync mappings. To use this permission, enable the `groupAttributeSync` feature toggle. |
+| `groupsync.mappings:write` | None | List, create, update, and delete group attribute sync mappings. To use this permission, enable the `groupAttributeSync` feature toggle. |
| `ldap.config:reload` | None | Reload the LDAP configuration. |
| `ldap.status:read` | None | Verify the availability of the LDAP server or servers. |
| `ldap.user:read` | None | Read users via LDAP. |
diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md
index ccc283db650..ed4b64817e7 100644
--- a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md
+++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md
@@ -54,13 +54,13 @@ The following tables list permissions associated with basic and fixed roles.
## Basic role assignments
-| Basic role | UID | Associated fixed roles | Description |
-| ------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader` `fixed:roles:writer` `fixed:users:reader` `fixed:users:writer` `fixed:org.users:reader` `fixed:org.users:writer` `fixed:ldap:reader` `fixed:ldap:writer` `fixed:stats:reader` `fixed:settings:reader` `fixed:settings:writer` `fixed:provisioning:writer` `fixed:organization:reader` `fixed:organization:maintainer` `fixed:licensing:reader` `fixed:licensing:writer` `fixed:datasources.caching:reader` `fixed:datasources.caching:writer` `fixed:dashboards.insights:reader` `fixed:datasources.insights:reader` `fixed:plugins:maintainer` `fixed:authentication.config:writer` `fixed:library.panels:creator` `fixed:library.panels:reader` `fixed:library.panels:general.reader` `fixed:library.panels:writer` `fixed:library.panels:general.writer` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. |
-| Admin | `basic_admin` | `fixed:reports:reader` `fixed:reports:writer` `fixed:datasources:reader` `fixed:datasources:writer` `fixed:organization:writer` `fixed:datasources.permissions:reader` `fixed:datasources.permissions:writer` `fixed:teams:writer` `fixed:dashboards:reader` `fixed:dashboards:writer` `fixed:dashboards.permissions:reader` `fixed:dashboards.permissions:writer` `fixed:dashboards.public:writer` `fixed:folders:reader` `fixed:folders:writer` `fixed:folders.permissions:reader` `fixed:folders.permissions:writer` `fixed:alerting:writer` `fixed:apikeys:reader` `fixed:apikeys:writer` `fixed:alerting.provisioning.secrets:reader` `fixed:alerting.provisioning:writer` `fixed:datasources.caching:reader` `fixed:datasources.caching:writer` `fixed:dashboards.insights:reader` `fixed:datasources.insights:reader` `fixed:plugins:writer` `fixed:library.panels:creator` `fixed:library.panels:reader` `fixed:library.panels:general.reader` `fixed:library.panels:writer` `fixed:library.panels:general.writer` `fixed:alerting.provisioning.status:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
-| Editor | `basic_editor` | `fixed:datasources:explorer` `fixed:dashboards:creator` `fixed:folders:creator` `fixed:annotations:writer` `fixed:teams:creator` if the `editors_can_admin` configuration flag is enabled `fixed:alerting:writer` `fixed:dashboards.insights:reader` `fixed:datasources.insights:reader` `fixed:library.panels:creator` `fixed:library.panels:general.reader` `fixed:library.panels:general.writer` `fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
-| Viewer | `basic_viewer` | `fixed:datasources.id:reader` `fixed:organization:reader` `fixed:annotations:reader` `fixed:annotations.dashboard:writer` `fixed:alerting:reader` `fixed:plugins.app:reader` `fixed:dashboards.insights:reader` `fixed:datasources.insights:reader` `fixed:library.panels:general.reader` `fixed:datasources:explorer` if the `viewers_can_edit` configuration flag is enabled | Default [Viewer](ref:rbac-basic-roles) assignments. |
-| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
+| Basic role | UID | Associated fixed roles | Description |
+| ------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader` `fixed:roles:writer` `fixed:users:reader` `fixed:users:writer` `fixed:org.users:reader` `fixed:org.users:writer` `fixed:ldap:reader` `fixed:ldap:writer` `fixed:stats:reader` `fixed:settings:reader` `fixed:settings:writer` `fixed:provisioning:writer` `fixed:organization:reader` `fixed:organization:maintainer` `fixed:licensing:reader` `fixed:licensing:writer` `fixed:datasources.caching:reader` `fixed:datasources.caching:writer` `fixed:dashboards.insights:reader` `fixed:datasources.insights:reader` `fixed:plugins:maintainer` `fixed:authentication.config:writer` `fixed:library.panels:creator` `fixed:library.panels:reader` `fixed:library.panels:general.reader` `fixed:library.panels:writer` `fixed:library.panels:general.writer` `fixed:groupsync:writer` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. |
+| Admin | `basic_admin` | `fixed:reports:reader` `fixed:reports:writer` `fixed:datasources:reader` `fixed:datasources:writer` `fixed:organization:writer` `fixed:datasources.permissions:reader` `fixed:datasources.permissions:writer` `fixed:teams:writer` `fixed:dashboards:reader` `fixed:dashboards:writer` `fixed:dashboards.permissions:reader` `fixed:dashboards.permissions:writer` `fixed:dashboards.public:writer` `fixed:folders:reader` `fixed:folders:writer` `fixed:folders.permissions:reader` `fixed:folders.permissions:writer` `fixed:alerting:writer` `fixed:apikeys:reader` `fixed:apikeys:writer` `fixed:alerting.provisioning.secrets:reader` `fixed:alerting.provisioning:writer` `fixed:datasources.caching:reader` `fixed:datasources.caching:writer` `fixed:dashboards.insights:reader` `fixed:datasources.insights:reader` `fixed:plugins:writer` `fixed:library.panels:creator` `fixed:library.panels:reader` `fixed:library.panels:general.reader` `fixed:library.panels:writer` `fixed:library.panels:general.writer` `fixed:alerting.provisioning.status:writer` `fixed:groupsync:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
+| Editor | `basic_editor` | `fixed:datasources:explorer` `fixed:dashboards:creator` `fixed:folders:creator` `fixed:annotations:writer` `fixed:teams:creator` if the `editors_can_admin` configuration flag is enabled `fixed:alerting:writer` `fixed:dashboards.insights:reader` `fixed:datasources.insights:reader` `fixed:library.panels:creator` `fixed:library.panels:general.reader` `fixed:library.panels:general.writer` `fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
+| Viewer | `basic_viewer` | `fixed:datasources.id:reader` `fixed:organization:reader` `fixed:annotations:reader` `fixed:annotations.dashboard:writer` `fixed:alerting:reader` `fixed:plugins.app:reader` `fixed:dashboards.insights:reader` `fixed:datasources.insights:reader` `fixed:library.panels:general.reader` `fixed:datasources:explorer` if the `viewers_can_edit` configuration flag is enabled | Default [Viewer](ref:rbac-basic-roles) assignments. |
+| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
## Fixed role definitions
@@ -115,6 +115,8 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
| `fixed:folders.permissions:reader` | `fixed_E06l4cx0JFm47EeLBE4nmv3pnSo` | `folders.permissions:read` | Read all folder permissions. |
| `fixed:folders.permissions:writer` | `fixed_3GAgpQ_hWG8o7-lwNb86_VB37eI` | All permissions from `fixed:folders.permissions:reader` and `folders.permissions:write` | Read and update all folder permissions. |
| `fixed:ldap:reader` | `fixed_lMcOPwSkxKY-qCK8NMJc5k6izLE` | `ldap.user:read` `ldap.status:read` | Read the LDAP configuration and LDAP status information. |
+| `fixed:groupsync:reader` | `fixed_tLIbDrE6kw93sKqooF8GVS9BF4E` | `groupsync.mappings:read` | List all group attribute sync mappings. To use this role, enable the `groupAttributeSync` feature toggle. |
+| `fixed:groupsync:writer` | `fixed_q7XUYx_efzxxsVmWhQgpiYClwBs` | `groupsync.mappings:read` `groupsync.mappings:write` | Create, read, update, and delete all group attribute sync mappings. To use this role, enable the `groupAttributeSync` feature toggle. |
| `fixed:ldap:writer` | `fixed_p6AvnU4GCQyIh7-hbwI-bk3GYnU` | All permissions from `fixed:ldap:reader` and `ldap.user:sync` `ldap.config:reload` | Read and update the LDAP configuration, and read LDAP status information. |
| `fixed:library.panels:creator` | `fixed_6eX6ItfegCIY5zLmPqTDW8ZV7KY` | `library.panels:create` `folders:read` | Create library panel at the root level. |
| `fixed:library.panels:general.reader` | `fixed_ct0DghiBWR_2BiQm3EvNPDVmpio` | `library.panels:read` | Read all library panels at the root level. |
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.
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.
|
-| Kiosk mode |
The side menu is hidden or removed.
The navbar and dashboard controls appear at the top of the screen.
You can disable or enable it manually using the `d v` sequence shortcut after the playlist has started.
|
-| Kiosk mode (with auto fit panels) |
The side menu is hidden or removed.
The navbar and dashboard controls appear at the top of the screen.
You can disable or enable it manually using the `d v` sequence shortcut after the playlist has started.
Dashboard panels automatically adjust to optimize space on screen.
|
+
-### Control a playlist
+| Mode | Description |
+| ---------------------------------- | ------------------------------------------------------------------------------------ |
+| Normal mode |
The main menu and navbar remain visible.
Dashboard controls are hidden.
Playlist controls are displayed at the top of the screen.
|
+| Normal mode (with auto fit panels) |
The main menu and navbar remain visible.
Dashboard controls are hidden.
Playlist controls are displayed at the top of the screen.
Dashboard panels automatically adjust to optimize space on screen.
|
+| Kiosk mode |
The main menu, navbar, and dashboard controls are hidden.
You can disable the playlist manually by pressing the `Esc` key after the playlist has started. Doing so causes the playlist controls to be displayed at the top of the screen briefly.
|
+| Kiosk mode (with auto fit panels) |
<
The main menu, navbar, and dashboard controls are hidden.
You can disable the playlist manually by pressing the `Esc` key after the playlist has started. Doing so causes the playlist controls to be displayed at the top of the screen briefly.
Dashboard panels automatically adjust to optimize space on screen.
|
-You can control a playlist in **Normal** or **TV** mode after it's started, using the navigation bar at the top of your screen. Press the Esc key in your keyboard to stop the playlist.
+
-| Button | Result |
-| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
-| Next (double-right arrow) | Advances to the next dashboard. |
-| Back (left arrow) | Returns to the previous dashboard. |
-| Stop (square) | Ends the playlist, and exits to the current dashboard. |
-| Cycle view mode (monitor icon) | Rotates the display of the dashboards in different view modes. |
-| Time range | Displays data within a time range. It can be set to display the last 5 minutes up to 5 years ago, or a custom time range, using the down arrow. |
-| Refresh (circle arrow) | Reloads the dashboard, to display the current data. It can be set to reload automatically every 5 seconds to 1 day, using the drop-down arrow. |
+### Playlist controls
+
+You can control a playlist in **Normal** mode after it's started, using the buttons at the top of your screen. Press the `Esc` key to stop the playlist.
+
+- **Next (double-right arrow)** - Advances to the next dashboard.
+- **Back (doublt-left arrow)** - Returns to the previous dashboard.
+- **Stop playlist** - Ends the playlist, and exits to the current dashboard.
## Create a playlist
diff --git a/docs/sources/dashboards/create-reports/index.md b/docs/sources/dashboards/create-reports/index.md
index efdf394eedc..71efdd8d33f 100644
--- a/docs/sources/dashboards/create-reports/index.md
+++ b/docs/sources/dashboards/create-reports/index.md
@@ -328,6 +328,8 @@ font_regular = DejaVuSansCondensed.ttf
font_bold = DejaVuSansCondensed-Bold.ttf
# Name of the TrueType font file with italic style
font_italic = DejaVuSansCondensed-Oblique.ttf
+# Allowed domains to receive reports. Use * to allow all domains. Use a comma-separated list to allow multiple domains. Example: allowed_domains = grafana.com, example.org
+allowed_domains = *
```
## Report settings
@@ -338,11 +340,19 @@ You can configure organization-wide report settings in the **Settings** under **
You can customize the branding options.
-Report branding:
+### Attachment settings
+
+#### PDF
- **Company logo:** Company logo displayed in the report PDF. It can be configured by specifying a URL, or by uploading a file. The maximum file size is 16 MB. Defaults to the Grafana logo.
-Email branding:
+- **Theme:** Theme of the PDF attached to the report. Defaults to **Light**. The selected theme is also applied to the PDFs generated when you click **Preview PDF** during report creation or select the **Export as PDF** option on a dashboard. If **Current** is selected, the PDF in the report will be in the Admin's instance theme, but the preview and exported PDFs will be in the user's instance theme.
+
+#### Embedded Image
+
+- **Theme:** Theme of the dashboard image embedded in the email. Defaults to **Dark**.
+
+### Email branding
- **Company logo:** Company logo displayed in the report email. It can be configured by specifying a URL, or by uploading a file. The maximum file size is 16 MB. Defaults to the Grafana logo.
- **Email footer:** Toggle to enable the report email footer. Select **Sent by** or **None**.
diff --git a/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md b/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md
index 1c92c0cee32..711127d2db4 100644
--- a/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md
+++ b/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md
@@ -77,6 +77,8 @@ This feature was previously called **email sharing**.
Available in [Grafana Enterprise](ref:grafana-enterprise) and [Grafana Cloud](/docs/grafana-cloud).
{{< /admonition >}}
+{{< docs/private-preview product="Sharing externally with specific people" >}}
+
To share a dashboard with specific external users, you can send them a link by email. Use this option when you only want to share your dashboard with specific people. When you share dashboards by email, recipients receive a one-time use link that's valid for **one hour**. Once the link is used, the viewer has access to the shared dashboard for **30 days**.
When you share a dashboard with an email link, your organization is billed per user, regardless of how many dashboards are shared. Grafana bills monthly per user until access is revoked.
diff --git a/docs/sources/dashboards/use-dashboards/index.md b/docs/sources/dashboards/use-dashboards/index.md
index 75f4342ad0c..df3323b38f0 100644
--- a/docs/sources/dashboards/use-dashboards/index.md
+++ b/docs/sources/dashboards/use-dashboards/index.md
@@ -76,6 +76,11 @@ refs:
destination: /docs/grafana//dashboards/variables/add-template-variables/#add-ad-hoc-filters
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/visualizations/dashboards/variables/add-template-variables/#add-ad-hoc-filters
+ shared-dashboards:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//dashboards/share-dashboards-panels/shared-dashboards/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/visualizations/dashboards/share-dashboards-panels/shared-dashboards/
---
# Use dashboards
@@ -90,19 +95,18 @@ The dashboard user interface provides a number of features that you can use to c
The following image and descriptions highlight all dashboard features.
-
+
-1. **Grafana home** - Click **Home** in the breadcrumb to go to the home page configured in the Grafana instance.
1. **Dashboard folder** - When you click the dashboard folder name, you can search for other dashboards contained in the folder and perform other [folder management tasks](ref:dashboard-folders).
1. **Dashboard title** - You can create your own dashboard titles or have Grafana create them for you using [generative AI features](ref:generative-ai-features).
+1. **Kiosk mode** - Click to display the dashboard on a large screen such as a TV or a kiosk. Kiosk mode hides the main menu, navbar, and dashboard controls. Learn more about kiosk mode in our [How to Create Kiosks to Display Dashboards on a TV blog post](https://grafana.com/blog/2019/05/02/grafana-tutorial-how-to-create-kiosks-to-display-dashboards-on-a-tv/). Press `Esc` to leave kiosk mode.
1. **Mark as favorite** - Mark the dashboard as one of your favorites so it's included in your list of **Starred** dashboards in the main menu.
-1. **Public label** - When you share a dashboard externally, it's marked with the **Public** label.
+1. **Public label** - When you [share a dashboard externally](ref:shared-dashboards), it's marked with the **Public** label.
1. **Dashboard insights** - Click to view analytics about your dashboard including information about users, activity, query counts. Learn more about [dashboard analytics](ref:dashboard-analytics).
1. **Edit** - Click to leave view-only mode and enter edit mode, where you can make changes directly to the dashboard and access dashboard settings, as well as several panel editing functions.
1. **Export** - Access [dashboard exporting](ref:export-dashboards) options.
1. **Share dashboard** - Access several [dashboard sharing](ref:sharing) options.
-1. **Kiosk mode** - Click to display the dashboard on a large screen such as a TV or a kiosk. Kiosk mode hides elements such as navigation menus. Learn more about kiosk mode in our [How to Create Kiosks to Display Dashboards on a TV blog post](https://grafana.com/blog/2019/05/02/grafana-tutorial-how-to-create-kiosks-to-display-dashboards-on-a-tv/). Press `Enter` to leave kiosk mode.
-1. **Variables** - Use [variables](ref:variables) to create more interactive and dynamic dashboards.
+1. **Variables** - Use [variables](ref:variables), including ad hoc filters, to create more interactive and dynamic dashboards.
1. **Dashboard links** - Link to other dashboards, panels, and external websites. Learn more about [dashboard links](ref:dashboard-links).
1. **Current dashboard time range and time picker** - Click to select [relative time range](#relative-time-range) options and set custom [absolute time ranges](#absolute-time-range).
- You can change the **Timezone** and **Fiscal year** settings from the time range controls by clicking the **Change time settings** button.
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/" >}})
diff --git a/docs/sources/developers/http_api/serviceaccount.md b/docs/sources/developers/http_api/serviceaccount.md
index 15e424cf151..680c3472647 100644
--- a/docs/sources/developers/http_api/serviceaccount.md
+++ b/docs/sources/developers/http_api/serviceaccount.md
@@ -445,10 +445,13 @@ Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
{
- "name": "grafana"
+ "name": "grafana",
+ "secondsToLive": 604800
}
```
+Default value for the `secondsToLive` is 0, which means that the service account token will never expire.
+
**Example Response**:
```http
diff --git a/docs/sources/panels-visualizations/visualizations/canvas/index.md b/docs/sources/panels-visualizations/visualizations/canvas/index.md
index 5e4cdaab205..4dab56e9f61 100644
--- a/docs/sources/panels-visualizations/visualizations/canvas/index.md
+++ b/docs/sources/panels-visualizations/visualizations/canvas/index.md
@@ -91,7 +91,23 @@ The text element lets you easily add text to the canvas. The element also suppor
### Icon
-The icon element lets you add a supported icon to the canvas. Icons can have their color set based on thresholds / value mappings.
+The icon element lets you add a supported icon to the canvas. Icons can have their color set based on thresholds or value mappings.
+
+#### Add a custom icon
+
+You can add a custom icon by referencing an SVG file. To add a custom icon, follow these steps:
+
+1. Under **Icon > SVG Path**, if it's not already selected, select **Fixed** as your file source.
+1. Click **Select a value** in the field below.
+1. In the dialog box that opens, click the **URL** tab.
+1. Enter the URL in the field below the **URL** tab.
+
+ {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-canvas-custom-image-v11.3.png" max-width="300px" alt="Add a custom image URL" >}}
+
+1. Click **Select**.
+1. (Optional) Add a background image to your icon with the **Background (icon)** option by following the steps to [add a custom image](#add-custom-images-to-elements).
+
+If you don't have an SVG file, you can use a rectangle element instead of an icon and set its background image to an image file type. To add a custom image for another element type, follow the steps to [add a custom image](#add-custom-images-to-elements).
### Server
@@ -111,6 +127,25 @@ A button click will only trigger an API call when [inline editing](#inline-editi
{{< docs/play title="Canvas Visualization: Buttons" url="https://play.grafana.org/d/c9ea65f5-ed5a-45cf-8fb7-f82af7c3afdf/" >}}
+## Add custom images to elements
+
+You can add custom background images to all elements except **Button** by referencing an image URL.
+The image must be hosted at a URL that allows requests from your Grafana instance.
+
+To upload a custom image, follow these steps:
+
+1. Under **Background (\)**, if it's not already selected, select **Fixed** as your image source.
+
+ {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-canvas-custom-image-src-v11.3.png" max-width="300px" alt="Custom image source selection" >}}
+
+1. Click **Select a value** in the field below.
+1. In the dialog box that opens, click the **URL** tab.
+1. Enter the URL in the field below the **URL** tab.
+
+ {{< figure src="/media/docs/grafana/panels-visualizations/screenshot-canvas-custom-image-v11.3.png" max-width="300px" alt="Add a custom image URL" >}}
+
+1. Click **Select**.
+
## Connections
When building a canvas, you can connect elements together to create more complex visualizations. Connections are created by dragging from the connection anchor of one element to the connection anchor of another element. You can also create connections to the background of the canvas. Connection anchors are displayed when you hover over an element and inline editing is turned on.
diff --git a/docs/sources/panels-visualizations/visualizations/node-graph/index.md b/docs/sources/panels-visualizations/visualizations/node-graph/index.md
index a57b4304de2..f5860c717c0 100644
--- a/docs/sources/panels-visualizations/visualizations/node-graph/index.md
+++ b/docs/sources/panels-visualizations/visualizations/node-graph/index.md
@@ -83,6 +83,12 @@ If a node lacks edge connections, it’s displayed on its own outside of the net
{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="" >}}
+## Node graph options
+
+Use the following options to refine your node graph visualization.
+
+- **Zoom mode** - Choose how the node graph should handle zoom and scroll events.
+
## Nodes options
The **Nodes** options section provides configurations for node behaviors.
diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
index 61dee43370e..fab815be73a 100644
--- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
+++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
@@ -28,7 +28,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `publicDashboardsScene` | Enables public dashboard rendering using scenes | Yes |
| `featureHighlights` | Highlight Grafana Enterprise features | |
| `correlations` | Correlations page | Yes |
-| `autoMigrateXYChartPanel` | Migrate old XYChart panel to new XYChart2 model | Yes |
| `cloudWatchCrossAccountQuerying` | Enables cross-account querying in CloudWatch datasources | Yes |
| `accessControlOnCall` | Access control primitives for OnCall | Yes |
| `nestedFolders` | Enable folder nesting | Yes |
@@ -80,6 +79,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `cloudWatchRoundUpEndTime` | Round up end time for metric queries to the next minute to avoid missing data | Yes |
| `cloudwatchMetricInsightsCrossAccount` | Enables cross account observability for Cloudwatch Metric Insights query builder | Yes |
| `azureMonitorDisableLogLimit` | Disables the log limit restriction for Azure Monitor when true. The limit is enabled by default. | |
+| `preinstallAutoUpdate` | Enables automatic updates for pre-installed plugins | Yes |
## Public preview feature toggles
@@ -114,7 +114,9 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `ssoSettingsSAML` | Use the new SSO Settings API to configure the SAML connector |
| `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars |
| `ssoSettingsLDAP` | Use the new SSO Settings API to configure LDAP |
+| `singleTopNav` | Unifies the top search bar and breadcrumb bar into one |
| `useSessionStorageForRedirection` | Use session storage for handling the redirection after login |
+| `reportingUseRawTimeRange` | Uses the original report or dashboard time range instead of making an absolute transformation |
## Experimental feature toggles
@@ -203,7 +205,6 @@ Experimental features might be changed or removed without prior notice.
| `newFiltersUI` | Enables new combobox style UI for the Ad hoc filters variable in scenes architecture |
| `lokiSendDashboardPanelNames` | Send dashboard and panel names to Loki when querying |
| `alertingPrometheusRulesPrimary` | Uses Prometheus rules as the primary source of truth for ruler-enabled data sources |
-| `singleTopNav` | Unifies the top search bar and breadcrumb bar into one |
| `exploreLogsShardSplitting` | Used in Explore Logs to split queries into multiple queries based on the number of shards |
| `exploreLogsAggregatedMetrics` | Used in Explore Logs to query by aggregated metrics |
| `exploreLogsLimitedTimeRange` | Used in Explore Logs to limit the time range |
@@ -216,7 +217,6 @@ Experimental features might be changed or removed without prior notice.
| `timeRangeProvider` | Enables time pickers sync |
| `prometheusUsesCombobox` | Use new combobox component for Prometheus query editor |
| `userStorageAPI` | Enables the user storage API |
-| `preinstallAutoUpdate` | Enables automatic updates for pre-installed plugins |
| `dashboardSchemaV2` | Enables the new dashboard schema version 2, implementing changes necessary for dynamic dashboards and dashboards as code. |
| `playlistsWatcher` | Enables experimental watcher for playlists |
| `enableExtensionsAdminPage` | Enables the extension admin page regardless of development mode |
diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md
index fe98f938a20..651eed8c172 100644
--- a/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md
+++ b/docs/sources/setup-grafana/configure-security/configure-authentication/_index.md
@@ -159,10 +159,19 @@ Defaults to `false`.
auto_login = true
```
-### Avoid automatic OAuth login
+### Avoid automatic login
-To sign in with a username and password and avoid automatic OAuth login, add the `disableAutoLogin` parameter to your login URL.
-For example: `grafana.example.com/login?disableAutoLogin` or `grafana.example.com/login?disableAutoLogin=true`
+The `disableAutoLogin=true` URL parameter allows users to bypass the automatic login feature in scenarios where incorrect configuration changes prevent normal login functionality.
+This feature is especially helpful when you need to access the login screen to troubleshoot and fix misconfigurations.
+
+#### How to use
+
+1. Add `disableAutoLogin=true` as a query parameter to your Grafana URL.
+ - Example: `grafana.example.net/login?disableAutoLogin=true` or `grafana.example.net/login?disableAutoLogin`
+1. This will redirect you to the standard login screen, bypassing the automatic login mechanism.
+1. Fix any configuration issues and test your login setup.
+
+This feature is available for both for OAuth and SAML. Ensure that after fixing the issue, you remove the parameter or revert the configuration to re-enable the automatic login feature, if desired.
### Hide sign-out menu
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..adde6ae745b 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,60 @@ 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` |
+{{< admonition type="note" >}}
+If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"`
+{{< /admonition >}}
-## Examples of setting up generic OAuth2
+| 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. | |
-This section includes examples of setting up generic OAuth2 integration.
+## Examples of setting up Generic OAuth
+
+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 +454,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 +487,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 +522,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 +558,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..600cc9f26e4 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.
@@ -156,7 +166,18 @@ All other users are granted the `Viewer` role.
role_attribute_path = contains(groups[*], '@my-github-organization/my-github-team') && 'Editor' || 'Viewer'
```
-#### Map server administrator role
+##### Map roles using multiple GitHub teams
+
+In this example, the users from GitHub teams `admins` and `devops` have been granted the `Admin` role,
+the users from GitHub teams `engineers` and `managers` have been granted the `Editor` role,
+the users from GitHub team `qa` have been granted the `Viewer` role and
+all other users are granted the `None` role.
+
+```bash
+role_attribute_path = contains(groups[*], '@my-github-organization/admins') && 'Admin' || contains(groups[*], '@my-github-organization/devops') && 'Admin' || contains(groups[*], '@my-github-organization/engineers') && 'Editor' || contains(groups[*], '@my-github-organization/managers') && 'Editor' || contains(groups[*], '@my-github-organization/qa') && 'Viewer' || 'None'
+```
+
+##### Map server administrator role
In this example, the user with login `octocat` has been granted the `Admin` organization role as well as the Grafana server admin role.
All other users are granted the `Viewer` role.
@@ -215,30 +236,35 @@ 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. | |
+{{< admonition type="note" >}}
+If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"`
+{{< /admonition >}}
+
+| 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..075047b7c49 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,36 @@ 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` |
+{{< admonition type="note" >}}
+If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"`
+{{< /admonition >}}
+
+| 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..acf3e070cdd 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,32 @@ 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` |
+{{< admonition type="note" >}}
+If the configuration option requires a JMESPath expression that includes a colon, enclose the entire expression in quotes to prevent parsing errors. For example `role_attribute_path: "role:view"`
+{{< /admonition >}}
+
+| 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. | |
diff --git a/emails/templates/passwordless_verify_existing_user.mjml b/emails/templates/passwordless_verify_existing_user.mjml
new file mode 100644
index 00000000000..1c583bc03ad
--- /dev/null
+++ b/emails/templates/passwordless_verify_existing_user.mjml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+ {{ Subject .Subject .TemplateData "Verify your email" }}
+
+
+
+
+
+
+
+
+
+
+
Please verify your email
+
+
+ Copy and paste the confirmation code into the login form to verify your email address. This confirmation code
+ will expire in {{ .Expire }} minutes.
+
+
+
+
+
+ {{ .ConfirmationCode }}
+
+
+
+
+ Alternatively, you can use the button below to verify your email address.
+
+ Verify your email
+
+ You can also copy and paste this link into your browser directly:
+
+ {{ .AppUrl }}login?code={{ .Code }}&confirmationCode={{ .ConfirmationCode }}
+
+
+
+
+
+
+
+
+
diff --git a/emails/templates/passwordless_verify_existing_user.txt b/emails/templates/passwordless_verify_existing_user.txt
new file mode 100644
index 00000000000..ccc8f1f3d1d
--- /dev/null
+++ b/emails/templates/passwordless_verify_existing_user.txt
@@ -0,0 +1,10 @@
+[[HiddenSubject .Subject "Verify your email"]]
+
+Hi,
+
+Copy and paste the email verification code:
+[[.ConfirmationCode]]
+into the login form to verify your email address. This confirmation code will expire in {{ .Expire }} minutes.
+Alternatively, you can use the button below to verify your email address.
+
+[[.AppUrl]]login/?code=[[.Code]]&confirmationCode=[[.ConfirmationCode]]
diff --git a/emails/templates/passwordless_verify_new_user.mjml b/emails/templates/passwordless_verify_new_user.mjml
new file mode 100644
index 00000000000..d08457ea7e5
--- /dev/null
+++ b/emails/templates/passwordless_verify_new_user.mjml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+ {{ Subject .Subject .TemplateData "Welcome to Grafana, please complete your sign up!" }}
+
+
+
+
+
+
+
+
+
+
+
Please complete your signup
+
+
+ Copy and paste the confirmation code into the sign up form to verify your email address. This confirmation
+ code will expire in {{ .Expire }} minutes.
+
+
+
+
+
+ {{ .ConfirmationCode }}
+
+
+
+
+ Alternatively, you can use the button below to complete your sign up.
+
+ Complete Sign Up
+
+ You can also copy and paste this link into your browser directly:
+
+ {{ .AppUrl }}login?code={{ .Code }}&confirmationCode={{ .ConfirmationCode }}&signup=true
+
+
+
+
+
+
+
+
+
diff --git a/emails/templates/passwordless_verify_new_user.txt b/emails/templates/passwordless_verify_new_user.txt
new file mode 100644
index 00000000000..938d1dab934
--- /dev/null
+++ b/emails/templates/passwordless_verify_new_user.txt
@@ -0,0 +1,10 @@
+[[HiddenSubject .Subject "Welcome to Grafana, please complete your signup!"]]
+
+Hi,
+
+Copy and paste the email verification code:
+[[.ConfirmationCode]]
+into the sign up form to verify your email address. This confirmation code will expire in {{ .Expire }} minutes.
+Alternatively, you can use the button below to verify your email address.
+
+[[.AppUrl]]login/?code=[[.Code]]&confirmationCode=[[.ConfirmationCode]]
diff --git a/go.mod b/go.mod
index 7b8fc74c59d..df73af80bc0 100644
--- a/go.mod
+++ b/go.mod
@@ -61,7 +61,7 @@ require (
github.com/go-stack/stack v1.8.1 // @grafana/grafana-backend-group
github.com/gobwas/glob v0.2.3 // @grafana/grafana-backend-group
github.com/gogo/protobuf v1.3.2 // @grafana/alerting-backend
- github.com/golang-jwt/jwt/v4 v4.5.0 // @grafana/grafana-backend-group
+ github.com/golang-jwt/jwt/v4 v4.5.1 // @grafana/grafana-backend-group
github.com/golang-migrate/migrate/v4 v4.7.0 // @grafana/grafana-backend-group
github.com/golang/mock v1.6.0 // @grafana/alerting-backend
github.com/golang/protobuf v1.5.4 // @grafana/grafana-backend-group
diff --git a/go.sum b/go.sum
index 31f3022ffb8..e87e90b4184 100644
--- a/go.sum
+++ b/go.sum
@@ -2060,8 +2060,9 @@ github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzq
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
+github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
diff --git a/package.json b/package.json
index 5a422a6f0be..e7cf16bafa2 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",
@@ -225,7 +225,7 @@
"redux-mock-store": "1.5.5",
"rimraf": "6.0.1",
"rudder-sdk-js": "2.48.26",
- "sass": "1.79.5",
+ "sass": "1.80.7",
"sass-loader": "16.0.3",
"smtp-tester": "^2.1.0",
"style-loader": "4.0.0",
@@ -253,7 +253,7 @@
"@emotion/css": "11.13.4",
"@emotion/react": "11.13.3",
"@fingerprintjs/fingerprintjs": "^3.4.2",
- "@floating-ui/react": "0.26.27",
+ "@floating-ui/react": "0.26.28",
"@formatjs/intl-durationformat": "^0.6.0",
"@glideapps/glide-data-grid": "^6.0.0",
"@grafana/aws-sdk": "0.5.0",
@@ -272,8 +272,8 @@
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/saga-icons": "workspace:*",
- "@grafana/scenes": "5.23.1",
- "@grafana/scenes-react": "5.23.1",
+ "@grafana/scenes": "5.25.0",
+ "@grafana/scenes-react": "5.25.0",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",
@@ -361,7 +361,7 @@
"prismjs": "1.29.0",
"rc-slider": "11.1.7",
"rc-time-picker": "3.7.3",
- "rc-tree": "5.9.0",
+ "rc-tree": "5.10.1",
"re-resizable": "6.10.1",
"react": "18.2.0",
"react-diff-viewer-continued": "^3.4.0",
diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts
index ceda30f6674..50b9461ba19 100644
--- a/packages/grafana-data/src/types/config.ts
+++ b/packages/grafana-data/src/types/config.ts
@@ -278,5 +278,6 @@ export interface AuthSettings {
GenericOAuthSkipOrgRoleSync?: boolean;
disableLogin?: boolean;
+ passwordlessEnabled?: boolean;
basicAuthStrongPasswordPolicy?: boolean;
}
diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts
index d3dbd6c2c7f..3d27b8456cf 100644
--- a/packages/grafana-data/src/types/featureToggles.gen.ts
+++ b/packages/grafana-data/src/types/featureToggles.gen.ts
@@ -35,7 +35,6 @@ export interface FeatureToggles {
autoMigratePiechartPanel?: boolean;
autoMigrateWorldmapPanel?: boolean;
autoMigrateStatPanel?: boolean;
- autoMigrateXYChartPanel?: boolean;
disableAngular?: boolean;
canvasPanelNesting?: boolean;
vizActions?: boolean;
@@ -232,9 +231,11 @@ export interface FeatureToggles {
preinstallAutoUpdate?: boolean;
dashboardSchemaV2?: boolean;
playlistsWatcher?: boolean;
+ passwordlessMagicLinkAuthentication?: boolean;
exploreMetricsRelatedLogs?: boolean;
enableExtensionsAdminPage?: boolean;
zipkinBackendMigration?: boolean;
enableSCIM?: boolean;
crashDetection?: boolean;
+ reportingUseRawTimeRange?: boolean;
}
diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts
index 3d84faa1538..92f73b85303 100644
--- a/packages/grafana-e2e-selectors/src/selectors/pages.ts
+++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts
@@ -39,6 +39,19 @@ export const versionedPages = {
'10.2.3': 'data-testid Skip change password button',
},
},
+ PasswordlessLogin: {
+ url: {
+ [MIN_GRAFANA_VERSION]: '/login/passwordless/authenticate',
+ },
+ email: {
+ '10.2.3': 'data-testid Email input field',
+ [MIN_GRAFANA_VERSION]: 'Email input field',
+ },
+ submit: {
+ '10.2.3': 'data-testid PasswordlessLogin button',
+ [MIN_GRAFANA_VERSION]: 'PasswordlessLogin button',
+ },
+ },
Home: {
url: {
[MIN_GRAFANA_VERSION]: '/',
diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json
index b305c672b5d..6d241e46306 100644
--- a/packages/grafana-prometheus/package.json
+++ b/packages/grafana-prometheus/package.json
@@ -37,10 +37,10 @@
},
"dependencies": {
"@emotion/css": "11.13.4",
- "@floating-ui/react": "0.26.27",
+ "@floating-ui/react": "0.26.28",
"@grafana/data": "11.4.0-pre",
"@grafana/experimental": "2.1.2",
- "@grafana/faro-web-sdk": "1.11.0",
+ "@grafana/faro-web-sdk": "1.12.0",
"@grafana/runtime": "11.4.0-pre",
"@grafana/schema": "11.4.0-pre",
"@grafana/ui": "11.4.0-pre",
@@ -130,7 +130,7 @@
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-esbuild": "6.1.1",
"rollup-plugin-node-externals": "^7.1.3",
- "sass": "1.79.5",
+ "sass": "1.80.7",
"sass-loader": "16.0.3",
"style-loader": "4.0.0",
"testing-library-selector": "0.3.1",
diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx
index 4aa698fa7cb..e983eb23be9 100644
--- a/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx
+++ b/packages/grafana-prometheus/src/querybuilder/components/MetricSelect.tsx
@@ -17,11 +17,11 @@ import {
Icon,
InlineField,
InlineFieldRow,
+ ScrollContainer,
SelectMenuOptions,
useStyles2,
useTheme2,
} from '@grafana/ui';
-import { ScrollContainer } from '@grafana/ui/src/unstable';
import { PrometheusDatasource } from '../../datasource';
import { truncateResult } from '../../language_utils';
diff --git a/packages/grafana-runtime/src/services/SidecarContext_EXPERIMENTAL.ts b/packages/grafana-runtime/src/services/SidecarContext_EXPERIMENTAL.ts
index 1597fb25b39..dd689499573 100644
--- a/packages/grafana-runtime/src/services/SidecarContext_EXPERIMENTAL.ts
+++ b/packages/grafana-runtime/src/services/SidecarContext_EXPERIMENTAL.ts
@@ -21,17 +21,20 @@ export function useSidecar_EXPERIMENTAL() {
throw new Error('No SidecarContext found');
}
- const activePluginId = useObservable(service.activePluginIdObservable, service.activePluginId);
const initialContext = useObservable(service.initialContextObservable, service.initialContext);
+ const activePluginId = useObservable(service.activePluginIdObservable, service.activePluginId);
+ const locationService = service.getLocationService();
return {
activePluginId,
initialContext,
+ locationService,
// TODO: currently this allows anybody to open any app, in the future we should probably scope this to the
// current app but that means we will need to incorporate this better into the plugin platform APIs which
// we will do once the functionality is reasonably stable
- openApp: (pluginId: string) => service.openApp(pluginId),
- closeApp: (pluginId: string) => service.closeApp(pluginId),
+ openApp: (pluginId: string, context?: unknown) => service.openApp(pluginId, context),
+ openAppV2: (pluginId: string, path?: string) => service.openAppV2(pluginId, path),
+ closeApp: () => service.closeApp(),
isAppOpened: (pluginId: string) => service.isAppOpened(pluginId),
};
}
diff --git a/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.test.ts b/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.test.ts
index a58d5483fd3..afa9f3aaf18 100644
--- a/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.test.ts
+++ b/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.test.ts
@@ -11,37 +11,51 @@ describe('SidecarService_EXPERIMENTAL', () => {
});
it('has the correct state after opening and closing an app', () => {
- const sidecarService = new SidecarService_EXPERIMENTAL({});
+ const sidecarService = new SidecarService_EXPERIMENTAL();
sidecarService.openApp('pluginId', { filter: 'test' });
expect(sidecarService.activePluginId).toBe('pluginId');
expect(sidecarService.initialContext).toMatchObject({ filter: 'test' });
+ expect(sidecarService.getLocationService().getLocation().pathname).toBe('/a/pluginId');
- sidecarService.closeApp('pluginId');
+ sidecarService.closeApp();
expect(sidecarService.activePluginId).toBe(undefined);
expect(sidecarService.initialContext).toBe(undefined);
+ expect(sidecarService.getLocationService().getLocation().pathname).toBe('/');
+ });
+
+ it('has the correct state after opening and closing an app v2', () => {
+ const sidecarService = new SidecarService_EXPERIMENTAL();
+ sidecarService.openAppV2('pluginId', '/test');
+
+ expect(sidecarService.activePluginId).toBe('pluginId');
+ expect(sidecarService.getLocationService().getLocation().pathname).toBe('/a/pluginId/test');
+
+ sidecarService.closeApp();
+ expect(sidecarService.activePluginId).toBe(undefined);
+ expect(sidecarService.initialContext).toBe(undefined);
+ expect(sidecarService.getLocationService().getLocation().pathname).toBe('/');
});
it('reports correct opened state', () => {
- const sidecarService = new SidecarService_EXPERIMENTAL({});
+ const sidecarService = new SidecarService_EXPERIMENTAL();
expect(sidecarService.isAppOpened('pluginId')).toBe(false);
sidecarService.openApp('pluginId');
-
expect(sidecarService.isAppOpened('pluginId')).toBe(true);
- sidecarService.closeApp('pluginId');
-
+ sidecarService.closeApp();
expect(sidecarService.isAppOpened('pluginId')).toBe(false);
});
- it('does not close app that is not opened', () => {
- const sidecarService = new SidecarService_EXPERIMENTAL({});
- sidecarService.openApp('pluginId');
-
- sidecarService.closeApp('foobar');
+ it('reports correct opened state v2', () => {
+ const sidecarService = new SidecarService_EXPERIMENTAL();
+ expect(sidecarService.isAppOpened('pluginId')).toBe(false);
+ sidecarService.openAppV2('pluginId');
expect(sidecarService.isAppOpened('pluginId')).toBe(true);
- expect(sidecarService.activePluginId).toBe('pluginId');
+
+ sidecarService.closeApp();
+ expect(sidecarService.isAppOpened('pluginId')).toBe(false);
});
});
diff --git a/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.ts b/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.ts
index 67e8dd776f7..c91d2dc9181 100644
--- a/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.ts
+++ b/packages/grafana-runtime/src/services/SidecarService_EXPERIMENTAL.ts
@@ -1,16 +1,14 @@
-import { BehaviorSubject } from 'rxjs';
+import * as H from 'history';
+import { pick } from 'lodash';
+import { BehaviorSubject, map, Observable } from 'rxjs';
import { config } from '../config';
-import { locationService } from './LocationService';
-
-interface Options {
- localStorageKey?: string;
-}
+import { HistoryWrapper, LocationService, locationService } from './LocationService';
/**
* This is a service that handles state and operation of a sidecar feature (sideview to render a second app in grafana).
- * At this moment this is highly experimental and if used should be understand to break easily with newer versions.
+ * At this moment this is highly experimental and if used should be understood to break easily with newer versions.
* None of this functionality works without a feature toggle `appSidecar` being enabled.
*
* Right now this being in a single service is more of a practical tradeoff for easier isolation in the future these
@@ -19,18 +17,16 @@ interface Options {
* @experimental
*/
export class SidecarService_EXPERIMENTAL {
- private _activePluginId: BehaviorSubject;
private _initialContext: BehaviorSubject;
- private localStorageKey: string | undefined;
+ private memoryLocationService: LocationService;
+ private history: LocationStorageHistory;
- constructor(options: Options) {
- this.localStorageKey = options.localStorageKey;
- let initialId = undefined;
- if (this.localStorageKey) {
- initialId = localStorage.getItem(this.localStorageKey) || undefined;
- }
- this._activePluginId = new BehaviorSubject(initialId);
+ constructor() {
this._initialContext = new BehaviorSubject(undefined);
+ // We need a local ref for this so we can tap into the location changes and drive rerendering of components based
+ // on it without having a parent Router.
+ this.history = createLocationStorageHistory({ storageKey: 'grafana.sidecar.history' });
+ this.memoryLocationService = new HistoryWrapper(this.history);
}
private assertFeatureEnabled() {
@@ -50,7 +46,11 @@ export class SidecarService_EXPERIMENTAL {
* @experimental
*/
get activePluginIdObservable() {
- return this._activePluginId.asObservable();
+ return this.history.getLocationObservable().pipe(
+ map((val) => {
+ return getPluginIdFromUrl(val?.pathname || '');
+ })
+ );
}
/**
@@ -74,39 +74,48 @@ export class SidecarService_EXPERIMENTAL {
* @experimental
*/
get activePluginId() {
- return this._activePluginId.getValue();
+ return getPluginIdFromUrl(this.memoryLocationService.getLocation().pathname);
+ }
+
+ getLocationService() {
+ return this.memoryLocationService;
}
/**
* Opens an app in a sidecar. You can also pass some context object that will be then available to the app.
+ * @deprecated
* @experimental
*/
openApp(pluginId: string, context?: unknown) {
if (!this.assertFeatureEnabled()) {
return;
}
- if (this.localStorageKey) {
- localStorage.setItem(this.localStorageKey, pluginId);
+ this._initialContext.next(context);
+ this.memoryLocationService.push({ pathname: `/a/${pluginId}` });
+ }
+
+ /**
+ * Opens an app in a sidecar. You can also relative path inside the app to open.
+ * @experimental
+ */
+ openAppV2(pluginId: string, path?: string) {
+ if (!this.assertFeatureEnabled()) {
+ return;
}
- this._activePluginId.next(pluginId);
- this._initialContext.next(context);
+ this.memoryLocationService.push({ pathname: `/a/${pluginId}${path || ''}` });
}
/**
* @experimental
*/
- closeApp(pluginId: string) {
+ closeApp() {
if (!this.assertFeatureEnabled()) {
return;
}
- if (this._activePluginId.getValue() === pluginId) {
- if (this.localStorageKey) {
- localStorage.removeItem(this.localStorageKey);
- }
- this._activePluginId.next(undefined);
- this._initialContext.next(undefined);
- }
+
+ this._initialContext.next(undefined);
+ this.memoryLocationService.replace({ pathname: '/' });
}
/**
@@ -130,23 +139,23 @@ export class SidecarService_EXPERIMENTAL {
return false;
}
- return !!(
- this._activePluginId.getValue() &&
- (this._activePluginId.getValue() === pluginId || getMainAppPluginId() === pluginId)
- );
+ return !!(this.activePluginId && (this.activePluginId === pluginId || getMainAppPluginId() === pluginId));
}
}
-export const sidecarServiceSingleton_EXPERIMENTAL = new SidecarService_EXPERIMENTAL({
- localStorageKey: 'grafana.sidecar.activePluginId',
-});
+const pluginIdUrlRegex = /a\/([^\/]+)/;
+function getPluginIdFromUrl(url: string) {
+ return url.match(pluginIdUrlRegex)?.[1];
+}
// The app plugin that is "open" in the main Grafana view
function getMainAppPluginId() {
+ // TODO: not great but we have to get a handle on the other locationService used for the main view and easiest way
+ // right now is through this global singleton
const { pathname } = locationService.getLocation();
// A naive way to sort of simulate core features being an app and having an appID
- let mainApp = pathname.match(/\/a\/([^/]+)/)?.[1];
+ let mainApp = getPluginIdFromUrl(pathname);
if (!mainApp && pathname.match(/\/explore/)) {
mainApp = 'explore';
}
@@ -157,3 +166,88 @@ function getMainAppPluginId() {
return mainApp || 'unknown';
}
+
+type LocalStorageHistoryOptions = {
+ storageKey: string;
+};
+
+interface LocationStorageHistory extends H.MemoryHistory {
+ getLocationObservable(): Observable;
+}
+
+/**
+ * Simple wrapper over the memory history that persists the location in the localStorage.
+ *
+ * @param options
+ */
+function createLocationStorageHistory(options: LocalStorageHistoryOptions): LocationStorageHistory {
+ const storedLocation = localStorage.getItem(options.storageKey);
+ const initialEntry = storedLocation ? JSON.parse(storedLocation) : '/';
+ const locationSubject = new BehaviorSubject(initialEntry);
+ const memoryHistory = H.createMemoryHistory({ initialEntries: [initialEntry] });
+
+ let currentLocation = memoryHistory.location;
+
+ function maybeUpdateLocation() {
+ if (memoryHistory.location !== currentLocation) {
+ localStorage.setItem(
+ options.storageKey,
+ JSON.stringify(pick(memoryHistory.location, 'pathname', 'search', 'hash'))
+ );
+ currentLocation = memoryHistory.location;
+ locationSubject.next(memoryHistory.location);
+ }
+ }
+
+ // This creates a sort of proxy over the memory location just to add the localStorage persistence and the location
+ // observer. We could achieve the same effect by a listener but that would create a memory leak as there would be no
+ // reasonable way to unsubcribe the listener later on.
+ // Another issue is that react router for some reason does not care about proper `this` binding and just calls these
+ // as normal functions. So if this were to be a class we would still need to bind each of these methods to the
+ // instance so at that moment this just seems easier.
+ return {
+ ...memoryHistory,
+ // Getter aren't destructured as getter but as values, so they have to be still here even though we are not
+ // modifying them.
+ get index() {
+ return memoryHistory.index;
+ },
+ get entries() {
+ return memoryHistory.entries;
+ },
+ get length() {
+ return memoryHistory.length;
+ },
+ get action() {
+ return memoryHistory.action;
+ },
+ get location() {
+ return memoryHistory.location;
+ },
+ push(location: H.Path | H.LocationDescriptor, state?: H.LocationState) {
+ memoryHistory.push(location, state);
+ maybeUpdateLocation();
+ },
+ replace(location: H.Path | H.LocationDescriptor, state?: H.LocationState) {
+ memoryHistory.replace(location, state);
+ maybeUpdateLocation();
+ },
+ go(n: number) {
+ memoryHistory.go(n);
+ maybeUpdateLocation();
+ },
+ goBack() {
+ memoryHistory.goBack();
+ maybeUpdateLocation();
+ },
+ goForward() {
+ memoryHistory.goForward();
+ maybeUpdateLocation();
+ },
+ getLocationObservable() {
+ return locationSubject.asObservable();
+ },
+ };
+}
+
+export const sidecarServiceSingleton_EXPERIMENTAL = new SidecarService_EXPERIMENTAL();
diff --git a/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts
index 357969cb116..7e1b4a63d64 100644
--- a/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts
+++ b/packages/grafana-schema/src/raw/composable/nodegraph/panelcfg/x/NodeGraphPanelCfg_types.gen.ts
@@ -21,6 +21,11 @@ export interface ArcOption {
field?: string;
}
+export enum ZoomMode {
+ Cooperative = 'cooperative',
+ Greedy = 'greedy',
+}
+
export interface Options {
edges?: {
/**
@@ -46,4 +51,8 @@ export interface Options {
*/
arcs?: Array;
};
+ /**
+ * How to handle zoom/scroll events in the node graph
+ */
+ zoomMode?: ZoomMode;
}
diff --git a/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts
index 8e98dfaa449..5e5267a9be4 100644
--- a/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts
+++ b/packages/grafana-schema/src/raw/composable/xychart/panelcfg/x/XYChartPanelCfg_types.gen.ts
@@ -12,66 +12,85 @@ import * as common from '@grafana/schema';
export const pluginVersion = "11.4.0-pre";
-/**
- * Auto is "table" in the UI
- */
+export enum PointShape {
+ Circle = 'circle',
+ Square = 'square',
+}
+
export enum SeriesMapping {
Auto = 'auto',
Manual = 'manual',
}
-export enum ScatterShow {
+export enum XYShowMode {
Lines = 'lines',
Points = 'points',
PointsAndLines = 'points+lines',
}
/**
- * Configuration for the Table/Auto mode
+ * NOTE: (copied from dashboard_kind.cue, since not exported)
+ * Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.
+ * It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.
*/
-export interface XYDimensionConfig {
- exclude?: Array;
- frame: number;
- x?: string;
+export interface MatcherConfig {
+ /**
+ * The matcher id. This is used to find the matcher implementation from registry.
+ */
+ id: string;
+ /**
+ * The matcher options. This is specific to the matcher implementation.
+ */
+ options?: unknown;
}
-export const defaultXYDimensionConfig: Partial = {
- exclude: [],
+export const defaultMatcherConfig: Partial = {
+ id: '',
};
export interface FieldConfig extends common.HideableFieldConfig, common.AxisConfig {
- label?: common.VisibilityMode;
- labelValue?: common.TextDimensionConfig;
- lineColor?: common.ColorDimensionConfig;
+ fillOpacity?: number;
lineStyle?: common.LineStyle;
lineWidth?: number;
- pointColor?: common.ColorDimensionConfig;
- pointSize?: common.ScaleDimensionConfig;
- show?: ScatterShow;
+ pointShape?: PointShape;
+ pointSize?: {
+ fixed?: number;
+ min?: number;
+ max?: number;
+ };
+ pointStrokeWidth?: number;
+ show?: XYShowMode;
}
export const defaultFieldConfig: Partial = {
- label: common.VisibilityMode.Auto,
- show: ScatterShow.Points,
+ fillOpacity: 50,
+ show: XYShowMode.Points,
};
-export interface ScatterSeriesConfig extends FieldConfig {
- frame?: number;
- name?: string;
- x?: string;
- y?: string;
+export interface XYSeriesConfig {
+ color?: {
+ matcher: MatcherConfig;
+ };
+ frame?: {
+ matcher: MatcherConfig;
+ };
+ name?: {
+ fixed?: string;
+ };
+ size?: {
+ matcher: MatcherConfig;
+ };
+ x?: {
+ matcher: MatcherConfig;
+ };
+ y?: {
+ matcher: MatcherConfig;
+ };
}
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip {
- /**
- * Table Mode (auto)
- */
- dims: XYDimensionConfig;
- /**
- * Manual Mode
- */
- series: Array;
- seriesMapping?: SeriesMapping;
+ mapping: SeriesMapping;
+ series: Array;
}
export const defaultOptions: Partial = {
diff --git a/packages/grafana-sql/src/components/QueryEditor.tsx b/packages/grafana-sql/src/components/QueryEditor.tsx
index 3adffb79efb..487c4f2c1c9 100644
--- a/packages/grafana-sql/src/components/QueryEditor.tsx
+++ b/packages/grafana-sql/src/components/QueryEditor.tsx
@@ -14,11 +14,11 @@ import { QueryHeader, QueryHeaderProps } from './QueryHeader';
import { RawEditor } from './query-editor-raw/RawEditor';
import { VisualEditor } from './visual-query-builder/VisualEditor';
-interface SqlQueryEditorProps extends QueryEditorProps {
+export interface SqlQueryEditorProps extends QueryEditorProps {
queryHeaderProps?: Pick;
}
-export function SqlQueryEditor({
+export default function SqlQueryEditor({
datasource,
query,
onChange,
diff --git a/packages/grafana-sql/src/components/QueryEditorLazy.tsx b/packages/grafana-sql/src/components/QueryEditorLazy.tsx
new file mode 100644
index 00000000000..ed76b6a492c
--- /dev/null
+++ b/packages/grafana-sql/src/components/QueryEditorLazy.tsx
@@ -0,0 +1,27 @@
+import { css } from '@emotion/css';
+import { lazy, Suspense } from 'react';
+
+import { GrafanaTheme2 } from '@grafana/data';
+import { LoadingPlaceholder, useStyles2 } from '@grafana/ui';
+
+import type { SqlQueryEditorProps } from './QueryEditor';
+const QueryEditor = lazy(() => import(/* webpackChunkName: "sql-query-editor" */ './QueryEditor'));
+
+export function SqlQueryEditorLazy(props: SqlQueryEditorProps) {
+ const styles = useStyles2(getStyles);
+
+ return (
+ }>
+
+
+ );
+}
+
+const getStyles = (theme: GrafanaTheme2) => {
+ return {
+ container: css({
+ marginBottom: 'unset',
+ marginLeft: theme.spacing(1),
+ }),
+ };
+};
diff --git a/packages/grafana-sql/src/datasource/SqlDatasource.ts b/packages/grafana-sql/src/datasource/SqlDatasource.ts
index ce5d3bc7ea2..121d30a4731 100644
--- a/packages/grafana-sql/src/datasource/SqlDatasource.ts
+++ b/packages/grafana-sql/src/datasource/SqlDatasource.ts
@@ -30,7 +30,7 @@ import {
} from '@grafana/runtime';
import { ResponseParser } from '../ResponseParser';
-import { SqlQueryEditor } from '../components/QueryEditor';
+import { SqlQueryEditorLazy } from '../components/QueryEditorLazy';
import { MACRO_NAMES } from '../constants';
import { DB, SQLQuery, SQLOptions, SqlQueryModel, QueryFormat } from '../types';
import migrateAnnotation from '../utils/migration';
@@ -63,7 +63,7 @@ export abstract class SqlDatasource extends DataSourceWithBackend
+
+## 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.test.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.test.tsx
index 1c3ac5766da..d45c2cc2402 100644
--- a/packages/grafana-ui/src/components/Combobox/Combobox.test.tsx
+++ b/packages/grafana-ui/src/components/Combobox/Combobox.test.tsx
@@ -166,7 +166,7 @@ describe('Combobox', () => {
expect(onChangeHandler).toHaveBeenCalledWith(expect.objectContaining({ value: 'custom value' }));
});
- it('should proivde custom string when all options are numbers', async () => {
+ it('should provide custom string when all options are numbers', async () => {
const options = [
{ label: '1', value: 1 },
{ label: '2', value: 2 },
@@ -333,7 +333,7 @@ describe('Combobox', () => {
jest.advanceTimersByTime(500); // Custom value while typing
});
- const customItem = screen.queryByRole('option', { name: 'fir Create custom value' });
+ const customItem = screen.queryByRole('option', { name: 'Custom value: fir' });
expect(customItem).toBeInTheDocument();
});
diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.tsx
index eb8112ba6be..148c9e61cd3 100644
--- a/packages/grafana-ui/src/components/Combobox/Combobox.tsx
+++ b/packages/grafana-ui/src/components/Combobox/Combobox.tsx
@@ -5,6 +5,7 @@ import { debounce } from 'lodash';
import { ReactNode, useCallback, useId, useMemo, useState } from 'react';
import { useStyles2 } from '../../themes';
+import { logOptions } from '../../utils';
import { t, Trans } from '../../utils/i18n';
import { Icon } from '../Icon/Icon';
import { AutoSizeInput } from '../Input/AutoSizeInput';
@@ -13,8 +14,8 @@ import { Box } from '../Layout/Box/Box';
import { Stack } from '../Layout/Stack/Stack';
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
-import { getComboboxStyles } from './getComboboxStyles';
-import { useComboboxFloat, OPTION_HEIGHT } from './useComboboxFloat';
+import { getComboboxStyles, MENU_OPTION_HEIGHT } from './getComboboxStyles';
+import { useComboboxFloat } from './useComboboxFloat';
import { StaleResultError, useLatestAsyncCall } from './useLatestAsyncCall';
export type ComboboxOption = {
@@ -27,7 +28,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;
@@ -42,10 +49,18 @@ interface ComboboxBaseProps
width?: number | 'auto';
}
+const RECOMMENDED_ITEMS_AMOUNT = 100_000;
+
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;
}
| {
@@ -57,6 +72,9 @@ type AutoSizeConditionals =
type ComboboxProps = ComboboxBaseProps & AutoSizeConditionals;
function itemToString(item: ComboboxOption | null) {
+ if (item?.label?.includes('Custom value: ')) {
+ return item?.value.toString();
+ }
return item?.label ?? item?.value.toString() ?? '';
}
@@ -108,15 +126,20 @@ export const Combobox = (props: ComboboxProps) =>
const setItems = useCallback(
(items: Array>, inputValue: string | undefined) => {
let itemsToSet = items;
-
+ logOptions(itemsToSet.length, RECOMMENDED_ITEMS_AMOUNT, id, ariaLabelledBy);
if (inputValue && createCustomValue) {
- const optionMatchingInput = items.find((opt) => opt.label === inputValue || opt.value === inputValue);
+ const optionMatchingInput = items.find(
+ (opt) => opt.label === 'Custom value: ' + inputValue || opt.value === inputValue
+ );
if (!optionMatchingInput) {
const customValueOption = {
+ label: t('combobox.custom-value.label', 'Custom value: ') + inputValue,
// Type casting needed to make this work when T is a number
value: inputValue as unknown as T,
+ /* TODO: Add this back when we do support descriptions and have need for it
description: t('combobox.custom-value.create', 'Create custom value'),
+ */
};
itemsToSet = items.slice(0);
@@ -126,7 +149,7 @@ export const Combobox = (props: ComboboxProps) =>
baseSetItems(itemsToSet);
},
- [createCustomValue]
+ [createCustomValue, id, ariaLabelledBy]
);
const selectedItemIndex = useMemo(() => {
@@ -162,7 +185,7 @@ export const Combobox = (props: ComboboxProps) =>
const virtualizerOptions = {
count: items.length,
getScrollElement: () => scrollRef.current,
- estimateSize: () => OPTION_HEIGHT,
+ estimateSize: () => MENU_OPTION_HEIGHT,
overscan: 4,
};
@@ -195,8 +218,6 @@ export const Combobox = (props: ComboboxProps) =>
getMenuProps,
getItemProps,
- openMenu,
- closeMenu,
selectItem,
} = useCombobox({
menuId,
@@ -307,10 +328,6 @@ export const Combobox = (props: ComboboxProps) =>
const { inputRef, floatingRef, floatStyles, scrollRef } = useComboboxFloat(items, rowVirtualizer.range, isOpen);
- const handleSuffixClick = useCallback(() => {
- isOpen ? closeMenu() : openMenu();
- }, [isOpen, openMenu, closeMenu]);
-
const InputComponent = width === 'auto' ? AutoSizeInput : Input;
const suffixIcon = asyncLoading
@@ -326,6 +343,7 @@ export const Combobox = (props: ComboboxProps) =>
{!!value && value === selectedItem?.value && isClearable && (
@@ -346,11 +364,7 @@ export const Combobox = (props: ComboboxProps) =>
/>
)}
- {/* When you click the input, it should just focus the text box. However, clicks on input suffix arent
- translated to the input, so it blocks the input from being focused. So we need an additional event
- handler here to open/close the menu. It should not have button role because we intentionally don't
- want it in the a11y tree. */}
-
+
>
}
{...restProps}
diff --git a/packages/grafana-ui/src/components/Combobox/getComboboxStyles.ts b/packages/grafana-ui/src/components/Combobox/getComboboxStyles.ts
index c6926de6537..9b3ff494f7a 100644
--- a/packages/grafana-ui/src/components/Combobox/getComboboxStyles.ts
+++ b/packages/grafana-ui/src/components/Combobox/getComboboxStyles.ts
@@ -6,7 +6,12 @@ import { GrafanaTheme2 } from '@grafana/data';
// This should be in sync with the body font size in the theme.
export const MENU_ITEM_FONT_SIZE = 14;
export const MENU_ITEM_FONT_WEIGHT = 500;
-export const MENU_ITEM_PADDING_X = 8;
+export const MENU_ITEM_PADDING = 8;
+export const MENU_ITEM_LINE_HEIGHT = 1.5;
+
+// Used with Downshift to get the height of each item
+export const MENU_OPTION_HEIGHT = MENU_ITEM_PADDING * 2 + MENU_ITEM_FONT_SIZE * MENU_ITEM_LINE_HEIGHT;
+export const POPOVER_MAX_HEIGHT = MENU_OPTION_HEIGHT * 8.5;
export const getComboboxStyles = (theme: GrafanaTheme2) => {
return {
@@ -27,7 +32,7 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
}),
option: css({
label: 'grafana-select-option',
- padding: MENU_ITEM_PADDING_X,
+ padding: MENU_ITEM_PADDING,
position: 'absolute',
display: 'flex',
alignItems: 'center',
@@ -65,7 +70,7 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
fontWeight: 'normal',
fontSize: theme.typography.bodySmall.fontSize,
color: theme.colors.text.secondary,
- lineHeight: theme.typography.body.lineHeight,
+ lineHeight: MENU_ITEM_LINE_HEIGHT,
textOverflow: 'ellipsis',
overflow: 'hidden',
}),
@@ -84,7 +89,7 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
borderRadius: theme.shape.radius.default,
content: '" "',
display: 'block',
- height: '100%',
+ height: MENU_OPTION_HEIGHT,
position: 'absolute',
width: theme.spacing(0.5),
left: 0,
@@ -94,6 +99,7 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
clear: css({
label: 'grafana-select-clear',
cursor: 'pointer',
+ pointerEvents: 'auto',
'&:hover': {
color: theme.colors.text.primary,
},
@@ -102,5 +108,11 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
label: 'grafana-select-warning-icon',
color: theme.colors.text.secondary,
}),
+ input: css({
+ label: 'grafana-select-input',
+ '> div > div:last-child': {
+ pointerEvents: 'none',
+ },
+ }),
};
};
diff --git a/packages/grafana-ui/src/components/Combobox/useComboboxFloat.ts b/packages/grafana-ui/src/components/Combobox/useComboboxFloat.ts
index e2f7e3ac851..3a72680203f 100644
--- a/packages/grafana-ui/src/components/Combobox/useComboboxFloat.ts
+++ b/packages/grafana-ui/src/components/Combobox/useComboboxFloat.ts
@@ -4,15 +4,17 @@ import { useMemo, useRef, useState } from 'react';
import { measureText } from '../../utils';
import { ComboboxOption } from './Combobox';
-import { MENU_ITEM_FONT_SIZE, MENU_ITEM_FONT_WEIGHT, MENU_ITEM_PADDING_X } from './getComboboxStyles';
+import {
+ MENU_ITEM_FONT_SIZE,
+ MENU_ITEM_FONT_WEIGHT,
+ MENU_ITEM_PADDING,
+ MENU_OPTION_HEIGHT,
+ POPOVER_MAX_HEIGHT,
+} from './getComboboxStyles';
// Only consider the first n items when calculating the width of the popover.
const WIDTH_CALCULATION_LIMIT_ITEMS = 100_000;
-// Used with Downshift to get the height of each item
-export const OPTION_HEIGHT = 45;
-const POPOVER_MAX_HEIGHT = OPTION_HEIGHT * 8.5;
-
// Clearance around the popover to prevent it from being too close to the edge of the viewport
const POPOVER_PADDING = 16;
@@ -41,7 +43,7 @@ export const useComboboxFloat = (
const preferredMaxHeight = availableHeight - POPOVER_PADDING;
const width = Math.max(preferredMaxWidth, 0);
- const height = Math.min(Math.max(preferredMaxHeight, OPTION_HEIGHT * 6), POPOVER_MAX_HEIGHT);
+ const height = Math.min(Math.max(preferredMaxHeight, MENU_OPTION_HEIGHT * 6), POPOVER_MAX_HEIGHT);
setPopoverMaxSize({ width, height });
},
@@ -68,7 +70,7 @@ export const useComboboxFloat = (
const size = measureText(longestItem, MENU_ITEM_FONT_SIZE, MENU_ITEM_FONT_WEIGHT).width;
- return size + MENU_ITEM_PADDING_X * 2 + scrollbarWidth;
+ return size + MENU_ITEM_PADDING * 2 + scrollbarWidth;
}, [items, scrollbarWidth]);
const floatStyles = {
diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx
index ac775afbe97..541eb9c8db8 100644
--- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx
+++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx
@@ -32,6 +32,7 @@ interface Props {
/**
* Wraps component into component from `react-custom-scrollbars`
+ * @deprecated Use `ScrollContainer` from `@grafana/ui` instead. It uses native scrollbars and has a simpler API.
*/
export const CustomScrollbar = ({
autoHide = false,
diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/CalendarBody.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/CalendarBody.tsx
index a70611dd109..e5e835dd188 100644
--- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/CalendarBody.tsx
+++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/CalendarBody.tsx
@@ -1,7 +1,6 @@
import { css } from '@emotion/css';
import { useCallback } from 'react';
-import Calendar from 'react-calendar';
-import { CalendarType } from 'react-calendar/dist/cjs/shared/types';
+import Calendar, { CalendarType } from 'react-calendar';
import { GrafanaTheme2, dateTimeParse, DateTime, TimeZone } from '@grafana/data';
diff --git a/packages/grafana-ui/src/components/Input/AutoSizeInput.test.tsx b/packages/grafana-ui/src/components/Input/AutoSizeInput.test.tsx
index f3372a6045d..148f35c6af3 100644
--- a/packages/grafana-ui/src/components/Input/AutoSizeInput.test.tsx
+++ b/packages/grafana-ui/src/components/Input/AutoSizeInput.test.tsx
@@ -58,6 +58,23 @@ describe('AutoSizeInput', () => {
expect(getComputedStyle(inputWrapper).width).toBe('304px');
});
+ it('should use placeholder for width if input is empty', () => {
+ render();
+
+ const inputWrapper: HTMLDivElement = screen.getByTestId('input-wrapper');
+ expect(getComputedStyle(inputWrapper).width).toBe('304px');
+ });
+
+ it('should use value for width even with a placeholder', () => {
+ render();
+
+ const input: HTMLInputElement = screen.getByTestId('autosize-input');
+ const inputWrapper: HTMLDivElement = screen.getByTestId('input-wrapper');
+
+ fireEvent.change(input, { target: { value: 'very very long value' } });
+ expect(getComputedStyle(inputWrapper).width).toBe('304px');
+ });
+
it('should call onBlur if set when blurring', () => {
const onBlur = jest.fn();
const onCommitChange = jest.fn();
diff --git a/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx b/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx
index 7d8eccee748..b6899a4caa3 100644
--- a/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx
+++ b/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx
@@ -24,6 +24,7 @@ export const AutoSizeInput = React.forwardRef((props, r
onKeyDown,
onBlur,
value: controlledValue,
+ placeholder,
...restProps
} = props;
// Initialize internal state
@@ -36,13 +37,16 @@ export const AutoSizeInput = React.forwardRef((props, r
// Update input width when `value`, `minWidth`, or `maxWidth` change
const inputWidth = useMemo(() => {
- const valueString = typeof value === 'string' ? value : value.toString();
+ const displayValue = value || placeholder || '';
+ const valueString = typeof displayValue === 'string' ? displayValue : displayValue.toString();
+
return getWidthFor(valueString, minWidth, maxWidth);
- }, [value, minWidth, maxWidth]);
+ }, [placeholder, value, minWidth, maxWidth]);
return (
{
diff --git a/packages/grafana-ui/src/components/ScrollContainer/ScrollContainer.mdx b/packages/grafana-ui/src/components/ScrollContainer/ScrollContainer.mdx
new file mode 100644
index 00000000000..a9b0213ea5e
--- /dev/null
+++ b/packages/grafana-ui/src/components/ScrollContainer/ScrollContainer.mdx
@@ -0,0 +1,65 @@
+import { Meta, Preview, ArgTypes } from '@storybook/blocks';
+import { ScrollContainer } from './ScrollContainer';
+
+
+
+# ScrollContainer
+
+This component is used to create a scrollable container. It uses native scrollbars, has an option to show scroll indicators, and supports most `Box` properties.
+
+## Migrating from CustomScrollbar
+
+ScrollContainer is intended to be a functionally equivalent replacement for CustomScrollbar. If you're not using any additional props in CustomScrollbar, it should be a drop-in replacement.
+
+If you were using additional props, there are roughly 3 categories of changes:
+
+1. Props that have been **renamed**
+2. Props that have been removed but the **same behavior can be achieved in a different way**
+3. Props that have been removed and are **no longer supported**
+
+### Props that have been renamed
+
+- `autoHeightMin` is now `minHeight`
+- `autoHeightMax` is now `maxHeight`
+- `divId` is now `id`
+- `testId` is now `data-testid`
+- `scrollRefCallback` is now `ref`
+
+### Props that have been removed but the same behavior can be achieved in a different way
+
+- `hideHorizontalTrack` and `hideVerticalTrack` have been removed.
+ - You can achieve the same behavior by setting either `overflowX` or `overflowY` to `hidden`. These names more closely match the CSS properties they represent.
+- `scrollTop` and `setScrollTop`.
+
+ - You can achieve the same behavior by using the `ref` prop to get a reference to the `ScrollContainer` component, and then calling `scrollTo` on that reference.
+ - Before:
+
+ ```tsx
+ const [scrollTop, setScrollTop] = useState(0);
+
+ return // Your amazing scrolling content;
+ ```
+
+ - After:
+
+ ```tsx
+ const [scrollTop, setScrollTop] = useState(0);
+ const scrollRef = useRef(null);
+
+ useEffect(() => {
+ scrollRef.current?.scrollTo(0, scrollTop);
+ }, [scrollTop]);
+
+ return // Your amazing scrolling content;
+ ```
+
+### Props that have been removed and are no longer supported
+
+- `autoHide`, `autoHideTimeout` and `hideTracksWhenNotNeeded`.
+ - These props no longer make sense when using native scrollbars, since some operating systems (Windows for example) don't have overlay scrollbars.
+- `className`
+ - Like our other design system components, the intention is that you'll never need to write custom CSS for this component. We extend all the base `Box` props, so everything _should_ be available as a prop.
+- `updateAfterMountMs`
+ - This wasn't very react-y, and could cause inconsistent behaviour.
+
+
diff --git a/packages/grafana-ui/src/components/ScrollContainer/ScrollContainer.story.tsx b/packages/grafana-ui/src/components/ScrollContainer/ScrollContainer.story.tsx
new file mode 100644
index 00000000000..a633b347cf0
--- /dev/null
+++ b/packages/grafana-ui/src/components/ScrollContainer/ScrollContainer.story.tsx
@@ -0,0 +1,93 @@
+import { action } from '@storybook/addon-actions';
+import { Meta, StoryFn } from '@storybook/react';
+
+import { ScrollContainer } from './ScrollContainer';
+import mdx from './ScrollContainer.mdx';
+
+const meta: Meta = {
+ title: 'General/Layout/ScrollContainer',
+ component: ScrollContainer,
+ parameters: {
+ controls: {
+ exclude: ['onScroll'],
+ },
+ docs: {
+ page: mdx,
+ },
+ },
+ argTypes: {},
+ args: {
+ onScroll: action('onScroll'),
+ height: 50,
+ overflowX: 'auto',
+ overflowY: 'auto',
+ scrollbarWidth: 'thin',
+ showScrollIndicators: true,
+ width: 50,
+ },
+};
+
+export const Basic: StoryFn = (args) => {
+ return (
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ laborum.
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ laborum.
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ laborum.
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ laborum.
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ laborum.
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ laborum.
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ laborum.
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+ laborum.
+