mirror of
https://github.com/grafana/grafana.git
synced 2026-08-18 17:15:08 -05:00
Adding FGAC annotations validation for creation and deletion (#46736)
Access Control: Adding FGAC annotations validation for creation and deletion Co-authored-by: IevaVasiljeva <ieva.vasiljeva@grafana.com>
This commit is contained in:
@@ -274,15 +274,17 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
|||||||
Grants: []string{string(models.ROLE_VIEWER)},
|
Grants: []string{string(models.ROLE_VIEWER)},
|
||||||
}
|
}
|
||||||
|
|
||||||
localAnnotationsWriterRole := ac.RoleRegistration{
|
dashboardAnnotationsWriterRole := ac.RoleRegistration{
|
||||||
Role: ac.RoleDTO{
|
Role: ac.RoleDTO{
|
||||||
Name: "fixed:annotations.local:writer",
|
Name: "fixed:annotations.dashboard:writer",
|
||||||
DisplayName: "Local annotation writer",
|
DisplayName: "Dashboard annotation writer",
|
||||||
Description: "Update annotations associated with dashboards.",
|
Description: "Update annotations associated with dashboards.",
|
||||||
Group: "Annotations",
|
Group: "Annotations",
|
||||||
Version: 1,
|
Version: 2,
|
||||||
Permissions: []ac.Permission{
|
Permissions: []ac.Permission{
|
||||||
{Action: ac.ActionAnnotationsWrite, Scope: ac.ScopeAnnotationsTypeLocal},
|
{Action: ac.ActionAnnotationsCreate},
|
||||||
|
{Action: ac.ActionAnnotationsDelete, Scope: ac.ScopeAnnotationsTypeDashboard},
|
||||||
|
{Action: ac.ActionAnnotationsWrite, Scope: ac.ScopeAnnotationsTypeDashboard},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Grants: []string{string(models.ROLE_VIEWER)},
|
Grants: []string{string(models.ROLE_VIEWER)},
|
||||||
@@ -296,6 +298,8 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
|||||||
Group: "Annotations",
|
Group: "Annotations",
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Permissions: []ac.Permission{
|
Permissions: []ac.Permission{
|
||||||
|
{Action: ac.ActionAnnotationsCreate},
|
||||||
|
{Action: ac.ActionAnnotationsDelete, Scope: ac.ScopeAnnotationsAll},
|
||||||
{Action: ac.ActionAnnotationsWrite, Scope: ac.ScopeAnnotationsAll},
|
{Action: ac.ActionAnnotationsWrite, Scope: ac.ScopeAnnotationsAll},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -405,7 +409,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
|||||||
provisioningWriterRole, datasourcesReaderRole, datasourcesWriterRole, datasourcesIdReaderRole,
|
provisioningWriterRole, datasourcesReaderRole, datasourcesWriterRole, datasourcesIdReaderRole,
|
||||||
datasourcesCompatibilityReaderRole, orgReaderRole, orgWriterRole,
|
datasourcesCompatibilityReaderRole, orgReaderRole, orgWriterRole,
|
||||||
orgMaintainerRole, teamsCreatorRole, teamsWriterRole, datasourcesExplorerRole,
|
orgMaintainerRole, teamsCreatorRole, teamsWriterRole, datasourcesExplorerRole,
|
||||||
annotationsReaderRole, localAnnotationsWriterRole, annotationsWriterRole,
|
annotationsReaderRole, dashboardAnnotationsWriterRole, annotationsWriterRole,
|
||||||
dashboardsCreatorRole, dashboardsReaderRole, dashboardsWriterRole,
|
dashboardsCreatorRole, dashboardsReaderRole, dashboardsWriterRole,
|
||||||
foldersCreatorRole, foldersReaderRole, foldersWriterRole, apikeyWriterRole,
|
foldersCreatorRole, foldersReaderRole, foldersWriterRole, apikeyWriterRole,
|
||||||
)
|
)
|
||||||
|
|||||||
+35
-18
@@ -66,10 +66,18 @@ func (hs *HTTPServer) PostAnnotation(c *models.ReqContext) response.Response {
|
|||||||
var canSave bool
|
var canSave bool
|
||||||
var err error
|
var err error
|
||||||
if cmd.DashboardId != 0 {
|
if cmd.DashboardId != 0 {
|
||||||
canSave, err = canSaveLocalAnnotation(c, cmd.DashboardId)
|
canSave, err = canSaveDashboardAnnotation(c, cmd.DashboardId)
|
||||||
} else {
|
} else { // organization annotations
|
||||||
canSave = canSaveGlobalAnnotation(c)
|
if !hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) {
|
||||||
|
canSave = canSaveOrganizationAnnotation(c)
|
||||||
|
} else {
|
||||||
|
// This is an additional validation needed only for FGAC Organization Annotations.
|
||||||
|
// It is not possible to do it in the middleware because we need to look
|
||||||
|
// into the request to determine if this is a Organization annotation or not
|
||||||
|
canSave, err = hs.canCreateOrganizationAnnotation(c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil || !canSave {
|
if err != nil || !canSave {
|
||||||
return dashboardGuardianResponse(err)
|
return dashboardGuardianResponse(err)
|
||||||
}
|
}
|
||||||
@@ -190,13 +198,14 @@ func (hs *HTTPServer) UpdateAnnotation(c *models.ReqContext) response.Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canSave := true
|
canSave := true
|
||||||
if annotation.GetType() == annotations.Local {
|
if annotation.GetType() == annotations.Dashboard {
|
||||||
canSave, err = canSaveLocalAnnotation(c, annotation.DashboardId)
|
canSave, err = canSaveDashboardAnnotation(c, annotation.DashboardId)
|
||||||
} else {
|
} else {
|
||||||
if !hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) {
|
if !hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) {
|
||||||
canSave = canSaveGlobalAnnotation(c)
|
canSave = canSaveOrganizationAnnotation(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil || !canSave {
|
if err != nil || !canSave {
|
||||||
return dashboardGuardianResponse(err)
|
return dashboardGuardianResponse(err)
|
||||||
}
|
}
|
||||||
@@ -236,11 +245,11 @@ func (hs *HTTPServer) PatchAnnotation(c *models.ReqContext) response.Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canSave := true
|
canSave := true
|
||||||
if annotation.GetType() == annotations.Local {
|
if annotation.GetType() == annotations.Dashboard {
|
||||||
canSave, err = canSaveLocalAnnotation(c, annotation.DashboardId)
|
canSave, err = canSaveDashboardAnnotation(c, annotation.DashboardId)
|
||||||
} else {
|
} else {
|
||||||
if !hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) {
|
if !hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) {
|
||||||
canSave = canSaveGlobalAnnotation(c)
|
canSave = canSaveOrganizationAnnotation(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil || !canSave {
|
if err != nil || !canSave {
|
||||||
@@ -314,12 +323,15 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
|
|||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
var canSave bool
|
canSave := true
|
||||||
if annotation.GetType() == annotations.Local {
|
if annotation.GetType() == annotations.Dashboard {
|
||||||
canSave, err = canSaveLocalAnnotation(c, annotation.DashboardId)
|
canSave, err = canSaveDashboardAnnotation(c, annotation.DashboardId)
|
||||||
} else {
|
} else {
|
||||||
canSave = canSaveGlobalAnnotation(c)
|
if !hs.Features.IsEnabled(featuremgmt.FlagAccesscontrol) {
|
||||||
|
canSave = canSaveOrganizationAnnotation(c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil || !canSave {
|
if err != nil || !canSave {
|
||||||
return dashboardGuardianResponse(err)
|
return dashboardGuardianResponse(err)
|
||||||
}
|
}
|
||||||
@@ -335,7 +347,7 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
|
|||||||
return response.Success("Annotation deleted")
|
return response.Success("Annotation deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
func canSaveLocalAnnotation(c *models.ReqContext, dashboardID int64) (bool, error) {
|
func canSaveDashboardAnnotation(c *models.ReqContext, dashboardID int64) (bool, error) {
|
||||||
guard := guardian.New(c.Req.Context(), dashboardID, c.OrgId, c.SignedInUser)
|
guard := guardian.New(c.Req.Context(), dashboardID, c.OrgId, c.SignedInUser)
|
||||||
if canEdit, err := guard.CanEdit(); err != nil || !canEdit {
|
if canEdit, err := guard.CanEdit(); err != nil || !canEdit {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -344,7 +356,7 @@ func canSaveLocalAnnotation(c *models.ReqContext, dashboardID int64) (bool, erro
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func canSaveGlobalAnnotation(c *models.ReqContext) bool {
|
func canSaveOrganizationAnnotation(c *models.ReqContext) bool {
|
||||||
return c.SignedInUser.HasRole(models.ROLE_EDITOR)
|
return c.SignedInUser.HasRole(models.ROLE_EDITOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,11 +411,16 @@ func AnnotationTypeScopeResolver() (string, accesscontrol.AttributeScopeResolveF
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if annotation.GetType() == annotations.Global {
|
if annotation.GetType() == annotations.Organization {
|
||||||
return accesscontrol.ScopeAnnotationsTypeGlobal, nil
|
return accesscontrol.ScopeAnnotationsTypeOrganization, nil
|
||||||
} else {
|
} else {
|
||||||
return accesscontrol.ScopeAnnotationsTypeLocal, nil
|
return accesscontrol.ScopeAnnotationsTypeDashboard, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return accesscontrol.ScopeAnnotationsProvider.GetResourceScope(""), annotationTypeResolver
|
return accesscontrol.ScopeAnnotationsProvider.GetResourceScope(""), annotationTypeResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hs *HTTPServer) canCreateOrganizationAnnotation(c *models.ReqContext) (bool, error) {
|
||||||
|
evaluator := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsCreate, accesscontrol.ScopeAnnotationsTypeOrganization)
|
||||||
|
return hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, evaluator)
|
||||||
|
}
|
||||||
|
|||||||
+251
-17
@@ -396,20 +396,47 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
|
|||||||
_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID)
|
_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
dashboardAnnotation := annotations.ItemDTO{Id: 1, DashboardId: 1}
|
||||||
|
organizationAnnotation := annotations.ItemDTO{Id: 2, DashboardId: 0}
|
||||||
|
|
||||||
localAnnotation := annotations.Item{
|
fakeAnnoRepo = &fakeAnnotationsRepo{
|
||||||
OrgId: sc.initCtx.OrgId,
|
annotations: map[int64]annotations.ItemDTO{1: dashboardAnnotation, 2: organizationAnnotation},
|
||||||
|
}
|
||||||
|
annotations.SetRepository(fakeAnnoRepo)
|
||||||
|
|
||||||
|
postOrganizationCmd := dtos.PostAnnotationsCmd{
|
||||||
|
Time: 1000,
|
||||||
|
Text: "annotation text",
|
||||||
|
Tags: []string{"tag1", "tag2"},
|
||||||
|
PanelId: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
postDashboardCmd := dtos.PostAnnotationsCmd{
|
||||||
|
Time: 1000,
|
||||||
|
Text: "annotation text",
|
||||||
|
Tags: []string{"tag1", "tag2"},
|
||||||
DashboardId: 1,
|
DashboardId: 1,
|
||||||
}
|
PanelId: 1,
|
||||||
globalAnnotation := annotations.Item{
|
|
||||||
OrgId: sc.initCtx.OrgId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = repo.Save(&localAnnotation)
|
updateCmd := dtos.UpdateAnnotationsCmd{
|
||||||
require.NoError(t, err)
|
Time: 1000,
|
||||||
err = repo.Save(&globalAnnotation)
|
Text: "annotation text",
|
||||||
require.NoError(t, err)
|
Tags: []string{"tag1", "tag2"},
|
||||||
|
}
|
||||||
|
|
||||||
|
patchCmd := dtos.PatchAnnotationsCmd{
|
||||||
|
Time: 1000,
|
||||||
|
Text: "annotation text",
|
||||||
|
Tags: []string{"tag1", "tag2"},
|
||||||
|
}
|
||||||
|
|
||||||
|
postGraphiteCmd := dtos.PostGraphiteAnnotationsCmd{
|
||||||
|
When: 1000,
|
||||||
|
What: "annotation text",
|
||||||
|
Data: "Deploy",
|
||||||
|
Tags: []string{"tag1", "tag2"},
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
permissions []*accesscontrol.Permission
|
permissions []*accesscontrol.Permission
|
||||||
@@ -459,10 +486,217 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: http.StatusForbidden,
|
want: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl update dashboard annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/1",
|
||||||
|
method: http.MethodPut,
|
||||||
|
body: mockRequestBody(updateCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl update dashboard annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{},
|
||||||
|
url: "/api/annotations/1",
|
||||||
|
method: http.MethodPut,
|
||||||
|
body: mockRequestBody(updateCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl update organization annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsAll,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/2",
|
||||||
|
method: http.MethodPut,
|
||||||
|
body: mockRequestBody(updateCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl update organization annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/2",
|
||||||
|
method: http.MethodPut,
|
||||||
|
body: mockRequestBody(updateCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl patch dashboard annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/1",
|
||||||
|
method: http.MethodPatch,
|
||||||
|
body: mockRequestBody(patchCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl patch dashboard annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{},
|
||||||
|
url: "/api/annotations/1",
|
||||||
|
method: http.MethodPatch,
|
||||||
|
body: mockRequestBody(patchCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl patch organization annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsAll,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/2",
|
||||||
|
method: http.MethodPatch,
|
||||||
|
body: mockRequestBody(patchCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl patch organization annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsWrite, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/2",
|
||||||
|
method: http.MethodPatch,
|
||||||
|
body: mockRequestBody(patchCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl create dashboard annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsCreate, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations",
|
||||||
|
method: http.MethodPost,
|
||||||
|
body: mockRequestBody(postDashboardCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl create dashboard annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{},
|
||||||
|
url: "/api/annotations",
|
||||||
|
method: http.MethodPost,
|
||||||
|
body: mockRequestBody(postDashboardCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl create organization annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsCreate, Scope: accesscontrol.ScopeAnnotationsAll,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations",
|
||||||
|
method: http.MethodPost,
|
||||||
|
body: mockRequestBody(postOrganizationCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl create organization annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsCreate, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations",
|
||||||
|
method: http.MethodPost,
|
||||||
|
body: mockRequestBody(postOrganizationCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl delete dashboard annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsDelete, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/1",
|
||||||
|
method: http.MethodDelete,
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl delete dashboard annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{},
|
||||||
|
url: "/api/annotations/1",
|
||||||
|
method: http.MethodDelete,
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl delete organization annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsDelete, Scope: accesscontrol.ScopeAnnotationsAll,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/2",
|
||||||
|
method: http.MethodDelete,
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl delete organization annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsDelete, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/2",
|
||||||
|
method: http.MethodDelete,
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl create graphite annotation with permissions is allowed",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsCreate, Scope: accesscontrol.ScopeAnnotationsAll,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/graphite",
|
||||||
|
method: http.MethodPost,
|
||||||
|
body: mockRequestBody(postGraphiteCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AccessControl create organization annotation without permissions is forbidden",
|
||||||
|
args: args{
|
||||||
|
permissions: []*accesscontrol.Permission{{
|
||||||
|
Action: accesscontrol.ActionAnnotationsCreate, Scope: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
|
}},
|
||||||
|
url: "/api/annotations/graphite",
|
||||||
|
method: http.MethodPost,
|
||||||
|
body: mockRequestBody(postGraphiteCmd),
|
||||||
|
},
|
||||||
|
want: http.StatusForbidden,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
setUpACL()
|
||||||
|
sc.acmock.RegisterAttributeScopeResolver(AnnotationTypeScopeResolver())
|
||||||
setAccessControlPermissions(sc.acmock, tt.args.permissions, sc.initCtx.OrgId)
|
setAccessControlPermissions(sc.acmock, tt.args.permissions, sc.initCtx.OrgId)
|
||||||
|
|
||||||
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
|
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
|
||||||
assert.Equalf(t, tt.want, r.Code, "Annotations API(%v)", tt.args.url)
|
assert.Equalf(t, tt.want, r.Code, "Annotations API(%v)", tt.args.url)
|
||||||
})
|
})
|
||||||
@@ -479,15 +713,15 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []testCaseResolver{
|
testCases := []testCaseResolver{
|
||||||
{
|
{
|
||||||
desc: "correctly resolves local annotations",
|
desc: "correctly resolves dashboard annotations",
|
||||||
given: "annotations:id:1",
|
given: "annotations:id:1",
|
||||||
want: accesscontrol.ScopeAnnotationsTypeLocal,
|
want: accesscontrol.ScopeAnnotationsTypeDashboard,
|
||||||
wantErr: nil,
|
wantErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "correctly resolves global annotations",
|
desc: "correctly resolves organization annotations",
|
||||||
given: "annotations:id:2",
|
given: "annotations:id:2",
|
||||||
want: accesscontrol.ScopeAnnotationsTypeGlobal,
|
want: accesscontrol.ScopeAnnotationsTypeOrganization,
|
||||||
wantErr: nil,
|
wantErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -504,11 +738,11 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
localAnnotation := annotations.ItemDTO{Id: 1, DashboardId: 1}
|
dashboardAnnotation := annotations.ItemDTO{Id: 1, DashboardId: 1}
|
||||||
globalAnnotation := annotations.ItemDTO{Id: 2}
|
organizationAnnotation := annotations.ItemDTO{Id: 2}
|
||||||
|
|
||||||
fakeAnnoRepo = &fakeAnnotationsRepo{
|
fakeAnnoRepo = &fakeAnnotationsRepo{
|
||||||
annotations: map[int64]annotations.ItemDTO{1: localAnnotation, 2: globalAnnotation},
|
annotations: map[int64]annotations.ItemDTO{1: dashboardAnnotation, 2: organizationAnnotation},
|
||||||
}
|
}
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
annotations.SetRepository(fakeAnnoRepo)
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -440,11 +440,11 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, routing.Wrap(hs.DeleteAnnotations))
|
apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, routing.Wrap(hs.DeleteAnnotations))
|
||||||
|
|
||||||
apiRoute.Group("/annotations", func(annotationsRoute routing.RouteRegister) {
|
apiRoute.Group("/annotations", func(annotationsRoute routing.RouteRegister) {
|
||||||
annotationsRoute.Post("/", routing.Wrap(hs.PostAnnotation))
|
annotationsRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsCreate)), routing.Wrap(hs.PostAnnotation))
|
||||||
annotationsRoute.Delete("/:annotationId", routing.Wrap(hs.DeleteAnnotationByID))
|
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.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.Patch("/:annotationId", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsWrite, ac.ScopeAnnotationsID)), routing.Wrap(hs.PatchAnnotation))
|
||||||
annotationsRoute.Post("/graphite", reqEditorRole, routing.Wrap(hs.PostGraphiteAnnotation))
|
annotationsRoute.Post("/graphite", authorize(reqEditorRole, ac.EvalPermission(ac.ActionAnnotationsCreate, ac.ScopeAnnotationsTypeOrganization)), routing.Wrap(hs.PostGraphiteAnnotation))
|
||||||
annotationsRoute.Get("/tags", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsTagsRead, ac.ScopeAnnotationsTagsAll)), routing.Wrap(hs.GetAnnotationTags))
|
annotationsRoute.Get("/tags", authorize(reqSignedIn, ac.EvalPermission(ac.ActionAnnotationsTagsRead, ac.ScopeAnnotationsTagsAll)), routing.Wrap(hs.GetAnnotationTags))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ type GetAnnotationsParams struct {
|
|||||||
// in:query
|
// in:query
|
||||||
// required:false
|
// required:false
|
||||||
Limit int64 `json:"limit"`
|
Limit int64 `json:"limit"`
|
||||||
// Use this to filter global annotations. Global annotations are annotations from an annotation data source that are not connected specifically to a dashboard or panel. You can filter by multiple tags.
|
// Use this to filter global annotations. Organization annotations are annotations from an annotation data source that are not connected specifically to a dashboard or panel. You can filter by multiple tags.
|
||||||
// in:query
|
// in:query
|
||||||
// required:false
|
// required:false
|
||||||
// type: array
|
// type: array
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("When matching route path", func(t *testing.T) {
|
t.Run("When matching route path", func(t *testing.T) {
|
||||||
ctx, req := setUp()
|
ctx, req := setUp()
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider,
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider,
|
||||||
&oauthtoken.Service{}, dsService, tracer, secretsService)
|
&oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -143,7 +143,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("When matching route path and has dynamic url", func(t *testing.T) {
|
t.Run("When matching route path and has dynamic url", func(t *testing.T) {
|
||||||
ctx, req := setUp()
|
ctx, req := setUp()
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/common/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/common/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
proxy.matchedRoute = routes[3]
|
proxy.matchedRoute = routes[3]
|
||||||
@@ -155,7 +155,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("When matching route path with no url", func(t *testing.T) {
|
t.Run("When matching route path with no url", func(t *testing.T) {
|
||||||
ctx, req := setUp()
|
ctx, req := setUp()
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
proxy.matchedRoute = routes[4]
|
proxy.matchedRoute = routes[4]
|
||||||
@@ -166,7 +166,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("When matching route path and has dynamic body", func(t *testing.T) {
|
t.Run("When matching route path and has dynamic body", func(t *testing.T) {
|
||||||
ctx, req := setUp()
|
ctx, req := setUp()
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/body", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/body", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
proxy.matchedRoute = routes[5]
|
proxy.matchedRoute = routes[5]
|
||||||
@@ -180,7 +180,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
t.Run("Validating request", func(t *testing.T) {
|
t.Run("Validating request", func(t *testing.T) {
|
||||||
t.Run("plugin route with valid role", func(t *testing.T) {
|
t.Run("plugin route with valid role", func(t *testing.T) {
|
||||||
ctx, _ := setUp()
|
ctx, _ := setUp()
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/v4/some/method", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = proxy.validateRequest()
|
err = proxy.validateRequest()
|
||||||
@@ -189,7 +189,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("plugin route with admin role and user is editor", func(t *testing.T) {
|
t.Run("plugin route with admin role and user is editor", func(t *testing.T) {
|
||||||
ctx, _ := setUp()
|
ctx, _ := setUp()
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = proxy.validateRequest()
|
err = proxy.validateRequest()
|
||||||
@@ -199,7 +199,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
t.Run("plugin route with admin role and user is admin", func(t *testing.T) {
|
t.Run("plugin route with admin role and user is admin", func(t *testing.T) {
|
||||||
ctx, _ := setUp()
|
ctx, _ := setUp()
|
||||||
ctx.SignedInUser.OrgRole = models.ROLE_ADMIN
|
ctx.SignedInUser.OrgRole = models.ROLE_ADMIN
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "api/admin", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = proxy.validateRequest()
|
err = proxy.validateRequest()
|
||||||
@@ -290,7 +290,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg)
|
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg)
|
||||||
@@ -306,7 +306,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
|
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
client = newFakeHTTPClient(t, json2)
|
client = newFakeHTTPClient(t, json2)
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken2", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken2", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[1], dsInfo, cfg)
|
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[1], dsInfo, cfg)
|
||||||
@@ -323,7 +323,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
client = newFakeHTTPClient(t, []byte{})
|
client = newFakeHTTPClient(t, []byte{})
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "pathwithtoken1", cfg, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg)
|
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, routes[0], dsInfo, cfg)
|
||||||
@@ -345,7 +345,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
ctx := &models.ReqContext{}
|
ctx := &models.ReqContext{}
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{BuildVersion: "5.3.0"}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{BuildVersion: "5.3.0"}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||||
@@ -371,7 +371,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
ctx := &models.ReqContext{}
|
ctx := &models.ReqContext{}
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
ctx := &models.ReqContext{}
|
ctx := &models.ReqContext{}
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -423,7 +423,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
ctx := &models.ReqContext{}
|
ctx := &models.ReqContext{}
|
||||||
var pluginRoutes []*plugins.Route
|
var pluginRoutes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, pluginRoutes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, pluginRoutes, ctx, "", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -446,7 +446,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
ctx := &models.ReqContext{}
|
ctx := &models.ReqContext{}
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||||
@@ -510,7 +510,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
|
|||||||
|
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &mockAuthToken, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/to/folder/", &setting.Cfg{}, httpClientProvider, &mockAuthToken, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||||
@@ -643,7 +643,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
|
|||||||
ctx, ds := setUp(t)
|
ctx, ds := setUp(t)
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -661,7 +661,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
|
|||||||
})
|
})
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -675,7 +675,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
|
|||||||
ctx, ds := setUp(t)
|
ctx, ds := setUp(t)
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -697,7 +697,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
|
|||||||
})
|
})
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/render", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -722,7 +722,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
|
|||||||
ctx.Req = httptest.NewRequest("GET", "/api/datasources/proxy/1/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", nil)
|
ctx.Req = httptest.NewRequest("GET", "/api/datasources/proxy/1/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", nil)
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -746,7 +746,7 @@ func TestDataSourceProxy_requestHandling(t *testing.T) {
|
|||||||
ctx.Req = httptest.NewRequest("GET", "/api/datasources/proxy/1/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", nil)
|
ctx.Req = httptest.NewRequest("GET", "/api/datasources/proxy/1/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F", nil)
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "/path/%2Ftest%2Ftest%2F", &setting.Cfg{}, httpClientProvider, &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -771,7 +771,7 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
_, err = NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
_, err = NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.True(t, strings.HasPrefix(err.Error(), `validation of data source URL "://host/root" failed`))
|
assert.True(t, strings.HasPrefix(err.Error(), `validation of data source URL "://host/root" failed`))
|
||||||
@@ -792,7 +792,7 @@ func TestNewDataSourceProxy_ProtocolLessURL(t *testing.T) {
|
|||||||
|
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
_, err = NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
_, err = NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -835,7 +835,7 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
|
|||||||
|
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
p, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
p, err := NewDataSourceProxy(&ds, routes, &ctx, "api/method", &cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
if tc.err == nil {
|
if tc.err == nil {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -862,7 +862,7 @@ func getDatasourceProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *sett
|
|||||||
|
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(ds, routes, ctx, "", cfg, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||||
@@ -986,7 +986,7 @@ func runDatasourceAuthTest(t *testing.T, secretsService secrets.Service, test *t
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var routes []*plugins.Route
|
var routes []*plugins.Route
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(test.datasource, routes, ctx, "", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(test.datasource, routes, ctx, "", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -1028,7 +1028,7 @@ func Test_PathCheck(t *testing.T) {
|
|||||||
}
|
}
|
||||||
ctx, _ := setUp()
|
ctx, _ := setUp()
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
proxy, err := NewDataSourceProxy(&models.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
proxy, err := NewDataSourceProxy(&models.DataSource{}, routes, ctx, "b", &setting.Cfg{}, httpclient.NewProvider(), &oauthtoken.Service{}, dsService, tracer, secretsService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
|||||||
hs := &HTTPServer{
|
hs := &HTTPServer{
|
||||||
Cfg: settings,
|
Cfg: settings,
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
AccessControl: &acmock.Mock{},
|
AccessControl: acmock.New(),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockResult := models.SearchUserQueryResult{
|
mockResult := models.SearchUserQueryResult{
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ type Mock struct {
|
|||||||
GetUserBuiltInRolesFunc func(user *models.SignedInUser) []string
|
GetUserBuiltInRolesFunc func(user *models.SignedInUser) []string
|
||||||
RegisterFixedRolesFunc func() error
|
RegisterFixedRolesFunc func() error
|
||||||
RegisterAttributeScopeResolverFunc func(string, accesscontrol.AttributeScopeResolveFunc)
|
RegisterAttributeScopeResolverFunc func(string, accesscontrol.AttributeScopeResolveFunc)
|
||||||
|
|
||||||
|
scopeResolver accesscontrol.ScopeResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the mock stays in line with the interface
|
// Ensure the mock stays in line with the interface
|
||||||
@@ -53,10 +55,11 @@ var _ fullAccessControl = New()
|
|||||||
|
|
||||||
func New() *Mock {
|
func New() *Mock {
|
||||||
mock := &Mock{
|
mock := &Mock{
|
||||||
Calls: Calls{},
|
Calls: Calls{},
|
||||||
disabled: false,
|
disabled: false,
|
||||||
permissions: []*accesscontrol.Permission{},
|
permissions: []*accesscontrol.Permission{},
|
||||||
builtInRoles: []string{},
|
builtInRoles: []string{},
|
||||||
|
scopeResolver: accesscontrol.NewScopeResolver(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return mock
|
return mock
|
||||||
@@ -90,13 +93,18 @@ func (m *Mock) Evaluate(ctx context.Context, user *models.SignedInUser, evaluato
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return evaluator.Evaluate(accesscontrol.GroupScopesByAction(permissions))
|
|
||||||
|
attributeMutator := m.scopeResolver.GetResolveAttributeScopeMutator(user.OrgId)
|
||||||
|
resolvedEvaluator, err := evaluator.MutateScopes(ctx, attributeMutator)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return resolvedEvaluator.Evaluate(accesscontrol.GroupScopesByAction(permissions))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserPermissions returns user permissions.
|
// GetUserPermissions returns user permissions.
|
||||||
// This mock return m.permissions unless an override is provided.
|
// This mock return m.permissions unless an override is provided.
|
||||||
func (m *Mock) GetUserPermissions(ctx context.Context, user *models.SignedInUser,
|
func (m *Mock) GetUserPermissions(ctx context.Context, user *models.SignedInUser, opts accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
||||||
opts accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
|
||||||
m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user, opts})
|
m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user, opts})
|
||||||
// Use override if provided
|
// Use override if provided
|
||||||
if m.GetUserPermissionsFunc != nil {
|
if m.GetUserPermissionsFunc != nil {
|
||||||
@@ -169,6 +177,7 @@ func (m *Mock) RegisterFixedRoles() error {
|
|||||||
// RegisterAttributeScopeResolver allows the caller to register a scope resolver for a
|
// RegisterAttributeScopeResolver allows the caller to register a scope resolver for a
|
||||||
// specific scope prefix (ex: datasources:name:)
|
// specific scope prefix (ex: datasources:name:)
|
||||||
func (m *Mock) RegisterAttributeScopeResolver(scopePrefix string, resolver accesscontrol.AttributeScopeResolveFunc) {
|
func (m *Mock) RegisterAttributeScopeResolver(scopePrefix string, resolver accesscontrol.AttributeScopeResolveFunc) {
|
||||||
|
m.scopeResolver.AddAttributeResolver(scopePrefix, resolver)
|
||||||
m.Calls.RegisterAttributeScopeResolver = append(m.Calls.RegisterAttributeScopeResolver, []struct{}{})
|
m.Calls.RegisterAttributeScopeResolver = append(m.Calls.RegisterAttributeScopeResolver, []struct{}{})
|
||||||
// Use override if provided
|
// Use override if provided
|
||||||
if m.RegisterAttributeScopeResolverFunc != nil {
|
if m.RegisterAttributeScopeResolverFunc != nil {
|
||||||
|
|||||||
@@ -319,6 +319,8 @@ const (
|
|||||||
ScopeTeamsAll = "teams:*"
|
ScopeTeamsAll = "teams:*"
|
||||||
|
|
||||||
// Annotations related actions
|
// Annotations related actions
|
||||||
|
ActionAnnotationsCreate = "annotations:create"
|
||||||
|
ActionAnnotationsDelete = "annotations:delete"
|
||||||
ActionAnnotationsRead = "annotations:read"
|
ActionAnnotationsRead = "annotations:read"
|
||||||
ActionAnnotationsWrite = "annotations:write"
|
ActionAnnotationsWrite = "annotations:write"
|
||||||
ActionAnnotationsTagsRead = "annotations.tags:read"
|
ActionAnnotationsTagsRead = "annotations.tags:read"
|
||||||
@@ -373,12 +375,12 @@ var (
|
|||||||
ScopeTeamsID = Scope("teams", "id", Parameter(":teamId"))
|
ScopeTeamsID = Scope("teams", "id", Parameter(":teamId"))
|
||||||
|
|
||||||
// Annotation scopes
|
// Annotation scopes
|
||||||
ScopeAnnotationsRoot = "annotations"
|
ScopeAnnotationsRoot = "annotations"
|
||||||
ScopeAnnotationsProvider = NewScopeProvider(ScopeAnnotationsRoot)
|
ScopeAnnotationsProvider = NewScopeProvider(ScopeAnnotationsRoot)
|
||||||
ScopeAnnotationsAll = ScopeAnnotationsProvider.GetResourceAllScope()
|
ScopeAnnotationsAll = ScopeAnnotationsProvider.GetResourceAllScope()
|
||||||
ScopeAnnotationsID = Scope(ScopeAnnotationsRoot, "id", Parameter(":annotationId"))
|
ScopeAnnotationsID = Scope(ScopeAnnotationsRoot, "id", Parameter(":annotationId"))
|
||||||
ScopeAnnotationsTypeLocal = ScopeAnnotationsProvider.GetResourceScopeType("dashboard")
|
ScopeAnnotationsTypeDashboard = ScopeAnnotationsProvider.GetResourceScopeType("dashboard")
|
||||||
ScopeAnnotationsTypeGlobal = ScopeAnnotationsProvider.GetResourceScopeType("organization")
|
ScopeAnnotationsTypeOrganization = ScopeAnnotationsProvider.GetResourceScopeType("organization")
|
||||||
|
|
||||||
// Annotation tag scopes
|
// Annotation tag scopes
|
||||||
ScopeAnnotationsTagsAll = "annotations:tags:*"
|
ScopeAnnotationsTagsAll = "annotations:tags:*"
|
||||||
|
|||||||
@@ -149,13 +149,13 @@ type ItemDTO struct {
|
|||||||
type annotationType int
|
type annotationType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Global annotationType = iota
|
Organization annotationType = iota
|
||||||
Local
|
Dashboard
|
||||||
)
|
)
|
||||||
|
|
||||||
func (annotation *ItemDTO) GetType() annotationType {
|
func (annotation *ItemDTO) GetType() annotationType {
|
||||||
if annotation.DashboardId != 0 {
|
if annotation.DashboardId != 0 {
|
||||||
return Local
|
return Dashboard
|
||||||
}
|
}
|
||||||
return Global
|
return Organization
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func TestService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
|
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
|
||||||
s := ProvideService(bus.New(), sqlStore, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
s := ProvideService(bus.New(), sqlStore, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
var ds *models.DataSource
|
var ds *models.DataSource
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ func TestService_GetHttpTransport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
rt1, err := dsService.GetHTTPTransport(&ds, provider)
|
rt1, err := dsService.GetHTTPTransport(&ds, provider)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -267,7 +267,7 @@ func TestService_GetHttpTransport(t *testing.T) {
|
|||||||
json.Set("tlsAuthWithCACert", true)
|
json.Set("tlsAuthWithCACert", true)
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
tlsCaCert, err := secretsService.Encrypt(context.Background(), []byte(caCert), secrets.WithoutScope())
|
tlsCaCert, err := secretsService.Encrypt(context.Background(), []byte(caCert), secrets.WithoutScope())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -317,7 +317,7 @@ func TestService_GetHttpTransport(t *testing.T) {
|
|||||||
json.Set("tlsAuth", true)
|
json.Set("tlsAuth", true)
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
tlsClientCert, err := secretsService.Encrypt(context.Background(), []byte(clientCert), secrets.WithoutScope())
|
tlsClientCert, err := secretsService.Encrypt(context.Background(), []byte(clientCert), secrets.WithoutScope())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -360,7 +360,7 @@ func TestService_GetHttpTransport(t *testing.T) {
|
|||||||
json.Set("serverName", "server-name")
|
json.Set("serverName", "server-name")
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
tlsCaCert, err := secretsService.Encrypt(context.Background(), []byte(caCert), secrets.WithoutScope())
|
tlsCaCert, err := secretsService.Encrypt(context.Background(), []byte(caCert), secrets.WithoutScope())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -397,7 +397,7 @@ func TestService_GetHttpTransport(t *testing.T) {
|
|||||||
json.Set("tlsSkipVerify", true)
|
json.Set("tlsSkipVerify", true)
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
ds := models.DataSource{
|
ds := models.DataSource{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
@@ -428,7 +428,7 @@ func TestService_GetHttpTransport(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
encryptedData, err := secretsService.Encrypt(context.Background(), []byte(`Bearer xf5yhfkpsnmgo`), secrets.WithoutScope())
|
encryptedData, err := secretsService.Encrypt(context.Background(), []byte(`Bearer xf5yhfkpsnmgo`), secrets.WithoutScope())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -487,7 +487,7 @@ func TestService_GetHttpTransport(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
ds := models.DataSource{
|
ds := models.DataSource{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
@@ -520,7 +520,7 @@ func TestService_GetHttpTransport(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
ds := models.DataSource{
|
ds := models.DataSource{
|
||||||
Type: models.DS_ES,
|
Type: models.DS_ES,
|
||||||
@@ -554,7 +554,7 @@ func TestService_getTimeout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
ds := &models.DataSource{
|
ds := &models.DataSource{
|
||||||
@@ -567,7 +567,7 @@ func TestService_getTimeout(t *testing.T) {
|
|||||||
func TestService_DecryptedValue(t *testing.T) {
|
func TestService_DecryptedValue(t *testing.T) {
|
||||||
t.Run("When datasource hasn't been updated, encrypted JSON should be fetched from cache", func(t *testing.T) {
|
t.Run("When datasource hasn't been updated, encrypted JSON should be fetched from cache", func(t *testing.T) {
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
encryptedJsonData, err := secretsService.EncryptJsonData(
|
encryptedJsonData, err := secretsService.EncryptJsonData(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
@@ -621,7 +621,7 @@ func TestService_DecryptedValue(t *testing.T) {
|
|||||||
SecureJsonData: encryptedJsonData,
|
SecureJsonData: encryptedJsonData,
|
||||||
}
|
}
|
||||||
|
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
// Populate cache
|
// Populate cache
|
||||||
password, ok := dsService.DecryptedValue(&ds, "password")
|
password, ok := dsService.DecryptedValue(&ds, "password")
|
||||||
@@ -657,7 +657,7 @@ func TestService_HTTPClientOptions(t *testing.T) {
|
|||||||
t.Cleanup(func() { ds.JsonData = emptyJsonData; ds.SecureJsonData = emptySecureJsonData })
|
t.Cleanup(func() { ds.JsonData = emptyJsonData; ds.SecureJsonData = emptySecureJsonData })
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
opts, err := dsService.httpClientOptions(&ds)
|
opts, err := dsService.httpClientOptions(&ds)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -675,7 +675,7 @@ func TestService_HTTPClientOptions(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
opts, err := dsService.httpClientOptions(&ds)
|
opts, err := dsService.httpClientOptions(&ds)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -694,7 +694,7 @@ func TestService_HTTPClientOptions(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
_, err := dsService.httpClientOptions(&ds)
|
_, err := dsService.httpClientOptions(&ds)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
@@ -708,7 +708,7 @@ func TestService_HTTPClientOptions(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
|
|
||||||
opts, err := dsService.httpClientOptions(&ds)
|
opts, err := dsService.httpClientOptions(&ds)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func TestAnnotations(t *testing.T) {
|
|||||||
assert.Equal(t, int64(20), annotation2.Epoch)
|
assert.Equal(t, int64(20), annotation2.Epoch)
|
||||||
assert.Equal(t, int64(21), annotation2.EpochEnd)
|
assert.Equal(t, int64(21), annotation2.EpochEnd)
|
||||||
|
|
||||||
globalAnnotation1 := &annotations.Item{
|
organizationAnnotation1 := &annotations.Item{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
UserId: 1,
|
UserId: 1,
|
||||||
Text: "deploy",
|
Text: "deploy",
|
||||||
@@ -64,9 +64,9 @@ func TestAnnotations(t *testing.T) {
|
|||||||
Epoch: 15,
|
Epoch: 15,
|
||||||
Tags: []string{"deploy"},
|
Tags: []string{"deploy"},
|
||||||
}
|
}
|
||||||
err = repo.Save(globalAnnotation1)
|
err = repo.Save(organizationAnnotation1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Greater(t, globalAnnotation1.Id, int64(0))
|
assert.Greater(t, organizationAnnotation1.Id, int64(0))
|
||||||
|
|
||||||
globalAnnotation2 := &annotations.Item{
|
globalAnnotation2 := &annotations.Item{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func TestHandleRequest(t *testing.T) {
|
|||||||
return backend.NewQueryDataResponse(), nil
|
return backend.NewQueryDataResponse(), nil
|
||||||
}
|
}
|
||||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||||
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), &acmock.Mock{}, acmock.NewPermissionsServicesMock())
|
dsService := datasourceservice.ProvideService(bus.New(), nil, secretsService, featuremgmt.WithFeatures(), acmock.New(), acmock.NewPermissionsServicesMock())
|
||||||
s := ProvideService(client, nil, dsService)
|
s := ProvideService(client, nil, dsService)
|
||||||
|
|
||||||
ds := &models.DataSource{Id: 12, Type: "unregisteredType", JsonData: simplejson.New()}
|
ds := &models.DataSource{Id: 12, Type: "unregisteredType", JsonData: simplejson.New()}
|
||||||
|
|||||||
Reference in New Issue
Block a user