4982ca3b1d
* Add actions and scopes * add resource service for dashboard and folder * Add dashboard guardian with fgac permission evaluation * Add CanDelete function to guardian interface * Add CanDelete property to folder and dashboard dto and set values * change to correct function name * Add accesscontrol to folder endpoints * add access control to dashboard endpoints * check access for nav links * Add fixed roles for dashboard and folders * use correct package * add hack to override guardian Constructor if accesscontrol is enabled * Add services * Add function to handle api backward compatability * Add permissionServices to HttpServer * Set permission when new dashboard is created * Add default permission when creating new dashboard * Set default permission when creating folder and dashboard * Add access control filter for dashboard search * Add to accept list * Add accesscontrol to dashboardimport * Disable access control in tests * Add check to see if user is allow to create a dashboard * Use SetPermissions * Use function to set several permissions at once * remove permissions for folder and dashboard on delete * update required permission * set permission for provisioning * Add CanCreate to dashboard guardian and set correct permisisons for provisioning * Dont set admin on folder / dashboard creation * Add dashboard and folder permission migrations * Add tests for CanCreate * Add roles and update descriptions * Solve uid to id for dashboard and folder permissions * Add folder and dashboard actions to permission filter * Handle viewer_can_edit flag * set folder and dashboard permissions services * Add dashboard permissions when importing a new dashboard * Set access control permissions on provisioning * Pass feature flags and only set permissions if access control is enabled * only add default permissions for folders and dashboards without folders * Batch create permissions in migrations * Remove `dashboards:edit` action * Remove unused function from interface * Update pkg/services/guardian/accesscontrol_guardian_test.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
187 lines
6.6 KiB
Go
187 lines
6.6 KiB
Go
package resourcepermissions
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/grafana/grafana/pkg/api/dtos"
|
|
"github.com/grafana/grafana/pkg/api/response"
|
|
"github.com/grafana/grafana/pkg/api/routing"
|
|
"github.com/grafana/grafana/pkg/models"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/middleware"
|
|
"github.com/grafana/grafana/pkg/web"
|
|
)
|
|
|
|
type api struct {
|
|
ac accesscontrol.AccessControl
|
|
router routing.RouteRegister
|
|
service *Service
|
|
permissions []string
|
|
}
|
|
|
|
func newApi(ac accesscontrol.AccessControl, router routing.RouteRegister, manager *Service) *api {
|
|
permissions := make([]string, 0, len(manager.permissions))
|
|
// reverse the permissions order for display
|
|
for i := len(manager.permissions) - 1; i >= 0; i-- {
|
|
permissions = append(permissions, manager.permissions[i])
|
|
}
|
|
return &api{ac, router, manager, permissions}
|
|
}
|
|
|
|
func (a *api) registerEndpoints() {
|
|
auth := middleware.Middleware(a.ac)
|
|
uidSolver := solveUID(a.service.options.UidSolver)
|
|
disable := middleware.Disable(a.ac.IsDisabled())
|
|
a.router.Group(fmt.Sprintf("/api/access-control/%s", a.service.options.Resource), func(r routing.RouteRegister) {
|
|
idScope := accesscontrol.Scope(a.service.options.Resource, "id", accesscontrol.Parameter(":resourceID"))
|
|
actionWrite, actionRead := fmt.Sprintf("%s.permissions:write", a.service.options.Resource), fmt.Sprintf("%s.permissions:read", a.service.options.Resource)
|
|
r.Get("/description", auth(disable, accesscontrol.EvalPermission(actionRead)), routing.Wrap(a.getDescription))
|
|
r.Get("/:resourceID", uidSolver, auth(disable, accesscontrol.EvalPermission(actionRead, idScope)), routing.Wrap(a.getPermissions))
|
|
r.Post("/:resourceID/users/:userID", uidSolver, auth(disable, accesscontrol.EvalPermission(actionWrite, idScope)), routing.Wrap(a.setUserPermission))
|
|
r.Post("/:resourceID/teams/:teamID", uidSolver, auth(disable, accesscontrol.EvalPermission(actionWrite, idScope)), routing.Wrap(a.setTeamPermission))
|
|
r.Post("/:resourceID/builtInRoles/:builtInRole", uidSolver, auth(disable, accesscontrol.EvalPermission(actionWrite, idScope)), routing.Wrap(a.setBuiltinRolePermission))
|
|
})
|
|
}
|
|
|
|
type Assignments struct {
|
|
Users bool `json:"users"`
|
|
Teams bool `json:"teams"`
|
|
BuiltInRoles bool `json:"builtInRoles"`
|
|
}
|
|
|
|
type Description struct {
|
|
Assignments Assignments `json:"assignments"`
|
|
Permissions []string `json:"permissions"`
|
|
}
|
|
|
|
func (a *api) getDescription(c *models.ReqContext) response.Response {
|
|
return response.JSON(http.StatusOK, &Description{
|
|
Permissions: a.permissions,
|
|
Assignments: a.service.options.Assignments,
|
|
})
|
|
}
|
|
|
|
type resourcePermissionDTO struct {
|
|
ID int64 `json:"id"`
|
|
ResourceID string `json:"resourceId"`
|
|
RoleName string `json:"roleName"`
|
|
IsManaged bool `json:"isManaged"`
|
|
UserID int64 `json:"userId,omitempty"`
|
|
UserLogin string `json:"userLogin,omitempty"`
|
|
UserAvatarUrl string `json:"userAvatarUrl,omitempty"`
|
|
Team string `json:"team,omitempty"`
|
|
TeamID int64 `json:"teamId,omitempty"`
|
|
TeamAvatarUrl string `json:"teamAvatarUrl,omitempty"`
|
|
BuiltInRole string `json:"builtInRole,omitempty"`
|
|
Actions []string `json:"actions"`
|
|
Permission string `json:"permission"`
|
|
}
|
|
|
|
func (a *api) getPermissions(c *models.ReqContext) response.Response {
|
|
resourceID := web.Params(c.Req)[":resourceID"]
|
|
|
|
permissions, err := a.service.GetPermissions(c.Req.Context(), c.SignedInUser, resourceID)
|
|
if err != nil {
|
|
return response.Error(http.StatusInternalServerError, "failed to get permissions", err)
|
|
}
|
|
|
|
dto := make([]resourcePermissionDTO, 0, len(permissions))
|
|
for _, p := range permissions {
|
|
if permission := a.service.MapActions(p); permission != "" {
|
|
teamAvatarUrl := ""
|
|
if p.TeamId != 0 {
|
|
teamAvatarUrl = dtos.GetGravatarUrlWithDefault(p.TeamEmail, p.Team)
|
|
}
|
|
|
|
dto = append(dto, resourcePermissionDTO{
|
|
ID: p.ID,
|
|
ResourceID: p.ResourceID,
|
|
RoleName: p.RoleName,
|
|
IsManaged: p.IsManaged(),
|
|
UserID: p.UserId,
|
|
UserLogin: p.UserLogin,
|
|
UserAvatarUrl: dtos.GetGravatarUrl(p.UserEmail),
|
|
Team: p.Team,
|
|
TeamID: p.TeamId,
|
|
TeamAvatarUrl: teamAvatarUrl,
|
|
BuiltInRole: p.BuiltInRole,
|
|
Actions: p.Actions,
|
|
Permission: permission,
|
|
})
|
|
}
|
|
}
|
|
|
|
return response.JSON(http.StatusOK, dto)
|
|
}
|
|
|
|
type setPermissionCommand struct {
|
|
Permission string `json:"permission"`
|
|
}
|
|
|
|
func (a *api) setUserPermission(c *models.ReqContext) response.Response {
|
|
userID, err := strconv.ParseInt(web.Params(c.Req)[":userID"], 10, 64)
|
|
if err != nil {
|
|
return response.Error(http.StatusBadRequest, "userID is invalid", err)
|
|
}
|
|
resourceID := web.Params(c.Req)[":resourceID"]
|
|
|
|
var cmd setPermissionCommand
|
|
if err := web.Bind(c.Req, &cmd); err != nil {
|
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
|
}
|
|
|
|
_, err = a.service.SetUserPermission(c.Req.Context(), c.OrgId, accesscontrol.User{ID: userID}, resourceID, cmd.Permission)
|
|
if err != nil {
|
|
return response.Error(http.StatusBadRequest, "failed to set user permission", err)
|
|
}
|
|
|
|
return permissionSetResponse(cmd)
|
|
}
|
|
|
|
func (a *api) setTeamPermission(c *models.ReqContext) response.Response {
|
|
teamID, err := strconv.ParseInt(web.Params(c.Req)[":teamID"], 10, 64)
|
|
if err != nil {
|
|
return response.Error(http.StatusBadRequest, "teamID is invalid", err)
|
|
}
|
|
resourceID := web.Params(c.Req)[":resourceID"]
|
|
|
|
var cmd setPermissionCommand
|
|
if err := web.Bind(c.Req, &cmd); err != nil {
|
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
|
}
|
|
|
|
_, err = a.service.SetTeamPermission(c.Req.Context(), c.OrgId, teamID, resourceID, cmd.Permission)
|
|
if err != nil {
|
|
return response.Error(http.StatusBadRequest, "failed to set team permission", err)
|
|
}
|
|
|
|
return permissionSetResponse(cmd)
|
|
}
|
|
|
|
func (a *api) setBuiltinRolePermission(c *models.ReqContext) response.Response {
|
|
builtInRole := web.Params(c.Req)[":builtInRole"]
|
|
resourceID := web.Params(c.Req)[":resourceID"]
|
|
|
|
cmd := setPermissionCommand{}
|
|
if err := web.Bind(c.Req, &cmd); err != nil {
|
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
|
}
|
|
|
|
_, err := a.service.SetBuiltInRolePermission(c.Req.Context(), c.OrgId, builtInRole, resourceID, cmd.Permission)
|
|
if err != nil {
|
|
return response.Error(http.StatusBadRequest, "failed to set role permission", err)
|
|
}
|
|
|
|
return permissionSetResponse(cmd)
|
|
}
|
|
|
|
func permissionSetResponse(cmd setPermissionCommand) response.Response {
|
|
message := "Permission updated"
|
|
if cmd.Permission == "" {
|
|
message = "Permission removed"
|
|
}
|
|
return response.Success(message)
|
|
}
|