mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into readonly_dashboards
This commit is contained in:
commit
d7688241c6
@ -1,6 +1,6 @@
|
|||||||
[run]
|
[run]
|
||||||
init_cmds = [
|
init_cmds = [
|
||||||
["go", "run", "build.go", "build"],
|
["go", "run", "build.go", "build-server"],
|
||||||
["./bin/grafana-server", "cfg:app_mode=development"]
|
["./bin/grafana-server", "cfg:app_mode=development"]
|
||||||
]
|
]
|
||||||
watch_all = true
|
watch_all = true
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
* **IE11**: IE 11 compatibility [#11165](https://github.com/grafana/grafana/issues/11165)
|
* **IE11**: IE 11 compatibility [#11165](https://github.com/grafana/grafana/issues/11165)
|
||||||
* **Scrolling**: Better scrolling experience [#11053](https://github.com/grafana/grafana/issues/11053), [#11252](https://github.com/grafana/grafana/issues/11252), [#10836](https://github.com/grafana/grafana/issues/10836), [#11185](https://github.com/grafana/grafana/issues/11185), [#11168](https://github.com/grafana/grafana/issues/11168)
|
* **Scrolling**: Better scrolling experience [#11053](https://github.com/grafana/grafana/issues/11053), [#11252](https://github.com/grafana/grafana/issues/11252), [#10836](https://github.com/grafana/grafana/issues/10836), [#11185](https://github.com/grafana/grafana/issues/11185), [#11168](https://github.com/grafana/grafana/issues/11168)
|
||||||
* **Docker**: Improved docker image (breaking changes regarding file ownership) [grafana-docker #141](https://github.com/grafana/grafana-docker/issues/141), thx [@Spindel](https://github.com/Spindel), [@ChristianKniep](https://github.com/ChristianKniep), [@brancz](https://github.com/brancz) and [@jangaraj](https://github.com/jangaraj)
|
* **Docker**: Improved docker image (breaking changes regarding file ownership) [grafana-docker #141](https://github.com/grafana/grafana-docker/issues/141), thx [@Spindel](https://github.com/Spindel), [@ChristianKniep](https://github.com/ChristianKniep), [@brancz](https://github.com/brancz) and [@jangaraj](https://github.com/jangaraj)
|
||||||
|
* **Folders** A folder admin cannot add user/team permissions for folder/its dashboards [#11173](https://github.com/grafana/grafana/issues/11173)
|
||||||
|
|
||||||
### Minor
|
### Minor
|
||||||
|
|
||||||
|
@ -149,8 +149,6 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
|
|
||||||
// team (admin permission required)
|
// team (admin permission required)
|
||||||
apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
|
apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
|
||||||
teamsRoute.Get("/:teamId", wrap(GetTeamByID))
|
|
||||||
teamsRoute.Get("/search", wrap(SearchTeams))
|
|
||||||
teamsRoute.Post("/", bind(m.CreateTeamCommand{}), wrap(CreateTeam))
|
teamsRoute.Post("/", bind(m.CreateTeamCommand{}), wrap(CreateTeam))
|
||||||
teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
|
teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
|
||||||
teamsRoute.Delete("/:teamId", wrap(DeleteTeamByID))
|
teamsRoute.Delete("/:teamId", wrap(DeleteTeamByID))
|
||||||
@ -159,6 +157,12 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
|
teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
|
||||||
}, reqOrgAdmin)
|
}, reqOrgAdmin)
|
||||||
|
|
||||||
|
// team without requirement of user to be org admin
|
||||||
|
apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
|
||||||
|
teamsRoute.Get("/:teamId", wrap(GetTeamByID))
|
||||||
|
teamsRoute.Get("/search", wrap(SearchTeams))
|
||||||
|
})
|
||||||
|
|
||||||
// org information available to all users.
|
// org information available to all users.
|
||||||
apiRoute.Group("/org", func(orgRoute RouteRegister) {
|
apiRoute.Group("/org", func(orgRoute RouteRegister) {
|
||||||
orgRoute.Get("/", wrap(GetOrgCurrent))
|
orgRoute.Get("/", wrap(GetOrgCurrent))
|
||||||
@ -170,7 +174,6 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrgCurrent))
|
orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrgCurrent))
|
||||||
orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddressCurrent))
|
orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddressCurrent))
|
||||||
orgRoute.Post("/users", quota("user"), bind(m.AddOrgUserCommand{}), wrap(AddOrgUserToCurrentOrg))
|
orgRoute.Post("/users", quota("user"), bind(m.AddOrgUserCommand{}), wrap(AddOrgUserToCurrentOrg))
|
||||||
orgRoute.Get("/users", wrap(GetOrgUsersForCurrentOrg))
|
|
||||||
orgRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUserForCurrentOrg))
|
orgRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUserForCurrentOrg))
|
||||||
orgRoute.Delete("/users/:userId", wrap(RemoveOrgUserForCurrentOrg))
|
orgRoute.Delete("/users/:userId", wrap(RemoveOrgUserForCurrentOrg))
|
||||||
|
|
||||||
@ -184,6 +187,11 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
orgRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateOrgPreferences))
|
orgRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateOrgPreferences))
|
||||||
}, reqOrgAdmin)
|
}, reqOrgAdmin)
|
||||||
|
|
||||||
|
// current org without requirement of user to be org admin
|
||||||
|
apiRoute.Group("/org", func(orgRoute RouteRegister) {
|
||||||
|
orgRoute.Get("/users", wrap(GetOrgUsersForCurrentOrg))
|
||||||
|
})
|
||||||
|
|
||||||
// create new org
|
// create new org
|
||||||
apiRoute.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), wrap(CreateOrg))
|
apiRoute.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), wrap(CreateOrg))
|
||||||
|
|
||||||
|
@ -225,6 +225,10 @@ func GetFolderUrl(folderUid string, slug string) string {
|
|||||||
return fmt.Sprintf("%s/dashboards/f/%s/%s", setting.AppSubUrl, folderUid, slug)
|
return fmt.Sprintf("%s/dashboards/f/%s/%s", setting.AppSubUrl, folderUid, slug)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ValidateDashboardBeforeSaveResult struct {
|
||||||
|
IsParentFolderChanged bool
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// COMMANDS
|
// COMMANDS
|
||||||
//
|
//
|
||||||
@ -269,6 +273,7 @@ type ValidateDashboardBeforeSaveCommand struct {
|
|||||||
OrgId int64
|
OrgId int64
|
||||||
Dashboard *Dashboard
|
Dashboard *Dashboard
|
||||||
Overwrite bool
|
Overwrite bool
|
||||||
|
Result *ValidateDashboardBeforeSaveResult
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -103,6 +103,16 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if validateBeforeSaveCmd.Result.IsParentFolderChanged {
|
||||||
|
folderGuardian := guardian.New(dash.FolderId, dto.OrgId, dto.User)
|
||||||
|
if canSave, err := folderGuardian.CanSave(); err != nil || !canSave {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, models.ErrDashboardUpdateAccessDenied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if validateProvisionedDashboard {
|
if validateProvisionedDashboard {
|
||||||
isDashboardProvisioned := &models.IsDashboardProvisionedQuery{DashboardId: dash.Id}
|
isDashboardProvisioned := &models.IsDashboardProvisionedQuery{DashboardId: dash.Id}
|
||||||
err := bus.Dispatch(isDashboardProvisioned)
|
err := bus.Dispatch(isDashboardProvisioned)
|
||||||
|
@ -53,6 +53,7 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||||
|
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -97,6 +98,7 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||||
|
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -140,6 +142,7 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||||
|
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -176,6 +179,7 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||||
|
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ func TestFolderService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||||
|
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||||
return models.ErrDashboardUpdateAccessDenied
|
return models.ErrDashboardUpdateAccessDenied
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -92,6 +93,7 @@ func TestFolderService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||||
|
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ func (g *dashboardGuardianImpl) CheckPermissionBeforeUpdate(permission m.Permiss
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return g.checkAcl(permission, acl)
|
return g.checkAcl(permission, existingPermissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAcl returns dashboard acl
|
// GetAcl returns dashboard acl
|
||||||
|
File diff suppressed because it is too large
Load Diff
256
pkg/services/guardian/guardian_util_test.go
Normal file
256
pkg/services/guardian/guardian_util_test.go
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
package guardian
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
type scenarioContext struct {
|
||||||
|
t *testing.T
|
||||||
|
orgRoleScenario string
|
||||||
|
permissionScenario string
|
||||||
|
g DashboardGuardian
|
||||||
|
givenUser *m.SignedInUser
|
||||||
|
givenDashboardID int64
|
||||||
|
givenPermissions []*m.DashboardAclInfoDTO
|
||||||
|
givenTeams []*m.Team
|
||||||
|
updatePermissions []*m.DashboardAcl
|
||||||
|
expectedFlags permissionFlags
|
||||||
|
callerFile string
|
||||||
|
callerLine int
|
||||||
|
}
|
||||||
|
|
||||||
|
type scenarioFunc func(c *scenarioContext)
|
||||||
|
|
||||||
|
func orgRoleScenario(desc string, t *testing.T, role m.RoleType, fn scenarioFunc) {
|
||||||
|
user := &m.SignedInUser{
|
||||||
|
UserId: userID,
|
||||||
|
OrgId: orgID,
|
||||||
|
OrgRole: role,
|
||||||
|
}
|
||||||
|
guard := New(dashboardID, orgID, user)
|
||||||
|
sc := &scenarioContext{
|
||||||
|
t: t,
|
||||||
|
orgRoleScenario: desc,
|
||||||
|
givenUser: user,
|
||||||
|
givenDashboardID: dashboardID,
|
||||||
|
g: guard,
|
||||||
|
}
|
||||||
|
|
||||||
|
Convey(desc, func() {
|
||||||
|
fn(sc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func permissionScenario(desc string, dashboardID int64, sc *scenarioContext, permissions []*m.DashboardAclInfoDTO, fn scenarioFunc) {
|
||||||
|
bus.ClearBusHandlers()
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
|
||||||
|
if query.OrgId != sc.givenUser.OrgId {
|
||||||
|
sc.reportFailure("Invalid organization id for GetDashboardAclInfoListQuery", sc.givenUser.OrgId, query.OrgId)
|
||||||
|
}
|
||||||
|
if query.DashboardId != sc.givenDashboardID {
|
||||||
|
sc.reportFailure("Invalid dashboard id for GetDashboardAclInfoListQuery", sc.givenDashboardID, query.DashboardId)
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Result = permissions
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
teams := []*m.Team{}
|
||||||
|
|
||||||
|
for _, p := range permissions {
|
||||||
|
if p.TeamId > 0 {
|
||||||
|
teams = append(teams, &m.Team{Id: p.TeamId})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
|
||||||
|
if query.OrgId != sc.givenUser.OrgId {
|
||||||
|
sc.reportFailure("Invalid organization id for GetTeamsByUserQuery", sc.givenUser.OrgId, query.OrgId)
|
||||||
|
}
|
||||||
|
if query.UserId != sc.givenUser.UserId {
|
||||||
|
sc.reportFailure("Invalid user id for GetTeamsByUserQuery", sc.givenUser.UserId, query.UserId)
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Result = teams
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
sc.permissionScenario = desc
|
||||||
|
sc.g = New(dashboardID, sc.givenUser.OrgId, sc.givenUser)
|
||||||
|
sc.givenDashboardID = dashboardID
|
||||||
|
sc.givenPermissions = permissions
|
||||||
|
sc.givenTeams = teams
|
||||||
|
|
||||||
|
Convey(desc, func() {
|
||||||
|
fn(sc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type permissionType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
USER permissionType = 1 << iota
|
||||||
|
TEAM
|
||||||
|
EDITOR
|
||||||
|
VIEWER
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p permissionType) String() string {
|
||||||
|
names := map[uint8]string{
|
||||||
|
uint8(USER): "user",
|
||||||
|
uint8(TEAM): "team",
|
||||||
|
uint8(EDITOR): "editor role",
|
||||||
|
uint8(VIEWER): "viewer role",
|
||||||
|
}
|
||||||
|
return names[uint8(p)]
|
||||||
|
}
|
||||||
|
|
||||||
|
type permissionFlags uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
NO_ACCESS permissionFlags = 1 << iota
|
||||||
|
CAN_ADMIN
|
||||||
|
CAN_EDIT
|
||||||
|
CAN_SAVE
|
||||||
|
CAN_VIEW
|
||||||
|
FULL_ACCESS = CAN_ADMIN | CAN_EDIT | CAN_SAVE | CAN_VIEW
|
||||||
|
EDITOR_ACCESS = CAN_EDIT | CAN_SAVE | CAN_VIEW
|
||||||
|
VIEWER_ACCESS = CAN_VIEW
|
||||||
|
)
|
||||||
|
|
||||||
|
func (flag permissionFlags) canAdmin() bool {
|
||||||
|
return flag&CAN_ADMIN != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flag permissionFlags) canEdit() bool {
|
||||||
|
return flag&CAN_EDIT != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flag permissionFlags) canSave() bool {
|
||||||
|
return flag&CAN_SAVE != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flag permissionFlags) canView() bool {
|
||||||
|
return flag&CAN_VIEW != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flag permissionFlags) noAccess() bool {
|
||||||
|
return flag&(CAN_ADMIN|CAN_EDIT|CAN_SAVE|CAN_VIEW) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f permissionFlags) String() string {
|
||||||
|
r := []string{}
|
||||||
|
|
||||||
|
if f.canAdmin() {
|
||||||
|
r = append(r, "admin")
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.canEdit() {
|
||||||
|
r = append(r, "edit")
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.canSave() {
|
||||||
|
r = append(r, "save")
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.canView() {
|
||||||
|
r = append(r, "view")
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.noAccess() {
|
||||||
|
r = append(r, "<no access>")
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(r[:], ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *scenarioContext) reportSuccess() {
|
||||||
|
So(true, ShouldBeTrue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *scenarioContext) reportFailure(desc string, expected interface{}, actual interface{}) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteString("\n")
|
||||||
|
buf.WriteString(sc.orgRoleScenario)
|
||||||
|
buf.WriteString(" ")
|
||||||
|
buf.WriteString(sc.permissionScenario)
|
||||||
|
buf.WriteString("\n ")
|
||||||
|
buf.WriteString(desc)
|
||||||
|
buf.WriteString("\n")
|
||||||
|
buf.WriteString(fmt.Sprintf("Source test: %s:%d\n", sc.callerFile, sc.callerLine))
|
||||||
|
buf.WriteString(fmt.Sprintf("Expected: %v\n", expected))
|
||||||
|
buf.WriteString(fmt.Sprintf("Actual: %v\n", actual))
|
||||||
|
buf.WriteString("Context:")
|
||||||
|
buf.WriteString(fmt.Sprintf("\n Given user: orgRole=%s, id=%d, orgId=%d", sc.givenUser.OrgRole, sc.givenUser.UserId, sc.givenUser.OrgId))
|
||||||
|
buf.WriteString(fmt.Sprintf("\n Given dashboard id: %d", sc.givenDashboardID))
|
||||||
|
|
||||||
|
for i, p := range sc.givenPermissions {
|
||||||
|
r := "<nil>"
|
||||||
|
if p.Role != nil {
|
||||||
|
r = string(*p.Role)
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("\n Given permission (%d): dashboardId=%d, userId=%d, teamId=%d, role=%v, permission=%s", i, p.DashboardId, p.UserId, p.TeamId, r, p.Permission.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, t := range sc.givenTeams {
|
||||||
|
buf.WriteString(fmt.Sprintf("\n Given team (%d): id=%d", i, t.Id))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, p := range sc.updatePermissions {
|
||||||
|
r := "<nil>"
|
||||||
|
if p.Role != nil {
|
||||||
|
r = string(*p.Role)
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("\n Update permission (%d): dashboardId=%d, userId=%d, teamId=%d, role=%v, permission=%s", i, p.DashboardId, p.UserId, p.TeamId, r, p.Permission.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.t.Fatalf(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCustomUserPermission(dashboardID int64, userID int64, permission m.PermissionType) *m.DashboardAcl {
|
||||||
|
return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, UserId: userID, Permission: permission}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultUserPermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
|
||||||
|
return newCustomUserPermission(dashboardID, userID, permission)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCustomTeamPermission(dashboardID int64, teamID int64, permission m.PermissionType) *m.DashboardAcl {
|
||||||
|
return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, TeamId: teamID, Permission: permission}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultTeamPermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
|
||||||
|
return newCustomTeamPermission(dashboardID, teamID, permission)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAdminRolePermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
|
||||||
|
return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, Role: &adminRole, Permission: permission}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEditorRolePermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
|
||||||
|
return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, Role: &editorRole, Permission: permission}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newViewerRolePermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
|
||||||
|
return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, Role: &viewerRole, Permission: permission}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toDto(acl *m.DashboardAcl) *m.DashboardAclInfoDTO {
|
||||||
|
return &m.DashboardAclInfoDTO{
|
||||||
|
OrgId: acl.OrgId,
|
||||||
|
DashboardId: acl.DashboardId,
|
||||||
|
UserId: acl.UserId,
|
||||||
|
TeamId: acl.TeamId,
|
||||||
|
Role: acl.Role,
|
||||||
|
Permission: acl.Permission,
|
||||||
|
PermissionName: acl.Permission.String(),
|
||||||
|
}
|
||||||
|
}
|
@ -544,6 +544,10 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
|
|||||||
dash.SetId(existingByUid.Id)
|
dash.SetId(existingByUid.Id)
|
||||||
dash.SetUid(existingByUid.Uid)
|
dash.SetUid(existingByUid.Uid)
|
||||||
existing = existingByUid
|
existing = existingByUid
|
||||||
|
|
||||||
|
if !dash.IsFolder {
|
||||||
|
cmd.Result.IsParentFolderChanged = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existing.IsFolder && !dash.IsFolder) ||
|
if (existing.IsFolder && !dash.IsFolder) ||
|
||||||
@ -551,6 +555,10 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
|
|||||||
return m.ErrDashboardTypeMismatch
|
return m.ErrDashboardTypeMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !dash.IsFolder && dash.FolderId != existing.FolderId {
|
||||||
|
cmd.Result.IsParentFolderChanged = true
|
||||||
|
}
|
||||||
|
|
||||||
// check for is someone else has written in between
|
// check for is someone else has written in between
|
||||||
if dash.Version != existing.Version {
|
if dash.Version != existing.Version {
|
||||||
if cmd.Overwrite {
|
if cmd.Overwrite {
|
||||||
@ -586,6 +594,10 @@ func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashbo
|
|||||||
return m.ErrDashboardFolderWithSameNameAsDashboard
|
return m.ErrDashboardFolderWithSameNameAsDashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) {
|
||||||
|
cmd.Result.IsParentFolderChanged = true
|
||||||
|
}
|
||||||
|
|
||||||
if cmd.Overwrite {
|
if cmd.Overwrite {
|
||||||
dash.SetId(existing.Id)
|
dash.SetId(existing.Id)
|
||||||
dash.SetUid(existing.Uid)
|
dash.SetUid(existing.Uid)
|
||||||
@ -599,6 +611,7 @@ func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashbo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err error) {
|
func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err error) {
|
||||||
|
cmd.Result = &m.ValidateDashboardBeforeSaveResult{}
|
||||||
return inTransaction(func(sess *DBSession) error {
|
return inTransaction(func(sess *DBSession) error {
|
||||||
if err = getExistingDashboardByIdOrUidForUpdate(sess, cmd); err != nil {
|
if err = getExistingDashboardByIdOrUidForUpdate(sess, cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -78,7 +78,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
Convey("Given organization B", func() {
|
Convey("Given organization B", func() {
|
||||||
var otherOrgId int64 = 2
|
var otherOrgId int64 = 2
|
||||||
|
|
||||||
Convey("When saving a dashboard with id that are saved in organization A", func() {
|
Convey("When creating a dashboard with same id as dashboard in organization A", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: otherOrgId,
|
OrgId: otherOrgId,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -97,7 +97,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
permissionScenario("Given user has permission to save", true, func(sc *dashboardPermissionScenarioContext) {
|
permissionScenario("Given user has permission to save", true, func(sc *dashboardPermissionScenarioContext) {
|
||||||
Convey("When saving a dashboard with uid that are saved in organization A", func() {
|
Convey("When creating a dashboard with same uid as dashboard in organization A", func() {
|
||||||
var otherOrgId int64 = 2
|
var otherOrgId int64 = 2
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: otherOrgId,
|
OrgId: otherOrgId,
|
||||||
@ -110,7 +110,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
res := callSaveWithResult(cmd)
|
res := callSaveWithResult(cmd)
|
||||||
|
|
||||||
Convey("It should create dashboard in other organization", func() {
|
Convey("It should create a new dashboard in organization B", func() {
|
||||||
So(res, ShouldNotBeNil)
|
So(res, ShouldNotBeNil)
|
||||||
|
|
||||||
query := models.GetDashboardQuery{OrgId: otherOrgId, Uid: savedDashInFolder.Uid}
|
query := models.GetDashboardQuery{OrgId: otherOrgId, Uid: savedDashInFolder.Uid}
|
||||||
@ -130,7 +130,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
permissionScenario("Given user has no permission to save", false, func(sc *dashboardPermissionScenarioContext) {
|
permissionScenario("Given user has no permission to save", false, func(sc *dashboardPermissionScenarioContext) {
|
||||||
|
|
||||||
Convey("When trying to create a new dashboard in the General folder", func() {
|
Convey("When creating a new dashboard in the General folder", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: testOrgId,
|
OrgId: testOrgId,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -142,7 +142,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
err := callSaveWithError(cmd)
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
Convey("It should call dashboard guardian with correct arguments and result in access denied error", func() {
|
Convey("It should create dashboard guardian for General Folder with correct arguments and result in access denied error", func() {
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to create a new dashboard in other folder", func() {
|
Convey("When creating a new dashboard in other folder", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: testOrgId,
|
OrgId: testOrgId,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -165,7 +165,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
err := callSaveWithError(cmd)
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
Convey("It should call dashboard guardian with correct arguments and rsult in access denied error", func() {
|
Convey("It should create dashboard guardian for other folder with correct arguments and rsult in access denied error", func() {
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
@ -175,7 +175,54 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to update a dashboard by existing id in the General folder", func() {
|
Convey("When creating a new dashboard by existing title in folder", func() {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgId,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": savedDashInFolder.Title,
|
||||||
|
}),
|
||||||
|
FolderId: savedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
|
Convey("It should create dashboard guardian for folder with correct arguments and result in access denied error", func() {
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
|
So(sc.dashboardGuardianMock.DashId, ShouldEqual, savedFolder.Id)
|
||||||
|
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||||
|
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("When creating a new dashboard by existing uid in folder", func() {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgId,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": savedDashInFolder.Uid,
|
||||||
|
"title": "New dash",
|
||||||
|
}),
|
||||||
|
FolderId: savedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
|
Convey("It should create dashboard guardian for folder with correct arguments and result in access denied error", func() {
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
|
So(sc.dashboardGuardianMock.DashId, ShouldEqual, savedFolder.Id)
|
||||||
|
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||||
|
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("When updating a dashboard by existing id in the General folder", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: testOrgId,
|
OrgId: testOrgId,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -189,7 +236,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
err := callSaveWithError(cmd)
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
Convey("It should call dashboard guardian with correct arguments and result in access denied error", func() {
|
Convey("It should create dashboard guardian for dashboard with correct arguments and result in access denied error", func() {
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
@ -199,7 +246,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to update a dashboard by existing id in other folder", func() {
|
Convey("When updating a dashboard by existing id in other folder", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: testOrgId,
|
OrgId: testOrgId,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -213,7 +260,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
err := callSaveWithError(cmd)
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
Convey("It should call dashboard guardian with correct arguments and result in access denied error", func() {
|
Convey("It should create dashboard guardian for dashboard with correct arguments and result in access denied error", func() {
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
@ -222,6 +269,102 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("When moving a dashboard by existing id to other folder from General folder", func() {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgId,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": savedDashInGeneralFolder.Id,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: otherSavedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
|
Convey("It should create dashboard guardian for other folder with correct arguments and result in access denied error", func() {
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
|
So(sc.dashboardGuardianMock.DashId, ShouldEqual, otherSavedFolder.Id)
|
||||||
|
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||||
|
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("When moving a dashboard by existing id to the General folder from other folder", func() {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgId,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": savedDashInFolder.Id,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: 0,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
|
Convey("It should create dashboard guardian for General folder with correct arguments and result in access denied error", func() {
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
|
So(sc.dashboardGuardianMock.DashId, ShouldEqual, 0)
|
||||||
|
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||||
|
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("When moving a dashboard by existing uid to other folder from General folder", func() {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgId,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": savedDashInGeneralFolder.Uid,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: otherSavedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
|
Convey("It should create dashboard guardian for other folder with correct arguments and result in access denied error", func() {
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
|
So(sc.dashboardGuardianMock.DashId, ShouldEqual, otherSavedFolder.Id)
|
||||||
|
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||||
|
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("When moving a dashboard by existing uid to the General folder from other folder", func() {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgId,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": savedDashInFolder.Uid,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: 0,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd)
|
||||||
|
|
||||||
|
Convey("It should create dashboard guardian for General folder with correct arguments and result in access denied error", func() {
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
So(err, ShouldEqual, models.ErrDashboardUpdateAccessDenied)
|
||||||
|
|
||||||
|
So(sc.dashboardGuardianMock.DashId, ShouldEqual, 0)
|
||||||
|
So(sc.dashboardGuardianMock.OrgId, ShouldEqual, cmd.OrgId)
|
||||||
|
So(sc.dashboardGuardianMock.User.UserId, ShouldEqual, cmd.UserId)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Given user has permission to save
|
// Given user has permission to save
|
||||||
@ -672,7 +815,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to update existing folder to a dashboard using id", func() {
|
Convey("When updating existing folder to a dashboard using id", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -691,7 +834,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to update existing dashboard to a folder using id", func() {
|
Convey("When updating existing dashboard to a folder using id", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -710,7 +853,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to update existing folder to a dashboard using uid", func() {
|
Convey("When updating existing folder to a dashboard using uid", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -729,7 +872,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to update existing dashboard to a folder using uid", func() {
|
Convey("When updating existing dashboard to a folder using uid", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -748,7 +891,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to update existing folder to a dashboard using title", func() {
|
Convey("When updating existing folder to a dashboard using title", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -766,7 +909,7 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to update existing dashboard to a folder using title", func() {
|
Convey("When updating existing dashboard to a folder using title", func() {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@ -854,23 +997,6 @@ func callSaveWithError(cmd models.SaveDashboardCommand) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func dashboardServiceScenario(desc string, mock *guardian.FakeDashboardGuardian, fn scenarioFunc) {
|
|
||||||
Convey(desc, func() {
|
|
||||||
origNewDashboardGuardian := guardian.New
|
|
||||||
guardian.MockDashboardGuardian(mock)
|
|
||||||
|
|
||||||
sc := &scenarioContext{
|
|
||||||
dashboardGuardianMock: mock,
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
guardian.New = origNewDashboardGuardian
|
|
||||||
}()
|
|
||||||
|
|
||||||
fn(sc)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveTestDashboard(title string, orgId int64, folderId int64) *models.Dashboard {
|
func saveTestDashboard(title string, orgId int64, folderId int64) *models.Dashboard {
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: orgId,
|
OrgId: orgId,
|
||||||
|
@ -19,9 +19,12 @@ export class FolderPickerCtrl {
|
|||||||
newFolderNameTouched: boolean;
|
newFolderNameTouched: boolean;
|
||||||
hasValidationError: boolean;
|
hasValidationError: boolean;
|
||||||
validationError: any;
|
validationError: any;
|
||||||
|
isEditor: boolean;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private backendSrv, private validationSrv) {
|
constructor(private backendSrv, private validationSrv, private contextSrv) {
|
||||||
|
this.isEditor = this.contextSrv.isEditor;
|
||||||
|
|
||||||
if (!this.labelClass) {
|
if (!this.labelClass) {
|
||||||
this.labelClass = 'width-7';
|
this.labelClass = 'width-7';
|
||||||
}
|
}
|
||||||
@ -38,19 +41,20 @@ export class FolderPickerCtrl {
|
|||||||
|
|
||||||
return this.backendSrv.get('api/search', params).then(result => {
|
return this.backendSrv.get('api/search', params).then(result => {
|
||||||
if (
|
if (
|
||||||
query === '' ||
|
this.isEditor &&
|
||||||
|
(query === '' ||
|
||||||
query.toLowerCase() === 'g' ||
|
query.toLowerCase() === 'g' ||
|
||||||
query.toLowerCase() === 'ge' ||
|
query.toLowerCase() === 'ge' ||
|
||||||
query.toLowerCase() === 'gen' ||
|
query.toLowerCase() === 'gen' ||
|
||||||
query.toLowerCase() === 'gene' ||
|
query.toLowerCase() === 'gene' ||
|
||||||
query.toLowerCase() === 'gener' ||
|
query.toLowerCase() === 'gener' ||
|
||||||
query.toLowerCase() === 'genera' ||
|
query.toLowerCase() === 'genera' ||
|
||||||
query.toLowerCase() === 'general'
|
query.toLowerCase() === 'general')
|
||||||
) {
|
) {
|
||||||
result.unshift({ title: this.rootName, id: 0 });
|
result.unshift({ title: this.rootName, id: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.enableCreateNew && query === '') {
|
if (this.isEditor && this.enableCreateNew && query === '') {
|
||||||
result.unshift({ title: '-- New Folder --', id: -1 });
|
result.unshift({ title: '-- New Folder --', id: -1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user