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
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)
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user