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:
Ezequiel Victorero
2022-03-21 14:28:39 -03:00
committed by GitHub
parent 7ab1ef8d6e
commit c717320942
13 changed files with 377 additions and 111 deletions

View File

@@ -396,20 +396,47 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID)
require.NoError(t, err)
repo := annotations.GetRepository()
dashboardAnnotation := annotations.ItemDTO{Id: 1, DashboardId: 1}
organizationAnnotation := annotations.ItemDTO{Id: 2, DashboardId: 0}
localAnnotation := annotations.Item{
OrgId: sc.initCtx.OrgId,
fakeAnnoRepo = &fakeAnnotationsRepo{
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,
}
globalAnnotation := annotations.Item{
OrgId: sc.initCtx.OrgId,
PanelId: 1,
}
err = repo.Save(&localAnnotation)
require.NoError(t, err)
err = repo.Save(&globalAnnotation)
require.NoError(t, err)
updateCmd := dtos.UpdateAnnotationsCmd{
Time: 1000,
Text: "annotation text",
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 {
permissions []*accesscontrol.Permission
@@ -459,10 +486,217 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
},
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 {
t.Run(tt.name, func(t *testing.T) {
setUpACL()
sc.acmock.RegisterAttributeScopeResolver(AnnotationTypeScopeResolver())
setAccessControlPermissions(sc.acmock, tt.args.permissions, sc.initCtx.OrgId)
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)
})
@@ -479,15 +713,15 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) {
testCases := []testCaseResolver{
{
desc: "correctly resolves local annotations",
desc: "correctly resolves dashboard annotations",
given: "annotations:id:1",
want: accesscontrol.ScopeAnnotationsTypeLocal,
want: accesscontrol.ScopeAnnotationsTypeDashboard,
wantErr: nil,
},
{
desc: "correctly resolves global annotations",
desc: "correctly resolves organization annotations",
given: "annotations:id:2",
want: accesscontrol.ScopeAnnotationsTypeGlobal,
want: accesscontrol.ScopeAnnotationsTypeOrganization,
wantErr: nil,
},
{
@@ -504,11 +738,11 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) {
},
}
localAnnotation := annotations.ItemDTO{Id: 1, DashboardId: 1}
globalAnnotation := annotations.ItemDTO{Id: 2}
dashboardAnnotation := annotations.ItemDTO{Id: 1, DashboardId: 1}
organizationAnnotation := annotations.ItemDTO{Id: 2}
fakeAnnoRepo = &fakeAnnotationsRepo{
annotations: map[int64]annotations.ItemDTO{1: localAnnotation, 2: globalAnnotation},
annotations: map[int64]annotations.ItemDTO{1: dashboardAnnotation, 2: organizationAnnotation},
}
annotations.SetRepository(fakeAnnoRepo)