* adds team member data sanitizing (#35562) * adds team member data sanitizing * assert using require * adds data sanitizing to team members for user endpoint * team admin data visibility now tests with different user (cherry picked from commit 2be57a7ec0c67004b77c76386f20a630920196e3) * removes wrong argument in test helper calls * fix: add explicit permission grant in team members test (#36007) * fix: add explicit permission grant in team members test TestGetTeamMembersForUserRoleDataSanitization was relying on a permission side-effect leaked from concurrent tests. Under fullyparallel, another test temporarily adds PermissionReadOtherUsersTeams to system_user role, which the team admin subtest accidentally benefits from. Under sequential execution (binary parameters mode), no concurrent test leaks this permission, so the team admin correctly gets 403. Fix by explicitly granting ReadOtherUsersTeams in the subtest setup, matching the pattern used in adjacent subtests. Release Note NONE Co-authored-by: Claude <claude@anthropic.com> * fix: remove explanatory comment per review feedback --------- Co-authored-by: Claude <claude@anthropic.com> * removes extra arg from test helper call --------- Co-authored-by: Carlos Garcia <carlos.garcia@mattermost.com> Co-authored-by: Pavel Zeman <pavel.zeman@mattermost.com> Co-authored-by: Claude <claude@anthropic.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
787fca6a08
Коммит
610a28e9fa
@@ -604,6 +604,10 @@ func getTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionManageTeamRoles) {
|
||||
team.SanitizeRoleData(c.AppContext.Session().UserId)
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(team); err != nil {
|
||||
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
||||
}
|
||||
@@ -642,6 +646,13 @@ func getTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
currentUserId := c.AppContext.Session().UserId
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionManageTeamRoles) {
|
||||
for _, m := range members {
|
||||
m.SanitizeRoleData(currentUserId)
|
||||
}
|
||||
}
|
||||
|
||||
js, err := json.Marshal(members)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("getTeamMembers", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
@@ -681,6 +692,13 @@ func getTeamMembersForUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
currentUserId := c.AppContext.Session().UserId
|
||||
for _, m := range members {
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), m.TeamId, model.PermissionManageTeamRoles) {
|
||||
m.SanitizeRoleData(currentUserId)
|
||||
}
|
||||
}
|
||||
|
||||
js, err := json.Marshal(members)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("getTeamMembersForUser", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
@@ -724,6 +742,13 @@ func getTeamMembersByIds(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
currentUserId := c.AppContext.Session().UserId
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionManageTeamRoles) {
|
||||
for _, m := range members {
|
||||
m.SanitizeRoleData(currentUserId)
|
||||
}
|
||||
}
|
||||
|
||||
js, err := json.Marshal(members)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("getTeamMembersByIds", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
@@ -830,6 +855,10 @@ func addTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.AddEventObjectType("team_member") // TODO verify this is the final state. should it be the team instead?
|
||||
auditRec.Success()
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionManageTeamRoles) {
|
||||
tm.SanitizeRoleData(c.AppContext.Session().UserId)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
if err := json.NewEncoder(w).Encode(tm); err != nil {
|
||||
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
||||
@@ -984,6 +1013,15 @@ func addTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
currentUserId := c.AppContext.Session().UserId
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionManageTeamRoles) {
|
||||
for _, m := range membersWithErrors {
|
||||
if m.Member != nil {
|
||||
m.Member.SanitizeRoleData(currentUserId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
js []byte
|
||||
err error
|
||||
|
||||
@@ -4574,3 +4574,251 @@ func TestInvalidateAllEmailInvites(t *testing.T) {
|
||||
CheckOKStatus(t, res)
|
||||
})
|
||||
}
|
||||
|
||||
func setupTeamWithAdminAndMember(t *testing.T, th *TestHelper) *model.Client4 {
|
||||
t.Helper()
|
||||
th.UpdateUserToTeamAdmin(th.BasicUser2, th.BasicTeam)
|
||||
require.Nil(t, th.App.Srv().InvalidateAllCaches())
|
||||
teamAdminClient := th.CreateClient()
|
||||
_, _, err := teamAdminClient.Login(context.Background(), th.BasicUser2.Email, th.BasicUser2.Password)
|
||||
require.NoError(t, err)
|
||||
return teamAdminClient
|
||||
}
|
||||
|
||||
func assertRoleDataSanitized(t *testing.T, m *model.TeamMember) {
|
||||
t.Helper()
|
||||
assert.Empty(t, m.Roles)
|
||||
assert.Empty(t, m.ExplicitRoles)
|
||||
assert.False(t, m.SchemeAdmin)
|
||||
assert.False(t, m.SchemeGuest)
|
||||
assert.False(t, m.SchemeUser)
|
||||
assert.Equal(t, int64(-1), m.DeleteAt)
|
||||
}
|
||||
|
||||
func TestGetTeamMembersRoleDataSanitization(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := Setup(t).InitBasic()
|
||||
teamAdminClient := setupTeamWithAdminAndMember(t, th)
|
||||
|
||||
t.Run("non-admin cannot see role data of others", func(t *testing.T) {
|
||||
members, _, err := th.Client.GetTeamMembers(context.Background(), th.BasicTeam.Id, 0, 100, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, m := range members {
|
||||
if m.UserId != th.BasicUser.Id {
|
||||
assertRoleDataSanitized(t, m)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("non-admin sees own role data", func(t *testing.T) {
|
||||
members, _, err := th.Client.GetTeamMembers(context.Background(), th.BasicTeam.Id, 0, 100, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, m := range members {
|
||||
if m.UserId == th.BasicUser.Id {
|
||||
assert.True(t, m.SchemeUser)
|
||||
return
|
||||
}
|
||||
}
|
||||
require.Fail(t, "current user not found in members")
|
||||
})
|
||||
|
||||
t.Run("team admin sees full role data for other user", func(t *testing.T) {
|
||||
members, _, err := teamAdminClient.GetTeamMembers(context.Background(), th.BasicTeam.Id, 0, 100, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, m := range members {
|
||||
if m.UserId == th.BasicUser.Id {
|
||||
assert.True(t, m.SchemeUser)
|
||||
return
|
||||
}
|
||||
}
|
||||
require.Fail(t, "target user not found in members")
|
||||
})
|
||||
|
||||
t.Run("system admin sees full role data", func(t *testing.T) {
|
||||
members, _, err := th.SystemAdminClient.GetTeamMembers(context.Background(), th.BasicTeam.Id, 0, 100, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, m := range members {
|
||||
if m.UserId == th.BasicUser2.Id {
|
||||
assert.True(t, m.SchemeAdmin)
|
||||
return
|
||||
}
|
||||
}
|
||||
require.Fail(t, "team admin not found in members")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTeamMemberRoleDataSanitization(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := Setup(t).InitBasic()
|
||||
teamAdminClient := setupTeamWithAdminAndMember(t, th)
|
||||
|
||||
t.Run("non-admin cannot see role data of others", func(t *testing.T) {
|
||||
member, _, err := th.Client.GetTeamMember(context.Background(), th.BasicTeam.Id, th.BasicUser2.Id, "")
|
||||
require.NoError(t, err)
|
||||
assertRoleDataSanitized(t, member)
|
||||
})
|
||||
|
||||
t.Run("non-admin sees own role data", func(t *testing.T) {
|
||||
member, _, err := th.Client.GetTeamMember(context.Background(), th.BasicTeam.Id, th.BasicUser.Id, "")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, member.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("team admin sees full role data for other user", func(t *testing.T) {
|
||||
member, _, err := teamAdminClient.GetTeamMember(context.Background(), th.BasicTeam.Id, th.BasicUser.Id, "")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, member.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("system admin sees full role data", func(t *testing.T) {
|
||||
member, _, err := th.SystemAdminClient.GetTeamMember(context.Background(), th.BasicTeam.Id, th.BasicUser2.Id, "")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, member.SchemeAdmin)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTeamMembersByIdsRoleDataSanitization(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := Setup(t).InitBasic()
|
||||
teamAdminClient := setupTeamWithAdminAndMember(t, th)
|
||||
|
||||
t.Run("non-admin cannot see role data of others", func(t *testing.T) {
|
||||
members, _, err := th.Client.GetTeamMembersByIds(context.Background(), th.BasicTeam.Id, []string{th.BasicUser2.Id})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, members, 1)
|
||||
assertRoleDataSanitized(t, members[0])
|
||||
})
|
||||
|
||||
t.Run("non-admin sees own role data", func(t *testing.T) {
|
||||
members, _, err := th.Client.GetTeamMembersByIds(context.Background(), th.BasicTeam.Id, []string{th.BasicUser.Id})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, members, 1)
|
||||
assert.True(t, members[0].SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("team admin sees full role data for other user", func(t *testing.T) {
|
||||
members, _, err := teamAdminClient.GetTeamMembersByIds(context.Background(), th.BasicTeam.Id, []string{th.BasicUser.Id})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, members, 1)
|
||||
assert.True(t, members[0].SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("system admin sees full role data", func(t *testing.T) {
|
||||
members, _, err := th.SystemAdminClient.GetTeamMembersByIds(context.Background(), th.BasicTeam.Id, []string{th.BasicUser2.Id})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, members, 1)
|
||||
assert.True(t, members[0].SchemeAdmin)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddTeamMemberRoleDataSanitization(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := Setup(t).InitBasic()
|
||||
teamAdminClient := setupTeamWithAdminAndMember(t, th)
|
||||
|
||||
t.Run("team admin adding user sees full role data in response", func(t *testing.T) {
|
||||
newUser := th.CreateUser()
|
||||
tm, _, err := teamAdminClient.AddTeamMember(context.Background(), th.BasicTeam.Id, newUser.Id)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, tm.SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("non-admin adding user sees sanitized role data in response", func(t *testing.T) {
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
th.AddPermissionToRole(model.PermissionAddUserToTeam.Id, model.TeamUserRoleId)
|
||||
|
||||
newUser := th.CreateUser()
|
||||
tm, _, err := th.Client.AddTeamMember(context.Background(), th.BasicTeam.Id, newUser.Id)
|
||||
require.NoError(t, err)
|
||||
assertRoleDataSanitized(t, tm)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddTeamMembersRoleDataSanitization(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := Setup(t).InitBasic()
|
||||
teamAdminClient := setupTeamWithAdminAndMember(t, th)
|
||||
|
||||
t.Run("team admin adding users sees full role data in response", func(t *testing.T) {
|
||||
newUser := th.CreateUser()
|
||||
members, _, err := teamAdminClient.AddTeamMembers(context.Background(), th.BasicTeam.Id, []string{newUser.Id})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, members, 1)
|
||||
assert.True(t, members[0].SchemeUser)
|
||||
})
|
||||
|
||||
t.Run("non-admin adding users sees sanitized role data in response", func(t *testing.T) {
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
th.AddPermissionToRole(model.PermissionAddUserToTeam.Id, model.TeamUserRoleId)
|
||||
|
||||
newUser := th.CreateUser()
|
||||
members, _, err := th.Client.AddTeamMembers(context.Background(), th.BasicTeam.Id, []string{newUser.Id})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, members, 1)
|
||||
assertRoleDataSanitized(t, members[0])
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetTeamMembersForUserRoleDataSanitization(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := Setup(t).InitBasic()
|
||||
teamAdminClient := setupTeamWithAdminAndMember(t, th)
|
||||
|
||||
t.Run("user sees own role data", func(t *testing.T) {
|
||||
members, _, err := th.Client.GetTeamMembersForUser(context.Background(), th.BasicUser.Id, "")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, members)
|
||||
for _, m := range members {
|
||||
assert.True(t, m.SchemeUser)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("non-admin cannot see role data of another user", func(t *testing.T) {
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
th.AddPermissionToRole(model.PermissionReadOtherUsersTeams.Id, model.SystemUserRoleId)
|
||||
|
||||
members, _, err := th.Client.GetTeamMembersForUser(context.Background(), th.BasicUser2.Id, "")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, members)
|
||||
for _, m := range members {
|
||||
assertRoleDataSanitized(t, m)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("team admin sees full role data for other user in managed team", func(t *testing.T) {
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
th.AddPermissionToRole(model.PermissionReadOtherUsersTeams.Id, model.SystemUserRoleId)
|
||||
|
||||
members, _, err := teamAdminClient.GetTeamMembersForUser(context.Background(), th.BasicUser.Id, "")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, members)
|
||||
for _, m := range members {
|
||||
if m.TeamId == th.BasicTeam.Id {
|
||||
assert.True(t, m.SchemeUser)
|
||||
return
|
||||
}
|
||||
}
|
||||
require.Fail(t, "basic team membership not found")
|
||||
})
|
||||
|
||||
t.Run("system admin sees full role data", func(t *testing.T) {
|
||||
members, _, err := th.SystemAdminClient.GetTeamMembersForUser(context.Background(), th.BasicUser2.Id, "")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, members)
|
||||
for _, m := range members {
|
||||
if m.TeamId == th.BasicTeam.Id {
|
||||
assert.True(t, m.SchemeAdmin)
|
||||
return
|
||||
}
|
||||
}
|
||||
require.Fail(t, "basic team membership not found")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -142,3 +142,14 @@ func (o *TeamMember) PreUpdate() {
|
||||
func (o *TeamMember) GetRoles() []string {
|
||||
return strings.Fields(o.Roles)
|
||||
}
|
||||
|
||||
func (o *TeamMember) SanitizeRoleData(currentUserId string) {
|
||||
if o.UserId != currentUserId {
|
||||
o.Roles = ""
|
||||
o.ExplicitRoles = ""
|
||||
o.SchemeAdmin = false
|
||||
o.SchemeGuest = false
|
||||
o.SchemeUser = false
|
||||
o.DeleteAt = -1
|
||||
}
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user