summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--api/admin.go75
-rw-r--r--api/apitestlib.go6
-rw-r--r--api/authorization.go149
-rw-r--r--api/authorization_test.go36
-rw-r--r--api/channel.go145
-rw-r--r--api/channel_test.go27
-rw-r--r--api/cli_test.go4
-rw-r--r--api/command.go48
-rw-r--r--api/context.go92
-rw-r--r--api/context_test.go20
-rw-r--r--api/emoji.go2
-rw-r--r--api/file.go22
-rw-r--r--api/license.go7
-rw-r--r--api/oauth.go54
-rw-r--r--api/oauth_test.go2
-rw-r--r--api/post.go68
-rw-r--r--api/post_test.go1
-rw-r--r--api/slackimport.go49
-rw-r--r--api/team.go70
-rw-r--r--api/team_test.go9
-rw-r--r--api/user.go67
-rw-r--r--api/user_test.go72
-rw-r--r--api/webhook.go111
-rw-r--r--api/webhook_test.go30
-rw-r--r--i18n/en.json40
-rw-r--r--mattermost.go1
-rw-r--r--model/authorization.go371
-rw-r--r--model/channel_member.go11
-rw-r--r--model/channel_member_test.go7
-rw-r--r--model/session.go4
-rw-r--r--model/team_member.go44
-rw-r--r--model/team_member_test.go4
-rw-r--r--model/user.go24
-rw-r--r--model/user_test.go4
-rw-r--r--store/sql_team_store.go2
-rw-r--r--store/sql_upgrade.go16
-rw-r--r--store/sql_user_store.go2
-rw-r--r--store/sql_user_store_test.go2
-rw-r--r--utils/authorization.go81
-rw-r--r--utils/config.go6
-rw-r--r--web/web_test.go4
-rw-r--r--webapp/actions/team_actions.jsx2
-rw-r--r--webapp/client/client.jsx9
-rw-r--r--webapp/components/admin_console/generated_setting.jsx8
-rw-r--r--webapp/components/admin_console/password_settings.jsx14
-rw-r--r--webapp/components/admin_console/select_team_modal.jsx7
-rw-r--r--webapp/components/admin_console/user_item.jsx14
-rw-r--r--webapp/components/edit_post_modal.jsx3
-rw-r--r--webapp/components/help/components/composing.jsx2
-rw-r--r--webapp/components/navbar_dropdown.jsx17
-rw-r--r--webapp/components/post_view/components/post.jsx2
-rw-r--r--webapp/components/post_view/components/post_body.jsx14
-rw-r--r--webapp/components/post_view/components/post_header.jsx2
-rw-r--r--webapp/components/post_view/components/post_info.jsx8
-rw-r--r--webapp/components/post_view/components/post_list.jsx39
-rw-r--r--webapp/components/sidebar_header.jsx10
-rw-r--r--webapp/components/suggestion/search_channel_provider.jsx2
-rw-r--r--webapp/components/suggestion/search_user_provider.jsx2
-rw-r--r--webapp/components/team_import_tab.jsx4
-rw-r--r--webapp/components/team_members_dropdown.jsx14
-rw-r--r--webapp/i18n/en.json4
-rw-r--r--webapp/root.html3
-rw-r--r--webapp/sass/layout/_post.scss5
-rw-r--r--webapp/tests/client_admin.test.jsx22
-rw-r--r--webapp/tests/client_user.test.jsx15
-rw-r--r--webapp/utils/utils.jsx8
67 files changed, 1318 insertions, 703 deletions
diff --git a/Makefile b/Makefile
index 8eb027707..16162d976 100644
--- a/Makefile
+++ b/Makefile
@@ -82,7 +82,7 @@ start-docker:
-e LDAP_ORGANISATION="Mattermost Test" \
-e LDAP_DOMAIN="mm.test.com" \
-e LDAP_ADMIN_PASSWORD="mostest" \
- -d osixia/openldap:1.1.2 > /dev/null;\
+ -d osixia/openldap:1.1.6 > /dev/null;\
sleep 10; \
docker exec -ti mattermost-openldap bash -c 'echo -e "dn: ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: organizationalunit" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';\
docker exec -ti mattermost-openldap bash -c 'echo -e "dn: uid=test.one,ou=testusers,dc=mm,dc=test,dc=com\nobjectclass: iNetOrgPerson\nsn: User\ncn: Test1\nmail: success+testone@simulator.amazonses.com" | ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';\
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 f581b1bf4..c43bc9843 100644
--- a/api/post.go
+++ b/api/post.go
@@ -6,6 +6,7 @@ package api
import (
"crypto/tls"
"fmt"
+ "html"
"html/template"
"io"
"io/ioutil"
@@ -58,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
}
@@ -194,14 +209,12 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc
// parse attachment links into Markdown format
for i, aInt := range list {
attachment := aInt.(map[string]interface{})
- if _, ok := attachment["text"]; ok {
- aText := attachment["text"].(string)
+ if aText, ok := attachment["text"].(string); ok {
aText = linkWithTextRegex.ReplaceAllString(aText, "[${2}](${1})")
attachment["text"] = aText
list[i] = attachment
}
- if _, ok := attachment["pretext"]; ok {
- aText := attachment["pretext"].(string)
+ if aText, ok := attachment["pretext"].(string); ok {
aText = linkWithTextRegex.ReplaceAllString(aText, "[${2}](${1})")
attachment["pretext"] = aText
list[i] = attachment
@@ -211,8 +224,7 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc
// parse attachment field links into Markdown format
for j, fInt := range fields {
field := fInt.(map[string]interface{})
- if _, ok := field["value"]; ok {
- fValue := field["value"].(string)
+ if fValue, ok := field["value"].(string); ok {
fValue = linkWithTextRegex.ReplaceAllString(fValue, "[${2}](${1})")
field["value"] = fValue
fields[j] = field
@@ -893,7 +905,7 @@ func sendNotificationEmail(c *Context, post *model.Post, user *model.User, chann
"Hour": fmt.Sprintf("%02d", tm.Hour()), "Minute": fmt.Sprintf("%02d", tm.Minute()),
"TimeZone": zone, "Month": month, "Day": day}))
- if err := utils.SendMail(user.Email, subjectPage.Render(), bodyPage.Render()); err != nil {
+ if err := utils.SendMail(user.Email, html.UnescapeString(subjectPage.Render()), bodyPage.Render()); err != nil {
l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), user.Email, err)
}
}
@@ -1101,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
}
@@ -1206,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
}
@@ -1248,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
}
@@ -1281,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
}
@@ -1328,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
}
@@ -1363,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
@@ -1404,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 {
@@ -1414,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
@@ -1429,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
@@ -1509,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/slackimport.go b/api/slackimport.go
index f96a15be2..7790a21e3 100644
--- a/api/slackimport.go
+++ b/api/slackimport.go
@@ -66,7 +66,8 @@ func SlackParseChannels(data io.Reader) []SlackChannel {
var channels []SlackChannel
if err := decoder.Decode(&channels); err != nil {
- return make([]SlackChannel, 0)
+ l4g.Warn(utils.T("api.slackimport.slack_parse_channels.error"))
+ return channels
}
return channels
}
@@ -89,7 +90,8 @@ func SlackParsePosts(data io.Reader) []SlackPost {
var posts []SlackPost
if err := decoder.Decode(&posts); err != nil {
- return make([]SlackPost, 0)
+ l4g.Warn(utils.T("api.slackimport.slack_parse_posts.error"))
+ return posts
}
return posts
}
@@ -120,13 +122,23 @@ func SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Buffer) map
lastName = name
}
+ email := sUser.Profile["email"]
+
password := model.NewId()
+ // Check for email conflict and use existing user if found
+ if result := <-Srv.Store.User().GetByEmail(email); result.Err == nil {
+ existingUser := result.Data.(*model.User)
+ addedUsers[sUser.Id] = existingUser
+ log.WriteString(utils.T("api.slackimport.slack_add_users.merge_existing", map[string]interface{}{"Email": existingUser.Email, "Username": existingUser.Username}))
+ continue
+ }
+
newUser := model.User{
Username: sUser.Username,
FirstName: firstName,
LastName: lastName,
- Email: sUser.Profile["email"],
+ Email: email,
Password: password,
}
@@ -175,9 +187,23 @@ func SlackAddPosts(channel *model.Channel, posts []SlackPost, users map[string]*
}
ImportPost(&newPost)
case sPost.Type == "message" && sPost.SubType == "bot_message":
- // In the future this will use the "Action Post" spec to post
- // a message without using a username. For now we just warn that we don't handle this case
- l4g.Warn(utils.T("api.slackimport.slack_add_posts.bot.warn"))
+ continue
+ case sPost.Type == "message" && (sPost.SubType == "channel_join" || sPost.SubType == "channel_leave"):
+ if sPost.User == "" {
+ l4g.Debug(utils.T("api.slackimport.slack_add_posts.msg_no_usr.debug"))
+ continue
+ } else if users[sPost.User] == nil {
+ l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
+ continue
+ }
+ newPost := model.Post{
+ UserId: users[sPost.User].Id,
+ ChannelId: channel.Id,
+ Message: sPost.Text,
+ CreateAt: SlackConvertTimeStamp(sPost.TimeStamp),
+ Type: model.POST_JOIN_LEAVE,
+ }
+ ImportPost(&newPost)
default:
l4g.Warn(utils.T("api.slackimport.slack_add_posts.unsupported.warn"), sPost.Type, sPost.SubType)
}
@@ -260,20 +286,22 @@ func SlackConvertUserMentions(users []SlackUser, posts map[string][]SlackPost) m
}
func SlackImport(fileData multipart.File, fileSize int64, teamID string) (*model.AppError, *bytes.Buffer) {
+ // Create log file
+ log := bytes.NewBufferString(utils.T("api.slackimport.slack_import.log"))
+
zipreader, err := zip.NewReader(fileData, fileSize)
if err != nil || zipreader.File == nil {
- return model.NewLocAppError("SlackImport", "api.slackimport.slack_import.zip.app_error", nil, err.Error()), nil
+ log.WriteString(utils.T("api.slackimport.slack_import.zip.app_error"))
+ return model.NewLocAppError("SlackImport", "api.slackimport.slack_import.zip.app_error", nil, err.Error()), log
}
- // Create log file
- log := bytes.NewBufferString(utils.T("api.slackimport.slack_import.log"))
-
var channels []SlackChannel
var users []SlackUser
posts := make(map[string][]SlackPost)
for _, file := range zipreader.File {
reader, err := file.Open()
if err != nil {
+ log.WriteString(utils.T("api.slackimport.slack_import.open.app_error", map[string]interface{}{"Filename": file.Name}))
return model.NewLocAppError("SlackImport", "api.slackimport.slack_import.open.app_error", map[string]interface{}{"Filename": file.Name}, err.Error()), log
}
if file.Name == "channels.json" {
@@ -305,6 +333,7 @@ func SlackImport(fileData multipart.File, fileSize int64, teamID string) (*model
log.WriteString(utils.T("api.slackimport.slack_import.note1"))
log.WriteString(utils.T("api.slackimport.slack_import.note2"))
+ log.WriteString(utils.T("api.slackimport.slack_import.note3"))
return nil, log
}
diff --git a/api/team.go b/api/team.go
index 834d722ce..83367f31f 100644
--- a/api/team.go
+++ b/api/team.go
@@ -7,11 +7,11 @@ import (
"bytes"
"fmt"
"html/template"
+ "io"
"net/http"
"net/url"
"strconv"
"strings"
- "time"
l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
@@ -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
@@ -896,7 +894,11 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Disposition", "attachment; filename=MattermostImportLog.txt")
w.Header().Set("Content-Type", "application/octet-stream")
- http.ServeContent(w, r, "MattermostImportLog.txt", time.Now(), bytes.NewReader(log.Bytes()))
+ if c.Err != nil {
+ w.WriteHeader(c.Err.StatusCode)
+ }
+ io.Copy(w, bytes.NewReader(log.Bytes()))
+ //http.ServeContent(w, r, "MattermostImportLog.txt", time.Now(), bytes.NewReader(log.Bytes()))
}
func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -926,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..35cc3612e 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,18 +932,15 @@ func getUser(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["user_id"]
- if !c.HasPermissionsToUser(id, "getUser") {
- return
- }
-
if result := <-Srv.Store.User().Get(id); result.Err != nil {
c.Err = result.Err
return
} else if HandleEtag(result.Data.(*model.User).Etag(utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress), w, r) {
return
} else {
- result.Data.(*model.User).Sanitize(map[string]bool{})
- w.Header().Set(model.HEADER_ETAG_SERVER, result.Data.(*model.User).Etag(utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress))
+ user := sanitizeProfile(c, result.Data.(*model.User))
+
+ w.Header().Set(model.HEADER_ETAG_SERVER, user.Etag(utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress))
w.Write([]byte(result.Data.(*model.User).ToJson()))
return
}
@@ -956,7 +954,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 +983,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 +1033,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 +1290,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 +1430,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 +1456,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 +1486,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 +1497,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 +1511,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 +1571,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 +1622,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 +1810,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 +1907,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 +2563,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..a68d1199a 100644
--- a/api/user_test.go
+++ b/api/user_test.go
@@ -345,7 +345,7 @@ func TestGetUser(t *testing.T) {
LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id))
- user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1"}
+ user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", FirstName: "Corey", LastName: "Hulen"}
ruser2, _ := Client.CreateUser(&user2, "")
LinkUserToTeam(ruser2.Data.(*model.User), rteam.Data.(*model.Team))
store.Must(Srv.Store.User().VerifyEmail(ruser2.Data.(*model.User).Id))
@@ -387,8 +387,52 @@ func TestGetUser(t *testing.T) {
t.Fatal("shouldn't exist")
}
- if _, err := Client.GetUser(ruser2.Data.(*model.User).Id, ""); err == nil {
- t.Fatal("shouldn't have accss")
+ emailPrivacy := utils.Cfg.PrivacySettings.ShowEmailAddress
+ namePrivacy := utils.Cfg.PrivacySettings.ShowFullName
+ defer func() {
+ utils.Cfg.PrivacySettings.ShowEmailAddress = emailPrivacy
+ utils.Cfg.PrivacySettings.ShowFullName = namePrivacy
+ }()
+ utils.Cfg.PrivacySettings.ShowEmailAddress = false
+ utils.Cfg.PrivacySettings.ShowFullName = false
+
+ if result, err := Client.GetUser(ruser2.Data.(*model.User).Id, ""); err != nil {
+ t.Fatal(err)
+ } else {
+ u := result.Data.(*model.User)
+ if u.Password != "" {
+ t.Fatal("password must be empty")
+ }
+ if *u.AuthData != "" {
+ t.Fatal("auth data must be empty")
+ }
+ if u.Email != "" {
+ t.Fatal("email should be sanitized")
+ }
+ if u.FirstName != "" {
+ t.Fatal("full name should be sanitized")
+ }
+ if u.LastName != "" {
+ t.Fatal("full name should be sanitized")
+ }
+ }
+
+ utils.Cfg.PrivacySettings.ShowEmailAddress = true
+ utils.Cfg.PrivacySettings.ShowFullName = true
+
+ if result, err := Client.GetUser(ruser2.Data.(*model.User).Id, ""); err != nil {
+ t.Fatal(err)
+ } else {
+ u := result.Data.(*model.User)
+ if u.Email == "" {
+ t.Fatal("email should not be sanitized")
+ }
+ if u.FirstName == "" {
+ t.Fatal("full name should not be sanitized")
+ }
+ if u.LastName == "" {
+ t.Fatal("full name should not be sanitized")
+ }
}
if userMap, err := Client.GetProfiles(rteam.Data.(*model.Team).Id, ""); err != nil {
@@ -420,7 +464,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 +792,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 +801,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 +1001,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 +1009,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 +1017,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 +1025,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 +1035,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 +1051,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 +1059,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 +1067,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)
diff --git a/i18n/en.json b/i18n/en.json
index e9594b5e3..5306be466 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -48,8 +48,24 @@
"translation": "September"
},
{
- "id": "api.admin.add_certificate.array.app_error",
- "translation": "Empty array under 'certificate' in request"
+ "id": "authentication.permissions.team_invite_user.name",
+ "translation": "Invite User"
+ },
+ {
+ "id": "authentication.permissions.team_invite_user.description",
+ "translation": "Ability to invite users to a team"
+ },
+ {
+ "id": "authentication.permissions.team_use_slash_commands.name",
+ "translation": "User Slash Commands"
+ },
+ {
+ "id": "authentication.permissions.team_use_slash_commands.description",
+ "translation": "Ability to user slash commands"
+ },
+ {
+ "id": "api.auth.unable_to_get_user.app_error",
+ "translation": "Unable to get user to check permissions."
},
{
"id": "api.admin.add_certificate.no_file.app_error",
@@ -1154,6 +1170,10 @@
"translation": "Bad filename discarded, filename=%v"
},
{
+ "id": "api.post.create_post.can_not_post_to_deleted.error",
+ "translation": "Can not post to deleted channel."
+ },
+ {
"id": "api.post.create_post.channel_root_id.app_error",
"translation": "Invalid ChannelId for RootId parameter"
},
@@ -1424,6 +1444,14 @@
"translation": "\r\n Channels Added \r\n"
},
{
+ "id": "api.slackimport.slack_parse_channels.error",
+ "translation": "Error parsing slack channels. Import may work anyway."
+ },
+ {
+ "id": "api.slackimport.slack_parse_posts.error",
+ "translation": "Error parsing some slack posts. Import may work anyway."
+ },
+ {
"id": "api.slackimport.slack_add_channels.failed_to_add_user",
"translation": "Failed to add user to channel: {{.Username}}\r\n"
},
@@ -1468,6 +1496,10 @@
"translation": "Email, Password: {{.Email}}, {{.Password}}\r\n"
},
{
+ "id": "api.slackimport.slack_add_users.merge_existing",
+ "translation": "Merged user with existing account: {{.Email}}, {{.Username}}\r\n"
+ },
+ {
"id": "api.slackimport.slack_add_users.unable_import",
"translation": "Unable to import user: {{.Username}}\r\n"
},
@@ -1492,6 +1524,10 @@
"translation": "- Slack bot posts are currently not supported.\r\n"
},
{
+ "id": "api.slackimport.slack_import.note3",
+ "translation": "- Additional errors may be found in the server logs.\r\n"
+ },
+ {
"id": "api.slackimport.slack_import.notes",
"translation": "\r\n Notes \r\n"
},
diff --git a/mattermost.go b/mattermost.go
index 6d6130604..73fd472ef 100644
--- a/mattermost.go
+++ b/mattermost.go
@@ -437,6 +437,7 @@ func setupClientTests() {
*utils.Cfg.ServiceSettings.EnableCustomEmoji = true
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = false
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = false
+ utils.SetDefaultRolesBasedOnConfig()
}
func executeTestCommand(cmd *exec.Cmd) {
diff --git a/model/authorization.go b/model/authorization.go
new file mode 100644
index 000000000..a4d8bed86
--- /dev/null
+++ b/model/authorization.go
@@ -0,0 +1,371 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+type Permission struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+}
+
+type Role struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Permissions []string `json:"permissions"`
+}
+
+var PERMISSION_INVITE_USER *Permission
+var PERMISSION_ADD_USER_TO_TEAM *Permission
+var PERMISSION_USE_SLASH_COMMANDS *Permission
+var PERMISSION_MANAGE_SLASH_COMMANDS *Permission
+var PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS *Permission
+var PERMISSION_CREATE_PUBLIC_CHANNEL *Permission
+var PERMISSION_CREATE_PRIVATE_CHANNEL *Permission
+var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission
+var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission
+var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission
+var PERMISSION_MANAGE_ROLES *Permission
+var PERMISSION_CREATE_DIRECT_CHANNEL *Permission
+var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission
+var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission
+var PERMISSION_LIST_TEAM_CHANNELS *Permission
+var PERMISSION_JOIN_PUBLIC_CHANNELS *Permission
+var PERMISSION_DELETE_PUBLIC_CHANNEL *Permission
+var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission
+var PERMISSION_EDIT_OTHER_USERS *Permission
+var PERMISSION_READ_CHANNEL *Permission
+var PERMISSION_PERMANENT_DELETE_USER *Permission
+var PERMISSION_UPLOAD_FILE *Permission
+var PERMISSION_GET_PUBLIC_LINK *Permission
+var PERMISSION_MANAGE_WEBHOOKS *Permission
+var PERMISSION_MANAGE_OTHERS_WEBHOOKS *Permission
+var PERMISSION_MANAGE_OAUTH *Permission
+var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission
+var PERMISSION_CREATE_POST *Permission
+var PERMISSION_EDIT_POST *Permission
+var PERMISSION_EDIT_OTHERS_POSTS *Permission
+var PERMISSION_REMOVE_USER_FROM_TEAM *Permission
+var PERMISSION_MANAGE_TEAM *Permission
+var PERMISSION_IMPORT_TEAM *Permission
+
+// General permission that encompases all system admin functions
+// in the future this could be broken up to allow access to some
+// admin functions but not others
+var PERMISSION_MANAGE_SYSTEM *Permission
+
+var ROLE_SYSTEM_USER *Role
+var ROLE_SYSTEM_ADMIN *Role
+
+var ROLE_TEAM_USER *Role
+var ROLE_TEAM_ADMIN *Role
+
+var ROLE_CHANNEL_USER *Role
+var ROLE_CHANNEL_ADMIN *Role
+var ROLE_CHANNEL_GUEST *Role
+
+var BuiltInRoles map[string]*Role
+
+func InitalizePermissions() {
+ PERMISSION_INVITE_USER = &Permission{
+ "invite_user",
+ "authentication.permissions.team_invite_user.name",
+ "authentication.permissions.team_invite_user.description",
+ }
+ PERMISSION_ADD_USER_TO_TEAM = &Permission{
+ "add_user_to_team",
+ "authentication.permissions.add_user_to_team.name",
+ "authentication.permissions.add_user_to_team.description",
+ }
+ PERMISSION_USE_SLASH_COMMANDS = &Permission{
+ "use_slash_commands",
+ "authentication.permissions.team_use_slash_commands.name",
+ "authentication.permissions.team_use_slash_commands.description",
+ }
+ PERMISSION_MANAGE_SLASH_COMMANDS = &Permission{
+ "manage_slash_commands",
+ "authentication.permissions.manage_slash_commands.name",
+ "authentication.permissions.manage_slash_commands.description",
+ }
+ PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS = &Permission{
+ "manage_others_slash_commands",
+ "authentication.permissions.manage_others_slash_commands.name",
+ "authentication.permissions.manage_others_slash_commands.description",
+ }
+ PERMISSION_CREATE_PUBLIC_CHANNEL = &Permission{
+ "create_public_channel",
+ "authentication.permissions.create_public_channel.name",
+ "authentication.permissions.create_public_channel.description",
+ }
+ PERMISSION_CREATE_PRIVATE_CHANNEL = &Permission{
+ "create_private_channel",
+ "authentication.permissions.create_private_channel.name",
+ "authentication.permissions.create_private_channel.description",
+ }
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS = &Permission{
+ "manage_public_channel_members",
+ "authentication.permissions.manage_public_channel_members.name",
+ "authentication.permissions.manage_public_channel_members.description",
+ }
+ PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS = &Permission{
+ "manage_private_channel_members",
+ "authentication.permissions.manage_private_channel_members.name",
+ "authentication.permissions.manage_private_channel_members.description",
+ }
+ PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE = &Permission{
+ "assign_system_admin_role",
+ "authentication.permissions.assign_system_admin_role.name",
+ "authentication.permissions.assign_system_admin_role.description",
+ }
+ PERMISSION_MANAGE_ROLES = &Permission{
+ "manage_roles",
+ "authentication.permissions.manage_roles.name",
+ "authentication.permissions.manage_roles.description",
+ }
+ PERMISSION_MANAGE_SYSTEM = &Permission{
+ "manage_system",
+ "authentication.permissions.manage_system.name",
+ "authentication.permissions.manage_system.description",
+ }
+ PERMISSION_CREATE_DIRECT_CHANNEL = &Permission{
+ "create_direct_channel",
+ "authentication.permissions.create_direct_channel.name",
+ "authentication.permissions.create_direct_channel.description",
+ }
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{
+ "manage__publicchannel_properties",
+ "authentication.permissions.manage_public_channel_properties.name",
+ "authentication.permissions.manage_public_channel_properties.description",
+ }
+ PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES = &Permission{
+ "manage_private_channel_properties",
+ "authentication.permissions.manage_private_channel_properties.name",
+ "authentication.permissions.manage_private_channel_properties.description",
+ }
+ PERMISSION_LIST_TEAM_CHANNELS = &Permission{
+ "list_team_channels",
+ "authentication.permissions.list_team_channels.name",
+ "authentication.permissions.list_team_channels.description",
+ }
+ PERMISSION_JOIN_PUBLIC_CHANNELS = &Permission{
+ "join_public_channels",
+ "authentication.permissions.join_public_channels.name",
+ "authentication.permissions.join_public_channels.description",
+ }
+ PERMISSION_DELETE_PUBLIC_CHANNEL = &Permission{
+ "delete_public_channel",
+ "authentication.permissions.delete_public_channel.name",
+ "authentication.permissions.delete_public_channel.description",
+ }
+ PERMISSION_DELETE_PRIVATE_CHANNEL = &Permission{
+ "delete_private_channel",
+ "authentication.permissions.delete_private_channel.name",
+ "authentication.permissions.delete_private_channel.description",
+ }
+ PERMISSION_EDIT_OTHER_USERS = &Permission{
+ "edit_other_users",
+ "authentication.permissions.edit_other_users.name",
+ "authentication.permissions.edit_other_users.description",
+ }
+ PERMISSION_READ_CHANNEL = &Permission{
+ "read_channel",
+ "authentication.permissions.read_channel.name",
+ "authentication.permissions.read_channel.description",
+ }
+ PERMISSION_PERMANENT_DELETE_USER = &Permission{
+ "permanent_delete_user",
+ "authentication.permissions.permanent_delete_user.name",
+ "authentication.permissions.permanent_delete_user.description",
+ }
+ PERMISSION_UPLOAD_FILE = &Permission{
+ "upload_file",
+ "authentication.permissions.upload_file.name",
+ "authentication.permissions.upload_file.description",
+ }
+ PERMISSION_GET_PUBLIC_LINK = &Permission{
+ "get_public_link",
+ "authentication.permissions.get_public_link.name",
+ "authentication.permissions.get_public_link.description",
+ }
+ PERMISSION_MANAGE_WEBHOOKS = &Permission{
+ "manage_webhooks",
+ "authentication.permissions.manage_webhooks.name",
+ "authentication.permissions.manage_webhooks.description",
+ }
+ PERMISSION_MANAGE_OTHERS_WEBHOOKS = &Permission{
+ "manage_others_webhooks",
+ "authentication.permissions.manage_others_webhooks.name",
+ "authentication.permissions.manage_others_webhooks.description",
+ }
+ PERMISSION_MANAGE_OAUTH = &Permission{
+ "manage_oauth",
+ "authentication.permissions.manage_oauth.name",
+ "authentication.permissions.manage_oauth.description",
+ }
+ PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH = &Permission{
+ "manage_sytem_wide_oauth",
+ "authentication.permissions.manage_sytem_wide_oauth.name",
+ "authentication.permissions.manage_sytem_wide_oauth.description",
+ }
+ PERMISSION_CREATE_POST = &Permission{
+ "create_post",
+ "authentication.permissions.create_post.name",
+ "authentication.permissions.create_post.description",
+ }
+ PERMISSION_EDIT_POST = &Permission{
+ "edit_post",
+ "authentication.permissions.edit_post.name",
+ "authentication.permissions.edit_post.description",
+ }
+ PERMISSION_EDIT_OTHERS_POSTS = &Permission{
+ "edit_others_posts",
+ "authentication.permissions.edit_others_posts.name",
+ "authentication.permissions.edit_others_posts.description",
+ }
+ PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{
+ "remove_user_from_team",
+ "authentication.permissions.remove_user_from_team.name",
+ "authentication.permissions.remove_user_from_team.description",
+ }
+ PERMISSION_MANAGE_TEAM = &Permission{
+ "manage_team",
+ "authentication.permissions.manage_team.name",
+ "authentication.permissions.manage_team.description",
+ }
+ PERMISSION_IMPORT_TEAM = &Permission{
+ "import_team",
+ "authentication.permissions.import_team.name",
+ "authentication.permissions.import_team.description",
+ }
+}
+
+func InitalizeRoles() {
+ InitalizePermissions()
+ BuiltInRoles = make(map[string]*Role)
+
+ ROLE_CHANNEL_USER = &Role{
+ "channel_user",
+ "authentication.roles.channel_user.name",
+ "authentication.roles.channel_user.description",
+ []string{
+ PERMISSION_READ_CHANNEL.Id,
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
+ PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
+ PERMISSION_UPLOAD_FILE.Id,
+ PERMISSION_GET_PUBLIC_LINK.Id,
+ PERMISSION_CREATE_POST.Id,
+ PERMISSION_EDIT_POST.Id,
+ PERMISSION_USE_SLASH_COMMANDS.Id,
+ },
+ }
+ BuiltInRoles[ROLE_CHANNEL_USER.Id] = ROLE_CHANNEL_USER
+ ROLE_CHANNEL_ADMIN = &Role{
+ "channel_admin",
+ "authentication.roles.channel_admin.name",
+ "authentication.roles.channel_admin.description",
+ []string{},
+ }
+ BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN
+ ROLE_CHANNEL_GUEST = &Role{
+ "guest",
+ "authentication.roles.global_guest.name",
+ "authentication.roles.global_guest.description",
+ []string{},
+ }
+ BuiltInRoles[ROLE_CHANNEL_GUEST.Id] = ROLE_CHANNEL_GUEST
+
+ ROLE_TEAM_USER = &Role{
+ "team_user",
+ "authentication.roles.team_user.name",
+ "authentication.roles.team_user.description",
+ []string{
+ PERMISSION_LIST_TEAM_CHANNELS.Id,
+ PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
+ },
+ }
+ BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER
+ ROLE_TEAM_ADMIN = &Role{
+ "team_admin",
+ "authentication.roles.team_admin.name",
+ "authentication.roles.team_admin.description",
+ []string{
+ PERMISSION_EDIT_OTHERS_POSTS.Id,
+ PERMISSION_ADD_USER_TO_TEAM.Id,
+ PERMISSION_REMOVE_USER_FROM_TEAM.Id,
+ PERMISSION_MANAGE_TEAM.Id,
+ PERMISSION_IMPORT_TEAM.Id,
+ PERMISSION_MANAGE_ROLES.Id,
+ PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
+ PERMISSION_MANAGE_SLASH_COMMANDS.Id,
+ PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
+ PERMISSION_MANAGE_WEBHOOKS.Id,
+ },
+ }
+ BuiltInRoles[ROLE_TEAM_ADMIN.Id] = ROLE_TEAM_ADMIN
+
+ ROLE_SYSTEM_USER = &Role{
+ "system_user",
+ "authentication.roles.global_user.name",
+ "authentication.roles.global_user.description",
+ []string{
+ PERMISSION_CREATE_DIRECT_CHANNEL.Id,
+ PERMISSION_PERMANENT_DELETE_USER.Id,
+ },
+ }
+ BuiltInRoles[ROLE_SYSTEM_USER.Id] = ROLE_SYSTEM_USER
+ ROLE_SYSTEM_ADMIN = &Role{
+ "system_admin",
+ "authentication.roles.global_admin.name",
+ "authentication.roles.global_admin.description",
+ // System admins can do anything channel and team admins can do
+ // plus everything members of teams and channels can do to all teams
+ // and channels on the system
+ append(
+ append(
+ append(
+ append(
+ []string{
+ PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
+ PERMISSION_MANAGE_SYSTEM.Id,
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
+ PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
+ PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
+ PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id,
+ PERMISSION_DELETE_PRIVATE_CHANNEL.Id,
+ PERMISSION_CREATE_PRIVATE_CHANNEL.Id,
+ PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id,
+ PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
+ PERMISSION_EDIT_OTHER_USERS.Id,
+ PERMISSION_MANAGE_OAUTH.Id,
+ PERMISSION_INVITE_USER.Id,
+ },
+ ROLE_TEAM_USER.Permissions...,
+ ),
+ ROLE_CHANNEL_USER.Permissions...,
+ ),
+ ROLE_TEAM_ADMIN.Permissions...,
+ ),
+ ROLE_CHANNEL_ADMIN.Permissions...,
+ ),
+ }
+ BuiltInRoles[ROLE_SYSTEM_ADMIN.Id] = ROLE_SYSTEM_ADMIN
+
+}
+
+func RoleIdsToString(roles []string) string {
+ output := ""
+ for _, role := range roles {
+ output += role + ", "
+ }
+
+ if output == "" {
+ return "[<NO ROLES>]"
+ }
+
+ return output[:len(output)-1]
+}
+
+func init() {
+ InitalizeRoles()
+}
diff --git a/model/channel_member.go b/model/channel_member.go
index 66e20da64..705c6bfbd 100644
--- a/model/channel_member.go
+++ b/model/channel_member.go
@@ -10,7 +10,6 @@ import (
)
const (
- CHANNEL_ROLE_ADMIN = "admin"
CHANNEL_NOTIFY_DEFAULT = "default"
CHANNEL_NOTIFY_ALL = "all"
CHANNEL_NOTIFY_MENTION = "mention"
@@ -60,12 +59,6 @@ func (o *ChannelMember) IsValid() *AppError {
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "")
}
- for _, role := range strings.Split(o.Roles, " ") {
- if !(role == "" || role == CHANNEL_ROLE_ADMIN) {
- return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.role.app_error", nil, "role="+role)
- }
- }
-
notifyLevel := o.NotifyProps["desktop"]
if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) {
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error",
@@ -89,6 +82,10 @@ func (o *ChannelMember) PreUpdate() {
o.LastUpdateAt = GetMillis()
}
+func (o *ChannelMember) GetRoles() []string {
+ return strings.Fields(o.Roles)
+}
+
func IsChannelNotifyLevelValid(notifyLevel string) bool {
return notifyLevel == CHANNEL_NOTIFY_DEFAULT ||
notifyLevel == CHANNEL_NOTIFY_ALL ||
diff --git a/model/channel_member_test.go b/model/channel_member_test.go
index cff48ae27..e43560cee 100644
--- a/model/channel_member_test.go
+++ b/model/channel_member_test.go
@@ -30,14 +30,15 @@ func TestChannelMemberIsValid(t *testing.T) {
t.Fatal("should be invalid")
}
- o.Roles = "missing"
+ o.NotifyProps = GetDefaultChannelNotifyProps()
+ o.UserId = NewId()
+ /*o.Roles = "missing"
o.NotifyProps = GetDefaultChannelNotifyProps()
o.UserId = NewId()
if err := o.IsValid(); err == nil {
t.Fatal("should be invalid")
- }
+ }*/
- o.Roles = CHANNEL_ROLE_ADMIN
o.NotifyProps["desktop"] = "junk"
if err := o.IsValid(); err == nil {
t.Fatal("should be invalid")
diff --git a/model/session.go b/model/session.go
index e8b04fbe2..c3171ed7c 100644
--- a/model/session.go
+++ b/model/session.go
@@ -115,6 +115,10 @@ func (me *Session) IsMobileApp() bool {
(strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_ANDROID+":"))
}
+func (me *Session) GetUserRoles() []string {
+ return strings.Fields(me.Roles)
+}
+
func SessionsToJson(o []*Session) string {
if b, err := json.Marshal(o); err != nil {
return "[]"
diff --git a/model/team_member.go b/model/team_member.go
index 7d932dec4..f03b064ec 100644
--- a/model/team_member.go
+++ b/model/team_member.go
@@ -9,10 +9,6 @@ import (
"strings"
)
-const (
- ROLE_TEAM_ADMIN = "admin"
-)
-
type TeamMember struct {
TeamId string `json:"team_id"`
UserId string `json:"user_id"`
@@ -59,31 +55,6 @@ func TeamMembersFromJson(data io.Reader) []*TeamMember {
}
}
-func IsValidTeamRoles(teamRoles string) bool {
-
- roles := strings.Split(teamRoles, " ")
-
- for _, r := range roles {
- if !isValidTeamRole(r) {
- return false
- }
- }
-
- return true
-}
-
-func isValidTeamRole(role string) bool {
- if role == "" {
- return true
- }
-
- if role == ROLE_TEAM_ADMIN {
- return true
- }
-
- return false
-}
-
func IsInTeamRole(teamRoles string, inRole string) bool {
roles := strings.Split(teamRoles, " ")
@@ -98,7 +69,7 @@ func IsInTeamRole(teamRoles string, inRole string) bool {
}
func (o *TeamMember) IsTeamAdmin() bool {
- return IsInTeamRole(o.Roles, ROLE_TEAM_ADMIN)
+ return true
}
func (o *TeamMember) IsValid() *AppError {
@@ -111,11 +82,18 @@ func (o *TeamMember) IsValid() *AppError {
return NewLocAppError("TeamMember.IsValid", "model.team_member.is_valid.user_id.app_error", nil, "")
}
- for _, role := range strings.Split(o.Roles, " ") {
- if !(role == "" || role == ROLE_TEAM_ADMIN) {
+ /*for _, role := range strings.Split(o.Roles, " ") {
+ if !(role == "" || role == ROLE_TEAM_ADMIN.Id) {
return NewLocAppError("TeamMember.IsValid", "model.team_member.is_valid.role.app_error", nil, "role="+role)
}
- }
+ }*/
return nil
}
+
+func (o *TeamMember) PreUpdate() {
+}
+
+func (o *TeamMember) GetRoles() []string {
+ return strings.Fields(o.Roles)
+}
diff --git a/model/team_member_test.go b/model/team_member_test.go
index d5b2e3b79..eac07c646 100644
--- a/model/team_member_test.go
+++ b/model/team_member_test.go
@@ -30,7 +30,7 @@ func TestTeamMemberIsValid(t *testing.T) {
t.Fatal("should be invalid")
}
- o.UserId = NewId()
+ /*o.UserId = NewId()
o.Roles = "blahblah"
if err := o.IsValid(); err == nil {
t.Fatal("should be invalid")
@@ -39,5 +39,5 @@ func TestTeamMemberIsValid(t *testing.T) {
o.Roles = ""
if err := o.IsValid(); err != nil {
t.Fatal(err)
- }
+ }*/
}
diff --git a/model/user.go b/model/user.go
index 680bc48c9..b7ac85baa 100644
--- a/model/user.go
+++ b/model/user.go
@@ -15,7 +15,6 @@ import (
)
const (
- ROLE_SYSTEM_ADMIN = "system_admin"
USER_NOTIFY_ALL = "all"
USER_NOTIFY_MENTION = "mention"
USER_NOTIFY_NONE = "none"
@@ -319,9 +318,17 @@ func (u *User) GetDisplayNameForPreference(nameFormat string) string {
return displayName
}
+func (u *User) GetRoles() []string {
+ return strings.Fields(u.Roles)
+}
+
+func (u *User) GetRawRoles() string {
+ return u.Roles
+}
+
func IsValidUserRoles(userRoles string) bool {
- roles := strings.Split(userRoles, " ")
+ roles := strings.Fields(userRoles)
for _, r := range roles {
if !isValidRole(r) {
@@ -332,16 +339,9 @@ func IsValidUserRoles(userRoles string) bool {
return true
}
-func isValidRole(role string) bool {
- if role == "" {
- return true
- }
-
- if role == ROLE_SYSTEM_ADMIN {
- return true
- }
-
- return false
+func isValidRole(roleId string) bool {
+ _, ok := BuiltInRoles[roleId]
+ return ok
}
// Make sure you acually want to use this function. In context.go there are functions to check permissions
diff --git a/model/user_test.go b/model/user_test.go
index 16ac2583b..2f6524c05 100644
--- a/model/user_test.go
+++ b/model/user_test.go
@@ -225,6 +225,10 @@ func TestRoles(t *testing.T) {
t.Fatal()
}
+ if !IsValidUserRoles("system_user system_admin") {
+ t.Fatal()
+ }
+
if IsInRole("system_admin junk", "admin") {
t.Fatal()
}
diff --git a/store/sql_team_store.go b/store/sql_team_store.go
index 0544ec76c..b9db80588 100644
--- a/store/sql_team_store.go
+++ b/store/sql_team_store.go
@@ -395,6 +395,8 @@ func (s SqlTeamStore) UpdateMember(member *model.TeamMember) StoreChannel {
go func() {
result := StoreResult{}
+ member.PreUpdate()
+
if result.Err = member.IsValid(); result.Err != nil {
storeChannel <- result
close(storeChannel)
diff --git a/store/sql_upgrade.go b/store/sql_upgrade.go
index 0e25d76be..445f3f349 100644
--- a/store/sql_upgrade.go
+++ b/store/sql_upgrade.go
@@ -15,6 +15,7 @@ import (
)
const (
+ VERSION_3_5_0 = "3.5.0"
VERSION_3_4_0 = "3.4.0"
VERSION_3_3_0 = "3.3.0"
VERSION_3_2_0 = "3.2.0"
@@ -35,6 +36,7 @@ func UpgradeDatabase(sqlStore *SqlStore) {
UpgradeDatabaseToVersion32(sqlStore)
UpgradeDatabaseToVersion33(sqlStore)
UpgradeDatabaseToVersion34(sqlStore)
+ UpgradeDatabaseToVersion35(sqlStore)
// If the SchemaVersion is empty this this is the first time it has ran
// so lets set it to the current version.
@@ -187,3 +189,17 @@ func UpgradeDatabaseToVersion34(sqlStore *SqlStore) {
saveSchemaVersion(sqlStore, VERSION_3_4_0)
}
}
+
+func UpgradeDatabaseToVersion35(sqlStore *SqlStore) {
+ //if shouldPerformUpgrade(sqlStore, VERSION_3_4_0, VERSION_3_5_0) {
+
+ sqlStore.GetMaster().Exec("UPDATE Users SET Roles = 'system_user' WHERE Roles = ''")
+ sqlStore.GetMaster().Exec("UPDATE Users SET Roles = 'system_user system_admin' WHERE Roles = 'system_admin'")
+ sqlStore.GetMaster().Exec("UPDATE TeamMembers SET Roles = 'team_user' WHERE Roles = ''")
+ sqlStore.GetMaster().Exec("UPDATE TeamMembers SET Roles = 'team_user team_admin' WHERE Roles = 'admin'")
+ sqlStore.GetMaster().Exec("UPDATE ChannelMembers SET Roles = 'channel_user' WHERE Roles = ''")
+ sqlStore.GetMaster().Exec("UPDATE ChannelMembers SET Roles = 'channel_user channel_admin' WHERE Roles = 'admin'")
+
+ //saveSchemaVersion(sqlStore, VERSION_3_5_0)
+ //}
+}
diff --git a/store/sql_user_store.go b/store/sql_user_store.go
index fff78c8f2..574385cd0 100644
--- a/store/sql_user_store.go
+++ b/store/sql_user_store.go
@@ -653,7 +653,7 @@ func (us SqlUserStore) GetSystemAdminProfiles() StoreChannel {
var users []*model.User
- if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users WHERE Roles = :Roles", map[string]interface{}{"Roles": "system_admin"}); err != nil {
+ if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users WHERE Roles LIKE :Roles", map[string]interface{}{"Roles": "%system_admin%"}); err != nil {
result.Err = model.NewLocAppError("SqlUserStore.GetSystemAdminProfiles", "store.sql_user.get_sysadmin_profiles.app_error", nil, err.Error())
} else {
diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go
index 753c54a7b..076be1a81 100644
--- a/store/sql_user_store_test.go
+++ b/store/sql_user_store_test.go
@@ -342,7 +342,7 @@ func TestUserStoreGetSystemAdminProfiles(t *testing.T) {
u1 := &model.User{}
u1.Email = model.NewId()
- u1.Roles = model.ROLE_SYSTEM_ADMIN
+ u1.Roles = model.ROLE_SYSTEM_USER.Id + " " + model.ROLE_SYSTEM_ADMIN.Id
Must(store.User().Save(u1))
Must(store.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}))
diff --git a/utils/authorization.go b/utils/authorization.go
new file mode 100644
index 000000000..23a7673fe
--- /dev/null
+++ b/utils/authorization.go
@@ -0,0 +1,81 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package utils
+
+import "github.com/mattermost/platform/model"
+
+func SetDefaultRolesBasedOnConfig() {
+ // Reset the roles to default to make this logic easier
+ model.InitalizeRoles()
+
+ switch *Cfg.TeamSettings.RestrictPublicChannelManagement {
+ case model.PERMISSIONS_ALL:
+ model.ROLE_CHANNEL_USER.Permissions = append(
+ model.ROLE_CHANNEL_USER.Permissions,
+ model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
+ )
+ model.ROLE_TEAM_USER.Permissions = append(
+ model.ROLE_TEAM_USER.Permissions,
+ model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
+ model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
+ )
+ break
+ case model.PERMISSIONS_TEAM_ADMIN:
+ model.ROLE_TEAM_ADMIN.Permissions = append(
+ model.ROLE_TEAM_ADMIN.Permissions,
+ model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
+ model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
+ model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
+ )
+ break
+ }
+
+ switch *Cfg.TeamSettings.RestrictPrivateChannelManagement {
+ case model.PERMISSIONS_ALL:
+ model.ROLE_CHANNEL_USER.Permissions = append(
+ model.ROLE_CHANNEL_USER.Permissions,
+ model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id,
+ )
+ model.ROLE_TEAM_USER.Permissions = append(
+ model.ROLE_TEAM_USER.Permissions,
+ model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id,
+ model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id,
+ )
+ break
+ case model.PERMISSIONS_TEAM_ADMIN:
+ model.ROLE_TEAM_ADMIN.Permissions = append(
+ model.ROLE_TEAM_ADMIN.Permissions,
+ model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id,
+ model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id,
+ model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id,
+ )
+ break
+ }
+
+ if !*Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ model.ROLE_TEAM_USER.Permissions = append(
+ model.ROLE_TEAM_USER.Permissions,
+ model.PERMISSION_MANAGE_WEBHOOKS.Id,
+ model.PERMISSION_MANAGE_SLASH_COMMANDS.Id,
+ )
+ model.ROLE_SYSTEM_USER.Permissions = append(
+ model.ROLE_SYSTEM_USER.Permissions,
+ model.PERMISSION_MANAGE_OAUTH.Id,
+ )
+ }
+
+ // If team admins are given permission
+ if *Cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_TEAM_ADMIN {
+ model.ROLE_TEAM_ADMIN.Permissions = append(
+ model.ROLE_TEAM_ADMIN.Permissions,
+ model.PERMISSION_INVITE_USER.Id,
+ )
+ // If it's not restricted to system admin or team admin, then give all users permission
+ } else if *Cfg.TeamSettings.RestrictTeamInvite != model.PERMISSIONS_SYSTEM_ADMIN {
+ model.ROLE_SYSTEM_USER.Permissions = append(
+ model.ROLE_SYSTEM_USER.Permissions,
+ model.PERMISSION_INVITE_USER.Id,
+ )
+ }
+}
diff --git a/utils/config.go b/utils/config.go
index 2bfb14255..25dec7ace 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -207,6 +207,12 @@ func LoadConfig(fileName string) {
if samlI := einterfaces.GetSamlInterface(); samlI != nil {
samlI.ConfigureSP()
}
+
+ SetDefaultRolesBasedOnConfig()
+}
+
+func RegenerateClientConfig() {
+ ClientCfg = getClientConfig(Cfg)
}
func getClientConfig(c *model.Config) map[string]string {
diff --git a/web/web_test.go b/web/web_test.go
index 5f74430fa..21819191b 100644
--- a/web/web_test.go
+++ b/web/web_test.go
@@ -83,8 +83,10 @@ func TestGetAccessToken(t *testing.T) {
ApiClient.Must(ApiClient.LoginById(ruser.Id, "passwd1"))
ApiClient.SetTeamId(rteam.Data.(*model.Team).Id)
*utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false
+ utils.SetDefaultRolesBasedOnConfig()
app = ApiClient.Must(ApiClient.RegisterApp(app)).Data.(*model.OAuthApp)
*utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true
+ utils.SetDefaultRolesBasedOnConfig()
redirect := ApiClient.Must(ApiClient.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, app.Id, app.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
rurl, _ := url.Parse(redirect)
@@ -207,7 +209,7 @@ func TestIncomingWebhook(t *testing.T) {
c := &api.Context{}
c.RequestId = model.NewId()
c.IpAddress = "cmd_line"
- api.UpdateUserRoles(c, user, model.ROLE_SYSTEM_ADMIN)
+ api.UpdateUserRoles(c, user, model.ROLE_SYSTEM_ADMIN.Id)
ApiClient.Login(user.Email, "passwd1")
ApiClient.SetTeamId(team.Id)
diff --git a/webapp/actions/team_actions.jsx b/webapp/actions/team_actions.jsx
index ea6be8504..3bf25c193 100644
--- a/webapp/actions/team_actions.jsx
+++ b/webapp/actions/team_actions.jsx
@@ -24,7 +24,7 @@ export function createTeam(team, onSuccess, onError) {
AppDispatcher.handleServerAction({
type: ActionTypes.CREATED_TEAM,
team: rteam,
- member: {team_id: rteam.id, user_id: UserStore.getCurrentId(), roles: 'admin'}
+ member: {team_id: rteam.id, user_id: UserStore.getCurrentId(), roles: 'team_admin team_user'}
});
browserHistory.push('/' + rteam.name + '/channels/town-square');
diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx
index 6aabee080..a5d179a0d 100644
--- a/webapp/client/client.jsx
+++ b/webapp/client/client.jsx
@@ -856,6 +856,15 @@ export default class Client {
end(this.handleResponse.bind(this, 'getMe', success, error));
}
+ getUser(userId, success, error) {
+ request.
+ get(`${this.getUserNeededRoute(userId)}/get`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getUser', success, error));
+ }
+
login(loginId, password, mfaToken, success, error) {
this.doLogin({login_id: loginId, password, token: mfaToken}, success, error);
diff --git a/webapp/components/admin_console/generated_setting.jsx b/webapp/components/admin_console/generated_setting.jsx
index ba6c5924b..d4feb9332 100644
--- a/webapp/components/admin_console/generated_setting.jsx
+++ b/webapp/components/admin_console/generated_setting.jsx
@@ -37,14 +37,9 @@ export default class GeneratedSetting extends React.Component {
constructor(props) {
super(props);
- this.handleChange = this.handleChange.bind(this);
this.regenerate = this.regenerate.bind(this);
}
- handleChange(e) {
- this.props.onChange(this.props.id, e.target.value === 'true');
- }
-
regenerate(e) {
e.preventDefault();
@@ -76,8 +71,7 @@ export default class GeneratedSetting extends React.Component {
id={this.props.id}
placeholder={this.props.placeholder}
value={this.props.value}
- onChange={this.handleChange}
- disabled={this.props.disabled}
+ disabled={true}
/>
{disabledText}
<div className='help-text'>
diff --git a/webapp/components/admin_console/password_settings.jsx b/webapp/components/admin_console/password_settings.jsx
index ad805b38c..6fa1dc9c4 100644
--- a/webapp/components/admin_console/password_settings.jsx
+++ b/webapp/components/admin_console/password_settings.jsx
@@ -23,6 +23,7 @@ export default class PasswordSettings extends AdminSettings {
this.getSampleErrorMsg = this.getSampleErrorMsg.bind(this);
this.handlePasswordLengthChange = this.handlePasswordLengthChange.bind(this);
+ this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
this.state = Object.assign(this.state, {
passwordMinimumLength: props.config.PasswordSettings.MinimumLength,
@@ -136,6 +137,11 @@ export default class PasswordSettings extends AdminSettings {
this.handleChange(id, value);
}
+ handleCheckboxChange(id, value) {
+ this.sampleErrorMsg = this.getSampleErrorMsg(this.state.passwordMinimumLength);
+ this.handleChange(id, value);
+ }
+
renderTitle() {
return (
<h3>
@@ -212,7 +218,7 @@ export default class PasswordSettings extends AdminSettings {
ref='lowercase'
defaultChecked={this.state.passwordLowercase}
name='admin.password.lowercase'
- onChange={this.handleChange}
+ onChange={this.handleCheckboxChange}
/>
<FormattedMessage
id='admin.password.lowercase'
@@ -227,7 +233,7 @@ export default class PasswordSettings extends AdminSettings {
ref='uppercase'
defaultChecked={this.state.passwordUppercase}
name='admin.password.uppercase'
- onChange={this.handleChange}
+ onChange={this.handleCheckboxChange}
/>
<FormattedMessage
id='admin.password.uppercase'
@@ -242,7 +248,7 @@ export default class PasswordSettings extends AdminSettings {
ref='number'
defaultChecked={this.state.passwordNumber}
name='admin.password.number'
- onChange={this.handleChange}
+ onChange={this.handleCheckboxChange}
/>
<FormattedMessage
id='admin.password.number'
@@ -257,7 +263,7 @@ export default class PasswordSettings extends AdminSettings {
ref='symbol'
defaultChecked={this.state.passwordSymbol}
name='admin.password.symbol'
- onChange={this.handleChange}
+ onChange={this.handleCheckboxChange}
/>
<FormattedMessage
id='admin.password.symbol'
diff --git a/webapp/components/admin_console/select_team_modal.jsx b/webapp/components/admin_console/select_team_modal.jsx
index 2ea6cc58c..a661dd2f0 100644
--- a/webapp/components/admin_console/select_team_modal.jsx
+++ b/webapp/components/admin_console/select_team_modal.jsx
@@ -25,10 +25,13 @@ export default class SelectTeamModal extends React.Component {
this.props.onModalDismissed();
}
compare(a, b) {
- if (a.display_name < b.display_name) {
+ const teamA = a.display_name.toLowerCase();
+ const teamB = b.display_name.toLowerCase();
+
+ if (teamA < teamB) {
return -1;
}
- if (a.display_name > b.display_name) {
+ if (teamA > teamB) {
return 1;
}
return 0;
diff --git a/webapp/components/admin_console/user_item.jsx b/webapp/components/admin_console/user_item.jsx
index 974ef8bc9..78fdb085c 100644
--- a/webapp/components/admin_console/user_item.jsx
+++ b/webapp/components/admin_console/user_item.jsx
@@ -97,12 +97,12 @@ export default class UserItem extends React.Component {
e.preventDefault();
const me = UserStore.getCurrentUser();
if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, 'admin');
+ this.handleDemote(this.props.user, 'team_user team_admin');
} else {
Client.updateRoles(
this.props.team.id,
this.props.user.id,
- 'admin',
+ 'team_user team_admin',
() => {
this.props.refreshProfiles();
},
@@ -119,7 +119,7 @@ export default class UserItem extends React.Component {
Client.updateRoles(
this.props.team.id,
this.props.user.id,
- 'system_admin',
+ 'system_user system_admin',
() => {
this.props.refreshProfiles();
},
@@ -238,11 +238,11 @@ export default class UserItem extends React.Component {
const me = UserStore.getCurrentUser();
const email = user.email;
- let showMakeMember = teamMember.roles === 'admin' || user.roles === 'system_admin';
- let showMakeAdmin = teamMember.roles === '' && user.roles !== 'system_admin';
- let showMakeSystemAdmin = user.roles === '' || user.roles === 'admin';
+ let showMakeMember = Utils.isAdmin(teamMember.roles) || Utils.isSystemAdmin(user.roles);
+ let showMakeAdmin = !Utils.isAdmin(teamMember.roles) && !Utils.isSystemAdmin(user.roles);
+ let showMakeSystemAdmin = !Utils.isSystemAdmin(user.roles);
let showMakeActive = false;
- let showMakeNotActive = user.roles !== 'system_admin';
+ let showMakeNotActive = !Utils.isSystemAdmin(user.roles);
const mfaEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MFA === 'true' && global.window.mm_config.EnableMultifactorAuthentication === 'true';
const showMfaReset = mfaEnabled && user.mfa_active;
diff --git a/webapp/components/edit_post_modal.jsx b/webapp/components/edit_post_modal.jsx
index 00c8e0f09..24b1b5c3d 100644
--- a/webapp/components/edit_post_modal.jsx
+++ b/webapp/components/edit_post_modal.jsx
@@ -4,6 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import Client from 'client/web_client.jsx';
+import * as UserAgent from 'utils/user_agent.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import Textbox from './textbox.jsx';
@@ -94,7 +95,7 @@ export default class EditPostModal extends React.Component {
}
handleEditKeyPress(e) {
- if (!this.state.ctrlSend && e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
+ if (!UserAgent.isMobileApp() && !this.state.ctrlSend && e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
e.preventDefault();
ReactDOM.findDOMNode(this.refs.editbox).blur();
this.handleEdit();
diff --git a/webapp/components/help/components/composing.jsx b/webapp/components/help/components/composing.jsx
index 398580ffa..9092027b7 100644
--- a/webapp/components/help/components/composing.jsx
+++ b/webapp/components/help/components/composing.jsx
@@ -15,7 +15,7 @@ export default class HelpComposing extends React.Component {
message.push(localizeMessage('help.composing.types', '## Message Types\nReply to posts to keep conversations organized in threads.'));
message.push(localizeMessage('help.composing.posts', '#### Posts\nPosts can be considered parent messages. They are the messages that often start a thread of replies. Posts are composed and sent from the text input box at the bottom of the center pane.'));
message.push(localizeMessage('help.composing.replies', '#### Replies\nReply to a message by clicking the reply icon next to any message text. This action opens the right-hand-side (RHS) where you can see the message thread, then compose and send your reply. Replies are indented slightly in the center pane to indicate that they are child messages of a parent post.\n\nWhen composing a reply in the right-hand side, click the expand/collapse icon with two arrows at the top of the sidebar to make things easier to read.'));
- message.push(localizeMessage('help.composing.posting', '## Posting a Message\nWrite a message by typing into the text input box, then press **ENTER** to send it. Use **Shift + ENTER** to create a new line without sending a message. To send messages by pressing **Ctrl+Enter** go to **Main Menu > Account Settings > Send messages on Ctrl + Enter**.'));
+ message.push(localizeMessage('help.composing.posting', '## Posting a Message\nWrite a message by typing into the text input box, then press **ENTER** to send it. Use **Shift + ENTER** to create a new line without sending a message. To send messages by pressing **Ctrl + ENTER** go to **Main Menu > Account Settings > Send messages on Ctrl + ENTER**.'));
message.push(localizeMessage('help.composing.editing', '## Editing a Message\nEdit a message by clicking the **[...]** icon next to any message text that you’ve composed, then click **Edit**. After making modifications to the message text, press **ENTER** to save the modifications. Message edits do not trigger new @mention notifications, desktop notifications or notification sounds.'));
message.push(localizeMessage('help.composing.deleting', '## Deleting a message\nDelete a message by clicking the **[...]** icon next to any message text that you’ve composed, then click **Delete**. System and Team Admins can delete any message on their system or team.'));
message.push(localizeMessage('help.composing.linking', '## Linking to a message\nThe **Permalink** feature creates a link to any message. Sharing this link with other users in the channel lets them view the linked message in the Message Archives. Users who are not a member of the channel where the message was posted cannot view the permalink. Get the permalink to any message by clicking the **[...]** icon next to the message text > **Permalink** > **Copy Link**.'));
diff --git a/webapp/components/navbar_dropdown.jsx b/webapp/components/navbar_dropdown.jsx
index f7547f803..413942865 100644
--- a/webapp/components/navbar_dropdown.jsx
+++ b/webapp/components/navbar_dropdown.jsx
@@ -1,8 +1,6 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
-import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
@@ -49,14 +47,6 @@ export default class NavbarDropdown extends React.Component {
}
componentDidMount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
- $('.sidebar--left .dropdown-menu').scrollTop(0);
- this.blockToggle = true;
- setTimeout(() => {
- this.blockToggle = false;
- }, 100);
- });
-
TeamStore.addChangeListener(this.onTeamChange);
document.addEventListener('keydown', this.openAccountSettings);
}
@@ -69,7 +59,6 @@ export default class NavbarDropdown extends React.Component {
}
componentWillUnmount() {
- $(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
TeamStore.removeChangeListener(this.onTeamChange);
document.removeEventListener('keydown', this.openAccountSettings);
}
@@ -363,9 +352,8 @@ export default class NavbarDropdown extends React.Component {
<a
href='#'
className='dropdown-toggle'
- data-toggle='dropdown'
role='button'
- aria-expanded='false'
+ onClick={this.props.toggleDropdown}
>
<span
className='dropdown__icon'
@@ -449,5 +437,6 @@ NavbarDropdown.propTypes = {
teamType: React.PropTypes.string,
teamDisplayName: React.PropTypes.string,
teamName: React.PropTypes.string,
- currentUser: React.PropTypes.object
+ currentUser: React.PropTypes.object,
+ toggleDropdown: React.PropTypes.function
};
diff --git a/webapp/components/post_view/components/post.jsx b/webapp/components/post_view/components/post.jsx
index e9019bf38..7aa0c028e 100644
--- a/webapp/components/post_view/components/post.jsx
+++ b/webapp/components/post_view/components/post.jsx
@@ -236,7 +236,6 @@ export default class Post extends React.Component {
post={post}
sameRoot={this.props.sameRoot}
commentCount={commentCount}
- isCommentMention={this.props.isCommentMention}
handleCommentClick={this.handleCommentClick}
handleDropdownOpened={this.handleDropdownOpened}
isLastComment={this.props.isLastComment}
@@ -255,6 +254,7 @@ export default class Post extends React.Component {
handleCommentClick={this.handleCommentClick}
compactDisplay={this.props.compactDisplay}
previewCollapsed={this.props.previewCollapsed}
+ isCommentMention={this.props.isCommentMention}
/>
</div>
</div>
diff --git a/webapp/components/post_view/components/post_body.jsx b/webapp/components/post_view/components/post_body.jsx
index 8c3b3009f..3295f5bac 100644
--- a/webapp/components/post_view/components/post_body.jsx
+++ b/webapp/components/post_view/components/post_body.jsx
@@ -23,6 +23,10 @@ export default class PostBody extends React.Component {
this.removePost = this.removePost.bind(this);
}
shouldComponentUpdate(nextProps) {
+ if (nextProps.isCommentMention !== this.props.isCommentMention) {
+ return true;
+ }
+
if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
return true;
}
@@ -190,10 +194,15 @@ export default class PostBody extends React.Component {
);
}
+ let mentionHighlightClass = '';
+ if (this.props.isCommentMention) {
+ mentionHighlightClass = 'mention-comment';
+ }
+
return (
<div>
{comment}
- <div className='post__body'>
+ <div className={'post__body ' + mentionHighlightClass}>
{messageWithAdditionalContent}
{fileAttachmentHolder}
</div>
@@ -208,5 +217,6 @@ PostBody.propTypes = {
retryPost: React.PropTypes.func.isRequired,
handleCommentClick: React.PropTypes.func.isRequired,
compactDisplay: React.PropTypes.bool,
- previewCollapsed: React.PropTypes.string
+ previewCollapsed: React.PropTypes.string,
+ isCommentMention: React.PropTypes.bool
};
diff --git a/webapp/components/post_view/components/post_header.jsx b/webapp/components/post_view/components/post_header.jsx
index 27715e5f5..5900c8281 100644
--- a/webapp/components/post_view/components/post_header.jsx
+++ b/webapp/components/post_view/components/post_header.jsx
@@ -64,7 +64,6 @@ export default class PostHeader extends React.Component {
<PostInfo
post={post}
commentCount={this.props.commentCount}
- isCommentMention={this.props.isCommentMention}
handleCommentClick={this.props.handleCommentClick}
handleDropdownOpened={this.props.handleDropdownOpened}
allowReply={!isSystemMessage}
@@ -92,7 +91,6 @@ PostHeader.propTypes = {
user: React.PropTypes.object,
currentUser: React.PropTypes.object.isRequired,
commentCount: React.PropTypes.number.isRequired,
- isCommentMention: React.PropTypes.bool.isRequired,
isLastComment: React.PropTypes.bool.isRequired,
handleCommentClick: React.PropTypes.func.isRequired,
handleDropdownOpened: React.PropTypes.func.isRequired,
diff --git a/webapp/components/post_view/components/post_info.jsx b/webapp/components/post_view/components/post_info.jsx
index 3c0eb9880..c95a121da 100644
--- a/webapp/components/post_view/components/post_info.jsx
+++ b/webapp/components/post_view/components/post_info.jsx
@@ -250,7 +250,6 @@ export default class PostInfo extends React.Component {
var post = this.props.post;
var comments = '';
var showCommentClass = '';
- var highlightMentionClass = '';
var commentCountText = this.props.commentCount;
const flagIcon = Constants.FLAG_ICON_SVG;
@@ -260,15 +259,11 @@ export default class PostInfo extends React.Component {
commentCountText = '';
}
- if (this.props.isCommentMention) {
- highlightMentionClass = ' mention--highlight';
- }
-
if (post.state !== Constants.POST_FAILED && post.state !== Constants.POST_LOADING && !Utils.isPostEphemeral(post) && this.props.allowReply) {
comments = (
<a
href='#'
- className={'comment-icon__container' + showCommentClass + highlightMentionClass}
+ className={'comment-icon__container' + showCommentClass}
onClick={this.props.handleCommentClick}
>
<span
@@ -386,7 +381,6 @@ PostInfo.defaultProps = {
PostInfo.propTypes = {
post: React.PropTypes.object.isRequired,
commentCount: React.PropTypes.number.isRequired,
- isCommentMention: React.PropTypes.bool.isRequired,
isLastComment: React.PropTypes.bool.isRequired,
allowReply: React.PropTypes.bool.isRequired,
handleCommentClick: React.PropTypes.func.isRequired,
diff --git a/webapp/components/post_view/components/post_list.jsx b/webapp/components/post_view/components/post_list.jsx
index b008245f6..a05507703 100644
--- a/webapp/components/post_view/components/post_list.jsx
+++ b/webapp/components/post_view/components/post_list.jsx
@@ -253,35 +253,34 @@ export default class PostList extends React.Component {
}
let commentCount = 0;
- let nonOwnCommentsExists = false;
let isCommentMention = false;
+ let lastCommentOnThreadTime = Number.MAX_SAFE_INTEGER;
let commentRootId;
if (parentPost) {
commentRootId = post.root_id;
} else {
commentRootId = post.id;
}
- if (commentRootId) {
- const commentsNotifyLevel = this.props.currentUser.notify_props.comments || 'never';
- for (const postId in posts) {
- if (posts[postId].root_id === commentRootId) {
- commentCount += 1;
- if (posts[postId].user_id !== this.props.currentUser.id) {
- nonOwnCommentsExists = true;
- }
- if (posts[postId].user_id === this.props.currentUser.id && commentsNotifyLevel === 'any' && !isCommentMention) {
- for (const nextPostId in posts) {
- if (posts[nextPostId].root_id === commentRootId && posts[nextPostId].user_id !== this.props.currentUser.id &&
- posts[postId].create_at < posts[nextPostId].create_at) {
- isCommentMention = true;
- break;
- }
- }
- }
+
+ for (const postId in posts) {
+ if (posts[postId].root_id === commentRootId) {
+ commentCount += 1;
+ if (posts[postId].user_id === userId && (lastCommentOnThreadTime === Number.MAX_SAFE_INTEGER || lastCommentOnThreadTime < posts[postId].create_at)) {
+ lastCommentOnThreadTime = posts[postId].create_at;
}
}
- if (nonOwnCommentsExists && posts[commentRootId].user_id === this.props.currentUser.id && commentsNotifyLevel !== 'never') {
- isCommentMention = true;
+ }
+
+ if (parentPost && commentRootId) {
+ const commentsNotifyLevel = this.props.currentUser.notify_props.comments || 'never';
+ const notCurrentUser = post.user_id !== userId || (post.props && post.props.from_webhook);
+ const notViewed = this.props.lastViewed !== 0 && post.create_at > this.props.lastViewed;
+ if (notCurrentUser && notViewed) {
+ if (commentsNotifyLevel === 'any' && (posts[commentRootId].user_id === userId || post.create_at > lastCommentOnThreadTime)) {
+ isCommentMention = true;
+ } else if (commentsNotifyLevel === 'root' && posts[commentRootId].user_id === userId) {
+ isCommentMention = true;
+ }
}
}
diff --git a/webapp/components/sidebar_header.jsx b/webapp/components/sidebar_header.jsx
index 9a7db54f8..a57a809be 100644
--- a/webapp/components/sidebar_header.jsx
+++ b/webapp/components/sidebar_header.jsx
@@ -1,7 +1,6 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
import NavbarDropdown from './navbar_dropdown.jsx';
import 'bootstrap';
@@ -49,7 +48,13 @@ export default class SidebarHeader extends React.Component {
this.refs.dropdown.blockToggle = false;
return;
}
- $('.team__header').find('.dropdown-toggle').dropdown('toggle');
+ const menu = document.querySelector('.team__header .dropdown-toggle');
+ const isOpen = menu.parentElement.classList.toggle('open');
+ menu.setAttribute('aria-expanded', isOpen);
+
+ if (!isOpen) {
+ document.querySelector('.sidebar--left .dropdown-menu').scrollTop = 0;
+ }
}
render() {
var me = this.props.currentUser;
@@ -100,6 +105,7 @@ export default class SidebarHeader extends React.Component {
teamDisplayName={this.props.teamDisplayName}
teamName={this.props.teamName}
currentUser={this.props.currentUser}
+ toggleDropdown={this.toggleDropdown}
/>
</div>
);
diff --git a/webapp/components/suggestion/search_channel_provider.jsx b/webapp/components/suggestion/search_channel_provider.jsx
index 2e60d579f..6513f0f2b 100644
--- a/webapp/components/suggestion/search_channel_provider.jsx
+++ b/webapp/components/suggestion/search_channel_provider.jsx
@@ -31,7 +31,7 @@ class SearchChannelSuggestion extends Suggestion {
export default class SearchChannelProvider {
handlePretextChanged(suggestionId, pretext) {
- const captured = (/\b(?:in|channel):\s*(\S*)$/i).exec(pretext);
+ const captured = (/\b(?:in|channel):\s*(\S*)$/i).exec(pretext.toLowerCase());
if (captured) {
const channelPrefix = captured[1];
diff --git a/webapp/components/suggestion/search_user_provider.jsx b/webapp/components/suggestion/search_user_provider.jsx
index 7a5bdabf1..e33c206a7 100644
--- a/webapp/components/suggestion/search_user_provider.jsx
+++ b/webapp/components/suggestion/search_user_provider.jsx
@@ -35,7 +35,7 @@ class SearchUserSuggestion extends Suggestion {
export default class SearchUserProvider {
handlePretextChanged(suggestionId, pretext) {
- const captured = (/\bfrom:\s*(\S*)$/i).exec(pretext);
+ const captured = (/\bfrom:\s*(\S*)$/i).exec(pretext.toLowerCase());
if (captured) {
const usernamePrefix = captured[1];
diff --git a/webapp/components/team_import_tab.jsx b/webapp/components/team_import_tab.jsx
index 49d7f7f1f..0fee6a3d8 100644
--- a/webapp/components/team_import_tab.jsx
+++ b/webapp/components/team_import_tab.jsx
@@ -29,8 +29,8 @@ class TeamImportTab extends React.Component {
};
}
- onImportFailure() {
- this.setState({status: 'fail', link: ''});
+ onImportFailure(e, err, res) {
+ this.setState({status: 'fail', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(res.text)});
}
onImportSuccess(data, res) {
diff --git a/webapp/components/team_members_dropdown.jsx b/webapp/components/team_members_dropdown.jsx
index 527161103..6b9390ade 100644
--- a/webapp/components/team_members_dropdown.jsx
+++ b/webapp/components/team_members_dropdown.jsx
@@ -37,12 +37,12 @@ export default class TeamMembersDropdown extends React.Component {
handleMakeMember() {
const me = UserStore.getCurrentUser();
if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, '');
+ this.handleDemote(this.props.user, 'team_user');
} else {
Client.updateRoles(
this.props.teamMember.team_id,
this.props.user.id,
- '',
+ 'team_user',
() => {
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
AsyncClient.getProfiles();
@@ -93,12 +93,12 @@ export default class TeamMembersDropdown extends React.Component {
handleMakeAdmin() {
const me = UserStore.getCurrentUser();
if (this.props.user.id === me.id) {
- this.handleDemote(this.props.user, 'admin');
+ this.handleDemote(this.props.user, 'team_user team_admin');
} else {
Client.updateRoles(
this.props.teamMember.team_id,
this.props.user.id,
- 'admin',
+ 'team_user team_admin',
() => {
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
AsyncClient.getProfiles();
@@ -186,10 +186,10 @@ export default class TeamMembersDropdown extends React.Component {
}
const me = UserStore.getCurrentUser();
- let showMakeMember = teamMember.roles === 'admin' && user.roles !== 'system_admin';
- let showMakeAdmin = teamMember.roles === '' && user.roles !== 'system_admin';
+ let showMakeMember = Utils.isAdmin(teamMember.roles) && !Utils.isSystemAdmin(user.roles);
+ let showMakeAdmin = !Utils.isAdmin(teamMember.roles) && !Utils.isSystemAdmin(user.roles);
let showMakeActive = false;
- let showMakeNotActive = user.roles !== 'system_admin';
+ let showMakeNotActive = Utils.isSystemAdmin(user.roles);
if (user.delete_at > 0) {
currentRoles = (
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 45afbd184..66e994893 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -1230,7 +1230,7 @@
"help.composing.deleting": "## Deleting a message\nDelete a message by clicking the **[...]** icon next to any message text that you’ve composed, then click **Delete**. System and Team Admins can delete any message on their system or team.",
"help.composing.editing": "## Editing a Message\nEdit a message by clicking the **[...]** icon next to any message text that you’ve composed, then click **Edit**. After making modifications to the message text, press **ENTER** to save the modifications. Message edits do not trigger new @mention notifications, desktop notifications or notification sounds.",
"help.composing.linking": "## Linking to a message\nThe **Permalink** feature creates a link to any message. Sharing this link with other users in the channel lets them view the linked message in the Message Archives. Users who are not a member of the channel where the message was posted cannot view the permalink. Get the permalink to any message by clicking the **[...]** icon next to the message text > **Permalink** > **Copy Link**.",
- "help.composing.posting": "## Posting a Message\nWrite a message by typing into the text input box, then press **ENTER** to send it. Use **Shift + ENTER** to create a new line without sending a message. To send messages by pressing **CTRL+ENTER** go to **Main Menu > Account Settings > Send messages on CTRL + ENTER**.",
+ "help.composing.posting": "## Posting a Message\nWrite a message by typing into the text input box, then press **ENTER** to send it. Use **Shift + ENTER** to create a new line without sending a message. To send messages by pressing **CTRL + ENTER** go to **Main Menu > Account Settings > Send messages on CTRL + ENTER**.",
"help.composing.posts": "#### Posts\nPosts can be considered parent messages. They are the messages that often start a thread of replies. Posts are composed and sent from the text input box at the bottom of the center pane.",
"help.composing.replies": "#### Replies\nReply to a message by clicking the reply icon next to any message text. This action opens the right-hand-side (RHS) where you can see the message thread, then compose and send your reply. Replies are indented slightly in the center pane to indicate that they are child messages of a parent post.\n\nWhen composing a reply in the right-hand side, click the expand/collapse icon with two arrows at the top of the sidebar to make things easier to read.",
"help.composing.title": "# Sending Messages\n_____",
@@ -1238,7 +1238,7 @@
"help.formatting.checklist": "Make a task list by including square brackets:",
"help.formatting.checklistExample": "- [ ] Item one\n- [ ] Item two\n- [x] Completed item",
"help.formatting.code": "## Code Block\n\nCreate a code block by indenting each line by four spaces, or by placing ``` on the line above and below your code.",
- "help.formatting.codeBlock": "code block",
+ "help.formatting.codeBlock": "Code block",
"help.formatting.emojiExample": ":smile: :+1: :sheep:",
"help.formatting.emojis": "## Emojis\n\nOpen the emoji autocomplete by typing `:`. A full list of emojis can be found [here](http://www.emoji-cheat-sheet.com/). It is also possible to create your own [Custom Emoji](http://docs.mattermost.com/help/settings/custom-emoji.html) if the emoji you want to use doesn't exist.",
"help.formatting.example": "Example:",
diff --git a/webapp/root.html b/webapp/root.html
index 7987a52fc..bca775c8b 100644
--- a/webapp/root.html
+++ b/webapp/root.html
@@ -52,5 +52,8 @@
<script>
window.setup_root();
</script>
+ <noscript>
+ To use Mattermost, please enable JavaScript.
+ </noscript>
</body>
</html>
diff --git a/webapp/sass/layout/_post.scss b/webapp/sass/layout/_post.scss
index c5901ca0e..41033a67c 100644
--- a/webapp/sass/layout/_post.scss
+++ b/webapp/sass/layout/_post.scss
@@ -681,6 +681,11 @@ body.ios {
.post__body {
border-left: 4px solid $gray;
padding-left: 7px;
+
+ &.mention-comment {
+ border-left: 4px solid $yellow;
+ border-color: $yellow;
+ }
}
}
diff --git a/webapp/tests/client_admin.test.jsx b/webapp/tests/client_admin.test.jsx
index a6a6e1a85..2ed08dc62 100644
--- a/webapp/tests/client_admin.test.jsx
+++ b/webapp/tests/client_admin.test.jsx
@@ -15,7 +15,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -30,7 +30,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -45,7 +45,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -69,7 +69,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -84,7 +84,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -99,7 +99,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -114,7 +114,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -131,7 +131,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -148,7 +148,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -167,7 +167,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
@@ -186,7 +186,7 @@ describe('Client.Admin', function() {
done(new Error('should need system admin permissions'));
},
function(err) {
- assert.equal(err.id, 'api.context.system_permissions.app_error');
+ assert.equal(err.id, 'api.context.permissions.app_error');
done();
}
);
diff --git a/webapp/tests/client_user.test.jsx b/webapp/tests/client_user.test.jsx
index 116eee4ae..6c65e8ef5 100644
--- a/webapp/tests/client_user.test.jsx
+++ b/webapp/tests/client_user.test.jsx
@@ -21,6 +21,21 @@ describe('Client.User', function() {
});
});
+ it('getUser', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getUser(
+ TestHelper.basicUser().id,
+ function(data) {
+ assert.equal(data.id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
it('getInitialLoad', function(done) {
TestHelper.initBasic(() => {
TestHelper.basicClient().getInitialLoad(
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 2780196db..4dc9aab86 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -56,7 +56,7 @@ export function isInRole(roles, inRole) {
}
export function isAdmin(roles) {
- if (isInRole(roles, 'admin')) {
+ if (isInRole(roles, 'team_admin')) {
return true;
}
@@ -663,9 +663,7 @@ export function applyTheme(theme) {
if (theme.mentionHighlightBg) {
changeCss('.app__body .mention--highlight, .app__body .search-highlight', 'background:' + theme.mentionHighlightBg, 1);
- }
-
- if (theme.mentionHighlightBg) {
+ changeCss('.mention-comment', 'border-color:' + theme.mentionHighlightBg + ' !important', 1);
changeCss('.app__body .post.post--highlight', 'background:' + changeOpacity(theme.mentionHighlightBg, 0.5), 1);
}
@@ -1375,4 +1373,4 @@ export function handleFormattedTextClick(e) {
browserHistory.push(linkAttribute.value);
}
}
-} \ No newline at end of file
+}