Integration permission management changes (#34421)

* Support for permissions allowing end users to create and manage their own integrations if sysadmin deems necessary

* Adjustments based on new understanding

* remove extra functions now that we've consolidated

* Fix webapp i18n

* Update snapshots

* Fix test

* Fix some tests, refactor some more, and add a few extra

* fix linter

* Update snapshots

* Fix test

* Missed some cleanup

* Fix e2e

* Fi

* Fix

* Fixes from PR feedback

* Update snapshots

* Fix tests

* Fix slash command list endpoint per PR feedback. Remove changes around OAuth Apps

* Further reversions of oauth stuff

* Update tests

* Small changes to fix when customOnly=false

* Remove extra perm from cypress

* Fixes from Eva's feedback

* Fix i18n

* More fixing

* More fixing
This commit is contained in:
Nick Misasi 2025-11-13 06:12:30 -05:00 committed by GitHub
parent ef16fcfad2
commit 91dfcbbdd1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 1937 additions and 272 deletions

File diff suppressed because one or more lines are too long

View file

@ -40,12 +40,28 @@ func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageSlashCommands) {
c.SetPermissionError(model.PermissionManageSlashCommands)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageOwnSlashCommands) {
c.SetPermissionError(model.PermissionManageOwnSlashCommands)
return
}
cmd.CreatorId = c.AppContext.Session().UserId
userId := c.AppContext.Session().UserId
if cmd.CreatorId != "" && cmd.CreatorId != userId {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageOthersSlashCommands) {
c.LogAudit("fail - inappropriate permissions")
c.SetPermissionError(model.PermissionManageOthersSlashCommands)
return
}
if _, err := c.App.GetUser(cmd.CreatorId); err != nil {
c.Err = err
return
}
userId = cmd.CreatorId
}
cmd.CreatorId = userId
rcmd, err := c.App.CreateCommand(&cmd)
if err != nil {
@ -94,7 +110,7 @@ func updateCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), oldCmd.TeamId, model.PermissionManageSlashCommands) {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), oldCmd.TeamId, model.PermissionManageOwnSlashCommands) {
c.LogAudit("fail - inappropriate permissions")
// here we return Not_found instead of a permissions error so we don't leak the existence of
// a command to someone without permissions for the team it belongs to.
@ -148,9 +164,9 @@ func moveCommand(c *Context, w http.ResponseWriter, r *http.Request) {
}
model.AddEventParameterAuditableToAuditRec(auditRec, "team", newTeam)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), newTeam.Id, model.PermissionManageSlashCommands) {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), newTeam.Id, model.PermissionManageOwnSlashCommands) {
c.LogAudit("fail - inappropriate permissions")
c.SetPermissionError(model.PermissionManageSlashCommands)
c.SetPermissionError(model.PermissionManageOwnSlashCommands)
return
}
@ -161,7 +177,7 @@ func moveCommand(c *Context, w http.ResponseWriter, r *http.Request) {
}
auditRec.AddEventPriorState(cmd)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageSlashCommands) {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageOwnSlashCommands) {
c.LogAudit("fail - inappropriate permissions")
// here we return Not_found instead of a permissions error so we don't leak the existence of
// a command to someone without permissions for the team it belongs to.
@ -169,6 +185,20 @@ func moveCommand(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if c.AppContext.Session().UserId != cmd.CreatorId && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageOthersSlashCommands) {
c.LogAudit("fail - inappropriate permissions")
c.SetPermissionError(model.PermissionManageOthersSlashCommands)
return
}
// Verify that the command creator has permission to the new team
// This prevents moving a command to a team where its creator doesn't have access
if !c.App.HasPermissionToTeam(c.AppContext, cmd.CreatorId, newTeam.Id, model.PermissionManageOwnSlashCommands) {
c.LogAudit("fail - command creator does not have permission to new team")
c.Err = model.NewAppError("moveCommand", "api.command.move_command.creator_no_permission.app_error", nil, "creator_id="+cmd.CreatorId+" team_id="+newTeam.Id, http.StatusBadRequest)
return
}
if appErr = c.App.MoveCommand(newTeam, cmd); appErr != nil {
c.Err = appErr
return
@ -200,7 +230,7 @@ func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
}
auditRec.AddEventPriorState(cmd)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageSlashCommands) {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageOwnSlashCommands) {
c.LogAudit("fail - inappropriate permissions")
// here we return Not_found instead of a permissions error so we don't leak the existence of
// a command to someone without permissions for the team it belongs to.
@ -244,25 +274,38 @@ func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
var commands []*model.Command
var err *model.AppError
if customOnly {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageSlashCommands) {
c.SetPermissionError(model.PermissionManageSlashCommands)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageOwnSlashCommands) {
c.SetPermissionError(model.PermissionManageOwnSlashCommands)
return
}
commands, err = c.App.ListTeamCommands(teamId)
// Filter to only commands the user can manage
userIdFilter := c.AppContext.Session().UserId
if c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageOthersSlashCommands) {
userIdFilter = "" // Empty means return all commands
}
commands, err = c.App.ListTeamCommandsByUser(teamId, userIdFilter)
if err != nil {
c.Err = err
return
}
} else {
//User with no permission should see only system commands
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageSlashCommands) {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageOwnSlashCommands) {
commands, err = c.App.ListAutocompleteCommands(teamId, c.AppContext.T)
if err != nil {
c.Err = err
return
}
} else {
commands, err = c.App.ListAllCommands(teamId, c.AppContext.T)
// Filter custom commands to only those the user can manage
userIdFilter := c.AppContext.Session().UserId
if c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageOthersSlashCommands) {
userIdFilter = "" // Empty means return all commands
}
commands, err = c.App.ListAllCommandsByUser(teamId, userIdFilter, c.AppContext.T)
if err != nil {
c.Err = err
return
@ -296,11 +339,16 @@ func getCommand(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetCommandNotFoundError()
return
}
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageSlashCommands) {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageOwnSlashCommands) {
// again, return not_found to ensure id existence does not leak.
c.SetCommandNotFoundError()
return
}
if c.AppContext.Session().UserId != cmd.CreatorId && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageOthersSlashCommands) {
c.SetCommandNotFoundError()
return
}
if err := json.NewEncoder(w).Encode(cmd); err != nil {
c.Logger.Warn("Error while writing response", mlog.Err(err))
}
@ -473,7 +521,7 @@ func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddEventPriorState(cmd)
model.AddEventParameterToAuditRec(auditRec, "command_id", c.Params.CommandId)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageSlashCommands) {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), cmd.TeamId, model.PermissionManageOwnSlashCommands) {
c.LogAudit("fail - inappropriate permissions")
// here we return Not_found instead of a permissions error so we don't leak the existence of
// a command to someone without permissions for the team it belongs to.

View file

@ -32,11 +32,10 @@ func TestCreateCommand(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
newCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger",
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger",
}
_, resp, err := client.CreateCommand(context.Background(), newCmd)
@ -55,6 +54,7 @@ func TestCreateCommand(t *testing.T) {
CheckErrorID(t, err, "api.command.duplicate_trigger.app_error")
newCmd.Trigger = "Local"
newCmd.CreatorId = th.BasicUser.Id
localCreatedCmd, resp, err := LocalClient.CreateCommand(context.Background(), newCmd)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
@ -82,6 +82,87 @@ func TestCreateCommand(t *testing.T) {
CheckErrorID(t, err, "api.command.disabled.app_error")
}
func TestCreateCommandForOtherUser(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
enableCommands := *th.App.Config().ServiceSettings.EnableCommands
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableCommands = &enableCommands })
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
// Give BasicUser permission to manage their own commands
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
t.Run("UserWithOnlyManageOwnCannotCreateForOthers", func(t *testing.T) {
cmdForOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_for_other_fail",
}
_, resp, err := th.Client.CreateCommand(context.Background(), cmdForOther)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("UserWithManageOthersCanCreateForOthers", func(t *testing.T) {
// Give BasicUser permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
cmdForOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_for_other_success",
}
createdCmd, _, err := th.Client.CreateCommand(context.Background(), cmdForOther)
require.NoError(t, err)
require.Equal(t, th.BasicUser2.Id, createdCmd.CreatorId, "command should be owned by BasicUser2")
require.Equal(t, th.BasicTeam.Id, createdCmd.TeamId)
})
t.Run("UserWithManageOthersCannotCreateForNonExistentUser", func(t *testing.T) {
// Give BasicUser permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
cmdForInvalidUser := &model.Command{
CreatorId: model.NewId(), // Non-existent user ID
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_invalid_user",
}
_, resp, err := th.Client.CreateCommand(context.Background(), cmdForInvalidUser)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("SystemAdminCanCreateForOthers", func(t *testing.T) {
cmdForOther := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_admin_for_other",
}
createdCmd, _, err := th.SystemAdminClient.CreateCommand(context.Background(), cmdForOther)
require.NoError(t, err)
require.Equal(t, th.BasicUser.Id, createdCmd.CreatorId, "command should be owned by BasicUser")
require.Equal(t, th.BasicTeam.Id, createdCmd.TeamId)
})
}
func TestUpdateCommand(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
@ -160,6 +241,90 @@ func TestUpdateCommand(t *testing.T) {
_, resp, err := th.SystemAdminClient.UpdateCommand(context.Background(), cmd2)
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
// Permission tests
th.LoginBasic(t)
// Give BasicUser permission to manage their own commands
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
t.Run("UserCanUpdateTheirOwnCommand", func(t *testing.T) {
// Create a command owned by BasicUser
cmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own",
}
createdCmd, _ := th.App.CreateCommand(cmd)
// Update the command
createdCmd.URL = "http://newurl.com"
updatedCmd, _, err := th.Client.UpdateCommand(context.Background(), createdCmd)
require.NoError(t, err)
require.Equal(t, "http://newurl.com", updatedCmd.URL)
})
t.Run("UserWithoutManageOthersCannotUpdateOthersCommand", func(t *testing.T) {
// Create a command owned by BasicUser2
cmd := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other",
}
createdCmd, _ := th.App.CreateCommand(cmd)
// Try to update the command
createdCmd.URL = "http://newurl.com"
_, resp, err := th.Client.UpdateCommand(context.Background(), createdCmd)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("UserWithManageOthersCanUpdateOthersCommand", func(t *testing.T) {
// Give BasicUser permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
// Create a command owned by BasicUser2
cmd := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other2",
}
createdCmd, _ := th.App.CreateCommand(cmd)
// Update the command
createdCmd.URL = "http://newurl.com"
updatedCmd, _, err := th.Client.UpdateCommand(context.Background(), createdCmd)
require.NoError(t, err)
require.Equal(t, "http://newurl.com", updatedCmd.URL)
})
t.Run("UserWithOnlyManageOwnCannotUpdateOthersCommand", func(t *testing.T) {
// BasicUser should only have ManageOwn permission (already set up in the test)
// Create a command owned by BasicUser2
cmd := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other3",
}
createdCmd, _ := th.App.CreateCommand(cmd)
// Try to update the command
createdCmd.URL = "http://newurl.com"
_, resp, err := th.Client.UpdateCommand(context.Background(), createdCmd)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
}
func TestMoveCommand(t *testing.T) {
@ -219,6 +384,133 @@ func TestMoveCommand(t *testing.T) {
resp, err = th.SystemAdminClient.MoveCommand(context.Background(), newTeam.Id, rcmd2.Id)
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
// Set up for permission tests
th.LoginBasic(t)
th.LinkUserToTeam(t, th.BasicUser, newTeam)
th.LinkUserToTeam(t, th.BasicUser2, newTeam)
// Give BasicUser permission to manage their own commands on both teams
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
t.Run("UserWithoutManageOthersPermissionCannotMoveOthersCommand", func(t *testing.T) {
// Create a command owned by BasicUser2
cmd := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger3",
}
rcmd, _ := th.App.CreateCommand(cmd)
// BasicUser should not be able to move BasicUser2's command
resp, err := th.Client.MoveCommand(context.Background(), newTeam.Id, rcmd.Id)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
// Verify the command was not moved
movedCmd, _ := th.App.GetCommand(rcmd.Id)
require.Equal(t, team.Id, movedCmd.TeamId)
})
t.Run("UserWithManageOthersPermissionCanMoveOthersCommand", func(t *testing.T) {
// Create a command owned by BasicUser2
cmd := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger4",
}
rcmd, _ := th.App.CreateCommand(cmd)
// Give BasicUser the permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
// Now BasicUser should be able to move BasicUser2's command
_, err := th.Client.MoveCommand(context.Background(), newTeam.Id, rcmd.Id)
require.NoError(t, err)
// Verify the command was moved
movedCmd, _ := th.App.GetCommand(rcmd.Id)
require.Equal(t, newTeam.Id, movedCmd.TeamId)
})
t.Run("CreatorCanMoveTheirOwnCommand", func(t *testing.T) {
// Create a command owned by BasicUser
cmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger5",
}
rcmd, _ := th.App.CreateCommand(cmd)
// BasicUser should be able to move their own command
_, err := th.Client.MoveCommand(context.Background(), newTeam.Id, rcmd.Id)
require.NoError(t, err)
// Verify the command was moved
movedCmd, _ := th.App.GetCommand(rcmd.Id)
require.Equal(t, newTeam.Id, movedCmd.TeamId)
})
t.Run("UserWithOnlyManageOwnCannotMoveOthersCommand", func(t *testing.T) {
// BasicUser should only have ManageOwn permission (already set up in the test)
// Create a command owned by BasicUser2
cmd := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger6",
}
rcmd, _ := th.App.CreateCommand(cmd)
// BasicUser should not be able to move BasicUser2's command
resp, err := th.Client.MoveCommand(context.Background(), newTeam.Id, rcmd.Id)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
// Verify the command was not moved
notMovedCmd, _ := th.App.GetCommand(rcmd.Id)
require.Equal(t, team.Id, notMovedCmd.TeamId)
})
t.Run("CannotMoveCommandWhenCreatorHasNoPermissionToNewTeam", func(t *testing.T) {
// Create a third team that the command creator (BasicUser2) is NOT a member of
thirdTeam := th.CreateTeam(t)
th.LinkUserToTeam(t, th.BasicUser, thirdTeam)
// Give BasicUser permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
// Create a command owned by BasicUser2
// Note: BasicUser2 is NOT a member of thirdTeam (only member of team and newTeam)
cmd := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger7",
}
rcmd, _ := th.App.CreateCommand(cmd)
// BasicUser attempts to move BasicUser2's command to thirdTeam
// This should fail because BasicUser2 doesn't have permission to thirdTeam
resp, err := th.Client.MoveCommand(context.Background(), thirdTeam.Id, rcmd.Id)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
// Verify the command was not moved
notMovedCmd, _ := th.App.GetCommand(rcmd.Id)
require.Equal(t, team.Id, notMovedCmd.TeamId)
})
}
func TestDeleteCommand(t *testing.T) {
@ -278,6 +570,94 @@ func TestDeleteCommand(t *testing.T) {
resp, err = th.SystemAdminClient.DeleteCommand(context.Background(), rcmd2.Id)
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
// Permission tests for ManageOwn vs ManageOthers
th.LoginBasic(t)
// Give BasicUser permission to manage their own commands
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
t.Run("UserWithManageOwnCanDeleteOnlyOwnCommand", func(t *testing.T) {
// Create a command owned by BasicUser
cmdOwn := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own_delete",
}
createdCmdOwn, _ := th.App.CreateCommand(cmdOwn)
// Create a command owned by BasicUser2
cmdOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other_delete",
}
createdCmdOther, _ := th.App.CreateCommand(cmdOther)
// Should be able to delete own command
_, err := th.Client.DeleteCommand(context.Background(), createdCmdOwn.Id)
require.NoError(t, err)
// Verify the command was deleted
deletedCmd, _ := th.App.GetCommand(createdCmdOwn.Id)
require.Nil(t, deletedCmd)
// Should not be able to delete other user's command
resp, err := th.Client.DeleteCommand(context.Background(), createdCmdOther.Id)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
// Verify the command was not deleted
notDeletedCmd, _ := th.App.GetCommand(createdCmdOther.Id)
require.NotNil(t, notDeletedCmd)
})
t.Run("UserWithManageOthersCanDeleteAnyCommand", func(t *testing.T) {
// Give BasicUser permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
// Create a command owned by BasicUser
cmdOwn := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own_delete2",
}
createdCmdOwn, _ := th.App.CreateCommand(cmdOwn)
// Create a command owned by BasicUser2
cmdOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: team.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other_delete2",
}
createdCmdOther, _ := th.App.CreateCommand(cmdOther)
// Should be able to delete own command
_, err := th.Client.DeleteCommand(context.Background(), createdCmdOwn.Id)
require.NoError(t, err)
// Verify the command was deleted
deletedCmd, _ := th.App.GetCommand(createdCmdOwn.Id)
require.Nil(t, deletedCmd)
// Should be able to delete other user's command
_, err = th.Client.DeleteCommand(context.Background(), createdCmdOther.Id)
require.NoError(t, err)
// Verify the command was deleted
deletedCmd, _ = th.App.GetCommand(createdCmdOther.Id)
require.Nil(t, deletedCmd)
})
}
func TestListCommands(t *testing.T) {
@ -292,11 +672,10 @@ func TestListCommands(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
newCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "custom_command",
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "custom_command",
}
_, _, rootErr := th.SystemAdminClient.CreateCommand(context.Background(), newCmd)
require.NoError(t, rootErr)
@ -376,6 +755,134 @@ func TestListCommands(t *testing.T) {
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
})
// Permission tests for ManageOwn vs ManageOthers
th.LoginBasic(t)
// Give BasicUser permission to manage their own commands
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
t.Run("UserWithManageOwnCanListOnlyOwnCustomCommands", func(t *testing.T) {
// Create a command owned by BasicUser
cmdOwn := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own_list",
}
createdCmdOwn, _ := th.App.CreateCommand(cmdOwn)
// Create a command owned by BasicUser2
cmdOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other_list",
}
createdCmdOther, _ := th.App.CreateCommand(cmdOther)
// List custom commands only
listCommands, _, err := th.Client.ListCommands(context.Background(), th.BasicTeam.Id, true)
require.NoError(t, err)
foundOwn := false
foundOther := false
for _, command := range listCommands {
if command.Id == createdCmdOwn.Id {
foundOwn = true
}
if command.Id == createdCmdOther.Id {
foundOther = true
}
}
require.True(t, foundOwn, "Should list own command")
require.False(t, foundOther, "Should not list other user's command")
// List all commands (system + custom)
listCommandsAll, _, err := th.Client.ListCommands(context.Background(), th.BasicTeam.Id, false)
require.NoError(t, err)
foundOwn = false
foundOther = false
foundSystem := false
for _, command := range listCommandsAll {
if command.Id == createdCmdOwn.Id {
foundOwn = true
}
if command.Id == createdCmdOther.Id {
foundOther = true
}
if command.Trigger == "echo" {
foundSystem = true
}
}
require.True(t, foundOwn, "Should list own command")
require.False(t, foundOther, "Should not list other user's command")
require.True(t, foundSystem, "Should list system commands")
})
t.Run("UserWithManageOthersCanListAllCustomCommands", func(t *testing.T) {
// Give BasicUser permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
// Create a command owned by BasicUser
cmdOwn := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own_list2",
}
createdCmdOwn, _ := th.App.CreateCommand(cmdOwn)
// Create a command owned by BasicUser2
cmdOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other_list2",
}
createdCmdOther, _ := th.App.CreateCommand(cmdOther)
// List custom commands only
listCommands, _, err := th.Client.ListCommands(context.Background(), th.BasicTeam.Id, true)
require.NoError(t, err)
foundOwn := false
foundOther := false
for _, command := range listCommands {
if command.Id == createdCmdOwn.Id {
foundOwn = true
}
if command.Id == createdCmdOther.Id {
foundOther = true
}
}
require.True(t, foundOwn, "Should list own command")
require.True(t, foundOther, "Should list other user's command")
// List all commands (system + custom)
listCommandsAll, _, err := th.Client.ListCommands(context.Background(), th.BasicTeam.Id, false)
require.NoError(t, err)
foundOwn = false
foundOther = false
for _, command := range listCommandsAll {
if command.Id == createdCmdOwn.Id {
foundOwn = true
}
if command.Id == createdCmdOther.Id {
foundOther = true
}
}
require.True(t, foundOwn, "Should list own command")
require.True(t, foundOther, "Should list other user's command")
})
}
func TestListAutocompleteCommands(t *testing.T) {
@ -384,11 +891,10 @@ func TestListAutocompleteCommands(t *testing.T) {
client := th.Client
newCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "custom_command",
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "custom_command",
}
_, _, err := th.SystemAdminClient.CreateCommand(context.Background(), newCmd)
@ -458,11 +964,10 @@ func TestListCommandAutocompleteSuggestions(t *testing.T) {
client := th.Client
newCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "custom_command",
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "custom_command",
}
_, _, err := th.SystemAdminClient.CreateCommand(context.Background(), newCmd)
@ -560,11 +1065,10 @@ func TestGetCommand(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
newCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "roger",
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "roger",
}
newCmd, _, rootErr := th.SystemAdminClient.CreateCommand(context.Background(), newCmd)
require.NoError(t, rootErr)
@ -612,6 +1116,81 @@ func TestGetCommand(t *testing.T) {
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
})
// Permission tests for ManageOwn vs ManageOthers
th.LoginBasic(t)
// Give BasicUser permission to manage their own commands
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
t.Run("UserWithManageOwnCanGetOnlyOwnCommand", func(t *testing.T) {
// Create a command owned by BasicUser
cmdOwn := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own_get",
}
createdCmdOwn, _ := th.App.CreateCommand(cmdOwn)
// Create a command owned by BasicUser2
cmdOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other_get",
}
createdCmdOther, _ := th.App.CreateCommand(cmdOther)
// Should be able to get own command
cmd, _, err := th.Client.GetCommandById(context.Background(), createdCmdOwn.Id)
require.NoError(t, err)
require.Equal(t, createdCmdOwn.Id, cmd.Id)
// Should not be able to get other user's command
_, resp, err := th.Client.GetCommandById(context.Background(), createdCmdOther.Id)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("UserWithManageOthersCanGetAnyCommand", func(t *testing.T) {
// Give BasicUser permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
// Create a command owned by BasicUser
cmdOwn := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own_get2",
}
createdCmdOwn, _ := th.App.CreateCommand(cmdOwn)
// Create a command owned by BasicUser2
cmdOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other_get2",
}
createdCmdOther, _ := th.App.CreateCommand(cmdOther)
// Should be able to get own command
cmd, _, err := th.Client.GetCommandById(context.Background(), createdCmdOwn.Id)
require.NoError(t, err)
require.Equal(t, createdCmdOwn.Id, cmd.Id)
// Should be able to get other user's command
cmd, _, err = th.Client.GetCommandById(context.Background(), createdCmdOther.Id)
require.NoError(t, err)
require.Equal(t, createdCmdOther.Id, cmd.Id)
})
}
func TestRegenToken(t *testing.T) {
@ -626,11 +1205,10 @@ func TestRegenToken(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCommands = true })
newCmd := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger",
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger",
}
createdCmd, resp, err := th.SystemAdminClient.CreateCommand(context.Background(), newCmd)
@ -645,6 +1223,84 @@ func TestRegenToken(t *testing.T) {
require.Error(t, err)
CheckNotFoundStatus(t, resp)
require.Empty(t, token, "should not return the token")
// Permission tests for ManageOwn vs ManageOthers
th.LoginBasic(t)
// Give BasicUser permission to manage their own commands
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamUserRoleId)
t.Run("UserWithManageOwnCanRegenOnlyOwnCommandToken", func(t *testing.T) {
// Create a command owned by BasicUser
cmdOwn := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own_regen",
}
createdCmdOwn, _ := th.App.CreateCommand(cmdOwn)
oldToken := createdCmdOwn.Token
// Create a command owned by BasicUser2
cmdOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other_regen",
}
createdCmdOther, _ := th.App.CreateCommand(cmdOther)
// Should be able to regenerate own command token
newToken, _, err := th.Client.RegenCommandToken(context.Background(), createdCmdOwn.Id)
require.NoError(t, err)
require.NotEqual(t, oldToken, newToken)
// Should not be able to regenerate other user's command token
_, resp, err := th.Client.RegenCommandToken(context.Background(), createdCmdOther.Id)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("UserWithManageOthersCanRegenAnyCommandToken", func(t *testing.T) {
// Give BasicUser permission to manage others' commands
th.AddPermissionToRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
defer th.RemovePermissionFromRole(t, model.PermissionManageOthersSlashCommands.Id, model.TeamUserRoleId)
// Create a command owned by BasicUser
cmdOwn := &model.Command{
CreatorId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_own_regen2",
}
createdCmdOwn, _ := th.App.CreateCommand(cmdOwn)
oldTokenOwn := createdCmdOwn.Token
// Create a command owned by BasicUser2
cmdOther := &model.Command{
CreatorId: th.BasicUser2.Id,
TeamId: th.BasicTeam.Id,
URL: "http://nowhere.com",
Method: model.CommandMethodPost,
Trigger: "trigger_other_regen2",
}
createdCmdOther, _ := th.App.CreateCommand(cmdOther)
oldTokenOther := createdCmdOther.Token
// Should be able to regenerate own command token
newToken, _, err := th.Client.RegenCommandToken(context.Background(), createdCmdOwn.Id)
require.NoError(t, err)
require.NotEqual(t, oldTokenOwn, newToken)
// Should be able to regenerate other user's command token
newToken, _, err = th.Client.RegenCommandToken(context.Background(), createdCmdOther.Id)
require.NoError(t, err)
require.NotEqual(t, oldTokenOther, newToken)
})
}
func TestExecuteInvalidCommand(t *testing.T) {

View file

@ -36,12 +36,12 @@ func (api *API) InitOutgoingOAuthConnection() {
// other users can use them in their outgoing webhooks and slash commands if they have permissions to manage those.
func checkOutgoingOAuthConnectionReadPermissions(c *Context, teamId string) bool {
if c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageOutgoingOAuthConnections) ||
c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageOutgoingWebhooks) ||
c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageSlashCommands) {
c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageOwnOutgoingWebhooks) ||
c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamId, model.PermissionManageOwnSlashCommands) {
return true
}
c.SetPermissionError(model.PermissionManageOutgoingWebhooks, model.PermissionManageSlashCommands)
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks, model.PermissionManageOwnSlashCommands)
return false
}

