Guest accounts feature (#11428)
* MM-14139: Creating permissions for invite/promote/demote guests (#10778) * MM-14139: Creating permissions for invite/promote/demote guests * Fixing tests * Adding invite guest api endpoint (#10792) * Adding invite guest api endpoint * Adding i18n * Adding some tests * WIP * Migrating Token.Extra info to bigger size (2048) * Fixing tests * Adding client function for invite guests * Adding send guests invites tests * Renaming file from guest to guest_invite * Adding Promote/Demote users from/to guest endpoints (#10791) * Adding Promote/Demote users from/to guest endpoints * Adding i18n translations * Adding the client functions * Using getQueryBuilder function * Addressing PR review comments * Adding default channels to users on promte from guest (#10851) * Adding default channels to users on promte from guest * Addressing PR review comments * Fixing merge problems * Sending websockets events on promote/demote (#11403) * Sending websockets events on promote/demote * Fixing merge problems * Fixing govet shadowing problem * Fixing feature branch tests * Avoiding leaking users data through websockets for guest accounts (#11489) * Avoiding leaking users data through websockets for guest accounts * Adding tests and fixing code error * Fixing i18n * Allow to enable/disable guests and other extra config settings (#11481) * Allow to enable/disable guests and other extra config settings * Fixing tests and moving license and config validation to api level * Update api4/role_test.go Co-Authored-By: George Goldberg <george@gberg.me> * Update api4/role_test.go Co-Authored-By: George Goldberg <george@gberg.me> * Fixing typo * fixing tests * Managing correctly the guest channel leave behavior (#11578) * MM-15134: Removing guests from teams or system on leave channels if needed * WIP * No deactivating the guest user when leave the last team * Adding a couple of tests * Fixing shadow variables * Fixing tests * fixing tests * fixing shadow variables * Adding guest counts for channel stats (#11646) * Adding guest counts for channel stats * Adding tests * Fixing tests * Fixing guest domain restrictions (#11660) * Adding needed migration for the database * Fixing migration
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
fdde7c8287
Коммит
fe8a0f6485
@@ -486,7 +486,13 @@ func getChannelStats(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
stats := model.ChannelStats{ChannelId: c.Params.ChannelId, MemberCount: memberCount}
|
||||
guestCount, err := c.App.GetChannelGuestCount(c.Params.ChannelId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
stats := model.ChannelStats{ChannelId: c.Params.ChannelId, MemberCount: memberCount, GuestCount: guestCount}
|
||||
w.Write([]byte(stats.ToJson()))
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,10 @@ func patchRole(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if c.App.License() == nil && patch.Permissions != nil {
|
||||
if oldRole.Name == "system_guest" || oldRole.Name == "team_guest" || oldRole.Name == "channel_guest" {
|
||||
c.Err = model.NewAppError("Api4.PatchRoles", "api.roles.patch_roles.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
allowedPermissions := []string{
|
||||
model.PERMISSION_CREATE_TEAM.Id,
|
||||
model.PERMISSION_MANAGE_INCOMING_WEBHOOKS.Id,
|
||||
@@ -125,6 +129,11 @@ func patchRole(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if c.App.License() != nil && (oldRole.Name == "system_guest" || oldRole.Name == "team_guest" || oldRole.Name == "channel_guest") && !*c.App.License().Features.GuestAccountsPermissions {
|
||||
c.Err = model.NewAppError("Api4.PatchRoles", "api.roles.patch_roles.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
@@ -193,7 +194,9 @@ func TestPatchRole(t *testing.T) {
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
|
||||
// Add a license.
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
license := model.NewTestLicense()
|
||||
license.Features.GuestAccountsPermissions = model.NewBool(false)
|
||||
th.App.SetLicense(license)
|
||||
|
||||
// Try again, should succeed
|
||||
received, resp = th.SystemAdminClient.PatchRole(role.Id, patch)
|
||||
@@ -205,4 +208,25 @@ func TestPatchRole(t *testing.T) {
|
||||
assert.Equal(t, received.Description, role.Description)
|
||||
assert.EqualValues(t, received.Permissions, []string{"manage_system", "manage_incoming_webhooks", "manage_outgoing_webhooks"})
|
||||
assert.Equal(t, received.SchemeManaged, role.SchemeManaged)
|
||||
|
||||
t.Run("Check guest permissions editing without E20 license", func(t *testing.T) {
|
||||
license := model.NewTestLicense()
|
||||
license.Features.GuestAccountsPermissions = model.NewBool(false)
|
||||
th.App.SetLicense(license)
|
||||
|
||||
guestRole, err := th.App.Srv.Store.Role().GetByName("system_guest")
|
||||
require.Nil(t, err)
|
||||
received, resp = th.SystemAdminClient.PatchRole(guestRole.Id, patch)
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("Check guest permissions editing with E20 license", func(t *testing.T) {
|
||||
license := model.NewTestLicense()
|
||||
license.Features.GuestAccountsPermissions = model.NewBool(true)
|
||||
th.App.SetLicense(license)
|
||||
guestRole, err := th.App.Srv.Store.Role().GetByName("system_guest")
|
||||
require.Nil(t, err)
|
||||
_, resp = th.SystemAdminClient.PatchRole(guestRole.Id, patch)
|
||||
CheckNoError(t, resp)
|
||||
})
|
||||
}
|
||||
|
||||
37
api4/team.go
37
api4/team.go
@@ -66,6 +66,7 @@ func (api *API) InitTeam() {
|
||||
api.BaseRoutes.TeamMember.Handle("/schemeRoles", api.ApiSessionRequired(updateTeamMemberSchemeRoles)).Methods("PUT")
|
||||
api.BaseRoutes.Team.Handle("/import", api.ApiSessionRequired(importTeam)).Methods("POST")
|
||||
api.BaseRoutes.Team.Handle("/invite/email", api.ApiSessionRequired(inviteUsersToTeam)).Methods("POST")
|
||||
api.BaseRoutes.Team.Handle("/invite-guests/email", api.ApiSessionRequired(inviteGuestsToChannels)).Methods("POST")
|
||||
api.BaseRoutes.Teams.Handle("/invites/email", api.ApiSessionRequired(invalidateAllEmailInvites)).Methods("DELETE")
|
||||
api.BaseRoutes.Teams.Handle("/invite/{invite_id:[A-Za-z0-9]+}", api.ApiHandler(getInviteInfo)).Methods("GET")
|
||||
|
||||
@@ -913,6 +914,42 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func inviteGuestsToChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("Api4.InviteGuestsToChannels", "api.team.invate_guests_to_channels.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !*c.App.Config().GuestAccountsSettings.Enable {
|
||||
c.Err = model.NewAppError("Api4.InviteGuestsToChannels", "api.team.invate_guests_to_channels.disabled.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.App.Session, c.Params.TeamId, model.PERMISSION_INVITE_GUEST) {
|
||||
c.SetPermissionError(model.PERMISSION_INVITE_GUEST)
|
||||
return
|
||||
}
|
||||
|
||||
guestsInvite := model.GuestsInviteFromJson(r.Body)
|
||||
if err := guestsInvite.IsValid(); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
err := c.App.InviteGuestsToChannels(c.Params.TeamId, guestsInvite, c.App.Session.UserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireInviteId()
|
||||
if c.Err != nil {
|
||||
|
||||
@@ -2290,6 +2290,136 @@ func TestInviteUsersToTeam(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestInviteGuestsToTeam(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
guest1 := th.GenerateTestEmail()
|
||||
guest2 := th.GenerateTestEmail()
|
||||
|
||||
emailList := []string{guest1, guest2}
|
||||
|
||||
//Delete all the messages before check the sample email
|
||||
mailservice.DeleteMailBox(guest1)
|
||||
mailservice.DeleteMailBox(guest2)
|
||||
|
||||
enableEmailInvitations := *th.App.Config().ServiceSettings.EnableEmailInvitations
|
||||
restrictCreationToDomains := th.App.Config().TeamSettings.RestrictCreationToDomains
|
||||
enableGuestAccounts := *th.App.Config().GuestAccountsSettings.Enable
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableEmailInvitations = &enableEmailInvitations })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.RestrictCreationToDomains = restrictCreationToDomains })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GuestAccountsSettings.Enable = &enableGuestAccounts })
|
||||
}()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense(""))
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = false })
|
||||
_, resp := th.SystemAdminClient.InviteGuestsToTeam(th.BasicTeam.Id, emailList, []string{th.BasicChannel.Id}, "test-message")
|
||||
assert.NotNil(t, resp.Error, "Should be disabled")
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableEmailInvitations = false })
|
||||
_, resp = th.SystemAdminClient.InviteGuestsToTeam(th.BasicTeam.Id, emailList, []string{th.BasicChannel.Id}, "test-message")
|
||||
if resp.Error == nil {
|
||||
t.Fatal("Should be disabled")
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableEmailInvitations = true })
|
||||
|
||||
th.App.SetLicense(nil)
|
||||
|
||||
_, resp = th.SystemAdminClient.InviteGuestsToTeam(th.BasicTeam.Id, emailList, []string{th.BasicChannel.Id}, "test-message")
|
||||
if resp.Error == nil {
|
||||
t.Fatal("Should be disabled")
|
||||
}
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense(""))
|
||||
defer th.App.SetLicense(nil)
|
||||
|
||||
okMsg, resp := th.SystemAdminClient.InviteGuestsToTeam(th.BasicTeam.Id, emailList, []string{th.BasicChannel.Id}, "test-message")
|
||||
CheckNoError(t, resp)
|
||||
if !okMsg {
|
||||
t.Fatal("should return true")
|
||||
}
|
||||
|
||||
nameFormat := *th.App.Config().TeamSettings.TeammateNameDisplay
|
||||
expectedSubject := utils.T("api.templates.invite_guest_subject",
|
||||
map[string]interface{}{"SenderName": th.SystemAdminUser.GetDisplayName(nameFormat),
|
||||
"TeamDisplayName": th.BasicTeam.DisplayName,
|
||||
"SiteName": th.App.ClientConfig()["SiteName"]})
|
||||
|
||||
//Check if the email was send to the rigth email address
|
||||
for _, email := range emailList {
|
||||
var resultsMailbox mailservice.JSONMessageHeaderInbucket
|
||||
err := mailservice.RetryInbucket(5, func() error {
|
||||
var err error
|
||||
resultsMailbox, err = mailservice.GetMailBox(email)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Log("No email was received, maybe due load on the server. Disabling this verification")
|
||||
}
|
||||
if err == nil && len(resultsMailbox) > 0 {
|
||||
if !strings.ContainsAny(resultsMailbox[len(resultsMailbox)-1].To[0], email) {
|
||||
t.Fatal("Wrong To recipient")
|
||||
} else {
|
||||
if resultsEmail, err := mailservice.GetMessageFromMailbox(email, resultsMailbox[len(resultsMailbox)-1].ID); err == nil {
|
||||
if resultsEmail.Subject != expectedSubject {
|
||||
t.Log(resultsEmail.Subject)
|
||||
t.Log(expectedSubject)
|
||||
t.Fatal("Wrong Subject")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictCreationToDomains = "@global.com,@common.com" })
|
||||
|
||||
t.Run("restricted domains", func(t *testing.T) {
|
||||
err := th.App.InviteGuestsToChannels(th.BasicTeam.Id, &model.GuestsInvite{Emails: emailList, Channels: []string{th.BasicChannel.Id}, Message: "test message"}, th.BasicUser.Id)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Adding users with non-restricted domains was allowed")
|
||||
}
|
||||
if err.Where != "InviteGuestsToChannels" || err.Id != "api.team.invite_members.invalid_email.app_error" {
|
||||
t.Log(err)
|
||||
t.Fatal("Got wrong error message!")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("override restricted domains", func(t *testing.T) {
|
||||
th.BasicTeam.AllowedDomains = "invalid.com,common.com"
|
||||
if _, err := th.App.UpdateTeam(th.BasicTeam); err == nil {
|
||||
t.Fatal("Should not update the team")
|
||||
}
|
||||
|
||||
th.BasicTeam.AllowedDomains = "common.com"
|
||||
if _, err := th.App.UpdateTeam(th.BasicTeam); err != nil {
|
||||
t.Log(err)
|
||||
t.Fatal("Should update the team")
|
||||
}
|
||||
|
||||
if err := th.App.InviteGuestsToChannels(th.BasicTeam.Id, &model.GuestsInvite{Emails: []string{"test@global.com"}, Channels: []string{th.BasicChannel.Id}, Message: "test message"}, th.BasicUser.Id); err == nil || err.Where != "InviteGuestsToChannels" {
|
||||
t.Log(err)
|
||||
t.Fatal("Per team restriction should take precedence over the global restriction")
|
||||
}
|
||||
|
||||
if err := th.App.InviteGuestsToChannels(th.BasicTeam.Id, &model.GuestsInvite{Emails: []string{"test@common.com"}, Channels: []string{th.BasicChannel.Id}, Message: "test message"}, th.BasicUser.Id); err != nil {
|
||||
t.Log(err)
|
||||
t.Fatal("Failed to invite user which was common between team and global domain restriction")
|
||||
}
|
||||
|
||||
if err := th.App.InviteGuestsToChannels(th.BasicTeam.Id, &model.GuestsInvite{Emails: []string{"test@invalid.com"}, Channels: []string{th.BasicChannel.Id}, Message: "test message"}, th.BasicUser.Id); err == nil {
|
||||
t.Log(err)
|
||||
t.Fatal("Should not invite user")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTeamInviteInfo(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
112
api4/user.go
112
api4/user.go
@@ -40,6 +40,8 @@ func (api *API) InitUser() {
|
||||
api.BaseRoutes.User.Handle("/roles", api.ApiSessionRequired(updateUserRoles)).Methods("PUT")
|
||||
api.BaseRoutes.User.Handle("/active", api.ApiSessionRequired(updateUserActive)).Methods("PUT")
|
||||
api.BaseRoutes.User.Handle("/password", api.ApiSessionRequired(updatePassword)).Methods("PUT")
|
||||
api.BaseRoutes.User.Handle("/promote", api.ApiSessionRequired(promoteGuestToUser)).Methods("POST")
|
||||
api.BaseRoutes.User.Handle("/demote", api.ApiSessionRequired(demoteUserToGuest)).Methods("POST")
|
||||
api.BaseRoutes.Users.Handle("/password/reset", api.ApiHandler(resetPassword)).Methods("POST")
|
||||
api.BaseRoutes.Users.Handle("/password/reset/send", api.ApiHandler(sendPasswordReset)).Methods("POST")
|
||||
api.BaseRoutes.Users.Handle("/email/verify", api.ApiHandler(verifyUserEmail)).Methods("POST")
|
||||
@@ -92,7 +94,24 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
var ruser *model.User
|
||||
var err *model.AppError
|
||||
if len(tokenId) > 0 {
|
||||
ruser, err = c.App.CreateUserWithToken(user, tokenId)
|
||||
var token *model.Token
|
||||
token, err = c.App.Srv.Store.Token().GetByToken(tokenId)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("CreateUserWithToken", "api.user.create_user.signup_link_invalid.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if token.Type == app.TOKEN_TYPE_GUEST_INVITATION {
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("CreateUserWithToken", "api.user.create_user.guest_accounts.license.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !*c.App.Config().GuestAccountsSettings.Enable {
|
||||
c.Err = model.NewAppError("CreateUserWithToken", "api.user.create_user.guest_accounts.disabled.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
ruser, err = c.App.CreateUserWithToken(user, token)
|
||||
} else if len(inviteId) > 0 {
|
||||
ruser, err = c.App.CreateUserWithInviteId(user, inviteId)
|
||||
} else if c.IsSystemAdmin() {
|
||||
@@ -1372,6 +1391,17 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if user.IsGuest() {
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("login", "api.user.login.guest_accounts.license.error", nil, "", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if !*c.App.Config().GuestAccountsSettings.Enable {
|
||||
c.Err = model.NewAppError("login", "api.user.login.guest_accounts.disabled.error", nil, "", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAuditWithUserId(user.Id, "authenticated")
|
||||
|
||||
session, err := c.App.DoLogin(w, r, user, deviceId)
|
||||
@@ -1941,3 +1971,83 @@ func getUserTermsOfService(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
w.Write([]byte(result.ToJson()))
|
||||
}
|
||||
|
||||
func promoteGuestToUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireUserId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("Api4.promoteGuestToUser", "api.team.promote_guest_to_user.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !*c.App.Config().GuestAccountsSettings.Enable {
|
||||
c.Err = model.NewAppError("Api4.promoteGuestToUser", "api.team.promote_guest_to_user.disabled.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_PROMOTE_GUEST) {
|
||||
c.SetPermissionError(model.PERMISSION_PROMOTE_GUEST)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := c.App.GetUser(c.Params.UserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !user.IsGuest() {
|
||||
c.Err = model.NewAppError("Api4.promoteGuestToUser", "api.user.promote_guest_to_user.no_guest.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.PromoteGuestToUser(user, c.App.Session.UserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func demoteUserToGuest(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireUserId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("Api4.demoteUserToGuest", "api.team.demote_user_to_guest.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !*c.App.Config().GuestAccountsSettings.Enable {
|
||||
c.Err = model.NewAppError("Api4.demoteUserToGuest", "api.team.demote_user_to_guest.disabled.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_DEMOTE_TO_GUEST) {
|
||||
c.SetPermissionError(model.PERMISSION_DEMOTE_TO_GUEST)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := c.App.GetUser(c.Params.UserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if user.IsGuest() {
|
||||
c.Err = model.NewAppError("Api4.demoteUserToGuest", "api.user.demote_user_to_guest.already_guest.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.DemoteUserToGuest(user); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
@@ -158,6 +158,9 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
|
||||
model.PERMISSION_EDIT_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_MANAGE_OAUTH.Id,
|
||||
model.PERMISSION_INVITE_USER.Id,
|
||||
model.PERMISSION_INVITE_GUEST.Id,
|
||||
model.PERMISSION_PROMOTE_GUEST.Id,
|
||||
model.PERMISSION_DEMOTE_TO_GUEST.Id,
|
||||
model.PERMISSION_DELETE_POST.Id,
|
||||
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_CREATE_TEAM.Id,
|
||||
@@ -340,6 +343,9 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
|
||||
model.PERMISSION_EDIT_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_MANAGE_OAUTH.Id,
|
||||
model.PERMISSION_INVITE_USER.Id,
|
||||
model.PERMISSION_INVITE_GUEST.Id,
|
||||
model.PERMISSION_PROMOTE_GUEST.Id,
|
||||
model.PERMISSION_DEMOTE_TO_GUEST.Id,
|
||||
model.PERMISSION_DELETE_POST.Id,
|
||||
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_CREATE_TEAM.Id,
|
||||
@@ -484,6 +490,9 @@ func TestDoEmojisPermissionsMigration(t *testing.T) {
|
||||
model.PERMISSION_EDIT_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_MANAGE_OAUTH.Id,
|
||||
model.PERMISSION_INVITE_USER.Id,
|
||||
model.PERMISSION_INVITE_GUEST.Id,
|
||||
model.PERMISSION_PROMOTE_GUEST.Id,
|
||||
model.PERMISSION_DEMOTE_TO_GUEST.Id,
|
||||
model.PERMISSION_DELETE_POST.Id,
|
||||
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_CREATE_TEAM.Id,
|
||||
|
||||
@@ -1317,6 +1317,10 @@ func (a *App) GetChannelMemberCount(channelId string) (int64, *model.AppError) {
|
||||
return a.Srv.Store.Channel().GetMemberCount(channelId, true)
|
||||
}
|
||||
|
||||
func (a *App) GetChannelGuestCount(channelId string) (int64, *model.AppError) {
|
||||
return a.Srv.Store.Channel().GetGuestCount(channelId, true)
|
||||
}
|
||||
|
||||
func (a *App) GetChannelCounts(teamId string, userId string) (*model.ChannelCounts, *model.AppError) {
|
||||
return a.Srv.Store.Channel().GetChannelCounts(teamId, userId)
|
||||
}
|
||||
@@ -1577,8 +1581,16 @@ func (a *App) postRemoveFromChannelMessage(removerUserId string, removedUser *mo
|
||||
}
|
||||
|
||||
func (a *App) removeUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError {
|
||||
user, err := a.Srv.Store.User().Get(userIdToRemove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isGuest := user.IsGuest()
|
||||
|
||||
if channel.Name == model.DEFAULT_CHANNEL {
|
||||
return model.NewAppError("RemoveUserFromChannel", "api.channel.remove.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "", http.StatusBadRequest)
|
||||
if !isGuest {
|
||||
return model.NewAppError("RemoveUserFromChannel", "api.channel.remove.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "", http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
if channel.IsGroupConstrained() && userIdToRemove != removerUserId {
|
||||
@@ -1603,6 +1615,23 @@ func (a *App) removeUserFromChannel(userIdToRemove string, removerUserId string,
|
||||
return err
|
||||
}
|
||||
|
||||
if isGuest {
|
||||
currentMembers, err := a.GetChannelMembersForUser(channel.TeamId, userIdToRemove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(*currentMembers) == 0 {
|
||||
teamMember, err := a.GetTeamMember(channel.TeamId, userIdToRemove)
|
||||
if err != nil {
|
||||
return model.NewAppError("removeUserFromChannel", "api.team.remove_user_from_team.missing.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if err = a.RemoveTeamMemberFromTeam(teamMember, removerUserId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.InvalidateCacheForUser(userIdToRemove)
|
||||
a.InvalidateCacheForChannelMembers(channel.Id)
|
||||
|
||||
|
||||
@@ -437,6 +437,63 @@ func TestAddUserToChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
assert.NotNil(t, histories[0].LeaveTime)
|
||||
}*/
|
||||
|
||||
func TestLeaveDefaultChannel(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
guest := th.CreateGuest()
|
||||
th.LinkUserToTeam(guest, th.BasicTeam)
|
||||
|
||||
townSquare, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, false)
|
||||
require.Nil(t, err)
|
||||
th.AddUserToChannel(guest, townSquare)
|
||||
th.AddUserToChannel(th.BasicUser, townSquare)
|
||||
|
||||
t.Run("User tries to leave the default channel", func(t *testing.T) {
|
||||
err = th.App.LeaveChannel(townSquare.Id, th.BasicUser.Id)
|
||||
assert.NotNil(t, err, "It should fail to remove a regular user from the default channel")
|
||||
assert.Equal(t, err.Id, "api.channel.remove.default.app_error")
|
||||
_, err = th.App.GetChannelMember(townSquare.Id, th.BasicUser.Id)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Guest leaves the default channel", func(t *testing.T) {
|
||||
err = th.App.LeaveChannel(townSquare.Id, guest.Id)
|
||||
assert.Nil(t, err, "It should allow to remove a guest user from the default channel")
|
||||
_, err = th.App.GetChannelMember(townSquare.Id, guest.Id)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLeaveLastChannel(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
guest := th.CreateGuest()
|
||||
th.LinkUserToTeam(guest, th.BasicTeam)
|
||||
|
||||
townSquare, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, false)
|
||||
require.Nil(t, err)
|
||||
th.AddUserToChannel(guest, townSquare)
|
||||
th.AddUserToChannel(guest, th.BasicChannel)
|
||||
|
||||
t.Run("Guest leaves not last channel", func(t *testing.T) {
|
||||
err = th.App.LeaveChannel(townSquare.Id, guest.Id)
|
||||
require.Nil(t, err)
|
||||
_, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
|
||||
assert.Nil(t, err, "It should maintain the team membership")
|
||||
})
|
||||
|
||||
t.Run("Guest leaves last channel", func(t *testing.T) {
|
||||
err = th.App.LeaveChannel(th.BasicChannel.Id, guest.Id)
|
||||
assert.Nil(t, err, "It should allow to remove a guest user from the default channel")
|
||||
_, err = th.App.GetChannelMember(th.BasicChannel.Id, guest.Id)
|
||||
assert.NotNil(t, err)
|
||||
_, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
|
||||
assert.Nil(t, err, "It should remove the team membership")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddChannelMemberNoUserRequestor(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -38,18 +38,41 @@ func (me *LeaveProvider) DoCommand(a *App, args *model.CommandArgs, message stri
|
||||
if channel, noChannelErr = a.GetChannel(args.ChannelId); noChannelErr != nil {
|
||||
return &model.CommandResponse{Text: args.T("api.command_leave.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
if channel.Name == model.DEFAULT_CHANNEL {
|
||||
return &model.CommandResponse{Text: args.T("api.channel.leave.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
err := a.LeaveChannel(args.ChannelId, args.UserId)
|
||||
if err != nil {
|
||||
return &model.CommandResponse{Text: args.T("api.command_leave.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
team, err := a.GetTeam(args.TeamId)
|
||||
if err != nil {
|
||||
return &model.CommandResponse{Text: args.T("api.command_leave.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
err = a.LeaveChannel(args.ChannelId, args.UserId)
|
||||
if err != nil {
|
||||
if channel.Name == model.DEFAULT_CHANNEL {
|
||||
return &model.CommandResponse{Text: args.T("api.channel.leave.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
return &model.CommandResponse{Text: args.T("api.command_leave.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
member, err := a.GetTeamMember(team.Id, args.UserId)
|
||||
if err != nil || member.DeleteAt != 0 {
|
||||
return &model.CommandResponse{GotoLocation: args.SiteURL + "/"}
|
||||
}
|
||||
|
||||
user, err := a.GetUser(args.UserId)
|
||||
if err != nil {
|
||||
return &model.CommandResponse{Text: args.T("api.command_leave.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
if user.IsGuest() {
|
||||
members, err := a.GetChannelMembersForUser(team.Id, args.UserId)
|
||||
if err != nil || len(*members) == 0 {
|
||||
return &model.CommandResponse{Text: args.T("api.command_leave.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
channel, err := a.GetChannel((*members)[0].ChannelId)
|
||||
if err != nil {
|
||||
return &model.CommandResponse{Text: args.T("api.command_leave.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
return &model.CommandResponse{GotoLocation: args.SiteURL + "/" + team.Name + "/channels/" + channel.Name}
|
||||
}
|
||||
|
||||
return &model.CommandResponse{GotoLocation: args.SiteURL + "/" + team.Name + "/channels/" + model.DEFAULT_CHANNEL}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ package app
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
@@ -34,53 +34,114 @@ func TestLeaveProviderDoCommand(t *testing.T) {
|
||||
CreatorId: th.BasicUser.Id,
|
||||
}, false)
|
||||
|
||||
defaultChannel, err := th.App.GetChannelByName(model.DEFAULT_CHANNEL, th.BasicTeam.Id, false)
|
||||
require.Nil(t, err)
|
||||
|
||||
guest := th.CreateGuest()
|
||||
|
||||
th.App.AddUserToTeam(th.BasicTeam.Id, th.BasicUser.Id, th.BasicUser.Id)
|
||||
th.App.AddUserToChannel(th.BasicUser, publicChannel)
|
||||
th.App.AddUserToChannel(th.BasicUser, privateChannel)
|
||||
th.App.AddUserToTeam(th.BasicTeam.Id, guest.Id, guest.Id)
|
||||
th.App.AddUserToChannel(guest, publicChannel)
|
||||
th.App.AddUserToChannel(guest, defaultChannel)
|
||||
|
||||
args := &model.CommandArgs{
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
}
|
||||
t.Run("Should error when no Channel ID in args", func(t *testing.T) {
|
||||
args := &model.CommandArgs{
|
||||
UserId: th.BasicUser.Id,
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
}
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.command_leave.fail.app_error", actual.Text)
|
||||
assert.Equal(t, model.COMMAND_RESPONSE_TYPE_EPHEMERAL, actual.ResponseType)
|
||||
})
|
||||
|
||||
// Should error when no Channel ID in args
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.command_leave.fail.app_error", actual.Text)
|
||||
assert.Equal(t, model.COMMAND_RESPONSE_TYPE_EPHEMERAL, actual.ResponseType)
|
||||
t.Run("Should error when no Team ID in args", func(t *testing.T) {
|
||||
args := &model.CommandArgs{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: publicChannel.Id,
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
}
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.command_leave.fail.app_error", actual.Text)
|
||||
assert.Equal(t, model.COMMAND_RESPONSE_TYPE_EPHEMERAL, actual.ResponseType)
|
||||
})
|
||||
|
||||
// Should error when no Team ID in args
|
||||
args.ChannelId = publicChannel.Id
|
||||
actual = lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.command_leave.fail.app_error", actual.Text)
|
||||
assert.Equal(t, model.COMMAND_RESPONSE_TYPE_EPHEMERAL, actual.ResponseType)
|
||||
t.Run("Leave a public channel", func(t *testing.T) {
|
||||
args := &model.CommandArgs{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: publicChannel.Id,
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
TeamId: th.BasicTeam.Id,
|
||||
SiteURL: "http://localhost:8065",
|
||||
}
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "", actual.Text)
|
||||
assert.Equal(t, args.SiteURL+"/"+th.BasicTeam.Name+"/channels/"+model.DEFAULT_CHANNEL, actual.GotoLocation)
|
||||
assert.Equal(t, "", actual.ResponseType)
|
||||
|
||||
// Leave a public channel
|
||||
siteURL := "http://localhost:8065"
|
||||
args.TeamId = th.BasicTeam.Id
|
||||
args.SiteURL = siteURL
|
||||
actual = lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "", actual.Text)
|
||||
assert.Equal(t, siteURL+"/"+th.BasicTeam.Name+"/channels/"+model.DEFAULT_CHANNEL, actual.GotoLocation)
|
||||
assert.Equal(t, "", actual.ResponseType)
|
||||
_, err = th.App.GetChannelMember(publicChannel.Id, th.BasicUser.Id)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotNil(t, err.Id, "store.sql_channel.get_member.missing.app_error")
|
||||
})
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
t.Run("Leave a private channel", func(t *testing.T) {
|
||||
args := &model.CommandArgs{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: privateChannel.Id,
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
TeamId: th.BasicTeam.Id,
|
||||
SiteURL: "http://localhost:8065",
|
||||
}
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "", actual.Text)
|
||||
})
|
||||
|
||||
member, err := th.App.GetChannelMember(publicChannel.Id, th.BasicUser.Id)
|
||||
if member == nil {
|
||||
t.Errorf("Expected member object, got nil")
|
||||
}
|
||||
t.Run("Should not leave a default channel", func(t *testing.T) {
|
||||
args := &model.CommandArgs{
|
||||
UserId: th.BasicUser.Id,
|
||||
ChannelId: defaultChannel.Id,
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
TeamId: th.BasicTeam.Id,
|
||||
SiteURL: "http://localhost:8065",
|
||||
}
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.channel.leave.default.app_error", actual.Text)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil object, got %s", err)
|
||||
}
|
||||
t.Run("Should allow to leave a default channel if user is guest", func(t *testing.T) {
|
||||
args := &model.CommandArgs{
|
||||
UserId: guest.Id,
|
||||
ChannelId: defaultChannel.Id,
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
TeamId: th.BasicTeam.Id,
|
||||
SiteURL: "http://localhost:8065",
|
||||
}
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "", actual.Text)
|
||||
assert.Equal(t, args.SiteURL+"/"+th.BasicTeam.Name+"/channels/"+publicChannel.Name, actual.GotoLocation)
|
||||
assert.Equal(t, "", actual.ResponseType)
|
||||
|
||||
// Leave a private channel
|
||||
args.ChannelId = privateChannel.Id
|
||||
actual = lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "", actual.Text)
|
||||
_, err = th.App.GetChannelMember(defaultChannel.Id, guest.Id)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotNil(t, err.Id, "store.sql_channel.get_member.missing.app_error")
|
||||
})
|
||||
|
||||
// Should not leave a default channel
|
||||
defaultChannel, _ := th.App.GetChannelByName(model.DEFAULT_CHANNEL, th.BasicTeam.Id, false)
|
||||
args.ChannelId = defaultChannel.Id
|
||||
actual = lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.channel.leave.default.app_error", actual.Text)
|
||||
t.Run("Should redirect to the team if is the last channel", func(t *testing.T) {
|
||||
args := &model.CommandArgs{
|
||||
UserId: guest.Id,
|
||||
ChannelId: publicChannel.Id,
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
TeamId: th.BasicTeam.Id,
|
||||
SiteURL: "http://localhost:8065",
|
||||
}
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "", actual.Text)
|
||||
assert.Equal(t, args.SiteURL+"/", actual.GotoLocation)
|
||||
assert.Equal(t, "", actual.ResponseType)
|
||||
|
||||
_, err = th.App.GetChannelMember(publicChannel.Id, guest.Id)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotNil(t, err.Id, "store.sql_channel.get_member.missing.app_error")
|
||||
})
|
||||
}
|
||||
|
||||
82
app/email.go
82
app/email.go
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"net/http"
|
||||
|
||||
@@ -357,6 +358,87 @@ func (a *App) SendInviteEmails(team *model.Team, senderName string, senderUserId
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) SendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, invites []string, siteURL string, message string) {
|
||||
if a.Srv.EmailRateLimiter == nil {
|
||||
a.Log.Error("Email invite not sent, rate limiting could not be setup.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id))
|
||||
return
|
||||
}
|
||||
rateLimited, result, err := a.Srv.EmailRateLimiter.RateLimit(senderUserId, len(invites))
|
||||
if err != nil {
|
||||
a.Log.Error("Error rate limiting invite email.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
if rateLimited {
|
||||
a.Log.Error("Invite emails rate limited.",
|
||||
mlog.String("user_id", senderUserId),
|
||||
mlog.String("team_id", team.Id),
|
||||
mlog.String("retry_after", result.RetryAfter.String()),
|
||||
mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, invite := range invites {
|
||||
if len(invite) > 0 {
|
||||
senderRole := utils.T("api.team.invite_members.member")
|
||||
|
||||
subject := utils.T("api.templates.invite_guest_subject",
|
||||
map[string]interface{}{"SenderName": senderName,
|
||||
"TeamDisplayName": team.DisplayName,
|
||||
"SiteName": a.ClientConfig()["SiteName"]})
|
||||
|
||||
bodyPage := a.NewEmailTemplate("invite_body", model.DEFAULT_LOCALE)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title")
|
||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body_guest.info",
|
||||
map[string]interface{}{"SenderStatus": senderRole, "SenderName": senderName, "TeamDisplayName": team.DisplayName})
|
||||
bodyPage.Props["Button"] = utils.T("api.templates.invite_body.button")
|
||||
bodyPage.Props["Message"] = ""
|
||||
if message != "" {
|
||||
bodyPage.Props["Message"] = message
|
||||
}
|
||||
bodyPage.Html["ExtraInfo"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.extra_info",
|
||||
map[string]interface{}{"TeamDisplayName": team.DisplayName})
|
||||
bodyPage.Props["TeamURL"] = siteURL + "/" + team.Name
|
||||
|
||||
channelIds := []string{}
|
||||
for _, channel := range channels {
|
||||
channelIds = append(channelIds, channel.Id)
|
||||
}
|
||||
|
||||
token := model.NewToken(
|
||||
TOKEN_TYPE_GUEST_INVITATION,
|
||||
model.MapToJson(map[string]string{
|
||||
"teamId": team.Id,
|
||||
"channels": strings.Join(channelIds, " "),
|
||||
"email": invite,
|
||||
"guest": "true",
|
||||
}),
|
||||
)
|
||||
|
||||
props := make(map[string]string)
|
||||
props["email"] = invite
|
||||
props["display_name"] = team.DisplayName
|
||||
props["name"] = team.Name
|
||||
data := model.MapToJson(props)
|
||||
|
||||
if err := a.Srv.Store.Token().Save(token); err != nil {
|
||||
mlog.Error(fmt.Sprintf("Failed to send invite email successfully err=%v", err))
|
||||
continue
|
||||
}
|
||||
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token))
|
||||
|
||||
if !*a.Config().EmailSettings.SendEmailNotifications {
|
||||
mlog.Info(fmt.Sprintf("sending invitation to %v %v", invite, bodyPage.Props["Link"]))
|
||||
}
|
||||
|
||||
if err := a.SendMail(invite, subject, bodyPage.Render()); err != nil {
|
||||
mlog.Error(fmt.Sprintf("Failed to send invite email successfully err=%v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) NewEmailTemplate(name, locale string) *utils.HTMLTemplate {
|
||||
t := utils.NewHTMLTemplate(a.HTMLTemplates(), name)
|
||||
|
||||
|
||||
@@ -149,6 +149,11 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
session.AddProp(model.SESSION_PROP_PLATFORM, plat)
|
||||
session.AddProp(model.SESSION_PROP_OS, os)
|
||||
session.AddProp(model.SESSION_PROP_BROWSER, fmt.Sprintf("%v/%v", bname, bversion))
|
||||
if user.IsGuest() {
|
||||
session.AddProp(model.SESSION_PROP_IS_GUEST, "true")
|
||||
} else {
|
||||
session.AddProp(model.SESSION_PROP_IS_GUEST, "false")
|
||||
}
|
||||
|
||||
var err *model.AppError
|
||||
if session, err = a.CreateSession(session); err != nil {
|
||||
|
||||
@@ -23,6 +23,7 @@ const (
|
||||
MIGRATION_KEY_APPLY_CHANNEL_MANAGE_DELETE_TO_CHANNEL_USER = "apply_channel_manage_delete_to_channel_user"
|
||||
MIGRATION_KEY_REMOVE_CHANNEL_MANAGE_DELETE_FROM_TEAM_USER = "remove_channel_manage_delete_from_team_user"
|
||||
MIGRATION_KEY_VIEW_MEMBERS_NEW_PERMISSION = "view_members_new_permission"
|
||||
MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS = "add_manage_guests_permissions"
|
||||
|
||||
PERMISSION_MANAGE_SYSTEM = "manage_system"
|
||||
PERMISSION_MANAGE_EMOJIS = "manage_emojis"
|
||||
@@ -51,6 +52,10 @@ const (
|
||||
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = "manage_public_channel_properties"
|
||||
PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES = "manage_private_channel_properties"
|
||||
PERMISSION_VIEW_MEMBERS = "view_members"
|
||||
PERMISSION_INVITE_USER = "invite_user"
|
||||
PERMISSION_INVITE_GUEST = "invite_guest"
|
||||
PERMISSION_PROMOTE_GUEST = "promote_guest"
|
||||
PERMISSION_DEMOTE_TO_GUEST = "demote_to_guest"
|
||||
)
|
||||
|
||||
func isRole(role string) func(string, map[string]map[string]bool) bool {
|
||||
@@ -273,6 +278,19 @@ func getViewMembersPermissionMigration() permissionsMap {
|
||||
}
|
||||
}
|
||||
|
||||
func getAddManageGuestsPermissionsMigration() permissionsMap {
|
||||
return permissionsMap{
|
||||
permissionTransformation{
|
||||
On: isRole(model.SYSTEM_ADMIN_ROLE_ID),
|
||||
Add: []string{PERMISSION_PROMOTE_GUEST, PERMISSION_DEMOTE_TO_GUEST},
|
||||
},
|
||||
permissionTransformation{
|
||||
On: permissionExists(PERMISSION_INVITE_USER),
|
||||
Add: []string{PERMISSION_INVITE_GUEST},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DoPermissionsMigrations execute all the permissions migrations need by the current version.
|
||||
func (a *App) DoPermissionsMigrations() *model.AppError {
|
||||
PermissionsMigrations := []struct {
|
||||
@@ -287,6 +305,7 @@ func (a *App) DoPermissionsMigrations() *model.AppError {
|
||||
{Key: MIGRATION_KEY_APPLY_CHANNEL_MANAGE_DELETE_TO_CHANNEL_USER, Migration: applyChannelManageDeleteToChannelUser},
|
||||
{Key: MIGRATION_KEY_REMOVE_CHANNEL_MANAGE_DELETE_FROM_TEAM_USER, Migration: removeChannelManageDeleteFromTeamUser},
|
||||
{Key: MIGRATION_KEY_VIEW_MEMBERS_NEW_PERMISSION, Migration: getViewMembersPermissionMigration},
|
||||
{Key: MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS, Migration: getAddManageGuestsPermissionsMigration},
|
||||
}
|
||||
|
||||
for _, migration := range PermissionsMigrations {
|
||||
|
||||
@@ -342,7 +342,11 @@ func (api *PluginAPI) GetChannelStats(channelId string) (*model.ChannelStats, *m
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.ChannelStats{ChannelId: channelId, MemberCount: memberCount}, nil
|
||||
guestCount, err := api.app.GetChannelMemberCount(channelId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.ChannelStats{ChannelId: channelId, MemberCount: memberCount, GuestCount: guestCount}, nil
|
||||
}
|
||||
|
||||
func (api *PluginAPI) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError) {
|
||||
|
||||
@@ -93,6 +93,27 @@ func (a *App) GetSessions(userId string) ([]*model.Session, *model.AppError) {
|
||||
return a.Srv.Store.Session().GetSessions(userId)
|
||||
}
|
||||
|
||||
func (a *App) UpdateSessionsIsGuest(userId string, isGuest bool) {
|
||||
sessions, err := a.Srv.Store.Session().GetSessions(userId)
|
||||
if err != nil {
|
||||
mlog.Error(fmt.Sprintf("Unable to get user sessions: userId=%s err=%s", userId, err.Error()))
|
||||
}
|
||||
|
||||
for _, session := range sessions {
|
||||
if isGuest {
|
||||
session.AddProp(model.SESSION_PROP_IS_GUEST, "true")
|
||||
} else {
|
||||
session.AddProp(model.SESSION_PROP_IS_GUEST, "false")
|
||||
}
|
||||
err := a.Srv.Store.Session().UpdateProps(session)
|
||||
if err != nil {
|
||||
mlog.Error(fmt.Sprintf("Unable to update isGuest session: %s", err.Error()))
|
||||
continue
|
||||
}
|
||||
a.AddSessionToCache(session)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) RevokeAllSessions(userId string) *model.AppError {
|
||||
sessions, err := a.Srv.Store.Session().GetSessions(userId)
|
||||
if err != nil {
|
||||
@@ -329,6 +350,11 @@ func (a *App) createSessionForUserAccessToken(tokenString string) (*model.Sessio
|
||||
if user.IsBot {
|
||||
session.AddProp(model.SESSION_PROP_IS_BOT, model.SESSION_PROP_IS_BOT_VALUE)
|
||||
}
|
||||
if user.IsGuest() {
|
||||
session.AddProp(model.SESSION_PROP_IS_GUEST, "true")
|
||||
} else {
|
||||
session.AddProp(model.SESSION_PROP_IS_GUEST, "false")
|
||||
}
|
||||
session.SetExpireInDays(model.SESSION_USER_ACCESS_TOKEN_EXPIRY)
|
||||
|
||||
session, err = a.Srv.Store.Session().Save(session)
|
||||
|
||||
@@ -123,3 +123,57 @@ func TestGetSessionIdleTimeoutInMinutes(t *testing.T) {
|
||||
_, err = th.App.GetSession(session.Token)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestUpdateSessionOnPromoteDemote(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
|
||||
t.Run("Promote Guest to User updates the session", func(t *testing.T) {
|
||||
guest := th.CreateGuest()
|
||||
|
||||
session, err := th.App.CreateSession(&model.Session{UserId: guest.Id, Props: model.StringMap{model.SESSION_PROP_IS_GUEST: "true"}})
|
||||
require.Nil(t, err)
|
||||
|
||||
rsession, err := th.App.GetSession(session.Token)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "true", rsession.Props[model.SESSION_PROP_IS_GUEST])
|
||||
|
||||
err = th.App.PromoteGuestToUser(guest, th.BasicUser.Id)
|
||||
require.Nil(t, err)
|
||||
|
||||
rsession, err = th.App.GetSession(session.Token)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "false", rsession.Props[model.SESSION_PROP_IS_GUEST])
|
||||
|
||||
th.App.ClearSessionCacheForUser(session.UserId)
|
||||
|
||||
rsession, err = th.App.GetSession(session.Token)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "false", rsession.Props[model.SESSION_PROP_IS_GUEST])
|
||||
})
|
||||
|
||||
t.Run("Demote User to Guest updates the session", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
|
||||
session, err := th.App.CreateSession(&model.Session{UserId: user.Id, Props: model.StringMap{model.SESSION_PROP_IS_GUEST: "false"}})
|
||||
require.Nil(t, err)
|
||||
|
||||
rsession, err := th.App.GetSession(session.Token)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "false", rsession.Props[model.SESSION_PROP_IS_GUEST])
|
||||
|
||||
err = th.App.DemoteUserToGuest(user)
|
||||
require.Nil(t, err)
|
||||
|
||||
rsession, err = th.App.GetSession(session.Token)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "true", rsession.Props[model.SESSION_PROP_IS_GUEST])
|
||||
|
||||
th.App.ClearSessionCacheForUser(session.UserId)
|
||||
rsession, err = th.App.GetSession(session.Token)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "true", rsession.Props[model.SESSION_PROP_IS_GUEST])
|
||||
})
|
||||
}
|
||||
|
||||
208
app/team.go
208
app/team.go
@@ -421,11 +421,11 @@ func (a *App) AddUserToTeamByToken(userId string, tokenId string) (*model.Team,
|
||||
return nil, model.NewAppError("AddUserToTeamByToken", "api.user.create_user.signup_link_invalid.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if token.Type != TOKEN_TYPE_TEAM_INVITATION {
|
||||
if token.Type != TOKEN_TYPE_TEAM_INVITATION && token.Type != TOKEN_TYPE_GUEST_INVITATION {
|
||||
return nil, model.NewAppError("AddUserToTeamByToken", "api.user.create_user.signup_link_invalid.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if model.GetMillis()-token.CreateAt >= TEAM_INVITATION_EXPIRY_TIME {
|
||||
if model.GetMillis()-token.CreateAt >= INVITATION_EXPIRY_TIME {
|
||||
a.DeleteToken(token)
|
||||
return nil, model.NewAppError("AddUserToTeamByToken", "api.user.create_user.signup_link_expired.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
@@ -462,10 +462,31 @@ func (a *App) AddUserToTeamByToken(userId string, tokenId string) (*model.Team,
|
||||
}
|
||||
user := result.Data.(*model.User)
|
||||
|
||||
if user.IsGuest() && token.Type == TOKEN_TYPE_TEAM_INVITATION {
|
||||
return nil, model.NewAppError("AddUserToTeamByToken", "api.user.create_user.invalid_invitation_type.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
if !user.IsGuest() && token.Type == TOKEN_TYPE_GUEST_INVITATION {
|
||||
return nil, model.NewAppError("AddUserToTeamByToken", "api.user.create_user.invalid_invitation_type.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if err := a.JoinUserToTeam(team, user, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if token.Type == TOKEN_TYPE_GUEST_INVITATION {
|
||||
channels, err := a.Srv.Store.Channel().GetChannelsByIds(strings.Split(tokenData["channels"], " "))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, channel := range channels {
|
||||
_, err := a.AddUserToChannel(user, channel)
|
||||
if err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.DeleteToken(token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -589,9 +610,11 @@ func (a *App) JoinUserToTeam(team *model.Team, user *model.User, userRequestorId
|
||||
|
||||
shouldBeAdmin := team.Email == user.Email
|
||||
|
||||
// Soft error if there is an issue joining the default channels
|
||||
if err := a.JoinDefaultChannels(team.Id, user, shouldBeAdmin, userRequestorId); err != nil {
|
||||
mlog.Error(fmt.Sprintf("Encountered an issue joining default channels err=%v", err), mlog.String("user_id", user.Id), mlog.String("team_id", team.Id))
|
||||
if !user.IsGuest() {
|
||||
// Soft error if there is an issue joining the default channels
|
||||
if err := a.JoinDefaultChannels(team.Id, user, shouldBeAdmin, userRequestorId); err != nil {
|
||||
mlog.Error(fmt.Sprintf("Encountered an issue joining default channels err=%v", err), mlog.String("user_id", user.Id), mlog.String("team_id", team.Id))
|
||||
}
|
||||
}
|
||||
|
||||
a.ClearSessionCacheForUser(user.Id)
|
||||
@@ -824,54 +847,18 @@ func (a *App) RemoveUserFromTeam(teamId string, userId string, requestorId strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string) *model.AppError {
|
||||
teamMember, err := a.GetTeamMember(team.Id, user.Id)
|
||||
if err != nil {
|
||||
return model.NewAppError("LeaveTeam", "api.team.remove_user_from_team.missing.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
func (a *App) RemoveTeamMemberFromTeam(teamMember *model.TeamMember, requestorId string) *model.AppError {
|
||||
// Send the websocket message before we actually do the remove so the user being removed gets it.
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LEAVE_TEAM, teamMember.TeamId, "", "", nil)
|
||||
message.Add("user_id", teamMember.UserId)
|
||||
message.Add("team_id", teamMember.TeamId)
|
||||
a.Publish(message)
|
||||
|
||||
var channelList *model.ChannelList
|
||||
|
||||
if channelList, err = a.Srv.Store.Channel().GetChannels(team.Id, user.Id, true); err != nil {
|
||||
if err.Id == "store.sql_channel.get_channels.not_found.app_error" {
|
||||
channelList = &model.ChannelList{}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, channel := range *channelList {
|
||||
if !channel.IsGroupOrDirect() {
|
||||
a.InvalidateCacheForChannelMembers(channel.Id)
|
||||
if err = a.Srv.Store.Channel().RemoveMember(channel.Id, user.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
channel, err := a.Srv.Store.Channel().GetByName(team.Id, model.DEFAULT_CHANNEL, false)
|
||||
user, err := a.Srv.Store.User().Get(teamMember.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages {
|
||||
if requestorId == user.Id {
|
||||
if err := a.postLeaveTeamMessage(user, channel); err != nil {
|
||||
mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
|
||||
}
|
||||
} else {
|
||||
if err := a.postRemoveFromTeamMessage(user, channel); err != nil {
|
||||
mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send the websocket message before we actually do the remove so the user being removed gets it.
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LEAVE_TEAM, team.Id, "", "", nil)
|
||||
message.Add("user_id", user.Id)
|
||||
message.Add("team_id", team.Id)
|
||||
a.Publish(message)
|
||||
|
||||
teamMember.Roles = ""
|
||||
teamMember.DeleteAt = model.GetMillis()
|
||||
|
||||
@@ -908,7 +895,7 @@ func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string)
|
||||
}
|
||||
|
||||
// delete the preferences that set the last channel used in the team and other team specific preferences
|
||||
if err := a.Srv.Store.Preference().DeleteCategory(user.Id, team.Id); err != nil {
|
||||
if err := a.Srv.Store.Preference().DeleteCategory(user.Id, teamMember.TeamId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -919,6 +906,56 @@ func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string) *model.AppError {
|
||||
teamMember, err := a.GetTeamMember(team.Id, user.Id)
|
||||
if err != nil {
|
||||
return model.NewAppError("LeaveTeam", "api.team.remove_user_from_team.missing.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
var channelList *model.ChannelList
|
||||
|
||||
if channelList, err = a.Srv.Store.Channel().GetChannels(team.Id, user.Id, true); err != nil {
|
||||
if err.Id == "store.sql_channel.get_channels.not_found.app_error" {
|
||||
channelList = &model.ChannelList{}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, channel := range *channelList {
|
||||
if !channel.IsGroupOrDirect() {
|
||||
a.InvalidateCacheForChannelMembers(channel.Id)
|
||||
if err = a.Srv.Store.Channel().RemoveMember(channel.Id, user.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
channel, err := a.Srv.Store.Channel().GetByName(team.Id, model.DEFAULT_CHANNEL, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages {
|
||||
if requestorId == user.Id {
|
||||
if err = a.postLeaveTeamMessage(user, channel); err != nil {
|
||||
mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
|
||||
}
|
||||
} else {
|
||||
if err = a.postRemoveFromTeamMessage(user, channel); err != nil {
|
||||
mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = a.RemoveTeamMemberFromTeam(teamMember, requestorId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) postLeaveTeamMessage(user *model.User, channel *model.Channel) *model.AppError {
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
@@ -1011,6 +1048,77 @@ func (a *App) InviteNewUsersToTeam(emailList []string, teamId, senderId string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) InviteGuestsToChannels(teamId string, guestsInvite *model.GuestsInvite, senderId string) *model.AppError {
|
||||
if !*a.Config().ServiceSettings.EnableEmailInvitations {
|
||||
return model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if err := guestsInvite.IsValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tchan := make(chan store.StoreResult, 1)
|
||||
go func() {
|
||||
team, err := a.Srv.Store.Team().Get(teamId)
|
||||
tchan <- store.StoreResult{Data: team, Err: err}
|
||||
close(tchan)
|
||||
}()
|
||||
cchan := make(chan store.StoreResult, 1)
|
||||
go func() {
|
||||
channels, err := a.Srv.Store.Channel().GetChannelsByIds(guestsInvite.Channels)
|
||||
cchan <- store.StoreResult{Data: channels, Err: err}
|
||||
close(cchan)
|
||||
}()
|
||||
uchan := make(chan store.StoreResult, 1)
|
||||
go func() {
|
||||
user, err := a.Srv.Store.User().Get(senderId)
|
||||
uchan <- store.StoreResult{Data: user, Err: err}
|
||||
close(uchan)
|
||||
}()
|
||||
|
||||
result := <-cchan
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
channels := result.Data.([]*model.Channel)
|
||||
|
||||
result = <-uchan
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
user := result.Data.(*model.User)
|
||||
|
||||
result = <-tchan
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
team := result.Data.(*model.Team)
|
||||
|
||||
for _, channel := range channels {
|
||||
if channel.TeamId != teamId {
|
||||
return model.NewAppError("InviteGuestsToChannels", "api.team.invite_guests.channel_in_invalid_team.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
var invalidEmailList []string
|
||||
for _, email := range guestsInvite.Emails {
|
||||
if !a.isTeamEmailAddressAllowed(email, team.AllowedDomains) {
|
||||
invalidEmailList = append(invalidEmailList, email)
|
||||
}
|
||||
}
|
||||
|
||||
if len(invalidEmailList) > 0 {
|
||||
s := strings.Join(invalidEmailList, ", ")
|
||||
err := model.NewAppError("InviteGuestsToChannels", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest)
|
||||
return err
|
||||
}
|
||||
|
||||
nameFormat := *a.Config().TeamSettings.TeammateNameDisplay
|
||||
a.SendGuestInviteEmails(team, channels, user.GetDisplayName(nameFormat), user.Id, guestsInvite.Emails, a.GetSiteURL(), guestsInvite.Message)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) FindTeamByName(name string) bool {
|
||||
if _, err := a.Srv.Store.Team().GetByName(name); err != nil {
|
||||
return false
|
||||
@@ -1175,7 +1283,7 @@ func (a *App) GetTeamIdFromQuery(query url.Values) (string, *model.AppError) {
|
||||
return "", model.NewAppError("GetTeamIdFromQuery", "api.oauth.singup_with_oauth.invalid_link.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if model.GetMillis()-token.CreateAt >= TEAM_INVITATION_EXPIRY_TIME {
|
||||
if model.GetMillis()-token.CreateAt >= INVITATION_EXPIRY_TIME {
|
||||
a.DeleteToken(token)
|
||||
return "", model.NewAppError("GetTeamIdFromQuery", "api.oauth.singup_with_oauth.expired_link.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ func TestAddUserToTeamByToken(t *testing.T) {
|
||||
|
||||
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
||||
ruser, _ := th.App.CreateUser(&user)
|
||||
rguest := th.CreateGuest()
|
||||
|
||||
t.Run("invalid token", func(t *testing.T) {
|
||||
if _, err := th.App.AddUserToTeamByToken(ruser.Id, "123"); err == nil {
|
||||
@@ -217,7 +218,7 @@ func TestAddUserToTeamByToken(t *testing.T) {
|
||||
TOKEN_TYPE_TEAM_INVITATION,
|
||||
model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id}),
|
||||
)
|
||||
token.CreateAt = model.GetMillis() - TEAM_INVITATION_EXPIRY_TIME - 1
|
||||
token.CreateAt = model.GetMillis() - INVITATION_EXPIRY_TIME - 1
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
defer th.App.DeleteToken(token)
|
||||
if _, err := th.App.AddUserToTeamByToken(ruser.Id, token.Token); err == nil {
|
||||
@@ -261,6 +262,49 @@ func TestAddUserToTeamByToken(t *testing.T) {
|
||||
}
|
||||
_, err := th.App.Srv.Store.Token().GetByToken(token.Token)
|
||||
require.NotNil(t, err, "The token must be deleted after be used")
|
||||
|
||||
members, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, ruser.Id)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, *members, 2)
|
||||
})
|
||||
|
||||
t.Run("invalid add a guest using a regular invite", func(t *testing.T) {
|
||||
token := model.NewToken(
|
||||
TOKEN_TYPE_TEAM_INVITATION,
|
||||
model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id}),
|
||||
)
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
_, err := th.App.AddUserToTeamByToken(rguest.Id, token.Token)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("invalid add a regular user using a guest invite", func(t *testing.T) {
|
||||
token := model.NewToken(
|
||||
TOKEN_TYPE_GUEST_INVITATION,
|
||||
model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "channels": th.BasicChannel.Id}),
|
||||
)
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
_, err := th.App.AddUserToTeamByToken(ruser.Id, token.Token)
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("valid request from guest invite", func(t *testing.T) {
|
||||
token := model.NewToken(
|
||||
TOKEN_TYPE_GUEST_INVITATION,
|
||||
model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "channels": th.BasicChannel.Id}),
|
||||
)
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
if _, err := th.App.AddUserToTeamByToken(rguest.Id, token.Token); err != nil {
|
||||
t.Log(err)
|
||||
t.Fatal("Should add user to the team")
|
||||
}
|
||||
_, err := th.App.Srv.Store.Token().GetByToken(token.Token)
|
||||
require.NotNil(t, err, "The token must be deleted after be used")
|
||||
|
||||
members, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, rguest.Id)
|
||||
require.Nil(t, err)
|
||||
require.Len(t, *members, 1)
|
||||
assert.Equal(t, (*members)[0].ChannelId, th.BasicChannel.Id)
|
||||
})
|
||||
|
||||
t.Run("group-constrained team", func(t *testing.T) {
|
||||
|
||||
106
app/user.go
106
app/user.go
@@ -40,26 +40,22 @@ const (
|
||||
TOKEN_TYPE_PASSWORD_RECOVERY = "password_recovery"
|
||||
TOKEN_TYPE_VERIFY_EMAIL = "verify_email"
|
||||
TOKEN_TYPE_TEAM_INVITATION = "team_invitation"
|
||||
TOKEN_TYPE_GUEST_INVITATION = "guest_invitation"
|
||||
PASSWORD_RECOVER_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour
|
||||
TEAM_INVITATION_EXPIRY_TIME = 1000 * 60 * 60 * 48 // 48 hours
|
||||
INVITATION_EXPIRY_TIME = 1000 * 60 * 60 * 48 // 48 hours
|
||||
IMAGE_PROFILE_PIXEL_DIMENSION = 128
|
||||
)
|
||||
|
||||
func (a *App) CreateUserWithToken(user *model.User, tokenId string) (*model.User, *model.AppError) {
|
||||
func (a *App) CreateUserWithToken(user *model.User, token *model.Token) (*model.User, *model.AppError) {
|
||||
if err := a.IsUserSignUpAllowed(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token, err := a.Srv.Store.Token().GetByToken(tokenId)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("CreateUserWithToken", "api.user.create_user.signup_link_invalid.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if token.Type != TOKEN_TYPE_TEAM_INVITATION {
|
||||
if token.Type != TOKEN_TYPE_TEAM_INVITATION && token.Type != TOKEN_TYPE_GUEST_INVITATION {
|
||||
return nil, model.NewAppError("CreateUserWithToken", "api.user.create_user.signup_link_invalid.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if model.GetMillis()-token.CreateAt >= TEAM_INVITATION_EXPIRY_TIME {
|
||||
if model.GetMillis()-token.CreateAt >= INVITATION_EXPIRY_TIME {
|
||||
a.DeleteToken(token)
|
||||
return nil, model.NewAppError("CreateUserWithToken", "api.user.create_user.signup_link_expired.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
@@ -71,10 +67,20 @@ func (a *App) CreateUserWithToken(user *model.User, tokenId string) (*model.User
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channels, err := a.Srv.Store.Channel().GetChannelsByIds(strings.Split(tokenData["channels"], " "))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.Email = tokenData["email"]
|
||||
user.EmailVerified = true
|
||||
|
||||
ruser, err := a.CreateUser(user)
|
||||
var ruser *model.User
|
||||
if token.Type == TOKEN_TYPE_TEAM_INVITATION {
|
||||
ruser, err = a.CreateUser(user)
|
||||
} else {
|
||||
ruser, err = a.CreateGuest(user)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -85,6 +91,15 @@ func (a *App) CreateUserWithToken(user *model.User, tokenId string) (*model.User
|
||||
|
||||
a.AddDirectChannels(team.Id, ruser)
|
||||
|
||||
if token.Type == TOKEN_TYPE_GUEST_INVITATION {
|
||||
for _, channel := range channels {
|
||||
_, err := a.AddUserToChannel(ruser, channel)
|
||||
if err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.DeleteToken(token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -233,15 +248,19 @@ func (a *App) CreateGuest(user *model.User) (*model.User, *model.AppError) {
|
||||
}
|
||||
|
||||
func (a *App) createUserOrGuest(user *model.User, guest bool) (*model.User, *model.AppError) {
|
||||
if !user.IsLDAPUser() && !user.IsSAMLUser() && !CheckUserDomain(user, *a.Config().TeamSettings.RestrictCreationToDomains) {
|
||||
return nil, model.NewAppError("CreateUser", "api.user.create_user.accepted_domain.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
user.Roles = model.SYSTEM_USER_ROLE_ID
|
||||
if guest {
|
||||
user.Roles = model.SYSTEM_GUEST_ROLE_ID
|
||||
}
|
||||
|
||||
if !user.IsLDAPUser() && !user.IsSAMLUser() && !user.IsGuest() && !CheckUserDomain(user, *a.Config().TeamSettings.RestrictCreationToDomains) {
|
||||
return nil, model.NewAppError("CreateUser", "api.user.create_user.accepted_domain.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if !user.IsLDAPUser() && !user.IsSAMLUser() && user.IsGuest() && !CheckUserDomain(user, *a.Config().GuestAccountsSettings.RestrictCreationToDomains) {
|
||||
return nil, model.NewAppError("CreateUser", "api.user.create_user.accepted_domain.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Below is a special case where the first user in the entire
|
||||
// system is granted the system_admin role
|
||||
count, err := a.Srv.Store.User().Count(model.UserCountOptions{IncludeDeleted: true})
|
||||
@@ -1056,13 +1075,13 @@ func (a *App) sendUpdatedUserEvent(user model.User) {
|
||||
adminCopyOfUser := user.DeepCopy()
|
||||
a.SanitizeProfile(adminCopyOfUser, true)
|
||||
adminMessage := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
adminMessage.Add("user", *adminCopyOfUser)
|
||||
adminMessage.Add("user", &adminCopyOfUser)
|
||||
adminMessage.Broadcast.ContainsSensitiveData = true
|
||||
a.Publish(adminMessage)
|
||||
|
||||
a.SanitizeProfile(&user, false)
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", user)
|
||||
message.Add("user", &user)
|
||||
message.Broadcast.ContainsSanitizedData = true
|
||||
a.Publish(message)
|
||||
}
|
||||
@@ -2200,6 +2219,61 @@ func (a *App) getListOfAllowedChannelsForTeam(teamId string, viewRestrictions *m
|
||||
return listOfAllowedChannels, nil
|
||||
}
|
||||
|
||||
// PromoteGuestToUser Convert user's roles and all his mermbership's roles from
|
||||
// guest roles to regular user roles.
|
||||
func (a *App) PromoteGuestToUser(user *model.User, requestorId string) *model.AppError {
|
||||
err := a.Srv.Store.User().PromoteGuestToUser(user.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userTeams, err := a.Srv.Store.Team().GetTeamsByUserId(user.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, team := range userTeams {
|
||||
// Soft error if there is an issue joining the default channels
|
||||
if err = a.JoinDefaultChannels(team.Id, user, false, requestorId); err != nil {
|
||||
mlog.Error(fmt.Sprintf("Encountered an issue joining default channels err=%v", err), mlog.String("user_id", user.Id), mlog.String("team_id", team.Id), mlog.String("requestor_id", requestorId))
|
||||
}
|
||||
}
|
||||
|
||||
promotedUser, err := a.GetUser(user.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", promotedUser)
|
||||
a.Publish(message)
|
||||
|
||||
a.UpdateSessionsIsGuest(promotedUser.Id, promotedUser.IsGuest())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DemoteUserToGuest Convert user's roles and all his mermbership's roles from
|
||||
// regular user roles to guest roles.
|
||||
func (a *App) DemoteUserToGuest(user *model.User) *model.AppError {
|
||||
err := a.Srv.Store.User().DemoteUserToGuest(user.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
demotedUser, err := a.GetUser(user.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", demotedUser)
|
||||
a.Publish(message)
|
||||
|
||||
a.UpdateSessionsIsGuest(demotedUser.Id, demotedUser.IsGuest())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// invalidateUserCacheAndPublish Invalidates cache for a user and publishes user updated event
|
||||
func (a *App) invalidateUserCacheAndPublish(userId string) {
|
||||
a.InvalidateCacheForUser(userId)
|
||||
|
||||
273
app/user_test.go
273
app/user_test.go
@@ -561,7 +561,7 @@ func TestCreateUserWithToken(t *testing.T) {
|
||||
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
||||
|
||||
t.Run("invalid token", func(t *testing.T) {
|
||||
if _, err := th.App.CreateUserWithToken(&user, "123"); err == nil {
|
||||
if _, err := th.App.CreateUserWithToken(&user, &model.Token{Token: "123"}); err == nil {
|
||||
t.Fatal("Should fail on unexisting token")
|
||||
}
|
||||
})
|
||||
@@ -573,7 +573,7 @@ func TestCreateUserWithToken(t *testing.T) {
|
||||
)
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
defer th.App.DeleteToken(token)
|
||||
if _, err := th.App.CreateUserWithToken(&user, token.Token); err == nil {
|
||||
if _, err := th.App.CreateUserWithToken(&user, token); err == nil {
|
||||
t.Fatal("Should fail on bad token type")
|
||||
}
|
||||
})
|
||||
@@ -583,10 +583,10 @@ func TestCreateUserWithToken(t *testing.T) {
|
||||
TOKEN_TYPE_TEAM_INVITATION,
|
||||
model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "email": user.Email}),
|
||||
)
|
||||
token.CreateAt = model.GetMillis() - TEAM_INVITATION_EXPIRY_TIME - 1
|
||||
token.CreateAt = model.GetMillis() - INVITATION_EXPIRY_TIME - 1
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
defer th.App.DeleteToken(token)
|
||||
if _, err := th.App.CreateUserWithToken(&user, token.Token); err == nil {
|
||||
if _, err := th.App.CreateUserWithToken(&user, token); err == nil {
|
||||
t.Fatal("Should fail on expired token")
|
||||
}
|
||||
})
|
||||
@@ -598,28 +598,60 @@ func TestCreateUserWithToken(t *testing.T) {
|
||||
)
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
defer th.App.DeleteToken(token)
|
||||
if _, err := th.App.CreateUserWithToken(&user, token.Token); err == nil {
|
||||
if _, err := th.App.CreateUserWithToken(&user, token); err == nil {
|
||||
t.Fatal("Should fail on bad team id")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid request", func(t *testing.T) {
|
||||
t.Run("valid regular user request", func(t *testing.T) {
|
||||
invitationEmail := model.NewId() + "other-email@test.com"
|
||||
token := model.NewToken(
|
||||
TOKEN_TYPE_TEAM_INVITATION,
|
||||
model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "email": invitationEmail}),
|
||||
)
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
newUser, err := th.App.CreateUserWithToken(&user, token.Token)
|
||||
newUser, err := th.App.CreateUserWithToken(&user, token)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fatal("Should add user to the team")
|
||||
}
|
||||
assert.False(t, newUser.IsGuest())
|
||||
if newUser.Email != invitationEmail {
|
||||
t.Fatal("The user email must be the invitation one")
|
||||
}
|
||||
|
||||
_, err = th.App.Srv.Store.Token().GetByToken(token.Token)
|
||||
require.NotNil(t, err, "The token must be deleted after be used")
|
||||
|
||||
members, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, newUser.Id)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, *members, 2)
|
||||
})
|
||||
|
||||
t.Run("valid guest request", func(t *testing.T) {
|
||||
invitationEmail := model.NewId() + "other-email@test.com"
|
||||
token := model.NewToken(
|
||||
TOKEN_TYPE_GUEST_INVITATION,
|
||||
model.MapToJson(map[string]string{"teamId": th.BasicTeam.Id, "email": invitationEmail, "channels": th.BasicChannel.Id}),
|
||||
)
|
||||
require.Nil(t, th.App.Srv.Store.Token().Save(token))
|
||||
guest := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
||||
newGuest, err := th.App.CreateUserWithToken(&guest, token)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.Fatal("Should add user to the team")
|
||||
}
|
||||
assert.True(t, newGuest.IsGuest())
|
||||
if newGuest.Email != invitationEmail {
|
||||
t.Fatal("The user email must be the invitation one")
|
||||
}
|
||||
_, err = th.App.Srv.Store.Token().GetByToken(token.Token)
|
||||
require.NotNil(t, err, "The token must be deleted after be used")
|
||||
|
||||
members, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, newGuest.Id)
|
||||
require.Nil(t, err)
|
||||
require.Len(t, *members, 1)
|
||||
assert.Equal(t, (*members)[0].ChannelId, th.BasicChannel.Id)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -870,10 +902,6 @@ func TestGetViewUsersRestrictionsForTeam(t *testing.T) {
|
||||
|
||||
team2channel1 := th.CreateChannel(team2)
|
||||
th.CreateChannel(team2) // Another channel
|
||||
team2offtopic, err := th.App.GetChannelByName("off-topic", team2.Id, false)
|
||||
require.Nil(t, err)
|
||||
team2townsquare, err := th.App.GetChannelByName("town-square", team2.Id, false)
|
||||
require.Nil(t, err)
|
||||
|
||||
th.App.AddUserToChannel(user1, team1channel1)
|
||||
th.App.AddUserToChannel(user1, team1channel2)
|
||||
@@ -929,6 +957,227 @@ func TestGetViewUsersRestrictionsForTeam(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.NotNil(t, restrictions)
|
||||
assert.ElementsMatch(t, []string{team1townsquare.Id, team1offtopic.Id, team1channel1.Id, team1channel2.Id, team2townsquare.Id, team2offtopic.Id, team2channel1.Id}, restrictions)
|
||||
assert.ElementsMatch(t, []string{team1townsquare.Id, team1offtopic.Id, team1channel1.Id, team1channel2.Id}, restrictions)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPromoteGuestToUser(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
t.Run("Must fail with regular user", func(t *testing.T) {
|
||||
require.Equal(t, "system_user", th.BasicUser.Roles)
|
||||
err := th.App.PromoteGuestToUser(th.BasicUser, th.BasicUser.Id)
|
||||
require.Nil(t, err)
|
||||
|
||||
user, err := th.App.GetUser(th.BasicUser.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_user", user.Roles)
|
||||
})
|
||||
|
||||
t.Run("Must work with guest user without teams or channels", func(t *testing.T) {
|
||||
guest := th.CreateGuest()
|
||||
require.Equal(t, "system_guest", guest.Roles)
|
||||
|
||||
err := th.App.PromoteGuestToUser(guest, th.BasicUser.Id)
|
||||
require.Nil(t, err)
|
||||
guest, err = th.App.GetUser(guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_user", guest.Roles)
|
||||
})
|
||||
|
||||
t.Run("Must work with guest user with teams but no channels", func(t *testing.T) {
|
||||
guest := th.CreateGuest()
|
||||
require.Equal(t, "system_guest", guest.Roles)
|
||||
th.LinkUserToTeam(guest, th.BasicTeam)
|
||||
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, teamMember.SchemeGuest)
|
||||
require.False(t, teamMember.SchemeUser)
|
||||
|
||||
err = th.App.PromoteGuestToUser(guest, th.BasicUser.Id)
|
||||
require.Nil(t, err)
|
||||
guest, err = th.App.GetUser(guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_user", guest.Roles)
|
||||
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeGuest)
|
||||
assert.True(t, teamMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must work with guest user with teams and channels", func(t *testing.T) {
|
||||
guest := th.CreateGuest()
|
||||
require.Equal(t, "system_guest", guest.Roles)
|
||||
th.LinkUserToTeam(guest, th.BasicTeam)
|
||||
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, teamMember.SchemeGuest)
|
||||
require.False(t, teamMember.SchemeUser)
|
||||
|
||||
channelMember := th.AddUserToChannel(guest, th.BasicChannel)
|
||||
require.True(t, channelMember.SchemeGuest)
|
||||
require.False(t, channelMember.SchemeUser)
|
||||
|
||||
err = th.App.PromoteGuestToUser(guest, th.BasicUser.Id)
|
||||
require.Nil(t, err)
|
||||
guest, err = th.App.GetUser(guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_user", guest.Roles)
|
||||
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeGuest)
|
||||
assert.True(t, teamMember.SchemeUser)
|
||||
channelMember, err = th.App.GetChannelMember(th.BasicChannel.Id, guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeGuest)
|
||||
assert.True(t, teamMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must add the default channels", func(t *testing.T) {
|
||||
guest := th.CreateGuest()
|
||||
require.Equal(t, "system_guest", guest.Roles)
|
||||
th.LinkUserToTeam(guest, th.BasicTeam)
|
||||
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, teamMember.SchemeGuest)
|
||||
require.False(t, teamMember.SchemeUser)
|
||||
|
||||
channelMember := th.AddUserToChannel(guest, th.BasicChannel)
|
||||
require.True(t, channelMember.SchemeGuest)
|
||||
require.False(t, channelMember.SchemeUser)
|
||||
|
||||
channelMembers, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, guest.Id)
|
||||
require.Nil(t, err)
|
||||
require.Len(t, *channelMembers, 1)
|
||||
|
||||
err = th.App.PromoteGuestToUser(guest, th.BasicUser.Id)
|
||||
require.Nil(t, err)
|
||||
guest, err = th.App.GetUser(guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_user", guest.Roles)
|
||||
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeGuest)
|
||||
assert.True(t, teamMember.SchemeUser)
|
||||
channelMember, err = th.App.GetChannelMember(th.BasicChannel.Id, guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeGuest)
|
||||
assert.True(t, teamMember.SchemeUser)
|
||||
|
||||
channelMembers, err = th.App.GetChannelMembersForUser(th.BasicTeam.Id, guest.Id)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, *channelMembers, 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDemoteUserToGuest(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
t.Run("Must fail with guest user", func(t *testing.T) {
|
||||
guest := th.CreateGuest()
|
||||
require.Equal(t, "system_guest", guest.Roles)
|
||||
err := th.App.DemoteUserToGuest(guest)
|
||||
require.Nil(t, err)
|
||||
|
||||
user, err := th.App.GetUser(guest.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_guest", user.Roles)
|
||||
})
|
||||
|
||||
t.Run("Must work with user without teams or channels", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
require.Equal(t, "system_user", user.Roles)
|
||||
|
||||
err := th.App.DemoteUserToGuest(user)
|
||||
require.Nil(t, err)
|
||||
user, err = th.App.GetUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_guest", user.Roles)
|
||||
})
|
||||
|
||||
t.Run("Must work with user with teams but no channels", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
require.Equal(t, "system_user", user.Roles)
|
||||
th.LinkUserToTeam(user, th.BasicTeam)
|
||||
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, teamMember.SchemeUser)
|
||||
require.False(t, teamMember.SchemeGuest)
|
||||
|
||||
err = th.App.DemoteUserToGuest(user)
|
||||
require.Nil(t, err)
|
||||
user, err = th.App.GetUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_guest", user.Roles)
|
||||
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeUser)
|
||||
assert.True(t, teamMember.SchemeGuest)
|
||||
})
|
||||
|
||||
t.Run("Must work with user user with teams and channels", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
require.Equal(t, "system_user", user.Roles)
|
||||
th.LinkUserToTeam(user, th.BasicTeam)
|
||||
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, teamMember.SchemeUser)
|
||||
require.False(t, teamMember.SchemeGuest)
|
||||
|
||||
channelMember := th.AddUserToChannel(user, th.BasicChannel)
|
||||
require.True(t, channelMember.SchemeUser)
|
||||
require.False(t, channelMember.SchemeGuest)
|
||||
|
||||
err = th.App.DemoteUserToGuest(user)
|
||||
require.Nil(t, err)
|
||||
user, err = th.App.GetUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_guest", user.Roles)
|
||||
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeUser)
|
||||
assert.True(t, teamMember.SchemeGuest)
|
||||
channelMember, err = th.App.GetChannelMember(th.BasicChannel.Id, user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeUser)
|
||||
assert.True(t, teamMember.SchemeGuest)
|
||||
})
|
||||
|
||||
t.Run("Must respect the current channels not removing defaults", func(t *testing.T) {
|
||||
user := th.CreateUser()
|
||||
require.Equal(t, "system_user", user.Roles)
|
||||
th.LinkUserToTeam(user, th.BasicTeam)
|
||||
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, teamMember.SchemeUser)
|
||||
require.False(t, teamMember.SchemeGuest)
|
||||
|
||||
channelMember := th.AddUserToChannel(user, th.BasicChannel)
|
||||
require.True(t, channelMember.SchemeUser)
|
||||
require.False(t, channelMember.SchemeGuest)
|
||||
|
||||
channelMembers, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.Len(t, *channelMembers, 3)
|
||||
|
||||
err = th.App.DemoteUserToGuest(user)
|
||||
require.Nil(t, err)
|
||||
user, err = th.App.GetUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "system_guest", user.Roles)
|
||||
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeUser)
|
||||
assert.True(t, teamMember.SchemeGuest)
|
||||
channelMember, err = th.App.GetChannelMember(th.BasicChannel.Id, user.Id)
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, teamMember.SchemeUser)
|
||||
assert.True(t, teamMember.SchemeGuest)
|
||||
|
||||
channelMembers, err = th.App.GetChannelMembersForUser(th.BasicTeam.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, *channelMembers, 3)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -356,6 +356,15 @@ func (webCon *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool {
|
||||
return webCon.IsMemberOfTeam(msg.Broadcast.TeamId)
|
||||
}
|
||||
|
||||
if msg.Event == model.WEBSOCKET_EVENT_USER_UPDATED && webCon.GetSession().Props[model.SESSION_PROP_IS_GUEST] == "true" {
|
||||
canSee, err := webCon.App.UserCanSeeOtherUser(webCon.UserId, msg.Data["user"].(*model.User).Id)
|
||||
if err != nil {
|
||||
mlog.Error("webhub.shouldSendEvent: " + err.Error())
|
||||
return false
|
||||
}
|
||||
return canSee
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -246,6 +246,7 @@ func (a *App) InvalidateCacheForChannelMembers(channelId string) {
|
||||
func (a *App) InvalidateCacheForChannelMembersSkipClusterSend(channelId string) {
|
||||
a.Srv.Store.User().InvalidateProfilesInChannelCache(channelId)
|
||||
a.Srv.Store.Channel().InvalidateMemberCount(channelId)
|
||||
a.Srv.Store.Channel().InvalidateGuestCount(channelId)
|
||||
}
|
||||
|
||||
func (a *App) InvalidateCacheForChannelMembersNotifyProps(channelId string) {
|
||||
|
||||
@@ -273,6 +273,7 @@ func GenerateLimitedClientConfig(c *model.Config, diagnosticId string, license *
|
||||
props["CustomDescriptionText"] = *c.TeamSettings.CustomDescriptionText
|
||||
props["EnableMultifactorAuthentication"] = strconv.FormatBool(*c.ServiceSettings.EnableMultifactorAuthentication)
|
||||
props["EnforceMultifactorAuthentication"] = "false"
|
||||
props["EnableGuestAccounts"] = strconv.FormatBool(*c.GuestAccountsSettings.Enable)
|
||||
|
||||
if license != nil {
|
||||
if *license.Features.LDAP {
|
||||
|
||||
124
i18n/en.json
124
i18n/en.json
@@ -1862,6 +1862,14 @@
|
||||
"id": "api.team.add_user_to_team.missing_parameter.app_error",
|
||||
"translation": "Parameter required to add user to team."
|
||||
},
|
||||
{
|
||||
"id": "api.team.demote_user_to_guest.disabled.error",
|
||||
"translation": "Guest accounts are disabled."
|
||||
},
|
||||
{
|
||||
"id": "api.team.demote_user_to_guest.license.error",
|
||||
"translation": "Your license does not support guest accounts"
|
||||
},
|
||||
{
|
||||
"id": "api.team.get_invite_info.not_open_team",
|
||||
"translation": "Invite is invalid because this is not an open team."
|
||||
@@ -1906,6 +1914,18 @@
|
||||
"id": "api.team.invalidate_all_email_invites.app_error",
|
||||
"translation": "Error invalidating email invites."
|
||||
},
|
||||
{
|
||||
"id": "api.team.invate_guests_to_channels.disabled.error",
|
||||
"translation": "Guest accounts are disabled"
|
||||
},
|
||||
{
|
||||
"id": "api.team.invate_guests_to_channels.license.error",
|
||||
"translation": "Your license does not support guest accounts"
|
||||
},
|
||||
{
|
||||
"id": "api.team.invite_guests.channel_in_invalid_team.app_error",
|
||||
"translation": "The channels of the invite must be part of the team of the invite."
|
||||
},
|
||||
{
|
||||
"id": "api.team.invite_members.disabled.app_error",
|
||||
"translation": "Email invitations are disabled."
|
||||
@@ -1950,6 +1970,14 @@
|
||||
"id": "api.team.move_channel.success",
|
||||
"translation": "This channel has been moved to this team from %v."
|
||||
},
|
||||
{
|
||||
"id": "api.team.promote_guest_to_user.disabled.error",
|
||||
"translation": "Guest accounts are disabled."
|
||||
},
|
||||
{
|
||||
"id": "api.team.promote_guest_to_user.license.error",
|
||||
"translation": "Your license does not support guest accounts"
|
||||
},
|
||||
{
|
||||
"id": "api.team.remove_member.group_constrained.app_error",
|
||||
"translation": "Unable to remove a user from a group-constrained team."
|
||||
@@ -2118,6 +2146,14 @@
|
||||
"id": "api.templates.invite_body.title",
|
||||
"translation": "You've been invited"
|
||||
},
|
||||
{
|
||||
"id": "api.templates.invite_body_guest.info",
|
||||
"translation": "The team {{.SenderStatus}} [[{{.SenderName}}]], has invited you to join team [[{{.TeamDisplayName}}]] as a guest."
|
||||
},
|
||||
{
|
||||
"id": "api.templates.invite_guest_subject",
|
||||
"translation": "[{{ .SiteName }}] {{ .SenderName }} invited you to join the team {{ .TeamDisplayName }} as a guest"
|
||||
},
|
||||
{
|
||||
"id": "api.templates.invite_subject",
|
||||
"translation": "[{{ .SiteName }}] {{ .SenderName }} invited you to join {{ .TeamDisplayName }} Team"
|
||||
@@ -2366,6 +2402,18 @@
|
||||
"id": "api.user.create_user.disabled.app_error",
|
||||
"translation": "User creation is disabled."
|
||||
},
|
||||
{
|
||||
"id": "api.user.create_user.guest_accounts.disabled.app_error",
|
||||
"translation": "Guest accounts are disabled"
|
||||
},
|
||||
{
|
||||
"id": "api.user.create_user.guest_accounts.license.app_error",
|
||||
"translation": "Your license does not support guest accounts"
|
||||
},
|
||||
{
|
||||
"id": "api.user.create_user.invalid_invitation_type.app_error",
|
||||
"translation": "Unable to create the user, invalid invitation."
|
||||
},
|
||||
{
|
||||
"id": "api.user.create_user.no_open_server",
|
||||
"translation": "This server does not allow open signups. Please speak with your Administrator to receive an invitation."
|
||||
@@ -2382,6 +2430,10 @@
|
||||
"id": "api.user.create_user.signup_link_invalid.app_error",
|
||||
"translation": "The signup link does not appear to be valid"
|
||||
},
|
||||
{
|
||||
"id": "api.user.demote_user_to_guest.already_guest.app_error",
|
||||
"translation": "Unable to convert the user to guest because is already a guest"
|
||||
},
|
||||
{
|
||||
"id": "api.user.email_to_ldap.not_available.app_error",
|
||||
"translation": "AD/LDAP not available on this server"
|
||||
@@ -2422,6 +2474,14 @@
|
||||
"id": "api.user.login.client_side_cert.license.app_error",
|
||||
"translation": "Attempt to use the experimental feature ClientSideCertEnable without a valid enterprise license"
|
||||
},
|
||||
{
|
||||
"id": "api.user.login.guest_accounts.disabled.error",
|
||||
"translation": "Guest accounts are disabled"
|
||||
},
|
||||
{
|
||||
"id": "api.user.login.guest_accounts.license.error",
|
||||
"translation": "Your license does not support guest accounts"
|
||||
},
|
||||
{
|
||||
"id": "api.user.login.inactive.app_error",
|
||||
"translation": "Login failed because your account has been deactivated. Please contact an administrator."
|
||||
@@ -2474,6 +2534,10 @@
|
||||
"id": "api.user.oauth_to_email.not_available.app_error",
|
||||
"translation": "Authentication Transfer not configured or available on this server."
|
||||
},
|
||||
{
|
||||
"id": "api.user.promote_guest_to_user.no_guest.app_error",
|
||||
"translation": "Unable to convert the guest to regular user because is not a guest"
|
||||
},
|
||||
{
|
||||
"id": "api.user.reset_password.broken_token.app_error",
|
||||
"translation": "The reset password token does not appear to be valid."
|
||||
@@ -4802,6 +4866,22 @@
|
||||
"id": "model.group_syncable.type.app_error",
|
||||
"translation": "invalid type property for group syncable"
|
||||
},
|
||||
{
|
||||
"id": "model.guest.is_valid.channel.app_error",
|
||||
"translation": "Invalid channel"
|
||||
},
|
||||
{
|
||||
"id": "model.guest.is_valid.channels.app_error",
|
||||
"translation": "Invalid channels"
|
||||
},
|
||||
{
|
||||
"id": "model.guest.is_valid.email.app_error",
|
||||
"translation": "Invalid email"
|
||||
},
|
||||
{
|
||||
"id": "model.guest.is_valid.emails.app_error",
|
||||
"translation": "Invalid emails"
|
||||
},
|
||||
{
|
||||
"id": "model.incoming_hook.channel_id.app_error",
|
||||
"translation": "Invalid channel id"
|
||||
@@ -6514,6 +6594,10 @@
|
||||
"id": "store.sql_session.update_last_activity.app_error",
|
||||
"translation": "Unable to update the last_activity_at"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_session.update_props.app_error",
|
||||
"translation": "Unable to update session props"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_session.update_roles.app_error",
|
||||
"translation": "Unable to update the roles"
|
||||
@@ -6810,6 +6894,26 @@
|
||||
"id": "store.sql_user.count.app_error",
|
||||
"translation": "UserCountOptions don't make sense"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.demote_user_to_guest.channel_members_update.app_error",
|
||||
"translation": "Failed to update the user channels memberships"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.demote_user_to_guest.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.demote_user_to_guest.open_transaction.app_error",
|
||||
"translation": "Failed to begin the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.demote_user_to_guest.team_members_update.app_error",
|
||||
"translation": "Failed to update the user teams memberships"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.demote_user_to_guest.user_update.app_error",
|
||||
"translation": "Failed to update the user"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.get.app_error",
|
||||
"translation": "We encountered an error finding the account"
|
||||
@@ -6890,6 +6994,26 @@
|
||||
"id": "store.sql_user.permanent_delete.app_error",
|
||||
"translation": "Unable to delete the existing account"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.promote_guest.channel_members_update.app_error",
|
||||
"translation": "Failed to update the user channels memberships"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.promote_guest.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.promote_guest.open_transaction.app_error",
|
||||
"translation": "Failed to begin the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.promote_guest.team_members_update.app_error",
|
||||
"translation": "Failed to update the user teams memberships"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.promote_guest.user_update.app_error",
|
||||
"translation": "Failed to update the user"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.save.app_error",
|
||||
"translation": "Unable to save the account."
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
type ChannelStats struct {
|
||||
ChannelId string `json:"channel_id"`
|
||||
MemberCount int64 `json:"member_count"`
|
||||
GuestCount int64 `json:"guest_count"`
|
||||
}
|
||||
|
||||
func (o *ChannelStats) ToJson() string {
|
||||
|
||||
@@ -1078,6 +1078,26 @@ func (c *Client4) UpdateUserPassword(userId, currentPassword, newPassword string
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
|
||||
// PromoteGuestToUser convert a guest into a regular user
|
||||
func (c *Client4) PromoteGuestToUser(guestId string) (bool, *Response) {
|
||||
r, err := c.DoApiPost(c.GetUserRoute(guestId)+"/promote", "")
|
||||
if err != nil {
|
||||
return false, BuildErrorResponse(r, err)
|
||||
}
|
||||
defer closeBody(r)
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
|
||||
// DemoteUserToGuest convert a regular user into a guest
|
||||
func (c *Client4) DemoteUserToGuest(guestId string) (bool, *Response) {
|
||||
r, err := c.DoApiPost(c.GetUserRoute(guestId)+"/demote", "")
|
||||
if err != nil {
|
||||
return false, BuildErrorResponse(r, err)
|
||||
}
|
||||
defer closeBody(r)
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
|
||||
// UpdateUserRoles updates a user's roles in the system. A user can have "system_user" and "system_admin" roles.
|
||||
func (c *Client4) UpdateUserRoles(userId, roles string) (bool, *Response) {
|
||||
requestBody := map[string]string{"roles": roles}
|
||||
@@ -1904,6 +1924,21 @@ func (c *Client4) InviteUsersToTeam(teamId string, userEmails []string) (bool, *
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
|
||||
// InviteGuestsToTeam invite guest by email to some channels in a team.
|
||||
func (c *Client4) InviteGuestsToTeam(teamId string, userEmails []string, channels []string, message string) (bool, *Response) {
|
||||
guestsInvite := GuestsInvite{
|
||||
Emails: userEmails,
|
||||
Channels: channels,
|
||||
Message: message,
|
||||
}
|
||||
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite-guests/email", guestsInvite.ToJson())
|
||||
if err != nil {
|
||||
return false, BuildErrorResponse(r, err)
|
||||
}
|
||||
defer closeBody(r)
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
|
||||
// InvalidateEmailInvites will invalidate active email invitations that have not been accepted by the user.
|
||||
func (c *Client4) InvalidateEmailInvites() (bool, *Response) {
|
||||
r, err := c.DoApiDelete(c.GetTeamsRoute() + "/invites/email")
|
||||
|
||||
@@ -2312,6 +2312,31 @@ func (s *DisplaySettings) SetDefaults() {
|
||||
}
|
||||
}
|
||||
|
||||
type GuestAccountsSettings struct {
|
||||
Enable *bool
|
||||
AllowEmailAccounts *bool
|
||||
EnforceMultifactorAuthentication *bool
|
||||
RestrictCreationToDomains *string
|
||||
}
|
||||
|
||||
func (s *GuestAccountsSettings) SetDefaults() {
|
||||
if s.Enable == nil {
|
||||
s.Enable = NewBool(false)
|
||||
}
|
||||
|
||||
if s.AllowEmailAccounts == nil {
|
||||
s.AllowEmailAccounts = NewBool(true)
|
||||
}
|
||||
|
||||
if s.EnforceMultifactorAuthentication == nil {
|
||||
s.EnforceMultifactorAuthentication = NewBool(false)
|
||||
}
|
||||
|
||||
if s.RestrictCreationToDomains == nil {
|
||||
s.RestrictCreationToDomains = NewString("")
|
||||
}
|
||||
}
|
||||
|
||||
type ImageProxySettings struct {
|
||||
Enable *bool
|
||||
ImageProxyType *string
|
||||
@@ -2388,6 +2413,7 @@ type Config struct {
|
||||
JobSettings JobSettings
|
||||
PluginSettings PluginSettings
|
||||
DisplaySettings DisplaySettings
|
||||
GuestAccountsSettings GuestAccountsSettings
|
||||
ImageProxySettings ImageProxySettings
|
||||
}
|
||||
|
||||
@@ -2471,6 +2497,7 @@ func (o *Config) SetDefaults() {
|
||||
o.JobSettings.SetDefaults()
|
||||
o.MessageExportSettings.SetDefaults()
|
||||
o.DisplaySettings.SetDefaults()
|
||||
o.GuestAccountsSettings.SetDefaults()
|
||||
o.ImageProxySettings.SetDefaults(o.ServiceSettings)
|
||||
}
|
||||
|
||||
|
||||
53
model/guest_invite.go
Обычный файл
53
model/guest_invite.go
Обычный файл
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type GuestsInvite struct {
|
||||
Emails []string `json:"emails"`
|
||||
Channels []string `json:"channels"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// IsValid validates the user and returns an error if it isn't configured
|
||||
// correctly.
|
||||
func (i *GuestsInvite) IsValid() *AppError {
|
||||
if len(i.Emails) == 0 {
|
||||
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.emails.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
for _, email := range i.Emails {
|
||||
if len(email) > USER_EMAIL_MAX_LENGTH || len(email) == 0 || !IsValidEmail(email) {
|
||||
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.email.app_error", nil, "email="+email, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
if len(i.Channels) == 0 {
|
||||
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.channels.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
for _, channel := range i.Channels {
|
||||
if len(channel) != 26 {
|
||||
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.channel.app_error", nil, "channel="+channel, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GuestsInviteFromJson will decode the input and return a GuestsInvite
|
||||
func GuestsInviteFromJson(data io.Reader) *GuestsInvite {
|
||||
var invite *GuestsInvite
|
||||
json.NewDecoder(data).Decode(&invite)
|
||||
return invite
|
||||
}
|
||||
|
||||
func (invite *GuestsInvite) ToJson() string {
|
||||
b, _ := json.Marshal(invite)
|
||||
return string(b)
|
||||
}
|
||||
@@ -59,6 +59,7 @@ type Features struct {
|
||||
MessageExport *bool `json:"message_export"`
|
||||
CustomPermissionsSchemes *bool `json:"custom_permissions_schemes"`
|
||||
CustomTermsOfService *bool `json:"custom_terms_of_service"`
|
||||
GuestAccountsPermissions *bool `json:"guest_accounts_permissions"`
|
||||
|
||||
// after we enabled more features we'll need to control them with this
|
||||
FutureFeatures *bool `json:"future_features"`
|
||||
@@ -81,6 +82,7 @@ func (f *Features) ToMap() map[string]interface{} {
|
||||
"data_retention": *f.DataRetention,
|
||||
"message_export": *f.MessageExport,
|
||||
"custom_permissions_schemes": *f.CustomPermissionsSchemes,
|
||||
"guest_accounts_permissions": *f.GuestAccountsPermissions,
|
||||
"future": *f.FutureFeatures,
|
||||
}
|
||||
}
|
||||
@@ -162,6 +164,10 @@ func (f *Features) SetDefaults() {
|
||||
f.CustomPermissionsSchemes = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
|
||||
if f.GuestAccountsPermissions == nil {
|
||||
f.GuestAccountsPermissions = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
|
||||
if f.CustomTermsOfService == nil {
|
||||
f.CustomTermsOfService = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
CheckTrue(t, *f.DataRetention)
|
||||
CheckTrue(t, *f.MessageExport)
|
||||
CheckTrue(t, *f.CustomPermissionsSchemes)
|
||||
CheckTrue(t, *f.GuestAccountsPermissions)
|
||||
CheckTrue(t, *f.FutureFeatures)
|
||||
|
||||
f = Features{}
|
||||
@@ -73,6 +74,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
*f.DataRetention = true
|
||||
*f.MessageExport = true
|
||||
*f.CustomPermissionsSchemes = true
|
||||
*f.GuestAccountsPermissions = true
|
||||
*f.EmailNotificationContents = true
|
||||
|
||||
f.SetDefaults()
|
||||
@@ -93,6 +95,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
CheckTrue(t, *f.DataRetention)
|
||||
CheckTrue(t, *f.MessageExport)
|
||||
CheckTrue(t, *f.CustomPermissionsSchemes)
|
||||
CheckTrue(t, *f.GuestAccountsPermissions)
|
||||
CheckFalse(t, *f.FutureFeatures)
|
||||
}
|
||||
|
||||
@@ -176,6 +179,7 @@ func TestLicenseToFromJson(t *testing.T) {
|
||||
CheckBool(t, *f1.DataRetention, *f.DataRetention)
|
||||
CheckBool(t, *f1.MessageExport, *f.MessageExport)
|
||||
CheckBool(t, *f1.CustomPermissionsSchemes, *f.CustomPermissionsSchemes)
|
||||
CheckBool(t, *f1.GuestAccountsPermissions, *f.GuestAccountsPermissions)
|
||||
CheckBool(t, *f1.FutureFeatures, *f.FutureFeatures)
|
||||
|
||||
invalid := `{"asdf`
|
||||
|
||||
@@ -87,6 +87,9 @@ var PERMISSION_READ_OTHERS_BOTS *Permission
|
||||
var PERMISSION_MANAGE_BOTS *Permission
|
||||
var PERMISSION_MANAGE_OTHERS_BOTS *Permission
|
||||
var PERMISSION_VIEW_MEMBERS *Permission
|
||||
var PERMISSION_INVITE_GUEST *Permission
|
||||
var PERMISSION_PROMOTE_GUEST *Permission
|
||||
var PERMISSION_DEMOTE_TO_GUEST *Permission
|
||||
|
||||
// General permission that encompasses all system admin functions
|
||||
// in the future this could be broken up to allow access to some
|
||||
@@ -533,6 +536,25 @@ func initializePermissions() {
|
||||
"authentication.permisssions.view_members.description",
|
||||
PERMISSION_SCOPE_TEAM,
|
||||
}
|
||||
PERMISSION_INVITE_GUEST = &Permission{
|
||||
"invite_guest",
|
||||
"authentication.permissions.invite_guest.name",
|
||||
"authentication.permissions.invite_guest.description",
|
||||
PERMISSION_SCOPE_TEAM,
|
||||
}
|
||||
PERMISSION_PROMOTE_GUEST = &Permission{
|
||||
"promote_guest",
|
||||
"authentication.permissions.promote_guest.name",
|
||||
"authentication.permissions.promote_guest.description",
|
||||
PERMISSION_SCOPE_SYSTEM,
|
||||
}
|
||||
|
||||
PERMISSION_DEMOTE_TO_GUEST = &Permission{
|
||||
"demote_to_guest",
|
||||
"authentication.permissions.demote_to_guest.name",
|
||||
"authentication.permissions.demote_to_guest.description",
|
||||
PERMISSION_SCOPE_SYSTEM,
|
||||
}
|
||||
|
||||
ALL_PERMISSIONS = []*Permission{
|
||||
PERMISSION_INVITE_USER,
|
||||
@@ -606,6 +628,9 @@ func initializePermissions() {
|
||||
PERMISSION_MANAGE_OTHERS_BOTS,
|
||||
PERMISSION_MANAGE_SYSTEM,
|
||||
PERMISSION_VIEW_MEMBERS,
|
||||
PERMISSION_INVITE_GUEST,
|
||||
PERMISSION_PROMOTE_GUEST,
|
||||
PERMISSION_DEMOTE_TO_GUEST,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -382,6 +382,9 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
PERMISSION_EDIT_OTHERS_POSTS.Id,
|
||||
PERMISSION_MANAGE_OAUTH.Id,
|
||||
PERMISSION_INVITE_USER.Id,
|
||||
PERMISSION_INVITE_GUEST.Id,
|
||||
PERMISSION_PROMOTE_GUEST.Id,
|
||||
PERMISSION_DEMOTE_TO_GUEST.Id,
|
||||
PERMISSION_DELETE_POST.Id,
|
||||
PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||
PERMISSION_CREATE_TEAM.Id,
|
||||
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
SESSION_PROP_IS_BOT = "is_bot"
|
||||
SESSION_PROP_IS_BOT_VALUE = "true"
|
||||
SESSION_TYPE_USER_ACCESS_TOKEN = "UserAccessToken"
|
||||
SESSION_PROP_IS_GUEST = "is_guest"
|
||||
SESSION_ACTIVITY_TIMEOUT = 1000 * 60 * 5 // 5 minutes
|
||||
SESSION_USER_ACCESS_TOKEN_EXPIRY = 100 * 365 // 100 years
|
||||
)
|
||||
|
||||
@@ -32,6 +32,9 @@ const (
|
||||
CHANNEL_MEMBERS_COUNTS_CACHE_SIZE = model.CHANNEL_CACHE_SIZE
|
||||
CHANNEL_MEMBERS_COUNTS_CACHE_SEC = 1800 // 30 mins
|
||||
|
||||
CHANNEL_GUESTS_COUNTS_CACHE_SIZE = model.CHANNEL_CACHE_SIZE
|
||||
CHANNEL_GUESTS_COUNTS_CACHE_SEC = 1800 // 30 mins
|
||||
|
||||
CHANNEL_CACHE_SEC = 900 // 15 mins
|
||||
)
|
||||
|
||||
@@ -278,6 +281,7 @@ type publicChannel struct {
|
||||
}
|
||||
|
||||
var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE)
|
||||
var channelGuestCountsCache = utils.NewLru(CHANNEL_GUESTS_COUNTS_CACHE_SIZE)
|
||||
var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE)
|
||||
var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE)
|
||||
var channelCache = utils.NewLru(model.CHANNEL_CACHE_SIZE)
|
||||
@@ -285,6 +289,7 @@ var channelByNameCache = utils.NewLru(model.CHANNEL_CACHE_SIZE)
|
||||
|
||||
func (s SqlChannelStore) ClearCaches() {
|
||||
channelMemberCountsCache.Purge()
|
||||
channelGuestCountsCache.Purge()
|
||||
allChannelMembersForUserCache.Purge()
|
||||
allChannelMembersNotifyPropsForChannelCache.Purge()
|
||||
channelCache.Purge()
|
||||
@@ -1654,6 +1659,69 @@ func (s SqlChannelStore) GetMemberCount(channelId string, allowFromCache bool) (
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) InvalidateGuestCount(channelId string) {
|
||||
channelGuestCountsCache.Remove(channelId)
|
||||
if s.metrics != nil {
|
||||
s.metrics.IncrementMemCacheInvalidationCounter("Channel Guest Counts - Remove by ChannelId")
|
||||
}
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetGuestCountFromCache(channelId string) int64 {
|
||||
if cacheItem, ok := channelGuestCountsCache.Get(channelId); ok {
|
||||
if s.metrics != nil {
|
||||
s.metrics.IncrementMemCacheHitCounter("Channel Guest Counts")
|
||||
}
|
||||
return cacheItem.(int64)
|
||||
}
|
||||
|
||||
if s.metrics != nil {
|
||||
s.metrics.IncrementMemCacheMissCounter("Channel Guest Counts")
|
||||
}
|
||||
|
||||
count, err := s.GetGuestCount(channelId, true)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) GetGuestCount(channelId string, allowFromCache bool) (int64, *model.AppError) {
|
||||
if allowFromCache {
|
||||
if cacheItem, ok := channelGuestCountsCache.Get(channelId); ok {
|
||||
if s.metrics != nil {
|
||||
s.metrics.IncrementMemCacheHitCounter("Channel Guest Counts")
|
||||
}
|
||||
return cacheItem.(int64), nil
|
||||
}
|
||||
}
|
||||
|
||||
if s.metrics != nil {
|
||||
s.metrics.IncrementMemCacheMissCounter("Channel Guest Counts")
|
||||
}
|
||||
|
||||
count, err := s.GetReplica().SelectInt(`
|
||||
SELECT
|
||||
count(*)
|
||||
FROM
|
||||
ChannelMembers,
|
||||
Users
|
||||
WHERE
|
||||
ChannelMembers.UserId = Users.Id
|
||||
AND ChannelMembers.ChannelId = :ChannelId
|
||||
AND ChannelMembers.SchemeGuest = TRUE
|
||||
AND Users.DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId})
|
||||
if err != nil {
|
||||
return 0, model.NewAppError("SqlChannelStore.GetGuestCount", "store.sql_channel.get_member_count.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if allowFromCache {
|
||||
channelGuestCountsCache.AddWithExpiresInSecs(channelId, count, CHANNEL_GUESTS_COUNTS_CACHE_SEC)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) RemoveMember(channelId string, userId string) *model.AppError {
|
||||
_, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId})
|
||||
if err != nil {
|
||||
@@ -1871,8 +1939,7 @@ func (s SqlChannelStore) AnalyticsDeletedTypeCount(teamId string, channelType st
|
||||
|
||||
func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) (*model.ChannelMembers, *model.AppError) {
|
||||
var dbMembers channelMemberWithSchemeRolesList
|
||||
_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
|
||||
|
||||
_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId AND Teams.Id = :TeamId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("SqlChannelStore.GetMembersForUser", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -206,6 +206,23 @@ func (me SqlSessionStore) UpdateDeviceId(id string, deviceId string, expiresAt i
|
||||
return deviceId, nil
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) UpdateProps(session *model.Session) *model.AppError {
|
||||
oldSession, appErr := me.Get(session.Id)
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
oldSession.Props = session.Props
|
||||
|
||||
count, err := me.GetMaster().Update(oldSession)
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlSessionStore.UpdateProps", "store.sql_session.update_props.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
if count != 1 {
|
||||
return model.NewAppError("SqlSessionStore.UpdateProps", "store.sql_session.update_props.app_error", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (me SqlSessionStore) AnalyticsSessionCount() (int64, *model.AppError) {
|
||||
query :=
|
||||
`SELECT
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewSqlTokenStore(sqlStore SqlStore) store.TokenStore {
|
||||
table := db.AddTableWithName(model.Token{}, "Tokens").SetKeys(false, "Token")
|
||||
table.ColMap("Token").SetMaxSize(64)
|
||||
table.ColMap("Type").SetMaxSize(64)
|
||||
table.ColMap("Extra").SetMaxSize(128)
|
||||
table.ColMap("Extra").SetMaxSize(2048)
|
||||
}
|
||||
|
||||
return s
|
||||
|
||||
@@ -710,6 +710,12 @@ func UpgradeDatabaseToVersion514(sqlStore SqlStore) {
|
||||
// TODO: Uncomment following condition when version 5.14.0 is released
|
||||
// if shouldPerformUpgrade(sqlStore, VERSION_5_13_0, VERSION_5_14_0) {
|
||||
|
||||
if sqlStore.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
||||
sqlStore.GetMaster().Exec("ALTER TABLE Tokens ALTER COLUMN Extra TYPE varchar(2048)")
|
||||
} else if sqlStore.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
||||
sqlStore.GetMaster().Exec("ALTER TABLE Tokens MODIFY Extra text")
|
||||
}
|
||||
|
||||
// saveSchemaVersion(sqlStore, VERSION_5_14_0)
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1066,7 +1066,7 @@ func (us SqlUserStore) PermanentDelete(userId string) *model.AppError {
|
||||
}
|
||||
|
||||
func (us SqlUserStore) Count(options model.UserCountOptions) (int64, *model.AppError) {
|
||||
query := sq.Select("COUNT(DISTINCT u.Id)").From("Users AS u")
|
||||
query := us.getQueryBuilder().Select("COUNT(DISTINCT u.Id)").From("Users AS u")
|
||||
|
||||
if !options.IncludeDeleted {
|
||||
query = query.Where("u.DeleteAt = 0")
|
||||
@@ -1644,3 +1644,140 @@ func applyViewRestrictionsFilter(query sq.SelectBuilder, restrictions *model.Vie
|
||||
|
||||
return resultQuery
|
||||
}
|
||||
|
||||
func (us SqlUserStore) PromoteGuestToUser(userId string) *model.AppError {
|
||||
transaction, err := us.GetMaster().Begin()
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlUserStore.PromoteGuestToUser", "store.sql_user.promote_guest.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
defer finalizeTransaction(transaction)
|
||||
|
||||
user, appErr := us.Get(userId)
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
roles := user.GetRoles()
|
||||
|
||||
for idx, role := range roles {
|
||||
if role == "system_guest" {
|
||||
roles[idx] = "system_user"
|
||||
}
|
||||
}
|
||||
|
||||
query := us.getQueryBuilder().Update("Users").
|
||||
Set("Roles", strings.Join(roles, " ")).
|
||||
Where(sq.Eq{"Id": userId})
|
||||
|
||||
queryString, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlUserStore.PromoteGuestToUser", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if _, err = transaction.Exec(queryString, args...); err != nil {
|
||||
return model.NewAppError("SqlUserStore.PromoteGuestToUser", "store.sql_user.promote_guest.user_update.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
query = us.getQueryBuilder().Update("ChannelMembers").
|
||||
Set("SchemeUser", true).
|
||||
Set("SchemeGuest", false).
|
||||
Where(sq.Eq{"UserId": userId})
|
||||
|
||||
queryString, args, err = query.ToSql()
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlUserStore.PromoteGuestToUser", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if _, err = transaction.Exec(queryString, args...); err != nil {
|
||||
return model.NewAppError("SqlUserStore.PromoteGuestToUser", "store.sql_user.promote_guest.channel_members_update.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
query = us.getQueryBuilder().Update("TeamMembers").
|
||||
Set("SchemeUser", true).
|
||||
Set("SchemeGuest", false).
|
||||
Where(sq.Eq{"UserId": userId})
|
||||
|
||||
queryString, args, err = query.ToSql()
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlUserStore.PromoteGuestToUser", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if _, err := transaction.Exec(queryString, args...); err != nil {
|
||||
return model.NewAppError("SqlUserStore.PromoteGuestToUser", "store.sql_user.promote_guest.team_members_update.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := transaction.Commit(); err != nil {
|
||||
return model.NewAppError("SqlUserStore.PromoteGuestToUser", "store.sql_user.promote_guest.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us SqlUserStore) DemoteUserToGuest(userId string) *model.AppError {
|
||||
transaction, err := us.GetMaster().Begin()
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlUserStore.DemoteUserToGuest", "store.sql_user.demote_user_to_guest.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
defer finalizeTransaction(transaction)
|
||||
|
||||
user, appErr := us.Get(userId)
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
roles := user.GetRoles()
|
||||
|
||||
newRoles := []string{}
|
||||
for _, role := range roles {
|
||||
if role == "system_user" {
|
||||
newRoles = append(newRoles, "system_guest")
|
||||
} else if role != "system_admin" {
|
||||
newRoles = append(newRoles, role)
|
||||
}
|
||||
}
|
||||
|
||||
query := us.getQueryBuilder().Update("Users").
|
||||
Set("Roles", strings.Join(newRoles, " ")).
|
||||
Where(sq.Eq{"Id": userId})
|
||||
|
||||
queryString, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlUserStore.DemoteGuestToUser", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if _, err = transaction.Exec(queryString, args...); err != nil {
|
||||
return model.NewAppError("SqlUserStore.DemoteGuestToUser", "store.sql_user.demote_user_to_guest.user_update.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
query = us.getQueryBuilder().Update("ChannelMembers").
|
||||
Set("SchemeUser", false).
|
||||
Set("SchemeGuest", true).
|
||||
Where(sq.Eq{"UserId": userId})
|
||||
|
||||
queryString, args, err = query.ToSql()
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlUserStore.DemoteGuestToUser", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if _, err = transaction.Exec(queryString, args...); err != nil {
|
||||
return model.NewAppError("SqlUserStore.DemoteGuestToUser", "store.sql_user.demote_user_to_guest.channel_members_update.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
query = us.getQueryBuilder().Update("TeamMembers").
|
||||
Set("SchemeUser", false).
|
||||
Set("SchemeGuest", true).
|
||||
Where(sq.Eq{"UserId": userId})
|
||||
|
||||
queryString, args, err = query.ToSql()
|
||||
if err != nil {
|
||||
return model.NewAppError("SqlUserStore.DemoteGuestToUser", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if _, err := transaction.Exec(queryString, args...); err != nil {
|
||||
return model.NewAppError("SqlUserStore.DemoteGuestToUser", "store.sql_user.demote_user_to_guest.team_members_update.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := transaction.Commit(); err != nil {
|
||||
return model.NewAppError("SqlUserStore.DemoteGuestToUser", "store.sql_user.demote_user_to_guest.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -172,6 +172,9 @@ type ChannelStore interface {
|
||||
InvalidateMemberCount(channelId string)
|
||||
GetMemberCountFromCache(channelId string) int64
|
||||
GetMemberCount(channelId string, allowFromCache bool) (int64, *model.AppError)
|
||||
InvalidateGuestCount(channelId string)
|
||||
GetGuestCountFromCache(channelId string) int64
|
||||
GetGuestCount(channelId string, allowFromCache bool) (int64, *model.AppError)
|
||||
GetPinnedPosts(channelId string) (*model.PostList, *model.AppError)
|
||||
RemoveMember(channelId string, userId string) *model.AppError
|
||||
PermanentDeleteMembersByUser(userId string) *model.AppError
|
||||
@@ -307,6 +310,8 @@ type UserStore interface {
|
||||
Count(options model.UserCountOptions) (int64, *model.AppError)
|
||||
GetTeamGroupUsers(teamID string) ([]*model.User, *model.AppError)
|
||||
GetChannelGroupUsers(channelID string) ([]*model.User, *model.AppError)
|
||||
PromoteGuestToUser(userID string) *model.AppError
|
||||
DemoteUserToGuest(userID string) *model.AppError
|
||||
}
|
||||
|
||||
type BotStore interface {
|
||||
@@ -328,6 +333,7 @@ type SessionStore interface {
|
||||
UpdateLastActivityAt(sessionId string, time int64) *model.AppError
|
||||
UpdateRoles(userId string, roles string) (string, *model.AppError)
|
||||
UpdateDeviceId(id string, deviceId string, expiresAt int64) (string, *model.AppError)
|
||||
UpdateProps(session *model.Session) *model.AppError
|
||||
AnalyticsSessionCount() (int64, *model.AppError)
|
||||
Cleanup(expiryTime int64, batchSize int64)
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
||||
t.Run("GetMember", func(t *testing.T) { testGetMember(t, ss) })
|
||||
t.Run("GetMemberForPost", func(t *testing.T) { testChannelStoreGetMemberForPost(t, ss) })
|
||||
t.Run("GetMemberCount", func(t *testing.T) { testGetMemberCount(t, ss) })
|
||||
t.Run("GetGuestCount", func(t *testing.T) { testGetGuestCount(t, ss) })
|
||||
t.Run("SearchMore", func(t *testing.T) { testChannelStoreSearchMore(t, ss) })
|
||||
t.Run("SearchInTeam", func(t *testing.T) { testChannelStoreSearchInTeam(t, ss) })
|
||||
t.Run("SearchAllChannels", func(t *testing.T) { testChannelStoreSearchAllChannels(t, ss) })
|
||||
@@ -2045,6 +2046,124 @@ func testGetMemberCount(t *testing.T, ss store.Store) {
|
||||
}
|
||||
}
|
||||
|
||||
func testGetGuestCount(t *testing.T, ss store.Store) {
|
||||
teamId := model.NewId()
|
||||
|
||||
c1 := model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel1",
|
||||
Name: "zz" + model.NewId() + "b",
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}
|
||||
_, err := ss.Channel().Save(&c1, -1)
|
||||
require.Nil(t, err)
|
||||
|
||||
c2 := model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel2",
|
||||
Name: "zz" + model.NewId() + "b",
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}
|
||||
_, err = ss.Channel().Save(&c2, -1)
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("Regular member doesn't count", func(t *testing.T) {
|
||||
u1 := &model.User{
|
||||
Email: MakeEmail(),
|
||||
DeleteAt: 0,
|
||||
Roles: model.SYSTEM_USER_ROLE_ID,
|
||||
}
|
||||
_, err = ss.User().Save(u1)
|
||||
require.Nil(t, err)
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}, -1))
|
||||
|
||||
m1 := model.ChannelMember{
|
||||
ChannelId: c1.Id,
|
||||
UserId: u1.Id,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
SchemeGuest: false,
|
||||
}
|
||||
_, err = ss.Channel().SaveMember(&m1)
|
||||
require.Nil(t, err)
|
||||
|
||||
count, channelErr := ss.Channel().GetGuestCount(c1.Id, false)
|
||||
require.Nil(t, channelErr)
|
||||
require.Equal(t, count, int64(0))
|
||||
})
|
||||
|
||||
t.Run("Guest member does count", func(t *testing.T) {
|
||||
u2 := model.User{
|
||||
Email: MakeEmail(),
|
||||
DeleteAt: 0,
|
||||
Roles: model.SYSTEM_GUEST_ROLE_ID,
|
||||
}
|
||||
_, err = ss.User().Save(&u2)
|
||||
require.Nil(t, err)
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id}, -1))
|
||||
|
||||
m2 := model.ChannelMember{
|
||||
ChannelId: c1.Id,
|
||||
UserId: u2.Id,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
SchemeGuest: true,
|
||||
}
|
||||
_, err = ss.Channel().SaveMember(&m2)
|
||||
require.Nil(t, err)
|
||||
|
||||
count, channelErr := ss.Channel().GetGuestCount(c1.Id, false)
|
||||
require.Nil(t, channelErr)
|
||||
require.Equal(t, count, int64(1))
|
||||
})
|
||||
|
||||
t.Run("make sure members of other channels aren't counted", func(t *testing.T) {
|
||||
u3 := model.User{
|
||||
Email: MakeEmail(),
|
||||
DeleteAt: 0,
|
||||
Roles: model.SYSTEM_GUEST_ROLE_ID,
|
||||
}
|
||||
_, err = ss.User().Save(&u3)
|
||||
require.Nil(t, err)
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u3.Id}, -1))
|
||||
|
||||
m3 := model.ChannelMember{
|
||||
ChannelId: c2.Id,
|
||||
UserId: u3.Id,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
SchemeGuest: true,
|
||||
}
|
||||
_, err = ss.Channel().SaveMember(&m3)
|
||||
require.Nil(t, err)
|
||||
|
||||
count, channelErr := ss.Channel().GetGuestCount(c1.Id, false)
|
||||
require.Nil(t, channelErr)
|
||||
require.Equal(t, count, int64(1))
|
||||
})
|
||||
|
||||
t.Run("make sure inactive users aren't counted", func(t *testing.T) {
|
||||
u4 := &model.User{
|
||||
Email: MakeEmail(),
|
||||
DeleteAt: 10000,
|
||||
Roles: model.SYSTEM_GUEST_ROLE_ID,
|
||||
}
|
||||
_, err = ss.User().Save(u4)
|
||||
require.Nil(t, err)
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u4.Id}, -1))
|
||||
|
||||
m4 := model.ChannelMember{
|
||||
ChannelId: c1.Id,
|
||||
UserId: u4.Id,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
SchemeGuest: true,
|
||||
}
|
||||
_, err = ss.Channel().SaveMember(&m4)
|
||||
require.Nil(t, err)
|
||||
|
||||
count, channelErr := ss.Channel().GetGuestCount(c1.Id, false)
|
||||
require.Nil(t, channelErr)
|
||||
require.Equal(t, count, int64(1))
|
||||
})
|
||||
}
|
||||
|
||||
func testChannelStoreSearchMore(t *testing.T, ss store.Store) {
|
||||
teamId := model.NewId()
|
||||
otherTeamId := model.NewId()
|
||||
|
||||
@@ -744,6 +744,43 @@ func (_m *ChannelStore) GetFromMaster(id string) (*model.Channel, *model.AppErro
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetGuestCount provides a mock function with given fields: channelId, allowFromCache
|
||||
func (_m *ChannelStore) GetGuestCount(channelId string, allowFromCache bool) (int64, *model.AppError) {
|
||||
ret := _m.Called(channelId, allowFromCache)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(string, bool) int64); ok {
|
||||
r0 = rf(channelId, allowFromCache)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 *model.AppError
|
||||
if rf, ok := ret.Get(1).(func(string, bool) *model.AppError); ok {
|
||||
r1 = rf(channelId, allowFromCache)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*model.AppError)
|
||||
}
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetGuestCountFromCache provides a mock function with given fields: channelId
|
||||
func (_m *ChannelStore) GetGuestCountFromCache(channelId string) int64 {
|
||||
ret := _m.Called(channelId)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(string) int64); ok {
|
||||
r0 = rf(channelId)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetMember provides a mock function with given fields: channelId, userId
|
||||
func (_m *ChannelStore) GetMember(channelId string, userId string) (*model.ChannelMember, *model.AppError) {
|
||||
ret := _m.Called(channelId, userId)
|
||||
@@ -1092,6 +1129,11 @@ func (_m *ChannelStore) InvalidateChannelByName(teamId string, name string) {
|
||||
_m.Called(teamId, name)
|
||||
}
|
||||
|
||||
// InvalidateGuestCount provides a mock function with given fields: channelId
|
||||
func (_m *ChannelStore) InvalidateGuestCount(channelId string) {
|
||||
_m.Called(channelId)
|
||||
}
|
||||
|
||||
// InvalidateMemberCount provides a mock function with given fields: channelId
|
||||
func (_m *ChannelStore) InvalidateMemberCount(channelId string) {
|
||||
_m.Called(channelId)
|
||||
|
||||
@@ -227,6 +227,22 @@ func (_m *SessionStore) UpdateLastActivityAt(sessionId string, time int64) *mode
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdateProps provides a mock function with given fields: session
|
||||
func (_m *SessionStore) UpdateProps(session *model.Session) *model.AppError {
|
||||
ret := _m.Called(session)
|
||||
|
||||
var r0 *model.AppError
|
||||
if rf, ok := ret.Get(0).(func(*model.Session) *model.AppError); ok {
|
||||
r0 = rf(session)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.AppError)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdateRoles provides a mock function with given fields: userId, roles
|
||||
func (_m *SessionStore) UpdateRoles(userId string, roles string) (string, *model.AppError) {
|
||||
ret := _m.Called(userId, roles)
|
||||
|
||||
@@ -126,6 +126,22 @@ func (_m *UserStore) Count(options model.UserCountOptions) (int64, *model.AppErr
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DemoteUserToGuest provides a mock function with given fields: userID
|
||||
func (_m *UserStore) DemoteUserToGuest(userID string) *model.AppError {
|
||||
ret := _m.Called(userID)
|
||||
|
||||
var r0 *model.AppError
|
||||
if rf, ok := ret.Get(0).(func(string) *model.AppError); ok {
|
||||
r0 = rf(userID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.AppError)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: id
|
||||
func (_m *UserStore) Get(id string) (*model.User, *model.AppError) {
|
||||
ret := _m.Called(id)
|
||||
@@ -905,6 +921,22 @@ func (_m *UserStore) PermanentDelete(userId string) *model.AppError {
|
||||
return r0
|
||||
}
|
||||
|
||||
// PromoteGuestToUser provides a mock function with given fields: userID
|
||||
func (_m *UserStore) PromoteGuestToUser(userID string) *model.AppError {
|
||||
ret := _m.Called(userID)
|
||||
|
||||
var r0 *model.AppError
|
||||
if rf, ok := ret.Get(0).(func(string) *model.AppError); ok {
|
||||
r0 = rf(userID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.AppError)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// ResetLastPictureUpdate provides a mock function with given fields: userId
|
||||
func (_m *UserStore) ResetLastPictureUpdate(userId string) *model.AppError {
|
||||
ret := _m.Called(userId)
|
||||
|
||||
@@ -78,6 +78,8 @@ func TestUserStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
||||
t.Run("GetUsersBatchForIndexing", func(t *testing.T) { testUserStoreGetUsersBatchForIndexing(t, ss) })
|
||||
t.Run("GetTeamGroupUsers", func(t *testing.T) { testUserStoreGetTeamGroupUsers(t, ss) })
|
||||
t.Run("GetChannelGroupUsers", func(t *testing.T) { testUserStoreGetChannelGroupUsers(t, ss) })
|
||||
t.Run("PromoteGuestToUser", func(t *testing.T) { testUserStorePromoteGuestToUser(t, ss) })
|
||||
t.Run("DemoteUserToGuest", func(t *testing.T) { testUserStoreDemoteUserToGuest(t, ss) })
|
||||
t.Run("ResetLastPictureUpdate", func(t *testing.T) { testUserStoreResetLastPictureUpdate(t, ss) })
|
||||
}
|
||||
|
||||
@@ -4154,6 +4156,604 @@ func testUserStoreGetChannelGroupUsers(t *testing.T, ss store.Store) {
|
||||
requireNUsers(2)
|
||||
}
|
||||
|
||||
func testUserStorePromoteGuestToUser(t *testing.T, ss store.Store) {
|
||||
// create users
|
||||
t.Run("Must do nothing with regular user", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: true, SchemeUser: false}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, SchemeGuest: true, SchemeUser: false, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().PromoteGuestToUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_user", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedTeamMember.SchemeGuest)
|
||||
require.True(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedChannelMember.SchemeGuest)
|
||||
require.True(t, updatedChannelMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must do nothing with admin user", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user system_admin",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: true, SchemeUser: false}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, SchemeGuest: true, SchemeUser: false, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().PromoteGuestToUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_user system_admin", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedTeamMember.SchemeGuest)
|
||||
require.True(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedChannelMember.SchemeGuest)
|
||||
require.True(t, updatedChannelMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must work with guest user without teams or channels", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_guest",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().PromoteGuestToUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_user", updatedUser.Roles)
|
||||
})
|
||||
|
||||
t.Run("Must work with guest user with teams but no channels", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_guest",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: true, SchemeUser: false}, 999))
|
||||
|
||||
err = ss.User().PromoteGuestToUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_user", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedTeamMember.SchemeGuest)
|
||||
require.True(t, updatedTeamMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must work with guest user with teams and channels", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_guest",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: true, SchemeUser: false}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, SchemeGuest: true, SchemeUser: false, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().PromoteGuestToUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_user", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedTeamMember.SchemeGuest)
|
||||
require.True(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedChannelMember.SchemeGuest)
|
||||
require.True(t, updatedChannelMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must work with guest user with teams and channels and custom role", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_guest custom_role",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: true, SchemeUser: false}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, SchemeGuest: true, SchemeUser: false, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().PromoteGuestToUser(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_user custom_role", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedTeamMember.SchemeGuest)
|
||||
require.True(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedChannelMember.SchemeGuest)
|
||||
require.True(t, updatedChannelMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must no change any other user guest role", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user1, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_guest",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId1 := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId1, UserId: user1.Id, SchemeGuest: true, SchemeUser: false}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId1,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user1.Id, SchemeGuest: true, SchemeUser: false, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
id = model.NewId()
|
||||
user2, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_guest",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId2 := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId2, UserId: user2.Id, SchemeGuest: true, SchemeUser: false}, 999))
|
||||
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user2.Id, SchemeGuest: true, SchemeUser: false, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().PromoteGuestToUser(user1.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user1.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_user", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId1, user1.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedTeamMember.SchemeGuest)
|
||||
require.True(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user1.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, updatedChannelMember.SchemeGuest)
|
||||
require.True(t, updatedChannelMember.SchemeUser)
|
||||
|
||||
notUpdatedUser, err := ss.User().Get(user2.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_guest", notUpdatedUser.Roles)
|
||||
|
||||
notUpdatedTeamMember, err := ss.Team().GetMember(teamId2, user2.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, notUpdatedTeamMember.SchemeGuest)
|
||||
require.False(t, notUpdatedTeamMember.SchemeUser)
|
||||
|
||||
notUpdatedChannelMember, err := ss.Channel().GetMember(channel.Id, user2.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, notUpdatedChannelMember.SchemeGuest)
|
||||
require.False(t, notUpdatedChannelMember.SchemeUser)
|
||||
})
|
||||
}
|
||||
|
||||
func testUserStoreDemoteUserToGuest(t *testing.T, ss store.Store) {
|
||||
// create users
|
||||
t.Run("Must do nothing with guest", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_guest",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: false, SchemeUser: true}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, SchemeGuest: false, SchemeUser: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().DemoteUserToGuest(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_guest", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, updatedTeamMember.SchemeGuest)
|
||||
require.False(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, updatedChannelMember.SchemeGuest)
|
||||
require.False(t, updatedChannelMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must demote properly an admin user", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user system_admin",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: true, SchemeUser: false}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, SchemeGuest: true, SchemeUser: false, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().DemoteUserToGuest(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_guest", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, updatedTeamMember.SchemeGuest)
|
||||
require.False(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, updatedChannelMember.SchemeGuest)
|
||||
require.False(t, updatedChannelMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must work with user without teams or channels", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().DemoteUserToGuest(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_guest", updatedUser.Roles)
|
||||
})
|
||||
|
||||
t.Run("Must work with user with teams but no channels", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: false, SchemeUser: true}, 999))
|
||||
|
||||
err = ss.User().DemoteUserToGuest(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_guest", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, updatedTeamMember.SchemeGuest)
|
||||
require.False(t, updatedTeamMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must work with user with teams and channels", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: false, SchemeUser: true}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, SchemeGuest: false, SchemeUser: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().DemoteUserToGuest(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_guest", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, updatedTeamMember.SchemeGuest)
|
||||
require.False(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.True(t, updatedChannelMember.SchemeGuest)
|
||||
require.False(t, updatedChannelMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must work with user with teams and channels and custom role", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user custom_role",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: user.Id, SchemeGuest: false, SchemeUser: true}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, SchemeGuest: false, SchemeUser: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().DemoteUserToGuest(user.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_guest custom_role", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId, user.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, updatedTeamMember.SchemeGuest)
|
||||
require.False(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user.Id)
|
||||
assert.Nil(t, err)
|
||||
require.True(t, updatedChannelMember.SchemeGuest)
|
||||
require.False(t, updatedChannelMember.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("Must no change any other user role", func(t *testing.T) {
|
||||
id := model.NewId()
|
||||
user1, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId1 := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId1, UserId: user1.Id, SchemeGuest: false, SchemeUser: true}, 999))
|
||||
|
||||
channel, err := ss.Channel().Save(&model.Channel{
|
||||
TeamId: teamId1,
|
||||
DisplayName: "Channel name",
|
||||
Name: "channel-" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}, -1)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user1.Id, SchemeGuest: false, SchemeUser: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
id = model.NewId()
|
||||
user2, err := ss.User().Save(&model.User{
|
||||
Email: id + "@test.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
FirstName: "f_" + id,
|
||||
LastName: "l_" + id,
|
||||
Password: "Password1",
|
||||
Roles: "system_user",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
teamId2 := model.NewId()
|
||||
store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId2, UserId: user2.Id, SchemeGuest: false, SchemeUser: true}, 999))
|
||||
|
||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{ChannelId: channel.Id, UserId: user2.Id, SchemeGuest: false, SchemeUser: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = ss.User().DemoteUserToGuest(user1.Id)
|
||||
assert.Nil(t, err)
|
||||
updatedUser, err := ss.User().Get(user1.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_guest", updatedUser.Roles)
|
||||
|
||||
updatedTeamMember, err := ss.Team().GetMember(teamId1, user1.Id)
|
||||
require.Nil(t, err)
|
||||
require.True(t, updatedTeamMember.SchemeGuest)
|
||||
require.False(t, updatedTeamMember.SchemeUser)
|
||||
|
||||
updatedChannelMember, err := ss.Channel().GetMember(channel.Id, user1.Id)
|
||||
assert.Nil(t, err)
|
||||
require.True(t, updatedChannelMember.SchemeGuest)
|
||||
require.False(t, updatedChannelMember.SchemeUser)
|
||||
|
||||
notUpdatedUser, err := ss.User().Get(user2.Id)
|
||||
assert.Nil(t, err)
|
||||
require.Equal(t, "system_user", notUpdatedUser.Roles)
|
||||
|
||||
notUpdatedTeamMember, err := ss.Team().GetMember(teamId2, user2.Id)
|
||||
require.Nil(t, err)
|
||||
require.False(t, notUpdatedTeamMember.SchemeGuest)
|
||||
require.True(t, notUpdatedTeamMember.SchemeUser)
|
||||
|
||||
notUpdatedChannelMember, err := ss.Channel().GetMember(channel.Id, user2.Id)
|
||||
assert.Nil(t, err)
|
||||
require.False(t, notUpdatedChannelMember.SchemeGuest)
|
||||
require.True(t, notUpdatedChannelMember.SchemeUser)
|
||||
})
|
||||
}
|
||||
|
||||
func testUserStoreResetLastPictureUpdate(t *testing.T, ss store.Store) {
|
||||
u1 := &model.User{}
|
||||
u1.Email = MakeEmail()
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<p style="margin: 30px 0 15px">
|
||||
<a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
<p>{{.Props.Message}}</p>
|
||||
<br/>
|
||||
<p>{{.Html.ExtraInfo}}<br/><br/><a href='{{.Props.TeamURL}}'>{{.Props.TeamURL}}</a></p>
|
||||
</td>
|
||||
|
||||
@@ -155,6 +155,7 @@ func GetClientLicense(l *model.License) map[string]string {
|
||||
props["EmailNotificationContents"] = strconv.FormatBool(*l.Features.EmailNotificationContents)
|
||||
props["MessageExport"] = strconv.FormatBool(*l.Features.MessageExport)
|
||||
props["CustomPermissionsSchemes"] = strconv.FormatBool(*l.Features.CustomPermissionsSchemes)
|
||||
props["GuestAccountsPermissions"] = strconv.FormatBool(*l.Features.GuestAccountsPermissions)
|
||||
props["CustomTermsOfService"] = strconv.FormatBool(*l.Features.CustomTermsOfService)
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,9 @@ func (c *Context) MfaRequired() {
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "MfaRequired", http.StatusUnauthorized)
|
||||
return
|
||||
} else {
|
||||
if user.IsGuest() && !*c.App.Config().GuestAccountsSettings.EnforceMultifactorAuthentication {
|
||||
return
|
||||
}
|
||||
// Only required for email and ldap accounts
|
||||
if user.AuthService != "" &&
|
||||
user.AuthService != model.USER_AUTH_SERVICE_EMAIL &&
|
||||
|
||||
Ссылка в новой задаче
Block a user