diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 293d40ddf82..cb71e7eec9a 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -202,7 +202,13 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response { func (hs *HTTPServer) getAnnotationPermissionsByScope(c *models.ReqContext, actions *dtos.AnnotationActions, scope string) { var err error - evaluate := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsDelete, scope) + evaluate := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsCreate, scope) + actions.CanAdd, err = hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, evaluate) + if err != nil { + hs.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsCreate, "scope", scope) + } + + evaluate = accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsDelete, scope) actions.CanDelete, err = hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, evaluate) if err != nil { hs.log.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsDelete, "scope", scope) diff --git a/pkg/api/dtos/dashboard.go b/pkg/api/dtos/dashboard.go index 707bd549804..2e25ca4e132 100644 --- a/pkg/api/dtos/dashboard.go +++ b/pkg/api/dtos/dashboard.go @@ -40,6 +40,7 @@ type AnnotationPermission struct { } type AnnotationActions struct { + CanAdd bool `json:"canAdd"` CanEdit bool `json:"canEdit"` CanDelete bool `json:"canDelete"` } diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 3a082c19a07..890c6c86075 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -88,7 +88,7 @@ export class PanelChrome extends PureComponent { onAnnotationCreate: this.onAnnotationCreate, onAnnotationUpdate: this.onAnnotationUpdate, onAnnotationDelete: this.onAnnotationDelete, - canAddAnnotations: () => Boolean(props.dashboard.meta.canEdit || props.dashboard.meta.canMakeEditable), + canAddAnnotations: this.canAddAnnotation, onInstanceStateChange: this.onInstanceStateChange, onToggleLegendSort: this.onToggleLegendSort, canEditAnnotations: this.canEditAnnotation, @@ -98,6 +98,17 @@ export class PanelChrome extends PureComponent { }; } + canEditDashboard = () => Boolean(this.props.dashboard.meta.canEdit || this.props.dashboard.meta.canMakeEditable); + + canAddAnnotation = () => { + let canAdd = true; + + if (contextSrv.accessControlEnabled()) { + canAdd = !!this.props.dashboard.meta.annotationsPermissions?.dashboard.canAdd; + } + return canAdd && this.canEditDashboard(); + }; + canEditAnnotation = (dashboardId: number) => { let canEdit = true; @@ -108,7 +119,7 @@ export class PanelChrome extends PureComponent { canEdit = !!this.props.dashboard.meta.annotationsPermissions?.organization.canEdit; } } - return canEdit && Boolean(this.props.dashboard.meta.canEdit || this.props.dashboard.meta.canMakeEditable); + return canEdit && this.canEditDashboard(); }; canDeleteAnnotation = (dashboardId: number) => { @@ -121,7 +132,7 @@ export class PanelChrome extends PureComponent { canDelete = !!this.props.dashboard.meta.annotationsPermissions?.organization.canDelete; } } - return canDelete && Boolean(this.props.dashboard.meta.canEdit || this.props.dashboard.meta.canMakeEditable); + return canDelete && this.canEditDashboard(); }; // Due to a mutable panel model we get the sync settings via function that proactively reads from the model diff --git a/public/app/features/dashboard/state/DashboardModel.test.ts b/public/app/features/dashboard/state/DashboardModel.test.ts index 1a79a7f8aa1..764cccd8fcd 100644 --- a/public/app/features/dashboard/state/DashboardModel.test.ts +++ b/public/app/features/dashboard/state/DashboardModel.test.ts @@ -863,7 +863,7 @@ describe('DashboardModel', () => { dashboard.meta.canEdit = canEdit; dashboard.meta.canMakeEditable = canMakeEditable; - const result = dashboard.canAddAnnotations(); + const result = dashboard.canEditDashboard(); expect(result).toBe(expected); } diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index c307f004196..04776c6cbdc 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -1189,10 +1189,21 @@ export class DashboardModel implements TimeModel { canEdit = !!this.meta.annotationsPermissions?.dashboard.canEdit; } } - return this.canAddAnnotations() && canEdit; + return this.canEditDashboard() && canEdit; } canAddAnnotations() { + let canAdd = true; + + // if FGAC is enabled there are additional conditions to check + if (contextSrv.accessControlEnabled()) { + canAdd = !!this.meta.annotationsPermissions?.dashboard.canAdd; + } + + return this.canEditDashboard() && canAdd; + } + + canEditDashboard() { return this.meta.canEdit || this.meta.canMakeEditable; } diff --git a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx index 28fd8f6cfa3..ddd24370c6b 100644 --- a/public/app/plugins/panel/candlestick/CandlestickPanel.tsx +++ b/public/app/plugins/panel/candlestick/CandlestickPanel.tsx @@ -244,39 +244,49 @@ export const CandlestickPanel: React.FC = ({ )} {/* Enables annotations creation*/} - - {({ startAnnotating }) => { - return ( - { - if (!p) { - return; - } - startAnnotating({ coords: p.coords }); + {enableAnnotationCreation ? ( + + {({ startAnnotating }) => { + return ( + { + if (!p) { + return; + } + startAnnotating({ coords: p.coords }); + }, }, - }, - ], - }, - ] - : [] - } - /> - ); - }} - + ], + }, + ] + : [] + } + /> + ); + }} + + ) : ( + + )} {data.annotations && (