From 6fd7dbac14b151e1cc4ffb28a6d075aec5f5d069 Mon Sep 17 00:00:00 2001 From: Mahendra Paipuri <44365948+mahendrapaipuri@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:33:37 +0200 Subject: [PATCH] Setup access control (#86) * Use authz client to check perms on dashboard * Update playwright config. Use browser sessions in playwright tests. Add tests as normal user as well * Fix frontend config update * JWT typ header checks are verified only when present. We will use our own fork of authlib to ensure that JWT verification works across versions of Grafana. Upstream declined to merge the changes into authlib. * Use test specific snapshots as there are subtle differences between different tests --------- Signed-off-by: Mahendra Paipuri --- .ci/docker-compose.yaml | 4 +- .../alternative.pdf | Bin 0 -> 40952 bytes .../default.pdf | Bin 0 -> 42235 bytes .../alternative.pdf | Bin 39174 -> 39174 bytes .../default.pdf | Bin 40667 -> 40667 bytes .../alternative.pdf | Bin 0 -> 40270 bytes .../default.pdf | Bin 41517 -> 41517 bytes .../alternative.pdf | Bin 40078 -> 40078 bytes .../default.pdf | Bin 0 -> 41517 bytes .github/workflows/step_e2e-tests.yml | 39 ++- docker-compose.yaml | 2 +- go.mod | 12 +- go.sum | 52 +++- pkg/plugin/app.go | 13 +- pkg/plugin/resources.go | 250 ++++++++++++++++-- pkg/plugin/resources_test.go | 6 +- playwright.config.ts | 49 +++- src/README.md | 55 ++-- src/components/AppConfig/AppConfig.tsx | 34 ++- src/pages/Status/Status.tsx | 6 +- src/plugin.json | 3 +- tests/appConfig.spec.ts | 5 + tests/appReport.spec.ts | 40 +-- tests/setup/auth.setup.ts | 42 +++ 24 files changed, 492 insertions(+), 120 deletions(-) create mode 100644 .ci/reports/local-chrome-10.3.0-with-features/alternative.pdf create mode 100644 .ci/reports/local-chrome-10.3.0-with-features/default.pdf rename .ci/reports/{local-chrome => local-chrome-10.4.5-with-features}/alternative.pdf (99%) rename .ci/reports/{local-chrome => local-chrome-10.4.5-with-features}/default.pdf (99%) create mode 100644 .ci/reports/remote-chrome-10.4.7-without-features/alternative.pdf rename .ci/reports/{remote-chrome => remote-chrome-10.4.7-without-features}/default.pdf (99%) rename .ci/reports/{remote-chrome => remote-chrome-11.1.0-with-features}/alternative.pdf (99%) create mode 100644 .ci/reports/remote-chrome-11.1.0-with-features/default.pdf create mode 100644 tests/setup/auth.setup.ts diff --git a/.ci/docker-compose.yaml b/.ci/docker-compose.yaml index 7305b0e..0beee56 100644 --- a/.ci/docker-compose.yaml +++ b/.ci/docker-compose.yaml @@ -34,7 +34,7 @@ services: # - GF_AUTH_DISABLE_LOGIN_FORM=true # We need to toggle external service accounts so that Grafana will get # the token from a service account to read dashboards - - GF_FEATURE_TOGGLES_ENABLE=${GF_FEATURE_TOGGLES_ENABLE:-externalServiceAccounts} + - GF_FEATURE_TOGGLES_ENABLE=${GF_FEATURE_TOGGLES_ENABLE:-accessControlOnCall,idForwarding,externalServiceAccounts} # disable alerting because it vomits logs - GF_ALERTING_ENABLED=false - GF_UNIFIED_ALERTING_ENABLED=false @@ -95,7 +95,7 @@ services: # - GF_AUTH_DISABLE_LOGIN_FORM=true # We need to toggle external service accounts so that Grafana will get # the token from a service account to read dashboards - - GF_FEATURE_TOGGLES_ENABLE=${GF_FEATURE_TOGGLES_ENABLE:-externalServiceAccounts} + - GF_FEATURE_TOGGLES_ENABLE=${GF_FEATURE_TOGGLES_ENABLE:-accessControlOnCall,idForwarding,externalServiceAccounts} # disable alerting because it vomits logs - GF_ALERTING_ENABLED=false - GF_UNIFIED_ALERTING_ENABLED=false diff --git a/.ci/reports/local-chrome-10.3.0-with-features/alternative.pdf b/.ci/reports/local-chrome-10.3.0-with-features/alternative.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8195a755a2504d07e88ca21bf65be8cda371d0b2 GIT binary patch literal 40952 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 E0J&)oZU6uP diff --git a/.ci/reports/local-chrome/default.pdf b/.ci/reports/local-chrome-10.4.5-with-features/default.pdf similarity index 99% rename from .ci/reports/local-chrome/default.pdf rename to .ci/reports/local-chrome-10.4.5-with-features/default.pdf index d389cf89af9711e52e654d582ebff3cf8c64b24c..c94ee4ec3e48c1caa4b5d51aa473672596b9b3f1 100644 GIT binary patch delta 57 zcmcb;m+AIirU~^r7DkqaCI%LUmf8je>OiE)rSF@c;*waBs-WRwWn^Fkl!PhSxY}SI E0P!jiMF0Q* delta 57 zcmcb;m+AIirU~^rriK=VMkdClX4(b@>OiE)rSF@c;*waBs-WRwWn^GvVgOgNakarb E0Pmp?Hvj+t diff --git a/.ci/reports/remote-chrome-10.4.7-without-features/alternative.pdf b/.ci/reports/remote-chrome-10.4.7-without-features/alternative.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a0982e1f10007e092eeb65814186f0745507e511 GIT binary patch literal 40270 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 CeldOk literal 0 HcmV?d00001 diff --git a/.ci/reports/remote-chrome/default.pdf b/.ci/reports/remote-chrome-10.4.7-without-features/default.pdf similarity index 99% rename from .ci/reports/remote-chrome/default.pdf rename to .ci/reports/remote-chrome-10.4.7-without-features/default.pdf index 0dd76a6804cdb8666fa983809f77dfb25b9860f3..c88ac013c44db9fb71a8a84fc8dbeab4e114784b 100644 GIT binary patch delta 81 zcmZ2`glX*&rU_{-7Mfi8&PAz-C7JnoE{P?n3K}j}Mg~SE1{Ow^hQ_8AhL+j}2I@cr PROg$Yf>p`JRm}?lp`JRm}?l;SU!H diff --git a/.ci/reports/remote-chrome/alternative.pdf b/.ci/reports/remote-chrome-11.1.0-with-features/alternative.pdf similarity index 99% rename from .ci/reports/remote-chrome/alternative.pdf rename to .ci/reports/remote-chrome-11.1.0-with-features/alternative.pdf index 3e584dd4373bbdf9f58098e438d016f88d5ffb94..074a97f967cd3d4eff0e951d192fc1584e4aeaa0 100644 GIT binary patch delta 82 zcmeC%$<()#X+oN-g(jE2b5UwyNoIbYOJYf?f`*Hgk%5tkfrXK!p^2fffu**AfjSTY P)%oV9U{|tn_2bz9-scxo delta 82 zcmeC%$<()#X+oN-nI@OMb5UwyNoIbYOJYf?f`*Hgk%5tkfrWvEp`o#%iIKK}fjSTY P)%oV9U{|tn_2bz9+>94M diff --git a/.ci/reports/remote-chrome-11.1.0-with-features/default.pdf b/.ci/reports/remote-chrome-11.1.0-with-features/default.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2070de991f233ce00f3cb8eb482c935917e91631 GIT binary patch literal 41517 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 10.4.4 check for feature toggles + if cfg.FeatureToggles().IsEnabled(accessControlFeatureFlag) && cfg.FeatureToggles().IsEnabled(idForwardingFlag) { + return true + } + + return false +} + +// grafanaAppURL returns the Grafana's App URL. User configured URL has higher +// precedence than the App URL in the request's context +func (app *App) grafanaAppURL(grafanaConfig *backend.GrafanaCfg) (string, error) { + var grafanaAppURL string + + var err error + + if app.conf.AppURL != "" { + grafanaAppURL = app.conf.AppURL + } else { + grafanaAppURL, err = grafanaConfig.AppURL() + if err != nil { + return "", err + } + } + + return strings.TrimSuffix(grafanaAppURL, "/"), nil +} + +// HasAccess verifies if the current request context has access to certain action +func (app *App) HasAccess(req *http.Request, action string, resource authz.Resource) (bool, error) { + // Retrieve the id token + idToken := req.Header.Get(GrafanaUserSignInTokenHeaderName) + if idToken == "" { + return false, errors.New("id token not found") + } + + authzClient, err := app.GetAuthZClient(req) + if err != nil { + return false, err + } + + // Check user access + hasAccess, err := authzClient.HasAccess(req.Context(), idToken, action, resource) + if err != nil || !hasAccess { + return false, err + } + + return true, nil +} + +// GetAuthZClient returns an authz enforcement client configured thanks to the plugin context. +func (app *App) GetAuthZClient(req *http.Request) (authz.EnforcementClient, error) { + ctx := req.Context() + ctxLogger := log.DefaultLogger.FromContext(ctx) + + // Prevent two concurrent calls from updating the client + app.mx.Lock() + defer app.mx.Unlock() + + grafanaConfig := backend.GrafanaConfigFromContext(req.Context()) + + grafanaAppURL, err := app.grafanaAppURL(grafanaConfig) + if err != nil { + ctxLogger.Error("failed to get app URL", "err", err) + + return nil, err + } + + saToken, err := grafanaConfig.PluginAppClientSecret() + if err != nil || (saToken == "" && app.conf.Token == "") { + if err == nil { + err = errors.New("neither service account token nor configured token found") + } + + ctxLogger.Error("failed to fetch service account and configured token", "error", err) + + return nil, err + } else { + if saToken == "" { + saToken = app.conf.Token + } + } + + if saToken == app.saToken { + ctxLogger.Debug("token unchanged returning existing authz client") + + return app.authzClient, nil + } + + // Header "typ" has been added only in Grafana 11.1.0 (https://github.com/grafana/grafana/pull/87430) + // So this check will fail for Grafana < 11.1.0 + // Set VerifierConfig{DisableTypHeaderCheck: true} for those cases + var disableTypHeaderCheck = false + if semver.Compare(app.grafanaSemVer, "v11.1.0") == -1 { + disableTypHeaderCheck = true + } + + // Initialize the authorization client + client, err := authz.NewEnforcementClient(authz.Config{ + APIURL: grafanaAppURL, + Token: saToken, + // Grafana is signing the JWTs on local setups + JWKsURL: grafanaAppURL + "/api/signing-keys/keys", + }, + // Use the configured HTTP client + authz.WithHTTPClient(app.httpClient), + // Configure verifier + authz.WithVerifier(authn.NewVerifier[authz.CustomClaims](authn.VerifierConfig{ + DisableTypHeaderCheck: disableTypHeaderCheck, + }, + authn.TokenTypeID, + authn.NewKeyRetriever(authn.KeyRetrieverConfig{ + SigningKeysURL: grafanaAppURL + "/api/signing-keys/keys", + }, + authn.WithHTTPClientKeyRetrieverOpt(app.httpClient)), + )), + // Fetch all the user permission prefixed with dashboards + authz.WithSearchByPrefix("dashboards"), + // Use a cache with a lower expiry time + authz.WithCache(cache.NewLocalCache(cache.Config{ + Expiry: 10 * time.Second, + CleanupInterval: 5 * time.Second, + })), + ) + if err != nil { + ctxLogger.Error("failed to initialize authz client", "err", err) + + return nil, err + } + + app.authzClient = client + app.saToken = saToken + + return client, nil +} + // handleReport handles creating a PDF report from a given dashboard UID // GET /api/plugins/mahendrapaipuri-dashboardreporter-app/resources/report func (app *App) handleReport(w http.ResponseWriter, req *http.Request) { @@ -70,6 +235,38 @@ func (app *App) handleReport(w http.ResponseWriter, req *http.Request) { grafanaConfig := backend.GrafanaConfigFromContext(req.Context()) + // Get Grafana App URL by looking both at passed config and user defined config + grafanaAppURL, err := app.grafanaAppURL(grafanaConfig) + if err != nil { + ctxLogger.Error("failed to get app URL", "err", err) + http.Error(w, "failed to get app URL", http.StatusInternalServerError) + + return + } + + // If the required feature flags are enabled, check if user has access to the resource + // using authz client. + // Here we check if user has permissions to do an action "dashboards:read" on + // dashboards resource of a given dashboard UID + if app.featureTogglesEnabled(req.Context()) { + if hasAccess, err := app.HasAccess( + req, "dashboards:read", + authz.Resource{ + Kind: "dashboards", + Attr: "uid", + ID: dashboardUID, + }, + ); err != nil || !hasAccess { + if err != nil { + ctxLogger.Error("failed to check permissions", "err", err) + } + + http.Error(w, "permission denied", http.StatusForbidden) + + return + } + } + if req.URL.Query().Has("theme") { conf.Theme = req.URL.Query().Get("theme") if conf.Theme != "light" && conf.Theme != "dark" { @@ -144,21 +341,7 @@ func (app *App) handleReport(w http.ResponseWriter, req *http.Request) { ctxLogger.Info(fmt.Sprintf("generate report using config: %s", conf.String())) - var grafanaAppURL string - if conf.AppURL != "" { - grafanaAppURL = conf.AppURL - } else { - grafanaAppURL, err = grafanaConfig.AppURL() - if err != nil { - ctxLogger.Error("failed to get app URL", "err", err) - http.Error(w, "failed to get app URL", http.StatusInternalServerError) - - return - } - } - - grafanaAppURL = strings.TrimSuffix(grafanaAppURL, "/") - + // credential is header name value pair that will be used in API requests var credential client.Credential switch { @@ -167,30 +350,36 @@ func (app *App) handleReport(w http.ResponseWriter, req *http.Request) { // made changes to not forward the cookies to app plugins. // So we will not be able to use cookies to make requests to Grafana to fetch // dashboards. - // - // So we need to rely on either service accounts or user provided API tokens to - // make requests to Grafana case req.Header.Get(backend.CookiesHeaderName) != "": + ctxLogger.Debug("using user cookie") + credential = client.Credential{ HeaderName: backend.CookiesHeaderName, HeaderValue: req.Header.Get(backend.CookiesHeaderName), } - case req.Header.Get(backend.OAuthIdentityTokenHeaderName) != "": + case conf.Token != "": + ctxLogger.Debug("using user configured token") + credential = client.Credential{ HeaderName: backend.OAuthIdentityTokenHeaderName, - HeaderValue: req.Header.Get(backend.OAuthIdentityTokenHeaderName), + HeaderValue: "Bearer " + conf.Token, } default: + ctxLogger.Debug("using service account token") + saToken, err := grafanaConfig.PluginAppClientSecret() if err != nil { - if conf.Token == "" { - ctxLogger.Error("failed to get plugin app client secret", "err", err) - http.Error(w, "failed to get plugin app client secret", http.StatusInternalServerError) + ctxLogger.Error("failed to get plugin app client secret", "err", err) + http.Error(w, "error generating report", http.StatusInternalServerError) - return - } + return + } + + if saToken == "" { + ctxLogger.Error("failed to get plugin app client secret", "err", "empty client secret") + http.Error(w, "error generating report", http.StatusInternalServerError) - saToken = conf.Token + return } credential = client.Credential{ @@ -220,6 +409,7 @@ func (app *App) handleReport(w http.ResponseWriter, req *http.Request) { credential, variables, ) + ctxLogger.Info(fmt.Sprintf("generate report using %s chrome", app.chromeInstance.Name())) // Make app new Report to put all PNGs into app HTML template and print it into app PDF @@ -243,9 +433,6 @@ func (app *App) handleReport(w http.ResponseWriter, req *http.Request) { return } - // Add PDF file name to Header - addFilenameHeader(w, pdfReport.Title()) - // Generate report if err = pdfReport.Generate(req.Context(), w); err != nil { ctxLogger.Error("error generating report", "err", err) @@ -254,14 +441,19 @@ func (app *App) handleReport(w http.ResponseWriter, req *http.Request) { return } + // Add PDF file name to Header + addFilenameHeader(w, pdfReport.Title()) + ctxLogger.Info("report generated", "user", currentUser, "dash_uid", dashboardUID) } // handleHealth is an example HTTP GET resource that returns an OK response. func (app *App) handleHealth(w http.ResponseWriter, _ *http.Request) { w.Header().Add("Content-Type", "text/plan") + if _, err := w.Write([]byte("OK")); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) + return } diff --git a/pkg/plugin/resources_test.go b/pkg/plugin/resources_test.go index 41fb67f..120e8a7 100755 --- a/pkg/plugin/resources_test.go +++ b/pkg/plugin/resources_test.go @@ -29,6 +29,7 @@ func (s *mockCallResourceResponseSender) Send(response *backend.CallResourceResp // Test report resource func TestReportResource(t *testing.T) { var execPath string + locations := []string{ // Mac "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", @@ -52,7 +53,7 @@ func TestReportResource(t *testing.T) { if execPath == "" { t.Skip("Chrome not found. Skipping test") } - + // Initialize app inst, err := NewDashboardReporterApp(context.Background(), backend.AppInstanceSettings{ DecryptedSecureJSONData: map[string]string{ @@ -62,9 +63,11 @@ func TestReportResource(t *testing.T) { if err != nil { t.Fatalf("new app: %s", err) } + if inst == nil { t.Fatal("inst must not be nil") } + app, ok := inst.(*App) if !ok { t.Fatal("inst must be of type *App") @@ -102,6 +105,7 @@ func TestReportResource(t *testing.T) { Method: http.MethodGet, Path: "report?dashUid=testDash", }, &r) + So(repDashName, ShouldEqual, "testDash") }) diff --git a/playwright.config.ts b/playwright.config.ts index 83a0e85..0da36b8 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -10,10 +10,6 @@ const pluginE2eAuth = `${dirname(require.resolve("@grafana/plugin-e2e"))}/auth`; */ // require('dotenv').config(); -// Auth header -const btoa = (str: string) => Buffer.from(str).toString("base64"); -const credentialsBase64 = btoa(`admin:admin`); - /** * See https://playwright.dev/docs/test-configuration. */ @@ -41,8 +37,13 @@ export default defineConfig({ ignoreHTTPSErrors: true, /* Auth */ - extraHTTPHeaders: { - Authorization: `Basic ${credentialsBase64}`, + grafanaAPICredentials: { + user: "admin", + password: "admin", + }, + httpCredentials: { + username: "admin", + password: "admin", }, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ @@ -52,21 +53,53 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ - // Plain without TLS + // Login to Grafana with admin user and store the cookie on disk for use in other tests + { + name: "authenticate", + testDir: "./tests/setup", + testMatch: [/.*auth\.setup\.ts/], + use: { + baseURL: `http://localhost:3080`, + user: { + user: "admin", + password: "admin", + }, + }, + }, + // Login to Grafana with new user with viewer role and store the cookie on disk for use in other tests + { + name: "createUserAndAuthenticate", + testDir: "./tests/setup", + testMatch: [/.*auth\.setup\.ts/], + use: { + baseURL: `https://localhost:3443`, + user: { + user: "viewer", + password: "password", + role: "Viewer", + }, + }, + }, + // Plain without TLS and using admin user { name: "plain", use: { ...devices["Desktop Chrome"], baseURL: `http://localhost:3080`, + storageState: "playwright/.auth/admin.json", }, + dependencies: ["authenticate"], }, - // With TLS + // With TLS and using user with Viewer role { name: "tls", + testIgnore: /appConfig\.spec\.ts/, use: { ...devices["Desktop Chrome"], baseURL: `https://localhost:3443`, + storageState: "playwright/.auth/viewer.json", }, + dependencies: ["createUserAndAuthenticate"], }, ], }); diff --git a/src/README.md b/src/README.md index 2b5badd..0d74433 100755 --- a/src/README.md +++ b/src/README.md @@ -93,6 +93,24 @@ logger=plugin.loader t=2024-03-21T11:16:54.738166325Z level=info msg="Plugin reg ``` +The plugin depends on following features flags and it is **strongly** recommended to enable +them on Grafana server. + +- `accessControlOnCall`: Available in `Grafana >= 10.4.0` +- `idForwarding`: Available in `Grafana >= 10.4.0` +- `externalServiceAccounts`: Available in `Grafana >= 10.3.0` + +This can be done using `feature_toggles` section of Grafana as follows: + +```ini +[feature_toggles] +enable = accessControlOnCall,idForwarding,externalServiceAccounts +``` + +The plugin can work without enabling any of the above feature flags for `Grafana <= 10.4.3`. +However, for `Grafana > 10.4.4`, feature `externalServiceAccounts` must be enabled for +the plugin to work. + ### Install with Docker-compose There is a docker compose file provided in the repo. Create a directory `dist` in the @@ -275,9 +293,9 @@ extra configuration to get an API token from Grafana. - `Grafana > 10.4.3`: For these Grafana deployments, the plugin needs an API token from Grafana to make API requests to Grafana. This can be done automatically by enabling feature flag `externalServiceAccounts`, which will create a service account and - provision a service account token automatically for the plugin. To enable this feature, - it is necessary to set `enable = externalServiceAccounts` in `feature_toggles` section - of Grafana configuration. + provision a service account token automatically for the plugin. Please consult + [Local Installation](#local-installation) on how to configure the feature flags on + Grafana server. > [!NOTE] > If the operators do not wish or cannot use `externalServiceAccounts` feature flag on @@ -341,24 +359,19 @@ any HTTP client of your favorite programming language. ## Security -### `Grafana <= 10.4.3` - -When reports are generated from browser, there is minimal to no security risks as the -plugin forward the current Grafana cookie in the request to make API requests to other -Grafana resources. This ensures that user will not be able to generate reports for -the dashboards that contains data sources that they do not have permissions to query. The -plugin _always_ prioritizes the cookie for authentication when found. Disabling basic auth -for Grafana will force the users to generate reports from browser, thus forcing them to -use cookie for authentication. +All the feature flags listed in the [Local Installation](#local-installation) section +must be enabled on Grafana server for secure operation of your Grafana instance. +These feature flags enables the plugin to verify +the if the user who is making the request to generate the report has +enough permissions to view the dashboard before generating the report. +The plugin _always_ prioritizes the cookie for authentication when found. -### `Grafana > 10.4.3` + ## Examples @@ -388,6 +401,10 @@ error messages will be as follows: the host. In that case, we advise to install `chromium` on the machine which will install all the dependent libraries. +- If you get `permission denied` response when generating a report, it is due to the + user not having `View` permissions on the dashboard that they are attempting to generate + the report. + ## Development See [DEVELOPMENT.md](https://github.com/mahendrapaipuri/grafana-dashboard-reporter-app/blob/main/DEVELOPMENT.md) diff --git a/src/components/AppConfig/AppConfig.tsx b/src/components/AppConfig/AppConfig.tsx index 5ccf327..80f219e 100755 --- a/src/components/AppConfig/AppConfig.tsx +++ b/src/components/AppConfig/AppConfig.tsx @@ -18,7 +18,7 @@ import { PluginMeta, GrafanaTheme2, } from "@grafana/data"; -import { FetchResponse, getBackendSrv } from "@grafana/runtime"; +import { getBackendSrv } from "@grafana/runtime"; import { testIds } from "../testIds"; export type JsonData = { @@ -86,6 +86,8 @@ type State = { isSaTokenSet: boolean; // A Service account's token used to make requests to Grafana API. saToken: string; + // If the service has been reset + isSaTokenReset: boolean; }; interface Props extends PluginConfigPageProps> {} @@ -118,10 +120,9 @@ export const AppConfig = ({ plugin }: Props) => { remoteChromeUrlChanged: false, saToken: "", isSaTokenSet: Boolean(secureJsonFields?.saToken), + isSaTokenReset: false, }); - console.log("QQQQ", jsonData); - const themeOptions = [ { label: "Light", value: "light" }, { label: "Dark", value: "dark" }, @@ -235,6 +236,7 @@ export const AppConfig = ({ plugin }: Props) => { ...state, saToken: "", isSaTokenSet: false, + isSaTokenReset: true, }); const onChangeSaToken = (event: ChangeEvent) => { @@ -576,8 +578,8 @@ export const AppConfig = ({ plugin }: Props) => { }) } disabled={Boolean( - !state.appUrl && - !state.skipTlsCheck && + !state.appUrlChanged && + !state.skipTlsCheckChanged && !state.themeChanged && !state.layoutChanged && !state.orientationChanged && @@ -586,7 +588,8 @@ export const AppConfig = ({ plugin }: Props) => { !state.logoChanged && !state.maxBrowserWorkersChanged && !state.maxRenderWorkersChanged && - !state.remoteChromeUrl && + !state.remoteChromeUrlChanged && + !state.isSaTokenReset && !state.saToken )} > @@ -618,12 +621,19 @@ const getStyles = (theme: GrafanaTheme2) => ({ }), }); -const updatePluginAndReload = async (pluginId: string, data: Partial>) => { - await updatePlugin(pluginId, data).then((response: FetchResponse) => { - if (!response.ok) { - throw response.data; - } - }); +const updatePluginAndReload = async ( + pluginId: string, + data: Partial> +) => { + try { + await updatePlugin(pluginId, data); + + // Reloading the page as the changes made here wouldn't be propagated to the actual plugin otherwise. + // This is not ideal, however unfortunately currently there is no supported way for updating the plugin state. + window.location.reload(); + } catch (e) { + console.error("Error while updating the plugin", e); + } }; export const updatePlugin = async (pluginId: string, data: Partial) => { diff --git a/src/pages/Status/Status.tsx b/src/pages/Status/Status.tsx index 32cdc50..0d3ee10 100755 --- a/src/pages/Status/Status.tsx +++ b/src/pages/Status/Status.tsx @@ -3,7 +3,7 @@ import { getBackendSrv, PluginPage } from "@grafana/runtime"; import { PageLayoutType } from "@grafana/data"; import { testIds } from "../../components/testIds"; import { useAsync } from "react-use"; -import { Badge, HorizontalGroup, LinkButton } from "@grafana/ui"; +import { Badge, Stack, LinkButton } from "@grafana/ui"; // import { prefixRoute } from "../../utils/utils.routing"; import { ROUTES, NAVIGATION } from "../../constants"; @@ -38,12 +38,12 @@ export const Status = () => { return (
- +

Plugin Health Check

{" "} {renderHealth(health?.message)} -
+
Only users with Admin role can modify the configuration of the plugin
diff --git a/src/plugin.json b/src/plugin.json index 9e10dc5..6e676fa 100644 --- a/src/plugin.json +++ b/src/plugin.json @@ -58,7 +58,8 @@ "permissions": [ { "action": "dashboards:read", "scope": "folders:uid:*" }, { "action": "annotations:read", "scope": "annotations:type:*" }, - { "action": "datasources:query", "scope": "datasources:*" } + { "action": "datasources:query", "scope": "datasources:*" }, + { "action": "users.permissions:read", "scope": "users:*" } ] } } diff --git a/tests/appConfig.spec.ts b/tests/appConfig.spec.ts index cc443e8..b56c093 100644 --- a/tests/appConfig.spec.ts +++ b/tests/appConfig.spec.ts @@ -32,4 +32,9 @@ test("should be possible to save app configuration", async ({ await saveButton.click(); await expect(saveResponse).toBeOK(); + + // Reset the configured token and save settings + await resetButton.click(); + await saveButton.click(); + await expect(saveResponse).toBeOK(); }); diff --git a/tests/appReport.spec.ts b/tests/appReport.spec.ts index 4c9f5b7..c14e1fe 100644 --- a/tests/appReport.spec.ts +++ b/tests/appReport.spec.ts @@ -7,38 +7,24 @@ import pluginJson from "../src/plugin.json"; const dashboardUid = "fdlwjnyim1la8f"; const queryParams = `from=now-5m&to=now&var-testvar0=All&var-testvar1=foo&var-testvar2=1&layout=grid&excludePanelID=5&dashboardMode=full`; -test("should be possible to generate report", async ({ request }) => { +test("should be possible to generate report", async ({ request }, testInfo) => { // Set larger timeout test.setTimeout(60000); const report = await request.get( `/api/plugins/${pluginJson.id}/resources/report?dashUid=${dashboardUid}&${queryParams}` ); - expect(report.ok()).toBeTruthy(); - // const browser = await chromium.launch({ - // headless: true, - // acceptDownloads: true, - // AlwaysOpenPdfExternally: true, - // }); - // const context = await browser.newContext(); - // const page = await context.newPage(); - - // // let promise = page.waitForEvent("download"); - // // try { - // // await page.goto( - // // `/api/plugins/${pluginJson.id}/resources/report?dashUid=${dashboardUid}&${queryParams}`, - // // { waitUntil: "networkidle" } - // // ); - // // } catch (e) {} - // // let download = await promise; - // await page.goto( - // `/api/plugins/${pluginJson.id}/resources/report?dashUid=${dashboardUid}&${queryParams}`, - // { waitUntil: "networkidle" } - // ); - - // const imageName = "launcher-category.png"; - // expect(await page.screenshot()).toMatchSnapshot(imageName.toLowerCase()); - - // await browser.close(); + // TLS case will attempt to create a report by a user without View permission + // on dashboard except for the case where Grafana 10.4.7 and not using appropriate + // feature toogles. In the exceptional case, the test should pass + if ( + testInfo.project.name === "tls" && + process.env.GRAFANA_VERSION !== "10.4.7" && + process.env.GF_FEATURE_TOGGLES_ENABLE !== "externalServiceAccounts" + ) { + expect(report.ok()).toBeFalsy(); + } else { + expect(report.ok()).toBeTruthy(); + } }); diff --git a/tests/setup/auth.setup.ts b/tests/setup/auth.setup.ts new file mode 100644 index 0000000..88d5748 --- /dev/null +++ b/tests/setup/auth.setup.ts @@ -0,0 +1,42 @@ +import { test as setup } from "@grafana/plugin-e2e"; + +// Auth header +const btoa = (str: string) => Buffer.from(str).toString("base64"); + +setup( + "authenticate", + async ({ request, login, createUser, user, grafanaAPICredentials }) => { + if ( + user && + (user.user !== grafanaAPICredentials.user || + user.password !== grafanaAPICredentials.password) + ) { + await createUser(); + + // We remove the Viewer role on the dashboard here so that created user + // should not be able to generate report + const changePermissionReq = await request.post( + `/api/dashboards/uid/fdlwjnyim1la8f/permissions`, + { + data: { + items: [{ role: "Editor", permission: 2 }], + }, + headers: { + Authorization: + "Basic " + + btoa( + `${grafanaAPICredentials.user}:${grafanaAPICredentials.password}` + ), + }, + } + ); + + if (!changePermissionReq.ok()) { + throw new Error( + `Could not change permissions on dashboard: status ${changePermissionReq.status()}` + ); + } + } + await login(); + } +);