mirror of
https://github.com/grafana/grafana.git
synced 2024-12-28 01:41:24 -06:00
refactor: rename User Groups to Teams
This commit is contained in:
parent
b03b360414
commit
d8612380e9
@ -135,16 +135,16 @@ func (hs *HttpServer) registerRoutes() {
|
||||
usersRoute.Post("/:id/using/:orgId", wrap(UpdateUserActiveOrg))
|
||||
}, reqGrafanaAdmin)
|
||||
|
||||
// user group (admin permission required)
|
||||
apiRoute.Group("/user-groups", func(userGroupsRoute RouteRegister) {
|
||||
userGroupsRoute.Get("/:userGroupId", wrap(GetUserGroupById))
|
||||
userGroupsRoute.Get("/search", wrap(SearchUserGroups))
|
||||
userGroupsRoute.Post("/", quota("user-groups"), bind(m.CreateUserGroupCommand{}), wrap(CreateUserGroup))
|
||||
userGroupsRoute.Put("/:userGroupId", bind(m.UpdateUserGroupCommand{}), wrap(UpdateUserGroup))
|
||||
userGroupsRoute.Delete("/:userGroupId", wrap(DeleteUserGroupById))
|
||||
userGroupsRoute.Get("/:userGroupId/members", wrap(GetUserGroupMembers))
|
||||
userGroupsRoute.Post("/:userGroupId/members", quota("user-groups"), bind(m.AddUserGroupMemberCommand{}), wrap(AddUserGroupMember))
|
||||
userGroupsRoute.Delete("/:userGroupId/members/:userId", wrap(RemoveUserGroupMember))
|
||||
// team (admin permission required)
|
||||
apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
|
||||
teamsRoute.Get("/:teamId", wrap(GetTeamById))
|
||||
teamsRoute.Get("/search", wrap(SearchTeams))
|
||||
teamsRoute.Post("/", quota("teams"), bind(m.CreateTeamCommand{}), wrap(CreateTeam))
|
||||
teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
|
||||
teamsRoute.Delete("/:teamId", wrap(DeleteTeamById))
|
||||
teamsRoute.Get("/:teamId/members", wrap(GetTeamMembers))
|
||||
teamsRoute.Post("/:teamId/members", quota("teams"), bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember))
|
||||
teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
|
||||
}, reqOrgAdmin)
|
||||
|
||||
// org information available to all users.
|
||||
|
@ -43,7 +43,7 @@ func UpdateDashboardAcl(c *middleware.Context, apiCmd dtos.UpdateDashboardAclCom
|
||||
OrgId: c.OrgId,
|
||||
DashboardId: dashId,
|
||||
UserId: item.UserId,
|
||||
UserGroupId: item.UserGroupId,
|
||||
TeamId: item.TeamId,
|
||||
Role: item.Role,
|
||||
Permission: item.Permission,
|
||||
Created: time.Now(),
|
||||
|
@ -16,8 +16,8 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
|
||||
{Id: 1, OrgId: 1, DashboardId: 1, UserId: 2, Permission: m.PERMISSION_VIEW},
|
||||
{Id: 2, OrgId: 1, DashboardId: 1, UserId: 3, Permission: m.PERMISSION_EDIT},
|
||||
{Id: 3, OrgId: 1, DashboardId: 1, UserId: 4, Permission: m.PERMISSION_ADMIN},
|
||||
{Id: 4, OrgId: 1, DashboardId: 1, UserGroupId: 1, Permission: m.PERMISSION_VIEW},
|
||||
{Id: 5, OrgId: 1, DashboardId: 1, UserGroupId: 2, Permission: m.PERMISSION_ADMIN},
|
||||
{Id: 4, OrgId: 1, DashboardId: 1, TeamId: 1, Permission: m.PERMISSION_VIEW},
|
||||
{Id: 5, OrgId: 1, DashboardId: 1, TeamId: 2, Permission: m.PERMISSION_ADMIN},
|
||||
}
|
||||
dtoRes := transformDashboardAclsToDTOs(mockResult)
|
||||
|
||||
@ -31,9 +31,9 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
userGroupResp := []*m.UserGroup{}
|
||||
bus.AddHandler("test", func(query *m.GetUserGroupsByUserQuery) error {
|
||||
query.Result = userGroupResp
|
||||
teamResp := []*m.Team{}
|
||||
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
|
||||
query.Result = teamResp
|
||||
return nil
|
||||
})
|
||||
|
||||
@ -81,9 +81,9 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When user is a member of a user group in the ACL with admin permission", func() {
|
||||
Convey("When user is a member of a team in the ACL with admin permission", func() {
|
||||
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/1", "/api/dashboards/id/:dashboardsId/acl/:aclId", m.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||
userGroupResp = append(userGroupResp, &m.UserGroup{Id: 2, OrgId: 1, Name: "UG2"})
|
||||
teamResp = append(teamResp, &m.Team{Id: 2, OrgId: 1, Name: "UG2"})
|
||||
|
||||
bus.AddHandler("test3", func(cmd *m.RemoveDashboardAclCommand) error {
|
||||
return nil
|
||||
@ -165,7 +165,7 @@ func transformDashboardAclsToDTOs(acls []*m.DashboardAclInfoDTO) []*m.DashboardA
|
||||
DashboardId: acl.DashboardId,
|
||||
Permission: acl.Permission,
|
||||
UserId: acl.UserId,
|
||||
UserGroupId: acl.UserGroupId,
|
||||
TeamId: acl.TeamId,
|
||||
}
|
||||
dtos = append(dtos, dto)
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(query *m.GetUserGroupsByUserQuery) error {
|
||||
query.Result = []*m.UserGroup{}
|
||||
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
|
||||
query.Result = []*m.Team{}
|
||||
return nil
|
||||
})
|
||||
|
||||
@ -204,8 +204,8 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(query *m.GetUserGroupsByUserQuery) error {
|
||||
query.Result = []*m.UserGroup{}
|
||||
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
|
||||
query.Result = []*m.Team{}
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -10,7 +10,7 @@ type UpdateDashboardAclCommand struct {
|
||||
|
||||
type DashboardAclUpdateItem struct {
|
||||
UserId int64 `json:"userId"`
|
||||
UserGroupId int64 `json:"userGroupId"`
|
||||
TeamId int64 `json:"teamId"`
|
||||
Role *m.RoleType `json:"role,omitempty"`
|
||||
Permission m.PermissionType `json:"permission"`
|
||||
}
|
||||
|
@ -231,8 +231,8 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
||||
Text: "Teams",
|
||||
Id: "teams",
|
||||
Description: "Manage org groups",
|
||||
Icon: "gicon gicon-user-group",
|
||||
Url: setting.AppSubUrl + "/org/user-groups",
|
||||
Icon: "gicon gicon-team",
|
||||
Url: setting.AppSubUrl + "/org/teams",
|
||||
},
|
||||
{
|
||||
Text: "Plugins",
|
||||
|
@ -7,48 +7,48 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
// POST /api/user-groups
|
||||
func CreateUserGroup(c *middleware.Context, cmd m.CreateUserGroupCommand) Response {
|
||||
// POST /api/teams
|
||||
func CreateTeam(c *middleware.Context, cmd m.CreateTeamCommand) Response {
|
||||
cmd.OrgId = c.OrgId
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
if err == m.ErrUserGroupNameTaken {
|
||||
return ApiError(409, "User Group name taken", err)
|
||||
if err == m.ErrTeamNameTaken {
|
||||
return ApiError(409, "Team name taken", err)
|
||||
}
|
||||
return ApiError(500, "Failed to create User Group", err)
|
||||
return ApiError(500, "Failed to create Team", err)
|
||||
}
|
||||
|
||||
return Json(200, &util.DynMap{
|
||||
"userGroupId": cmd.Result.Id,
|
||||
"message": "User Group created",
|
||||
"teamId": cmd.Result.Id,
|
||||
"message": "Team created",
|
||||
})
|
||||
}
|
||||
|
||||
// PUT /api/user-groups/:userGroupId
|
||||
func UpdateUserGroup(c *middleware.Context, cmd m.UpdateUserGroupCommand) Response {
|
||||
cmd.Id = c.ParamsInt64(":userGroupId")
|
||||
// PUT /api/teams/:teamId
|
||||
func UpdateTeam(c *middleware.Context, cmd m.UpdateTeamCommand) Response {
|
||||
cmd.Id = c.ParamsInt64(":teamId")
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
if err == m.ErrUserGroupNameTaken {
|
||||
return ApiError(400, "User Group name taken", err)
|
||||
if err == m.ErrTeamNameTaken {
|
||||
return ApiError(400, "Team name taken", err)
|
||||
}
|
||||
return ApiError(500, "Failed to update User Group", err)
|
||||
return ApiError(500, "Failed to update Team", err)
|
||||
}
|
||||
|
||||
return ApiSuccess("User Group updated")
|
||||
return ApiSuccess("Team updated")
|
||||
}
|
||||
|
||||
// DELETE /api/user-groups/:userGroupId
|
||||
func DeleteUserGroupById(c *middleware.Context) Response {
|
||||
if err := bus.Dispatch(&m.DeleteUserGroupCommand{Id: c.ParamsInt64(":userGroupId")}); err != nil {
|
||||
if err == m.ErrUserGroupNotFound {
|
||||
return ApiError(404, "Failed to delete User Group. ID not found", nil)
|
||||
// DELETE /api/teams/:teamId
|
||||
func DeleteTeamById(c *middleware.Context) Response {
|
||||
if err := bus.Dispatch(&m.DeleteTeamCommand{Id: c.ParamsInt64(":teamId")}); err != nil {
|
||||
if err == m.ErrTeamNotFound {
|
||||
return ApiError(404, "Failed to delete Team. ID not found", nil)
|
||||
}
|
||||
return ApiError(500, "Failed to update User Group", err)
|
||||
return ApiError(500, "Failed to update Team", err)
|
||||
}
|
||||
return ApiSuccess("User Group deleted")
|
||||
return ApiSuccess("Team deleted")
|
||||
}
|
||||
|
||||
// GET /api/user-groups/search
|
||||
func SearchUserGroups(c *middleware.Context) Response {
|
||||
// GET /api/teams/search
|
||||
func SearchTeams(c *middleware.Context) Response {
|
||||
perPage := c.QueryInt("perpage")
|
||||
if perPage <= 0 {
|
||||
perPage = 1000
|
||||
@ -58,7 +58,7 @@ func SearchUserGroups(c *middleware.Context) Response {
|
||||
page = 1
|
||||
}
|
||||
|
||||
query := m.SearchUserGroupsQuery{
|
||||
query := m.SearchTeamsQuery{
|
||||
Query: c.Query("query"),
|
||||
Name: c.Query("name"),
|
||||
Page: page,
|
||||
@ -67,7 +67,7 @@ func SearchUserGroups(c *middleware.Context) Response {
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
return ApiError(500, "Failed to search User Groups", err)
|
||||
return ApiError(500, "Failed to search Teams", err)
|
||||
}
|
||||
|
||||
query.Result.Page = page
|
||||
@ -76,16 +76,16 @@ func SearchUserGroups(c *middleware.Context) Response {
|
||||
return Json(200, query.Result)
|
||||
}
|
||||
|
||||
// GET /api/user-groups/:userGroupId
|
||||
func GetUserGroupById(c *middleware.Context) Response {
|
||||
query := m.GetUserGroupByIdQuery{Id: c.ParamsInt64(":userGroupId")}
|
||||
// GET /api/teams/:teamId
|
||||
func GetTeamById(c *middleware.Context) Response {
|
||||
query := m.GetTeamByIdQuery{Id: c.ParamsInt64(":teamId")}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
if err == m.ErrUserGroupNotFound {
|
||||
return ApiError(404, "User Group not found", err)
|
||||
if err == m.ErrTeamNotFound {
|
||||
return ApiError(404, "Team not found", err)
|
||||
}
|
||||
|
||||
return ApiError(500, "Failed to get User Group", err)
|
||||
return ApiError(500, "Failed to get Team", err)
|
||||
}
|
||||
|
||||
return Json(200, &query.Result)
|
||||
|
@ -7,38 +7,38 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
// GET /api/user-groups/:userGroupId/members
|
||||
func GetUserGroupMembers(c *middleware.Context) Response {
|
||||
query := m.GetUserGroupMembersQuery{UserGroupId: c.ParamsInt64(":userGroupId")}
|
||||
// GET /api/teams/:teamId/members
|
||||
func GetTeamMembers(c *middleware.Context) Response {
|
||||
query := m.GetTeamMembersQuery{TeamId: c.ParamsInt64(":teamId")}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
return ApiError(500, "Failed to get User Group Members", err)
|
||||
return ApiError(500, "Failed to get Team Members", err)
|
||||
}
|
||||
|
||||
return Json(200, query.Result)
|
||||
}
|
||||
|
||||
// POST /api/user-groups/:userGroupId/members
|
||||
func AddUserGroupMember(c *middleware.Context, cmd m.AddUserGroupMemberCommand) Response {
|
||||
cmd.UserGroupId = c.ParamsInt64(":userGroupId")
|
||||
// POST /api/teams/:teamId/members
|
||||
func AddTeamMember(c *middleware.Context, cmd m.AddTeamMemberCommand) Response {
|
||||
cmd.TeamId = c.ParamsInt64(":teamId")
|
||||
cmd.OrgId = c.OrgId
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
if err == m.ErrUserGroupMemberAlreadyAdded {
|
||||
return ApiError(400, "User is already added to this user group", err)
|
||||
if err == m.ErrTeamMemberAlreadyAdded {
|
||||
return ApiError(400, "User is already added to this team", err)
|
||||
}
|
||||
return ApiError(500, "Failed to add Member to User Group", err)
|
||||
return ApiError(500, "Failed to add Member to Team", err)
|
||||
}
|
||||
|
||||
return Json(200, &util.DynMap{
|
||||
"message": "Member added to User Group",
|
||||
"message": "Member added to Team",
|
||||
})
|
||||
}
|
||||
|
||||
// DELETE /api/user-groups/:userGroupId/members/:userId
|
||||
func RemoveUserGroupMember(c *middleware.Context) Response {
|
||||
if err := bus.Dispatch(&m.RemoveUserGroupMemberCommand{UserGroupId: c.ParamsInt64(":userGroupId"), UserId: c.ParamsInt64(":userId")}); err != nil {
|
||||
return ApiError(500, "Failed to remove Member from User Group", err)
|
||||
// DELETE /api/teams/:teamId/members/:userId
|
||||
func RemoveTeamMember(c *middleware.Context) Response {
|
||||
if err := bus.Dispatch(&m.RemoveTeamMemberCommand{TeamId: c.ParamsInt64(":teamId"), UserId: c.ParamsInt64(":userId")}); err != nil {
|
||||
return ApiError(500, "Failed to remove Member from Team", err)
|
||||
}
|
||||
return ApiSuccess("User Group Member removed")
|
||||
return ApiSuccess("Team Member removed")
|
||||
}
|
||||
|
@ -10,21 +10,21 @@ import (
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestUserGroupApiEndpoint(t *testing.T) {
|
||||
Convey("Given two user groups", t, func() {
|
||||
mockResult := models.SearchUserGroupQueryResult{
|
||||
UserGroups: []*models.UserGroup{
|
||||
{Name: "userGroup1"},
|
||||
{Name: "userGroup2"},
|
||||
func TestTeamApiEndpoint(t *testing.T) {
|
||||
Convey("Given two teams", t, func() {
|
||||
mockResult := models.SearchTeamQueryResult{
|
||||
Teams: []*models.Team{
|
||||
{Name: "team1"},
|
||||
{Name: "team2"},
|
||||
},
|
||||
TotalCount: 2,
|
||||
}
|
||||
|
||||
Convey("When searching with no parameters", func() {
|
||||
loggedInUserScenario("When calling GET on", "/api/user-groups/search", func(sc *scenarioContext) {
|
||||
loggedInUserScenario("When calling GET on", "/api/teams/search", func(sc *scenarioContext) {
|
||||
var sentLimit int
|
||||
var sendPage int
|
||||
bus.AddHandler("test", func(query *models.SearchUserGroupsQuery) error {
|
||||
bus.AddHandler("test", func(query *models.SearchTeamsQuery) error {
|
||||
query.Result = mockResult
|
||||
|
||||
sentLimit = query.Limit
|
||||
@ -33,7 +33,7 @@ func TestUserGroupApiEndpoint(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
sc.handlerFunc = SearchUserGroups
|
||||
sc.handlerFunc = SearchTeams
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||
|
||||
So(sentLimit, ShouldEqual, 1000)
|
||||
@ -43,15 +43,15 @@ func TestUserGroupApiEndpoint(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(respJSON.Get("totalCount").MustInt(), ShouldEqual, 2)
|
||||
So(len(respJSON.Get("userGroups").MustArray()), ShouldEqual, 2)
|
||||
So(len(respJSON.Get("teams").MustArray()), ShouldEqual, 2)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When searching with page and perpage parameters", func() {
|
||||
loggedInUserScenario("When calling GET on", "/api/user-groups/search", func(sc *scenarioContext) {
|
||||
loggedInUserScenario("When calling GET on", "/api/teams/search", func(sc *scenarioContext) {
|
||||
var sentLimit int
|
||||
var sendPage int
|
||||
bus.AddHandler("test", func(query *models.SearchUserGroupsQuery) error {
|
||||
bus.AddHandler("test", func(query *models.SearchTeamsQuery) error {
|
||||
query.Result = mockResult
|
||||
|
||||
sentLimit = query.Limit
|
||||
@ -60,7 +60,7 @@ func TestUserGroupApiEndpoint(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
sc.handlerFunc = SearchUserGroups
|
||||
sc.handlerFunc = SearchTeams
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
|
||||
|
||||
So(sentLimit, ShouldEqual, 10)
|
||||
|
@ -24,7 +24,7 @@ func (p PermissionType) String() string {
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrDashboardAclInfoMissing = errors.New("User id and user group id cannot both be empty for a dashboard permission.")
|
||||
ErrDashboardAclInfoMissing = errors.New("User id and team id cannot both be empty for a dashboard permission.")
|
||||
ErrDashboardPermissionDashboardEmpty = errors.New("Dashboard Id must be greater than zero for a dashboard permission.")
|
||||
)
|
||||
|
||||
@ -35,7 +35,7 @@ type DashboardAcl struct {
|
||||
DashboardId int64
|
||||
|
||||
UserId int64
|
||||
UserGroupId int64
|
||||
TeamId int64
|
||||
Role *RoleType // pointer to be nullable
|
||||
Permission PermissionType
|
||||
|
||||
@ -54,8 +54,8 @@ type DashboardAclInfoDTO struct {
|
||||
UserId int64 `json:"userId"`
|
||||
UserLogin string `json:"userLogin"`
|
||||
UserEmail string `json:"userEmail"`
|
||||
UserGroupId int64 `json:"userGroupId"`
|
||||
UserGroup string `json:"userGroup"`
|
||||
TeamId int64 `json:"teamId"`
|
||||
Team string `json:"team"`
|
||||
Role *RoleType `json:"role,omitempty"`
|
||||
Permission PermissionType `json:"permission"`
|
||||
PermissionName string `json:"permissionName"`
|
||||
@ -74,7 +74,7 @@ type SetDashboardAclCommand struct {
|
||||
DashboardId int64
|
||||
OrgId int64
|
||||
UserId int64
|
||||
UserGroupId int64
|
||||
TeamId int64
|
||||
Permission PermissionType
|
||||
|
||||
Result DashboardAcl
|
||||
|
@ -7,12 +7,12 @@ import (
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrUserGroupNotFound = errors.New("User Group not found")
|
||||
ErrUserGroupNameTaken = errors.New("User Group name is taken")
|
||||
ErrTeamNotFound = errors.New("Team not found")
|
||||
ErrTeamNameTaken = errors.New("Team name is taken")
|
||||
)
|
||||
|
||||
// UserGroup model
|
||||
type UserGroup struct {
|
||||
// Team model
|
||||
type Team struct {
|
||||
Id int64 `json:"id"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
Name string `json:"name"`
|
||||
@ -24,45 +24,45 @@ type UserGroup struct {
|
||||
// ---------------------
|
||||
// COMMANDS
|
||||
|
||||
type CreateUserGroupCommand struct {
|
||||
type CreateTeamCommand struct {
|
||||
Name string `json:"name" binding:"Required"`
|
||||
OrgId int64 `json:"-"`
|
||||
|
||||
Result UserGroup `json:"-"`
|
||||
Result Team `json:"-"`
|
||||
}
|
||||
|
||||
type UpdateUserGroupCommand struct {
|
||||
type UpdateTeamCommand struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
type DeleteUserGroupCommand struct {
|
||||
type DeleteTeamCommand struct {
|
||||
Id int64
|
||||
}
|
||||
|
||||
type GetUserGroupByIdQuery struct {
|
||||
type GetTeamByIdQuery struct {
|
||||
Id int64
|
||||
Result *UserGroup
|
||||
Result *Team
|
||||
}
|
||||
|
||||
type GetUserGroupsByUserQuery struct {
|
||||
type GetTeamsByUserQuery struct {
|
||||
UserId int64 `json:"userId"`
|
||||
Result []*UserGroup `json:"userGroups"`
|
||||
Result []*Team `json:"teams"`
|
||||
}
|
||||
|
||||
type SearchUserGroupsQuery struct {
|
||||
type SearchTeamsQuery struct {
|
||||
Query string
|
||||
Name string
|
||||
Limit int
|
||||
Page int
|
||||
OrgId int64
|
||||
|
||||
Result SearchUserGroupQueryResult
|
||||
Result SearchTeamQueryResult
|
||||
}
|
||||
|
||||
type SearchUserGroupQueryResult struct {
|
||||
type SearchTeamQueryResult struct {
|
||||
TotalCount int64 `json:"totalCount"`
|
||||
UserGroups []*UserGroup `json:"userGroups"`
|
||||
Teams []*Team `json:"teams"`
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"perPage"`
|
||||
}
|
||||
|
@ -7,14 +7,14 @@ import (
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrUserGroupMemberAlreadyAdded = errors.New("User is already added to this user group")
|
||||
ErrTeamMemberAlreadyAdded = errors.New("User is already added to this team")
|
||||
)
|
||||
|
||||
// UserGroupMember model
|
||||
type UserGroupMember struct {
|
||||
// TeamMember model
|
||||
type TeamMember struct {
|
||||
Id int64
|
||||
OrgId int64
|
||||
UserGroupId int64
|
||||
TeamId int64
|
||||
UserId int64
|
||||
|
||||
Created time.Time
|
||||
@ -24,31 +24,31 @@ type UserGroupMember struct {
|
||||
// ---------------------
|
||||
// COMMANDS
|
||||
|
||||
type AddUserGroupMemberCommand struct {
|
||||
type AddTeamMemberCommand struct {
|
||||
UserId int64 `json:"userId" binding:"Required"`
|
||||
OrgId int64 `json:"-"`
|
||||
UserGroupId int64 `json:"-"`
|
||||
TeamId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type RemoveUserGroupMemberCommand struct {
|
||||
type RemoveTeamMemberCommand struct {
|
||||
UserId int64
|
||||
UserGroupId int64
|
||||
TeamId int64
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// QUERIES
|
||||
|
||||
type GetUserGroupMembersQuery struct {
|
||||
UserGroupId int64
|
||||
Result []*UserGroupMemberDTO
|
||||
type GetTeamMembersQuery struct {
|
||||
TeamId int64
|
||||
Result []*TeamMemberDTO
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// Projections and DTOs
|
||||
|
||||
type UserGroupMemberDTO struct {
|
||||
type TeamMemberDTO struct {
|
||||
OrgId int64 `json:"orgId"`
|
||||
UserGroupId int64 `json:"userGroupId"`
|
||||
TeamId int64 `json:"teamId"`
|
||||
UserId int64 `json:"userId"`
|
||||
Email string `json:"email"`
|
||||
Login string `json:"login"`
|
||||
|
@ -11,7 +11,7 @@ type DashboardGuardian struct {
|
||||
dashId int64
|
||||
orgId int64
|
||||
acl []*m.DashboardAclInfoDTO
|
||||
groups []*m.UserGroup
|
||||
groups []*m.Team
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ func (g *DashboardGuardian) HasPermission(permission m.PermissionType) (bool, er
|
||||
orgRole = m.ROLE_VIEWER
|
||||
}
|
||||
|
||||
userGroupAclItems := []*m.DashboardAclInfoDTO{}
|
||||
teamAclItems := []*m.DashboardAclInfoDTO{}
|
||||
|
||||
for _, p := range acl {
|
||||
// user match
|
||||
@ -71,26 +71,26 @@ func (g *DashboardGuardian) HasPermission(permission m.PermissionType) (bool, er
|
||||
}
|
||||
|
||||
// remember this rule for later
|
||||
if p.UserGroupId > 0 {
|
||||
userGroupAclItems = append(userGroupAclItems, p)
|
||||
if p.TeamId > 0 {
|
||||
teamAclItems = append(teamAclItems, p)
|
||||
}
|
||||
}
|
||||
|
||||
// do we have group rules?
|
||||
if len(userGroupAclItems) == 0 {
|
||||
if len(teamAclItems) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// load groups
|
||||
userGroups, err := g.getUserGroups()
|
||||
teams, err := g.getTeams()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// evalute group rules
|
||||
for _, p := range acl {
|
||||
for _, ug := range userGroups {
|
||||
if ug.Id == p.UserGroupId && p.Permission >= permission {
|
||||
for _, ug := range teams {
|
||||
if ug.Id == p.TeamId && p.Permission >= permission {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
@ -114,12 +114,12 @@ func (g *DashboardGuardian) GetAcl() ([]*m.DashboardAclInfoDTO, error) {
|
||||
return g.acl, nil
|
||||
}
|
||||
|
||||
func (g *DashboardGuardian) getUserGroups() ([]*m.UserGroup, error) {
|
||||
func (g *DashboardGuardian) getTeams() ([]*m.Team, error) {
|
||||
if g.groups != nil {
|
||||
return g.groups, nil
|
||||
}
|
||||
|
||||
query := m.GetUserGroupsByUserQuery{UserId: g.user.UserId}
|
||||
query := m.GetTeamsByUserQuery{UserId: g.user.UserId}
|
||||
err := bus.Dispatch(&query)
|
||||
|
||||
g.groups = query.Result
|
||||
|
@ -24,7 +24,7 @@ func UpdateDashboardAcl(cmd *m.UpdateDashboardAclCommand) error {
|
||||
}
|
||||
|
||||
for _, item := range cmd.Items {
|
||||
if item.UserId == 0 && item.UserGroupId == 0 && !item.Role.IsValid() {
|
||||
if item.UserId == 0 && item.TeamId == 0 && !item.Role.IsValid() {
|
||||
return m.ErrDashboardAclInfoMissing
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ func UpdateDashboardAcl(cmd *m.UpdateDashboardAclCommand) error {
|
||||
return m.ErrDashboardPermissionDashboardEmpty
|
||||
}
|
||||
|
||||
sess.Nullable("user_id", "user_group_id")
|
||||
sess.Nullable("user_id", "team_id")
|
||||
if _, err := sess.Insert(item); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -49,7 +49,7 @@ func UpdateDashboardAcl(cmd *m.UpdateDashboardAclCommand) error {
|
||||
|
||||
func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
if cmd.UserId == 0 && cmd.UserGroupId == 0 {
|
||||
if cmd.UserId == 0 && cmd.TeamId == 0 {
|
||||
return m.ErrDashboardAclInfoMissing
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
|
||||
return m.ErrDashboardPermissionDashboardEmpty
|
||||
}
|
||||
|
||||
if res, err := sess.Query("SELECT 1 from "+dialect.Quote("dashboard_acl")+" WHERE dashboard_id =? and (user_group_id=? or user_id=?)", cmd.DashboardId, cmd.UserGroupId, cmd.UserId); err != nil {
|
||||
if res, err := sess.Query("SELECT 1 from "+dialect.Quote("dashboard_acl")+" WHERE dashboard_id =? and (team_id=? or user_id=?)", cmd.DashboardId, cmd.TeamId, cmd.UserId); err != nil {
|
||||
return err
|
||||
} else if len(res) == 1 {
|
||||
|
||||
@ -66,7 +66,7 @@ func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Cols("updated", "permission").Where("dashboard_id =? and (user_group_id=? or user_id=?)", cmd.DashboardId, cmd.UserGroupId, cmd.UserId).Update(&entity); err != nil {
|
||||
if _, err := sess.Cols("updated", "permission").Where("dashboard_id =? and (team_id=? or user_id=?)", cmd.DashboardId, cmd.TeamId, cmd.UserId).Update(&entity); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
|
||||
|
||||
entity := m.DashboardAcl{
|
||||
OrgId: cmd.OrgId,
|
||||
UserGroupId: cmd.UserGroupId,
|
||||
TeamId: cmd.TeamId,
|
||||
UserId: cmd.UserId,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
@ -89,8 +89,8 @@ func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
|
||||
cols = append(cols, "user_id")
|
||||
}
|
||||
|
||||
if cmd.UserGroupId != 0 {
|
||||
cols = append(cols, "user_group_id")
|
||||
if cmd.TeamId != 0 {
|
||||
cols = append(cols, "team_id")
|
||||
}
|
||||
|
||||
_, err := sess.Cols(cols...).Insert(&entity)
|
||||
@ -138,17 +138,17 @@ func GetDashboardAclInfoList(query *m.GetDashboardAclInfoListQuery) error {
|
||||
da.org_id,
|
||||
da.dashboard_id,
|
||||
da.user_id,
|
||||
da.user_group_id,
|
||||
da.team_id,
|
||||
da.permission,
|
||||
da.role,
|
||||
da.created,
|
||||
da.updated,
|
||||
u.login AS user_login,
|
||||
u.email AS user_email,
|
||||
ug.name AS user_group
|
||||
ug.name AS team
|
||||
FROM` + dialect.Quote("dashboard_acl") + ` as da
|
||||
LEFT OUTER JOIN ` + dialect.Quote("user") + ` AS u ON u.id = da.user_id
|
||||
LEFT OUTER JOIN user_group ug on ug.id = da.user_group_id
|
||||
LEFT OUTER JOIN team ug on ug.id = da.team_id
|
||||
WHERE dashboard_id ` + dashboardFilter + ` AND da.org_id = ?
|
||||
|
||||
-- Also include default permission if has_acl = 0
|
||||
@ -159,14 +159,14 @@ func GetDashboardAclInfoList(query *m.GetDashboardAclInfoListQuery) error {
|
||||
da.org_id,
|
||||
da.dashboard_id,
|
||||
da.user_id,
|
||||
da.user_group_id,
|
||||
da.team_id,
|
||||
da.permission,
|
||||
da.role,
|
||||
da.created,
|
||||
da.updated,
|
||||
'' as user_login,
|
||||
'' as user_email,
|
||||
'' as user_group
|
||||
'' as team
|
||||
FROM dashboard_acl as da,
|
||||
dashboard as dash
|
||||
LEFT JOIN dashboard folder on dash.folder_id = folder.id
|
||||
|
@ -16,7 +16,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
savedFolder := insertTestDashboard("1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
childDash := insertTestDashboard("2 test dash", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
|
||||
Convey("When adding dashboard permission with userId and userGroupId set to 0", func() {
|
||||
Convey("When adding dashboard permission with userId and teamId set to 0", func() {
|
||||
err := SetDashboardAcl(&m.SetDashboardAclCommand{
|
||||
OrgId: 1,
|
||||
DashboardId: savedFolder.Id,
|
||||
@ -175,15 +175,15 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given a user group", func() {
|
||||
group1 := m.CreateUserGroupCommand{Name: "group1 name", OrgId: 1}
|
||||
err := CreateUserGroup(&group1)
|
||||
Convey("Given a team", func() {
|
||||
group1 := m.CreateTeamCommand{Name: "group1 name", OrgId: 1}
|
||||
err := CreateTeam(&group1)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Should be able to add a user permission for a user group", func() {
|
||||
Convey("Should be able to add a user permission for a team", func() {
|
||||
setDashAclCmd := m.SetDashboardAclCommand{
|
||||
OrgId: 1,
|
||||
UserGroupId: group1.Result.Id,
|
||||
TeamId: group1.Result.Id,
|
||||
DashboardId: savedFolder.Id,
|
||||
Permission: m.PERMISSION_EDIT,
|
||||
}
|
||||
@ -196,9 +196,9 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(q1.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
|
||||
So(q1.Result[0].Permission, ShouldEqual, m.PERMISSION_EDIT)
|
||||
So(q1.Result[0].UserGroupId, ShouldEqual, group1.Result.Id)
|
||||
So(q1.Result[0].TeamId, ShouldEqual, group1.Result.Id)
|
||||
|
||||
Convey("Should be able to delete an existing permission for a user group", func() {
|
||||
Convey("Should be able to delete an existing permission for a team", func() {
|
||||
err := RemoveDashboardAcl(&m.RemoveDashboardAclCommand{
|
||||
OrgId: 1,
|
||||
AclId: setDashAclCmd.Result.Id,
|
||||
@ -212,10 +212,10 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Should be able to update an existing permission for a user group", func() {
|
||||
Convey("Should be able to update an existing permission for a team", func() {
|
||||
err := SetDashboardAcl(&m.SetDashboardAclCommand{
|
||||
OrgId: 1,
|
||||
UserGroupId: group1.Result.Id,
|
||||
TeamId: group1.Result.Id,
|
||||
DashboardId: savedFolder.Id,
|
||||
Permission: m.PERMISSION_ADMIN,
|
||||
})
|
||||
@ -227,7 +227,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
So(len(q3.Result), ShouldEqual, 1)
|
||||
So(q3.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
|
||||
So(q3.Result[0].Permission, ShouldEqual, m.PERMISSION_ADMIN)
|
||||
So(q3.Result[0].UserGroupId, ShouldEqual, group1.Result.Id)
|
||||
So(q3.Result[0].TeamId, ShouldEqual, group1.Result.Id)
|
||||
})
|
||||
|
||||
})
|
||||
|
@ -10,7 +10,7 @@ func addDashboardAclMigrations(mg *Migrator) {
|
||||
{Name: "org_id", Type: DB_BigInt},
|
||||
{Name: "dashboard_id", Type: DB_BigInt},
|
||||
{Name: "user_id", Type: DB_BigInt, Nullable: true},
|
||||
{Name: "user_group_id", Type: DB_BigInt, Nullable: true},
|
||||
{Name: "team_id", Type: DB_BigInt, Nullable: true},
|
||||
{Name: "permission", Type: DB_SmallInt, Default: "4"},
|
||||
{Name: "role", Type: DB_Varchar, Length: 20, Nullable: true},
|
||||
{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
@ -19,7 +19,7 @@ func addDashboardAclMigrations(mg *Migrator) {
|
||||
Indices: []*Index{
|
||||
{Cols: []string{"dashboard_id"}},
|
||||
{Cols: []string{"dashboard_id", "user_id"}, Type: UniqueIndex},
|
||||
{Cols: []string{"dashboard_id", "user_group_id"}, Type: UniqueIndex},
|
||||
{Cols: []string{"dashboard_id", "team_id"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ func AddMigrations(mg *Migrator) {
|
||||
addAnnotationMig(mg)
|
||||
addTestDataMigrations(mg)
|
||||
addDashboardVersionMigration(mg)
|
||||
addUserGroupMigrations(mg)
|
||||
addTeamMigrations(mg)
|
||||
addDashboardAclMigrations(mg)
|
||||
addTagMigration(mg)
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ package migrations
|
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
|
||||
func addUserGroupMigrations(mg *Migrator) {
|
||||
userGroupV1 := Table{
|
||||
Name: "user_group",
|
||||
func addTeamMigrations(mg *Migrator) {
|
||||
teamV1 := Table{
|
||||
Name: "team",
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
@ -18,31 +18,31 @@ func addUserGroupMigrations(mg *Migrator) {
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create user group table", NewAddTableMigration(userGroupV1))
|
||||
mg.AddMigration("create team table", NewAddTableMigration(teamV1))
|
||||
|
||||
//------- indexes ------------------
|
||||
mg.AddMigration("add index user_group.org_id", NewAddIndexMigration(userGroupV1, userGroupV1.Indices[0]))
|
||||
mg.AddMigration("add unique index user_group_org_id_name", NewAddIndexMigration(userGroupV1, userGroupV1.Indices[1]))
|
||||
mg.AddMigration("add index team.org_id", NewAddIndexMigration(teamV1, teamV1.Indices[0]))
|
||||
mg.AddMigration("add unique index team_org_id_name", NewAddIndexMigration(teamV1, teamV1.Indices[1]))
|
||||
|
||||
userGroupMemberV1 := Table{
|
||||
Name: "user_group_member",
|
||||
teamMemberV1 := Table{
|
||||
Name: "team_member",
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "org_id", Type: DB_BigInt},
|
||||
{Name: "user_group_id", Type: DB_BigInt},
|
||||
{Name: "team_id", Type: DB_BigInt},
|
||||
{Name: "user_id", Type: DB_BigInt},
|
||||
{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
{Cols: []string{"org_id"}},
|
||||
{Cols: []string{"org_id", "user_group_id", "user_id"}, Type: UniqueIndex},
|
||||
{Cols: []string{"org_id", "team_id", "user_id"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create user group member table", NewAddTableMigration(userGroupMemberV1))
|
||||
mg.AddMigration("create team member table", NewAddTableMigration(teamMemberV1))
|
||||
|
||||
//------- indexes ------------------
|
||||
mg.AddMigration("add index user_group_member.org_id", NewAddIndexMigration(userGroupMemberV1, userGroupMemberV1.Indices[0]))
|
||||
mg.AddMigration("add unique index user_group_member_org_id_user_group_id_user_id", NewAddIndexMigration(userGroupMemberV1, userGroupMemberV1.Indices[1]))
|
||||
mg.AddMigration("add index team_member.org_id", NewAddIndexMigration(teamMemberV1, teamMemberV1.Indices[0]))
|
||||
mg.AddMigration("add unique index team_member_org_id_team_id_user_id", NewAddIndexMigration(teamMemberV1, teamMemberV1.Indices[1]))
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ func RemoveOrgUser(cmd *m.RemoveOrgUserCommand) error {
|
||||
deletes := []string{
|
||||
"DELETE FROM org_user WHERE org_id=? and user_id=?",
|
||||
"DELETE FROM dashboard_acl WHERE org_id=? and user_id = ?",
|
||||
"DELETE FROM user_group_member WHERE org_id=? and user_id = ?",
|
||||
"DELETE FROM team_member WHERE org_id=? and user_id = ?",
|
||||
}
|
||||
|
||||
for _, sql := range deletes {
|
||||
|
@ -179,7 +179,7 @@ func (sb *SearchBuilder) buildSearchWhereClause() {
|
||||
SELECT distinct d.id AS DashboardId
|
||||
FROM dashboard AS d
|
||||
LEFT JOIN dashboard_acl as da on d.folder_id = da.dashboard_id or d.id = da.dashboard_id
|
||||
LEFT JOIN user_group_member as ugm on ugm.user_group_id = da.user_group_id
|
||||
LEFT JOIN team_member as ugm on ugm.team_id = da.team_id
|
||||
LEFT JOIN org_user ou on ou.role = da.role
|
||||
WHERE
|
||||
d.has_acl = 1 and
|
||||
|
@ -442,7 +442,7 @@ func DeleteUser(cmd *m.DeleteUserCommand) error {
|
||||
"DELETE FROM org_user WHERE user_id = ?",
|
||||
"DELETE FROM dashboard_acl WHERE user_id = ?",
|
||||
"DELETE FROM preferences WHERE user_id = ?",
|
||||
"DELETE FROM user_group_member WHERE user_id = ?",
|
||||
"DELETE FROM team_member WHERE user_id = ?",
|
||||
}
|
||||
|
||||
for _, sql := range deletes {
|
||||
|
@ -9,82 +9,82 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", CreateUserGroup)
|
||||
bus.AddHandler("sql", UpdateUserGroup)
|
||||
bus.AddHandler("sql", DeleteUserGroup)
|
||||
bus.AddHandler("sql", SearchUserGroups)
|
||||
bus.AddHandler("sql", GetUserGroupById)
|
||||
bus.AddHandler("sql", GetUserGroupsByUser)
|
||||
bus.AddHandler("sql", CreateTeam)
|
||||
bus.AddHandler("sql", UpdateTeam)
|
||||
bus.AddHandler("sql", DeleteTeam)
|
||||
bus.AddHandler("sql", SearchTeams)
|
||||
bus.AddHandler("sql", GetTeamById)
|
||||
bus.AddHandler("sql", GetTeamsByUser)
|
||||
|
||||
bus.AddHandler("sql", AddUserGroupMember)
|
||||
bus.AddHandler("sql", RemoveUserGroupMember)
|
||||
bus.AddHandler("sql", GetUserGroupMembers)
|
||||
bus.AddHandler("sql", AddTeamMember)
|
||||
bus.AddHandler("sql", RemoveTeamMember)
|
||||
bus.AddHandler("sql", GetTeamMembers)
|
||||
}
|
||||
|
||||
func CreateUserGroup(cmd *m.CreateUserGroupCommand) error {
|
||||
func CreateTeam(cmd *m.CreateTeamCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
|
||||
if isNameTaken, err := isUserGroupNameTaken(cmd.Name, 0, sess); err != nil {
|
||||
if isNameTaken, err := isTeamNameTaken(cmd.Name, 0, sess); err != nil {
|
||||
return err
|
||||
} else if isNameTaken {
|
||||
return m.ErrUserGroupNameTaken
|
||||
return m.ErrTeamNameTaken
|
||||
}
|
||||
|
||||
userGroup := m.UserGroup{
|
||||
team := m.Team{
|
||||
Name: cmd.Name,
|
||||
OrgId: cmd.OrgId,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&userGroup)
|
||||
_, err := sess.Insert(&team)
|
||||
|
||||
cmd.Result = userGroup
|
||||
cmd.Result = team
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateUserGroup(cmd *m.UpdateUserGroupCommand) error {
|
||||
func UpdateTeam(cmd *m.UpdateTeamCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
|
||||
if isNameTaken, err := isUserGroupNameTaken(cmd.Name, cmd.Id, sess); err != nil {
|
||||
if isNameTaken, err := isTeamNameTaken(cmd.Name, cmd.Id, sess); err != nil {
|
||||
return err
|
||||
} else if isNameTaken {
|
||||
return m.ErrUserGroupNameTaken
|
||||
return m.ErrTeamNameTaken
|
||||
}
|
||||
|
||||
userGroup := m.UserGroup{
|
||||
team := m.Team{
|
||||
Name: cmd.Name,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
affectedRows, err := sess.Id(cmd.Id).Update(&userGroup)
|
||||
affectedRows, err := sess.Id(cmd.Id).Update(&team)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if affectedRows == 0 {
|
||||
return m.ErrUserGroupNotFound
|
||||
return m.ErrTeamNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteUserGroup(cmd *m.DeleteUserGroupCommand) error {
|
||||
func DeleteTeam(cmd *m.DeleteTeamCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
if res, err := sess.Query("SELECT 1 from user_group WHERE id=?", cmd.Id); err != nil {
|
||||
if res, err := sess.Query("SELECT 1 from team WHERE id=?", cmd.Id); err != nil {
|
||||
return err
|
||||
} else if len(res) != 1 {
|
||||
return m.ErrUserGroupNotFound
|
||||
return m.ErrTeamNotFound
|
||||
}
|
||||
|
||||
deletes := []string{
|
||||
"DELETE FROM user_group_member WHERE user_group_id = ?",
|
||||
"DELETE FROM user_group WHERE id = ?",
|
||||
"DELETE FROM dashboard_acl WHERE user_group_id = ?",
|
||||
"DELETE FROM team_member WHERE team_id = ?",
|
||||
"DELETE FROM team WHERE id = ?",
|
||||
"DELETE FROM dashboard_acl WHERE team_id = ?",
|
||||
}
|
||||
|
||||
for _, sql := range deletes {
|
||||
@ -97,28 +97,28 @@ func DeleteUserGroup(cmd *m.DeleteUserGroupCommand) error {
|
||||
})
|
||||
}
|
||||
|
||||
func isUserGroupNameTaken(name string, existingId int64, sess *DBSession) (bool, error) {
|
||||
var userGroup m.UserGroup
|
||||
exists, err := sess.Where("name=?", name).Get(&userGroup)
|
||||
func isTeamNameTaken(name string, existingId int64, sess *DBSession) (bool, error) {
|
||||
var team m.Team
|
||||
exists, err := sess.Where("name=?", name).Get(&team)
|
||||
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if exists && existingId != userGroup.Id {
|
||||
if exists && existingId != team.Id {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func SearchUserGroups(query *m.SearchUserGroupsQuery) error {
|
||||
query.Result = m.SearchUserGroupQueryResult{
|
||||
UserGroups: make([]*m.UserGroup, 0),
|
||||
func SearchTeams(query *m.SearchTeamsQuery) error {
|
||||
query.Result = m.SearchTeamQueryResult{
|
||||
Teams: make([]*m.Team, 0),
|
||||
}
|
||||
queryWithWildcards := "%" + query.Query + "%"
|
||||
|
||||
sess := x.Table("user_group")
|
||||
sess := x.Table("team")
|
||||
sess.Where("org_id=?", query.OrgId)
|
||||
|
||||
if query.Query != "" {
|
||||
@ -132,46 +132,46 @@ func SearchUserGroups(query *m.SearchUserGroupsQuery) error {
|
||||
offset := query.Limit * (query.Page - 1)
|
||||
sess.Limit(query.Limit, offset)
|
||||
sess.Cols("id", "name")
|
||||
if err := sess.Find(&query.Result.UserGroups); err != nil {
|
||||
if err := sess.Find(&query.Result.Teams); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userGroup := m.UserGroup{}
|
||||
team := m.Team{}
|
||||
|
||||
countSess := x.Table("user_group")
|
||||
countSess := x.Table("team")
|
||||
if query.Query != "" {
|
||||
countSess.Where("name LIKE ?", queryWithWildcards)
|
||||
}
|
||||
if query.Name != "" {
|
||||
countSess.Where("name=?", query.Name)
|
||||
}
|
||||
count, err := countSess.Count(&userGroup)
|
||||
count, err := countSess.Count(&team)
|
||||
query.Result.TotalCount = count
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetUserGroupById(query *m.GetUserGroupByIdQuery) error {
|
||||
var userGroup m.UserGroup
|
||||
exists, err := x.Id(query.Id).Get(&userGroup)
|
||||
func GetTeamById(query *m.GetTeamByIdQuery) error {
|
||||
var team m.Team
|
||||
exists, err := x.Id(query.Id).Get(&team)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return m.ErrUserGroupNotFound
|
||||
return m.ErrTeamNotFound
|
||||
}
|
||||
|
||||
query.Result = &userGroup
|
||||
query.Result = &team
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserGroupsByUser(query *m.GetUserGroupsByUserQuery) error {
|
||||
query.Result = make([]*m.UserGroup, 0)
|
||||
func GetTeamsByUser(query *m.GetTeamsByUserQuery) error {
|
||||
query.Result = make([]*m.Team, 0)
|
||||
|
||||
sess := x.Table("user_group")
|
||||
sess.Join("INNER", "user_group_member", "user_group.id=user_group_member.user_group_id")
|
||||
sess.Where("user_group_member.user_id=?", query.UserId)
|
||||
sess := x.Table("team")
|
||||
sess.Join("INNER", "team_member", "team.id=team_member.team_id")
|
||||
sess.Where("team_member.user_id=?", query.UserId)
|
||||
|
||||
err := sess.Find(&query.Result)
|
||||
if err != nil {
|
||||
@ -181,23 +181,23 @@ func GetUserGroupsByUser(query *m.GetUserGroupsByUserQuery) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddUserGroupMember(cmd *m.AddUserGroupMemberCommand) error {
|
||||
func AddTeamMember(cmd *m.AddTeamMemberCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
if res, err := sess.Query("SELECT 1 from user_group_member WHERE user_group_id=? and user_id=?", cmd.UserGroupId, cmd.UserId); err != nil {
|
||||
if res, err := sess.Query("SELECT 1 from team_member WHERE team_id=? and user_id=?", cmd.TeamId, cmd.UserId); err != nil {
|
||||
return err
|
||||
} else if len(res) == 1 {
|
||||
return m.ErrUserGroupMemberAlreadyAdded
|
||||
return m.ErrTeamMemberAlreadyAdded
|
||||
}
|
||||
|
||||
if res, err := sess.Query("SELECT 1 from user_group WHERE id=?", cmd.UserGroupId); err != nil {
|
||||
if res, err := sess.Query("SELECT 1 from team WHERE id=?", cmd.TeamId); err != nil {
|
||||
return err
|
||||
} else if len(res) != 1 {
|
||||
return m.ErrUserGroupNotFound
|
||||
return m.ErrTeamNotFound
|
||||
}
|
||||
|
||||
entity := m.UserGroupMember{
|
||||
entity := m.TeamMember{
|
||||
OrgId: cmd.OrgId,
|
||||
UserGroupId: cmd.UserGroupId,
|
||||
TeamId: cmd.TeamId,
|
||||
UserId: cmd.UserId,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
@ -208,10 +208,10 @@ func AddUserGroupMember(cmd *m.AddUserGroupMemberCommand) error {
|
||||
})
|
||||
}
|
||||
|
||||
func RemoveUserGroupMember(cmd *m.RemoveUserGroupMemberCommand) error {
|
||||
func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
var rawSql = "DELETE FROM user_group_member WHERE user_group_id=? and user_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.UserGroupId, cmd.UserId)
|
||||
var rawSql = "DELETE FROM team_member WHERE team_id=? and user_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.TeamId, cmd.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -220,12 +220,12 @@ func RemoveUserGroupMember(cmd *m.RemoveUserGroupMemberCommand) error {
|
||||
})
|
||||
}
|
||||
|
||||
func GetUserGroupMembers(query *m.GetUserGroupMembersQuery) error {
|
||||
query.Result = make([]*m.UserGroupMemberDTO, 0)
|
||||
sess := x.Table("user_group_member")
|
||||
sess.Join("INNER", "user", fmt.Sprintf("user_group_member.user_id=%s.id", x.Dialect().Quote("user")))
|
||||
sess.Where("user_group_member.user_group_id=?", query.UserGroupId)
|
||||
sess.Cols("user.org_id", "user_group_member.user_group_id", "user_group_member.user_id", "user.email", "user.login")
|
||||
func GetTeamMembers(query *m.GetTeamMembersQuery) error {
|
||||
query.Result = make([]*m.TeamMemberDTO, 0)
|
||||
sess := x.Table("team_member")
|
||||
sess.Join("INNER", "user", fmt.Sprintf("team_member.user_id=%s.id", x.Dialect().Quote("user")))
|
||||
sess.Where("team_member.team_id=?", query.TeamId)
|
||||
sess.Cols("user.org_id", "team_member.team_id", "team_member.user_id", "user.email", "user.login")
|
||||
sess.Asc("user.login", "user.email")
|
||||
|
||||
err := sess.Find(&query.Result)
|
||||
|
@ -9,12 +9,12 @@ import (
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func TestUserGroupCommandsAndQueries(t *testing.T) {
|
||||
func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
|
||||
Convey("Testing User Group commands & queries", t, func() {
|
||||
Convey("Testing Team commands & queries", t, func() {
|
||||
InitTestDB(t)
|
||||
|
||||
Convey("Given saved users and two user groups", func() {
|
||||
Convey("Given saved users and two teams", func() {
|
||||
var userIds []int64
|
||||
for i := 0; i < 5; i++ {
|
||||
userCmd := &m.CreateUserCommand{
|
||||
@ -27,81 +27,81 @@ func TestUserGroupCommandsAndQueries(t *testing.T) {
|
||||
userIds = append(userIds, userCmd.Result.Id)
|
||||
}
|
||||
|
||||
group1 := m.CreateUserGroupCommand{Name: "group1 name"}
|
||||
group2 := m.CreateUserGroupCommand{Name: "group2 name"}
|
||||
group1 := m.CreateTeamCommand{Name: "group1 name"}
|
||||
group2 := m.CreateTeamCommand{Name: "group2 name"}
|
||||
|
||||
err := CreateUserGroup(&group1)
|
||||
err := CreateTeam(&group1)
|
||||
So(err, ShouldBeNil)
|
||||
err = CreateUserGroup(&group2)
|
||||
err = CreateTeam(&group2)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Should be able to create user groups and add users", func() {
|
||||
query := &m.SearchUserGroupsQuery{Name: "group1 name", Page: 1, Limit: 10}
|
||||
err = SearchUserGroups(query)
|
||||
Convey("Should be able to create teams and add users", func() {
|
||||
query := &m.SearchTeamsQuery{Name: "group1 name", Page: 1, Limit: 10}
|
||||
err = SearchTeams(query)
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Page, ShouldEqual, 1)
|
||||
|
||||
userGroup1 := query.Result.UserGroups[0]
|
||||
So(userGroup1.Name, ShouldEqual, "group1 name")
|
||||
team1 := query.Result.Teams[0]
|
||||
So(team1.Name, ShouldEqual, "group1 name")
|
||||
|
||||
err = AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: userGroup1.Id, UserId: userIds[0]})
|
||||
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: team1.Id, UserId: userIds[0]})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
q1 := &m.GetUserGroupMembersQuery{UserGroupId: userGroup1.Id}
|
||||
err = GetUserGroupMembers(q1)
|
||||
q1 := &m.GetTeamMembersQuery{TeamId: team1.Id}
|
||||
err = GetTeamMembers(q1)
|
||||
So(err, ShouldBeNil)
|
||||
So(q1.Result[0].UserGroupId, ShouldEqual, userGroup1.Id)
|
||||
So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||
So(q1.Result[0].Login, ShouldEqual, "loginuser0")
|
||||
})
|
||||
|
||||
Convey("Should be able to search for user groups", func() {
|
||||
query := &m.SearchUserGroupsQuery{Query: "group", Page: 1}
|
||||
err = SearchUserGroups(query)
|
||||
Convey("Should be able to search for teams", func() {
|
||||
query := &m.SearchTeamsQuery{Query: "group", Page: 1}
|
||||
err = SearchTeams(query)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result.UserGroups), ShouldEqual, 2)
|
||||
So(len(query.Result.Teams), ShouldEqual, 2)
|
||||
So(query.Result.TotalCount, ShouldEqual, 2)
|
||||
|
||||
query2 := &m.SearchUserGroupsQuery{Query: ""}
|
||||
err = SearchUserGroups(query2)
|
||||
query2 := &m.SearchTeamsQuery{Query: ""}
|
||||
err = SearchTeams(query2)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query2.Result.UserGroups), ShouldEqual, 2)
|
||||
So(len(query2.Result.Teams), ShouldEqual, 2)
|
||||
})
|
||||
|
||||
Convey("Should be able to return all user groups a user is member of", func() {
|
||||
Convey("Should be able to return all teams a user is member of", func() {
|
||||
groupId := group2.Result.Id
|
||||
err := AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[0]})
|
||||
err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: groupId, UserId: userIds[0]})
|
||||
|
||||
query := &m.GetUserGroupsByUserQuery{UserId: userIds[0]}
|
||||
err = GetUserGroupsByUser(query)
|
||||
query := &m.GetTeamsByUserQuery{UserId: userIds[0]}
|
||||
err = GetTeamsByUser(query)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result), ShouldEqual, 1)
|
||||
So(query.Result[0].Name, ShouldEqual, "group2 name")
|
||||
})
|
||||
|
||||
Convey("Should be able to remove users from a group", func() {
|
||||
err = RemoveUserGroupMember(&m.RemoveUserGroupMemberCommand{UserGroupId: group1.Result.Id, UserId: userIds[0]})
|
||||
err = RemoveTeamMember(&m.RemoveTeamMemberCommand{TeamId: group1.Result.Id, UserId: userIds[0]})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
q1 := &m.GetUserGroupMembersQuery{UserGroupId: group1.Result.Id}
|
||||
err = GetUserGroupMembers(q1)
|
||||
q1 := &m.GetTeamMembersQuery{TeamId: group1.Result.Id}
|
||||
err = GetTeamMembers(q1)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(q1.Result), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should be able to remove a group with users and permissions", func() {
|
||||
groupId := group2.Result.Id
|
||||
err := AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[1]})
|
||||
err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: groupId, UserId: userIds[1]})
|
||||
So(err, ShouldBeNil)
|
||||
err = AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[2]})
|
||||
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: groupId, UserId: userIds[2]})
|
||||
So(err, ShouldBeNil)
|
||||
err = SetDashboardAcl(&m.SetDashboardAclCommand{DashboardId: 1, OrgId: 1, Permission: m.PERMISSION_EDIT, UserGroupId: groupId})
|
||||
err = SetDashboardAcl(&m.SetDashboardAclCommand{DashboardId: 1, OrgId: 1, Permission: m.PERMISSION_EDIT, TeamId: groupId})
|
||||
|
||||
err = DeleteUserGroup(&m.DeleteUserGroupCommand{Id: groupId})
|
||||
err = DeleteTeam(&m.DeleteTeamCommand{Id: groupId})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
query := &m.GetUserGroupByIdQuery{Id: groupId}
|
||||
err = GetUserGroupById(query)
|
||||
So(err, ShouldEqual, m.ErrUserGroupNotFound)
|
||||
query := &m.GetTeamByIdQuery{Id: groupId}
|
||||
err = GetTeamById(query)
|
||||
So(err, ShouldEqual, m.ErrTeamNotFound)
|
||||
|
||||
permQuery := &m.GetDashboardAclInfoListQuery{DashboardId: 1, OrgId: 1}
|
||||
err = GetDashboardAclInfoList(permQuery)
|
||||
|
@ -10,9 +10,9 @@ const template = `
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
`;
|
||||
export class UserGroupPickerCtrl {
|
||||
export class TeamPickerCtrl {
|
||||
group: any;
|
||||
userGroupPicked: any;
|
||||
teamPicked: any;
|
||||
debouncedSearchGroups: any;
|
||||
|
||||
/** @ngInject */
|
||||
@ -26,34 +26,34 @@ export class UserGroupPickerCtrl {
|
||||
}
|
||||
|
||||
searchGroups(query: string) {
|
||||
return Promise.resolve(this.backendSrv.get('/api/user-groups/search?perpage=10&page=1&query=' + query).then(result => {
|
||||
return _.map(result.userGroups, ug => {
|
||||
return Promise.resolve(this.backendSrv.get('/api/teams/search?perpage=10&page=1&query=' + query).then(result => {
|
||||
return _.map(result.teams, ug => {
|
||||
return {text: ug.name, value: ug};
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
onChange(option) {
|
||||
this.userGroupPicked({$group: option.value});
|
||||
this.teamPicked({$group: option.value});
|
||||
}
|
||||
}
|
||||
|
||||
export function userGroupPicker() {
|
||||
export function teamPicker() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller: UserGroupPickerCtrl,
|
||||
controller: TeamPickerCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
userGroupPicked: '&',
|
||||
teamPicked: '&',
|
||||
},
|
||||
link: function(scope, elem, attrs, ctrl) {
|
||||
scope.$on("user-group-picker-reset", () => {
|
||||
scope.$on("team-picker-reset", () => {
|
||||
ctrl.reset();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('userGroupPicker', userGroupPicker);
|
||||
coreModule.directive('teamPicker', teamPicker);
|
||||
|
@ -47,7 +47,7 @@ import {helpModal} from './components/help/help';
|
||||
import {JsonExplorer} from './components/json_explorer/json_explorer';
|
||||
import {NavModelSrv, NavModel} from './nav_model_srv';
|
||||
import {userPicker} from './components/user_picker';
|
||||
import {userGroupPicker} from './components/user_group_picker';
|
||||
import {teamPicker} from './components/user_group_picker';
|
||||
import {geminiScrollbar} from './components/scroll/scroll';
|
||||
import {gfPageDirective} from './components/gf_page';
|
||||
import {orgSwitcher} from './components/org_switcher';
|
||||
@ -85,7 +85,7 @@ export {
|
||||
NavModelSrv,
|
||||
NavModel,
|
||||
userPicker,
|
||||
userGroupPicker,
|
||||
teamPicker,
|
||||
geminiScrollbar,
|
||||
gfPageDirective,
|
||||
orgSwitcher,
|
||||
|
@ -109,15 +109,15 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
controller : 'OrgApiKeysCtrl',
|
||||
resolve: loadOrgBundle,
|
||||
})
|
||||
.when('/org/user-groups', {
|
||||
.when('/org/teams', {
|
||||
templateUrl: 'public/app/features/org/partials/user_groups.html',
|
||||
controller : 'UserGroupsCtrl',
|
||||
controller : 'TeamsCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadOrgBundle,
|
||||
})
|
||||
.when('/org/user-groups/edit/:id', {
|
||||
.when('/org/teams/edit/:id', {
|
||||
templateUrl: 'public/app/features/org/partials/user_group_details.html',
|
||||
controller : 'UserGroupDetailsCtrl',
|
||||
controller : 'TeamDetailsCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadOrgBundle,
|
||||
})
|
||||
|
@ -52,7 +52,7 @@
|
||||
<user-picker user-picked="ctrl.userPicked($user)"></user-picker>
|
||||
</div>
|
||||
<div class="gf-form" ng-show="ctrl.newType === 'Group'">
|
||||
<user-group-picker user-group-picked="ctrl.groupPicked($group)"></user-group-picker>
|
||||
<team-picker team-picked="ctrl.groupPicked($group)"></team-picker>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -101,9 +101,9 @@
|
||||
<!-- </a> -->
|
||||
<!-- </td> -->
|
||||
<!-- </tr> -->
|
||||
<!-- <tr ng-repeat="permission in ctrl.userGroupPermissions" class="permissionlist__item"> -->
|
||||
<!-- <tr ng-repeat="permission in ctrl.teamPermissions" class="permissionlist__item"> -->
|
||||
<!-- <td><i class="fa fa-fw fa-users"></i></td> -->
|
||||
<!-- <td>{{permission.userGroup}}</td> -->
|
||||
<!-- <td>{{permission.team}}</td> -->
|
||||
<!-- <td><select class="gf-form-input gf-size-auto" ng-model="permission.permissions" ng-options="p.value as p.text for p in ctrl.permissionTypeOptions" ng-change="ctrl.updatePermission(permission)"></select></td> -->
|
||||
<!-- <td class="text-right"> -->
|
||||
<!-- <a ng-click="ctrl.removePermission(permission)" class="btn btn-danger btn-small"> -->
|
||||
|
@ -58,10 +58,10 @@ export class AclCtrl {
|
||||
item.nameHtml = this.$sce.trustAsHtml(item.userLogin);
|
||||
item.sortName = item.userLogin;
|
||||
item.sortRank = 10;
|
||||
} else if (item.userGroupId > 0) {
|
||||
} else if (item.teamId > 0) {
|
||||
item.icon = "fa fa-fw fa-users";
|
||||
item.nameHtml = this.$sce.trustAsHtml(item.userGroup);
|
||||
item.sortName = item.userGroup;
|
||||
item.nameHtml = this.$sce.trustAsHtml(item.team);
|
||||
item.sortName = item.team;
|
||||
item.sortRank = 20;
|
||||
} else if (item.role) {
|
||||
item.icon = "fa fa-fw fa-street-view";
|
||||
@ -89,7 +89,7 @@ export class AclCtrl {
|
||||
updated.push({
|
||||
id: item.id,
|
||||
userId: item.userId,
|
||||
userGroupId: item.userGroupId,
|
||||
teamId: item.teamId,
|
||||
role: item.role,
|
||||
permission: item.permission,
|
||||
});
|
||||
@ -144,7 +144,7 @@ export class AclCtrl {
|
||||
|
||||
return (origItem.role && newItem.role && origItem.role === newItem.role) ||
|
||||
(origItem.userId && newItem.userId && origItem.userId === newItem.userId) ||
|
||||
(origItem.userGroupId && newItem.userGroupId && origItem.userGroupId === newItem.userGroupId);
|
||||
(origItem.teamId && newItem.teamId && origItem.teamId === newItem.teamId);
|
||||
}
|
||||
|
||||
userPicked(user) {
|
||||
@ -153,8 +153,8 @@ export class AclCtrl {
|
||||
}
|
||||
|
||||
groupPicked(group) {
|
||||
this.addNewItem({userGroupId: group.id, userGroup: group.name, permission: 1});
|
||||
this.$scope.$broadcast('user-group-picker-reset');
|
||||
this.addNewItem({teamId: group.id, team: group.name, permission: 1});
|
||||
this.$scope.$broadcast('team-picker-reset');
|
||||
}
|
||||
|
||||
removeItem(index) {
|
||||
@ -179,7 +179,7 @@ export function dashAclModal() {
|
||||
export interface FormModel {
|
||||
dashboardId: number;
|
||||
userId?: number;
|
||||
userGroupId?: number;
|
||||
teamId?: number;
|
||||
PermissionType: number;
|
||||
}
|
||||
|
||||
@ -189,8 +189,8 @@ export interface DashboardAcl {
|
||||
userId?: number;
|
||||
userLogin?: string;
|
||||
userEmail?: string;
|
||||
userGroupId?: number;
|
||||
userGroup?: string;
|
||||
teamId?: number;
|
||||
team?: string;
|
||||
permission?: number;
|
||||
permissionName?: string;
|
||||
role?: string;
|
||||
|
@ -40,12 +40,12 @@ describe('AclCtrl', () => {
|
||||
|
||||
ctx.ctrl.userPicked(userItem);
|
||||
|
||||
const userGroupItem = {
|
||||
const teamItem = {
|
||||
id: 2,
|
||||
name: 'ug1',
|
||||
};
|
||||
|
||||
ctx.ctrl.groupPicked(userGroupItem);
|
||||
ctx.ctrl.groupPicked(teamItem);
|
||||
|
||||
ctx.ctrl.newType = 'Editor';
|
||||
ctx.ctrl.typeChanged();
|
||||
@ -57,7 +57,7 @@ describe('AclCtrl', () => {
|
||||
it('should sort the result by role, user group and user', () => {
|
||||
expect(ctx.ctrl.items[0].role).to.eql('Viewer');
|
||||
expect(ctx.ctrl.items[1].role).to.eql('Editor');
|
||||
expect(ctx.ctrl.items[2].userGroupId).to.eql(2);
|
||||
expect(ctx.ctrl.items[2].teamId).to.eql(2);
|
||||
expect(ctx.ctrl.items[3].userId).to.eql(2);
|
||||
});
|
||||
|
||||
@ -71,7 +71,7 @@ describe('AclCtrl', () => {
|
||||
expect(backendSrv.post.getCall(0).args[1].items[0].permission).to.eql(1);
|
||||
expect(backendSrv.post.getCall(0).args[1].items[1].role).to.eql('Editor');
|
||||
expect(backendSrv.post.getCall(0).args[1].items[1].permission).to.eql(1);
|
||||
expect(backendSrv.post.getCall(0).args[1].items[2].userGroupId).to.eql(2);
|
||||
expect(backendSrv.post.getCall(0).args[1].items[2].teamId).to.eql(2);
|
||||
expect(backendSrv.post.getCall(0).args[1].items[2].permission).to.eql(1);
|
||||
expect(backendSrv.post.getCall(0).args[1].items[3].userId).to.eql(2);
|
||||
expect(backendSrv.post.getCall(0).args[1].items[3].permission).to.eql(1);
|
||||
@ -130,13 +130,13 @@ describe('AclCtrl', () => {
|
||||
backendSrv.post.reset();
|
||||
ctx.ctrl.items = [];
|
||||
|
||||
const userGroupItem = {
|
||||
const teamItem = {
|
||||
id: 2,
|
||||
name: 'ug1',
|
||||
};
|
||||
|
||||
ctx.ctrl.groupPicked(userGroupItem);
|
||||
ctx.ctrl.groupPicked(userGroupItem);
|
||||
ctx.ctrl.groupPicked(teamItem);
|
||||
ctx.ctrl.groupPicked(teamItem);
|
||||
});
|
||||
|
||||
it('should throw a validation error', () => {
|
||||
@ -154,19 +154,19 @@ describe('AclCtrl', () => {
|
||||
backendSrv.post.reset();
|
||||
ctx.ctrl.items = [];
|
||||
|
||||
const inheritedUserGroupItem = {
|
||||
const inheritedTeamItem = {
|
||||
id: 2,
|
||||
name: 'ug1',
|
||||
dashboardId: -1
|
||||
};
|
||||
|
||||
ctx.ctrl.items.push(inheritedUserGroupItem);
|
||||
ctx.ctrl.items.push(inheritedTeamItem);
|
||||
|
||||
const userGroupItem = {
|
||||
const teamItem = {
|
||||
id: 2,
|
||||
name: 'ug1',
|
||||
};
|
||||
ctx.ctrl.groupPicked(userGroupItem);
|
||||
ctx.ctrl.groupPicked(teamItem);
|
||||
});
|
||||
|
||||
it('should not throw a validation error', () => {
|
||||
|
@ -6,6 +6,8 @@ import './change_password_ctrl';
|
||||
import './new_org_ctrl';
|
||||
import './user_invite_ctrl';
|
||||
import './user_groups_ctrl';
|
||||
import './user_group_details_ctrl';
|
||||
import './create_user_group_modal';
|
||||
import './org_api_keys_ctrl';
|
||||
import './org_details_ctrl';
|
||||
import './prefs_control';
|
||||
|
@ -3,17 +3,17 @@
|
||||
import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
export class CreateUserGroupCtrl {
|
||||
userGroupName = '';
|
||||
export class CreateTeamCtrl {
|
||||
teamName = '';
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private backendSrv, private $location) {
|
||||
}
|
||||
|
||||
createUserGroup() {
|
||||
this.backendSrv.post('/api/user-groups', {name: this.userGroupName}).then((result) => {
|
||||
if (result.userGroupId) {
|
||||
this.$location.path('/org/user-groups/edit/' + result.userGroupId);
|
||||
createTeam() {
|
||||
this.backendSrv.post('/api/teams', {name: this.teamName}).then((result) => {
|
||||
if (result.teamId) {
|
||||
this.$location.path('/org/teams/edit/' + result.teamId);
|
||||
}
|
||||
this.dismiss();
|
||||
});
|
||||
@ -24,14 +24,14 @@ export class CreateUserGroupCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
export function createUserGroupModal() {
|
||||
export function createTeamModal() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/features/org/partials/create_user_group.html',
|
||||
controller: CreateUserGroupCtrl,
|
||||
controller: CreateTeamCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('createUserGroupModal', createUserGroupModal);
|
||||
coreModule.directive('createTeamModal', createTeamModal);
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="modal-body">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-header-title">
|
||||
<span class="p-l-1">Create User Group</span>
|
||||
<span class="p-l-1">Create Team</span>
|
||||
</h2>
|
||||
|
||||
<a class="modal-header-close" ng-click="ctrl.dismiss();">
|
||||
@ -10,14 +10,14 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-content">
|
||||
<form name="ctrl.createUserGroupForm" class="gf-form-group" novalidate>
|
||||
<form name="ctrl.createTeamForm" class="gf-form-group" novalidate>
|
||||
<div class="p-t-2">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-21">
|
||||
<input type="text" class="gf-form-input" ng-model='ctrl.userGroupName' required give-focus="true" placeholder="Enter User Group Name"></input>
|
||||
<input type="text" class="gf-form-input" ng-model='ctrl.teamName' required give-focus="true" placeholder="Enter User Group Name"></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<button class="btn gf-form-btn btn-success" ng-click="ctrl.createUserGroup();ctrl.dismiss();">Create</button>
|
||||
<button class="btn gf-form-btn btn-success" ng-click="ctrl.createTeam();ctrl.dismiss();">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,10 +5,10 @@
|
||||
<h1>Edit User Group</h1>
|
||||
</div>
|
||||
|
||||
<form name="userGroupDetailsForm" class="gf-form-group gf-form-inline">
|
||||
<form name="teamDetailsForm" class="gf-form-group gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">Name</span>
|
||||
<input type="text" required ng-model="ctrl.userGroup.name" class="gf-form-input max-width-14" >
|
||||
<input type="text" required ng-model="ctrl.team.name" class="gf-form-input max-width-14" >
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
@ -26,24 +26,24 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="grafana-options-table" ng-show="ctrl.userGroupMembers.length > 0">
|
||||
<table class="grafana-options-table" ng-show="ctrl.teamMembers.length > 0">
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Email</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr ng-repeat="member in ctrl.userGroupMembers">
|
||||
<tr ng-repeat="member in ctrl.teamMembers">
|
||||
<td>{{member.login}}</td>
|
||||
<td>{{member.email}}</td>
|
||||
<td style="width: 1%">
|
||||
<a ng-click="ctrl.removeUserGroupMember(member)" class="btn btn-danger btn-mini">
|
||||
<a ng-click="ctrl.removeTeamMember(member)" class="btn btn-danger btn-mini">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div>
|
||||
<em class="muted" ng-hide="ctrl.userGroupMembers.length > 0">
|
||||
<em class="muted" ng-hide="ctrl.teamMembers.length > 0">
|
||||
This user group has no members yet.
|
||||
</em>
|
||||
</div>
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
<div class="page-action-bar__spacer"></div>
|
||||
|
||||
<a class="btn btn-success" ng-click="ctrl.openUserGroupModal()">
|
||||
<a class="btn btn-success" ng-click="ctrl.openTeamModal()">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add Team
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="admin-list-table">
|
||||
<table class="filter-table form-inline" ng-show="ctrl.userGroups.length > 0">
|
||||
<table class="filter-table form-inline" ng-show="ctrl.teams.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
@ -27,18 +27,18 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="userGroup in ctrl.userGroups">
|
||||
<tr ng-repeat="team in ctrl.teams">
|
||||
<td>
|
||||
<a href="org/user-groups/edit/{{userGroup.id}}">{{userGroup.name}}</a>
|
||||
<a href="org/teams/edit/{{team.id}}">{{team.name}}</a>
|
||||
</td>
|
||||
<td>#Count</td>
|
||||
<td class="text-right">
|
||||
<a href="org/user-groups/edit/{{userGroup.id}}" class="btn btn-inverse btn-small">
|
||||
<a href="org/teams/edit/{{team.id}}" class="btn btn-inverse btn-small">
|
||||
<i class="fa fa-edit"></i>
|
||||
Edit
|
||||
</a>
|
||||
|
||||
<a ng-click="ctrl.deleteUserGroup(userGroup)" class="btn btn-danger btn-small">
|
||||
<a ng-click="ctrl.deleteTeam(team)" class="btn btn-danger btn-small">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</td>
|
||||
@ -58,7 +58,7 @@
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<em class="muted" ng-hide="ctrl.userGroups.length > 0">
|
||||
<em class="muted" ng-hide="ctrl.teams.length > 0">
|
||||
No User Groups found.
|
||||
</em>
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import '../user_group_details_ctrl';
|
||||
import {describe, beforeEach, it, expect, sinon, angularMocks} from 'test/lib/common';
|
||||
import UserGroupDetailsCtrl from '../user_group_details_ctrl';
|
||||
import TeamDetailsCtrl from '../user_group_details_ctrl';
|
||||
|
||||
describe('UserGroupDetailsCtrl', () => {
|
||||
describe('TeamDetailsCtrl', () => {
|
||||
var ctx: any = {};
|
||||
var backendSrv = {
|
||||
searchUsers: sinon.stub().returns(Promise.resolve([])),
|
||||
@ -16,7 +16,7 @@ var backendSrv = {
|
||||
beforeEach(angularMocks.inject(($rootScope, $controller, $q) => {
|
||||
ctx.$q = $q;
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.ctrl = $controller(UserGroupDetailsCtrl, {
|
||||
ctx.ctrl = $controller(TeamDetailsCtrl, {
|
||||
$scope: ctx.scope,
|
||||
backendSrv: backendSrv,
|
||||
$routeParams: {id: 1},
|
||||
@ -34,13 +34,13 @@ var backendSrv = {
|
||||
});
|
||||
|
||||
it('should parse the result and save to db', () => {
|
||||
expect(backendSrv.post.getCall(0).args[0]).to.eql('/api/user-groups/1/members');
|
||||
expect(backendSrv.post.getCall(0).args[0]).to.eql('/api/teams/1/members');
|
||||
expect(backendSrv.post.getCall(0).args[1].userId).to.eql(2);
|
||||
});
|
||||
|
||||
it('should refresh the list after saving.', () => {
|
||||
expect(backendSrv.get.getCall(0).args[0]).to.eql('/api/user-groups/1');
|
||||
expect(backendSrv.get.getCall(1).args[0]).to.eql('/api/user-groups/1/members');
|
||||
expect(backendSrv.get.getCall(0).args[0]).to.eql('/api/teams/1');
|
||||
expect(backendSrv.get.getCall(1).args[0]).to.eql('/api/teams/1/members');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
export default class UserGroupDetailsCtrl {
|
||||
userGroup: UserGroup;
|
||||
userGroupMembers: User[] = [];
|
||||
export default class TeamDetailsCtrl {
|
||||
team: Team;
|
||||
teamMembers: User[] = [];
|
||||
navModel: any;
|
||||
|
||||
constructor(private $scope, private backendSrv, private $routeParams, navModelSrv) {
|
||||
@ -14,49 +14,49 @@ export default class UserGroupDetailsCtrl {
|
||||
|
||||
get() {
|
||||
if (this.$routeParams && this.$routeParams.id) {
|
||||
this.backendSrv.get(`/api/user-groups/${this.$routeParams.id}`)
|
||||
this.backendSrv.get(`/api/teams/${this.$routeParams.id}`)
|
||||
.then(result => {
|
||||
this.userGroup = result;
|
||||
this.team = result;
|
||||
});
|
||||
this.backendSrv.get(`/api/user-groups/${this.$routeParams.id}/members`)
|
||||
this.backendSrv.get(`/api/teams/${this.$routeParams.id}/members`)
|
||||
.then(result => {
|
||||
this.userGroupMembers = result;
|
||||
this.teamMembers = result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
removeUserGroupMember(userGroupMember: UserGroupMember) {
|
||||
removeTeamMember(teamMember: TeamMember) {
|
||||
this.$scope.appEvent('confirm-modal', {
|
||||
title: 'Remove Member',
|
||||
text: 'Are you sure you want to remove ' + userGroupMember.name + ' from this group?',
|
||||
text: 'Are you sure you want to remove ' + teamMember.name + ' from this group?',
|
||||
yesText: "Remove",
|
||||
icon: "fa-warning",
|
||||
onConfirm: () => {
|
||||
this.removeMemberConfirmed(userGroupMember);
|
||||
this.removeMemberConfirmed(teamMember);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeMemberConfirmed(userGroupMember: UserGroupMember) {
|
||||
this.backendSrv.delete(`/api/user-groups/${this.$routeParams.id}/members/${userGroupMember.userId}`)
|
||||
removeMemberConfirmed(teamMember: TeamMember) {
|
||||
this.backendSrv.delete(`/api/teams/${this.$routeParams.id}/members/${teamMember.userId}`)
|
||||
.then(this.get.bind(this));
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this.$scope.userGroupDetailsForm.$valid) { return; }
|
||||
if (!this.$scope.teamDetailsForm.$valid) { return; }
|
||||
|
||||
this.backendSrv.put('/api/user-groups/' + this.userGroup.id, {name: this.userGroup.name});
|
||||
this.backendSrv.put('/api/teams/' + this.team.id, {name: this.team.name});
|
||||
}
|
||||
|
||||
userPicked(user) {
|
||||
this.backendSrv.post(`/api/user-groups/${this.$routeParams.id}/members`, {userId: user.id}).then(() => {
|
||||
this.backendSrv.post(`/api/teams/${this.$routeParams.id}/members`, {userId: user.id}).then(() => {
|
||||
this.$scope.$broadcast('user-picker-reset');
|
||||
this.get();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface UserGroup {
|
||||
export interface Team {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
@ -68,10 +68,10 @@ export interface User {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface UserGroupMember {
|
||||
export interface TeamMember {
|
||||
userId: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
coreModule.controller('UserGroupDetailsCtrl', UserGroupDetailsCtrl);
|
||||
coreModule.controller('TeamDetailsCtrl', TeamDetailsCtrl);
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
import coreModule from 'app/core/core_module';
|
||||
import {appEvents} from 'app/core/core';
|
||||
|
||||
export class UserGroupsCtrl {
|
||||
userGroups: any;
|
||||
export class TeamsCtrl {
|
||||
teams: any;
|
||||
pages = [];
|
||||
perPage = 50;
|
||||
page = 1;
|
||||
@ -20,9 +20,9 @@ export class UserGroupsCtrl {
|
||||
}
|
||||
|
||||
get() {
|
||||
this.backendSrv.get(`/api/user-groups/search?perpage=${this.perPage}&page=${this.page}&query=${this.query}`)
|
||||
this.backendSrv.get(`/api/teams/search?perpage=${this.perPage}&page=${this.page}&query=${this.query}`)
|
||||
.then((result) => {
|
||||
this.userGroups = result.userGroups;
|
||||
this.teams = result.teams;
|
||||
this.page = result.page;
|
||||
this.perPage = result.perPage;
|
||||
this.totalPages = Math.ceil(result.totalCount / result.perPage);
|
||||
@ -40,29 +40,29 @@ export class UserGroupsCtrl {
|
||||
this.get();
|
||||
}
|
||||
|
||||
deleteUserGroup(userGroup) {
|
||||
deleteTeam(team) {
|
||||
appEvents.emit('confirm-modal', {
|
||||
title: 'Delete',
|
||||
text: 'Are you sure you want to delete User Group ' + userGroup.name + '?',
|
||||
text: 'Are you sure you want to delete User Group ' + team.name + '?',
|
||||
yesText: "Delete",
|
||||
icon: "fa-warning",
|
||||
onConfirm: () => {
|
||||
this.deleteUserGroupConfirmed(userGroup);
|
||||
this.deleteTeamConfirmed(team);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteUserGroupConfirmed(userGroup) {
|
||||
this.backendSrv.delete('/api/user-groups/' + userGroup.id)
|
||||
deleteTeamConfirmed(team) {
|
||||
this.backendSrv.delete('/api/teams/' + team.id)
|
||||
.then(this.get.bind(this));
|
||||
}
|
||||
|
||||
openUserGroupModal() {
|
||||
openTeamModal() {
|
||||
appEvents.emit('show-modal', {
|
||||
templateHtml: '<create-user-group-modal></create-user-group-modal>',
|
||||
templateHtml: '<create-team-modal></create-team-modal>',
|
||||
modalClass: 'modal--narrow'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
coreModule.controller('UserGroupsCtrl', UserGroupsCtrl);
|
||||
coreModule.controller('TeamsCtrl', TeamsCtrl);
|
||||
|
@ -13,14 +13,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="card-item-wrapper" ng-repeat="permission in ctrl.userGroupPermissions">
|
||||
<li class="card-item-wrapper" ng-repeat="permission in ctrl.teamPermissions">
|
||||
<div class="card-item card-item--alert">
|
||||
<div class="card-item-header">
|
||||
<div class="card-item-sub-name">{{permission.permissionName}}</div>
|
||||
</div>
|
||||
<div class="card-item-body">
|
||||
<div class="card-item-details">
|
||||
<div class="card-item-notice">{{permission.userGroup}}</div>
|
||||
<div class="card-item-notice">{{permission.team}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,7 +7,7 @@ class PermissionListCtrl extends PanelCtrl {
|
||||
static templateUrl = 'module.html';
|
||||
|
||||
userPermissions: any[];
|
||||
userGroupPermissions: any[];
|
||||
teamPermissions: any[];
|
||||
roles: any[];
|
||||
|
||||
panelDefaults = {
|
||||
@ -48,7 +48,7 @@ class PermissionListCtrl extends PanelCtrl {
|
||||
return this.backendSrv.get(`/api/dashboards/id/${this.panel.folderId}/acl`)
|
||||
.then(result => {
|
||||
this.userPermissions = _.filter(result, p => { return p.userId > 0;});
|
||||
this.userGroupPermissions = _.filter(result, p => { return p.userGroupId > 0;});
|
||||
this.teamPermissions = _.filter(result, p => { return p.teamId > 0;});
|
||||
// this.roles = this.setRoles(result);
|
||||
});
|
||||
}
|
||||
|
@ -51,7 +51,7 @@
|
||||
background-image: url('../img/icons_#{$theme-name}_theme/icon_notification_channels.svg');
|
||||
}
|
||||
|
||||
.gicon-user-group {
|
||||
.gicon-team {
|
||||
background-image: url('../img/icons_#{$theme-name}_theme/icon_user_group.svg');
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user