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  }