Add search for list users endpoint (#490)

Search runs on name and email. Some refactoring to make the
functionality more generic.
This commit is contained in:
Zach Wasserman 2021-03-17 17:24:34 -07:00
parent 83d608962a
commit 6f381de04e
7 changed files with 44 additions and 16 deletions

View File

@ -250,43 +250,43 @@ func testListHostsQuery(t *testing.T, ds kolide.Datastore) {
require.Nil(t, err)
assert.Equal(t, len(hosts), len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "00"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "00"}})
require.Nil(t, err)
assert.Equal(t, 10, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "000"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "000"}})
require.Nil(t, err)
assert.Equal(t, 1, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "192.168."})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "192.168."}})
require.Nil(t, err)
assert.Equal(t, 10, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "192.168.1.1"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "192.168.1.1"}})
require.Nil(t, err)
assert.Equal(t, 1, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "hostname%00"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "hostname%00"}})
require.Nil(t, err)
assert.Equal(t, 10, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "hostname%003"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "hostname%003"}})
require.Nil(t, err)
assert.Equal(t, 1, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "uuid_"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "uuid_"}})
require.Nil(t, err)
assert.Equal(t, 10, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "uuid_006"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "uuid_006"}})
require.Nil(t, err)
assert.Equal(t, 1, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "serial"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "serial"}})
require.Nil(t, err)
assert.Equal(t, 10, len(gotHosts))
gotHosts, err = ds.ListHosts(kolide.HostListOptions{MatchQuery: "serial009"})
gotHosts, err = ds.ListHosts(kolide.HostListOptions{ListOptions: kolide.ListOptions{MatchQuery: "serial009"}})
require.Nil(t, err)
assert.Equal(t, 1, len(gotHosts))
}

View File

@ -34,6 +34,7 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){
testCreateUser,
testSaveUser,
testUserByID,
testListUsers,
testPasswordResetRequests,
testSearchHosts,
testSearchHostsLimit,

View File

@ -6,6 +6,7 @@ import (
"github.com/fleetdm/fleet/server/kolide"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func testCreateUser(t *testing.T, ds kolide.Datastore) {
@ -66,6 +67,7 @@ func createTestUsers(t *testing.T, ds kolide.Datastore) []*kolide.User {
for _, tt := range createTests {
u := &kolide.User{
Username: tt.username,
Name: tt.username,
Password: []byte(tt.password),
Admin: tt.isAdmin,
AdminForcedPasswordReset: tt.passwordReset,
@ -125,3 +127,21 @@ func testAdminAttribute(t *testing.T, ds kolide.Datastore, users []*kolide.User)
assert.Equal(t, user.Admin, verify.Admin)
}
}
func testListUsers(t *testing.T, ds kolide.Datastore) {
createTestUsers(t, ds)
users, err := ds.ListUsers(kolide.ListOptions{})
assert.NoError(t, err)
require.Len(t, users, 2)
users, err = ds.ListUsers(kolide.ListOptions{MatchQuery: "jason"})
assert.NoError(t, err)
require.Len(t, users, 1)
assert.Equal(t, "jason@kolide.co", users[0].Email)
users, err = ds.ListUsers(kolide.ListOptions{MatchQuery: "paia"})
assert.NoError(t, err)
require.Len(t, users, 1)
assert.Equal(t, "mike@kolide.co", users[0].Email)
}

View File

@ -8,6 +8,8 @@ import (
"github.com/pkg/errors"
)
var userSearchColumns = []string{"name", "email"}
// NewUser creates a new user
func (d *Datastore) NewUser(user *kolide.User) (*kolide.User, error) {
sqlStatement := `
@ -67,11 +69,14 @@ func (d *Datastore) User(username string) (*kolide.User, error) {
func (d *Datastore) ListUsers(opt kolide.ListOptions) ([]*kolide.User, error) {
sqlStatement := `
SELECT * FROM users
WHERE TRUE
`
sqlStatement, params := searchLike(sqlStatement, nil, opt.MatchQuery, userSearchColumns...)
sqlStatement = appendListOptionsToSQL(sqlStatement, opt)
users := []*kolide.User{}
if err := d.db.Select(&users, sqlStatement); err != nil {
if err := d.db.Select(&users, sqlStatement, params...); err != nil {
return nil, errors.Wrap(err, "list users")
}

View File

@ -296,6 +296,10 @@ type ListOptions struct {
OrderKey string
// Direction of ordering
OrderDirection OrderDirection
// MatchQuery is the query string to match against columns of the entity
// (varies depending on entity, eg. hostname, IP address for hosts).
// Handling for this parameter must be implemented separately for each type.
MatchQuery string
}
// EnrollSecret contains information about an enroll secret, name, and active

View File

@ -93,8 +93,6 @@ type HostListOptions struct {
AdditionalFilters []string
// StatusFilter selects the online status of the hosts.
StatusFilter HostStatus
// MatchQuery is the query string to match in various columns of the host.
MatchQuery string
}
type Host struct {

View File

@ -156,11 +156,14 @@ func listOptionsFromRequest(r *http.Request) (kolide.ListOptions, error) {
orderKey = "detail_update_time"
}
query := r.URL.Query().Get("query")
return kolide.ListOptions{
Page: uint(page),
PerPage: uint(perPage),
OrderKey: orderKey,
OrderDirection: orderDirection,
MatchQuery: query,
}, nil
}
@ -191,9 +194,6 @@ func hostListOptionsFromRequest(r *http.Request) (kolide.HostListOptions, error)
hopt.AdditionalFilters = strings.Split(additionalInfoFiltersString, ",")
}
query := r.URL.Query().Get("query")
hopt.MatchQuery = query
return hopt, nil
}