View file

@ -98,7 +98,7 @@ func TestCheckOutgoingOAuthConnectionReadPermissions(t *testing.T) {
c.App = th.App
c.Logger = th.App.Srv().Log()
th.AddPermissionToRole(t, model.PermissionManageSlashCommands.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamAdminRoleId)
canRead := checkOutgoingOAuthConnectionReadPermissions(c, th.BasicTeam.Id)
require.True(t, canRead)
@ -117,7 +117,7 @@ func TestCheckOutgoingOAuthConnectionReadPermissions(t *testing.T) {
c.App = th.App
c.Logger = th.App.Srv().Log()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
canRead := checkOutgoingOAuthConnectionReadPermissions(c, th.BasicTeam.Id)
require.True(t, canRead)
@ -177,8 +177,8 @@ func TestClientOutgoingOAuthConnectionGet(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageSlashCommands.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamAdminRoleId)
outgoingOauthIface := &mocks.OutgoingOAuthConnectionInterface{}
outgoingOauthImpl := th.App.Srv().OutgoingOAuthConnection
@ -207,8 +207,8 @@ func TestClientOutgoingOAuthConnectionGet(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageSlashCommands.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.TeamAdminRoleId)
outgoingOauthIface := &mocks.OutgoingOAuthConnectionInterface{}
outgoingOauthImpl := th.App.Srv().OutgoingOAuthConnection
@ -318,8 +318,8 @@ func TestClientListOutgoingOAuthConnection(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageSlashCommands.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.SystemUserRoleId)
outgoingOauthIface := &mocks.OutgoingOAuthConnectionInterface{}
th.App.Srv().OutgoingOAuthConnection = outgoingOauthIface
@ -356,8 +356,8 @@ func TestClientListOutgoingOAuthConnection(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageSlashCommands.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.SystemUserRoleId)
conn := newOutgoingOAuthConnection()
conn.Audiences = []string{"http://knowhere.com"}
@ -402,8 +402,8 @@ func TestClientListOutgoingOAuthConnection(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageSlashCommands.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnSlashCommands.Id, model.SystemUserRoleId)
conn := newOutgoingOAuthConnection()
conn.CreatorId = model.NewId()

View file

@ -45,8 +45,8 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
model.AddEventParameterAuditableToAuditRec(auditRec, "channel", channel)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionManageIncomingWebhooks) {
c.SetPermissionError(model.PermissionManageIncomingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionManageOwnIncomingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnIncomingWebhooks)
return
}
@ -72,6 +72,11 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
userId = hook.UserId
}
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionBypassIncomingWebhookChannelLock) {
hook.ChannelLocked = true
hook.ChannelId = channel.Id
}
incomingHook, err := c.App.CreateIncomingWebhookForChannel(userId, channel, &hook)
if err != nil {
c.Err = err
@ -143,8 +148,8 @@ func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionManageIncomingWebhooks) {
c.SetPermissionError(model.PermissionManageIncomingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionManageOwnIncomingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnIncomingWebhooks)
return
}
@ -160,6 +165,11 @@ func updateIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), channel.TeamId, model.PermissionBypassIncomingWebhookChannelLock) {
updatedHook.ChannelLocked = true
updatedHook.ChannelId = channel.Id
}
incomingHook, err := c.App.UpdateIncomingWebhook(oldHook, &updatedHook)
if err != nil {
c.Err = err
@ -188,8 +198,8 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
)
if teamID != "" {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamID, model.PermissionManageIncomingWebhooks) {
c.SetPermissionError(model.PermissionManageIncomingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamID, model.PermissionManageOwnIncomingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnIncomingWebhooks)
return
}
@ -200,8 +210,8 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
hooks, appErr = c.App.GetIncomingWebhooksForTeamPageByUser(teamID, userID, c.Params.Page, c.Params.PerPage)
} else {
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageIncomingWebhooks) {
c.SetPermissionError(model.PermissionManageIncomingWebhooks)
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageOwnIncomingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnIncomingWebhooks)
return
}
@ -275,10 +285,10 @@ func getIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageIncomingWebhooks) ||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOwnIncomingWebhooks) ||
(channel.Type != model.ChannelTypeOpen && !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel)) {
c.LogAudit("fail - bad permissions")
c.SetPermissionError(model.PermissionManageIncomingWebhooks)
c.SetPermissionError(model.PermissionManageOwnIncomingWebhooks)
return
}
@ -329,10 +339,10 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddMeta("channel_name", channel.Name)
auditRec.AddMeta("team_id", hook.TeamId)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageIncomingWebhooks) ||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOwnIncomingWebhooks) ||
(channel.Type != model.ChannelTypeOpen && !c.App.SessionHasPermissionToReadChannel(c.AppContext, *c.AppContext.Session(), channel)) {
c.LogAudit("fail - bad permissions")
c.SetPermissionError(model.PermissionManageIncomingWebhooks)
c.SetPermissionError(model.PermissionManageOwnIncomingWebhooks)
return
}
@ -391,8 +401,8 @@ func updateOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), updatedHook.TeamId, model.PermissionManageOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOutgoingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), updatedHook.TeamId, model.PermissionManageOwnOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks)
return
}
@ -430,8 +440,8 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
defer c.LogAuditRec(auditRec)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOutgoingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOwnOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks)
return
}
@ -481,8 +491,8 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
)
if channelID != "" {
if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelID, model.PermissionManageOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOutgoingWebhooks)
if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), channelID, model.PermissionManageOwnOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks)
return
}
@ -493,8 +503,8 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
hooks, appErr = c.App.GetOutgoingWebhooksForChannelPageByUser(channelID, userID, c.Params.Page, c.Params.PerPage)
} else if teamID != "" {
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamID, model.PermissionManageOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOutgoingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), teamID, model.PermissionManageOwnOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks)
return
}
@ -505,8 +515,8 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
hooks, appErr = c.App.GetOutgoingWebhooksForTeamPageByUser(teamID, userID, c.Params.Page, c.Params.PerPage)
} else {
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOutgoingWebhooks)
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageOwnOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks)
return
}
@ -555,8 +565,8 @@ func getOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddMeta("team_id", hook.TeamId)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOutgoingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOwnOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks)
return
}
@ -594,8 +604,8 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
auditRec.AddMeta("team_id", hook.TeamId)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOutgoingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOwnOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks)
return
}
@ -642,8 +652,8 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddMeta("team_id", hook.TeamId)
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOutgoingWebhooks)
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), hook.TeamId, model.PermissionManageOwnOutgoingWebhooks) {
c.SetPermissionError(model.PermissionManageOwnOutgoingWebhooks)
return
}

