The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/api/api.go

595 lines
39 KiB

// Package api contains API logic.
package api
import (
"time"
"github.com/grafana/grafana/pkg/api/frontendlogging"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/middleware"
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
6 years ago
"github.com/grafana/grafana/pkg/models"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/web"
)
var plog = log.New("api")
// registerRoutes registers all API HTTP routes.
func (hs *HTTPServer) registerRoutes() {
reqNoAuth := middleware.NoAuth()
reqSignedIn := middleware.ReqSignedIn
reqNotSignedIn := middleware.ReqNotSignedIn
reqSignedInNoAnonymous := middleware.ReqSignedInNoAnonymous
reqGrafanaAdmin := middleware.ReqGrafanaAdmin
reqEditorRole := middleware.ReqEditorRole
reqOrgAdmin := middleware.ReqOrgAdmin
reqOrgAdminFolderAdminOrTeamAdmin := middleware.OrgAdminFolderAdminOrTeamAdmin(hs.SQLStore)
Security: Sync security changes on main (#45083) * * Teams: Appropriately apply user id filter in /api/teams/:id and /api/teams/search * Teams: Ensure that users searching for teams are only able see teams they have access to * Teams: Require teamGuardian admin privileges to list team members * Teams: Prevent org viewers from administering teams * Teams: Add org_id condition to team count query * Teams: clarify permission requirements in teams api docs * Teams: expand scenarios for team search tests * Teams: mock teamGuardian in tests Co-authored-by: Dan Cech <dcech@grafana.com> * remove duplicate WHERE statement * Fix for CVE-2022-21702 (cherry picked from commit 202d7c190082c094bc1dc13f7fe9464746c37f9e) * Lint and test fixes (cherry picked from commit 3e6b67d5504abf4a1d7b8d621f04d062c048e981) * check content type properly (cherry picked from commit 70b4458892bf2f776302720c10d24c9ff34edd98) * basic csrf origin check (cherry picked from commit 3adaa5ff39832364f6390881fb5b42ad47df92e1) * compare origin to host (cherry picked from commit 5443892699e8ed42836bb2b9a44744ff3e970f42) * simplify url parsing (cherry picked from commit b2ffbc9513fed75468628370a48b929d30af2b1d) * check csrf for GET requests, only compare origin (cherry picked from commit 8b81dc12d8f8a1f07852809c5b4d44f0f0b1d709) * parse content type properly (cherry picked from commit 16f76f4902e6f2188bea9606c68b551af186bdc0) * mentioned get in the comment (cherry picked from commit a7e61811ef8ae558ce721e2e3fed04ce7a5a5345) * add content-type: application/json to test HTTP requests * fix pluginproxy test * Fix linter when comparing errors Co-authored-by: Kevin Minehart <kmineh0151@gmail.com> Co-authored-by: Dan Cech <dcech@grafana.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> Co-authored-by: Serge Zaitsev <serge.zaitsev@grafana.com> Co-authored-by: Vardan Torosyan <vardants@gmail.com>
3 years ago
reqCanAccessTeams := middleware.AdminOrEditorAndFeatureEnabled(hs.Cfg.EditorsCanAdmin)
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)
authorize := ac.Middleware(hs.AccessControl)
authorizeInOrg := ac.AuthorizeInOrgMiddleware(hs.AccessControl, hs.SQLStore)
quota := middleware.Quota(hs.QuotaService)
r := hs.RouteRegister
// not logged in views
r.Get("/logout", hs.Logout)
r.Post("/login", quota("session"), routing.Wrap(hs.LoginPost))
r.Get("/login/:name", quota("session"), hs.OAuthLogin)
r.Get("/login", hs.LoginView)
r.Get("/invite/:code", hs.Index)
// authed views
r.Get("/", reqSignedIn, hs.Index)
r.Get("/profile/", reqSignedInNoAnonymous, hs.Index)
r.Get("/profile/password", reqSignedInNoAnonymous, hs.Index)
r.Get("/.well-known/change-password", redirectToChangePassword)
r.Get("/profile/switch-org/:id", reqSignedInNoAnonymous, hs.ChangeActiveOrgAndRedirectToHome)
AccessControl: FGAC permissions for orgs endpoint on frontend (#41050) * AccessControl: FGAC permissions for orgs endpoint on frontend Protect org update endpoints add or refactor missing right messages cover org page * removing scopes from orgs * Perform permission control with global org * Perform the error handling in case of 403 * Simplify frontend code by requiring read access for sure * Remove roles I added to decrease the number of changes * Remove the check for server admin to reduce the number of changes * change error message * Cleaning todos * Remove unecessary changes * Fix tests * Update test snapshot * Update pkg/api/roles.go Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update public/app/features/admin/AdminEditOrgPage.tsx Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Format AdminEditOrgPage for linting * Update public/app/features/admin/AdminEditOrgPage.tsx Co-authored-by: Vardan Torosyan <vardants@gmail.com> * Update public/app/features/admin/AdminEditOrgPage.tsx Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com> * Update public/app/features/admin/AdminListOrgsPage.tsx Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com> * Commit suggestions * Commit suggestion canRead canWrite * fix typo Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Vardan Torosyan <vardants@gmail.com> Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
4 years ago
r.Get("/org/", authorize(reqOrgAdmin, orgPreferencesAccessEvaluator), hs.Index)
r.Get("/org/new", authorizeInOrg(reqGrafanaAdmin, ac.UseGlobalOrg, orgsCreateAccessEvaluator), hs.Index)
r.Get("/datasources/", authorize(reqOrgAdmin, datasources.ConfigurationPageAccess), hs.Index)
r.Get("/datasources/new", authorize(reqOrgAdmin, datasources.NewPageAccess), hs.Index)
r.Get("/datasources/edit/*", authorize(reqOrgAdmin, datasources.EditPageAccess), hs.Index)
r.Get("/org/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), hs.Index)
r.Get("/org/users/new", reqOrgAdmin, hs.Index)
r.Get("/org/users/invite", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionUsersCreate)), hs.Index)
r.Get("/org/teams", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsRead)), hs.Index)
r.Get("/org/teams/edit/*", authorize(reqCanAccessTeams, teamsEditAccessEvaluator), hs.Index)
r.Get("/org/teams/new", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsCreate)), hs.Index)
r.Get("/org/serviceaccounts", authorize(reqOrgAdmin, ac.EvalPermission(serviceaccounts.ActionRead)), hs.Index)
r.Get("/org/serviceaccounts/:serviceAccountId", authorize(reqOrgAdmin, ac.EvalPermission(serviceaccounts.ActionRead)), hs.Index)
r.Get("/org/apikeys/", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyRead)), hs.Index)
r.Get("/dashboard/import/", reqSignedIn, hs.Index)
r.Get("/configuration", reqGrafanaAdmin, hs.Index)
r.Get("/admin", reqGrafanaAdmin, hs.Index)
r.Get("/admin/settings", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionSettingsRead)), hs.Index)
r.Get("/admin/users", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, ac.ScopeGlobalUsersAll)), hs.Index)
r.Get("/admin/users/create", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersCreate)), hs.Index)
r.Get("/admin/users/edit/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead)), hs.Index)
r.Get("/admin/orgs", authorizeInOrg(reqGrafanaAdmin, ac.UseGlobalOrg, orgsAccessEvaluator), hs.Index)
r.Get("/admin/orgs/edit/:id", authorizeInOrg(reqGrafanaAdmin, ac.UseGlobalOrg, orgsAccessEvaluator), hs.Index)
r.Get("/admin/stats", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionServerStatsRead)), hs.Index)
r.Get("/admin/ldap", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)), hs.Index)
r.Get("/styleguide", reqSignedIn, hs.Index)
r.Get("/live", reqGrafanaAdmin, hs.Index)
r.Get("/live/pipeline", reqGrafanaAdmin, hs.Index)
r.Get("/live/cloud", reqGrafanaAdmin, hs.Index)
r.Get("/plugins", reqSignedIn, hs.Index)
r.Get("/plugins/:id/", reqSignedIn, hs.Index)
r.Get("/plugins/:id/edit", reqSignedIn, hs.Index) // deprecated
r.Get("/plugins/:id/page/:page", reqSignedIn, hs.Index)
r.Get("/a/:id/*", reqSignedIn, hs.Index) // App Root Page
r.Get("/a/:id", reqSignedIn, hs.Index)
r.Get("/d/:uid/:slug", reqSignedIn, redirectFromLegacyPanelEditURL, hs.Index)
r.Get("/d/:uid", reqSignedIn, redirectFromLegacyPanelEditURL, hs.Index)
r.Get("/dashboard/script/*", reqSignedIn, hs.Index)
r.Get("/dashboard/new", reqSignedIn, hs.Index)
r.Get("/dashboard-solo/snapshot/*", hs.Index)
r.Get("/d-solo/:uid/:slug", reqSignedIn, hs.Index)
r.Get("/d-solo/:uid", reqSignedIn, hs.Index)
r.Get("/dashboard-solo/script/*", reqSignedIn, hs.Index)
r.Get("/import/dashboard", reqSignedIn, hs.Index)
r.Get("/dashboards/", reqSignedIn, hs.Index)
r.Get("/dashboards/*", reqSignedIn, hs.Index)
Dashboard: Allow shortlink generation (#27409) * intial frontend resolution/redirection logic * backend scaffolding * enough of the frontend to actually test end to end * bugfixes * add tests * cleanup * explore too hard for now * fix build * Docs: add docs * FE test * redirect directly from backend * validate incoming uids * add last_seen_at * format documentation * more documentation feedback * very shaky migration of get route to middleware * persist unix timestamps * add id, orgId to table * fixes for orgId scoping * whoops forgot the middleware * only redirect to absolute URLs under the AppUrl domain * move lookup route to /goto/:uid, stop manually setting 404 response code * renaming things according to PR feedback * tricky deletion * sneaky readd * fix test * more BE renaming * FE updates -- no more @ts-ignore hacking :) and accounting for subpath * Simplify code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Short URLs: Drop usage of bus Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * ShortURLService: Make injectable Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Rename file Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Add handling of url parsing and creating of full shortURL to backend * Update test, remove unused imports * Update pkg/api/short_urls.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Add correct import * Pass context to short url service * Remove not needed error log * Rename dto and field to denote URL rather than path * Update api docs based on feedback/suggestion * Rename files to singular * Revert to send relative path to backend * Fixes after review * Return dto when creating short URL that includes the full url Use full url to provide shorten URL to the user * Fix after review * Fix relative url path when creating new short url Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Ivana <ivana.huckova@gmail.com> Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
5 years ago
r.Get("/goto/:uid", reqSignedIn, hs.redirectFromShortURL, hs.Index)
r.Get("/explore", authorize(func(c *models.ReqContext) {
if f, ok := reqSignedIn.(func(c *models.ReqContext)); ok {
f(c)
}
middleware.EnsureEditorOrViewerCanEdit(c)
}, ac.EvalPermission(ac.ActionDatasourcesExplore)), hs.Index)
r.Get("/playlists/", reqSignedIn, hs.Index)
r.Get("/playlists/*", reqSignedIn, hs.Index)
r.Get("/alerting/", reqSignedIn, hs.Index)
r.Get("/alerting/*", reqSignedIn, hs.Index)
// sign up
r.Get("/verify", hs.Index)
r.Get("/signup", hs.Index)
r.Get("/api/user/signup/options", routing.Wrap(GetSignUpOptions))
r.Post("/api/user/signup", quota("user"), routing.Wrap(hs.SignUp))
r.Post("/api/user/signup/step2", routing.Wrap(hs.SignUpStep2))
// invited
r.Get("/api/user/invite/:code", routing.Wrap(hs.GetInviteInfoByCode))
r.Post("/api/user/invite/complete", routing.Wrap(hs.CompleteInvite))
// reset password
r.Get("/user/password/send-reset-email", reqNotSignedIn, hs.Index)
r.Get("/user/password/reset", hs.Index)
r.Post("/api/user/password/send-reset-email", routing.Wrap(hs.SendResetPasswordEmail))
r.Post("/api/user/password/reset", routing.Wrap(hs.ResetPassword))
// dashboard snapshots
r.Get("/dashboard/snapshot/*", reqNoAuth, hs.Index)
r.Get("/dashboard/snapshots/", reqSignedIn, hs.Index)
// api renew session based on cookie
r.Get("/api/login/ping", quota("session"), routing.Wrap(hs.LoginAPIPing))
// expose plugin file system assets
r.Get("/public/plugins/:pluginId/*", hs.getPluginAssets)
if hs.Features.IsEnabled(featuremgmt.FlagSwaggerUi) {
r.Get("/swagger-ui", swaggerUI)
}
// authed api
r.Group("/api", func(apiRoute routing.RouteRegister) {
// user (signed in)
apiRoute.Group("/user", func(userRoute routing.RouteRegister) {
userRoute.Get("/", routing.Wrap(hs.GetSignedInUser))
userRoute.Put("/", routing.Wrap(hs.UpdateSignedInUser))
userRoute.Post("/using/:id", routing.Wrap(hs.UserSetUsingOrg))
userRoute.Get("/orgs", routing.Wrap(hs.GetSignedInUserOrgList))
userRoute.Get("/teams", routing.Wrap(hs.GetSignedInUserTeamList))
userRoute.Get("/stars", routing.Wrap(hs.GetStars))
userRoute.Post("/stars/dashboard/:id", routing.Wrap(hs.StarDashboard))
userRoute.Delete("/stars/dashboard/:id", routing.Wrap(hs.UnstarDashboard))
userRoute.Put("/password", routing.Wrap(hs.ChangeUserPassword))
userRoute.Get("/quotas", routing.Wrap(hs.GetUserQuotas))
userRoute.Put("/helpflags/:id", routing.Wrap(hs.SetHelpFlag))
// For dev purpose
userRoute.Get("/helpflags/clear", routing.Wrap(hs.ClearHelpFlags))
userRoute.Get("/preferences", routing.Wrap(hs.GetUserPreferences))
userRoute.Put("/preferences", routing.Wrap(hs.UpdateUserPreferences))
Navigation: Introduce a preferences table to store Navbar preferences (#44914) * First attempt at creating new navbar_preferences table in db * Apply to every nav item instead of just home * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * Chore: introduce initTestDB options for features * fix unit tests * Add another unit test and some logic for detecting if a preference already exists * tidy up * Only override IsFeatureToggleEnabled if it's defined * Extract setNavPreferences out into it's own function, initialise features correctly * Make the linter happy * Use new structure * user essentials mob! :trident: * user essentials mob! :trident: * Split NavbarPreferences from Preferences * user essentials mob! :trident: * user essentials mob! :trident: * Fix lint error * Start adding tests * Change internal db structure to be a generic json object * GetJsonData -> GetPreferencesJsonData * Stop using simplejson + add some more unit tests * Update pkg/api/preferences.go Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> * Updates following review comments * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * Change patch to upsert, add a unit test * remove commented out code * introduce patch user/org preferences methods * Return Navbar preferences in the get call * Fix integration test by instantiating JsonData * Address review comments * Rename HideFromNavbar -> Hide * add swagger:model comment * Add patch to the preferences documentation * Add openapi annotations * Add a short description * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * Update unit tests * remove unneeded url * remove outdated comment * Update integration tests * update generated swagger Co-authored-by: Alexandra Vargas <alexa1866@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
3 years ago
userRoute.Patch("/preferences", routing.Wrap(hs.PatchUserPreferences))
userRoute.Get("/auth-tokens", routing.Wrap(hs.GetUserAuthTokens))
userRoute.Post("/revoke-auth-token", routing.Wrap(hs.RevokeUserAuthToken))
}, reqSignedInNoAnonymous)
apiRoute.Group("/users", func(usersRoute routing.RouteRegister) {
userIDScope := ac.Scope("global.users", "id", ac.Parameter(":id"))
usersRoute.Get("/", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, ac.ScopeGlobalUsersAll)), routing.Wrap(hs.searchUsersService.SearchUsers))
usersRoute.Get("/search", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, ac.ScopeGlobalUsersAll)), routing.Wrap(hs.searchUsersService.SearchUsersWithPaging))
usersRoute.Get("/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, userIDScope)), routing.Wrap(hs.GetUserByID))
usersRoute.Get("/:id/teams", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersTeamRead, userIDScope)), routing.Wrap(hs.GetUserTeams))
usersRoute.Get("/:id/orgs", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, userIDScope)), routing.Wrap(hs.GetUserOrgList))
// query parameters /users/lookup?loginOrEmail=admin@example.com
usersRoute.Get("/lookup", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, ac.ScopeGlobalUsersAll)), routing.Wrap(hs.GetUserByLoginOrEmail))
usersRoute.Put("/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersWrite, userIDScope)), routing.Wrap(hs.UpdateUser))
usersRoute.Post("/:id/using/:orgId", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersWrite, userIDScope)), routing.Wrap(hs.UpdateUserActiveOrg))
})
// team (admin permission required)
apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) {
AccessControl: Implement teams resource service (#43951) * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * move some changes from branch to the skeleton PR * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * moving resourceservice to the main wire file pt2 * move team related actions so that they can be reused * PR feedback * fix * typo * Access Control: adding hooks for team member endpoints (#43991) * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * add access control to list and add team member endpoint, and hooks for adding team members * member permission type is 0 * add ID scope for team permission checks * add more team actions, use Member for member permission name * protect team member update endpoint with FGAC permissions * update SQL functions for teams and the corresponding tests * also protect team member removal endpoint with FGAC permissions and add a hook to permission service * a few small fixes, provide team permission service to test setup * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * move some changes from branch to the skeleton PR * remove resource services from wireexts * remove unneeded actions * linting fix * remove comments * feedback fixes * feedback * simplifying * remove team member within the same transaction * fix a mistake with the error * call the correct sql fction * linting * Access control: tests for team member endpoints (#44177) * tests for team member endpoints * clean up and fix the tests * fixing tests take 2 * don't import enterprise test license * don't import enterprise test license * remove unused variable Co-authored-by: gamab <gabi.mabs@gmail.com> Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com>
3 years ago
teamsRoute.Post("/", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsCreate)), routing.Wrap(hs.CreateTeam))
teamsRoute.Put("/:teamId", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsWrite, ac.ScopeTeamsID)), routing.Wrap(hs.UpdateTeam))
teamsRoute.Delete("/:teamId", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsDelete, ac.ScopeTeamsID)), routing.Wrap(hs.DeleteTeamByID))
AccessControl: Implement teams resource service (#43951) * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * move some changes from branch to the skeleton PR * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * moving resourceservice to the main wire file pt2 * move team related actions so that they can be reused * PR feedback * fix * typo * Access Control: adding hooks for team member endpoints (#43991) * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * add access control to list and add team member endpoint, and hooks for adding team members * member permission type is 0 * add ID scope for team permission checks * add more team actions, use Member for member permission name * protect team member update endpoint with FGAC permissions * update SQL functions for teams and the corresponding tests * also protect team member removal endpoint with FGAC permissions and add a hook to permission service * a few small fixes, provide team permission service to test setup * AccessControl: cover team permissions Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Add background service as a consumer to resource_services Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Define actions in roles.go Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * Remove action from accesscontrol model Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com> * As suggested by kalle * move some changes from branch to the skeleton PR * remove resource services from wireexts * remove unneeded actions * linting fix * remove comments * feedback fixes * feedback * simplifying * remove team member within the same transaction * fix a mistake with the error * call the correct sql fction * linting * Access control: tests for team member endpoints (#44177) * tests for team member endpoints * clean up and fix the tests * fixing tests take 2 * don't import enterprise test license * don't import enterprise test license * remove unused variable Co-authored-by: gamab <gabi.mabs@gmail.com> Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: ievaVasiljeva <ieva.vasiljeva@grafana.com>
3 years ago
teamsRoute.Get("/:teamId/members", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsPermissionsRead, ac.ScopeTeamsID)), routing.Wrap(hs.GetTeamMembers))
teamsRoute.Post("/:teamId/members", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsPermissionsWrite, ac.ScopeTeamsID)), routing.Wrap(hs.AddTeamMember))
teamsRoute.Put("/:teamId/members/:userId", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsPermissionsWrite, ac.ScopeTeamsID)), routing.Wrap(hs.UpdateTeamMember))
teamsRoute.Delete("/:teamId/members/:userId", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsPermissionsWrite, ac.ScopeTeamsID)), routing.Wrap(hs.RemoveTeamMember))
teamsRoute.Get("/:teamId/preferences", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsRead, ac.ScopeTeamsID)), routing.Wrap(hs.GetTeamPreferences))
teamsRoute.Put("/:teamId/preferences", authorize(reqCanAccessTeams, ac.EvalPermission(ac.ActionTeamsWrite, ac.ScopeTeamsID)), routing.Wrap(hs.UpdateTeamPreferences))
})
// team without requirement of user to be org admin
apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) {
teamsRoute.Get("/:teamId", authorize(reqSignedIn, ac.EvalPermission(ac.ActionTeamsRead, ac.ScopeTeamsID)), routing.Wrap(hs.GetTeamByID))
teamsRoute.Get("/search", authorize(reqSignedIn, ac.EvalPermission(ac.ActionTeamsRead)), routing.Wrap(hs.SearchTeams))
})
// org information available to all users.
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
orgRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(ActionOrgsRead)), routing.Wrap(hs.GetCurrentOrg))
orgRoute.Get("/quotas", authorize(reqSignedIn, ac.EvalPermission(ActionOrgsQuotasRead)), routing.Wrap(hs.GetCurrentOrgQuotas))
})
if hs.Features.IsEnabled(featuremgmt.FlagStorage) {
apiRoute.Group("/storage", func(orgRoute routing.RouteRegister) {
orgRoute.Get("/list/", routing.Wrap(hs.StorageService.List))
orgRoute.Get("/list/*", routing.Wrap(hs.StorageService.List))
orgRoute.Get("/read/*", routing.Wrap(hs.StorageService.Read))
if hs.Features.IsEnabled(featuremgmt.FlagStorageLocalUpload) {
orgRoute.Delete("/delete/*", reqSignedIn, routing.Wrap(hs.StorageService.Delete))
orgRoute.Post("/upload", reqSignedIn, routing.Wrap(hs.StorageService.Upload))
}
})
}
// current org
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
userIDScope := ac.Scope("users", "id", ac.Parameter(":userId"))
orgRoute.Put("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsWrite)), routing.Wrap(hs.UpdateCurrentOrg))
orgRoute.Put("/address", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsWrite)), routing.Wrap(hs.UpdateCurrentOrgAddress))
orgRoute.Get("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.GetOrgUsersForCurrentOrg))
orgRoute.Get("/users/search", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead)), routing.Wrap(hs.SearchOrgUsersWithPaging))
orgRoute.Post("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), quota("user"), routing.Wrap(hs.AddOrgUserToCurrentOrg))
orgRoute.Patch("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRoleUpdate, userIDScope)), routing.Wrap(hs.UpdateOrgUserForCurrentOrg))
orgRoute.Delete("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRemove, userIDScope)), routing.Wrap(hs.RemoveOrgUserForCurrentOrg))
// invites
orgRoute.Get("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionUsersCreate)), routing.Wrap(hs.GetPendingOrgInvites))
orgRoute.Post("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionUsersCreate)), quota("user"), routing.Wrap(hs.AddOrgInvite))
orgRoute.Patch("/invites/:code/revoke", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionUsersCreate)), routing.Wrap(hs.RevokeInvite))
// prefs
orgRoute.Get("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesRead)), routing.Wrap(hs.GetOrgPreferences))
orgRoute.Put("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesWrite)), routing.Wrap(hs.UpdateOrgPreferences))
Navigation: Introduce a preferences table to store Navbar preferences (#44914) * First attempt at creating new navbar_preferences table in db * Apply to every nav item instead of just home * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * Chore: introduce initTestDB options for features * fix unit tests * Add another unit test and some logic for detecting if a preference already exists * tidy up * Only override IsFeatureToggleEnabled if it's defined * Extract setNavPreferences out into it's own function, initialise features correctly * Make the linter happy * Use new structure * user essentials mob! :trident: * user essentials mob! :trident: * Split NavbarPreferences from Preferences * user essentials mob! :trident: * user essentials mob! :trident: * Fix lint error * Start adding tests * Change internal db structure to be a generic json object * GetJsonData -> GetPreferencesJsonData * Stop using simplejson + add some more unit tests * Update pkg/api/preferences.go Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> * Updates following review comments * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * Change patch to upsert, add a unit test * remove commented out code * introduce patch user/org preferences methods * Return Navbar preferences in the get call * Fix integration test by instantiating JsonData * Address review comments * Rename HideFromNavbar -> Hide * add swagger:model comment * Add patch to the preferences documentation * Add openapi annotations * Add a short description * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * user essentials mob! :trident: * Update unit tests * remove unneeded url * remove outdated comment * Update integration tests * update generated swagger Co-authored-by: Alexandra Vargas <alexa1866@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
3 years ago
orgRoute.Patch("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesWrite)), routing.Wrap(hs.PatchOrgPreferences))
})
// current org without requirement of user to be org admin
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
lookupEvaluator := func() ac.Evaluator {
if hs.Cfg.IsEnterprise {
return ac.EvalPermission(ac.ActionOrgUsersRead)
}
// For oss we allow users with access to update permissions on either folders, teams or dashboards to perform the lookup
return ac.EvalAny(
ac.EvalPermission(ac.ActionOrgUsersRead),
ac.EvalPermission(ac.ActionTeamsPermissionsWrite),
ac.EvalPermission(dashboards.ActionDashboardsPermissionsWrite),
ac.EvalPermission(dashboards.ActionFoldersPermissionsWrite),
)
}
orgRoute.Get("/users/lookup", authorize(reqOrgAdminFolderAdminOrTeamAdmin, lookupEvaluator()), routing.Wrap(hs.GetOrgUsersForCurrentOrgLookup))
})
// create new org
apiRoute.Post("/orgs", authorizeInOrg(reqSignedIn, ac.UseGlobalOrg, ac.EvalPermission(ActionOrgsCreate)), quota("org"), routing.Wrap(hs.CreateOrg))
// search all orgs
apiRoute.Get("/orgs", authorizeInOrg(reqGrafanaAdmin, ac.UseGlobalOrg, ac.EvalPermission(ActionOrgsRead)), routing.Wrap(hs.SearchOrgs))
// orgs (admin routes)
apiRoute.Group("/orgs/:orgId", func(orgsRoute routing.RouteRegister) {
userIDScope := ac.Scope("users", "id", ac.Parameter(":userId"))
orgsRoute.Get("/", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsRead)), routing.Wrap(hs.GetOrgByID))
orgsRoute.Put("/", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsWrite)), routing.Wrap(hs.UpdateOrg))
orgsRoute.Put("/address", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsWrite)), routing.Wrap(hs.UpdateOrgAddress))
orgsRoute.Delete("/", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsDelete)), routing.Wrap(hs.DeleteOrgByID))
orgsRoute.Get("/users", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRead, ac.ScopeUsersAll)), routing.Wrap(hs.GetOrgUsers))
orgsRoute.Post("/users", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), routing.Wrap(hs.AddOrgUser))
orgsRoute.Patch("/users/:userId", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRoleUpdate, userIDScope)), routing.Wrap(hs.UpdateOrgUser))
orgsRoute.Delete("/users/:userId", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRemove, userIDScope)), routing.Wrap(hs.RemoveOrgUser))
orgsRoute.Get("/quotas", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsQuotasRead)), routing.Wrap(hs.GetOrgQuotas))
orgsRoute.Put("/quotas/:target", authorizeInOrg(reqGrafanaAdmin, ac.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsQuotasWrite)), routing.Wrap(hs.UpdateOrgQuota))
})
// orgs (admin routes)
apiRoute.Get("/orgs/name/:name/", authorizeInOrg(reqGrafanaAdmin, ac.UseGlobalOrg, ac.EvalPermission(ActionOrgsRead)), routing.Wrap(hs.GetOrgByName))
// auth api keys
apiRoute.Group("/auth/keys", func(keysRoute routing.RouteRegister) {
apikeyIDScope := ac.Scope("apikeys", "id", ac.Parameter(":id"))
keysRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyRead)), routing.Wrap(hs.GetAPIKeys))
keysRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyCreate)), quota("api_key"), routing.Wrap(hs.AddAPIKey))
keysRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAPIKeyDelete, apikeyIDScope)), routing.Wrap(hs.DeleteAPIKey))
})
// Preferences
apiRoute.Group("/preferences", func(prefRoute routing.RouteRegister) {
prefRoute.Post("/set-home-dash", routing.Wrap(hs.SetHomeDashboard))
})
// Data sources
apiRoute.Group("/datasources", func(datasourceRoute routing.RouteRegister) {
datasourceRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionRead)), routing.Wrap(hs.GetDataSources))
datasourceRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionCreate)), quota("data_source"), routing.Wrap(hs.AddDataSource))
datasourceRoute.Put("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionWrite, datasources.ScopeProvider.GetResourceScope(ac.Parameter(":id")))), routing.Wrap(hs.UpdateDataSource))
datasourceRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDelete, datasources.ScopeProvider.GetResourceScope(ac.Parameter(":id")))), routing.Wrap(hs.DeleteDataSourceById))
datasourceRoute.Delete("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDelete, datasources.ScopeProvider.GetResourceScopeUID(ac.Parameter(":uid")))), routing.Wrap(hs.DeleteDataSourceByUID))
datasourceRoute.Delete("/name/:name", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDelete, datasources.ScopeProvider.GetResourceScopeName(ac.Parameter(":name")))), routing.Wrap(hs.DeleteDataSourceByName))
datasourceRoute.Get("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionRead)), routing.Wrap(hs.GetDataSourceById))
datasourceRoute.Get("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionRead)), routing.Wrap(hs.GetDataSourceByUID))
datasourceRoute.Get("/name/:name", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionRead)), routing.Wrap(hs.GetDataSourceByName))
})
apiRoute.Get("/datasources/id/:name", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionIDRead, datasources.ScopeProvider.GetResourceScopeName(ac.Parameter(":name")))), routing.Wrap(hs.GetDataSourceIdByName))
apiRoute.Get("/plugins", routing.Wrap(hs.GetPluginList))
PluginManager: Make Plugins, Renderer and DataSources non-global (#31866) * PluginManager: Make Plugins and DataSources non-global Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix integration tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Replace outdated command Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * DashboardService: Ensure it gets constructed with necessary parameters Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix build Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * DashboardService: Ensure it gets constructed with necessary parameters Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove dead code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove FocusConvey Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove dead code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Undo interface changes Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Backend: Move tsdbifaces.RequestHandler to plugins.DataRequestHandler Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Rename to DataSourceCount Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Consolidate dashboard interfaces into one Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix dashboard integration tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
4 years ago
apiRoute.Get("/plugins/:pluginId/settings", routing.Wrap(hs.GetPluginSettingByID))
apiRoute.Get("/plugins/:pluginId/markdown/:name", routing.Wrap(hs.GetPluginMarkdown))
apiRoute.Get("/plugins/:pluginId/health", routing.Wrap(hs.CheckHealth))
apiRoute.Any("/plugins/:pluginId/resources", hs.CallResource)
apiRoute.Any("/plugins/:pluginId/resources/*", hs.CallResource)
Plugins: Enable plugin runtime install/uninstall capabilities (#33836) * add uninstall flow * add install flow * small cleanup * smaller-footprint solution * cleanup + make bp start auto * fix interface contract * improve naming * accept version arg * ensure use of shared logger * make installer a field * add plugin decommissioning * add basic error checking * fix api docs * making initialization idempotent * add mutex * fix comment * fix test * add test for decommission * improve existing test * add more test coverage * more tests * change test func to use read lock * refactoring + adding test asserts * improve purging old install flow * improve dupe checking * change log name * skip over dupe scanned * make test assertion more flexible * remove trailing line * fix pointer receiver name * update comment * add context to API * add config flag * add base http api test + fix update functionality * simplify existing check * clean up test * refactor tests based on feedback * add single quotes to errs * use gcmp in tests + fix logo issue * make plugin list testing more flexible * address feedback * fix API test * fix linter * undo preallocate * Update docs/sources/administration/configuration.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * fix linting issue in test * add docs placeholder * update install notes * Update docs/sources/plugins/marketplace.md Co-authored-by: Marcus Olsson <marcus.olsson@hey.com> * update access wording * add more placeholder docs * add link to more info * PR feedback - improved errors, refactor, lock fix * improve err details * propagate plugin version errors * don't autostart renderer * add H1 * fix imports Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> Co-authored-by: Marcus Olsson <marcus.olsson@hey.com>
4 years ago
apiRoute.Get("/plugins/errors", routing.Wrap(hs.GetPluginErrorsList))
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
pluginRoute.Post("/:pluginId/install", routing.Wrap(hs.InstallPlugin))
pluginRoute.Post("/:pluginId/uninstall", routing.Wrap(hs.UninstallPlugin))
}, reqGrafanaAdmin)
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
pluginRoute.Get("/:pluginId/dashboards/", routing.Wrap(hs.GetPluginDashboards))
pluginRoute.Post("/:pluginId/settings", routing.Wrap(hs.UpdatePluginSetting))
pluginRoute.Get("/:pluginId/metrics", routing.Wrap(hs.CollectPluginMetrics))
}, reqOrgAdmin)
apiRoute.Get("/frontend/settings/", hs.GetFrontendSettings)
apiRoute.Any("/datasources/proxy/:id/*", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), hs.ProxyDataSourceRequest)
apiRoute.Any("/datasources/proxy/uid/:uid/*", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), hs.ProxyDataSourceRequestWithUID)
apiRoute.Any("/datasources/proxy/:id", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), hs.ProxyDataSourceRequest)
apiRoute.Any("/datasources/proxy/uid/:uid", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), hs.ProxyDataSourceRequestWithUID)
apiRoute.Any("/datasources/:id/resources", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), hs.CallDatasourceResource)
apiRoute.Any("/datasources/:id/resources/*", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), hs.CallDatasourceResource)
apiRoute.Any("/datasources/:id/health", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), routing.Wrap(hs.CheckDatasourceHealth))
// Folders
apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) {
idScope := dashboards.ScopeFoldersProvider.GetResourceScope(ac.Parameter(":id"))
uidScope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(ac.Parameter(":uid"))
folderRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersRead)), routing.Wrap(hs.GetFolders))
folderRoute.Get("/id/:id", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersRead, idScope)), routing.Wrap(hs.GetFolderByID))
folderRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersCreate)), routing.Wrap(hs.CreateFolder))
folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) {
folderUidRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersRead, uidScope)), routing.Wrap(hs.GetFolderByUID))
folderUidRoute.Put("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersWrite, uidScope)), routing.Wrap(hs.UpdateFolder))
folderUidRoute.Delete("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersDelete, uidScope)), routing.Wrap(hs.DeleteFolder))
folderUidRoute.Group("/permissions", func(folderPermissionRoute routing.RouteRegister) {
folderPermissionRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersPermissionsRead, uidScope)), routing.Wrap(hs.GetFolderPermissionList))
folderPermissionRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersPermissionsWrite, uidScope)), routing.Wrap(hs.UpdateFolderPermissions))
})
})
})
// Dashboard
apiRoute.Group("/dashboards", func(dashboardRoute routing.RouteRegister) {
dashboardRoute.Get("/uid/:uid", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsRead)), routing.Wrap(hs.GetDashboard))
dashboardRoute.Delete("/uid/:uid", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsDelete)), routing.Wrap(hs.DeleteDashboardByUID))
dashboardRoute.Group("/uid/:uid", func(dashUidRoute routing.RouteRegister) {
dashUidRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) {
dashboardPermissionRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsRead)), routing.Wrap(hs.GetDashboardPermissionList))
dashboardPermissionRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsWrite)), routing.Wrap(hs.UpdateDashboardPermissions))
})
})
if hs.ThumbService != nil {
Dash previews: populate crawler queue from SQL query (#44083) * add SQL migrations * dashboard previews from sql: poc * added todos * refactor: use the same enums where possible * use useEffect, always return json * added todo * refactor + delete files after use * refactor + fix manual thumbnail upload * refactor: move all interactions with sqlStore to thumbnail repo * refactor: remove file operations in thumb crawler/service * refactor: fix dashboard_thumbs sql store * refactor: extracted thumbnail fetching/updating to a hook * refactor: store thumbnails in redux store * refactor: store thumbnails in redux store * refactor: private'd repo methods * removed redux storage, saving images as blobs * allow for configurable rendering timeouts * added 1) query for dashboards with stale thumbnails, 2) command for marking thumbnails as stale * use sql-based queue in crawler * ui for marking thumbnails as stale * replaced `stale` boolean prop with `state` enum * introduce rendering session * compilation errors * fix crawler stop button * rename thumbnail state frozen to locked * #44449: fix merge conflicts * #44449: remove thumb methods from `Store` interface * #44449: clean filepath, defer file closing * #44449: fix rendering.Theme cyclic import * #44449: linting * #44449: linting * #44449: mutex'd crawlerStatus access * #44449: added integration tests for `sqlstore.dashboard_thumbs` * #44449: added comments to explain the `ThumbnailState` enum * #44449: use os.ReadFile rather then os.Open * #44449: always enable dashboardPreviews feature during integration tests * #44449: remove sleep time, adjust number of threads * #44449: review fix: add `orgId` to `DashboardThumbnailMeta` * #44449: review fix: automatic parsing of thumbnailState * #44449: lint fixes * #44449: review fix: prefer `WithDbSession` over `WithTransactionalDbSession` * #44449: review fix: add a comment explaining source of the filepath * #44449: review fix: added filepath validation * #44449: review fixes https://github.com/grafana/grafana/pull/45063/files @fzambia Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com>
3 years ago
dashboardRoute.Get("/uid/:uid/img/:kind/:theme", hs.ThumbService.GetImage)
if hs.Features.IsEnabled(featuremgmt.FlagDashboardPreviewsAdmin) {
dashboardRoute.Post("/uid/:uid/img/:kind/:theme", reqGrafanaAdmin, hs.ThumbService.SetImage)
dashboardRoute.Put("/uid/:uid/img/:kind/:theme", reqGrafanaAdmin, hs.ThumbService.UpdateThumbnailState)
}
}
dashboardRoute.Post("/calculate-diff", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.CalculateDashboardDiff))
dashboardRoute.Post("/trim", routing.Wrap(hs.TrimDashboard))
dashboardRoute.Post("/db", authorize(reqSignedIn, ac.EvalAny(ac.EvalPermission(dashboards.ActionDashboardsCreate), ac.EvalPermission(dashboards.ActionDashboardsWrite))), routing.Wrap(hs.PostDashboard))
dashboardRoute.Get("/home", routing.Wrap(hs.GetHomeDashboard))
dashboardRoute.Get("/tags", hs.GetDashboardTags)
// Deprecated: use /uid/:uid API instead.
dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute routing.RouteRegister) {
dashIdRoute.Get("/versions", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.GetDashboardVersions))
dashIdRoute.Get("/versions/:id", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.GetDashboardVersion))
dashIdRoute.Post("/restore", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(hs.RestoreDashboardVersion))
dashIdRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) {
dashboardPermissionRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsRead)), routing.Wrap(hs.GetDashboardPermissionList))
dashboardPermissionRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionDashboardsPermissionsWrite)), routing.Wrap(hs.UpdateDashboardPermissions))
})
})
})
10 years ago
// Dashboard snapshots
apiRoute.Group("/dashboard/snapshots", func(dashboardRoute routing.RouteRegister) {
dashboardRoute.Get("/", routing.Wrap(hs.SearchDashboardSnapshots))
10 years ago
})
// Playlist
apiRoute.Group("/playlists", func(playlistRoute routing.RouteRegister) {
playlistRoute.Get("/", routing.Wrap(hs.SearchPlaylists))
playlistRoute.Get("/:id", hs.ValidateOrgPlaylist, routing.Wrap(hs.GetPlaylist))
playlistRoute.Get("/:id/items", hs.ValidateOrgPlaylist, routing.Wrap(hs.GetPlaylistItems))
playlistRoute.Get("/:id/dashboards", hs.ValidateOrgPlaylist, routing.Wrap(hs.GetPlaylistDashboards))
playlistRoute.Delete("/:id", reqEditorRole, hs.ValidateOrgPlaylist, routing.Wrap(hs.DeletePlaylist))
playlistRoute.Put("/:id", reqEditorRole, hs.ValidateOrgPlaylist, routing.Wrap(hs.UpdatePlaylist))
playlistRoute.Post("/", reqEditorRole, routing.Wrap(hs.CreatePlaylist))
})
// Search
apiRoute.Get("/search/sorting", routing.Wrap(hs.ListSortOptions))
apiRoute.Get("/search/", routing.Wrap(hs.Search))
// metrics
// Deprecated: use /ds/query API instead.
apiRoute.Post("/tsdb/query", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), routing.Wrap(hs.QueryMetrics))
// DataSource w/ expressions
apiRoute.Post("/ds/query", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), routing.Wrap(hs.QueryMetricsV2))
// Validated query
apiRoute.Post("/dashboards/org/:orgId/uid/:dashboardUid/panels/:panelId/query", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionQuery)), routing.Wrap(hs.QueryMetricsFromDashboard))
apiRoute.Group("/alerts", func(alertsRoute routing.RouteRegister) {
alertsRoute.Post("/test", routing.Wrap(hs.AlertTest))
alertsRoute.Post("/:alertId/pause", reqEditorRole, routing.Wrap(hs.PauseAlert))
alertsRoute.Get("/:alertId", hs.ValidateOrgAlert, routing.Wrap(hs.GetAlert))
alertsRoute.Get("/", routing.Wrap(hs.GetAlerts))
alertsRoute.Get("/states-for-dashboard", routing.Wrap(hs.GetAlertStatesForDashboard))
})
var notifiersAuthHandler web.Handler
if hs.Cfg.UnifiedAlerting.IsEnabled() {
notifiersAuthHandler = reqSignedIn
} else {
notifiersAuthHandler = reqEditorRole
}
apiRoute.Get("/alert-notifiers", notifiersAuthHandler, routing.Wrap(
hs.GetAlertNotifiers(hs.Cfg.UnifiedAlerting.IsEnabled())),
)
apiRoute.Group("/alert-notifications", func(alertNotifications routing.RouteRegister) {
alertNotifications.Get("/", routing.Wrap(hs.GetAlertNotifications))
alertNotifications.Post("/test", routing.Wrap(hs.NotificationTest))
alertNotifications.Post("/", routing.Wrap(hs.CreateAlertNotification))
alertNotifications.Put("/:notificationId", routing.Wrap(hs.UpdateAlertNotification))
alertNotifications.Get("/:notificationId", routing.Wrap(hs.GetAlertNotificationByID))
alertNotifications.Delete("/:notificationId", routing.Wrap(hs.DeleteAlertNotification))
alertNotifications.Get("/uid/:uid", routing.Wrap(hs.GetAlertNotificationByUID))
alertNotifications.Put("/uid/:uid", routing.Wrap(hs.UpdateAlertNotificationByUID))
alertNotifications.Delete("/uid/:uid", routing.Wrap(hs.DeleteAlertNotificationByUID))
}, reqEditorRole)
// alert notifications without requirement of user to be org editor
apiRoute.Group("/alert-notifications", func(orgRoute routing.RouteRegister) {
orgRoute.Get("/lookup", routing.Wrap(hs.GetAlertNotificationLookup))
})
apiRoute.Get("/annotations", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsRead)), routing.Wrap(hs.GetAnnotations))
apiRoute.Post("/annotations/mass-delete", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionAnnotationsDelete)), routing.Wrap(hs.MassDeleteAnnotations))
apiRoute.Group("/annotations", func(annotationsRoute routing.RouteRegister) {
annotationsRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsCreate)), routing.Wrap(hs.PostAnnotation))
annotationsRoute.Delete("/:annotationId", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsDelete, ac.ScopeAnnotationsID)), routing.Wrap(hs.DeleteAnnotationByID))
annotationsRoute.Put("/:annotationId", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsWrite, ac.ScopeAnnotationsID)), routing.Wrap(hs.UpdateAnnotation))
annotationsRoute.Patch("/:annotationId", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsWrite, ac.ScopeAnnotationsID)), routing.Wrap(hs.PatchAnnotation))
annotationsRoute.Post("/graphite", authorize(reqEditorRole, ac.EvalPermission(ac.ActionAnnotationsCreate, ac.ScopeAnnotationsTypeOrganization)), routing.Wrap(hs.PostGraphiteAnnotation))
annotationsRoute.Get("/tags", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsRead)), routing.Wrap(hs.GetAnnotationTags))
})
apiRoute.Post("/frontend-metrics", routing.Wrap(hs.PostFrontendMetrics))
apiRoute.Group("/live", func(liveRoute routing.RouteRegister) {
// the channel path is in the name
liveRoute.Post("/publish", routing.Wrap(hs.Live.HandleHTTPPublish))
// POST influx line protocol.
liveRoute.Post("/push/:streamId", hs.LivePushGateway.Handle)
// List available streams and fields
liveRoute.Get("/list", routing.Wrap(hs.Live.HandleListHTTP))
// Some channels may have info
liveRoute.Get("/info/*", routing.Wrap(hs.Live.HandleInfoHTTP))
if hs.Features.IsEnabled(featuremgmt.FlagLivePipeline) {
// POST Live data to be processed according to channel rules.
liveRoute.Post("/pipeline/push/*", hs.LivePushGateway.HandlePipelinePush)
liveRoute.Post("/pipeline-convert-test", routing.Wrap(hs.Live.HandlePipelineConvertTestHTTP), reqOrgAdmin)
liveRoute.Get("/pipeline-entities", routing.Wrap(hs.Live.HandlePipelineEntitiesListHTTP), reqOrgAdmin)
liveRoute.Get("/channel-rules", routing.Wrap(hs.Live.HandleChannelRulesListHTTP), reqOrgAdmin)
liveRoute.Post("/channel-rules", routing.Wrap(hs.Live.HandleChannelRulesPostHTTP), reqOrgAdmin)
liveRoute.Put("/channel-rules", routing.Wrap(hs.Live.HandleChannelRulesPutHTTP), reqOrgAdmin)
liveRoute.Delete("/channel-rules", routing.Wrap(hs.Live.HandleChannelRulesDeleteHTTP), reqOrgAdmin)
liveRoute.Get("/write-configs", routing.Wrap(hs.Live.HandleWriteConfigsListHTTP), reqOrgAdmin)
liveRoute.Post("/write-configs", routing.Wrap(hs.Live.HandleWriteConfigsPostHTTP), reqOrgAdmin)
liveRoute.Put("/write-configs", routing.Wrap(hs.Live.HandleWriteConfigsPutHTTP), reqOrgAdmin)
liveRoute.Delete("/write-configs", routing.Wrap(hs.Live.HandleWriteConfigsDeleteHTTP), reqOrgAdmin)
}
})
Dashboard: Allow shortlink generation (#27409) * intial frontend resolution/redirection logic * backend scaffolding * enough of the frontend to actually test end to end * bugfixes * add tests * cleanup * explore too hard for now * fix build * Docs: add docs * FE test * redirect directly from backend * validate incoming uids * add last_seen_at * format documentation * more documentation feedback * very shaky migration of get route to middleware * persist unix timestamps * add id, orgId to table * fixes for orgId scoping * whoops forgot the middleware * only redirect to absolute URLs under the AppUrl domain * move lookup route to /goto/:uid, stop manually setting 404 response code * renaming things according to PR feedback * tricky deletion * sneaky readd * fix test * more BE renaming * FE updates -- no more @ts-ignore hacking :) and accounting for subpath * Simplify code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Short URLs: Drop usage of bus Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * ShortURLService: Make injectable Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Rename file Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Add handling of url parsing and creating of full shortURL to backend * Update test, remove unused imports * Update pkg/api/short_urls.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Add correct import * Pass context to short url service * Remove not needed error log * Rename dto and field to denote URL rather than path * Update api docs based on feedback/suggestion * Rename files to singular * Revert to send relative path to backend * Fixes after review * Return dto when creating short URL that includes the full url Use full url to provide shorten URL to the user * Fix after review * Fix relative url path when creating new short url Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Ivana <ivana.huckova@gmail.com> Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
5 years ago
// short urls
apiRoute.Post("/short-urls", routing.Wrap(hs.createShortURL))
apiRoute.Group("/comments", func(commentRoute routing.RouteRegister) {
commentRoute.Post("/get", routing.Wrap(hs.commentsGet))
commentRoute.Post("/create", routing.Wrap(hs.commentsCreate))
})
}, reqSignedIn)
// admin api
r.Group("/api/admin", func(adminRoute routing.RouteRegister) {
adminRoute.Get("/settings", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionSettingsRead)), routing.Wrap(hs.AdminGetSettings))
if hs.Features.IsEnabled(featuremgmt.FlagShowFeatureFlagsInUI) {
adminRoute.Get("/settings/features", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionSettingsRead)), hs.Features.HandleGetSettings)
}
adminRoute.Get("/stats", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionServerStatsRead)), routing.Wrap(hs.AdminGetStats))
adminRoute.Post("/pause-all-alerts", reqGrafanaAdmin, routing.Wrap(hs.PauseAllAlerts))
if hs.ThumbService != nil && hs.Features.IsEnabled(featuremgmt.FlagDashboardPreviewsAdmin) {
adminRoute.Post("/crawler/start", reqGrafanaAdmin, routing.Wrap(hs.ThumbService.StartCrawler))
adminRoute.Post("/crawler/stop", reqGrafanaAdmin, routing.Wrap(hs.ThumbService.StopCrawler))
adminRoute.Get("/crawler/status", reqGrafanaAdmin, routing.Wrap(hs.ThumbService.CrawlerStatus))
}
if hs.Features.IsEnabled(featuremgmt.FlagExport) {
adminRoute.Get("/export", reqGrafanaAdmin, routing.Wrap(hs.ExportService.HandleGetStatus))
adminRoute.Post("/export", reqGrafanaAdmin, routing.Wrap(hs.ExportService.HandleRequestExport))
}
adminRoute.Post("/provisioning/dashboards/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersDashboards)), routing.Wrap(hs.AdminProvisioningReloadDashboards))
adminRoute.Post("/provisioning/plugins/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersPlugins)), routing.Wrap(hs.AdminProvisioningReloadPlugins))
adminRoute.Post("/provisioning/datasources/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersDatasources)), routing.Wrap(hs.AdminProvisioningReloadDatasources))
adminRoute.Post("/provisioning/notifications/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersNotifications)), routing.Wrap(hs.AdminProvisioningReloadNotifications))
adminRoute.Post("/ldap/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPConfigReload)), routing.Wrap(hs.ReloadLDAPCfg))
adminRoute.Post("/ldap/sync/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPUsersSync)), routing.Wrap(hs.PostSyncUserWithLDAP))
adminRoute.Get("/ldap/:username", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPUsersRead)), routing.Wrap(hs.GetUserFromLDAP))
adminRoute.Get("/ldap/status", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionLDAPStatusRead)), routing.Wrap(hs.GetLDAPStatus))
})
// Administering users
r.Group("/api/admin/users", func(adminUserRoute routing.RouteRegister) {
userIDScope := ac.Scope("global.users", "id", ac.Parameter(":id"))
adminUserRoute.Post("/", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersCreate)), routing.Wrap(hs.AdminCreateUser))
adminUserRoute.Put("/:id/password", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersPasswordUpdate, userIDScope)), routing.Wrap(hs.AdminUpdateUserPassword))
adminUserRoute.Put("/:id/permissions", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersPermissionsUpdate, userIDScope)), routing.Wrap(hs.AdminUpdateUserPermissions))
adminUserRoute.Delete("/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersDelete, userIDScope)), routing.Wrap(hs.AdminDeleteUser))
adminUserRoute.Post("/:id/disable", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersDisable, userIDScope)), routing.Wrap(hs.AdminDisableUser))
adminUserRoute.Post("/:id/enable", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersEnable, userIDScope)), routing.Wrap(hs.AdminEnableUser))
adminUserRoute.Get("/:id/quotas", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersQuotasList, userIDScope)), routing.Wrap(hs.GetUserQuotas))
adminUserRoute.Put("/:id/quotas/:target", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersQuotasUpdate, userIDScope)), routing.Wrap(hs.UpdateUserQuota))
adminUserRoute.Post("/:id/logout", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersLogout, userIDScope)), routing.Wrap(hs.AdminLogoutUser))
adminUserRoute.Get("/:id/auth-tokens", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersAuthTokenList, userIDScope)), routing.Wrap(hs.AdminGetUserAuthTokens))
adminUserRoute.Post("/:id/revoke-auth-token", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersAuthTokenUpdate, userIDScope)), routing.Wrap(hs.AdminRevokeUserAuthToken))
})
// rendering
r.Get("/render/*", reqSignedIn, hs.RenderToPng)
// grafana.net proxy
Plugins Catalog: Install and show the latest compatible version of a plugin (#41003) * fix(catalog): prefer rendering installed version over latest version * feat(catalog): signify installed version in version history * feat(catalog): introduce installedVersion and latestVersion * refactor(catalog): use latestVersion for installation, simplify plugindetails header logic * refactor(catalog): clean up installedVersion and latestVersion * feat(catalog): use table-layout so versions list table has consistent column widths * test(catalog): update failing tests * removed the need of having a latest version in the plugin catalog type root level. * fixed flaky test depending on what locale it was being running with. * added missing test to verify version for a remote plugin. * fixed version in header. * preventing the UI from break if no versions are available. * fixed failing test due to missing mock data. * added todo as a reminder. * refactor(catalog): prefer grafana plugin icons over gcom notfound images * refactor(Plugins/Admin): change constant name * refactor(Plugins/Admin): add comment to make condition easier to understand * chore: update go modules * feat(Backend/Plugins): add "dependencies" field to `PluginListItem` * feat(Plugins/Admin): show the grafana dependency for the installed version * refactor(Plugins/Admin): use the local version of links * refactor(Plugins/Admin): prefer the local version for `.type` * refactor(Plugins/ADmin): prefer the local `.description` field * fix(Plugins/Admin): fix tests * test(plugins/api): update the expected response for the `api/plugins` tests * chore(Plugins/Admin): add todo comments to check preferation of remote/local values * feat(backend/api): always send the grafana version as a header when proxying to GCOM * feat(plugins/admin): use the `isCompatible` flag to get the latest compatible version * feat(plugins/admin): show the latest compatible version in the versions list * fix(plugins/admin): show the grafana dependency for the latest compatible version * fix(plugins/admin): update the version list when installing/uninstalling a plugin * test(plugins/admin): add some test-cases for the latest-compatible-version * fix(plugins/admin): show the grafana dependency for the installed version (if installed) * feat(plugins/backend): add the `dependencies.grafanaDependency` property to the plugin object * test(plugins/backend): fix tests by adjusting expected response json Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
4 years ago
r.Any("/api/gnet/*", reqSignedIn, hs.ProxyGnetRequest)
// Gravatar service.
r.Get("/avatar/:hash", hs.AvatarCacheServer.Handler)
// Snapshots
r.Post("/api/snapshots/", reqSnapshotPublicModeOrSignedIn, hs.CreateDashboardSnapshot)
r.Get("/api/snapshot/shared-options/", reqSignedIn, GetSharingOptions)
r.Get("/api/snapshots/:key", routing.Wrap(hs.GetDashboardSnapshot))
r.Get("/api/snapshots-delete/:deleteKey", reqSnapshotPublicModeOrSignedIn, routing.Wrap(hs.DeleteDashboardSnapshotByDeleteKey))
r.Delete("/api/snapshots/:key", reqEditorRole, routing.Wrap(hs.DeleteDashboardSnapshot))
// Frontend logs
Plugins: Refactor Plugin Management (#40477) * add core plugin flow * add instrumentation * move func * remove cruft * support external backend plugins * refactor + clean up * remove comments * refactor loader * simplify core plugin path arg * cleanup loggers * move signature validator to plugins package * fix sig packaging * cleanup plugin model * remove unnecessary plugin field * add start+stop for pm * fix failures * add decommissioned state * export fields just to get things flowing * fix comments * set static routes * make image loading idempotent * merge with backend plugin manager * re-use funcs * reorder imports + remove unnecessary interface * add some TODOs + remove unused func * remove unused instrumentation func * simplify client usage * remove import alias * re-use backendplugin.Plugin interface * re order funcs * improve var name * fix log statements * refactor data model * add logic for dupe check during loading * cleanup state setting * refactor loader * cleanup manager interface * add rendering flow * refactor loading + init * add renderer support * fix renderer plugin * reformat imports * track errors * fix plugin signature inheritance * name param in interface * update func comment * fix func arg name * introduce class concept * remove func * fix external plugin check * apply changes from pm-experiment * fix core plugins * fix imports * rename interface * comment API interface * add support for testdata plugin * enable alerting + use correct core plugin contracts * slim manager API * fix param name * fix filter * support static routes * fix rendering * tidy rendering * get tests compiling * fix install+uninstall * start finder test * add finder test coverage * start loader tests * add test for core plugins * load core + bundled test * add test for nested plugin loading * add test files * clean interface + fix registering some core plugins * refactoring * reformat and create sub packages * simplify core plugin init * fix ctx cancel scenario * migrate initializer * remove Init() funcs * add test starter * new logger * flesh out initializer tests * refactoring * remove unused svc * refactor rendering flow * fixup loader tests * add enabled helper func * fix logger name * fix data fetchers * fix case where plugin dir doesn't exist * improve coverage + move dupe checking to loader * remove noisy debug logs * register core plugins automagically * add support for renderer in catalog * make private func + fix req validation * use interface * re-add check for renderer in catalog * tidy up from moving to auto reg core plugins * core plugin registrar * guards * copy over core plugins for test infra * all tests green * renames * propagate new interfaces * kill old manager * get compiling * tidy up * update naming * refactor manager test + cleanup * add more cases to finder test * migrate validator to field * more coverage * refactor dupe checking * add test for plugin class * add coverage for initializer * split out rendering * move * fixup tests * fix uss test * fix frontend settings * fix grafanads test * add check when checking sig errors * fix enabled map * fixup * allow manual setup of CM * rename to cloud-monitoring * remove TODO * add installer interface for testing * loader interface returns * tests passing * refactor + add more coverage * support 'stackdriver' * fix frontend settings loading * improve naming based on package name * small tidy * refactor test * fix renderer start * make cloud-monitoring plugin ID clearer * add plugin update test * add integration tests * don't break all if sig can't be calculated * add root URL check test * add more signature verification tests * update DTO name * update enabled plugins comment * update comments * fix linter * revert fe naming change * fix errors endpoint * reset error code field name * re-order test to help verify * assert -> require * pm check * add missing entry + re-order * re-check * dump icon log * verify manager contents first * reformat * apply PR feedback * apply style changes * fix one vs all loading err * improve log output * only start when no signature error * move log * rework plugin update check * fix test * fix multi loading from cfg.PluginSettings * improve log output #2 * add error abstraction to capture errors without registering a plugin * add debug log * add unsigned warning * e2e test attempt * fix logger * set home path * prevent panic * alternate * ugh.. fix home path * return renderer even if not started * make renderer plugin managed * add fallback renderer icon, update renderer badge + prevent changes when renderer is installed * fix icon loading * rollback renderer changes * use correct field * remove unneccessary block * remove newline * remove unused func * fix bundled plugins base + module fields * remove unused field since refactor * add authorizer abstraction * loader only returns plugins expected to run * fix multi log output
4 years ago
sourceMapStore := frontendlogging.NewSourceMapStore(hs.Cfg, hs.pluginStaticRouteResolver, frontendlogging.ReadSourceMapFromFS)
r.Post("/log", middleware.RateLimit(hs.Cfg.Sentry.EndpointRPS, hs.Cfg.Sentry.EndpointBurst, time.Now),
routing.Wrap(NewFrontendLogMessageHandler(sourceMapStore)))
}