diff --git a/api4/user.go b/api4/user.go index 1d9f848d81..d3fea45ded 100644 --- a/api4/user.go +++ b/api4/user.go @@ -533,6 +533,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) { groupConstrained := r.URL.Query().Get("group_constrained") withoutTeam := r.URL.Query().Get("without_team") inactive := r.URL.Query().Get("inactive") + active := r.URL.Query().Get("active") role := r.URL.Query().Get("role") sort := r.URL.Query().Get("sort") @@ -560,6 +561,11 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) { withoutTeamBool, _ := strconv.ParseBool(withoutTeam) groupConstrainedBool, _ := strconv.ParseBool(groupConstrained) inactiveBool, _ := strconv.ParseBool(inactive) + activeBool, _ := strconv.ParseBool(active) + + if inactiveBool && activeBool { + c.SetInvalidUrlParam("inactive") + } restrictions, err := c.App.GetViewUsersRestrictions(c.App.Session().UserId) if err != nil { @@ -576,6 +582,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) { GroupConstrained: groupConstrainedBool, WithoutTeam: withoutTeamBool, Inactive: inactiveBool, + Active: activeBool, Role: role, Sort: sort, Page: c.Params.Page, diff --git a/api4/user_test.go b/api4/user_test.go index 092b684399..a9c964657b 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -2245,6 +2245,35 @@ func TestGetRecentlyActiveUsersInTeam(t *testing.T) { CheckUnauthorizedStatus(t, resp) } +func TestGetActiveUsersInTeam(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + teamId := th.BasicTeam.Id + + th.SystemAdminClient.UpdateUserActive(th.BasicUser2.Id, false) + rusers, resp := th.Client.GetActiveUsersInTeam(teamId, 0, 60, "") + CheckNoError(t, resp) + + require.NotZero(t, len(rusers)) + for _, u := range rusers { + require.Zero(t, u.DeleteAt, "should not be deleted") + require.NotEqual(t, th.BasicUser2.Id, "should not include deactivated user") + CheckUserSanitization(t, u) + } + + rusers, resp = th.Client.GetActiveUsersInTeam(teamId, 0, 1, "") + CheckNoError(t, resp) + require.Len(t, rusers, 1, "should be 1 per page") + + // Check case where we have supplied both active and inactive flags + _, err := th.Client.DoApiGet("/users?inactive=true&active=true", "") + require.NotNil(t, err) + + th.Client.Logout() + _, resp = th.Client.GetActiveUsersInTeam(teamId, 0, 1, "") + CheckUnauthorizedStatus(t, resp) +} + func TestGetUsersWithoutTeam(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() diff --git a/model/client4.go b/model/client4.go index 448c9f9ab1..a2491c1799 100644 --- a/model/client4.go +++ b/model/client4.go @@ -939,6 +939,17 @@ func (c *Client4) GetRecentlyActiveUsersInTeam(teamId string, page int, perPage return UserListFromJson(r.Body), BuildResponse(r) } +// GetActiveUsersInTeam returns a page of users on a team. Page counting starts at 0. +func (c *Client4) GetActiveUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?active=true&in_team=%v&page=%v&per_page=%v", teamId, page, perPage) + r, err := c.DoApiGet(c.GetUsersRoute()+query, etag) + if err != nil { + return nil, BuildErrorResponse(r, err) + } + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) +} + // GetUsersNotInTeam returns a page of users who are not in a team. Page counting starts at 0. func (c *Client4) GetUsersNotInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { query := fmt.Sprintf("?not_in_team=%v&page=%v&per_page=%v", teamId, page, perPage) diff --git a/model/user_get.go b/model/user_get.go index e7ce0ae8c9..0d975e3319 100644 --- a/model/user_get.go +++ b/model/user_get.go @@ -20,6 +20,8 @@ type UserGetOptions struct { WithoutTeam bool // Filters the inactive users Inactive bool + // Filters the active users + Active bool // Filters for the given role Role string // Sorting option diff --git a/store/sqlstore/user_store.go b/store/sqlstore/user_store.go index 827946e220..eb297fa568 100644 --- a/store/sqlstore/user_store.go +++ b/store/sqlstore/user_store.go @@ -422,6 +422,8 @@ func (us SqlUserStore) GetAllProfiles(options *model.UserGetOptions) ([]*model.U if options.Inactive { query = query.Where("u.DeleteAt != 0") + } else if options.Active { + query = query.Where("u.DeleteAt = 0") } queryString, args, err := query.ToSql() @@ -526,6 +528,8 @@ func (us SqlUserStore) GetProfiles(options *model.UserGetOptions) ([]*model.User if options.Inactive { query = query.Where("u.DeleteAt != 0") + } else if options.Active { + query = query.Where("u.DeleteAt = 0") } queryString, args, err := query.ToSql() @@ -711,6 +715,8 @@ func (us SqlUserStore) GetProfilesWithoutTeam(options *model.UserGetOptions) ([] if options.Inactive { query = query.Where("u.DeleteAt != 0") + } else if options.Active { + query = query.Where("u.DeleteAt = 0") } queryString, args, err := query.ToSql() diff --git a/store/storetest/user_store.go b/store/storetest/user_store.go index 982be5898d..e25927feb9 100644 --- a/store/storetest/user_store.go +++ b/store/storetest/user_store.go @@ -516,6 +516,36 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) { sanitized(u7), }, actual) }) + + t.Run("filter to active", func(t *testing.T) { + actual, err := ss.User().GetAllProfiles(&model.UserGetOptions{ + Page: 0, + PerPage: 10, + Active: true, + }) + require.Nil(t, err) + require.Equal(t, []*model.User{ + sanitized(u1), + sanitized(u2), + sanitized(u3), + sanitized(u4), + sanitized(u5), + }, actual) + }) + + t.Run("try to filter to active and inactive", func(t *testing.T) { + actual, err := ss.User().GetAllProfiles(&model.UserGetOptions{ + Page: 0, + PerPage: 10, + Inactive: true, + Active: true, + }) + require.Nil(t, err) + require.Equal(t, []*model.User{ + sanitized(u6), + sanitized(u7), + }, actual) + }) } func testUserStoreGetProfiles(t *testing.T, ss store.Store) { @@ -655,6 +685,36 @@ func testUserStoreGetProfiles(t *testing.T, ss store.Store) { sanitized(u5), }, actual) }) + + t.Run("filter to active", func(t *testing.T) { + actual, err := ss.User().GetProfiles(&model.UserGetOptions{ + InTeamId: teamId, + Page: 0, + PerPage: 10, + Active: true, + }) + require.Nil(t, err) + require.Equal(t, []*model.User{ + sanitized(u1), + sanitized(u2), + sanitized(u3), + sanitized(u4), + }, actual) + }) + + t.Run("try to filter to active and inactive", func(t *testing.T) { + actual, err := ss.User().GetProfiles(&model.UserGetOptions{ + InTeamId: teamId, + Page: 0, + PerPage: 10, + Inactive: true, + Active: true, + }) + require.Nil(t, err) + require.Equal(t, []*model.User{ + sanitized(u5), + }, actual) + }) } func testUserStoreGetProfilesInChannel(t *testing.T, ss store.Store) {