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/common_test.go

336 lines
10 KiB

package api
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models/usertoken"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
"github.com/grafana/grafana/pkg/services/auth/authtest"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/authn/authntest"
"github.com/grafana/grafana/pkg/services/contexthandler"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/login/authinfotest"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/searchusers"
Chore: Split get user by ID (#52442) * Remove user from preferences, stars, orguser, team member * Fix lint * Add Delete user from org and dashboard acl * Delete user from user auth * Add DeleteUser to quota * Add test files and adjust user auth store * Rename package in wire for user auth * Import Quota Service interface in other services * do the same in tests * fix lint tests * Fix tests * Add some tests * Rename InsertUser and DeleteUser to InsertOrgUser and DeleteOrgUser * Rename DeleteUser to DeleteByUser in quota * changing a method name in few additional places * Fix in other places * Fix lint * Fix tests * Chore: Split Delete User method * Add fakes for userauth * Add mock for access control Delete User permossion, use interface * Use interface for ream guardian * Add simple fake for dashboard acl * Add go routines, clean up, use interfaces * fix lint * Update pkg/services/user/userimpl/user_test.go Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> * Update pkg/services/user/userimpl/user_test.go Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> * Update pkg/services/user/userimpl/user_test.go Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> * Split get user by ID * Use new method in api * Add tests * Aplly emthod in auth info service * Fix lint and some tests * Fix get user by ID * Fix lint Remove unused fakes * Use split get user id in admin users * Use GetbyID in cli commands * Clean up after merge * Remove commented out code * Clena up imports * add back ) * Fix wire generation for runner after merge with main Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
3 years ago
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
"github.com/grafana/grafana/pkg/web/webtest"
)
func loggedInUserScenario(t *testing.T, desc string, url string, routePattern string, fn scenarioFunc, sqlStore db.DB) {
loggedInUserScenarioWithRole(t, desc, "GET", url, routePattern, org.RoleEditor, fn, sqlStore)
}
func loggedInUserScenarioWithRole(t *testing.T, desc string, method string, url string, routePattern string, role org.RoleType, fn scenarioFunc, sqlStore db.DB) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
sc := setupScenarioContext(t, url)
sc.sqlStore = sqlStore
sc.userService = usertest.NewUserServiceFake()
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
sc.context = c
sc.context.UserID = testUserID
sc.context.OrgID = testOrgID
sc.context.Login = testUserLogin
sc.context.OrgRole = role
sc.context.IsAnonymous = false
if sc.handlerFunc != nil {
return sc.handlerFunc(sc.context)
}
return nil
})
switch method {
case "GET":
sc.m.Get(routePattern, sc.defaultHandler)
case "DELETE":
sc.m.Delete(routePattern, sc.defaultHandler)
}
fn(sc)
})
}
func anonymousUserScenario(t *testing.T, desc string, method string, url string, routePattern string, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
sc.context = c
if sc.handlerFunc != nil {
return sc.handlerFunc(sc.context)
}
return nil
})
switch method {
case "GET":
sc.m.Get(routePattern, sc.defaultHandler)
case "DELETE":
sc.m.Delete(routePattern, sc.defaultHandler)
}
fn(sc)
})
}
func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
sc.resp = httptest.NewRecorder()
req, err := http.NewRequest(method, url, nil)
require.NoError(sc.t, err)
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
req.Header.Add("Content-Type", "application/json")
sc.req = req
return sc
}
func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map[string]string) *scenarioContext {
sc.resp = httptest.NewRecorder()
req, err := http.NewRequest(method, url, nil)
// TODO: Depend on sc.t
if sc.t != nil {
require.NoError(sc.t, err)
} else if err != nil {
panic(fmt.Sprintf("Making request failed: %s", err))
}
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
req.Header.Add("Content-Type", "application/json")
q := req.URL.Query()
for k, v := range queryParams {
q.Add(k, v)
}
req.URL.RawQuery = q.Encode()
sc.req = req
return sc
}
func (sc *scenarioContext) fakeReqNoAssertions(method, url string) *scenarioContext {
sc.resp = httptest.NewRecorder()
req, _ := http.NewRequest(method, url, nil)
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
req.Header.Add("Content-Type", "application/json")
sc.req = req
return sc
}
func (sc *scenarioContext) fakeReqNoAssertionsWithCookie(method, url string, cookie http.Cookie) *scenarioContext {
sc.resp = httptest.NewRecorder()
http.SetCookie(sc.resp, &cookie)
req, _ := http.NewRequest(method, url, nil)
req.Header = http.Header{"Cookie": sc.resp.Header()["Set-Cookie"]}
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
req.Header.Add("Content-Type", "application/json")
sc.req = req
return sc
}
type scenarioContext struct {
t *testing.T
cfg *setting.Cfg
m *web.Mux
context *contextmodel.ReqContext
resp *httptest.ResponseRecorder
handlerFunc handlerFunc
defaultHandler web.Handler
req *http.Request
url string
userAuthTokenService *authtest.FakeUserAuthTokenService
sqlStore db.DB
authInfoService *authinfotest.FakeService
dashboardVersionService dashver.Service
Chore: Split get user by ID (#52442) * Remove user from preferences, stars, orguser, team member * Fix lint * Add Delete user from org and dashboard acl * Delete user from user auth * Add DeleteUser to quota * Add test files and adjust user auth store * Rename package in wire for user auth * Import Quota Service interface in other services * do the same in tests * fix lint tests * Fix tests * Add some tests * Rename InsertUser and DeleteUser to InsertOrgUser and DeleteOrgUser * Rename DeleteUser to DeleteByUser in quota * changing a method name in few additional places * Fix in other places * Fix lint * Fix tests * Chore: Split Delete User method * Add fakes for userauth * Add mock for access control Delete User permossion, use interface * Use interface for ream guardian * Add simple fake for dashboard acl * Add go routines, clean up, use interfaces * fix lint * Update pkg/services/user/userimpl/user_test.go Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> * Update pkg/services/user/userimpl/user_test.go Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> * Update pkg/services/user/userimpl/user_test.go Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> * Split get user by ID * Use new method in api * Add tests * Aplly emthod in auth info service * Fix lint and some tests * Fix get user by ID * Fix lint Remove unused fakes * Use split get user id in admin users * Use GetbyID in cli commands * Clean up after merge * Remove commented out code * Clena up imports * add back ) * Fix wire generation for runner after merge with main Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
3 years ago
userService user.Service
ctxHdlr *contexthandler.ContextHandler
}
func (sc *scenarioContext) exec() {
sc.m.ServeHTTP(sc.resp, sc.req)
}
type scenarioFunc func(c *scenarioContext)
type handlerFunc func(c *contextmodel.ReqContext) response.Response
func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHandler {
t.Helper()
if cfg == nil {
cfg = setting.NewCfg()
}
return contexthandler.ProvideService(
cfg,
tracing.InitializeTracerForTest(),
featuremgmt.WithFeatures(),
&authntest.FakeService{ExpectedIdentity: &authn.Identity{ID: authn.AnonymousNamespaceID, SessionToken: &usertoken.UserToken{}}},
)
}
func setupScenarioContext(t *testing.T, url string) *scenarioContext {
cfg := setting.NewCfg()
ctxHdlr := getContextHandler(t, cfg)
sc := &scenarioContext{
url: url,
t: t,
cfg: cfg,
ctxHdlr: ctxHdlr,
}
viewsPath, err := filepath.Abs("../../public/views")
require.NoError(t, err)
exists, err := fs.Exists(viewsPath)
require.NoError(t, err)
require.Truef(t, exists, "Views should be in %q", viewsPath)
sc.m = web.New()
sc.m.UseMiddleware(web.Renderer(viewsPath, "[[", "]]"))
sc.m.Use(ctxHdlr.Middleware)
return sc
}
func setupScenarioContextSamlLogout(t *testing.T, url string) *scenarioContext {
cfg := setting.NewCfg()
//seed sections and keys
cfg.Raw.DeleteSection("DEFAULT")
saml, err := cfg.Raw.NewSection("auth.saml")
assert.NoError(t, err)
_, err = saml.NewKey("enabled", "true")
assert.NoError(t, err)
_, err = saml.NewKey("allow_idp_initiated", "false")
assert.NoError(t, err)
_, err = saml.NewKey("single_logout", "true")
assert.NoError(t, err)
ctxHdlr := getContextHandler(t, cfg)
sc := &scenarioContext{
url: url,
t: t,
cfg: cfg,
ctxHdlr: ctxHdlr,
}
viewsPath, err := filepath.Abs("../../public/views")
require.NoError(t, err)
exists, err := fs.Exists(viewsPath)
require.NoError(t, err)
require.Truef(t, exists, "Views should be in %q", viewsPath)
sc.m = web.New()
sc.m.UseMiddleware(web.Renderer(viewsPath, "[[", "]]"))
sc.m.Use(ctxHdlr.Middleware)
return sc
}
// FIXME: This user should not be anonymous
func authedUserWithPermissions(userID, orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser {
return &user.SignedInUser{UserID: userID, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByActionContext(context.Background(), permissions)}}
}
// FIXME: This user should not be anonymous
func userWithPermissions(orgID int64, permissions []accesscontrol.Permission) *user.SignedInUser {
return &user.SignedInUser{IsAnonymous: true, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByActionContext(context.Background(), permissions)}}
}
func setupSimpleHTTPServer(features featuremgmt.FeatureToggles) *HTTPServer {
if features == nil {
features = featuremgmt.WithFeatures()
}
// nolint:staticcheck
cfg := setting.NewCfgWithFeatures(features.IsEnabledGlobally)
return &HTTPServer{
Cfg: cfg,
Features: features,
License: &licensing.OSSLicensingService{},
AccessControl: acimpl.ProvideAccessControl(featuremgmt.WithFeatures()),
annotationsRepo: annotationstest.NewFakeAnnotationsRepo(),
authInfoService: &authinfotest.FakeService{
ExpectedLabels: map[int64]string{int64(1): login.GetAuthProviderLabel(login.LDAPAuthModule)},
},
tracer: tracing.InitializeTracerForTest(),
}
}
func mockRequestBody(v any) io.ReadCloser {
b, _ := json.Marshal(v)
return io.NopCloser(bytes.NewReader(b))
}
// APITestServerOption option func for customizing HTTPServer configuration
// when setting up an API test server via SetupAPITestServer.
type APITestServerOption func(hs *HTTPServer)
// SetupAPITestServer sets up a webtest.Server ready for testing all
// routes registered via HTTPServer.registerRoutes().
// Optionally customize HTTPServer configuration by providing APITestServerOption
// option(s).
func SetupAPITestServer(t *testing.T, opts ...APITestServerOption) *webtest.Server {
t.Helper()
hs := &HTTPServer{
RouteRegister: routing.NewRouteRegister(),
License: &licensing.OSSLicensingService{},
Features: featuremgmt.WithFeatures(),
QuotaService: quotatest.New(false, nil),
searchUsersService: &searchusers.OSSService{},
tracer: tracing.InitializeTracerForTest(),
}
for _, opt := range opts {
opt(hs)
}
if hs.Cfg == nil {
hs.Cfg = setting.NewCfg()
}
if hs.AccessControl == nil {
hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures())
}
hs.registerRoutes()
s := webtest.NewServer(t, hs.RouteRegister)
viewsPath, err := filepath.Abs("../../public/views")
require.NoError(t, err)
s.Mux.UseMiddleware(web.Renderer(viewsPath, "[[", "]]"))
return s
}
type mockSearchService struct{ ExpectedResult model.HitList }
func (mss *mockSearchService) SearchHandler(_ context.Context, q *search.Query) (model.HitList, error) {
return mss.ExpectedResult, nil
}
func (mss *mockSearchService) SortOptions() []model.SortOption { return nil }