MM-64330 - filter abac users in channel invite (#31219)
* MM-64330 - filter abac users in channel invite * implement cursor functionality for abac user filtering * remove unnecessary comments * refactor the backend implementation simplifying the functions * refactor api to use opts as parameters, rename function * add missing translation * remove unnecesary test code --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
@@ -880,6 +880,126 @@ func TestGetUsersByStatus(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetUsersNotInAbacChannel(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
// Set license to EnterpriseAdvanced
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("enterprise.advanced"))
|
||||
|
||||
// Enable ABAC in config
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.AccessControlSettings.EnableAttributeBasedAccessControl = true
|
||||
})
|
||||
|
||||
// Create an ABAC channel
|
||||
abacChannel := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||
|
||||
// Create three test users and add them to the team
|
||||
user1 := th.CreateUser() // Will have matching attributes for ABAC
|
||||
user2 := th.CreateUser() // Won't have matching attributes
|
||||
user3 := th.CreateUser() // Won't have matching attributes
|
||||
th.LinkUserToTeam(user1, th.BasicTeam)
|
||||
th.LinkUserToTeam(user2, th.BasicTeam)
|
||||
th.LinkUserToTeam(user3, th.BasicTeam)
|
||||
|
||||
// Create a policy with the same ID as the ABAC channel
|
||||
channelPolicy := &model.AccessControlPolicy{
|
||||
Type: model.AccessControlPolicyTypeChannel,
|
||||
ID: abacChannel.Id,
|
||||
Name: "Test Channel Policy",
|
||||
Revision: 1,
|
||||
Version: model.AccessControlPolicyVersionV0_1,
|
||||
Rules: []model.AccessControlPolicyRule{
|
||||
{
|
||||
Actions: []string{"view", "join_channel"},
|
||||
Expression: "user.attributes.program == \"test-program\"",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Save the channel policy
|
||||
var storeErr error
|
||||
channelPolicy, storeErr = th.App.Srv().Store().AccessControlPolicy().Save(th.Context, channelPolicy)
|
||||
require.NoError(t, storeErr)
|
||||
require.NotNil(t, channelPolicy)
|
||||
t.Cleanup(func() {
|
||||
dErr := th.App.Srv().Store().AccessControlPolicy().Delete(th.Context, channelPolicy.ID)
|
||||
require.NoError(t, dErr)
|
||||
})
|
||||
|
||||
// Mock the AccessControl service
|
||||
mockAccessControl := &mocks.AccessControlServiceInterface{}
|
||||
originalAccessControl := th.App.Srv().ch.AccessControl
|
||||
th.App.Srv().ch.AccessControl = mockAccessControl
|
||||
defer func() {
|
||||
th.App.Srv().ch.AccessControl = originalAccessControl
|
||||
}()
|
||||
|
||||
t.Run("Returns users with matching attributes using cursor pagination", func(t *testing.T) {
|
||||
// Set up the mock to return user1 when querying for users
|
||||
mockAccessControl.On("QueryUsersForResource",
|
||||
mock.Anything,
|
||||
abacChannel.Id,
|
||||
"*",
|
||||
mock.MatchedBy(func(opts model.SubjectSearchOptions) bool {
|
||||
return opts.TeamID == th.BasicTeam.Id &&
|
||||
opts.Limit == 50 &&
|
||||
opts.Cursor.TargetID == ""
|
||||
})).Return([]*model.User{user1}, int64(1), nil).Once()
|
||||
|
||||
// Call the new ABAC-specific function with th.Context as first parameter
|
||||
users, appErr := th.App.GetUsersNotInAbacChannel(th.Context, th.BasicTeam.Id, abacChannel.Id, false, "", 50, true, nil)
|
||||
require.Nil(t, appErr)
|
||||
|
||||
// Create a map of user IDs for easier lookup
|
||||
userMap := make(map[string]bool)
|
||||
for _, u := range users {
|
||||
userMap[u.Id] = true
|
||||
}
|
||||
|
||||
// Verify only user1 is returned
|
||||
assert.True(t, userMap[user1.Id], "User1 should be returned for ABAC channel")
|
||||
assert.False(t, userMap[user2.Id], "User2 should not be returned for ABAC channel")
|
||||
assert.False(t, userMap[user3.Id], "User3 should not be returned for ABAC channel")
|
||||
assert.Len(t, users, 1, "Should return exactly 1 user")
|
||||
})
|
||||
|
||||
t.Run("Works with cursor-based pagination", func(t *testing.T) {
|
||||
cursorID := "some-cursor-id"
|
||||
|
||||
// Set up the mock to return user1 when querying with cursor
|
||||
mockAccessControl.On("QueryUsersForResource",
|
||||
mock.Anything,
|
||||
abacChannel.Id,
|
||||
"*",
|
||||
mock.MatchedBy(func(opts model.SubjectSearchOptions) bool {
|
||||
return opts.TeamID == th.BasicTeam.Id &&
|
||||
opts.Limit == 25 &&
|
||||
opts.Cursor.TargetID == cursorID
|
||||
})).Return([]*model.User{user1}, int64(1), nil).Once()
|
||||
|
||||
// Call with cursor ID and th.Context as first parameter
|
||||
users, appErr := th.App.GetUsersNotInAbacChannel(th.Context, th.BasicTeam.Id, abacChannel.Id, false, cursorID, 25, true, nil)
|
||||
require.Nil(t, appErr)
|
||||
assert.Len(t, users, 1, "Should return exactly 1 user with cursor pagination")
|
||||
})
|
||||
|
||||
t.Run("Returns error when AccessControl service is unavailable", func(t *testing.T) {
|
||||
// Temporarily set AccessControl to nil
|
||||
th.App.Srv().ch.AccessControl = nil
|
||||
defer func() {
|
||||
th.App.Srv().ch.AccessControl = mockAccessControl
|
||||
}()
|
||||
|
||||
// Call should return error with th.Context as first parameter
|
||||
users, appErr := th.App.GetUsersNotInAbacChannel(th.Context, th.BasicTeam.Id, abacChannel.Id, false, "", 50, true, nil)
|
||||
require.NotNil(t, appErr)
|
||||
require.Nil(t, users)
|
||||
assert.Equal(t, "api.user.get_users_not_in_abac_channel.access_control_unavailable.app_error", appErr.Id)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateUserWithInviteId(t *testing.T) {
|
||||
mainHelper.Parallel(t)
|
||||
th := Setup(t).InitBasic()
|
||||
|
||||
Ссылка в новой задаче
Block a user