From 1e7985a87a72bea9a308cf1506dacc828c6e2e1c Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Tue, 13 Sep 2016 12:42:48 -0400 Subject: Modifying permissions system. (#3897) --- api/admin.go | 75 ++++------------------- api/apitestlib.go | 6 +- api/authorization.go | 149 ++++++++++++++++++++++++++++++++++++++++++++++ api/authorization_test.go | 36 +++++++++++ api/channel.go | 145 ++++++++++++++++++-------------------------- api/channel_test.go | 27 ++++++++- api/cli_test.go | 4 +- api/command.go | 48 ++++++--------- api/context.go | 92 +++------------------------- api/context_test.go | 20 ------- api/emoji.go | 2 +- api/file.go | 22 +++---- api/license.go | 7 ++- api/oauth.go | 54 ++++++----------- api/oauth_test.go | 2 + api/post.go | 56 ++++++++--------- api/post_test.go | 1 + api/team.go | 62 ++++++++++--------- api/team_test.go | 9 ++- api/user.go | 60 +++++++++---------- api/user_test.go | 22 +++---- api/webhook.go | 111 ++++++++++++++-------------------- api/webhook_test.go | 30 ++++++++++ 23 files changed, 527 insertions(+), 513 deletions(-) create mode 100644 api/authorization.go create mode 100644 api/authorization_test.go (limited to 'api') diff --git a/api/admin.go b/api/admin.go index 573a22c6b..9ac071e6d 100644 --- a/api/admin.go +++ b/api/admin.go @@ -25,18 +25,18 @@ import ( func InitAdmin() { l4g.Debug(utils.T("api.admin.init.debug")) - BaseRoutes.Admin.Handle("/logs", ApiUserRequired(getLogs)).Methods("GET") - BaseRoutes.Admin.Handle("/audits", ApiUserRequired(getAllAudits)).Methods("GET") - BaseRoutes.Admin.Handle("/config", ApiUserRequired(getConfig)).Methods("GET") - BaseRoutes.Admin.Handle("/save_config", ApiUserRequired(saveConfig)).Methods("POST") - BaseRoutes.Admin.Handle("/reload_config", ApiUserRequired(reloadConfig)).Methods("GET") - BaseRoutes.Admin.Handle("/test_email", ApiUserRequired(testEmail)).Methods("POST") - BaseRoutes.Admin.Handle("/recycle_db_conn", ApiUserRequired(recycleDatabaseConnection)).Methods("GET") - BaseRoutes.Admin.Handle("/analytics/{id:[A-Za-z0-9]+}/{name:[A-Za-z0-9_]+}", ApiUserRequired(getAnalytics)).Methods("GET") - BaseRoutes.Admin.Handle("/analytics/{name:[A-Za-z0-9_]+}", ApiUserRequired(getAnalytics)).Methods("GET") - BaseRoutes.Admin.Handle("/save_compliance_report", ApiUserRequired(saveComplianceReport)).Methods("POST") - BaseRoutes.Admin.Handle("/compliance_reports", ApiUserRequired(getComplianceReports)).Methods("GET") - BaseRoutes.Admin.Handle("/download_compliance_report/{id:[A-Za-z0-9]+}", ApiUserRequiredTrustRequester(downloadComplianceReport)).Methods("GET") + BaseRoutes.Admin.Handle("/logs", ApiAdminSystemRequired(getLogs)).Methods("GET") + BaseRoutes.Admin.Handle("/audits", ApiAdminSystemRequired(getAllAudits)).Methods("GET") + BaseRoutes.Admin.Handle("/config", ApiAdminSystemRequired(getConfig)).Methods("GET") + BaseRoutes.Admin.Handle("/save_config", ApiAdminSystemRequired(saveConfig)).Methods("POST") + BaseRoutes.Admin.Handle("/reload_config", ApiAdminSystemRequired(reloadConfig)).Methods("GET") + BaseRoutes.Admin.Handle("/test_email", ApiAdminSystemRequired(testEmail)).Methods("POST") + BaseRoutes.Admin.Handle("/recycle_db_conn", ApiAdminSystemRequired(recycleDatabaseConnection)).Methods("GET") + BaseRoutes.Admin.Handle("/analytics/{id:[A-Za-z0-9]+}/{name:[A-Za-z0-9_]+}", ApiAdminSystemRequired(getAnalytics)).Methods("GET") + BaseRoutes.Admin.Handle("/analytics/{name:[A-Za-z0-9_]+}", ApiAdminSystemRequired(getAnalytics)).Methods("GET") + BaseRoutes.Admin.Handle("/save_compliance_report", ApiAdminSystemRequired(saveComplianceReport)).Methods("POST") + BaseRoutes.Admin.Handle("/compliance_reports", ApiAdminSystemRequired(getComplianceReports)).Methods("GET") + BaseRoutes.Admin.Handle("/download_compliance_report/{id:[A-Za-z0-9]+}", ApiAdminSystemRequiredTrustRequester(downloadComplianceReport)).Methods("GET") BaseRoutes.Admin.Handle("/upload_brand_image", ApiAdminSystemRequired(uploadBrandImage)).Methods("POST") BaseRoutes.Admin.Handle("/get_brand_image", ApiAppHandlerTrustRequester(getBrandImage)).Methods("GET") BaseRoutes.Admin.Handle("/reset_mfa", ApiAdminSystemRequired(adminResetMfa)).Methods("POST") @@ -52,11 +52,6 @@ func InitAdmin() { } func getLogs(c *Context, w http.ResponseWriter, r *http.Request) { - - if !c.HasSystemAdminPermissions("getLogs") { - return - } - lines, err := GetLogs() if err != nil { c.Err = err @@ -99,11 +94,6 @@ func GetLogs() ([]string, *model.AppError) { } func getClusterStatus(c *Context, w http.ResponseWriter, r *http.Request) { - - if !c.HasSystemAdminPermissions("getClusterStatus") { - return - } - infos := make([]*model.ClusterInfo, 0) if einterfaces.GetClusterInterface() != nil { infos = einterfaces.GetClusterInterface().GetClusterInfos() @@ -113,11 +103,6 @@ func getClusterStatus(c *Context, w http.ResponseWriter, r *http.Request) { } func getAllAudits(c *Context, w http.ResponseWriter, r *http.Request) { - - if !c.HasSystemAdminPermissions("getAllAudits") { - return - } - if result := <-Srv.Store.Audit().Get("", 200); result.Err != nil { c.Err = result.Err return @@ -139,10 +124,6 @@ func getAllAudits(c *Context, w http.ResponseWriter, r *http.Request) { } func getConfig(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("getConfig") { - return - } - json := utils.Cfg.ToJson() cfg := model.ConfigFromJson(strings.NewReader(json)) @@ -153,10 +134,6 @@ func getConfig(c *Context, w http.ResponseWriter, r *http.Request) { } func reloadConfig(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("reloadConfig") { - return - } - utils.LoadConfig(utils.CfgFileName) // start/restart email batching job if necessary @@ -167,10 +144,6 @@ func reloadConfig(c *Context, w http.ResponseWriter, r *http.Request) { } func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("getConfig") { - return - } - cfg := model.ConfigFromJson(r.Body) if cfg == nil { c.SetInvalidParam("saveConfig", "config") @@ -219,10 +192,6 @@ func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) { } func recycleDatabaseConnection(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("recycleDatabaseConnection") { - return - } - oldStore := Srv.Store l4g.Warn(utils.T("api.admin.recycle_db_start.warn")) @@ -238,10 +207,6 @@ func recycleDatabaseConnection(c *Context, w http.ResponseWriter, r *http.Reques } func testEmail(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("testEmail") { - return - } - cfg := model.ConfigFromJson(r.Body) if cfg == nil { c.SetInvalidParam("testEmail", "config") @@ -282,10 +247,6 @@ func testEmail(c *Context, w http.ResponseWriter, r *http.Request) { } func getComplianceReports(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("getComplianceReports") { - return - } - if !*utils.Cfg.ComplianceSettings.Enable || !utils.IsLicensed || !*utils.License.Features.Compliance { c.Err = model.NewLocAppError("getComplianceReports", "ent.compliance.licence_disable.app_error", nil, "") return @@ -301,10 +262,6 @@ func getComplianceReports(c *Context, w http.ResponseWriter, r *http.Request) { } func saveComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("getComplianceReports") { - return - } - if !*utils.Cfg.ComplianceSettings.Enable || !utils.IsLicensed || !*utils.License.Features.Compliance || einterfaces.GetComplianceInterface() == nil { c.Err = model.NewLocAppError("saveComplianceReport", "ent.compliance.licence_disable.app_error", nil, "") return @@ -331,10 +288,6 @@ func saveComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) { } func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("downloadComplianceReport") { - return - } - if !*utils.Cfg.ComplianceSettings.Enable || !utils.IsLicensed || !*utils.License.Features.Compliance || einterfaces.GetComplianceInterface() == nil { c.Err = model.NewLocAppError("downloadComplianceReport", "ent.compliance.licence_disable.app_error", nil, "") return @@ -380,10 +333,6 @@ func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request } func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasSystemAdminPermissions("getAnalytics") { - return - } - params := mux.Vars(r) teamId := params["id"] name := params["name"] diff --git a/api/apitestlib.go b/api/apitestlib.go index ea0de4716..2511513bb 100644 --- a/api/apitestlib.go +++ b/api/apitestlib.go @@ -91,7 +91,7 @@ func (me *TestHelper) InitSystemAdmin() *TestHelper { c := &Context{} c.RequestId = model.NewId() c.IpAddress = "cmd_line" - UpdateUserRoles(c, me.SystemAdminUser, model.ROLE_SYSTEM_ADMIN) + UpdateUserRoles(c, me.SystemAdminUser, model.ROLE_SYSTEM_USER.Id+" "+model.ROLE_SYSTEM_ADMIN.Id) me.SystemAdminUser.Password = "Password1" me.LoginSystemAdmin() me.SystemAdminChannel = me.CreateChannel(me.SystemAdminClient, me.SystemAdminTeam) @@ -157,13 +157,15 @@ func LinkUserToTeam(user *model.User, team *model.Team) { func UpdateUserToTeamAdmin(user *model.User, team *model.Team) { utils.DisableDebugLogForTest() - tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.ROLE_TEAM_ADMIN} + tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.ROLE_TEAM_USER.Id + " " + model.ROLE_TEAM_ADMIN.Id} if tmr := <-Srv.Store.Team().UpdateMember(tm); tmr.Err != nil { + utils.EnableDebugLogForTest() l4g.Error(tmr.Err.Error()) l4g.Close() time.Sleep(time.Second) panic(tmr.Err) } + utils.EnableDebugLogForTest() } func (me *TestHelper) CreateChannel(client *model.Client, team *model.Team) *model.Channel { diff --git a/api/authorization.go b/api/authorization.go new file mode 100644 index 000000000..fb04b069b --- /dev/null +++ b/api/authorization.go @@ -0,0 +1,149 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api + +import ( + "net/http" + + l4g "github.com/alecthomas/log4go" + "github.com/mattermost/platform/model" +) + +func HasPermissionToContext(c *Context, permission *model.Permission) bool { + userRoles := c.Session.GetUserRoles() + if !CheckIfRolesGrantPermission(userRoles, permission.Id) { + c.Err = model.NewLocAppError("HasPermissionToContext", "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", teamId="+c.TeamId+" permission="+permission.Id+" "+model.RoleIdsToString(userRoles)) + c.Err.StatusCode = http.StatusForbidden + return false + } + + return true +} + +func HasPermissionTo(user *model.User, permission *model.Permission) bool { + roles := user.GetRoles() + + return CheckIfRolesGrantPermission(roles, permission.Id) +} + +func HasPermissionToCurrentTeamContext(c *Context, permission *model.Permission) bool { + return HasPermissionToTeamContext(c, c.TeamId, permission) +} + +func HasPermissionToTeamContext(c *Context, teamId string, permission *model.Permission) bool { + teamMember := c.Session.GetTeamByTeamId(teamId) + if teamMember != nil { + roles := teamMember.GetRoles() + + if CheckIfRolesGrantPermission(roles, permission.Id) { + return true + } + } + + if HasPermissionToContext(c, permission) { + return true + } + + c.Err = model.NewLocAppError("HasPermissionToTeamContext", "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", teamId="+c.TeamId+" permission="+permission.Id) + c.Err.StatusCode = http.StatusForbidden + return false +} + +func HasPermissionToTeam(user *model.User, teamMember *model.TeamMember, permission *model.Permission) bool { + if teamMember == nil { + return false + } + + roles := teamMember.GetRoles() + + if CheckIfRolesGrantPermission(roles, permission.Id) { + return true + } + + return HasPermissionTo(user, permission) +} + +func HasPermissionToChannelContext(c *Context, channelId string, permission *model.Permission) bool { + cmc := Srv.Store.Channel().GetMember(channelId, c.Session.UserId) + + var channelRoles []string + if cmcresult := <-cmc; cmcresult.Err == nil { + channelMember := cmcresult.Data.(model.ChannelMember) + channelRoles = channelMember.GetRoles() + + if CheckIfRolesGrantPermission(channelRoles, permission.Id) { + return true + } + } + + cc := Srv.Store.Channel().Get(channelId) + if ccresult := <-cc; ccresult.Err == nil { + channel := ccresult.Data.(*model.Channel) + + if teamMember := c.Session.GetTeamByTeamId(channel.TeamId); teamMember != nil { + roles := teamMember.GetRoles() + + if CheckIfRolesGrantPermission(roles, permission.Id) { + return true + } + } + + } + + if HasPermissionToContext(c, permission) { + return true + } + + c.Err = model.NewLocAppError("HasPermissionToChannelContext", "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", "+"permission="+permission.Id+" channelRoles="+model.RoleIdsToString(channelRoles)) + c.Err.StatusCode = http.StatusForbidden + return false +} + +func HasPermissionToChannel(user *model.User, teamMember *model.TeamMember, channelMember *model.ChannelMember, permission *model.Permission) bool { + if channelMember == nil { + return false + } + + roles := channelMember.GetRoles() + + if CheckIfRolesGrantPermission(roles, permission.Id) { + return true + } + + return HasPermissionToTeam(user, teamMember, permission) +} + +func HasPermissionToUser(c *Context, userId string) bool { + // You are the user (users autmaticly have permissions to themselves) + if c.Session.UserId == userId { + return true + } + + // You have permission + if HasPermissionToContext(c, model.PERMISSION_EDIT_OTHER_USERS) { + return true + } + + c.Err = model.NewLocAppError("HasPermissionToUser", "api.context.permissions.app_error", nil, "userId="+userId) + c.Err.StatusCode = http.StatusForbidden + return false +} + +func CheckIfRolesGrantPermission(roles []string, permissionId string) bool { + for _, roleId := range roles { + if role, ok := model.BuiltInRoles[roleId]; !ok { + l4g.Debug("Bad role in system " + roleId) + return false + } else { + permissions := role.Permissions + for _, permission := range permissions { + if permission == permissionId { + return true + } + } + } + } + + return false +} diff --git a/api/authorization_test.go b/api/authorization_test.go new file mode 100644 index 000000000..5613751c2 --- /dev/null +++ b/api/authorization_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api + +import ( + "testing" + + "github.com/mattermost/platform/model" +) + +func TestCheckIfRolesGrantPermission(t *testing.T) { + Setup() + + cases := []struct { + roles []string + permissionId string + shouldGrant bool + }{ + {[]string{model.ROLE_SYSTEM_ADMIN.Id}, model.ROLE_SYSTEM_ADMIN.Permissions[0], true}, + {[]string{model.ROLE_SYSTEM_ADMIN.Id}, "non-existant-permission", false}, + {[]string{model.ROLE_CHANNEL_USER.Id}, model.ROLE_CHANNEL_USER.Permissions[0], true}, + {[]string{model.ROLE_CHANNEL_USER.Id}, model.PERMISSION_MANAGE_SYSTEM.Id, false}, + {[]string{model.ROLE_SYSTEM_ADMIN.Id, model.ROLE_CHANNEL_USER.Id}, model.PERMISSION_MANAGE_SYSTEM.Id, true}, + {[]string{model.ROLE_CHANNEL_USER.Id, model.ROLE_SYSTEM_ADMIN.Id}, model.PERMISSION_MANAGE_SYSTEM.Id, true}, + {[]string{model.ROLE_TEAM_USER.Id, model.ROLE_TEAM_ADMIN.Id}, model.PERMISSION_MANAGE_SLASH_COMMANDS.Id, true}, + {[]string{model.ROLE_TEAM_ADMIN.Id, model.ROLE_TEAM_USER.Id}, model.PERMISSION_MANAGE_SLASH_COMMANDS.Id, true}, + } + + for testnum, testcase := range cases { + if CheckIfRolesGrantPermission(testcase.roles, testcase.permissionId) != testcase.shouldGrant { + t.Fatal("Failed test case ", testnum) + } + } + +} diff --git a/api/channel.go b/api/channel.go index c477a5ee4..734dac744 100644 --- a/api/channel.go +++ b/api/channel.go @@ -60,22 +60,21 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { channel.TeamId = c.TeamId } - if err := CanManageChannel(c, channel); err != nil { - c.Err = err + if channel.Type == model.CHANNEL_DIRECT { + c.Err = model.NewLocAppError("createDirectChannel", "api.channel.create_channel.direct_channel.app_error", nil, "") return } - if !c.HasPermissionsToTeam(channel.TeamId, "createChannel") { + if strings.Index(channel.Name, "__") > 0 { + c.Err = model.NewLocAppError("createDirectChannel", "api.channel.create_channel.invalid_character.app_error", nil, "") return } - if channel.Type == model.CHANNEL_DIRECT { - c.Err = model.NewLocAppError("createDirectChannel", "api.channel.create_channel.direct_channel.app_error", nil, "") + if channel.Type == model.CHANNEL_OPEN && !HasPermissionToTeamContext(c, channel.TeamId, model.PERMISSION_CREATE_PUBLIC_CHANNEL) { return } - if strings.Index(channel.Name, "__") > 0 { - c.Err = model.NewLocAppError("createDirectChannel", "api.channel.create_channel.invalid_character.app_error", nil, "") + if channel.Type == model.CHANNEL_PRIVATE && !HasPermissionToTeamContext(c, channel.TeamId, model.PERMISSION_CREATE_PRIVATE_CHANNEL) { return } @@ -96,8 +95,12 @@ func CreateChannel(c *Context, channel *model.Channel, addMember bool) (*model.C sc := result.Data.(*model.Channel) if addMember { - cm := &model.ChannelMember{ChannelId: sc.Id, UserId: c.Session.UserId, - Roles: model.CHANNEL_ROLE_ADMIN, NotifyProps: model.GetDefaultChannelNotifyProps()} + cm := &model.ChannelMember{ + ChannelId: sc.Id, + UserId: c.Session.UserId, + Roles: model.ROLE_CHANNEL_USER.Id + " " + model.ROLE_CHANNEL_ADMIN.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil { return nil, cmresult.Err @@ -111,6 +114,9 @@ func CreateChannel(c *Context, channel *model.Channel, addMember bool) (*model.C } func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) { + if !HasPermissionToContext(c, model.PERMISSION_CREATE_DIRECT_CHANNEL) { + return + } data := model.MapFromJson(r.Body) @@ -146,10 +152,12 @@ func CreateDirectChannel(userId string, otherUserId string) (*model.Channel, *mo cm1 := &model.ChannelMember{ UserId: userId, NotifyProps: model.GetDefaultChannelNotifyProps(), + Roles: model.ROLE_CHANNEL_USER.Id, } cm2 := &model.ChannelMember{ UserId: otherUserId, NotifyProps: model.GetDefaultChannelNotifyProps(), + Roles: model.ROLE_CHANNEL_USER.Id, } if result := <-Srv.Store.Channel().SaveDirectChannel(channel, cm1, cm2); result.Err != nil { @@ -184,30 +192,16 @@ func CreateDefaultChannels(c *Context, teamId string) ([]*model.Channel, *model. return channels, nil } -func CanManageChannel(c *Context, channel *model.Channel) *model.AppError { - if utils.IsLicensed { - if channel.Type == model.CHANNEL_OPEN { - if *utils.Cfg.TeamSettings.RestrictPublicChannelManagement == model.PERMISSIONS_SYSTEM_ADMIN && !c.IsSystemAdmin() { - return model.NewLocAppError("CanManageChannel", "api.channel.can_manage_channel.public_restricted_system_admin.app_error", nil, "") - } - - if *utils.Cfg.TeamSettings.RestrictPublicChannelManagement == model.PERMISSIONS_TEAM_ADMIN && !c.IsTeamAdmin() { - return model.NewLocAppError("CanManageChannel", "api.channel.can_manage_channel.public_restricted_team_admin.app_error", nil, "") - } - } - - if channel.Type == model.CHANNEL_PRIVATE { - if *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement == model.PERMISSIONS_SYSTEM_ADMIN && !c.IsSystemAdmin() { - return model.NewLocAppError("CanManageChannel", "api.channel.can_manage_channel.private_restricted_system_admin.app_error", nil, "") - } +func CanManageChannel(c *Context, channel *model.Channel) bool { + if channel.Type == model.CHANNEL_OPEN && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES) { + return false + } - if *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement == model.PERMISSIONS_TEAM_ADMIN && !c.IsTeamAdmin() { - return model.NewLocAppError("CanManageChannel", "api.channel.can_manage_channel.private_restricted_team_admin.app_error", nil, "") - } - } + if channel.Type == model.CHANNEL_PRIVATE && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES) { + return false } - return nil + return true } func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { @@ -232,12 +226,7 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { oldChannel := cresult.Data.(*model.Channel) // Don't need to do anything with channel member, just wanted to confirm it exists - if err := CanManageChannel(c, oldChannel); err != nil { - c.Err = err - return - } - - if !c.HasPermissionsToTeam(oldChannel.TeamId, "updateChannel") { + if !CanManageChannel(c, channel) { return } @@ -308,14 +297,10 @@ func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) { channel := cresult.Data.(*model.Channel) // Don't need to do anything with channel member, just wanted to confirm it exists - if err := CanManageChannel(c, channel); err != nil { - c.Err = err + if !CanManageChannel(c, channel) { return } - if channel.TeamId != "" && !c.HasPermissionsToTeam(channel.TeamId, "updateChannelHeader") { - return - } oldChannelHeader := channel.Header channel.Header = channelHeader @@ -387,12 +372,7 @@ func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) { channel := cresult.Data.(*model.Channel) // Don't need to do anything with channel member, just wanted to confirm it exists - if err := CanManageChannel(c, channel); err != nil { - c.Err = err - return - } - - if !c.HasPermissionsToTeam(channel.TeamId, "updateChannelPurpose") { + if !CanManageChannel(c, channel) { return } @@ -411,6 +391,7 @@ func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) { func getChannels(c *Context, w http.ResponseWriter, r *http.Request) { // user is already in the team + // Get's all channels the user is a member of if result := <-Srv.Store.Channel().GetChannels(c.TeamId, c.Session.UserId); result.Err != nil { if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" { @@ -436,6 +417,9 @@ func getChannels(c *Context, w http.ResponseWriter, r *http.Request) { func getMoreChannels(c *Context, w http.ResponseWriter, r *http.Request) { // user is already in the team + if !HasPermissionToTeamContext(c, c.TeamId, model.PERMISSION_LIST_TEAM_CHANNELS) { + return + } if result := <-Srv.Store.Channel().GetMoreChannels(c.TeamId, c.Session.UserId); result.Err != nil { c.Err = result.Err @@ -523,7 +507,7 @@ func joinChannel(c *Context, channelChannel store.StoreChannel, userChannel stor return nil, channel } - if !c.HasPermissionsToTeam(channel.TeamId, "join") { + if !HasPermissionToTeamContext(c, channel.TeamId, model.PERMISSION_JOIN_PUBLIC_CHANNELS) { return c.Err, nil } @@ -581,7 +565,12 @@ func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelM return &channelMember, nil } - newMember := &model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps()} + newMember := &model.ChannelMember{ + ChannelId: channel.Id, + UserId: user.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + Roles: model.ROLE_CHANNEL_USER.Id, + } if result := <-Srv.Store.Channel().SaveMember(newMember); result.Err != nil { l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, result.Err) return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user.to.channel.failed.app_error", nil, "") @@ -669,10 +658,6 @@ func leave(c *Context, w http.ResponseWriter, r *http.Request) { user := uresult.Data.(*model.User) membersCount := ccmresult.Data.(int64) - if !c.HasPermissionsToTeam(channel.TeamId, "leave") { - return - } - if channel.Type == model.CHANNEL_DIRECT { c.Err = model.NewLocAppError("leave", "api.channel.leave.direct.app_error", nil, "") c.Err.StatusCode = http.StatusBadRequest @@ -746,14 +731,13 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { // Allow delete if user is the only member left in channel if memberCount > 1 { - if err := CanManageChannel(c, channel); err != nil { - c.Err = err + if channel.Type == model.CHANNEL_OPEN && !HasPermissionToTeamContext(c, channel.TeamId, model.PERMISSION_DELETE_PUBLIC_CHANNEL) { return } - } - if !c.HasPermissionsToTeam(channel.TeamId, "deleteChannel") { - return + if channel.Type == model.CHANNEL_PRIVATE && !HasPermissionToTeamContext(c, channel.TeamId, model.PERMISSION_DELETE_PRIVATE_CHANNEL) { + return + } } if channel.DeleteAt > 0 { @@ -901,7 +885,6 @@ func getChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] - //pchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) cchan := Srv.Store.Channel().Get(id) cmchan := Srv.Store.Channel().GetMember(id, c.Session.UserId) @@ -974,24 +957,20 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = ccmresult.Err return } else { - member := cmresult.Data.(model.ChannelMember) + //member := cmresult.Data.(model.ChannelMember) extraMembers := ecmresult.Data.([]model.ExtraMember) memberCount := ccmresult.Data.(int64) - if len(channel.TeamId) > 0 && !c.HasPermissionsToTeam(channel.TeamId, "getChannelExtraInfo") { - return - } - - if !c.HasPermissionsToUser(member.UserId, "getChannelExtraInfo") { - return - } - if channel.DeleteAt > 0 { c.Err = model.NewLocAppError("getChannelExtraInfo", "api.channel.get_channel_extra_info.deleted.app_error", nil, "") c.Err.StatusCode = http.StatusBadRequest return } + if !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_READ_CHANNEL) { + return + } + data := model.ChannelExtra{Id: channel.Id, Members: extraMembers, MemberCount: memberCount} w.Header().Set(model.HEADER_ETAG_SERVER, extraEtag) w.Write([]byte(data.ToJson())) @@ -1010,16 +989,9 @@ func addMember(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) sc := Srv.Store.Channel().Get(id) ouc := Srv.Store.User().Get(c.Session.UserId) nuc := Srv.Store.User().Get(userId) - - // Only need to be a member of the channel to add a new member - if !c.HasPermissionsToChannel(cchan, "addMember") { - return - } - if nresult := <-nuc; nresult.Err != nil { c.Err = model.NewLocAppError("addMember", "api.channel.add_member.find_user.app_error", nil, "") return @@ -1030,6 +1002,14 @@ func addMember(c *Context, w http.ResponseWriter, r *http.Request) { channel := cresult.Data.(*model.Channel) nUser := nresult.Data.(*model.User) + if channel.Type == model.CHANNEL_OPEN && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS) { + return + } + + if channel.Type == model.CHANNEL_PRIVATE && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS) { + return + } + if oresult := <-ouc; oresult.Err != nil { c.Err = model.NewLocAppError("addMember", "api.channel.add_member.user_adding.app_error", nil, "") return @@ -1082,15 +1062,12 @@ func removeMember(c *Context, w http.ResponseWriter, r *http.Request) { return } else { channel := cresult.Data.(*model.Channel) - removerChannelMember := cmcresult.Data.(model.ChannelMember) - if !c.HasPermissionsToTeam(channel.TeamId, "removeMember") { + if channel.Type == model.CHANNEL_OPEN && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS) { return } - if !strings.Contains(removerChannelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !c.IsTeamAdmin() { - c.Err = model.NewLocAppError("updateChannel", "api.channel.remove_member.permissions.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + if channel.Type == model.CHANNEL_PRIVATE && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS) { return } @@ -1145,13 +1122,7 @@ func updateNotifyProps(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) - - if !c.HasPermissionsToUser(userId, "updateNotifyLevel") { - return - } - - if !c.HasPermissionsToChannel(cchan, "updateNotifyLevel") { + if !HasPermissionToUser(c, userId) { return } diff --git a/api/channel_test.go b/api/channel_test.go index 7046a9868..a3f3e211c 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -4,13 +4,14 @@ package api import ( - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" - "github.com/mattermost/platform/utils" "net/http" "strings" "testing" "time" + + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/store" + "github.com/mattermost/platform/utils" ) func TestCreateChannel(t *testing.T) { @@ -100,9 +101,11 @@ func TestCreateChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel utils.IsLicensed = isLicensed + utils.SetDefaultRolesBasedOnConfig() }() *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL + utils.SetDefaultRolesBasedOnConfig() utils.IsLicensed = true channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} @@ -116,6 +119,7 @@ func TestCreateChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + utils.SetDefaultRolesBasedOnConfig() channel2.Name = "a" + model.NewId() + "a" channel3.Name = "a" + model.NewId() + "a" @@ -140,6 +144,7 @@ func TestCreateChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + utils.SetDefaultRolesBasedOnConfig() channel2.Name = "a" + model.NewId() + "a" channel3.Name = "a" + model.NewId() + "a" @@ -286,10 +291,12 @@ func TestUpdateChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel utils.IsLicensed = isLicensed + utils.SetDefaultRolesBasedOnConfig() }() *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL utils.IsLicensed = true + utils.SetDefaultRolesBasedOnConfig() channel2 := th.CreateChannel(Client, team) channel3 := th.CreatePrivateChannel(Client, team) @@ -310,6 +317,7 @@ func TestUpdateChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannel(channel2); err == nil { t.Fatal("should have errored not team admin") @@ -332,6 +340,7 @@ func TestUpdateChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannel(channel2); err == nil { t.Fatal("should have errored not system admin") @@ -419,10 +428,12 @@ func TestUpdateChannelHeader(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel utils.IsLicensed = isLicensed + utils.SetDefaultRolesBasedOnConfig() }() *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL utils.IsLicensed = true + utils.SetDefaultRolesBasedOnConfig() th.LoginBasic() channel2 := th.CreateChannel(Client, team) @@ -447,6 +458,7 @@ func TestUpdateChannelHeader(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannelHeader(data2); err == nil { t.Fatal("should have errored not team admin") @@ -469,6 +481,7 @@ func TestUpdateChannelHeader(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannelHeader(data2); err == nil { t.Fatal("should have errored not system admin") @@ -545,10 +558,12 @@ func TestUpdateChannelPurpose(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel utils.IsLicensed = isLicensed + utils.SetDefaultRolesBasedOnConfig() }() *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL utils.IsLicensed = true + utils.SetDefaultRolesBasedOnConfig() th.LoginBasic() channel2 := th.CreateChannel(Client, team) @@ -573,6 +588,7 @@ func TestUpdateChannelPurpose(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannelPurpose(data2); err == nil { t.Fatal("should have errored not team admin") @@ -595,6 +611,7 @@ func TestUpdateChannelPurpose(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannelPurpose(data2); err == nil { t.Fatal("should have errored not system admin") @@ -986,10 +1003,12 @@ func TestDeleteChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel utils.IsLicensed = isLicensed + utils.SetDefaultRolesBasedOnConfig() }() *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL utils.IsLicensed = true + utils.SetDefaultRolesBasedOnConfig() th.LoginSystemAdmin() LinkUserToTeam(th.BasicUser, team) @@ -1013,6 +1032,7 @@ func TestDeleteChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN + utils.SetDefaultRolesBasedOnConfig() th.LoginSystemAdmin() @@ -1044,6 +1064,7 @@ func TestDeleteChannel(t *testing.T) { *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + utils.SetDefaultRolesBasedOnConfig() th.LoginSystemAdmin() diff --git a/api/cli_test.go b/api/cli_test.go index 65afc81e9..c25394c17 100644 --- a/api/cli_test.go +++ b/api/cli_test.go @@ -118,7 +118,7 @@ func TestCliAssignRole(t *testing.T) { th := Setup().InitBasic() - cmd := exec.Command("bash", "-c", `go run ../mattermost.go -assign_role -email="`+th.BasicUser.Email+`" -role="system_admin"`) + cmd := exec.Command("bash", "-c", `go run ../mattermost.go -assign_role -email="`+th.BasicUser.Email+`" -role="system_user system_admin"`) output, err := cmd.CombinedOutput() if err != nil { t.Log(string(output)) @@ -129,7 +129,7 @@ func TestCliAssignRole(t *testing.T) { t.Fatal() } else { user := result.Data.(*model.User) - if user.Roles != "system_admin" { + if user.Roles != "system_user system_admin" { t.Fatal() } } diff --git a/api/command.go b/api/command.go index 5556ed817..5cf9d730b 100644 --- a/api/command.go +++ b/api/command.go @@ -97,9 +97,7 @@ func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) { } if len(channelId) > 0 { - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) - - if !c.HasPermissionsToChannel(cchan, "checkCommand") { + if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_USE_SLASH_COMMANDS) { return } } @@ -272,12 +270,10 @@ func createCommand(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("createCommand", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.Err = model.NewLocAppError("createCommand", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } c.LogAudit("attempt") @@ -330,12 +326,10 @@ func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("listTeamCommands", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.Err = model.NewLocAppError("listTeamCommands", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } if result := <-Srv.Store.Command().GetByTeam(c.TeamId); result.Err != nil { @@ -354,12 +348,10 @@ func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("regenCommandToken", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.Err = model.NewLocAppError("regenCommandToken", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } c.LogAudit("attempt") @@ -379,7 +371,7 @@ func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) { } else { cmd = result.Data.(*model.Command) - if c.TeamId != cmd.TeamId || (c.Session.UserId != cmd.CreatorId && !c.IsTeamAdmin()) { + if c.TeamId != cmd.TeamId || (c.Session.UserId != cmd.CreatorId && !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)) { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("regenToken", "api.command.regen.app_error", nil, "user_id="+c.Session.UserId) return @@ -403,12 +395,10 @@ func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("deleteCommand", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.Err = model.NewLocAppError("deleteCommand", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } c.LogAudit("attempt") @@ -425,7 +415,7 @@ func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = result.Err return } else { - if c.TeamId != result.Data.(*model.Command).TeamId || (c.Session.UserId != result.Data.(*model.Command).CreatorId && !c.IsTeamAdmin()) { + if c.TeamId != result.Data.(*model.Command).TeamId || (c.Session.UserId != result.Data.(*model.Command).CreatorId && HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)) { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("deleteCommand", "api.command.delete.app_error", nil, "user_id="+c.Session.UserId) return diff --git a/api/context.go b/api/context.go index b63005d64..81a2c021d 100644 --- a/api/context.go +++ b/api/context.go @@ -16,7 +16,6 @@ import ( "github.com/mattermost/platform/einterfaces" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" ) @@ -77,6 +76,10 @@ func ApiAdminSystemRequired(h func(*Context, http.ResponseWriter, *http.Request) return &handler{h, true, true, true, false, false, false} } +func ApiAdminSystemRequiredTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { + return &handler{h, true, true, true, false, false, true} +} + func ApiAppHandlerTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { return &handler{h, false, false, true, false, false, true} } @@ -202,10 +205,6 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { c.SystemAdminRequired() } - if c.Err == nil && len(c.TeamId) > 0 && !h.isTeamIndependent { - c.HasPermissionsToTeam(c.TeamId, "TeamRoute") - } - if c.Err == nil && h.isUserActivity && token != "" && len(c.Session.UserId) > 0 { SetStatusOnline(c.Session.UserId, c.Session.Id, false) } @@ -320,90 +319,13 @@ func (c *Context) SystemAdminRequired() { c.Err = model.NewLocAppError("", "api.context.session_expired.app_error", nil, "SystemAdminRequired") c.Err.StatusCode = http.StatusUnauthorized return - } else if !c.IsSystemAdmin() { + } else if !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { c.Err = model.NewLocAppError("", "api.context.permissions.app_error", nil, "AdminRequired") c.Err.StatusCode = http.StatusForbidden return } } -func (c *Context) HasPermissionsToUser(userId string, where string) bool { - - // You are the user - if c.Session.UserId == userId { - return true - } - - // You're a mattermost system admin and you're on the VPN - if c.IsSystemAdmin() { - return true - } - - c.Err = model.NewLocAppError(where, "api.context.permissions.app_error", nil, "userId="+userId) - c.Err.StatusCode = http.StatusForbidden - return false -} - -func (c *Context) HasPermissionsToTeam(teamId string, where string) bool { - if c.IsSystemAdmin() { - return true - } - - for _, teamMember := range c.Session.TeamMembers { - if teamId == teamMember.TeamId { - return true - } - } - - c.Err = model.NewLocAppError(where, "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", teamId="+teamId) - c.Err.StatusCode = http.StatusForbidden - return false -} - -func (c *Context) HasPermissionsToChannel(sc store.StoreChannel, where string) bool { - if cresult := <-sc; cresult.Err != nil { - c.Err = cresult.Err - return false - } else if cresult.Data.(int64) != 1 { - c.Err = model.NewLocAppError(where, "api.context.permissions.app_error", nil, "userId="+c.Session.UserId) - c.Err.StatusCode = http.StatusForbidden - return false - } - - return true -} - -func (c *Context) HasSystemAdminPermissions(where string) bool { - if c.IsSystemAdmin() { - return true - } - - c.Err = model.NewLocAppError(where, "api.context.system_permissions.app_error", nil, "userId="+c.Session.UserId) - c.Err.StatusCode = http.StatusForbidden - return false -} - -func (c *Context) IsSystemAdmin() bool { - if model.IsInRole(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) { - return true - } - return false -} - -func (c *Context) IsTeamAdmin() bool { - - if c.IsSystemAdmin() { - return true - } - - teamMember := c.Session.GetTeamByTeamId(c.TeamId) - if teamMember == nil { - return false - } - - return teamMember.IsTeamAdmin() -} - func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) { cookie := &http.Cookie{ Name: model.SESSION_COOKIE_TOKEN, @@ -463,6 +385,10 @@ func (c *Context) GetSiteURL() string { return c.siteURL } +func (c *Context) GetCurrentTeamMember() *model.TeamMember { + return c.Session.GetTeamByTeamId(c.TeamId) +} + func IsApiCall(r *http.Request) bool { return strings.Index(r.URL.Path, "/api/") == 0 } diff --git a/api/context_test.go b/api/context_test.go index c3c7a9768..88ba0f665 100644 --- a/api/context_test.go +++ b/api/context_test.go @@ -8,26 +8,6 @@ import ( "testing" ) -func TestContext(t *testing.T) { - context := Context{} - - context.IpAddress = "127.0.0.1" - context.Session.UserId = "5" - - if !context.HasPermissionsToUser("5", "") { - t.Fatal("should have permissions") - } - - if context.HasPermissionsToUser("6", "") { - t.Fatal("shouldn't have permissions") - } - - context.Session.Roles = model.ROLE_SYSTEM_ADMIN - if !context.HasPermissionsToUser("6", "") { - t.Fatal("should have permissions") - } -} - func TestCache(t *testing.T) { session := &model.Session{ Id: model.NewId(), diff --git a/api/emoji.go b/api/emoji.go index d84996230..39f57a3c8 100644 --- a/api/emoji.go +++ b/api/emoji.go @@ -182,7 +182,7 @@ func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = result.Err return } else { - if c.Session.UserId != result.Data.(*model.Emoji).CreatorId && !c.IsSystemAdmin() { + if c.Session.UserId != result.Data.(*model.Emoji).CreatorId && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { c.Err = model.NewLocAppError("deleteEmoji", "api.emoji.delete.permissions.app_error", nil, "user_id="+c.Session.UserId) c.Err.StatusCode = http.StatusUnauthorized return diff --git a/api/file.go b/api/file.go index 113666270..dd99a8caf 100644 --- a/api/file.go +++ b/api/file.go @@ -103,8 +103,6 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) - files := m.File["files"] resStruct := &model.FileUploadResponse{ @@ -115,7 +113,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { imageNameList := []string{} imageDataList := [][]byte{} - if !c.HasPermissionsToChannel(cchan, "uploadFile") { + if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_UPLOAD_FILE) { return } @@ -318,7 +316,9 @@ func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) + if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_READ_CHANNEL) { + return + } path := "teams/" + c.TeamId + "/channels/" + channelId + "/users/" + userId + "/" + filename var info *model.FileInfo @@ -339,10 +339,6 @@ func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) { } } - if !c.HasPermissionsToChannel(cchan, "getFileInfo") { - return - } - w.Header().Set("Cache-Control", "max-age=2592000, public") w.Write([]byte(info.ToJson())) @@ -356,7 +352,7 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { userId := params["user_id"] filename := params["filename"] - if !c.HasPermissionsToChannel(Srv.Store.Channel().CheckPermissionsTo(teamId, channelId, c.Session.UserId), "getFile") { + if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_READ_CHANNEL) { return } @@ -512,14 +508,12 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { userId := matches[0][2] filename = matches[0][3] - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) - - url := generatePublicLink(c.GetSiteURL(), c.TeamId, channelId, userId, filename) - - if !c.HasPermissionsToChannel(cchan, "getPublicLink") { + if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_GET_PUBLIC_LINK) { return } + url := generatePublicLink(c.GetSiteURL(), c.TeamId, channelId, userId, filename) + w.Write([]byte(model.StringToJson(url))) } diff --git a/api/license.go b/api/license.go index b7bf8a234..cff7d7515 100644 --- a/api/license.go +++ b/api/license.go @@ -173,14 +173,17 @@ func RemoveLicense() *model.AppError { } func getClientLicenceConfig(c *Context, w http.ResponseWriter, r *http.Request) { - etag := utils.GetClientLicenseEtag(!c.IsSystemAdmin()) + useSanitizedLicense := !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) + c.Err = nil + + etag := utils.GetClientLicenseEtag(useSanitizedLicense) if HandleEtag(etag, w, r) { return } var clientLicense map[string]string - if c.IsSystemAdmin() { + if useSanitizedLicense { clientLicense = utils.ClientLicense } else { clientLicense = utils.GetSanitizedClientLicense() diff --git a/api/oauth.go b/api/oauth.go index b1c7675ff..18a9979f6 100644 --- a/api/oauth.go +++ b/api/oauth.go @@ -53,12 +53,10 @@ func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !c.IsSystemAdmin() { - c.Err = model.NewLocAppError("registerOAuthApp", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToContext(c, model.PERMISSION_MANAGE_OAUTH) { + c.Err = model.NewLocAppError("registerOAuthApp", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } app := model.OAuthAppFromJson(r.Body) @@ -94,18 +92,14 @@ func getOAuthApps(c *Context, w http.ResponseWriter, r *http.Request) { return } - isSystemAdmin := c.IsSystemAdmin() - - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !isSystemAdmin { - c.Err = model.NewLocAppError("getOAuthAppsByUser", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToContext(c, model.PERMISSION_MANAGE_OAUTH) { + c.Err = model.NewLocAppError("getOAuthApps", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } var ochan store.StoreChannel - if isSystemAdmin { + if HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) { ochan = Srv.Store.OAuth().GetApps() } else { ochan = Srv.Store.OAuth().GetAppByUser(c.Session.UserId) @@ -872,14 +866,10 @@ func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) { return } - isSystemAdmin := c.IsSystemAdmin() - - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !isSystemAdmin { - c.Err = model.NewLocAppError("deleteOAuthApp", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToContext(c, model.PERMISSION_MANAGE_OAUTH) { + c.Err = model.NewLocAppError("deleteOAuthApp", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } c.LogAudit("attempt") @@ -896,7 +886,7 @@ func deleteOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = result.Err return } else { - if c.Session.UserId != result.Data.(*model.OAuthApp).CreatorId && !isSystemAdmin { + if c.Session.UserId != result.Data.(*model.OAuthApp).CreatorId && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("deleteOAuthApp", "api.oauth.delete.permissions.app_error", nil, "user_id="+c.Session.UserId) return @@ -964,16 +954,6 @@ func regenerateOAuthSecret(c *Context, w http.ResponseWriter, r *http.Request) { return } - isSystemAdmin := c.IsSystemAdmin() - - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !isSystemAdmin { - c.Err = model.NewLocAppError("registerOAuthApp", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } - } - params := mux.Vars(r) id := params["id"] @@ -989,9 +969,9 @@ func regenerateOAuthSecret(c *Context, w http.ResponseWriter, r *http.Request) { } else { app = result.Data.(*model.OAuthApp) - //validate that is a System Admin or the same user that registered the app - if !isSystemAdmin && app.CreatorId != c.Session.UserId { - c.Err = model.NewLocAppError("regenerateOAuthSecret", "api.oauth.regenerate_secret.app_error", nil, "") + if app.CreatorId != c.Session.UserId && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH) { + c.Err = model.NewLocAppError("registerOAuthApp", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden return } diff --git a/api/oauth_test.go b/api/oauth_test.go index a54fbc2c3..da069aefe 100644 --- a/api/oauth_test.go +++ b/api/oauth_test.go @@ -161,6 +161,7 @@ func TestGetOAuthAppsByUser(t *testing.T) { } *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() if result, err := Client.GetOAuthAppsByUser(); err != nil { t.Fatal(err) @@ -316,6 +317,7 @@ func TestOAuthDeleteApp(t *testing.T) { utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() app := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} diff --git a/api/post.go b/api/post.go index 9a11a5c59..c43bc9843 100644 --- a/api/post.go +++ b/api/post.go @@ -59,10 +59,24 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) { } post.UserId = c.Session.UserId - // Create and save post object to channel - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId) + cchan := Srv.Store.Channel().Get(post.ChannelId) + + if !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_CREATE_POST) { + return + } + + // Check that channel has not been deleted + var channel *model.Channel + if result := <-cchan; result.Err != nil { + c.SetInvalidParam("createPost", "post.channelId") + return + } else { + channel = result.Data.(*model.Channel) + } - if !c.HasPermissionsToChannel(cchan, "createPost") { + if channel.DeleteAt != 0 { + c.Err = model.NewLocAppError("createPost", "api.post.create_post.can_not_post_to_deleted.error", nil, "") + c.Err.StatusCode = http.StatusBadRequest return } @@ -1099,10 +1113,9 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId) pchan := Srv.Store.Post().Get(post.Id) - if !c.HasPermissionsToChannel(cchan, "updatePost") { + if !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_EDIT_POST) { return } @@ -1204,10 +1217,9 @@ func getPosts(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) etagChan := Srv.Store.Post().GetEtag(id) - if !c.HasPermissionsToChannel(cchan, "getPosts") { + if !HasPermissionToChannelContext(c, id, model.PERMISSION_CREATE_POST) { return } @@ -1246,10 +1258,9 @@ func getPostsSince(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) pchan := Srv.Store.Post().GetPostsSince(id, time) - if !c.HasPermissionsToChannel(cchan, "getPostsSince") { + if !HasPermissionToChannelContext(c, id, model.PERMISSION_READ_CHANNEL) { return } @@ -1279,10 +1290,9 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) pchan := Srv.Store.Post().Get(postId) - if !c.HasPermissionsToChannel(cchan, "getPost") { + if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_READ_CHANNEL) { return } @@ -1326,8 +1336,7 @@ func getPostById(c *Context, w http.ResponseWriter, r *http.Request) { } post := list.Posts[list.Order[0]] - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId) - if !c.HasPermissionsToChannel(cchan, "getPostById") { + if !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_READ_CHANNEL) { return } @@ -1361,12 +1370,7 @@ func getPermalinkTmp(c *Context, w http.ResponseWriter, r *http.Request) { } post := list.Posts[list.Order[0]] - if !c.HasPermissionsToTeam(c.TeamId, "permalink") { - return - } - - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId) - if !c.HasPermissionsToChannel(cchan, "getPermalinkTmp") { + if !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_READ_CHANNEL) { // If we don't have permissions attempt to join the channel to fix the problem if err, _ := JoinChannelById(c, c.Session.UserId, post.ChannelId); err != nil { // On error just return with permissions error @@ -1402,7 +1406,10 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) + if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_EDIT_POST) { + return + } + pchan := Srv.Store.Post().Get(postId) if result := <-pchan; result.Err != nil { @@ -1412,10 +1419,6 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { post := result.Data.(*model.PostList).Posts[postId] - if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin() { - return - } - if post == nil { c.SetInvalidParam("deletePost", "postId") return @@ -1427,7 +1430,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if post.UserId != c.Session.UserId && !c.IsTeamAdmin() { + if post.UserId != c.Session.UserId && !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_EDIT_OTHERS_POSTS) { c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden return @@ -1507,11 +1510,10 @@ func getPostsBeforeOrAfter(c *Context, w http.ResponseWriter, r *http.Request, b return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) // We can do better than this etag in this situation etagChan := Srv.Store.Post().GetEtag(id) - if !c.HasPermissionsToChannel(cchan, "getPostsBeforeOrAfter") { + if !HasPermissionToChannelContext(c, id, model.PERMISSION_READ_CHANNEL) { return } diff --git a/api/post_test.go b/api/post_test.go index 2239e92cd..8f8bca899 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -778,6 +778,7 @@ func TestDeletePosts(t *testing.T) { post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) th.LoginBasic2() + Client.Must(Client.JoinChannel(channel1.Id)) Client.Must(Client.DeletePost(channel1.Id, post4.Id)) } diff --git a/api/team.go b/api/team.go index 402a73564..83367f31f 100644 --- a/api/team.go +++ b/api/team.go @@ -259,9 +259,18 @@ func JoinUserToTeamById(teamId string, user *model.User) *model.AppError { func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError { - tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id} + tm := &model.TeamMember{ + TeamId: team.Id, + UserId: user.Id, + Roles: model.ROLE_TEAM_USER.Id, + } + + channelRole := model.ROLE_CHANNEL_USER.Id - channelRole := "" + if team.Email == user.Email { + tm.Roles = model.ROLE_TEAM_USER.Id + " " + model.ROLE_TEAM_ADMIN.Id + channelRole = model.ROLE_CHANNEL_USER.Id + " " + model.ROLE_CHANNEL_ADMIN.Id + } if etmr := <-Srv.Store.Team().GetMember(team.Id, user.Id); etmr.Err == nil { // Membership alredy exists. Check if deleted and and update, otherwise do nothing @@ -276,11 +285,6 @@ func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError { return tmr.Err } } else { - if team.Email == user.Email { - tm.Roles = model.ROLE_TEAM_ADMIN - channelRole = model.CHANNEL_ROLE_ADMIN - } - // Membership appears to be missing. Lets try to add. if tmr := <-Srv.Store.Team().SaveMember(tm); tmr.Err != nil { return tmr.Err @@ -361,7 +365,7 @@ func isTeamCreationAllowed(c *Context, email string) bool { email = strings.ToLower(email) - if !c.IsSystemAdmin() && !utils.Cfg.TeamSettings.EnableTeamCreation { + if !utils.Cfg.TeamSettings.EnableTeamCreation && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { c.Err = model.NewLocAppError("isTeamCreationAllowed", "api.team.is_team_creation_allowed.disabled.app_error", nil, "") return false } @@ -402,9 +406,10 @@ func GetAllTeamListings(c *Context, w http.ResponseWriter, r *http.Request) { m := make(map[string]*model.Team) for _, v := range teams { m[v.Id] = v - if !c.IsSystemAdmin() { + if !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { m[v.Id].Sanitize() } + c.Err = nil } w.Write([]byte(model.TeamMapToJson(m))) @@ -415,9 +420,10 @@ func GetAllTeamListings(c *Context, w http.ResponseWriter, r *http.Request) { // on the server. Otherwise, it will only be the teams of which the user is a member. func getAll(c *Context, w http.ResponseWriter, r *http.Request) { var tchan store.StoreChannel - if c.IsSystemAdmin() { + if HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { tchan = Srv.Store.Team().GetAll() } else { + c.Err = nil tchan = Srv.Store.Team().GetTeamsByUserId(c.Session.UserId) } @@ -472,13 +478,14 @@ func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) { } if utils.IsLicensed { - if *utils.Cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_SYSTEM_ADMIN && !c.IsSystemAdmin() { - c.Err = model.NewLocAppError("inviteMembers", "api.team.invite_members.restricted_system_admin.app_error", nil, "") - return - } - - if *utils.Cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_TEAM_ADMIN && !c.IsTeamAdmin() { - c.Err = model.NewLocAppError("inviteMembers", "api.team.invite_members.restricted_team_admin.app_error", nil, "") + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_INVITE_USER) { + if *utils.Cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_SYSTEM_ADMIN { + c.Err = model.NewLocAppError("inviteMembers", "api.team.invite_members.restricted_system_admin.app_error", nil, "") + } + if *utils.Cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_TEAM_ADMIN { + c.Err = model.NewLocAppError("inviteMembers", "api.team.invite_members.restricted_team_admin.app_error", nil, "") + } + c.Err.StatusCode = http.StatusForbidden return } } @@ -540,9 +547,7 @@ func addUserToTeam(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - if !c.IsTeamAdmin() { - c.Err = model.NewLocAppError("addUserToTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId) - c.Err.StatusCode = http.StatusForbidden + if !HasPermissionToTeamContext(c, team.Id, model.PERMISSION_ADD_USER_TO_TEAM) { return } @@ -584,9 +589,7 @@ func removeUserFromTeam(c *Context, w http.ResponseWriter, r *http.Request) { } if c.Session.UserId != user.Id { - if !c.IsTeamAdmin() { - c.Err = model.NewLocAppError("removeUserFromTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId) - c.Err.StatusCode = http.StatusForbidden + if !HasPermissionToTeamContext(c, team.Id, model.PERMISSION_REMOVE_USER_FROM_TEAM) { return } } @@ -703,12 +706,7 @@ func InviteMembers(c *Context, team *model.Team, user *model.User, invites []str sender := user.GetDisplayName() - senderRole := "" - if c.IsTeamAdmin() { - senderRole = c.T("api.team.invite_members.admin") - } else { - senderRole = c.T("api.team.invite_members.member") - } + senderRole := c.T("api.team.invite_members.member") subjectPage := utils.NewHTMLTemplate("invite_subject", c.Locale) subjectPage.Props["Subject"] = c.T("api.templates.invite_subject", @@ -755,7 +753,7 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) { team.Id = c.TeamId - if !c.IsTeamAdmin() { + if !HasPermissionToTeamContext(c, team.Id, model.PERMISSION_MANAGE_TEAM) { c.Err = model.NewLocAppError("updateTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId) c.Err.StatusCode = http.StatusForbidden return @@ -833,7 +831,7 @@ func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) { } func importTeam(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasPermissionsToTeam(c.TeamId, "import") || !c.IsTeamAdmin() { + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_IMPORT_TEAM) { c.Err = model.NewLocAppError("importTeam", "api.team.import_team.admin.app_error", nil, "userId="+c.Session.UserId) c.Err.StatusCode = http.StatusForbidden return @@ -930,7 +928,7 @@ func getMembers(c *Context, w http.ResponseWriter, r *http.Request) { id := params["id"] if c.Session.GetTeamByTeamId(id) == nil { - if !c.HasSystemAdminPermissions("getMembers") { + if !HasPermissionToTeamContext(c, id, model.PERMISSION_MANAGE_SYSTEM) { return } } diff --git a/api/team_test.go b/api/team_test.go index ade65edcd..1a754b5e6 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -175,14 +175,14 @@ func TestRemoveUserFromTeam(t *testing.T) { t.Fatal("should fail not enough permissions") } else { if err.Id != "api.context.permissions.app_error" { - t.Fatal("wrong error") + t.Fatal("wrong error. Got: " + err.Id) } } if _, err := th.BasicClient.RemoveUserFromTeam("", th.SystemAdminUser.Id); err == nil { t.Fatal("should fail not enough permissions") } else { - if err.Id != "api.team.update_team.permissions.app_error" { + if err.Id != "api.context.permissions.app_error" { t.Fatal("wrong error") } } @@ -318,7 +318,7 @@ func TestGetAllTeamListings(t *testing.T) { c := &Context{} c.RequestId = model.NewId() c.IpAddress = "cmd_line" - UpdateUserRoles(c, user, model.ROLE_SYSTEM_ADMIN) + UpdateUserRoles(c, user, model.ROLE_SYSTEM_ADMIN.Id) Client.Login(user.Email, "passwd1") Client.SetTeamId(team.Id) @@ -415,8 +415,10 @@ func TestInviteMembers(t *testing.T) { restrictTeamInvite := *utils.Cfg.TeamSettings.RestrictTeamInvite defer func() { *utils.Cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite + utils.SetDefaultRolesBasedOnConfig() }() *utils.Cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN + utils.SetDefaultRolesBasedOnConfig() th.LoginBasic2() LinkUserToTeam(th.BasicUser2, team) @@ -445,6 +447,7 @@ func TestInviteMembers(t *testing.T) { } *utils.Cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.InviteMembers(invites); err == nil { t.Fatal("should have errored not system admin and licensed") diff --git a/api/user.go b/api/user.go index a33e2f7d7..adcc44f30 100644 --- a/api/user.go +++ b/api/user.go @@ -230,16 +230,16 @@ func IsVerifyHashRequired(user *model.User, team *model.Team, hash string) bool func CreateUser(user *model.User) (*model.User, *model.AppError) { - user.Roles = "" + user.Roles = model.ROLE_SYSTEM_USER.Id // Below is a special case where the first user in the entire - // system is granted the system_admin role instead of admin + // system is granted the system_admin role if result := <-Srv.Store.User().GetTotalUsersCount(); result.Err != nil { return nil, result.Err } else { count := result.Data.(int64) if count <= 0 { - user.Roles = model.ROLE_SYSTEM_ADMIN + user.Roles = model.ROLE_SYSTEM_ADMIN.Id + " " + model.ROLE_SYSTEM_USER.Id } } @@ -561,7 +561,7 @@ func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service st // User MUST be authenticated completely before calling Login func doLogin(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) { - session := &model.Session{UserId: user.Id, Roles: user.Roles, DeviceId: deviceId, IsOAuth: false} + session := &model.Session{UserId: user.Id, Roles: user.GetRawRoles(), DeviceId: deviceId, IsOAuth: false} maxAge := *utils.Cfg.ServiceSettings.SessionLengthWebInDays * 60 * 60 * 24 @@ -788,7 +788,7 @@ func getSessions(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["user_id"] - if !c.HasPermissionsToUser(id, "getSessions") { + if !HasPermissionToUser(c, id) { return } @@ -918,11 +918,12 @@ func getInitialLoad(c *Context, w http.ResponseWriter, r *http.Request) { } il.ClientCfg = utils.ClientCfg - if c.IsSystemAdmin() { + if HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { il.LicenseCfg = utils.ClientLicense } else { il.LicenseCfg = utils.GetSanitizedClientLicense() } + c.Err = nil w.Write([]byte(il.ToJson())) } @@ -931,7 +932,7 @@ func getUser(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["user_id"] - if !c.HasPermissionsToUser(id, "getUser") { + if !HasPermissionToUser(c, id) { return } @@ -956,7 +957,7 @@ func getProfilesForDirectMessageList(c *Context, w http.ResponseWriter, r *http. if *utils.Cfg.TeamSettings.RestrictDirectMessage == model.DIRECT_MESSAGE_TEAM { if c.Session.GetTeamByTeamId(id) == nil { - if !c.HasSystemAdminPermissions("getProfiles") { + if !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { return } } @@ -985,7 +986,7 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) { id := params["id"] if c.Session.GetTeamByTeamId(id) == nil { - if !c.HasSystemAdminPermissions("getProfiles") { + if !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { return } } @@ -1035,7 +1036,7 @@ func getAudits(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["user_id"] - if !c.HasPermissionsToUser(id, "getAudits") { + if !HasPermissionToUser(c, id) { return } @@ -1292,7 +1293,7 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.HasPermissionsToUser(user.Id, "updateUser") { + if !HasPermissionToUser(c, user.Id) { return } @@ -1432,22 +1433,21 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { } new_roles := props["new_roles"] - if !(model.IsValidUserRoles(new_roles) || model.IsValidTeamRoles(new_roles)) { + if !(model.IsValidUserRoles(new_roles)) { c.SetInvalidParam("updateRoles", "new_roles") return } // If you are not the team admin then you can only demote yourself - if !c.IsTeamAdmin() && user_id != c.Session.UserId { + if user_id != c.Session.UserId && !HasPermissionToTeamContext(c, team_id, model.PERMISSION_MANAGE_ROLES) { c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.team_admin_needed.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden return } - // Only another system admin can add the system admin role - if model.IsInRole(new_roles, model.ROLE_SYSTEM_ADMIN) && !c.IsSystemAdmin() { + // If your trying to assign the system admin role, you must have that permission + if model.IsInRole(new_roles, model.ROLE_SYSTEM_ADMIN.Id) && !HasPermissionToContext(c, model.PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE) { c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.system_admin_set.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden return } @@ -1459,15 +1459,15 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - // only another system admin can remove another system admin - if model.IsInRole(user.Roles, model.ROLE_SYSTEM_ADMIN) && !c.IsSystemAdmin() { + // only another system admin can modify another system admin + if model.IsInRole(user.GetRawRoles(), model.ROLE_SYSTEM_ADMIN.Id) && !HasPermissionToContext(c, model.PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE) { c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.system_admin_needed.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden return } // if the team role has changed then lets update team members - if model.IsValidTeamRoles(new_roles) && len(team_id) > 0 { + if len(team_id) > 0 { var members []*model.TeamMember if result := <-Srv.Store.Team().GetTeamsForUser(user_id); result.Err != nil { @@ -1489,7 +1489,7 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.IsSystemAdmin() { + if !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { currentUserTeamMember := c.Session.GetTeamByTeamId(team_id) // Only the system admin can modify other team @@ -1500,12 +1500,13 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { } // Only another team admin can make a team admin - if !currentUserTeamMember.IsTeamAdmin() && model.IsInRole(new_roles, model.ROLE_TEAM_ADMIN) { + if model.IsInRole(new_roles, model.ROLE_TEAM_ADMIN.Id) && !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_ROLES) { c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.team_admin_needed.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden return } } + c.Err = nil member.Roles = new_roles @@ -1513,10 +1514,8 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = result.Err return } - } - - // If the users role has changed then lets update the user - if model.IsValidUserRoles(new_roles) { + } else { + // If the users role has changed then lets update the user UpdateUserRoles(c, user, new_roles) if c.Err != nil { return @@ -1575,7 +1574,7 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) { // true when you're trying to de-activate yourself isSelfDeactive := !active && user_id == c.Session.UserId - if !isSelfDeactive && !c.IsSystemAdmin() { + if !isSelfDeactive && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { c.Err = model.NewLocAppError("updateActive", "api.user.update_active.permissions.app_error", nil, "userId="+user_id) c.Err.StatusCode = http.StatusForbidden return @@ -1626,7 +1625,7 @@ func PermanentDeleteUser(c *Context, user *model.User) *model.AppError { c.Path = "/users/permanent_delete" c.LogAuditWithUserId(user.Id, fmt.Sprintf("attempt userId=%v", user.Id)) c.LogAuditWithUserId("", fmt.Sprintf("attempt userId=%v", user.Id)) - if user.IsInRole(model.ROLE_SYSTEM_ADMIN) { + if user.IsInRole(model.ROLE_SYSTEM_ADMIN.Id) { l4g.Warn(utils.T("api.user.permanent_delete_user.system_admin.warn"), user.Email) } @@ -1814,7 +1813,7 @@ func ResetPassword(c *Context, userId, newPassword string) *model.AppError { user = result.Data.(*model.User) } - if user.AuthData != nil && len(*user.AuthData) != 0 && !c.IsSystemAdmin() { + if user.AuthData != nil && len(*user.AuthData) != 0 && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { return model.NewLocAppError("ResetPassword", "api.user.reset_password.sso.app_error", nil, "userId="+user.Id) } @@ -1911,7 +1910,7 @@ func updateUserNotify(c *Context, w http.ResponseWriter, r *http.Request) { uchan := Srv.Store.User().Get(user_id) - if !c.HasPermissionsToUser(user_id, "updateUserNotify") { + if !HasPermissionToUser(c, user_id) { return } @@ -2567,10 +2566,11 @@ func userTyping(req *model.WebSocketRequest) (map[string]interface{}, *model.App func sanitizeProfile(c *Context, user *model.User) *model.User { options := utils.Cfg.GetSanitizeOptions() - if c.IsSystemAdmin() { + if HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) { options["email"] = true options["fullname"] = true } + c.Err = nil user.SanitizeProfile(options) diff --git a/api/user_test.go b/api/user_test.go index 15397ff0f..d2c15f730 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -420,7 +420,7 @@ func TestGetUser(t *testing.T) { c := &Context{} c.RequestId = model.NewId() c.IpAddress = "cmd_line" - UpdateUserRoles(c, ruser.Data.(*model.User), model.ROLE_SYSTEM_ADMIN) + UpdateUserRoles(c, ruser.Data.(*model.User), model.ROLE_SYSTEM_ADMIN.Id) Client.Login(user.Email, "passwd1") @@ -748,7 +748,7 @@ func TestUserUpdate(t *testing.T) { Client.SetTeamId(team.Id) user.Nickname = "Jim Jimmy" - user.Roles = model.ROLE_TEAM_ADMIN + user.Roles = model.ROLE_SYSTEM_ADMIN.Id user.LastPasswordUpdate = 123 if result, err := Client.UpdateUser(user); err != nil { @@ -757,7 +757,7 @@ func TestUserUpdate(t *testing.T) { if result.Data.(*model.User).Nickname != "Jim Jimmy" { t.Fatal("Nickname did not update properly") } - if result.Data.(*model.User).Roles != "" { + if result.Data.(*model.User).Roles != model.ROLE_SYSTEM_USER.Id { t.Fatal("Roles should not have updated") } if result.Data.(*model.User).LastPasswordUpdate == 123 { @@ -957,7 +957,7 @@ func TestUserUpdateRolesMoreCases(t *testing.T) { // user 1 is trying to promote user 2 data["user_id"] = th.BasicUser2.Id - data["new_roles"] = model.ROLE_TEAM_ADMIN + data["new_roles"] = model.ROLE_TEAM_ADMIN.Id data["team_id"] = th.BasicTeam.Id if _, err := th.BasicClient.UpdateUserRoles(data); err == nil { t.Fatal("Should have errored, you can only demote yourself") @@ -965,7 +965,7 @@ func TestUserUpdateRolesMoreCases(t *testing.T) { // user 1 is trying to promote user 2 data["user_id"] = th.BasicUser2.Id - data["new_roles"] = model.ROLE_SYSTEM_ADMIN + data["new_roles"] = model.ROLE_SYSTEM_ADMIN.Id data["team_id"] = th.BasicTeam.Id if _, err := th.BasicClient.UpdateUserRoles(data); err == nil { t.Fatal("Should have errored, you can only demote yourself") @@ -973,7 +973,7 @@ func TestUserUpdateRolesMoreCases(t *testing.T) { // user 1 is trying to promote himself data["user_id"] = th.BasicUser.Id - data["new_roles"] = model.ROLE_TEAM_ADMIN + data["new_roles"] = model.ROLE_TEAM_ADMIN.Id data["team_id"] = th.BasicTeam.Id if _, err := th.BasicClient.UpdateUserRoles(data); err == nil { t.Fatal("Should have errored, you cannot elevate your permissions") @@ -981,7 +981,7 @@ func TestUserUpdateRolesMoreCases(t *testing.T) { // user 1 is trying to promote himself data["user_id"] = th.BasicUser.Id - data["new_roles"] = model.ROLE_SYSTEM_ADMIN + data["new_roles"] = model.ROLE_SYSTEM_ADMIN.Id data["team_id"] = th.BasicTeam.Id if _, err := th.BasicClient.UpdateUserRoles(data); err == nil { t.Fatal("Should have errored, you cannot elevate your permissions") @@ -991,7 +991,7 @@ func TestUserUpdateRolesMoreCases(t *testing.T) { // promote user to team admin data["user_id"] = th.BasicUser.Id - data["new_roles"] = model.ROLE_TEAM_ADMIN + data["new_roles"] = model.ROLE_TEAM_ADMIN.Id data["team_id"] = th.BasicTeam.Id if _, err := th.SystemAdminClient.UpdateUserRoles(data); err != nil { t.Fatal("Should have succeeded since they are system admin") @@ -1007,7 +1007,7 @@ func TestUserUpdateRolesMoreCases(t *testing.T) { // re-promote user to team admin data["user_id"] = th.BasicUser.Id - data["new_roles"] = model.ROLE_TEAM_ADMIN + data["new_roles"] = model.ROLE_TEAM_ADMIN.Id data["team_id"] = th.BasicTeam.Id if _, err := th.SystemAdminClient.UpdateUserRoles(data); err != nil { t.Fatal("Should have succeeded since they are system admin") @@ -1015,7 +1015,7 @@ func TestUserUpdateRolesMoreCases(t *testing.T) { // user 1 is promoting user 2 to team admin data["user_id"] = th.BasicUser2.Id - data["new_roles"] = model.ROLE_TEAM_ADMIN + data["new_roles"] = model.ROLE_TEAM_ADMIN.Id data["team_id"] = th.BasicTeam.Id if _, err := th.BasicClient.UpdateUserRoles(data); err != nil { t.Fatal("Should have succeeded since they are team admin") @@ -1023,7 +1023,7 @@ func TestUserUpdateRolesMoreCases(t *testing.T) { // user 1 is trying to promote user 2 from team admin to system admin data["user_id"] = th.BasicUser2.Id - data["new_roles"] = model.ROLE_SYSTEM_ADMIN + data["new_roles"] = model.ROLE_SYSTEM_ADMIN.Id data["team_id"] = th.BasicTeam.Id if _, err := th.BasicClient.UpdateUserRoles(data); err == nil { t.Fatal("Should have errored, can only be system admin") diff --git a/api/webhook.go b/api/webhook.go index a9eded2ac..2995a4a9d 100644 --- a/api/webhook.go +++ b/api/webhook.go @@ -41,12 +41,8 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("createIncomingHook", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_WEBHOOKS) { + return } c.LogAudit("attempt") @@ -59,7 +55,6 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { } cchan := Srv.Store.Channel().Get(hook.ChannelId) - pchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, hook.ChannelId, c.Session.UserId) hook.UserId = c.Session.UserId hook.TeamId = c.TeamId @@ -72,12 +67,9 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { channel = result.Data.(*model.Channel) } - if !c.HasPermissionsToChannel(pchan, "createIncomingHook") { - if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.TeamId { - c.LogAudit("fail - bad channel permissions") - return - } - c.Err = nil + if channel.Type != model.CHANNEL_OPEN && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_READ_CHANNEL) { + c.LogAudit("fail - bad channel permissions") + return } if result := <-Srv.Store.Webhook().SaveIncoming(hook); result.Err != nil { @@ -97,12 +89,10 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("deleteIncomingHook", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_WEBHOOKS) { + c.Err = model.NewLocAppError("deleteIncomingHook", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } c.LogAudit("attempt") @@ -119,7 +109,7 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = result.Err return } else { - if c.Session.UserId != result.Data.(*model.IncomingWebhook).UserId && !c.IsTeamAdmin() { + if c.Session.UserId != result.Data.(*model.IncomingWebhook).UserId && !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("deleteIncomingHook", "api.webhook.delete_incoming.permissions.app_errror", nil, "user_id="+c.Session.UserId) return @@ -142,12 +132,10 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("getIncomingHooks", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_WEBHOOKS) { + c.Err = model.NewLocAppError("getIncomingHooks", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } if result := <-Srv.Store.Webhook().GetIncomingByTeam(c.TeamId); result.Err != nil { @@ -166,12 +154,10 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("createOutgoingHook", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_WEBHOOKS) { + c.Err = model.NewLocAppError("createOutgoingHook", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } c.LogAudit("attempt") @@ -188,7 +174,6 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { if len(hook.ChannelId) != 0 { cchan := Srv.Store.Channel().Get(hook.ChannelId) - pchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, hook.ChannelId, c.Session.UserId) var channel *model.Channel if result := <-cchan; result.Err != nil { @@ -204,14 +189,10 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.HasPermissionsToChannel(pchan, "createOutgoingHook") { - if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.TeamId { - c.LogAudit("fail - bad channel permissions") - c.Err = model.NewLocAppError("createOutgoingHook", "api.webhook.create_outgoing.permissions.app_error", nil, "") - return - } else { - c.Err = nil - } + if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.TeamId { + c.LogAudit("fail - bad channel permissions") + c.Err = model.NewLocAppError("createOutgoingHook", "api.webhook.create_outgoing.permissions.app_error", nil, "") + return } } else if len(hook.TriggerWords) == 0 { c.Err = model.NewLocAppError("createOutgoingHook", "api.webhook.create_outgoing.triggers.app_error", nil, "") @@ -252,12 +233,10 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("getOutgoingHooks", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_WEBHOOKS) { + c.Err = model.NewLocAppError("getOutgoingHooks", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } if result := <-Srv.Store.Webhook().GetOutgoingByTeam(c.TeamId); result.Err != nil { @@ -276,12 +255,10 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("deleteOutgoingHook", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_WEBHOOKS) { + c.Err = model.NewLocAppError("deleteOutgoingHook", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } c.LogAudit("attempt") @@ -298,7 +275,7 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = result.Err return } else { - if c.Session.UserId != result.Data.(*model.OutgoingWebhook).CreatorId && !c.IsTeamAdmin() { + if c.Session.UserId != result.Data.(*model.OutgoingWebhook).CreatorId && !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("deleteOutgoingHook", "api.webhook.delete_outgoing.permissions.app_error", nil, "user_id="+c.Session.UserId) return @@ -321,12 +298,10 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request) return } - if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations { - if !(c.IsSystemAdmin() || c.IsTeamAdmin()) { - c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } + if !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_WEBHOOKS) { + c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.command.admin_only.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + return } c.LogAudit("attempt") @@ -346,7 +321,7 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request) } else { hook = result.Data.(*model.OutgoingWebhook) - if c.TeamId != hook.TeamId && c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() { + if c.TeamId != hook.TeamId && c.Session.UserId != hook.CreatorId && !HasPermissionToCurrentTeamContext(c, model.PERMISSION_MANAGE_OTHERS_WEBHOOKS) { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.webhook.regen_outgoing_token.permissions.app_error", nil, "user_id="+c.Session.UserId) return @@ -465,18 +440,20 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { channel = result.Data.(*model.Channel) } - pchan := Srv.Store.Channel().CheckPermissionsTo(hook.TeamId, channel.Id, hook.UserId) - // create a mock session c.Session = model.Session{ - UserId: hook.UserId, - TeamMembers: []*model.TeamMember{{TeamId: hook.TeamId, UserId: hook.UserId}}, - IsOAuth: false, + UserId: hook.UserId, + TeamMembers: []*model.TeamMember{{ + TeamId: hook.TeamId, + UserId: hook.UserId, + Roles: model.ROLE_CHANNEL_USER.Id, + }}, + IsOAuth: false, } c.TeamId = hook.TeamId - if !c.HasPermissionsToChannel(pchan, "createIncomingHook") && channel.Type != model.CHANNEL_OPEN { + if channel.Type != model.CHANNEL_OPEN && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_READ_CHANNEL) { c.Err = model.NewLocAppError("incomingWebhook", "web.incoming_webhook.permissions.app_error", nil, "") return } diff --git a/api/webhook_test.go b/api/webhook_test.go index f2375fb19..b3fa04d88 100644 --- a/api/webhook_test.go +++ b/api/webhook_test.go @@ -26,9 +26,11 @@ func TestCreateIncomingHook(t *testing.T) { defer func() { utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() }() utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() hook := &model.IncomingWebhook{ChannelId: channel1.Id} @@ -85,7 +87,17 @@ func TestCreateIncomingHook(t *testing.T) { t.Fatal("should have failed - not system/team admin") } + Client.Logout() + UpdateUserToTeamAdmin(user2, team) + Client.Must(Client.LoginById(user2.Id, user2.Password)) + Client.SetTeamId(team.Id) + + if _, err := Client.CreateIncomingWebhook(hook); err != nil { + t.Fatal(err) + } + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.CreateIncomingWebhook(hook); err != nil { t.Fatal(err) @@ -117,9 +129,11 @@ func TestListIncomingHooks(t *testing.T) { defer func() { utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() }() utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() hook1 := &model.IncomingWebhook{ChannelId: channel1.Id} hook1 = Client.Must(Client.CreateIncomingWebhook(hook1)).Data.(*model.IncomingWebhook) @@ -146,6 +160,7 @@ func TestListIncomingHooks(t *testing.T) { } *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.ListIncomingWebhooks(); err != nil { t.Fatal(err) @@ -171,9 +186,11 @@ func TestDeleteIncomingHook(t *testing.T) { defer func() { utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() }() utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() hook := &model.IncomingWebhook{ChannelId: channel1.Id} hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook) @@ -207,6 +224,7 @@ func TestDeleteIncomingHook(t *testing.T) { } *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil { t.Fatal("should have failed - not creator or team admin") @@ -244,9 +262,11 @@ func TestCreateOutgoingHook(t *testing.T) { defer func() { utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() }() utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} @@ -317,6 +337,7 @@ func TestCreateOutgoingHook(t *testing.T) { } *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.CreateOutgoingWebhook(hook); err != nil { t.Fatal(err) @@ -350,9 +371,11 @@ func TestListOutgoingHooks(t *testing.T) { defer func() { utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() }() utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() hook1 := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook1 = Client.Must(Client.CreateOutgoingWebhook(hook1)).Data.(*model.OutgoingWebhook) @@ -379,6 +402,7 @@ func TestListOutgoingHooks(t *testing.T) { } *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.ListOutgoingWebhooks(); err != nil { t.Fatal(err) @@ -404,9 +428,11 @@ func TestDeleteOutgoingHook(t *testing.T) { defer func() { utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() }() utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) @@ -440,6 +466,7 @@ func TestDeleteOutgoingHook(t *testing.T) { } *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil { t.Fatal("should have failed - not creator or team admin") @@ -475,9 +502,11 @@ func TestRegenOutgoingHookToken(t *testing.T) { defer func() { utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks + utils.SetDefaultRolesBasedOnConfig() }() utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true + utils.SetDefaultRolesBasedOnConfig() hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) @@ -507,6 +536,7 @@ func TestRegenOutgoingHookToken(t *testing.T) { } *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + utils.SetDefaultRolesBasedOnConfig() hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) -- cgit v1.2.3-1-g7c22