diff --git a/server/channels/api4/channel.go b/server/channels/api4/channel.go index b1202c5f7b..454969cee9 100644 --- a/server/channels/api4/channel.go +++ b/server/channels/api4/channel.go @@ -1972,16 +1972,15 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { lastError = err continue } - } else { - // user is already a member, go to next - c.Logger.Warn("User is already a channel member, skipping", mlog.String("UserId", userId), mlog.String("ChannelId", channel.Id)) - newChannelMembers = append(newChannelMembers, *existingMember) - continue } if channel.Type == model.ChannelTypeOpen { isSelfAdd := member.UserId == c.AppContext.Session().UserId - if isSelfAdd && !canAddSelf { + if isSelfAdd && existingMember != nil { + // users should be able to add themselves if they're already a member, even if they don't have permissions + newChannelMembers = append(newChannelMembers, *existingMember) + continue + } else if isSelfAdd && !canAddSelf { c.Logger.Warn("Error adding channel member, Invalid Permission to add self", mlog.String("UserId", userId), mlog.String("ChannelId", channel.Id)) c.SetPermissionError(model.PermissionJoinPublicChannels) lastError = c.Err @@ -1994,6 +1993,13 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { } } + if existingMember != nil { + // user is already a member, go to next + c.Logger.Warn("User is already a channel member, skipping", mlog.String("UserId", userId), mlog.String("ChannelId", channel.Id)) + newChannelMembers = append(newChannelMembers, *existingMember) + continue + } + cm, err := c.App.AddChannelMember(c.AppContext, member.UserId, channel, app.ChannelMemberOpts{ UserRequestorID: c.AppContext.Session().UserId, PostRootID: postRootId, diff --git a/server/channels/api4/channel_test.go b/server/channels/api4/channel_test.go index 87faa43a6e..4ac462aae9 100644 --- a/server/channels/api4/channel_test.go +++ b/server/channels/api4/channel_test.go @@ -4481,6 +4481,37 @@ func TestAddChannelMember(t *testing.T) { require.NoError(t, err) }) + t.Run("requester is not a member of the team and tries to add a user to a channel where it is already a member", func(t *testing.T) { + // Create two teams using SystemAdminClient + t1 := th.CreateTeamWithClient(th.SystemAdminClient) + t2 := th.CreateTeamWithClient(th.SystemAdminClient) + + // Use existing users - user will be BasicUser, user2 will be BasicUser2 + u1 := th.BasicUser + u2 := th.BasicUser2 + + // Add user1 to team1 and user2 to team2 (they're already on BasicTeam) + th.LinkUserToTeam(u1, t1) + th.LinkUserToTeam(u2, t2) + + // Create a public channel in team1 + pubChannel := th.CreateChannelWithClientAndTeam(th.SystemAdminClient, model.ChannelTypeOpen, t1.Id) + + // Add user1 to the public channel + th.AddUserToChannel(u1, pubChannel) + + // Create client for user2 + client2 := th.CreateClient() + _, _, err := client2.Login(context.Background(), u2.Email, u2.Password) + require.NoError(t, err) + + // Try to add user1 to the public channel using user2's credentials + // This should fail with 403 since user2 is not a member of the team + _, resp, err := client2.AddChannelMember(context.Background(), pubChannel.Id, u1.Id) + CheckForbiddenStatus(t, resp) + require.Error(t, err) + }) + t.Run("invalid request data", func(t *testing.T) { th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) { // correct type for user ids (string) but invalid value.