github.com/mattermost/mattermost-server/v5@v5.39.3/services/sharedchannel/channelinvite.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sharedchannel 5 6 import ( 7 "context" 8 "encoding/json" 9 "fmt" 10 "strings" 11 12 "github.com/mattermost/mattermost-server/v5/app/request" 13 "github.com/mattermost/mattermost-server/v5/model" 14 "github.com/mattermost/mattermost-server/v5/services/remotecluster" 15 "github.com/mattermost/mattermost-server/v5/shared/mlog" 16 ) 17 18 // channelInviteMsg represents an invitation for a remote cluster to start sharing a channel. 19 type channelInviteMsg struct { 20 ChannelId string `json:"channel_id"` 21 TeamId string `json:"team_id"` 22 ReadOnly bool `json:"read_only"` 23 Name string `json:"name"` 24 DisplayName string `json:"display_name"` 25 Header string `json:"header"` 26 Purpose string `json:"purpose"` 27 Type string `json:"type"` 28 DirectParticipantIDs []string `json:"direct_participant_ids"` 29 } 30 31 type InviteOption func(msg *channelInviteMsg) 32 33 func WithDirectParticipantID(participantID string) InviteOption { 34 return func(msg *channelInviteMsg) { 35 msg.DirectParticipantIDs = append(msg.DirectParticipantIDs, participantID) 36 } 37 } 38 39 // SendChannelInvite asynchronously sends a channel invite to a remote cluster. The remote cluster is 40 // expected to create a new channel with the same channel id, and respond with status OK. 41 // If an error occurs on the remote cluster then an ephemeral message is posted to in the channel for userId. 42 func (scs *Service) SendChannelInvite(channel *model.Channel, userId string, rc *model.RemoteCluster, options ...InviteOption) error { 43 rcs := scs.server.GetRemoteClusterService() 44 if rcs == nil { 45 return fmt.Errorf("cannot invite remote cluster for channel id %s; Remote Cluster Service not enabled", channel.Id) 46 } 47 48 sc, err := scs.server.GetStore().SharedChannel().Get(channel.Id) 49 if err != nil { 50 return err 51 } 52 53 invite := channelInviteMsg{ 54 ChannelId: channel.Id, 55 TeamId: rc.RemoteTeamId, 56 ReadOnly: sc.ReadOnly, 57 Name: sc.ShareName, 58 DisplayName: sc.ShareDisplayName, 59 Header: sc.ShareHeader, 60 Purpose: sc.SharePurpose, 61 Type: channel.Type, 62 } 63 64 for _, option := range options { 65 option(&invite) 66 } 67 68 json, err := json.Marshal(invite) 69 if err != nil { 70 return err 71 } 72 73 msg := model.NewRemoteClusterMsg(TopicChannelInvite, json) 74 75 ctx, cancel := context.WithTimeout(context.Background(), remotecluster.SendTimeout) 76 defer cancel() 77 78 return rcs.SendMsg(ctx, msg, rc, func(msg model.RemoteClusterMsg, rc *model.RemoteCluster, resp *remotecluster.Response, err error) { 79 if err != nil || !resp.IsSuccess() { 80 scs.sendEphemeralPost(channel.Id, userId, fmt.Sprintf("Error sending channel invite for %s: %s", rc.DisplayName, combineErrors(err, resp.Err))) 81 return 82 } 83 84 scr := &model.SharedChannelRemote{ 85 ChannelId: sc.ChannelId, 86 CreatorId: userId, 87 RemoteId: rc.RemoteId, 88 IsInviteAccepted: true, 89 IsInviteConfirmed: true, 90 } 91 if _, err = scs.server.GetStore().SharedChannel().SaveRemote(scr); err != nil { 92 scs.sendEphemeralPost(channel.Id, userId, fmt.Sprintf("Error confirming channel invite for %s: %v", rc.DisplayName, err)) 93 return 94 } 95 scs.NotifyChannelChanged(sc.ChannelId) 96 scs.sendEphemeralPost(channel.Id, userId, fmt.Sprintf("`%s` has been added to channel.", rc.DisplayName)) 97 }) 98 } 99 100 func combineErrors(err error, serror string) string { 101 var sb strings.Builder 102 if err != nil { 103 sb.WriteString(err.Error()) 104 } 105 if serror != "" { 106 if sb.Len() > 0 { 107 sb.WriteString("; ") 108 } 109 sb.WriteString(serror) 110 } 111 return sb.String() 112 } 113 114 func (scs *Service) onReceiveChannelInvite(msg model.RemoteClusterMsg, rc *model.RemoteCluster, _ *remotecluster.Response) error { 115 if len(msg.Payload) == 0 { 116 return nil 117 } 118 119 var invite channelInviteMsg 120 121 if err := json.Unmarshal(msg.Payload, &invite); err != nil { 122 return fmt.Errorf("invalid channel invite: %w", err) 123 } 124 125 scs.server.GetLogger().Log(mlog.LvlSharedChannelServiceDebug, "Channel invite received", 126 mlog.String("remote", rc.DisplayName), 127 mlog.String("channel_id", invite.ChannelId), 128 mlog.String("channel_name", invite.Name), 129 mlog.String("team_id", invite.TeamId), 130 ) 131 132 // create channel if it doesn't exist; the channel may already exist, such as if it was shared then unshared at some point. 133 channel, err := scs.server.GetStore().Channel().Get(invite.ChannelId, true) 134 if err != nil { 135 if channel, err = scs.handleChannelCreation(invite, rc); err != nil { 136 return err 137 } 138 } 139 140 if invite.ReadOnly { 141 if err := scs.makeChannelReadOnly(channel); err != nil { 142 return fmt.Errorf("cannot make channel readonly `%s`: %w", invite.ChannelId, err) 143 } 144 } 145 146 sharedChannel := &model.SharedChannel{ 147 ChannelId: channel.Id, 148 TeamId: channel.TeamId, 149 Home: false, 150 ReadOnly: invite.ReadOnly, 151 ShareName: channel.Name, 152 ShareDisplayName: channel.DisplayName, 153 SharePurpose: channel.Purpose, 154 ShareHeader: channel.Header, 155 CreatorId: rc.CreatorId, 156 RemoteId: rc.RemoteId, 157 Type: channel.Type, 158 } 159 160 if _, err := scs.server.GetStore().SharedChannel().Save(sharedChannel); err != nil { 161 scs.app.PermanentDeleteChannel(channel) 162 return fmt.Errorf("cannot create shared channel (channel_id=%s): %w", invite.ChannelId, err) 163 } 164 165 sharedChannelRemote := &model.SharedChannelRemote{ 166 Id: model.NewId(), 167 ChannelId: channel.Id, 168 CreatorId: channel.CreatorId, 169 IsInviteAccepted: true, 170 IsInviteConfirmed: true, 171 RemoteId: rc.RemoteId, 172 } 173 174 if _, err := scs.server.GetStore().SharedChannel().SaveRemote(sharedChannelRemote); err != nil { 175 scs.app.PermanentDeleteChannel(channel) 176 scs.server.GetStore().SharedChannel().Delete(sharedChannel.ChannelId) 177 return fmt.Errorf("cannot create shared channel remote (channel_id=%s): %w", invite.ChannelId, err) 178 } 179 return nil 180 } 181 182 func (scs *Service) handleChannelCreation(invite channelInviteMsg, rc *model.RemoteCluster) (*model.Channel, error) { 183 if invite.Type == model.CHANNEL_DIRECT { 184 return scs.createDirectChannel(invite) 185 } 186 187 channelNew := &model.Channel{ 188 Id: invite.ChannelId, 189 TeamId: invite.TeamId, 190 Type: invite.Type, 191 DisplayName: invite.DisplayName, 192 Name: invite.Name, 193 Header: invite.Header, 194 Purpose: invite.Purpose, 195 CreatorId: rc.CreatorId, 196 Shared: model.NewBool(true), 197 } 198 199 // check user perms? 200 channel, appErr := scs.app.CreateChannelWithUser(request.EmptyContext(), channelNew, rc.CreatorId) 201 if appErr != nil { 202 return nil, fmt.Errorf("cannot create channel `%s`: %w", invite.ChannelId, appErr) 203 } 204 205 return channel, nil 206 } 207 208 func (scs *Service) createDirectChannel(invite channelInviteMsg) (*model.Channel, error) { 209 if len(invite.DirectParticipantIDs) != 2 { 210 return nil, fmt.Errorf("cannot create direct channel `%s` insufficient participant count `%d`", invite.ChannelId, len(invite.DirectParticipantIDs)) 211 } 212 213 channel, err := scs.app.GetOrCreateDirectChannel(request.EmptyContext(), invite.DirectParticipantIDs[0], invite.DirectParticipantIDs[1], model.WithID(invite.ChannelId)) 214 if err != nil { 215 return nil, fmt.Errorf("cannot create direct channel `%s`: %w", invite.ChannelId, err) 216 } 217 218 return channel, nil 219 }