* API: serve org users lookup from unified storage in single-org mode
The user picker for adding folder/dashboard permissions calls
/api/org/users/lookup, which joins the legacy org_user/user SQL tables. When
users are served from unified storage (kubernetesUsersApi), those tables are
empty, so the picker shows no users. In single-organization deployments, route
the lookup through the k8s-redirected userService.Search so it reads the shared
users. Falls back to the legacy org-scoped SQL search otherwise.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* API: address review feedback on org users lookup
- Use the OpenFeature client to check the kubernetesUsersRedirect toggle
instead of the deprecated IsEnabledGlobally (fixes lint-go failure)
- Fall back to a Gravatar URL when the searched user has no AvatarURL
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Analytics: Add PostHog echo backend
Add PostHog as a supported analytics backend for Grafana's echo service.
Users can configure `posthog_token` and optionally `posthog_host` in the
[analytics] section to forward all echo events to PostHog.
* add eslint ignore comments
* update sample.ini
---------
Co-authored-by: joshhunt <josh.hunt@grafana.com>
Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
The built-in "-- Grafana --" datasource has no row in the datasource table,
so legacyConnectionClientImpl.GetConnectionByUID could never resolve its plugin
type. For RBAC roles carrying the seeded datasources:uid:grafana grant this was
silently unresolved, and for global roles (orgID 0, multi-org) the store lookup
failed with ErrDataSourceIdentifierNotSet, breaking role provisioning.
Its UID and type are fixed and identical across orgs, so resolve it directly in
the legacy connection client. This fixes datasource-type resolution for every
caller that goes through this client (provisioning save and the k8s migration
type resolvers) without a store row.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Plugin Proxy: Narrow data source config to DataSourceProxySettings
The data source proxy only needs a handful of fields from setting.Cfg.
Introduce a focused DataSourceProxySettings struct that exposes only
those fields, load Azure settings lazily via a resolver callback so the
proxy avoids touching Azure configuration unless an Azure-authenticated
route is matched, and pass the new struct through the datasource proxy
service in place of *setting.Cfg.
* legacy prefs api uses k8s impl
* tests
* fix workspace
* fix preferences flag
* fix team preferences not using open feature
* reword comment
* split tests into legacy + k8s, update first test
* update remaining tests
* kick CI
* kick CI again
* fix go linting
* refactor to use shared utils
* remove unnecessary skipping of test
* adjust logic of team authorizer
* stop checking if AccessClient is nil
* change comment for authoriser special case
* refactor to use context.Context
---------
Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
* feat(annotations): expose AnnotationAppPlatform.Enabled to FE bootstrap
Mirrors [annotations.app_platform] enabled into the frontend bootstrap
settings DTO so the client can route reads/writes through
/apis/annotation.grafana.app when on. Avoids adding a feature toggle
since the gate is fundamentally a deployment-time decision: "is the
new annotation app platform endpoint serving requests?".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* cleanup
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Data Source: Add forward_user_agent option to preserve client User-Agent
* Update pkg/api/pluginproxy/ds_proxy.go
Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
* Data Source: Fix gofmt and add test for empty DataProxyUserAgent
---------
Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
* feat: use dedicated token for plugin related requests to Grafana.com
Introduce install_token in the [plugins] config section as a dedicated
token for all plugin-related requests to the Grafana.com API (catalog
browsing, plugin downloads, grafana-cli installs). Falls back to
sso_api_token when install_token is not configured, preserving
backward compatibility.
* feat: guard dedicated gnet_proxy_token with feature flag
* ref: rename install_token to gnet_proxy_token under [grafana_com] section
- Move config key from [plugins] to [grafana_com], rename install_token -> proxy_token
* ref: rename feature flag to grafana.dedicatedGrafanaComProxyAPIToken
* Folder: Wrap remaining validation errors in metav1.Status (follow-up)
Continues #123709 by extending APIStatus coverage to the remaining folder
admission validator paths that surfaced as 500 in production:
- Dashboard UID errors: validate.go now returns ErrAPIInvalidUID/ErrAPIUIDTooLong
(errutil wrappers around the legacy dashboards sentinels) so they implement
APIStatus and render as 400 instead of "Unhandled Error" 500.
- ErrNameExists: both call sites use .Errorf(...) so the apiserver gets an
APIStatus error instead of a raw Base value.
- New ErrFolderCannotBeMovedToK6 for the k6 move rejection.
- ErrCircularReference reused for the move-under-descendant case.
ToFolderStatusError now copies errutil.Error.Status().Details into the
returned metav1.Status so downstream consumers can match on the structured
messageID via Status.Details.UID without parsing the human message.
ToFolderErrorResponse: dashboard branch only strips the errutil prefix when
the chain contains an errutil.Error AND the underlying matches one of
stableDashboardErrSentinels — keeps legacy /api/folders messages
byte-stable without dropping custom context from non-errutil callers.
Tests: TestIntegrationFolderValidationReturns400 is now a 9-row table
covering all create + update validation paths, each asserting Code=400,
exact message, and the messageID via Status.Details.UID.
doCreateCircularReferenceFolderTest fixed (broken JSON was silently
passing) and extended with a create-then-move circular flow.
TestToFolderErrorResponse moved to apierrors_test package so it can
reference the production folders.ErrAPI* wrappers directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Folder: gofmt fix
* Folder: clarify ErrAPIInvalidUID/ErrAPIUIDTooLong comment
* Folder: clarify validation error comments
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
issue: grafana/identity-access-team#2044
ENT: grafana/grafana-enterprise#11790
What is this feature?
We want to have one place which contains all the fixed: roles to register them on IAM service side.
This PR modifies the existing code to export the fixed: roles.
* Folder: Wrap validation errors in metav1.Status
The folder admission validator returned bare errors that the k8s apiserver
could not convert to a structured response, surfacing them as "Unhandled
Error" 500s instead of proper 4xx responses (alert #1782995). The bulk of
the SLO budget burn came from the max-depth check; the same pattern also
affected invalid-uid, title-empty, parent-of-itself, and cyclic-reference.
Wrap each one with errutil so they implement the APIStatus interface and
the apiserver renders them as structured 4xx. The legacy /api routes keep
their original errors.New sentinels and unchanged err.Error() strings;
new ErrAPIxxx variants in pkg/services/folder/model.go are pre-wrapped via
%w so errors.Is keeps matching the legacy sentinels for the existing
ToFolderErrorResponse handler.
Also moved the cyclic-reference sentinel from a local var in parents.go
into model.go for consistency, and retyped TestValidateCreate.expectedErr
from substring to typed error so assertions go through errors.Is.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Folder: Integration test for max-depth returning 400 not 500
Boots the folder apiserver via apis.NewK8sTestHelper, creates the
maximum allowed nesting (default depth 4), then attempts one more
folder and asserts the response is HTTP 400 BadRequest, not 500
InternalError. Guards the regression that fired alert #1782995.
Note: placed in pkg/tests/apis/folder/ rather than provisioning
because the bug is in the folder apiserver itself; provisioning is
just one of many consumers of that error path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Folder: Assert exact error message format for max-depth in integration test
Provisioning and external observability tooling consume the message
returned by the apiserver. Pin the full string ("[folder.maximum-depth-reached]
folder max depth exceeded, max depth is 4") so any change to the messageID
or the depth log message format fails this test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Folder: gofmt fix
* Folder: add validation tests for other scenarios
- title-empty, invalid-uid, cannot-be-parent-of-itself
- fix TestIntegrationFoldersCreateAPIEndpointK8S
* Folder: Validation tests for other scenarios - title-empty, invalid-uid, cannot-be-parent-of-itself - fix TestIntegrationFoldersCreateAPIEndpointK8S
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(frontend): rename assetSriChecks and generate go
* feat(frontend-service): pass sri checks feature flag to frontend html template
* feat(backend): pass asset sri check feature flag to st frontend template
* build(webpack): use global __grafanaAssetSriChecksEnabled var to control sri checks in async loading
* chore(frontend-service): add assetSriChecks to goff-flags.yaml
* docs(webpack): add more info on what and why to featureflaggedsriplugin
* add config option [unified_alerting].allowed_integrations that controls what integration types are allowed for configuration.
* fix contact point front-end component to disable not allowed integration types in the list.
---------
Co-authored-by: Santiago Hernández <santiagohernandez.1997@gmail.com>
Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
* Nav: Add access to Connections section for plugin routes
* Nav: Fix whitespace lint in hasConnectionsPluginItems
* refactor(navtree): extract hasAccessibleInclude method
* Nav: create Connections section before addAppLinks
* fix(connections): show section to users with plugin children, not only datasource admins
Previously the Connections section was only added to the nav tree when
the user had ConfigurationPageAccess (datasources:create or read+write).
This meant plugin pages registered under the "connections" section ID
(e.g. grafana-collector-app, grafana-pdc-app) were invisible to viewers
who lacked datasource write permissions, even when they had access to
those plugins.
Changes:
- buildDataConnectionsNavLink now always returns the Connections section
so that addAppLinks can attach plugin children regardless of the
caller's datasource permissions. Core items (add-new-connection,
datasources) remain gated by ConfigurationPageAccess.
- NavTreeRoot.RemoveEmptyConnectionsSection prunes the section after all
app links and enterprise hooks have run if no children were registered.
Called from setIndexViewData alongside RemoveEmptyAdminSections.
- /connections landing page route relaxed to reqSignedIn. The page
derives its cards from the nav tree (grafana/grafana#122017), so any
signed-in user who has nav children can view it; sub-pages retain their
own per-route authorization.
- Tests added covering: admin sees core items, viewer gets empty section,
viewer with plugin attachment sees the section, and empty pruning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve cherry-pick conflicts remove duplicated test
---------
Co-authored-by: Jára Benc <jaroslav.benc@grafana.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This PR adds `FlagDatasourceUseNewCRUDAPIs` and uses it for the backend
rollout of new CRUD APIs for datasources.
Previously we were using `FlagQueryServiceWithConnections`, but this
caused coupling withe rollout of the query service and caching.
Go's net/http silently strips double-quote characters from cookie
values because they are not valid per RFC 6265. This causes redirectTo
URLs containing quotes (e.g. PromQL label matchers like {label="value"})
to be corrupted during the OAuth login redirect flow.
URL-encode the redirectTo value before writing it to the cookie, pairing
with the existing url.QueryUnescape on the read side in handleLogin.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>