diff --git a/server/datastore/datastore_hosts.go b/server/datastore/datastore_hosts.go index 6aed48d5b4..1e652e99fe 100644 --- a/server/datastore/datastore_hosts.go +++ b/server/datastore/datastore_hosts.go @@ -11,10 +11,10 @@ import ( "github.com/WatchBeam/clock" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" "github.com/fleetdm/fleet/server/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) var enrollTests = []struct { @@ -495,7 +495,7 @@ func testSearchHosts(t *testing.T, ds kolide.Datastore) { }) require.Nil(t, err) - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} filter := kolide.TeamFilter{User: user} // We once threw errors when the search query was empty. Verify that we @@ -551,7 +551,7 @@ func testSearchHosts(t *testing.T, ds kolide.Datastore) { } func testSearchHostsLimit(t *testing.T, ds kolide.Datastore) { - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} filter := kolide.TeamFilter{User: user} for i := 0; i < 15; i++ { @@ -939,7 +939,7 @@ func testAddHostsToTeam(t *testing.T, ds kolide.Datastore) { for i := 1; i <= 10; i++ { host, err := ds.Host(uint(i)) require.NoError(t, err) - assert.Equal(t, null.Int{}, host.TeamID) + assert.Nil(t, host.TeamID) } require.NoError(t, ds.AddHostsToTeam(&team1.ID, []uint{1, 2, 3})) @@ -948,12 +948,12 @@ func testAddHostsToTeam(t *testing.T, ds kolide.Datastore) { for i := 1; i <= 10; i++ { host, err := ds.Host(uint(i)) require.NoError(t, err) - expectedID := null.Int{} + var expectedID *uint switch { case i <= 2: - expectedID = null.IntFrom(int64(team1.ID)) + expectedID = &team1.ID case i <= 5: - expectedID = null.IntFrom(int64(team2.ID)) + expectedID = &team2.ID } assert.Equal(t, expectedID, host.TeamID) } @@ -964,10 +964,10 @@ func testAddHostsToTeam(t *testing.T, ds kolide.Datastore) { for i := 1; i <= 10; i++ { host, err := ds.Host(uint(i)) require.NoError(t, err) - expectedID := null.Int{} + var expectedID *uint switch { case i >= 5: - expectedID = null.IntFrom(int64(team1.ID)) + expectedID = &team1.ID } assert.Equal(t, expectedID, host.TeamID) } diff --git a/server/datastore/datastore_labels.go b/server/datastore/datastore_labels.go index ed74463f86..c526da143d 100644 --- a/server/datastore/datastore_labels.go +++ b/server/datastore/datastore_labels.go @@ -8,10 +8,10 @@ import ( "time" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" "github.com/fleetdm/fleet/server/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) func testLabels(t *testing.T, db kolide.Datastore) { @@ -240,7 +240,7 @@ func testSearchLabels(t *testing.T, db kolide.Datastore) { l3, err := db.Label(specs[2].ID) require.Nil(t, err) - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} filter := kolide.TeamFilter{User: user} // We once threw errors when the search query was empty. Verify that we @@ -285,7 +285,7 @@ func testSearchLabelsLimit(t *testing.T, db kolide.Datastore) { require.Nil(t, err) } - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} filter := kolide.TeamFilter{User: user} labels, err := db.SearchLabels(filter, "foo") @@ -357,7 +357,7 @@ func testListHostsInLabel(t *testing.T, db kolide.Datastore) { func testBuiltInLabels(t *testing.T, db kolide.Datastore) { require.Nil(t, db.MigrateData()) - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} filter := kolide.TeamFilter{User: user} hits, err := db.SearchLabels(filter, "macOS") diff --git a/server/datastore/datastore_packs.go b/server/datastore/datastore_packs.go index c1ba78a6d4..e7793d1c31 100644 --- a/server/datastore/datastore_packs.go +++ b/server/datastore/datastore_packs.go @@ -5,6 +5,7 @@ import ( "github.com/WatchBeam/clock" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" "github.com/fleetdm/fleet/server/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -191,9 +192,6 @@ func setupPackSpecsTest(t *testing.T, ds kolide.Datastore) []*kolide.PackSpec { err = ds.ApplyLabelSpecs(labels) require.Nil(t, err) - boolPtr := func(b bool) *bool { return &b } - uintPtr := func(x uint) *uint { return &x } - stringPtr := func(s string) *string { return &s } expectedSpecs := []*kolide.PackSpec{ &kolide.PackSpec{ ID: 1, @@ -216,18 +214,18 @@ func setupPackSpecsTest(t *testing.T, ds kolide.Datastore) []*kolide.PackSpec { QueryName: queries[0].Name, Name: "foo_snapshot", Interval: 600, - Snapshot: boolPtr(true), - Denylist: boolPtr(false), + Snapshot: ptr.Bool(true), + Denylist: ptr.Bool(false), }, kolide.PackSpecQuery{ Name: "q2", QueryName: queries[1].Name, Interval: 600, - Removed: boolPtr(false), - Shard: uintPtr(73), - Platform: stringPtr("foobar"), - Version: stringPtr("0.0.0.0.0.1"), - Denylist: boolPtr(true), + Removed: ptr.Bool(false), + Shard: ptr.Uint(73), + Platform: ptr.String("foobar"), + Version: ptr.String("0.0.0.0.0.1"), + Denylist: ptr.Bool(true), }, }, }, @@ -253,16 +251,16 @@ func setupPackSpecsTest(t *testing.T, ds kolide.Datastore) []*kolide.PackSpec { QueryName: queries[0].Name, Name: "foo_snapshot", Interval: 600, - Snapshot: boolPtr(true), + Snapshot: ptr.Bool(true), }, kolide.PackSpecQuery{ Name: "q2", QueryName: queries[1].Name, Interval: 600, - Removed: boolPtr(false), - Shard: uintPtr(73), - Platform: stringPtr("foobar"), - Version: stringPtr("0.0.0.0.0.1"), + Removed: ptr.Bool(false), + Shard: ptr.Uint(73), + Platform: ptr.String("foobar"), + Version: ptr.String("0.0.0.0.0.1"), }, }, }, diff --git a/server/datastore/datastore_scheduled_queries.go b/server/datastore/datastore_scheduled_queries.go index 3525930f4c..f1341796e1 100644 --- a/server/datastore/datastore_scheduled_queries.go +++ b/server/datastore/datastore_scheduled_queries.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" "github.com/fleetdm/fleet/server/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,11 +20,11 @@ func testListScheduledQueriesInPack(t *testing.T, ds kolide.Datastore) { require.Nil(t, err) specs := []*kolide.PackSpec{ - &kolide.PackSpec{ + { Name: "baz", Targets: kolide.PackSpecTargets{Labels: []string{}}, Queries: []kolide.PackSpecQuery{ - kolide.PackSpecQuery{ + { QueryName: queries[0].Name, Description: "test_foo", Interval: 60, @@ -41,29 +42,28 @@ func testListScheduledQueriesInPack(t *testing.T, ds kolide.Datastore) { assert.Equal(t, "test_foo", gotQueries[0].Description) assert.Equal(t, "select * from foo", gotQueries[0].Query) - boolPtr := func(b bool) *bool { return &b } specs = []*kolide.PackSpec{ - &kolide.PackSpec{ + { Name: "baz", Targets: kolide.PackSpecTargets{Labels: []string{}}, Queries: []kolide.PackSpecQuery{ - kolide.PackSpecQuery{ + { QueryName: queries[0].Name, Description: "test_foo", Interval: 60, }, - kolide.PackSpecQuery{ + { QueryName: queries[1].Name, Name: "test bar", Description: "test_bar", Interval: 60, }, - kolide.PackSpecQuery{ + { QueryName: queries[1].Name, Name: "test bar snapshot", Description: "test_bar", Interval: 60, - Snapshot: boolPtr(true), + Snapshot: ptr.Bool(true), }, }, }, @@ -143,22 +143,22 @@ func testCascadingDeletionOfQueries(t *testing.T, ds kolide.Datastore) { require.Nil(t, err) specs := []*kolide.PackSpec{ - &kolide.PackSpec{ + { Name: "baz", Targets: kolide.PackSpecTargets{Labels: []string{}}, Queries: []kolide.PackSpecQuery{ - kolide.PackSpecQuery{ + { QueryName: queries[0].Name, Description: "test_foo", Interval: 60, }, - kolide.PackSpecQuery{ + { QueryName: queries[1].Name, Name: "test bar", Description: "test_bar", Interval: 60, }, - kolide.PackSpecQuery{ + { QueryName: queries[1].Name, Name: "test bar snapshot", Description: "test_bar", diff --git a/server/datastore/datastore_targets.go b/server/datastore/datastore_targets.go index 17e8367596..9559b648f9 100644 --- a/server/datastore/datastore_targets.go +++ b/server/datastore/datastore_targets.go @@ -7,10 +7,10 @@ import ( "github.com/WatchBeam/clock" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" "github.com/fleetdm/fleet/server/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) func testCountHostsInTargets(t *testing.T, ds kolide.Datastore) { @@ -18,7 +18,7 @@ func testCountHostsInTargets(t *testing.T, ds kolide.Datastore) { t.Skip("inmem is being deprecated, test skipped") } - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} filter := kolide.TeamFilter{User: user} mockClock := clock.NewMockClock() @@ -136,7 +136,7 @@ func testHostStatus(t *testing.T, ds kolide.Datastore) { h, err := ds.EnrollHost("1", "key1", "default", 0) require.Nil(t, err) - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} filter := kolide.TeamFilter{User: user} // Make host no longer appear new @@ -193,7 +193,7 @@ func testHostIDsInTargets(t *testing.T, ds kolide.Datastore) { t.Skip("inmem is being deprecated, test skipped") } - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} filter := kolide.TeamFilter{User: user} hostCount := 0 diff --git a/server/datastore/datastore_users.go b/server/datastore/datastore_users.go index 45120b51c9..65c9c5d894 100644 --- a/server/datastore/datastore_users.go +++ b/server/datastore/datastore_users.go @@ -5,9 +5,9 @@ import ( "testing" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) func testCreateUser(t *testing.T, ds kolide.Datastore) { @@ -117,7 +117,7 @@ func testEmailAttribute(t *testing.T, ds kolide.Datastore, users []*kolide.User) func testUserGlobalRole(t *testing.T, ds kolide.Datastore, users []*kolide.User) { for _, user := range users { - user.GlobalRole = null.StringFrom("admin") + user.GlobalRole = ptr.String("admin") err := ds.SaveUser(user) assert.Nil(t, err) diff --git a/server/datastore/mysql/mysql.go b/server/datastore/mysql/mysql.go index 2f1c6cc1b8..d45f65c721 100644 --- a/server/datastore/mysql/mysql.go +++ b/server/datastore/mysql/mysql.go @@ -353,20 +353,22 @@ func (d *Datastore) whereFilterHostsByTeams(filter kolide.TeamFilter, hostKey st return "FALSE" } - switch filter.User.GlobalRole.String { + if filter.User.GlobalRole != nil { + switch *filter.User.GlobalRole { - case kolide.RoleAdmin, kolide.RoleMaintainer: - return "TRUE" - - case kolide.RoleObserver: - if filter.IncludeObserver { + case kolide.RoleAdmin, kolide.RoleMaintainer: return "TRUE" - } else { - return "FALSE" - } - default: - // Fall through to specific teams + case kolide.RoleObserver: + if filter.IncludeObserver { + return "TRUE" + } else { + return "FALSE" + } + + default: + // Fall through to specific teams + } } // Collect matching teams diff --git a/server/datastore/mysql/mysql_test.go b/server/datastore/mysql/mysql_test.go index 94e732681a..52c2b61a88 100644 --- a/server/datastore/mysql/mysql_test.go +++ b/server/datastore/mysql/mysql_test.go @@ -6,13 +6,13 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/VividCortex/mysqlerr" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" "github.com/go-kit/kit/log" "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) func TestSanitizeColumn(t *testing.T) { @@ -308,25 +308,25 @@ func TestWhereFilterHostsByTeams(t *testing.T) { // Global role { filter: kolide.TeamFilter{ - User: &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)}, + User: &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)}, }, expected: "TRUE", }, { filter: kolide.TeamFilter{ - User: &kolide.User{GlobalRole: null.StringFrom(kolide.RoleMaintainer)}, + User: &kolide.User{GlobalRole: ptr.String(kolide.RoleMaintainer)}, }, expected: "TRUE", }, { filter: kolide.TeamFilter{ - User: &kolide.User{GlobalRole: null.StringFrom(kolide.RoleObserver)}, + User: &kolide.User{GlobalRole: ptr.String(kolide.RoleObserver)}, }, expected: "FALSE", }, { filter: kolide.TeamFilter{ - User: &kolide.User{GlobalRole: null.StringFrom(kolide.RoleObserver)}, + User: &kolide.User{GlobalRole: ptr.String(kolide.RoleObserver)}, IncludeObserver: true, }, expected: "TRUE", diff --git a/server/kolide/hosts.go b/server/kolide/hosts.go index 659a2aee5a..4d269b1545 100644 --- a/server/kolide/hosts.go +++ b/server/kolide/hosts.go @@ -6,8 +6,6 @@ import ( "encoding/base64" "encoding/json" "time" - - "gopkg.in/guregu/null.v3" ) type HostStatus string @@ -155,7 +153,7 @@ type Host struct { Additional *json.RawMessage `json:"additional,omitempty" db:"additional"` EnrollSecretName string `json:"enroll_secret_name" db:"enroll_secret_name"` - TeamID null.Int `json:"team_id" db:"team_id"` + TeamID *uint `json:"team_id" db:"team_id"` // TeamName is the name of the team, loaded by JOIN to the teams table. TeamName *string `json:"team_name" db:"team_name"` // Loaded via JOIN in DB diff --git a/server/kolide/users.go b/server/kolide/users.go index 14d9361d92..20acf4632d 100644 --- a/server/kolide/users.go +++ b/server/kolide/users.go @@ -7,7 +7,6 @@ import ( "fmt" "golang.org/x/crypto/bcrypt" - "gopkg.in/guregu/null.v3" ) // UserStore contains methods for managing users in a datastore @@ -101,8 +100,8 @@ type User struct { GravatarURL string `json:"gravatar_url" db:"gravatar_url"` Position string `json:"position,omitempty"` // job role // SSOEnabled if true, the user may only log in via SSO - SSOEnabled bool `json:"sso_enabled" db:"sso_enabled"` - GlobalRole null.String `json:"global_role" db:"global_role"` + SSOEnabled bool `json:"sso_enabled" db:"sso_enabled"` + GlobalRole *string `json:"global_role" db:"global_role"` // Teams is the teams this user has roles in. Teams []UserTeam `json:"teams"` @@ -171,7 +170,7 @@ func (p UserPayload) User(keySize, cost int) (*User, error) { user.Teams = *p.Teams } if p.GlobalRole != nil { - user.GlobalRole = null.StringFrom(*p.GlobalRole) + user.GlobalRole = p.GlobalRole } return user, nil diff --git a/server/ptr/ptr.go b/server/ptr/ptr.go new file mode 100644 index 0000000000..76cd25c329 --- /dev/null +++ b/server/ptr/ptr.go @@ -0,0 +1,22 @@ +// Package ptr includes functions for creating pointers from values. +package ptr + +// String returns a pointer to the provided string. +func String(x string) *string { + return &x +} + +// Int returns a pointer to the provided int. +func Int(x int) *int { + return &x +} + +// Uint returns a pointer to the provided uint. +func Uint(x uint) *uint { + return &x +} + +// Bool returns a pointer to the provided bool. +func Bool(x bool) *bool { + return &x +} diff --git a/server/service/service_agent_options.go b/server/service/service_agent_options.go index daeea79fe5..a367933d64 100644 --- a/server/service/service_agent_options.go +++ b/server/service/service_agent_options.go @@ -10,8 +10,8 @@ import ( func (svc service) AgentOptionsForHost(ctx context.Context, host *kolide.Host) (json.RawMessage, error) { // If host has a team and team has non-empty options, prioritize that. - if host.TeamID.Valid { - team, err := svc.ds.Team(uint(host.TeamID.Int64)) + if host.TeamID != nil { + team, err := svc.ds.Team(*host.TeamID) if err != nil { return nil, errors.Wrap(err, "load team for host") } diff --git a/server/service/service_agent_options_test.go b/server/service/service_agent_options_test.go index 1a396d5e8b..3f75d05fed 100644 --- a/server/service/service_agent_options_test.go +++ b/server/service/service_agent_options_test.go @@ -9,7 +9,6 @@ import ( "github.com/fleetdm/fleet/server/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) func TestAgentOptionsForHost(t *testing.T) { @@ -28,7 +27,7 @@ func TestAgentOptionsForHost(t *testing.T) { } host := &kolide.Host{ - TeamID: null.IntFrom(int64(teamID)), + TeamID: &teamID, Platform: "darwin", } @@ -42,7 +41,7 @@ func TestAgentOptionsForHost(t *testing.T) { assert.JSONEq(t, `{"foo":"bar"}`, string(opt)) // Should take gobal option with no team - host.TeamID.Valid = false + host.TeamID = nil opt, err = svc.AgentOptionsForHost(context.Background(), host) require.NoError(t, err) assert.JSONEq(t, `{"baz":"bar"}`, string(opt)) diff --git a/server/service/service_appconfig_test.go b/server/service/service_appconfig_test.go index cd3da50dba..7cb54c0c33 100644 --- a/server/service/service_appconfig_test.go +++ b/server/service/service_appconfig_test.go @@ -6,6 +6,7 @@ import ( "github.com/fleetdm/fleet/server/kolide" "github.com/fleetdm/fleet/server/mock" + "github.com/fleetdm/fleet/server/ptr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -45,12 +46,12 @@ func TestCreateAppConfig(t *testing.T) { { configPayload: kolide.AppConfigPayload{ OrgInfo: &kolide.OrgInfo{ - OrgLogoURL: stringPtr("acme.co/images/logo.png"), - OrgName: stringPtr("Acme"), + OrgLogoURL: ptr.String("acme.co/images/logo.png"), + OrgName: ptr.String("Acme"), }, ServerSettings: &kolide.ServerSettings{ - KolideServerURL: stringPtr("https://acme.co:8080/"), - LiveQueryDisabled: boolPtr(true), + KolideServerURL: ptr.String("https://acme.co:8080/"), + LiveQueryDisabled: ptr.Bool(true), }, }, }, diff --git a/server/service/service_invites_test.go b/server/service/service_invites_test.go index 468715d44e..d387ca07c4 100644 --- a/server/service/service_invites_test.go +++ b/server/service/service_invites_test.go @@ -10,6 +10,7 @@ import ( "github.com/fleetdm/fleet/server/contexts/viewer" "github.com/fleetdm/fleet/server/kolide" "github.com/fleetdm/fleet/server/mock" + "github.com/fleetdm/fleet/server/ptr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -34,7 +35,7 @@ func TestInviteNewUserMock(t *testing.T) { ctx := context.Background() ctx = viewer.NewContext(ctx, viewer.Viewer{User: &kolide.User{ID: 3}}) payload := kolide.InvitePayload{ - Email: stringPtr("user@acme.co"), + Email: ptr.String("user@acme.co"), } // happy path diff --git a/server/service/service_targets_test.go b/server/service/service_targets_test.go index eedd9ea6b8..b54bd9383d 100644 --- a/server/service/service_targets_test.go +++ b/server/service/service_targets_test.go @@ -7,9 +7,9 @@ import ( "github.com/fleetdm/fleet/server/contexts/viewer" "github.com/fleetdm/fleet/server/kolide" "github.com/fleetdm/fleet/server/mock" + "github.com/fleetdm/fleet/server/ptr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) func TestSearchTargets(t *testing.T) { @@ -17,7 +17,7 @@ func TestSearchTargets(t *testing.T) { svc, err := newTestService(ds, nil, nil) require.Nil(t, err) - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} ctx := viewer.NewContext(context.Background(), viewer.Viewer{User: user}) hosts := []*kolide.Host{ @@ -50,7 +50,7 @@ func TestSearchWithOmit(t *testing.T) { svc, err := newTestService(ds, nil, nil) require.Nil(t, err) - user := &kolide.User{GlobalRole: null.StringFrom(kolide.RoleAdmin)} + user := &kolide.User{GlobalRole: ptr.String(kolide.RoleAdmin)} ctx := viewer.NewContext(context.Background(), viewer.Viewer{User: user}) ds.SearchHostsFunc = func(filter kolide.TeamFilter, query string, omit ...uint) ([]*kolide.Host, error) { diff --git a/server/service/service_users.go b/server/service/service_users.go index 567bfb6dcf..c98f19dc21 100644 --- a/server/service/service_users.go +++ b/server/service/service_users.go @@ -11,7 +11,6 @@ import ( "github.com/fleetdm/fleet/server/kolide" "github.com/fleetdm/fleet/server/mail" "github.com/pkg/errors" - "gopkg.in/guregu/null.v3" ) func (svc service) CreateUserWithInvite(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) { @@ -84,7 +83,7 @@ func (svc service) ModifyUser(ctx context.Context, userID uint, p kolide.UserPay user.Name = *p.Name } - if p.Email != nil { + if p.Email != nil && *p.Email != user.Email { err = svc.modifyEmailAddress(ctx, user, *p.Email, p.Password) if err != nil { return nil, err @@ -103,18 +102,15 @@ func (svc service) ModifyUser(ctx context.Context, userID uint, p kolide.UserPay user.SSOEnabled = *p.SSOEnabled } - if p.Teams != nil { - if p.GlobalRole != nil { + if p.GlobalRole != nil && *p.GlobalRole != "" { + if p.Teams != nil && len(*p.Teams) > 0 { return nil, newInvalidArgumentError("teams", "may not be specified with global_role") } - - user.Teams = *p.Teams - user.GlobalRole = null.StringFromPtr(nil) - } - - if p.GlobalRole != nil { - user.GlobalRole = null.StringFrom(*p.GlobalRole) + user.GlobalRole = p.GlobalRole user.Teams = []kolide.UserTeam{} + } else if p.Teams != nil { + user.Teams = *p.Teams + user.GlobalRole = nil } err = svc.saveUser(user) diff --git a/server/service/service_users_test.go b/server/service/service_users_test.go index 75529a7737..5fa32583d7 100644 --- a/server/service/service_users_test.go +++ b/server/service/service_users_test.go @@ -10,6 +10,7 @@ import ( "github.com/fleetdm/fleet/server/contexts/viewer" "github.com/fleetdm/fleet/server/datastore/inmem" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" "github.com/WatchBeam/clock" "github.com/fleetdm/fleet/server/mock" @@ -74,9 +75,9 @@ func TestModifyUserEmail(t *testing.T) { ctx := context.Background() ctx = viewer.NewContext(ctx, viewer.Viewer{User: user}) payload := kolide.UserPayload{ - Email: stringPtr("zip@zap.com"), - Password: stringPtr("password"), - Position: stringPtr("minion"), + Email: ptr.String("zip@zap.com"), + Password: ptr.String("password"), + Position: ptr.String("minion"), } _, err = svc.ModifyUser(ctx, 3, payload) require.Nil(t, err) @@ -116,9 +117,9 @@ func TestModifyUserEmailNoPassword(t *testing.T) { ctx := context.Background() ctx = viewer.NewContext(ctx, viewer.Viewer{User: user}) payload := kolide.UserPayload{ - Email: stringPtr("zip@zap.com"), + Email: ptr.String("zip@zap.com"), // NO PASSWORD - // Password: stringPtr("password"), + // Password: ptr.String("password"), } _, err = svc.ModifyUser(ctx, 3, payload) require.NotNil(t, err) @@ -162,9 +163,9 @@ func TestModifyAdminUserEmailNoPassword(t *testing.T) { ctx := context.Background() ctx = viewer.NewContext(ctx, viewer.Viewer{User: user}) payload := kolide.UserPayload{ - Email: stringPtr("zip@zap.com"), + Email: ptr.String("zip@zap.com"), // NO PASSWORD - // Password: stringPtr("password"), + // Password: ptr.String("password"), } _, err = svc.ModifyUser(ctx, 3, payload) require.NotNil(t, err) @@ -208,8 +209,8 @@ func TestModifyAdminUserEmailPassword(t *testing.T) { ctx := context.Background() ctx = viewer.NewContext(ctx, viewer.Viewer{User: user}) payload := kolide.UserPayload{ - Email: stringPtr("zip@zap.com"), - Password: stringPtr("password"), + Email: ptr.String("zip@zap.com"), + Password: ptr.String("password"), } _, err = svc.ModifyUser(ctx, 3, payload) require.Nil(t, err) @@ -308,46 +309,46 @@ func TestCreateUserWithInvite(t *testing.T) { wantErr error }{ { - Username: stringPtr("admin2"), - Password: stringPtr("foobarbaz1234!"), + Username: ptr.String("admin2"), + Password: ptr.String("foobarbaz1234!"), InviteToken: &invites["admin2@example.com"].Token, wantErr: &invalidArgumentError{invalidArgument{name: "email", reason: "missing required argument"}}, }, { - Username: stringPtr("admin2"), - Password: stringPtr("foobarbaz1234!"), - Email: stringPtr("admin2@example.com"), + Username: ptr.String("admin2"), + Password: ptr.String("foobarbaz1234!"), + Email: ptr.String("admin2@example.com"), wantErr: &invalidArgumentError{invalidArgument{name: "invite_token", reason: "missing required argument"}}, }, { - Username: stringPtr("admin2"), - Password: stringPtr("foobarbaz1234!"), - Email: stringPtr("admin2@example.com"), - NeedsPasswordReset: boolPtr(true), + Username: ptr.String("admin2"), + Password: ptr.String("foobarbaz1234!"), + Email: ptr.String("admin2@example.com"), + NeedsPasswordReset: ptr.Bool(true), InviteToken: &invites["admin2@example.com"].Token, }, { // should return ErrNotFound because the invite is deleted // after a user signs up - Username: stringPtr("admin2"), - Password: stringPtr("foobarbaz1234!"), - Email: stringPtr("admin2@example.com"), - NeedsPasswordReset: boolPtr(true), + Username: ptr.String("admin2"), + Password: ptr.String("foobarbaz1234!"), + Email: ptr.String("admin2@example.com"), + NeedsPasswordReset: ptr.Bool(true), InviteToken: &invites["admin2@example.com"].Token, wantErr: errors.New("Invite with token admin2@example.com was not found in the datastore"), }, { - Username: stringPtr("admin3"), - Password: stringPtr("foobarbaz1234!"), + Username: ptr.String("admin3"), + Password: ptr.String("foobarbaz1234!"), Email: &invites["expired"].Email, - NeedsPasswordReset: boolPtr(true), + NeedsPasswordReset: ptr.Bool(true), InviteToken: &invites["expired"].Token, wantErr: &invalidArgumentError{{name: "invite_token", reason: "Invite token has expired."}}, }, { - Username: stringPtr("admin3@example.com"), - Password: stringPtr("foobarbaz1234!"), - Email: stringPtr("admin3@example.com"), - NeedsPasswordReset: boolPtr(true), + Username: ptr.String("admin3@example.com"), + Password: ptr.String("foobarbaz1234!"), + Email: ptr.String("admin3@example.com"), + NeedsPasswordReset: ptr.Bool(true), InviteToken: &invites["admin3@example.com"].Token, }, } diff --git a/server/service/util_test.go b/server/service/util_test.go index 34ea6703a9..8e8e65271d 100644 --- a/server/service/util_test.go +++ b/server/service/util_test.go @@ -6,9 +6,9 @@ import ( "github.com/WatchBeam/clock" "github.com/fleetdm/fleet/server/config" "github.com/fleetdm/fleet/server/kolide" + "github.com/fleetdm/fleet/server/ptr" kitlog "github.com/go-kit/kit/log" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v3" ) func newTestService(ds kolide.Datastore, rs kolide.QueryResultStore, lq kolide.LiveQueryStore) (kolide.Service, error) { @@ -65,25 +65,25 @@ var testUsers = map[string]struct { Username string Email string PlaintextPassword string - GlobalRole null.String + GlobalRole *string }{ "admin1": { Username: "admin1", PlaintextPassword: "foobarbaz1234!", Email: "admin1@example.com", - GlobalRole: null.StringFrom("admin"), + GlobalRole: ptr.String(kolide.RoleAdmin), }, "user1": { Username: "user1", PlaintextPassword: "foobarbaz1234!", Email: "user1@example.com", - GlobalRole: null.StringFrom("maintainer"), + GlobalRole: ptr.String(kolide.RoleMaintainer), }, "user2": { Username: "user2", PlaintextPassword: "bazfoo1234!", Email: "user2@example.com", - GlobalRole: null.StringFrom("maintainer"), + GlobalRole: ptr.String(kolide.RoleObserver), }, } @@ -96,11 +96,3 @@ func (svc *mockMailService) SendEmail(e kolide.Email) error { svc.Invoked = true return svc.SendEmailFn(e) } - -func stringPtr(s string) *string { - return &s -} - -func boolPtr(b bool) *bool { - return &b -}