MM-14753: Verifies that user can join teams and channels in spite of group constraints. (#10529)

* MM-147753: Verifies that users are allowed to be members of a team or a channel, based on group constraints, prior to allowing the API to add them.

* MM-14753: Allow methods to return meaningful results for deleted teams or channels.

* MM-14753: Renames methods to differentiate from permissions and other team and channel restrictions.

* MM-14753: Only check if users are team/channel members if team/channel is group constrained.

* MM-14753: Updates test function names.

* MM-14753: Changes a few method signatures.

* MM-14753: Small refactor and adds missing returns.

* MM-14753: Changes method names from Get* to Filter* name prefixes.

* MM-14753: Renames error variables.

* MM-14753: Updates method names for consistency with join table names.

* MM-14753: Adds case for non AppError return.

* Update i18n/en.json
Этот коммит содержится в:
Martin Kraft
2019-04-09 07:09:57 -04:00
коммит произвёл GitHub
родитель 43fa7e0548
Коммит 7bde0378cd
11 изменённых файлов: 629 добавлений и 2 удалений

Просмотреть файл

@@ -44,6 +44,7 @@ type TestHelper struct {
BasicDeletedChannel *model.Channel
BasicChannel2 *model.Channel
BasicPost *model.Post
Group *model.Group
SystemAdminClient *model.Client4
SystemAdminUser *model.User
@@ -210,6 +211,7 @@ func (me *TestHelper) InitBasic() *TestHelper {
me.App.UpdateUserRoles(me.BasicUser.Id, model.SYSTEM_USER_ROLE_ID, false)
me.Client.DeleteChannel(me.BasicDeletedChannel.Id)
me.LoginBasic()
me.Group = me.CreateGroup()
return me
}
@@ -509,6 +511,24 @@ func (me *TestHelper) GenerateTestEmail() string {
return strings.ToLower(model.NewId() + "@dockerhost")
}
func (me *TestHelper) CreateGroup() *model.Group {
id := model.NewId()
group := &model.Group{
Name: "n-" + id,
DisplayName: "dn_" + id,
Source: model.GroupSourceLdap,
RemoteId: "ri_" + id,
}
utils.DisableDebugLogForTest()
group, err := me.App.CreateGroup(group)
if err != nil {
panic(err)
}
utils.EnableDebugLogForTest()
return group
}
func GenerateTestUsername() string {
return "fakeuser" + model.NewRandomString(10)
}

Просмотреть файл

@@ -1174,6 +1174,22 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
if channel.GroupConstrained != nil && *channel.GroupConstrained {
nonMembers, err := c.App.FilterNonGroupChannelMembers([]string{member.UserId}, channel)
if err != nil {
if v, ok := err.(*model.AppError); ok {
c.Err = v
} else {
c.Err = model.NewAppError("addChannelMember", "api.channel.add_members.error", nil, err.Error(), http.StatusBadRequest)
}
return
}
if len(nonMembers) > 0 {
c.Err = model.NewAppError("addChannelMember", "api.channel.add_members.user_denied", map[string]interface{}{"UserIDs": nonMembers}, "", http.StatusBadRequest)
return
}
}
cm, err := c.App.AddChannelMember(member.UserId, channel, c.App.Session.UserId, postRootId, c.App.Session.Id)
if err != nil {
c.Err = err

Просмотреть файл

@@ -2021,6 +2021,30 @@ func TestAddChannelMember(t *testing.T) {
_, resp = Client.AddChannelMember(privateChannel.Id, user3.Id)
CheckNoError(t, resp)
Client.Logout()
// Set a channel to group-constrained
privateChannel.GroupConstrained = model.NewBool(true)
_, appErr := th.App.UpdateChannel(privateChannel)
require.Nil(t, appErr)
// User is not in associated groups so shouldn't be allowed
_, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user.Id)
CheckErrorMessage(t, resp, "api.channel.add_members.user_denied")
// Associate group to team
_, appErr = th.App.CreateGroupSyncable(&model.GroupSyncable{
GroupId: th.Group.Id,
SyncableId: privateChannel.Id,
Type: model.GroupSyncableTypeChannel,
})
require.Nil(t, appErr)
// Add user to group
_, appErr = th.App.CreateOrRestoreGroupMember(th.Group.Id, user.Id)
require.Nil(t, appErr)
_, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user.Id)
CheckNoError(t, resp)
}
func TestAddChannelMemberAddMyself(t *testing.T) {

Просмотреть файл

@@ -388,6 +388,28 @@ func addTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
team, err := c.App.GetTeam(member.TeamId)
if err != nil {
c.Err = err
return
}
if team.GroupConstrained != nil && *team.GroupConstrained {
nonMembers, err := c.App.FilterNonGroupTeamMembers([]string{member.UserId}, team)
if err != nil {
if v, ok := err.(*model.AppError); ok {
c.Err = v
} else {
c.Err = model.NewAppError("addTeamMember", "api.team.add_members.error", nil, err.Error(), http.StatusBadRequest)
}
return
}
if len(nonMembers) > 0 {
c.Err = model.NewAppError("addTeamMember", "api.team.add_members.user_denied", map[string]interface{}{"UserIDs": nonMembers}, "", http.StatusBadRequest)
return
}
}
member, err = c.App.AddTeamMember(member.TeamId, member.UserId)
if err != nil {
@@ -432,11 +454,43 @@ func addTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
var err *model.AppError
members := model.TeamMembersFromJson(r.Body)
if len(members) > MAX_ADD_MEMBERS_BATCH || len(members) == 0 {
if len(members) > MAX_ADD_MEMBERS_BATCH {
c.SetInvalidParam("too many members in batch")
return
}
if len(members) == 0 {
c.SetInvalidParam("no members in batch")
return
}
var memberIDs []string
for _, member := range members {
memberIDs = append(memberIDs, member.UserId)
}
team, err := c.App.GetTeam(c.Params.TeamId)
if err != nil {
c.Err = err
return
}
if team.GroupConstrained != nil && *team.GroupConstrained {
nonMembers, err := c.App.FilterNonGroupTeamMembers(memberIDs, team)
if err != nil {
if v, ok := err.(*model.AppError); ok {
c.Err = v
} else {
c.Err = model.NewAppError("addTeamMembers", "api.team.add_members.error", nil, err.Error(), http.StatusBadRequest)
}
return
}
if len(nonMembers) > 0 {
c.Err = model.NewAppError("addTeamMembers", "api.team.add_members.user_denied", map[string]interface{}{"UserIDs": nonMembers}, "", http.StatusBadRequest)
return
}
}
var userIds []string
for _, member := range members {
if member.TeamId != c.Params.TeamId {

Просмотреть файл

@@ -1442,6 +1442,30 @@ func TestAddTeamMember(t *testing.T) {
if tm != nil {
t.Fatal("should have not returned team member")
}
// Set a team to group-constrained
team.GroupConstrained = model.NewBool(true)
_, err := th.App.UpdateTeam(team)
require.Nil(t, err)
// User is not in associated groups so shouldn't be allowed
_, resp = th.SystemAdminClient.AddTeamMember(team.Id, otherUser.Id)
CheckErrorMessage(t, resp, "api.team.add_members.user_denied")
// Associate group to team
_, err = th.App.CreateGroupSyncable(&model.GroupSyncable{
GroupId: th.Group.Id,
SyncableId: team.Id,
Type: model.GroupSyncableTypeTeam,
})
require.Nil(t, err)
// Add user to group
_, err = th.App.CreateOrRestoreGroupMember(th.Group.Id, otherUser.Id)
require.Nil(t, err)
_, resp = th.SystemAdminClient.AddTeamMember(team.Id, otherUser.Id)
CheckNoError(t, resp)
}
func TestAddTeamMemberMyself(t *testing.T) {
@@ -1578,7 +1602,7 @@ func TestAddTeamMembers(t *testing.T) {
CheckBadRequestStatus(t, resp)
_, resp = Client.AddTeamMembers(GenerateTestId(), userList)
CheckForbiddenStatus(t, resp)
CheckNotFoundStatus(t, resp)
testUserList := append(userList, GenerateTestId())
_, resp = Client.AddTeamMembers(team.Id, testUserList)
@@ -1633,6 +1657,30 @@ func TestAddTeamMembers(t *testing.T) {
// Should work as a regular user.
_, resp = Client.AddTeamMembers(team.Id, userList)
CheckNoError(t, resp)
// Set a team to group-constrained
team.GroupConstrained = model.NewBool(true)
_, err := th.App.UpdateTeam(team)
require.Nil(t, err)
// User is not in associated groups so shouldn't be allowed
_, resp = Client.AddTeamMembers(team.Id, userList)
CheckErrorMessage(t, resp, "api.team.add_members.user_denied")
// Associate group to team
_, err = th.App.CreateGroupSyncable(&model.GroupSyncable{
GroupId: th.Group.Id,
SyncableId: team.Id,
Type: model.GroupSyncableTypeTeam,
})
require.Nil(t, err)
// Add user to group
_, err = th.App.CreateOrRestoreGroupMember(th.Group.Id, userList[0])
require.Nil(t, err)
_, resp = Client.AddTeamMembers(team.Id, userList)
CheckNoError(t, resp)
}
func TestRemoveTeamMember(t *testing.T) {