View file

@ -13,6 +13,22 @@ import (
"github.com/mattermost/mattermost/server/public/model"
)
func addIncomingWebhookPermissions(t *testing.T, th *TestHelper, roleID string) {
th.AddPermissionToRole(t, model.PermissionManageOwnIncomingWebhooks.Id, roleID)
th.AddPermissionToRole(t, model.PermissionBypassIncomingWebhookChannelLock.Id, roleID)
}
func addIncomingWebhookPermissionsWithOthers(t *testing.T, th *TestHelper, roleID string) {
addIncomingWebhookPermissions(t, th, roleID)
th.AddPermissionToRole(t, model.PermissionManageOthersIncomingWebhooks.Id, roleID)
}
func removeIncomingWebhookPermissions(t *testing.T, th *TestHelper, roleID string) {
th.RemovePermissionFromRole(t, model.PermissionManageOwnIncomingWebhooks.Id, roleID)
th.RemovePermissionFromRole(t, model.PermissionManageOthersIncomingWebhooks.Id, roleID)
th.RemovePermissionFromRole(t, model.PermissionBypassIncomingWebhookChannelLock.Id, roleID)
}
func TestCreateIncomingWebhook(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic(t)
@ -28,8 +44,8 @@ func TestCreateIncomingWebhook(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
hook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
@ -55,11 +71,30 @@ func TestCreateIncomingWebhook(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamUserRoleId)
_, _, err = client.CreateIncomingWebhook(context.Background(), hook)
require.NoError(t, err)
t.Run("channel lock enforced without bypass", func(t *testing.T) {
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnIncomingWebhooks.Id, model.TeamUserRoleId)
unlockedHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, ChannelLocked: false}
created, _, err2 := client.CreateIncomingWebhook(context.Background(), unlockedHook)
require.NoError(t, err2)
require.True(t, created.ChannelLocked)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
})
t.Run("channel lock optional with bypass", func(t *testing.T) {
hookWithBypass := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, ChannelLocked: false}
created, _, err2 := client.CreateIncomingWebhook(context.Background(), hookWithBypass)
require.NoError(t, err2)
require.False(t, created.ChannelLocked)
})
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnablePostUsernameOverride = false })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnablePostIconOverride = false })
@ -101,6 +136,19 @@ func TestCreateIncomingWebhook(t *testing.T) {
CheckBadRequestStatus(t, response)
})
t.Run("Cannot create for another user with only manage own permission", func(t *testing.T) {
// Setup user with only "manage own" permission
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnIncomingWebhooks.Id, model.TeamUserRoleId)
testHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser2.Id}
_, response, err2 := client.CreateIncomingWebhook(context.Background(), testHook)
require.Error(t, err2)
CheckForbiddenStatus(t, response)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
})
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = false })
_, resp, err = client.CreateIncomingWebhook(context.Background(), hook)
require.Error(t, err)
@ -117,9 +165,9 @@ func TestCreateIncomingWebhook_BypassTeamPermissions(t *testing.T) {
defaultRolePermissions := th.SaveDefaultRolePermissions(t)
defer th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
th.RemovePermissionFromRole(t, model.PermissionManageIncomingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
removeIncomingWebhookPermissions(t, th, model.SystemUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
hook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
@ -155,8 +203,8 @@ func TestGetIncomingWebhooks(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
hook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
rhook, _, err := th.SystemAdminClient.CreateIncomingWebhook(context.Background(), hook)
@ -202,7 +250,7 @@ func TestGetIncomingWebhooks(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
_, _, err = client.GetIncomingWebhooksForTeam(context.Background(), th.BasicTeam.Id, 0, 1000, "")
require.NoError(t, err)
@ -215,6 +263,24 @@ func TestGetIncomingWebhooks(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
t.Run("User with only manage own cannot see others webhooks", func(t *testing.T) {
// Create webhook as admin
adminHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
adminCreatedHook, _, err2 := th.SystemAdminClient.CreateIncomingWebhook(context.Background(), adminHook)
require.NoError(t, err2)
// Remove all permissions from team_user and give only "manage own"
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnIncomingWebhooks.Id, model.TeamUserRoleId)
// Should NOT be able to see admin's webhook
_, resp2, err2 := client.GetIncomingWebhook(context.Background(), adminCreatedHook.Id, "")
require.Error(t, err2)
CheckForbiddenStatus(t, resp2)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
})
_, err = client.Logout(context.Background())
require.NoError(t, err)
_, resp, err = client.GetIncomingWebhooks(context.Background(), 0, 1000, "")
@ -234,8 +300,8 @@ func TestGetIncomingWebhooksListByUser(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.SystemUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
addIncomingWebhookPermissions(t, th, model.SystemUserRoleId)
// Basic user webhook
bHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.BasicUser.Id}
@ -276,8 +342,8 @@ func TestGetIncomingWebhooksByTeam(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
// Basic user webhook
bHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.BasicUser.Id}
@ -319,8 +385,8 @@ func TestGetIncomingWebhooksWithCount(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.SystemUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.SystemUserRoleId)
// Basic user webhook
bHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.BasicUser.Id}
@ -446,6 +512,25 @@ func TestDeleteIncomingWebhook(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("Cannot delete others webhook with only manage own permission", func(t *testing.T) {
// Create webhook as admin
adminHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
adminCreatedHook, _, err2 := th.SystemAdminClient.CreateIncomingWebhook(context.Background(), adminHook)
require.NoError(t, err2)
// Give basic user only "manage own" permission
defaultPerms := th.SaveDefaultRolePermissions(t)
defer th.RestoreDefaultRolePermissions(t, defaultPerms)
th.RemovePermissionFromRole(t, model.PermissionManageOwnIncomingWebhooks.Id, model.TeamUserRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOthersIncomingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnIncomingWebhooks.Id, model.TeamUserRoleId)
th.LoginBasic(t)
resp, err2 := th.Client.DeleteIncomingWebhook(context.Background(), adminCreatedHook.Id)
require.Error(t, err2)
CheckForbiddenStatus(t, resp)
})
}
func TestCreateOutgoingWebhook(t *testing.T) {
@ -459,8 +544,8 @@ func TestCreateOutgoingWebhook(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
hook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}, Username: "some-user-name", IconURL: "http://some-icon-url/"}
@ -486,7 +571,7 @@ func TestCreateOutgoingWebhook(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
_, _, err = client.CreateOutgoingWebhook(context.Background(), hook)
require.NoError(t, err)
@ -526,6 +611,25 @@ func TestCreateOutgoingWebhook(t *testing.T) {
CheckBadRequestStatus(t, response)
})
t.Run("Cannot create for another user with only manage own permission", func(t *testing.T) {
// Remove all permissions and give only "manage own"
defaultPerms := th.SaveDefaultRolePermissions(t)
defer th.RestoreDefaultRolePermissions(t, defaultPerms)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
testHook := &model.OutgoingWebhook{
ChannelId: th.BasicChannel.Id,
TeamId: th.BasicTeam.Id,
CallbackURLs: []string{"http://nowhere.com"},
TriggerWords: []string{"test2"},
CreatorId: th.BasicUser2.Id,
}
_, resp2, err2 := client.CreateOutgoingWebhook(context.Background(), testHook)
require.Error(t, err2)
CheckForbiddenStatus(t, resp2)
})
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOutgoingWebhooks = false })
_, resp, err = client.CreateOutgoingWebhook(context.Background(), hook)
require.Error(t, err)
@ -541,8 +645,8 @@ func TestGetOutgoingWebhooks(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
hook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}}
rhook, _, err2 := th.SystemAdminClient.CreateOutgoingWebhook(context.Background(), hook)
@ -604,7 +708,7 @@ func TestGetOutgoingWebhooks(t *testing.T) {
require.Error(t, err2)
CheckForbiddenStatus(t, resp)
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
_, _, err2 = th.Client.GetOutgoingWebhooksForTeam(context.Background(), th.BasicTeam.Id, 0, 1000, "")
require.NoError(t, err2)
@ -624,6 +728,29 @@ func TestGetOutgoingWebhooks(t *testing.T) {
require.Error(t, err2)
CheckForbiddenStatus(t, resp)
t.Run("User with only manage own cannot see others outgoing webhooks", func(t *testing.T) {
// Create webhook as admin
adminHook := &model.OutgoingWebhook{
ChannelId: th.BasicChannel.Id,
TeamId: th.BasicTeam.Id,
CallbackURLs: []string{"http://nowhere.com"},
TriggerWords: []string{"admin"},
}
adminCreatedHook, _, err3 := th.SystemAdminClient.CreateOutgoingWebhook(context.Background(), adminHook)
require.NoError(t, err3)
// Remove all permissions and give only "manage own"
defaultPerms := th.SaveDefaultRolePermissions(t)
defer th.RestoreDefaultRolePermissions(t, defaultPerms)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
// Should NOT be able to get admin's webhook
_, resp2, err3 := th.Client.GetOutgoingWebhook(context.Background(), adminCreatedHook.Id)
require.Error(t, err3)
CheckForbiddenStatus(t, resp2)
})
_, err := th.Client.Logout(context.Background())
require.NoError(t, err)
_, resp, err2 = th.Client.GetOutgoingWebhooks(context.Background(), 0, 1000, "")
@ -641,8 +768,8 @@ func TestGetOutgoingWebhooksByTeam(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
// Basic user webhook
bHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}}
@ -682,8 +809,8 @@ func TestGetOutgoingWebhooksByChannel(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
// Basic user webhook
bHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}}
@ -724,8 +851,8 @@ func TestGetOutgoingWebhooksListByUser(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.SystemUserRoleId)
// Basic user webhook
bHook := &model.OutgoingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId, CallbackURLs: []string{"http://nowhere.com"}}
@ -800,8 +927,8 @@ func TestUpdateIncomingHook(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
hook1 := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
@ -908,11 +1035,33 @@ func TestUpdateIncomingHook(t *testing.T) {
CheckForbiddenStatus(t, resp)
})
th.RemovePermissionFromRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
t.Run("update cannot clear channel lock without bypass", func(t *testing.T) {
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnIncomingWebhooks.Id, model.TeamUserRoleId)
hookWithoutBypass := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
hookWithoutBypass, _, err := th.Client.CreateIncomingWebhook(context.Background(), hookWithoutBypass)
require.NoError(t, err)
require.True(t, hookWithoutBypass.ChannelLocked)
hookWithoutBypass.ChannelLocked = false
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnIncomingWebhooks.Id, model.TeamUserRoleId)
updated, _, err := th.Client.UpdateIncomingWebhook(context.Background(), hookWithoutBypass)
require.NoError(t, err)
require.True(t, updated.ChannelLocked)
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
})
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
t.Run("OnlyAdminIntegrationsDisabled", func(t *testing.T) {
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
t.Run("UpdateHookOfSameUser", func(t *testing.T) {
sameUserHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
@ -932,8 +1081,8 @@ func TestUpdateIncomingHook(t *testing.T) {
})
})
th.RemovePermissionFromRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
_, err := th.Client.Logout(context.Background())
require.NoError(t, err)
@ -990,6 +1139,22 @@ func TestUpdateIncomingHook(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("Cannot update others webhook with only manage own permission", func(t *testing.T) {
// Create webhook as admin
adminHook2 := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
adminCreatedHook2, _, err2 := th.SystemAdminClient.CreateIncomingWebhook(context.Background(), adminHook2)
require.NoError(t, err2)
// Remove all permissions and give only "manage own"
removeIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnIncomingWebhooks.Id, model.TeamUserRoleId)
adminCreatedHook2.DisplayName = "Hacked"
_, resp, err2 := th.Client.UpdateIncomingWebhook(context.Background(), adminCreatedHook2)
require.Error(t, err2)
CheckForbiddenStatus(t, resp)
})
}
func TestUpdateIncomingWebhook_BypassTeamPermissions(t *testing.T) {
@ -1002,9 +1167,9 @@ func TestUpdateIncomingWebhook_BypassTeamPermissions(t *testing.T) {
defaultRolePermissions := th.SaveDefaultRolePermissions(t)
defer th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
th.RemovePermissionFromRole(t, model.PermissionManageIncomingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageIncomingWebhooks.Id, model.TeamUserRoleId)
removeIncomingWebhookPermissions(t, th, model.SystemUserRoleId)
addIncomingWebhookPermissionsWithOthers(t, th, model.TeamAdminRoleId)
addIncomingWebhookPermissions(t, th, model.TeamUserRoleId)
hook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id}
@ -1071,8 +1236,8 @@ func TestUpdateOutgoingHook(t *testing.T) {
defer func() {
th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
}()
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
createdHook := &model.OutgoingWebhook{
ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId,
@ -1173,7 +1338,7 @@ func TestUpdateOutgoingHook(t *testing.T) {
CheckForbiddenStatus(t, resp)
})
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
hook2 := &model.OutgoingWebhook{
ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId,
CallbackURLs: []string{"http://nowhere.com"}, TriggerWords: []string{"rats2"},
@ -1186,8 +1351,8 @@ func TestUpdateOutgoingHook(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
th.RemovePermissionFromRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
_, err = th.Client.Logout(context.Background())
require.NoError(t, err)
@ -1282,6 +1447,29 @@ func TestUpdateOutgoingHook(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("Cannot update others webhook with only manage own permission", func(t *testing.T) {
// Create webhook as admin
adminHook := &model.OutgoingWebhook{
ChannelId: th.BasicChannel.Id,
TeamId: th.BasicTeam.Id,
CallbackURLs: []string{"http://nowhere.com"},
TriggerWords: []string{"admin2"},
}
adminCreatedHook, _, err2 := th.SystemAdminClient.CreateOutgoingWebhook(context.Background(), adminHook)
require.NoError(t, err2)
// Remove all permissions and give only "manage own"
defaultPerms := th.SaveDefaultRolePermissions(t)
defer th.RestoreDefaultRolePermissions(t, defaultPerms)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
adminCreatedHook.DisplayName = "Hacked"
_, resp, err2 := th.Client.UpdateOutgoingWebhook(context.Background(), adminCreatedHook)
require.Error(t, err2)
CheckForbiddenStatus(t, resp)
})
}
func TestUpdateOutgoingWebhook_BypassTeamPermissions(t *testing.T) {
@ -1292,9 +1480,9 @@ func TestUpdateOutgoingWebhook_BypassTeamPermissions(t *testing.T) {
defaultRolePermissions := th.SaveDefaultRolePermissions(t)
defer th.RestoreDefaultRolePermissions(t, defaultRolePermissions)
th.RemovePermissionFromRole(t, model.PermissionManageOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOutgoingWebhooks.Id, model.TeamUserRoleId)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.SystemUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamAdminRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
hook := &model.OutgoingWebhook{
ChannelId: th.BasicChannel.Id, TeamId: th.BasicChannel.TeamId,
@ -1370,4 +1558,27 @@ func TestDeleteOutgoingHook(t *testing.T) {
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("Cannot delete others webhook with only manage own permission", func(t *testing.T) {
// Create webhook as admin
adminHook := &model.OutgoingWebhook{
ChannelId: th.BasicChannel.Id,
TeamId: th.BasicChannel.TeamId,
CallbackURLs: []string{"http://nowhere.com"},
TriggerWords: []string{"admin3"},
}
adminCreatedHook, _, err2 := th.SystemAdminClient.CreateOutgoingWebhook(context.Background(), adminHook)
require.NoError(t, err2)
// Give basic user only "manage own" permission
defaultPerms := th.SaveDefaultRolePermissions(t)
defer th.RestoreDefaultRolePermissions(t, defaultPerms)
th.RemovePermissionFromRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
th.AddPermissionToRole(t, model.PermissionManageOwnOutgoingWebhooks.Id, model.TeamUserRoleId)
th.LoginBasic(t)
resp, err2 := th.Client.DeleteOutgoingWebhook(context.Background(), adminCreatedHook.Id)
require.Error(t, err2)
CheckForbiddenStatus(t, resp)
})
}

View file

@ -174,12 +174,13 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
model.PermissionImportTeam.Id,
model.PermissionManageTeamRoles.Id,
model.PermissionManageChannelRoles.Id,
model.PermissionManageOwnIncomingWebhooks.Id,
model.PermissionManageOthersIncomingWebhooks.Id,
model.PermissionManageOwnOutgoingWebhooks.Id,
model.PermissionManageOthersOutgoingWebhooks.Id,
model.PermissionManageSlashCommands.Id,
model.PermissionManageOwnSlashCommands.Id,
model.PermissionManageOthersSlashCommands.Id,
model.PermissionManageIncomingWebhooks.Id,
model.PermissionManageOutgoingWebhooks.Id,
model.PermissionBypassIncomingWebhookChannelLock.Id,
model.PermissionConvertPublicChannelToPrivate.Id,
model.PermissionConvertPrivateChannelToPublic.Id,
model.PermissionDeletePost.Id,

View file

@ -130,8 +130,8 @@ func TestCheckIfRolesGrantPermission(t *testing.T) {
{[]string{model.ChannelUserRoleId}, model.PermissionManageSystem.Id, false},
{[]string{model.SystemAdminRoleId, model.ChannelUserRoleId}, model.PermissionManageSystem.Id, true},
{[]string{model.ChannelUserRoleId, model.SystemAdminRoleId}, model.PermissionManageSystem.Id, true},
{[]string{model.TeamUserRoleId, model.TeamAdminRoleId}, model.PermissionManageSlashCommands.Id, true},
{[]string{model.TeamAdminRoleId, model.TeamUserRoleId}, model.PermissionManageSlashCommands.Id, true},
{[]string{model.TeamUserRoleId, model.TeamAdminRoleId}, model.PermissionManageOwnSlashCommands.Id, true},
{[]string{model.TeamAdminRoleId, model.TeamUserRoleId}, model.PermissionManageOwnSlashCommands.Id, true},
{[]string{model.ChannelGuestRoleId}, model.PermissionReadChannelContent.Id, true},
}

View file

@ -128,6 +128,10 @@ func (a *App) ListAutocompleteCommands(teamID string, T i18n.TranslateFunc) ([]*
}
func (a *App) ListTeamCommands(teamID string) ([]*model.Command, *model.AppError) {
return a.ListTeamCommandsByUser(teamID, "")
}
func (a *App) ListTeamCommandsByUser(teamID string, userID string) ([]*model.Command, *model.AppError) {
if !*a.Config().ServiceSettings.EnableCommands {
return nil, model.NewAppError("ListTeamCommands", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented)
}
@ -137,10 +141,25 @@ func (a *App) ListTeamCommands(teamID string) ([]*model.Command, *model.AppError
return nil, model.NewAppError("ListTeamCommands", "app.command.listteamcommands.internal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
// Filter by user if userID is specified
if userID != "" {
filteredCmds := make([]*model.Command, 0)
for _, cmd := range teamCmds {
if cmd.CreatorId == userID {
filteredCmds = append(filteredCmds, cmd)
}
}
return filteredCmds, nil
}
return teamCmds, nil
}
func (a *App) ListAllCommands(teamID string, T i18n.TranslateFunc) ([]*model.Command, *model.AppError) {
return a.ListAllCommandsByUser(teamID, "", T)
}
func (a *App) ListAllCommandsByUser(teamID string, userID string, T i18n.TranslateFunc) ([]*model.Command, *model.AppError) {
commands := make([]*model.Command, 0, 32)
seen := make(map[string]bool)
for _, value := range commandProviders {
@ -168,6 +187,10 @@ func (a *App) ListAllCommands(teamID string, T i18n.TranslateFunc) ([]*model.Com
}
for _, cmd := range teamCmds {
if !seen[cmd.Trigger] {
// Filter by user if userID is specified (before sanitizing)
if userID != "" && cmd.CreatorId != userID {
continue
}
cmd.Sanitize()
seen[cmd.Trigger] = true
commands = append(commands, cmd)

View file

@ -21,58 +21,64 @@ type permissionTransformation struct {
type permissionsMap []permissionTransformation
const (
PermissionManageSystem = "manage_system"
PermissionManageTeam = "manage_team"
PermissionManageEmojis = "manage_emojis"
PermissionManageOthersEmojis = "manage_others_emojis"
PermissionCreateEmojis = "create_emojis"
PermissionDeleteEmojis = "delete_emojis"
PermissionDeleteOthersEmojis = "delete_others_emojis"
PermissionManageWebhooks = "manage_webhooks"
PermissionManageOthersWebhooks = "manage_others_webhooks"
PermissionManageIncomingWebhooks = "manage_incoming_webhooks"
PermissionManageOthersIncomingWebhooks = "manage_others_incoming_webhooks"
PermissionManageOutgoingWebhooks = "manage_outgoing_webhooks"
PermissionManageOthersOutgoingWebhooks = "manage_others_outgoing_webhooks"
PermissionListPublicTeams = "list_public_teams"
PermissionListPrivateTeams = "list_private_teams"
PermissionJoinPublicTeams = "join_public_teams"
PermissionJoinPrivateTeams = "join_private_teams"
PermissionPermanentDeleteUser = "permanent_delete_user"
PermissionCreateBot = "create_bot"
PermissionReadBots = "read_bots"
PermissionReadOthersBots = "read_others_bots"
PermissionManageBots = "manage_bots"
PermissionManageOthersBots = "manage_others_bots"
PermissionDeletePublicChannel = "delete_public_channel"
PermissionDeletePrivateChannel = "delete_private_channel"
PermissionManagePublicChannelProperties = "manage_public_channel_properties"
PermissionManagePrivateChannelProperties = "manage_private_channel_properties"
PermissionConvertPublicChannelToPrivate = "convert_public_channel_to_private"
PermissionConvertPrivateChannelToPublic = "convert_private_channel_to_public"
PermissionViewMembers = "view_members"
PermissionInviteUser = "invite_user"
PermissionInviteGuest = "invite_guest"
PermissionPromoteGuest = "promote_guest"
PermissionDemoteToGuest = "demote_to_guest"
PermissionUseChannelMentions = "use_channel_mentions"
PermissionCreatePost = "create_post"
PermissionCreatePost_PUBLIC = "create_post_public"
PermissionUseGroupMentions = "use_group_mentions"
PermissionAddReaction = "add_reaction"
PermissionRemoveReaction = "remove_reaction"
PermissionManagePublicChannelMembers = "manage_public_channel_members"
PermissionManagePrivateChannelMembers = "manage_private_channel_members"
PermissionReadJobs = "read_jobs"
PermissionManageJobs = "manage_jobs"
PermissionReadOtherUsersTeams = "read_other_users_teams"
PermissionEditOtherUsers = "edit_other_users"
PermissionReadPublicChannelGroups = "read_public_channel_groups"
PermissionReadPrivateChannelGroups = "read_private_channel_groups"
PermissionEditBrand = "edit_brand"
PermissionManageSharedChannels = "manage_shared_channels"
PermissionManageSecureConnections = "manage_secure_connections"
PermissionManageRemoteClusters = "manage_remote_clusters" // deprecated; use `manage_secure_connections`
PermissionManageSystem = "manage_system"
PermissionManageTeam = "manage_team"
PermissionManageEmojis = "manage_emojis"
PermissionManageOthersEmojis = "manage_others_emojis"
PermissionCreateEmojis = "create_emojis"
PermissionDeleteEmojis = "delete_emojis"
PermissionDeleteOthersEmojis = "delete_others_emojis"
PermissionManageWebhooks = "manage_webhooks"
PermissionManageOthersWebhooks = "manage_others_webhooks"
PermissionManageIncomingWebhooks = "manage_incoming_webhooks"
PermissionManageOwnIncomingWebhooks = "manage_own_incoming_webhooks"
PermissionManageOthersIncomingWebhooks = "manage_others_incoming_webhooks"
PermissionManageOutgoingWebhooks = "manage_outgoing_webhooks"
PermissionManageOwnOutgoingWebhooks = "manage_own_outgoing_webhooks"
PermissionManageOthersOutgoingWebhooks = "manage_others_outgoing_webhooks"
PermissionBypassIncomingWebhookChannelLock = "bypass_incoming_webhook_channel_lock"
PermissionListPublicTeams = "list_public_teams"
PermissionListPrivateTeams = "list_private_teams"
PermissionJoinPublicTeams = "join_public_teams"
PermissionJoinPrivateTeams = "join_private_teams"
PermissionPermanentDeleteUser = "permanent_delete_user"
PermissionCreateBot = "create_bot"
PermissionReadBots = "read_bots"
PermissionReadOthersBots = "read_others_bots"
PermissionManageBots = "manage_bots"
PermissionManageOthersBots = "manage_others_bots"
PermissionManageSlashCommands = "manage_slash_commands"
PermissionManageOwnSlashCommands = "manage_own_slash_commands"
PermissionDeletePublicChannel = "delete_public_channel"
PermissionDeletePrivateChannel = "delete_private_channel"
PermissionManagePublicChannelProperties = "manage_public_channel_properties"
PermissionManagePrivateChannelProperties = "manage_private_channel_properties"
PermissionConvertPublicChannelToPrivate = "convert_public_channel_to_private"
PermissionConvertPrivateChannelToPublic = "convert_private_channel_to_public"
PermissionViewMembers = "view_members"
PermissionInviteUser = "invite_user"
PermissionInviteGuest = "invite_guest"
PermissionPromoteGuest = "promote_guest"
PermissionDemoteToGuest = "demote_to_guest"
PermissionUseChannelMentions = "use_channel_mentions"
PermissionCreatePost = "create_post"
PermissionCreatePost_PUBLIC = "create_post_public"
PermissionUseGroupMentions = "use_group_mentions"
PermissionAddReaction = "add_reaction"
PermissionRemoveReaction = "remove_reaction"
PermissionManagePublicChannelMembers = "manage_public_channel_members"
PermissionManagePrivateChannelMembers = "manage_private_channel_members"
PermissionReadJobs = "read_jobs"
PermissionManageJobs = "manage_jobs"
PermissionReadOtherUsersTeams = "read_other_users_teams"
PermissionEditOtherUsers = "edit_other_users"
PermissionReadPublicChannelGroups = "read_public_channel_groups"
PermissionReadPrivateChannelGroups = "read_private_channel_groups"
PermissionEditBrand = "edit_brand"
PermissionManageSharedChannels = "manage_shared_channels"
PermissionManageSecureConnections = "manage_secure_connections"
PermissionManageOAuth = "manage_oauth"
PermissionManageRemoteClusters = "manage_remote_clusters" // deprecated; use `manage_secure_connections`
)
// Deprecated: This function should only be used if a case arises where team and/or channel scheme roles do not need to be migrated.
@ -275,6 +281,36 @@ func (a *App) getWebhooksPermissionsSplitMigration() (permissionsMap, error) {
}, nil
}
func (a *App) getIntegrationsOwnPermissionsMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: permissionExists(PermissionManageIncomingWebhooks),
Add: []string{PermissionManageOwnIncomingWebhooks, PermissionBypassIncomingWebhookChannelLock},
Remove: []string{PermissionManageIncomingWebhooks},
},
permissionTransformation{
On: permissionExists(PermissionManageOutgoingWebhooks),
Add: []string{PermissionManageOwnOutgoingWebhooks},
Remove: []string{PermissionManageOutgoingWebhooks},
},
permissionTransformation{
On: permissionExists(PermissionManageSlashCommands),
Add: []string{PermissionManageOwnSlashCommands},
Remove: []string{PermissionManageSlashCommands},
},
// Ensure system admin has the new "manage others" permissions
permissionTransformation{
On: isExactRole(model.SystemAdminRoleId),
Add: []string{model.PermissionManageOthersIncomingWebhooks.Id, model.PermissionManageOthersOutgoingWebhooks.Id, model.PermissionManageOthersSlashCommands.Id},
},
// Ensure team admin (including scheme team admins) have the new "manage others" permissions
permissionTransformation{
On: isRole(model.TeamAdminRoleId),
Add: []string{model.PermissionManageOthersIncomingWebhooks.Id, model.PermissionManageOthersOutgoingWebhooks.Id, model.PermissionManageOthersSlashCommands.Id},
},
}, nil
}
func (a *App) getListJoinPublicPrivateTeamsPermissionsMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
@ -1225,6 +1261,7 @@ func (s *Server) doPermissionsMigrations() error {
}{
{Key: model.MigrationKeyEmojiPermissionsSplit, Migration: a.getEmojisPermissionsSplitMigration},
{Key: model.MigrationKeyWebhookPermissionsSplit, Migration: a.getWebhooksPermissionsSplitMigration},
{Key: model.MigrationKeyIntegrationsOwnPermissions, Migration: a.getIntegrationsOwnPermissionsMigration},
{Key: model.MigrationKeyListJoinPublicPrivateTeams, Migration: a.getListJoinPublicPrivateTeamsPermissionsMigration},
{Key: model.MigrationKeyRemovePermanentDeleteUser, Migration: a.removePermanentDeleteUserMigration},
{Key: model.MigrationKeyAddBotPermissions, Migration: a.getAddBotPermissionsMigration},

View file

@ -42,6 +42,7 @@ func GetMockStoreForSetupFunctions() *mocks.Store {
systemStore.On("GetByName", "content_flagging_setup_done").Return(&model.System{Name: "content_flagging_setup_done", Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyEmojiPermissionsSplit).Return(&model.System{Name: model.MigrationKeyEmojiPermissionsSplit, Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyWebhookPermissionsSplit).Return(&model.System{Name: model.MigrationKeyWebhookPermissionsSplit, Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyIntegrationsOwnPermissions).Return(&model.System{Name: model.MigrationKeyIntegrationsOwnPermissions, Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyListJoinPublicPrivateTeams).Return(&model.System{Name: model.MigrationKeyListJoinPublicPrivateTeams, Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyRemovePermanentDeleteUser).Return(&model.System{Name: model.MigrationKeyRemovePermanentDeleteUser, Value: "true"}, nil)
systemStore.On("GetByName", model.MigrationKeyAddBotPermissions).Return(&model.System{Name: model.MigrationKeyAddBotPermissions, Value: "true"}, nil)

View file

@ -500,16 +500,36 @@
"permission": "manage_incoming_webhooks",
"shouldHave": false
},
{
"roleName": "team_user",
"permission": "manage_own_incoming_webhooks",
"shouldHave": false
},
{
"roleName": "team_user",
"permission": "manage_outgoing_webhooks",
"shouldHave": false
},
{
"roleName": "team_user",
"permission": "manage_own_outgoing_webhooks",
"shouldHave": false
},
{
"roleName": "team_user",
"permission": "manage_slash_commands",
"shouldHave": false
},
{
"roleName": "team_user",
"permission": "manage_own_slash_commands",
"shouldHave": false
},
{
"roleName": "team_user",
"permission": "bypass_incoming_webhook_channel_lock",
"shouldHave": false
},
{
"roleName": "system_user",
"permission": "manage_oauth",
@ -522,16 +542,36 @@
"permission": "manage_incoming_webhooks",
"shouldHave": true
},
{
"roleName": "team_user",
"permission": "manage_own_incoming_webhooks",
"shouldHave": true
},
{
"roleName": "team_user",
"permission": "manage_outgoing_webhooks",
"shouldHave": true
},
{
"roleName": "team_user",
"permission": "manage_own_outgoing_webhooks",
"shouldHave": true
},
{
"roleName": "team_user",
"permission": "manage_slash_commands",
"shouldHave": true
},
{
"roleName": "team_user",
"permission": "manage_own_slash_commands",
"shouldHave": true
},
{
"roleName": "team_user",
"permission": "bypass_incoming_webhook_channel_lock",
"shouldHave": true
},
{
"roleName": "system_user",
"permission": "manage_oauth",

View file

@ -731,6 +731,10 @@
"id": "api.command.invite_people.sent",
"translation": "Email invite(s) sent"
},
{
"id": "api.command.move_command.creator_no_permission.app_error",
"translation": "No permission to move command"
},
{
"id": "api.command.team_mismatch.app_error",
"translation": "Unable to update commands across teams."

View file

@ -9,6 +9,7 @@ const (
MigrationKeyEmojiPermissionsSplit = "emoji_permissions_split"
MigrationKeyWebhookPermissionsSplit = "webhook_permissions_split"
MigrationKeyIntegrationsOwnPermissions = "integrations_own_permissions"
MigrationKeyListJoinPublicPrivateTeams = "list_join_public_private_teams"
MigrationKeyRemovePermanentDeleteUser = "remove_permanent_delete_user"
MigrationKeyAddBotPermissions = "add_bot_permissions"

View file

@ -69,11 +69,15 @@ var PermissionGetPublicLink *Permission
var PermissionManageWebhooks *Permission
var PermissionManageOthersWebhooks *Permission
var PermissionManageIncomingWebhooks *Permission
var PermissionManageOwnIncomingWebhooks *Permission
var PermissionManageOutgoingWebhooks *Permission
var PermissionManageOwnOutgoingWebhooks *Permission
var PermissionManageOthersIncomingWebhooks *Permission
var PermissionManageOthersOutgoingWebhooks *Permission
var PermissionManageOwnSlashCommands *Permission
var PermissionManageOAuth *Permission
var PermissionManageSystemWideOAuth *Permission
var PermissionBypassIncomingWebhookChannelLock *Permission
var PermissionManageEmojis *Permission
var PermissionManageOthersEmojis *Permission
var PermissionCreateEmojis *Permission
@ -431,12 +435,19 @@ func initializePermissions() {
"authentication.permissions.team_use_slash_commands.description",
PermissionScopeChannel,
}
// DEPRECATED - use PermissionManageOwnSlashCommands instead
PermissionManageSlashCommands = &Permission{
"manage_slash_commands",
"authentication.permissions.manage_slash_commands.name",
"authentication.permissions.manage_slash_commands.description",
PermissionScopeTeam,
}
PermissionManageOwnSlashCommands = &Permission{
"manage_own_slash_commands",
"authentication.permissions.manage_own_slash_commands.name",
"authentication.permissions.manage_own_slash_commands.description",
PermissionScopeTeam,
}
PermissionManageOthersSlashCommands = &Permission{
"manage_others_slash_commands",
"authentication.permissions.manage_others_slash_commands.name",
@ -668,18 +679,32 @@ func initializePermissions() {
"authentication.permissions.manage_others_webhooks.description",
PermissionScopeTeam,
}
// DEPRECATED - use PermissionManageOwnIncomingWebhooks instead
PermissionManageIncomingWebhooks = &Permission{
"manage_incoming_webhooks",
"authentication.permissions.manage_incoming_webhooks.name",
"authentication.permissions.manage_incoming_webhooks.description",
PermissionScopeTeam,
}
PermissionManageOwnIncomingWebhooks = &Permission{
"manage_own_incoming_webhooks",
"authentication.permissions.manage_own_incoming_webhooks.name",
"authentication.permissions.manage_own_incoming_webhooks.description",
PermissionScopeTeam,
}
// DEPRECATED - use PermissionManageOwnOutgoingWebhooks instead
PermissionManageOutgoingWebhooks = &Permission{
"manage_outgoing_webhooks",
"authentication.permissions.manage_outgoing_webhooks.name",
"authentication.permissions.manage_outgoing_webhooks.description",
PermissionScopeTeam,
}
PermissionManageOwnOutgoingWebhooks = &Permission{
"manage_own_outgoing_webhooks",
"authentication.permissions.manage_own_outgoing_webhooks.name",
"authentication.permissions.manage_own_outgoing_webhooks.description",
PermissionScopeTeam,
}
PermissionManageOthersIncomingWebhooks = &Permission{
"manage_others_incoming_webhooks",
"authentication.permissions.manage_others_incoming_webhooks.name",
@ -692,6 +717,12 @@ func initializePermissions() {
"authentication.permissions.manage_others_outgoing_webhooks.description",
PermissionScopeTeam,
}
PermissionBypassIncomingWebhookChannelLock = &Permission{
"bypass_incoming_webhook_channel_lock",
"authentication.permissions.bypass_incoming_webhook_channel_lock.name",
"authentication.permissions.bypass_incoming_webhook_channel_lock.description",
PermissionScopeTeam,
}
PermissionManageOAuth = &Permission{
"manage_oauth",
"authentication.permissions.manage_oauth.name",
@ -2414,7 +2445,6 @@ func initializePermissions() {
PermissionEditOtherUsers,
PermissionReadOtherUsersTeams,
PermissionGetPublicLink,
PermissionManageOAuth,
PermissionManageSystemWideOAuth,
PermissionCreateTeam,
PermissionListUsersWithoutTeam,
@ -2484,7 +2514,7 @@ func initializePermissions() {
TeamScopedPermissions := []*Permission{
PermissionInviteUser,
PermissionAddUserToTeam,
PermissionManageSlashCommands,
PermissionManageOwnSlashCommands,
PermissionManageOthersSlashCommands,
PermissionCreatePublicChannel,
PermissionCreatePrivateChannel,
@ -2492,10 +2522,11 @@ func initializePermissions() {
PermissionListTeamChannels,
PermissionJoinPublicChannels,
PermissionReadPublicChannel,
PermissionManageIncomingWebhooks,
PermissionManageOutgoingWebhooks,
PermissionManageOwnIncomingWebhooks,
PermissionManageOwnOutgoingWebhooks,
PermissionManageOthersIncomingWebhooks,
PermissionManageOthersOutgoingWebhooks,
PermissionBypassIncomingWebhookChannelLock,
PermissionCreateEmojis,
PermissionDeleteEmojis,
PermissionDeleteOthersEmojis,
@ -2562,6 +2593,10 @@ func initializePermissions() {
PermissionPermanentDeleteUser,
PermissionManageWebhooks,
PermissionManageOthersWebhooks,
PermissionManageIncomingWebhooks,
PermissionManageOutgoingWebhooks,
PermissionManageSlashCommands,
PermissionManageOAuth,
PermissionManageEmojis,
PermissionManageOthersEmojis,
PermissionSysconsoleReadAuthentication,

View file

@ -979,12 +979,13 @@ func MakeDefaultRoles() map[string]*Role {
PermissionImportTeam.Id,
PermissionManageTeamRoles.Id,
PermissionManageChannelRoles.Id,
PermissionManageOwnIncomingWebhooks.Id,
PermissionManageOthersIncomingWebhooks.Id,
PermissionManageOwnOutgoingWebhooks.Id,
PermissionManageOthersOutgoingWebhooks.Id,
PermissionManageSlashCommands.Id,
PermissionManageOwnSlashCommands.Id,
PermissionManageOthersSlashCommands.Id,
PermissionManageIncomingWebhooks.Id,
PermissionManageOutgoingWebhooks.Id,
PermissionBypassIncomingWebhookChannelLock.Id,
PermissionConvertPublicChannelToPrivate.Id,
PermissionConvertPrivateChannelToPublic.Id,
PermissionDeletePost.Id,

View file

@ -351,10 +351,29 @@ exports[`components/admin_console/permission_schemes_settings/permission_tree sh
Object {
"id": "integrations",
"permissions": Array [
"manage_incoming_webhooks",
"manage_outgoing_webhooks",
Object {
"id": "manage_incoming_webhooks_group",
"permissions": Array [
"manage_own_incoming_webhooks",
"manage_others_incoming_webhooks",
"bypass_incoming_webhook_channel_lock",
],
},
Object {
"id": "manage_outgoing_webhooks_group",
"permissions": Array [
"manage_own_outgoing_webhooks",
"manage_others_outgoing_webhooks",
],
},
"manage_oauth",
"manage_slash_commands",
Object {
"id": "manage_slash_commands_group",
"permissions": Array [
"manage_own_slash_commands",
"manage_others_slash_commands",
],
},
"create_emojis",
"delete_emojis",
"delete_others_emojis",
@ -542,10 +561,29 @@ exports[`components/admin_console/permission_schemes_settings/permission_tree sh
Object {
"id": "integrations",
"permissions": Array [
"manage_incoming_webhooks",
"manage_outgoing_webhooks",
Object {
"id": "manage_incoming_webhooks_group",
"permissions": Array [
"manage_own_incoming_webhooks",
"manage_others_incoming_webhooks",
"bypass_incoming_webhook_channel_lock",
],
},
Object {
"id": "manage_outgoing_webhooks_group",
"permissions": Array [
"manage_own_outgoing_webhooks",
"manage_others_outgoing_webhooks",
],
},
"manage_oauth",
"manage_slash_commands",
Object {
"id": "manage_slash_commands_group",
"permissions": Array [
"manage_own_slash_commands",
"manage_others_slash_commands",
],
},
"create_emojis",
"delete_emojis",
"delete_others_emojis",
@ -744,10 +782,29 @@ exports[`components/admin_console/permission_schemes_settings/permission_tree sh
Object {
"id": "integrations",
"permissions": Array [
"manage_incoming_webhooks",
"manage_outgoing_webhooks",
Object {
"id": "manage_incoming_webhooks_group",
"permissions": Array [
"manage_own_incoming_webhooks",
"manage_others_incoming_webhooks",
"bypass_incoming_webhook_channel_lock",
],
},
Object {
"id": "manage_outgoing_webhooks_group",
"permissions": Array [
"manage_own_outgoing_webhooks",
"manage_others_outgoing_webhooks",
],
},
"manage_oauth",
"manage_slash_commands",
Object {
"id": "manage_slash_commands_group",
"permissions": Array [
"manage_own_slash_commands",
"manage_others_slash_commands",
],
},
"create_emojis",
"delete_emojis",
"delete_others_emojis",
@ -946,10 +1003,29 @@ exports[`components/admin_console/permission_schemes_settings/permission_tree sh
Object {
"id": "integrations",
"permissions": Array [
"manage_incoming_webhooks",
"manage_outgoing_webhooks",
Object {
"id": "manage_incoming_webhooks_group",
"permissions": Array [
"manage_own_incoming_webhooks",
"manage_others_incoming_webhooks",
"bypass_incoming_webhook_channel_lock",
],
},
Object {
"id": "manage_outgoing_webhooks_group",
"permissions": Array [
"manage_own_outgoing_webhooks",
"manage_others_outgoing_webhooks",
],
},
"manage_oauth",
"manage_slash_commands",
Object {
"id": "manage_slash_commands_group",
"permissions": Array [
"manage_own_slash_commands",
"manage_others_slash_commands",
],
},
"create_emojis",
"delete_emojis",
"delete_others_emojis",
@ -1148,10 +1224,29 @@ exports[`components/admin_console/permission_schemes_settings/permission_tree sh
Object {
"id": "integrations",
"permissions": Array [
"manage_incoming_webhooks",
"manage_outgoing_webhooks",
Object {
"id": "manage_incoming_webhooks_group",
"permissions": Array [
"manage_own_incoming_webhooks",
"manage_others_incoming_webhooks",
"bypass_incoming_webhook_channel_lock",
],
},
Object {
"id": "manage_outgoing_webhooks_group",
"permissions": Array [
"manage_own_outgoing_webhooks",
"manage_others_outgoing_webhooks",
],
},
"manage_oauth",
"manage_slash_commands",
Object {
"id": "manage_slash_commands_group",
"permissions": Array [
"manage_own_slash_commands",
"manage_others_slash_commands",
],
},
"create_emojis",
"delete_emojis",
"delete_others_emojis",
@ -1357,10 +1452,29 @@ exports[`components/admin_console/permission_schemes_settings/permission_tree sh
Object {
"id": "integrations",
"permissions": Array [
"manage_incoming_webhooks",
"manage_outgoing_webhooks",
Object {
"id": "manage_incoming_webhooks_group",
"permissions": Array [
"manage_own_incoming_webhooks",
"manage_others_incoming_webhooks",
"bypass_incoming_webhook_channel_lock",
],
},
Object {
"id": "manage_outgoing_webhooks_group",
"permissions": Array [
"manage_own_outgoing_webhooks",
"manage_others_outgoing_webhooks",
],
},
"manage_oauth",
"manage_slash_commands",
Object {
"id": "manage_slash_commands_group",
"permissions": Array [
"manage_own_slash_commands",
"manage_others_slash_commands",
],
},
"create_emojis",
"delete_emojis",
"delete_others_emojis",

View file

@ -213,11 +213,30 @@ export default class PermissionsTree extends React.PureComponent<Props, State> {
const sharedChannelsGroup = this.groups[9];
const customGroupsGroup = this.groups[10];
if (config.EnableIncomingWebhooks === 'true' && !integrationsGroup.permissions.includes(Permissions.MANAGE_INCOMING_WEBHOOKS)) {
integrationsGroup.permissions.push(Permissions.MANAGE_INCOMING_WEBHOOKS);
if (config.EnableIncomingWebhooks === 'true') {
const incomingWebhookGroup = {
id: 'manage_incoming_webhooks_group',
permissions: [
Permissions.MANAGE_OWN_INCOMING_WEBHOOKS,
Permissions.MANAGE_OTHERS_INCOMING_WEBHOOKS,
Permissions.BYPASS_INCOMING_WEBHOOK_CHANNEL_LOCK,
],
};
if (!integrationsGroup.permissions.some((p: any) => p.id === 'manage_incoming_webhooks_group')) {
integrationsGroup.permissions.push(incomingWebhookGroup);
}
}
if (config.EnableOutgoingWebhooks === 'true' && !integrationsGroup.permissions.includes(Permissions.MANAGE_OUTGOING_WEBHOOKS)) {
integrationsGroup.permissions.push(Permissions.MANAGE_OUTGOING_WEBHOOKS);
if (config.EnableOutgoingWebhooks === 'true') {
const outgoingWebhookGroup = {
id: 'manage_outgoing_webhooks_group',
permissions: [
Permissions.MANAGE_OWN_OUTGOING_WEBHOOKS,
Permissions.MANAGE_OTHERS_OUTGOING_WEBHOOKS,
],
};
if (!integrationsGroup.permissions.some((p: any) => p.id === 'manage_outgoing_webhooks_group')) {
integrationsGroup.permissions.push(outgoingWebhookGroup);
}
}
if (config.EnableOAuthServiceProvider === 'true' && !integrationsGroup.permissions.includes(Permissions.MANAGE_OAUTH)) {
integrationsGroup.permissions.push(Permissions.MANAGE_OAUTH);
@ -225,8 +244,17 @@ export default class PermissionsTree extends React.PureComponent<Props, State> {
if (config.EnableOutgoingOAuthConnections === 'true' && !integrationsGroup.permissions.includes(Permissions.MANAGE_OUTGOING_OAUTH_CONNECTIONS)) {
integrationsGroup.permissions.push(Permissions.MANAGE_OUTGOING_OAUTH_CONNECTIONS);
}
if (config.EnableCommands === 'true' && !integrationsGroup.permissions.includes(Permissions.MANAGE_SLASH_COMMANDS)) {
integrationsGroup.permissions.push(Permissions.MANAGE_SLASH_COMMANDS);
if (config.EnableCommands === 'true') {
const slashCommandGroup = {
id: 'manage_slash_commands_group',
permissions: [
Permissions.MANAGE_OWN_SLASH_COMMANDS,
Permissions.MANAGE_OTHERS_SLASH_COMMANDS,
],
};
if (!integrationsGroup.permissions.some((p: any) => p.id === 'manage_slash_commands_group')) {
integrationsGroup.permissions.push(slashCommandGroup);
}
}
if (config.EnableCustomEmoji === 'true' && !integrationsGroup.permissions.includes(Permissions.CREATE_EMOJIS)) {
integrationsGroup.permissions.push(Permissions.CREATE_EMOJIS);

View file

@ -105,6 +105,46 @@ export const groupRolesStrings: Record<string, Record<string, MessageDescriptor>
defaultMessage: 'Edit own and others\' posts.',
},
}),
manage_incoming_webhooks_group: defineMessages({
name: {
id: 'admin.permissions.group.manage_incoming_webhooks.name',
defaultMessage: 'Manage Incoming Webhooks',
},
description: {
id: 'admin.permissions.group.manage_incoming_webhooks.description',
defaultMessage: 'Manage own and others\' incoming webhooks.',
},
}),
manage_outgoing_webhooks_group: defineMessages({
name: {
id: 'admin.permissions.group.manage_outgoing_webhooks.name',
defaultMessage: 'Manage Outgoing Webhooks',
},
description: {
id: 'admin.permissions.group.manage_outgoing_webhooks.description',
defaultMessage: 'Manage own and others\' outgoing webhooks.',
},
}),
manage_slash_commands_group: defineMessages({
name: {
id: 'admin.permissions.group.manage_slash_commands.name',
defaultMessage: 'Manage Slash Commands',
},
description: {
id: 'admin.permissions.group.manage_slash_commands.description',
defaultMessage: 'Manage own and others\' slash commands.',
},
}),
manage_oauth_group: defineMessages({
name: {
id: 'admin.permissions.group.manage_oauth.name',
defaultMessage: 'Manage OAuth Applications',
},
description: {
id: 'admin.permissions.group.manage_oauth.description',
defaultMessage: 'Manage own and others\' OAuth 2.0 applications.',
},
}),
teams_team_scope: defineMessages({
name: {
id: 'admin.permissions.group.teams_team_scope.name',

View file

@ -288,11 +288,31 @@ export const permissionRolesStrings: Record<string, Record<string, MessageDescri
manage_slash_commands: defineMessages({
name: {
id: 'admin.permissions.permission.manage_slash_commands.name',
defaultMessage: 'Manage Slash Commands',
defaultMessage: 'Manage Others\'',
},
description: {
id: 'admin.permissions.permission.manage_slash_commands.description',
defaultMessage: 'Create, edit and delete custom slash commands.',
defaultMessage: 'Create, edit and delete slash commands owned by other users.',
},
}),
manage_own_slash_commands: defineMessages({
name: {
id: 'admin.permissions.permission.manage_own_slash_commands.name',
defaultMessage: 'Manage Own',
},
description: {
id: 'admin.permissions.permission.manage_own_slash_commands.description',
defaultMessage: 'Create, edit and delete your own slash commands.',
},
}),
manage_others_slash_commands: defineMessages({
name: {
id: 'admin.permissions.permission.manage_others_slash_commands.name',
defaultMessage: 'Manage Others\'',
},
description: {
id: 'admin.permissions.permission.manage_others_slash_commands.description',
defaultMessage: 'Create, edit and delete slash commands owned by other users.',
},
}),
manage_system: defineMessages({
@ -328,21 +348,71 @@ export const permissionRolesStrings: Record<string, Record<string, MessageDescri
manage_incoming_webhooks: defineMessages({
name: {
id: 'admin.permissions.permission.manage_incoming_webhooks.name',
defaultMessage: 'Manage Incoming Webhooks',
defaultMessage: 'Manage Others\'',
},
description: {
id: 'admin.permissions.permission.manage_incoming_webhooks.description',
defaultMessage: 'Create, edit, and delete incoming webhooks.',
defaultMessage: 'Create, edit, and delete incoming webhooks owned by other users.',
},
}),
manage_own_incoming_webhooks: defineMessages({
name: {
id: 'admin.permissions.permission.manage_own_incoming_webhooks.name',
defaultMessage: 'Manage Own',
},
description: {
id: 'admin.permissions.permission.manage_own_incoming_webhooks.description',
defaultMessage: 'Create, edit, and delete your own incoming webhooks.',
},
}),
manage_others_incoming_webhooks: defineMessages({
name: {
id: 'admin.permissions.permission.manage_others_incoming_webhooks.name',
defaultMessage: 'Manage Others\'',
},
description: {
id: 'admin.permissions.permission.manage_others_incoming_webhooks.description',
defaultMessage: 'Create, edit, and delete incoming webhooks owned by other users.',
},
}),
bypass_incoming_webhook_channel_lock: defineMessages({
name: {
id: 'admin.permissions.permission.bypass_incoming_webhook_channel_lock.name',
defaultMessage: 'Bypass Channel Lock',
},
description: {
id: 'admin.permissions.permission.bypass_incoming_webhook_channel_lock.description',
defaultMessage: 'Allow incoming webhooks to post to any channel without requiring a locked default channel.',
},
}),
manage_outgoing_webhooks: defineMessages({
name: {
id: 'admin.permissions.permission.manage_outgoing_webhooks.name',
defaultMessage: 'Manage Outgoing Webhooks',
defaultMessage: 'Manage Others\'',
},
description: {
id: 'admin.permissions.permission.manage_outgoing_webhooks.description',
defaultMessage: 'Create, edit, and delete outgoing webhooks.',
defaultMessage: 'Create, edit, and delete outgoing webhooks owned by other users.',
},
}),
manage_own_outgoing_webhooks: defineMessages({
name: {
id: 'admin.permissions.permission.manage_own_outgoing_webhooks.name',
defaultMessage: 'Manage Own',
},
description: {
id: 'admin.permissions.permission.manage_own_outgoing_webhooks.description',
defaultMessage: 'Create, edit, and delete your own outgoing webhooks.',
},
}),
manage_others_outgoing_webhooks: defineMessages({
name: {
id: 'admin.permissions.permission.manage_others_outgoing_webhooks.name',
defaultMessage: 'Manage Others\'',
},
description: {
id: 'admin.permissions.permission.manage_others_outgoing_webhooks.description',
defaultMessage: 'Create, edit, and delete outgoing webhooks owned by other users.',
},
}),
permanent_delete_user: defineMessages({

View file

@ -56,7 +56,7 @@ export default class BackstageSidebar extends React.PureComponent<Props> {
if (this.props.enableIncomingWebhooks) {
incomingWebhooks = (
<TeamPermissionGate
permissions={[Permissions.MANAGE_INCOMING_WEBHOOKS]}
permissions={[Permissions.MANAGE_INCOMING_WEBHOOKS, Permissions.MANAGE_OWN_INCOMING_WEBHOOKS]}
teamId={this.props.team.id}
>
<BackstageSection
@ -78,7 +78,7 @@ export default class BackstageSidebar extends React.PureComponent<Props> {
if (this.props.enableOutgoingWebhooks) {
outgoingWebhooks = (
<TeamPermissionGate
permissions={[Permissions.MANAGE_OUTGOING_WEBHOOKS]}
permissions={[Permissions.MANAGE_OUTGOING_WEBHOOKS, Permissions.MANAGE_OWN_OUTGOING_WEBHOOKS]}
teamId={this.props.team.id}
>
<BackstageSection
@ -100,7 +100,7 @@ export default class BackstageSidebar extends React.PureComponent<Props> {
if (this.props.enableCommands) {
commands = (
<TeamPermissionGate
permissions={[Permissions.MANAGE_SLASH_COMMANDS]}
permissions={[Permissions.MANAGE_SLASH_COMMANDS, Permissions.MANAGE_OWN_SLASH_COMMANDS]}
teamId={this.props.team.id}
>
<BackstageSection

View file

@ -38,7 +38,15 @@ function mapStateToProps(state: GlobalState) {
}
}
const canManageTeamIntegrations = (haveITeamPermission(state, team?.id, Permissions.MANAGE_SLASH_COMMANDS) || haveITeamPermission(state, team?.id, Permissions.MANAGE_OAUTH) || haveITeamPermission(state, team?.id, Permissions.MANAGE_INCOMING_WEBHOOKS) || haveITeamPermission(state, team?.id, Permissions.MANAGE_OUTGOING_WEBHOOKS));
const canManageTeamIntegrations = (
haveITeamPermission(state, team?.id, Permissions.MANAGE_SLASH_COMMANDS) ||
haveITeamPermission(state, team?.id, Permissions.MANAGE_OWN_SLASH_COMMANDS) ||
haveITeamPermission(state, team?.id, Permissions.MANAGE_INCOMING_WEBHOOKS) ||
haveITeamPermission(state, team?.id, Permissions.MANAGE_OWN_INCOMING_WEBHOOKS) ||
haveITeamPermission(state, team?.id, Permissions.MANAGE_OUTGOING_WEBHOOKS) ||
haveITeamPermission(state, team?.id, Permissions.MANAGE_OWN_OUTGOING_WEBHOOKS) ||
haveISystemPermission(state, {permission: Permissions.MANAGE_OAUTH})
);
const canManageSystemBots = (haveISystemPermission(state, {permission: Permissions.MANAGE_BOTS}) || haveISystemPermission(state, {permission: Permissions.MANAGE_OTHERS_BOTS}));
const canManageIntegrations = canManageTeamIntegrations || canManageSystemBots;

View file

@ -46,7 +46,15 @@ function mapStateToProps(state: GlobalState) {
const enableOAuthServiceProvider = config.EnableOAuthServiceProvider === 'true';
const enableOutgoingWebhooks = config.EnableOutgoingWebhooks === 'true';
const enablePluginMarketplace = isMarketplaceEnabled(state);
const canManageTeamIntegrations = (haveICurrentTeamPermission(state, Permissions.MANAGE_SLASH_COMMANDS) || haveICurrentTeamPermission(state, Permissions.MANAGE_OAUTH) || haveICurrentTeamPermission(state, Permissions.MANAGE_INCOMING_WEBHOOKS) || haveICurrentTeamPermission(state, Permissions.MANAGE_OUTGOING_WEBHOOKS));
const canManageTeamIntegrations = (
haveICurrentTeamPermission(state, Permissions.MANAGE_SLASH_COMMANDS) ||
haveICurrentTeamPermission(state, Permissions.MANAGE_OWN_SLASH_COMMANDS) ||
haveICurrentTeamPermission(state, Permissions.MANAGE_INCOMING_WEBHOOKS) ||
haveICurrentTeamPermission(state, Permissions.MANAGE_OWN_INCOMING_WEBHOOKS) ||
haveICurrentTeamPermission(state, Permissions.MANAGE_OUTGOING_WEBHOOKS) ||
haveICurrentTeamPermission(state, Permissions.MANAGE_OWN_OUTGOING_WEBHOOKS) ||
haveISystemPermission(state, {permission: Permissions.MANAGE_OAUTH})
);
const canManageSystemBots = (haveISystemPermission(state, {permission: Permissions.MANAGE_BOTS}) || haveISystemPermission(state, {permission: Permissions.MANAGE_OTHERS_BOTS}));
const canManageIntegrations = canManageTeamIntegrations || canManageSystemBots;
const step = getInt(state, OnboardingTaskCategory, OnboardingTasksName.VISIT_SYSTEM_CONSOLE, 0);

View file

@ -530,6 +530,271 @@ exports[`components/integrations/AbstractIncomingWebhook should match snapshot 1
</div>
`;
exports[`components/integrations/AbstractIncomingWebhook should match snapshot when channelLocked is true 1`] = `
<div
className="backstage-content"
>
<BackstageHeader>
<Link
to="/team_name/integrations/incoming_webhooks"
>
<MemoizedFormattedMessage
defaultMessage="Incoming Webhooks"
id="incoming_webhooks.header"
/>
</Link>
<MemoizedFormattedMessage
defaultMessage="Header"
id="header_id"
/>
</BackstageHeader>
<div
className="backstage-form"
>
<form
className="form-horizontal"
onSubmit={[Function]}
>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
htmlFor="displayName"
>
<MemoizedFormattedMessage
defaultMessage="Title"
id="add_incoming_webhook.displayName"
/>
</label>
<div
className="col-md-5 col-sm-8"
>
<input
className="form-control"
id="displayName"
maxLength={64}
onChange={[Function]}
type="text"
value="testIncomingWebhook"
/>
<div
className="form__help"
>
<MemoizedFormattedMessage
defaultMessage="Specify a title, of up to 64 characters, for the webhook settings page."
id="add_incoming_webhook.displayName.help"
/>
</div>
</div>
</div>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
htmlFor="description"
>
<MemoizedFormattedMessage
defaultMessage="Description"
id="add_incoming_webhook.description"
/>
</label>
<div
className="col-md-5 col-sm-8"
>
<input
className="form-control"
id="description"
maxLength={500}
onChange={[Function]}
type="text"
value="testing"
/>
<div
className="form__help"
>
<MemoizedFormattedMessage
defaultMessage="Describe your incoming webhook."
id="add_incoming_webhook.description.help"
/>
</div>
</div>
</div>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
htmlFor="channelId"
>
<MemoizedFormattedMessage
defaultMessage="Channel"
id="add_incoming_webhook.channel"
/>
</label>
<div
className="col-md-5 col-sm-8"
>
<Connect(Component)
onChange={[Function]}
selectDm={false}
selectOpen={true}
selectPrivate={true}
value="88cxd9wpzpbpfp8pad78xj75pr"
/>
<div
className="form__help"
>
<MemoizedFormattedMessage
defaultMessage="This is the default public or private channel that receives the webhook payloads. When setting up the webhook, you must belong to the private channel."
id="add_incoming_webhook.channel.help"
/>
</div>
</div>
</div>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
htmlFor="channelLocked"
>
<MemoizedFormattedMessage
defaultMessage="Lock to this channel"
id="add_incoming_webhook.channelLocked"
/>
</label>
<div
className="col-md-5 col-sm-8 checkbox"
>
<input
checked={false}
id="channelLocked"
onChange={[Function]}
type="checkbox"
/>
<div
className="form__help"
>
<MemoizedFormattedMessage
defaultMessage="If set, the incoming webhook can post only to the selected channel."
id="add_incoming_webhook.channelLocked.help"
/>
</div>
</div>
</div>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
htmlFor="username"
>
<MemoizedFormattedMessage
defaultMessage="Username"
id="add_incoming_webhook.username"
/>
</label>
<div
className="col-md-5 col-sm-8"
>
<input
className="form-control"
id="username"
maxLength={22}
onChange={[Function]}
type="text"
value=""
/>
<div
className="form__help"
>
<MemoizedFormattedMessage
defaultMessage="Specify the username this integration will post as. Usernames can be up to 22 characters, and can contain lowercase letters, numbers and the symbols \\\\\\"-\\\\\\", \\\\\\"_\\\\\\", and \\\\\\".\\\\\\". If left blank, the name specified by the webhook creator is used."
id="add_incoming_webhook.username.help"
/>
</div>
</div>
</div>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
htmlFor="iconURL"
>
<MemoizedFormattedMessage
defaultMessage="Profile Picture"
id="add_incoming_webhook.icon_url"
/>
</label>
<div
className="col-md-5 col-sm-8"
>
<input
className="form-control"
id="iconURL"
maxLength={1024}
onChange={[Function]}
type="text"
value=""
/>
<div
className="form__help"
>
<MemoizedFormattedMessage
defaultMessage="Enter the URL of a .png or .jpg file for the profile picture of this integration when posting. The file should be at least 128 pixels by 128 pixels. If left blank, the profile picture specified by the webhook creator is used."
id="add_incoming_webhook.icon_url.help"
/>
</div>
</div>
</div>
<div
className="backstage-form__footer"
>
<Memo(FormError)
errors={
Array [
"",
null,
]
}
type="backstage"
/>
<Link
className="btn btn-tertiary"
to="/team_name/integrations/incoming_webhooks"
>
<MemoizedFormattedMessage
defaultMessage="Cancel"
id="add_incoming_webhook.cancel"
/>
</Link>
<Memo(SpinnerButton)
className="btn btn-primary"
id="saveWebhook"
onClick={[Function]}
spinning={false}
spinningText={
Object {
"defaultMessage": "Loading",
"id": "loading_id",
}
}
type="submit"
>
<MemoizedFormattedMessage
defaultMessage="Footer"
id="footer_id"
/>
</Memo(SpinnerButton)>
</div>
</form>
</div>
</div>
`;
exports[`components/integrations/AbstractIncomingWebhook should match snapshot, displays client error when no initial hook 1`] = `
<div
className="backstage-content"

View file

@ -67,6 +67,7 @@ describe('components/integrations/AbstractIncomingWebhook', () => {
enablePostUsernameOverride,
enablePostIconOverride,
action,
canBypassChannelLock: true,
};
test('should match snapshot', () => {
@ -175,4 +176,10 @@ describe('components/integrations/AbstractIncomingWebhook', () => {
expect(wrapper.state('iconURL')).toBe(newIconURL);
});
test('should match snapshot when channelLocked is true', () => {
const props = {...requiredProps, channelLocked: true};
const wrapper = shallow(<AbstractIncomingWebhook {...props}/>);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -69,6 +69,11 @@ interface Props {
*/
enablePostIconOverride: boolean;
/**
* Whether the user can bypass the channel lock requirement.
*/
canBypassChannelLock?: boolean;
/**
* The async function to run when the action button is pressed
*/
@ -280,31 +285,33 @@ export default class AbstractIncomingWebhook extends PureComponent<Props, State>
</div>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='channelLocked'
>
<FormattedMessage
id='add_incoming_webhook.channelLocked'
defaultMessage='Lock to this channel'
/>
</label>
<div className='col-md-5 col-sm-8 checkbox'>
<input
id='channelLocked'
type='checkbox'
checked={this.state.channelLocked}
onChange={this.updateChannelLocked}
/>
<div className='form__help'>
{ this.props.canBypassChannelLock &&
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='channelLocked'
>
<FormattedMessage
id='add_incoming_webhook.channelLocked.help'
defaultMessage='If set, the incoming webhook can post only to the selected channel.'
id='add_incoming_webhook.channelLocked'
defaultMessage='Lock to this channel'
/>
</label>
<div className='col-md-5 col-sm-8 checkbox'>
<input
id='channelLocked'
type='checkbox'
checked={this.state.channelLocked}
onChange={this.updateChannelLocked}
/>
<div className='form__help'>
<FormattedMessage
id='add_incoming_webhook.channelLocked.help'
defaultMessage='If set, the incoming webhook can post only to the selected channel.'
/>
</div>
</div>
</div>
</div>
}
{ this.props.enablePostUsernameOverride &&
<div className='form-group'>
<label

View file

@ -45,6 +45,11 @@ type Props = {
*/
enablePostIconOverride: boolean;
/**
* Whether the user can bypass the channel lock requirement.
*/
canBypassChannelLock?: boolean;
actions: {
/**
@ -58,6 +63,7 @@ const AddIncomingWebhook = ({
team,
enablePostUsernameOverride,
enablePostIconOverride,
canBypassChannelLock,
actions,
}: Props) => {
const [serverError, setServerError] = useState('');
@ -83,6 +89,7 @@ const AddIncomingWebhook = ({
loading={messages.loading}
enablePostUsernameOverride={enablePostUsernameOverride}
enablePostIconOverride={enablePostIconOverride}
canBypassChannelLock={canBypassChannelLock}
action={addIncomingHook}
serverError={serverError}
/>

View file

@ -8,7 +8,9 @@ import type {Dispatch} from 'redux';
import type {GlobalState} from '@mattermost/types/store';
import {createIncomingHook} from 'mattermost-redux/actions/integrations';
import {Permissions} from 'mattermost-redux/constants';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {haveICurrentTeamPermission} from 'mattermost-redux/selectors/entities/roles';
import AddIncomingWebhook from './add_incoming_webhook';
@ -16,10 +18,12 @@ function mapStateToProps(state: GlobalState) {
const config = getConfig(state);
const enablePostUsernameOverride = config.EnablePostUsernameOverride === 'true';
const enablePostIconOverride = config.EnablePostIconOverride === 'true';
const canBypassChannelLock = haveICurrentTeamPermission(state, Permissions.BYPASS_INCOMING_WEBHOOK_CHANNEL_LOCK);
return {
enablePostUsernameOverride,
enablePostIconOverride,
canBypassChannelLock,
};
}

View file

@ -61,6 +61,11 @@ type Props = {
*/
enablePostIconOverride: boolean;
/**
* Whether the user can bypass the channel lock requirement.
*/
canBypassChannelLock?: boolean;
actions: {
/**
@ -139,6 +144,7 @@ export default class EditIncomingWebhook extends React.PureComponent<Props, Stat
loading={messages.loading}
enablePostUsernameOverride={this.props.enablePostUsernameOverride}
enablePostIconOverride={this.props.enablePostIconOverride}
canBypassChannelLock={this.props.canBypassChannelLock}
action={this.editIncomingHook}
serverError={this.state.serverError}
initialHook={this.props.hook}

View file

@ -8,7 +8,9 @@ import type {Dispatch} from 'redux';
import type {GlobalState} from '@mattermost/types/store';
import {getIncomingHook, updateIncomingHook} from 'mattermost-redux/actions/integrations';
import {Permissions} from 'mattermost-redux/constants';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {haveICurrentTeamPermission} from 'mattermost-redux/selectors/entities/roles';
import EditIncomingWebhook from './edit_incoming_webhook';
@ -22,6 +24,7 @@ function mapStateToProps(state: GlobalState, ownProps: Props) {
const enablePostUsernameOverride = config.EnablePostUsernameOverride === 'true';
const enablePostIconOverride = config.EnablePostIconOverride === 'true';
const hookId = (new URLSearchParams(ownProps.location.search)).get('id') || '';
const canBypassChannelLock = haveICurrentTeamPermission(state, Permissions.BYPASS_INCOMING_WEBHOOK_CHANNEL_LOCK);
return {
hookId,
@ -29,6 +32,7 @@ function mapStateToProps(state: GlobalState, ownProps: Props) {
enableIncomingWebhooks,
enablePostUsernameOverride,
enablePostIconOverride,
canBypassChannelLock,
};
}

View file

@ -49,7 +49,7 @@ export default class Integrations extends React.PureComponent <Props> {
options.push(
<TeamPermissionGate
teamId={this.props.team.id}
permissions={[Permissions.MANAGE_INCOMING_WEBHOOKS]}
permissions={[Permissions.MANAGE_INCOMING_WEBHOOKS, Permissions.MANAGE_OWN_INCOMING_WEBHOOKS]}
key='incomingWebhookPermission'
>
<IntegrationOption
@ -77,7 +77,7 @@ export default class Integrations extends React.PureComponent <Props> {
options.push(
<TeamPermissionGate
teamId={this.props.team.id}
permissions={[Permissions.MANAGE_OUTGOING_WEBHOOKS]}
permissions={[Permissions.MANAGE_OUTGOING_WEBHOOKS, Permissions.MANAGE_OWN_OUTGOING_WEBHOOKS]}
key='outgoingWebhookPermission'
>
<IntegrationOption
@ -105,7 +105,7 @@ export default class Integrations extends React.PureComponent <Props> {
options.push(
<TeamPermissionGate
teamId={this.props.team.id}
permissions={[Permissions.MANAGE_SLASH_COMMANDS]}
permissions={[Permissions.MANAGE_SLASH_COMMANDS, Permissions.MANAGE_OWN_SLASH_COMMANDS]}
key='commandPermission'
>
<IntegrationOption

View file

@ -1871,6 +1871,12 @@
"admin.permissions.group.guest_use_group_mentions.name": "Group Mentions",
"admin.permissions.group.integrations.description": "Manage OAuth 2.0, slash commands, webhooks and emoji.",
"admin.permissions.group.integrations.name": "Integrations & Customizations",
"admin.permissions.group.manage_incoming_webhooks.description": "Manage own and others' incoming webhooks.",
"admin.permissions.group.manage_incoming_webhooks.name": "Manage Incoming Webhooks",
"admin.permissions.group.manage_oauth.description": "Manage own and others' OAuth 2.0 applications.",
"admin.permissions.group.manage_oauth.name": "Manage OAuth Applications",
"admin.permissions.group.manage_outgoing_webhooks.description": "Manage own and others' outgoing webhooks.",
"admin.permissions.group.manage_outgoing_webhooks.name": "Manage Outgoing Webhooks",
"admin.permissions.group.manage_private_channel_bookmarks.description": "Add, edit, delete and sort bookmarks",
"admin.permissions.group.manage_private_channel_bookmarks.name": "Manage Bookmarks",
"admin.permissions.group.manage_private_channel_members_and_read_groups.description": "Add and remove private channel members (including channel admins).",
@ -1881,6 +1887,8 @@
"admin.permissions.group.manage_public_channel_members_and_read_groups.name": "Manage Channel Members",
"admin.permissions.group.manage_shared_channels.description": "Manage Shared Channels",
"admin.permissions.group.manage_shared_channels.name": "Shared Channels",
"admin.permissions.group.manage_slash_commands.description": "Manage own and others' slash commands.",
"admin.permissions.group.manage_slash_commands.name": "Manage Slash Commands",
"admin.permissions.group.playbook_private.description": "Manage private playbooks.",
"admin.permissions.group.playbook_private.name": "Manage Private Playbooks",
"admin.permissions.group.playbook_public.description": "Manage public playbooks.",
@ -1907,6 +1915,8 @@
"admin.permissions.loadMoreSchemes": "Load more schemes",
"admin.permissions.permission.assign_system_admin_role.description": "Assign system admin role",
"admin.permissions.permission.assign_system_admin_role.name": "Assign system admin role",
"admin.permissions.permission.bypass_incoming_webhook_channel_lock.description": "Allow incoming webhooks to post to any channel without requiring a locked default channel.",
"admin.permissions.permission.bypass_incoming_webhook_channel_lock.name": "Bypass Channel Lock",
"admin.permissions.permission.convert_private_channel_to_public.description": "Convert private channels to public",
"admin.permissions.permission.convert_private_channel_to_public.name": "Convert to public",
"admin.permissions.permission.convert_public_channel_to_private.description": "Convert public channels to private",
@ -1965,16 +1975,28 @@
"admin.permissions.permission.manage_channel_roles.name": "Manage channel roles",
"admin.permissions.permission.manage_custom_group_members.description": "Add and remove custom group members.",
"admin.permissions.permission.manage_custom_group_members.name": "Manage members",
"admin.permissions.permission.manage_incoming_webhooks.description": "Create, edit, and delete incoming webhooks.",
"admin.permissions.permission.manage_incoming_webhooks.name": "Manage Incoming Webhooks",
"admin.permissions.permission.manage_incoming_webhooks.description": "Create, edit, and delete incoming webhooks owned by other users.",
"admin.permissions.permission.manage_incoming_webhooks.name": "Manage Others'",
"admin.permissions.permission.manage_jobs.description": "Manage jobs",
"admin.permissions.permission.manage_jobs.name": "Manage jobs",
"admin.permissions.permission.manage_oauth.description": "Create, edit and delete OAuth 2.0 application tokens.",
"admin.permissions.permission.manage_oauth.name": "Manage OAuth Applications",
"admin.permissions.permission.manage_oauth.name": "Manage Others'",
"admin.permissions.permission.manage_others_incoming_webhooks.description": "Create, edit, and delete incoming webhooks owned by other users.",
"admin.permissions.permission.manage_others_incoming_webhooks.name": "Manage Others'",
"admin.permissions.permission.manage_others_outgoing_webhooks.description": "Create, edit, and delete outgoing webhooks owned by other users.",
"admin.permissions.permission.manage_others_outgoing_webhooks.name": "Manage Others'",
"admin.permissions.permission.manage_others_slash_commands.description": "Create, edit and delete slash commands owned by other users.",
"admin.permissions.permission.manage_others_slash_commands.name": "Manage Others'",
"admin.permissions.permission.manage_outgoing_oauth_connections.description": "Create, edit, and delete outgoing OAuth credentials.",
"admin.permissions.permission.manage_outgoing_oauth_connections.name": "Manage Outgoing OAuth Credentials",
"admin.permissions.permission.manage_outgoing_webhooks.description": "Create, edit, and delete outgoing webhooks.",
"admin.permissions.permission.manage_outgoing_webhooks.name": "Manage Outgoing Webhooks",
"admin.permissions.permission.manage_outgoing_webhooks.description": "Create, edit, and delete outgoing webhooks owned by other users.",
"admin.permissions.permission.manage_outgoing_webhooks.name": "Manage Others'",
"admin.permissions.permission.manage_own_incoming_webhooks.description": "Create, edit, and delete your own incoming webhooks.",
"admin.permissions.permission.manage_own_incoming_webhooks.name": "Manage Own",
"admin.permissions.permission.manage_own_outgoing_webhooks.description": "Create, edit, and delete your own outgoing webhooks.",
"admin.permissions.permission.manage_own_outgoing_webhooks.name": "Manage Own",
"admin.permissions.permission.manage_own_slash_commands.description": "Create, edit and delete your own slash commands.",
"admin.permissions.permission.manage_own_slash_commands.name": "Manage Own",
"admin.permissions.permission.manage_private_channel_banner.description": "Enable, disable and edit channel banner.",
"admin.permissions.permission.manage_private_channel_banner.name": "Manage Channel Banner",
"admin.permissions.permission.manage_private_channel_properties.description": "Update private channel names, headers and purposes.",
@ -1989,8 +2011,8 @@
"admin.permissions.permission.manage_secure_connections.name": "Manage Secure Connections",
"admin.permissions.permission.manage_shared_channels.description": "Share, unshare and invite another instance to sync with a shared channel",
"admin.permissions.permission.manage_shared_channels.name": "Manage Shared Channels",
"admin.permissions.permission.manage_slash_commands.description": "Create, edit and delete custom slash commands.",
"admin.permissions.permission.manage_slash_commands.name": "Manage Slash Commands",
"admin.permissions.permission.manage_slash_commands.description": "Create, edit and delete slash commands owned by other users.",
"admin.permissions.permission.manage_slash_commands.name": "Manage Others'",
"admin.permissions.permission.manage_system.description": "Manage system",
"admin.permissions.permission.manage_system.name": "Manage system",
"admin.permissions.permission.manage_team_roles.description": "Manage team roles",

View file

@ -5,6 +5,7 @@ const values = {
INVITE_USER: 'invite_user',
ADD_USER_TO_TEAM: 'add_user_to_team',
MANAGE_SLASH_COMMANDS: 'manage_slash_commands',
MANAGE_OWN_SLASH_COMMANDS: 'manage_own_slash_commands',
MANAGE_OTHERS_SLASH_COMMANDS: 'manage_others_slash_commands',
CREATE_PUBLIC_CHANNEL: 'create_public_channel',
CREATE_PRIVATE_CHANNEL: 'create_private_channel',
@ -47,12 +48,14 @@ const values = {
MANAGE_WEBHOOKS: 'manage_webhooks',
MANAGE_OTHERS_WEBHOOKS: 'manage_others_webhooks',
MANAGE_INCOMING_WEBHOOKS: 'manage_incoming_webhooks',
MANAGE_OWN_INCOMING_WEBHOOKS: 'manage_own_incoming_webhooks',
BYPASS_INCOMING_WEBHOOK_CHANNEL_LOCK: 'bypass_incoming_webhook_channel_lock',
MANAGE_OTHERS_INCOMING_WEBHOOKS: 'manage_others_incoming_webhooks',
MANAGE_OUTGOING_WEBHOOKS: 'manage_outgoing_webhooks',
MANAGE_OWN_OUTGOING_WEBHOOKS: 'manage_own_outgoing_webhooks',
MANAGE_OTHERS_OUTGOING_WEBHOOKS: 'manage_others_outgoing_webhooks',
MANAGE_OAUTH: 'manage_oauth',
MANAGE_OUTGOING_OAUTH_CONNECTIONS: 'manage_outgoing_oauth_connections',
MANAGE_SYSTEM_WIDE_OAUTH: 'manage_system_wide_oauth',
CREATE_POST: 'create_post',
CREATE_POST_PUBLIC: 'create_post_public',
EDIT_POST: 'edit_post',

View file

@ -1198,6 +1198,7 @@ export const PermissionsScope = {
[Permissions.INVITE_GUEST]: 'team_scope',
[Permissions.ADD_USER_TO_TEAM]: 'team_scope',
[Permissions.MANAGE_SLASH_COMMANDS]: 'team_scope',
[Permissions.MANAGE_OWN_SLASH_COMMANDS]: 'team_scope',
[Permissions.MANAGE_OTHERS_SLASH_COMMANDS]: 'team_scope',
[Permissions.CREATE_PUBLIC_CHANNEL]: 'team_scope',
[Permissions.CREATE_PRIVATE_CHANNEL]: 'team_scope',
@ -1231,11 +1232,13 @@ export const PermissionsScope = {
[Permissions.UPLOAD_FILE]: 'channel_scope',
[Permissions.GET_PUBLIC_LINK]: 'system_scope',
[Permissions.MANAGE_INCOMING_WEBHOOKS]: 'team_scope',
[Permissions.MANAGE_OWN_INCOMING_WEBHOOKS]: 'team_scope',
[Permissions.BYPASS_INCOMING_WEBHOOK_CHANNEL_LOCK]: 'team_scope',
[Permissions.MANAGE_OTHERS_INCOMING_WEBHOOKS]: 'team_scope',
[Permissions.MANAGE_OUTGOING_WEBHOOKS]: 'team_scope',
[Permissions.MANAGE_OWN_OUTGOING_WEBHOOKS]: 'team_scope',
[Permissions.MANAGE_OTHERS_OUTGOING_WEBHOOKS]: 'team_scope',
[Permissions.MANAGE_OAUTH]: 'system_scope',
[Permissions.MANAGE_SYSTEM_WIDE_OAUTH]: 'system_scope',
[Permissions.CREATE_POST]: 'channel_scope',
[Permissions.CREATE_POST_PUBLIC]: 'channel_scope',
[Permissions.EDIT_POST]: 'channel_scope',
@ -1384,15 +1387,16 @@ export const DefaultRolePermissions = {
Permissions.IMPORT_TEAM,
Permissions.MANAGE_TEAM_ROLES,
Permissions.MANAGE_CHANNEL_ROLES,
Permissions.MANAGE_SLASH_COMMANDS,
Permissions.MANAGE_OWN_SLASH_COMMANDS,
Permissions.MANAGE_OTHERS_SLASH_COMMANDS,
Permissions.MANAGE_INCOMING_WEBHOOKS,
Permissions.MANAGE_OUTGOING_WEBHOOKS,
Permissions.MANAGE_OWN_INCOMING_WEBHOOKS,
Permissions.MANAGE_OTHERS_INCOMING_WEBHOOKS,
Permissions.BYPASS_INCOMING_WEBHOOK_CHANNEL_LOCK,
Permissions.MANAGE_OWN_OUTGOING_WEBHOOKS,
Permissions.MANAGE_OTHERS_OUTGOING_WEBHOOKS,
Permissions.DELETE_POST,
Permissions.DELETE_OTHERS_POSTS,
Permissions.MANAGE_OTHERS_OUTGOING_WEBHOOKS,
Permissions.ADD_REACTION,
Permissions.MANAGE_OTHERS_INCOMING_WEBHOOKS,
Permissions.USE_CHANNEL_MENTIONS,
Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS,
Permissions.CONVERT_PUBLIC_CHANNEL_TO_PRIVATE,