Access control: SQL filtering for annotation listing (#47467)

* pass in user to attribute scope resolver

* add SQL filter to annotation listing

* check annotation FGAC permissions before exposing them for commenting

* remove the requirement to be able to list all annotations from annotation listing endpoint

* adding tests for annotation listing

* remove changes that got moved to a different PR

* unused var

* Update pkg/services/sqlstore/annotation.go

Co-authored-by: Ezequiel Victorero <evictorero@gmail.com>

* remove unneeded check

* remove unneeded check

* undo accidental change

* undo accidental change

* doc update

* move tests

* redo the approach for passing the user in for scope resolution

* accidental change

* cleanup

* error handling

Co-authored-by: Ezequiel Victorero <evictorero@gmail.com>
This commit is contained in:
Ieva
2022-04-11 13:18:38 +01:00
committed by GitHub
co-authored by Ezequiel Victorero
parent 4bc582570e
commit ef4c2672b3
10 changed files with 258 additions and 39 deletions
+37 -19
View File
@@ -20,17 +20,18 @@ import (
func (hs *HTTPServer) GetAnnotations(c *models.ReqContext) response.Response {
query := &annotations.ItemQuery{
From: c.QueryInt64("from"),
To: c.QueryInt64("to"),
OrgId: c.OrgId,
UserId: c.QueryInt64("userId"),
AlertId: c.QueryInt64("alertId"),
DashboardId: c.QueryInt64("dashboardId"),
PanelId: c.QueryInt64("panelId"),
Limit: c.QueryInt64("limit"),
Tags: c.QueryStrings("tags"),
Type: c.Query("type"),
MatchAny: c.QueryBool("matchAny"),
From: c.QueryInt64("from"),
To: c.QueryInt64("to"),
OrgId: c.OrgId,
UserId: c.QueryInt64("userId"),
AlertId: c.QueryInt64("alertId"),
DashboardId: c.QueryInt64("dashboardId"),
PanelId: c.QueryInt64("panelId"),
Limit: c.QueryInt64("limit"),
Tags: c.QueryStrings("tags"),
Type: c.Query("type"),
MatchAny: c.QueryBool("matchAny"),
SignedInUser: c.SignedInUser,
}
repo := annotations.GetRepository()
@@ -192,7 +193,7 @@ func (hs *HTTPServer) UpdateAnnotation(c *models.ReqContext) response.Response {
repo := annotations.GetRepository()
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.OrgId)
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
if resp != nil {
return resp
}
@@ -239,7 +240,7 @@ func (hs *HTTPServer) PatchAnnotation(c *models.ReqContext) response.Response {
repo := annotations.GetRepository()
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.OrgId)
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
if resp != nil {
return resp
}
@@ -310,7 +311,7 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
var dashboardId int64
if cmd.AnnotationId != 0 {
annotation, respErr := findAnnotationByID(c.Req.Context(), repo, cmd.AnnotationId, c.OrgId)
annotation, respErr := findAnnotationByID(c.Req.Context(), repo, cmd.AnnotationId, c.SignedInUser)
if respErr != nil {
return respErr
}
@@ -358,7 +359,7 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
repo := annotations.GetRepository()
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.OrgId)
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
if resp != nil {
return resp
}
@@ -400,8 +401,13 @@ func canSaveOrganizationAnnotation(c *models.ReqContext) bool {
return c.SignedInUser.HasRole(models.ROLE_EDITOR)
}
func findAnnotationByID(ctx context.Context, repo annotations.Repository, annotationID int64, orgID int64) (*annotations.ItemDTO, response.Response) {
items, err := repo.Find(ctx, &annotations.ItemQuery{AnnotationId: annotationID, OrgId: orgID})
func findAnnotationByID(ctx context.Context, repo annotations.Repository, annotationID int64, user *models.SignedInUser) (*annotations.ItemDTO, response.Response) {
query := &annotations.ItemQuery{
AnnotationId: annotationID,
OrgId: user.OrgId,
SignedInUser: user,
}
items, err := repo.Find(ctx, query)
if err != nil {
return nil, response.Error(500, "Failed to find annotation", err)
@@ -446,9 +452,21 @@ func AnnotationTypeScopeResolver() (string, accesscontrol.AttributeScopeResolveF
return "", accesscontrol.ErrInvalidScope
}
annotation, resp := findAnnotationByID(ctx, annotations.GetRepository(), int64(annotationId), orgID)
// tempUser is used to resolve annotation type.
// The annotation doesn't get returned to the real user, so real user's permissions don't matter here.
tempUser := &models.SignedInUser{
OrgId: orgID,
Permissions: map[int64]map[string][]string{
orgID: {
accesscontrol.ActionDashboardsRead: {accesscontrol.ScopeDashboardsAll},
accesscontrol.ActionAnnotationsRead: {accesscontrol.ScopeAnnotationsAll},
},
},
}
annotation, resp := findAnnotationByID(ctx, annotations.GetRepository(), int64(annotationId), tempUser)
if resp != nil {
return "", err
return "", errors.New("could not resolve annotation type")
}
if annotation.GetType() == annotations.Organization {