From 6b8abc32bf2ebc1f691e0e4bb641e282f86c28d1 Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Thu, 13 May 2021 13:51:22 -0400 Subject: [PATCH] update login / register screens --- cmd/drone-server/inject_service.go | 1 - core/user.go | 13 ++++++++++++ go.mod | 2 +- go.sum | 2 ++ handler/web/login.go | 13 +++++++++--- metric/sink/config.go | 2 -- metric/sink/datadog.go | 7 ++++++- metric/sink/datadog_test.go | 4 +++- metric/sink/tags.go | 20 ++++++++++++------ mock/mock_gen.go | 15 ++++++++++++++ store/user/user.go | 33 ++++++++++++++++++++++++++++++ 11 files changed, 97 insertions(+), 15 deletions(-) diff --git a/cmd/drone-server/inject_service.go b/cmd/drone-server/inject_service.go index 2fd13de3..ceb47cd8 100644 --- a/cmd/drone-server/inject_service.go +++ b/cmd/drone-server/inject_service.go @@ -211,7 +211,6 @@ func provideDatadog( sink.Config{ Endpoint: config.Datadog.Endpoint, Token: config.Datadog.Token, - Contact2: config.Server.Email, License: license.Kind, Licensor: license.Licensor, Subscription: license.Subscription, diff --git a/core/user.go b/core/user.go index 21f6d0b8..c4d62ae7 100644 --- a/core/user.go +++ b/core/user.go @@ -47,6 +47,16 @@ type ( Hash string `json:"-"` } + // UserParams defines user query parameters. + UserParams struct { + // Sort instructs the system to sort by Login if true, + // else sort by primary key. + Sort bool + + Page int64 + Size int64 + } + // UserStore defines operations for working with users. UserStore interface { // Find returns a user from the datastore. @@ -61,6 +71,9 @@ type ( // List returns a list of users from the datastore. List(context.Context) ([]*User, error) + // ListRange returns a range of users from the datastore. + ListRange(context.Context, UserParams) ([]*User, error) + // Create persists a new user to the datastore. Create(context.Context, *User) error diff --git a/go.mod b/go.mod index c2ff707e..6598af1f 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/drone/drone-go v1.4.1-0.20201109202657-b9e58bbbcf27 github.com/drone/drone-runtime v1.1.1-0.20200623162453-61e33e2cab5d - github.com/drone/drone-ui v0.0.0-20210505020539-1b93fc25c7d8 + github.com/drone/drone-ui v0.0.0-20210512200715-d96f1e26d448 github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4 github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629 github.com/drone/go-license v1.0.2 diff --git a/go.sum b/go.sum index 3e3fed1e..8c89fe74 100644 --- a/go.sum +++ b/go.sum @@ -116,6 +116,8 @@ github.com/drone/drone-ui v0.0.0-20210427231613-96a9f6e17fb7 h1:EUvu5J51N2diuFqL github.com/drone/drone-ui v0.0.0-20210427231613-96a9f6e17fb7/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI= github.com/drone/drone-ui v0.0.0-20210505020539-1b93fc25c7d8 h1:mIFBOdP8Tif/4li4bmiVjqp6vbxMpTHOuZR80eypm6A= github.com/drone/drone-ui v0.0.0-20210505020539-1b93fc25c7d8/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI= +github.com/drone/drone-ui v0.0.0-20210512200715-d96f1e26d448 h1:skfTTwMRWSSi3Dv5NrpU8mJn7faccG5q+lqiBQikLiw= +github.com/drone/drone-ui v0.0.0-20210512200715-d96f1e26d448/go.mod h1:NBtVWW7NNJpD9+huMD/5TAE1db2nrEh0i35/9Rf1MPI= github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4 h1:XsstoCeXC2t8lA9OLTdoFwckaptqahxwjCWsenySfX8= github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcejWW1uz/10= github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629 h1:rIaZZalMGGPb2cU/+ypuggZ8aMlpa17RUlJUtsMv8pw= diff --git a/handler/web/login.go b/handler/web/login.go index 89698330..d7ca7128 100644 --- a/handler/web/login.go +++ b/handler/web/login.go @@ -70,11 +70,13 @@ func HandleLogin( logger := logrus.WithField("login", account.Login) logger.Debugf("attempting authentication") + redirect := "/" user, err := users.FindLogin(ctx, account.Login) if err == sql.ErrNoRows { + redirect = "/register" + user = &core.User{ Login: account.Login, - Email: account.Email, Avatar: account.Avatar, Admin: false, Machine: false, @@ -140,7 +142,6 @@ func HandleLogin( } user.Avatar = account.Avatar - user.Email = account.Email user.Token = tok.Access user.Refresh = tok.Refresh user.LastLogin = time.Now().Unix() @@ -169,10 +170,16 @@ func HandleLogin( go synchronize(ctx, syncer, user) } + // If the user account has not completed registration, + // redirect to the registration form. + if len(user.Email) == 0 && user.Created > 1619841600 { + redirect = "/register" + } + logger.Debugf("authentication successful") session.Create(w, user) - http.Redirect(w, r, "/", 303) + http.Redirect(w, r, redirect, 303) } } diff --git a/metric/sink/config.go b/metric/sink/config.go index 63be195c..9dcac6ca 100644 --- a/metric/sink/config.go +++ b/metric/sink/config.go @@ -19,8 +19,6 @@ type Config struct { Endpoint string Token string - Contact1 string - Contact2 string License string Licensor string Subscription string diff --git a/metric/sink/datadog.go b/metric/sink/datadog.go index ea9850a3..9495d94a 100644 --- a/metric/sink/datadog.go +++ b/metric/sink/datadog.go @@ -90,6 +90,11 @@ func (d *Datadog) do(ctx context.Context, unix int64) error { if err != nil { return err } + userList, _ := d.users.ListRange(ctx, core.UserParams{ + Sort: false, + Page: 1, + Size: 5, + }) tags := createTags(d.config) data := new(payload) data.Series = []series{ @@ -98,7 +103,7 @@ func (d *Datadog) do(ctx context.Context, unix int64) error { Points: [][]int64{[]int64{unix, users}}, Type: "gauge", Host: d.system.Host, - Tags: tags, + Tags: append(tags, createInstallerTags(userList)...), }, { Metric: "drone.repos", diff --git a/metric/sink/datadog_test.go b/metric/sink/datadog_test.go index 317c273a..552b4ae1 100644 --- a/metric/sink/datadog_test.go +++ b/metric/sink/datadog_test.go @@ -18,6 +18,7 @@ import ( "context" "testing" + "github.com/drone/drone/core" "github.com/drone/drone/mock" "github.com/drone/drone/version" "github.com/golang/mock/gomock" @@ -38,6 +39,7 @@ func TestDo(t *testing.T) { users := mock.NewMockUserStore(controller) users.EXPECT().Count(gomock.Any()).Return(int64(10), nil) + users.EXPECT().ListRange(gomock.Any(), gomock.Any()).Return([]*core.User{{Email: "jane@acme.com"}}, nil) repos := mock.NewMockRepositoryStore(controller) repos.EXPECT().Count(gomock.Any()).Return(int64(20), nil) @@ -73,7 +75,7 @@ var sample = `{ "points": [[915148800, 10]], "type": "gauge", "host": "test.example.com", - "tags": ["version:` + version.Version.String() + `","remote:github:cloud","scheduler:internal:agents","license:trial"] + "tags": ["version:` + version.Version.String() + `","remote:github:cloud","scheduler:internal:agents","license:trial","installer:jane@acme.com"] }, { "metric": "drone.repos", diff --git a/metric/sink/tags.go b/metric/sink/tags.go index b289b87b..34bc7a40 100644 --- a/metric/sink/tags.go +++ b/metric/sink/tags.go @@ -17,6 +17,7 @@ package sink import ( "fmt" + "github.com/drone/drone/core" "github.com/drone/drone/version" ) @@ -68,14 +69,21 @@ func createTags(config Config) []string { tag := fmt.Sprintf("license:%s", config.License) tags = append(tags, tag) } + return tags +} - if config.Contact1 != "" { - tag := fmt.Sprintf("contact1:%s", config.Contact1) - tags = append(tags, tag) - } - if config.Contact2 != "" { - tag := fmt.Sprintf("contact2:%s", config.Contact2) +func createInstallerTags(users []*core.User) []string { + var tags []string + for _, user := range users { + if user.Machine { + continue + } + if len(user.Email) == 0 { + continue + } + tag := fmt.Sprintf("installer:%s", user.Email) tags = append(tags, tag) + break } return tags } diff --git a/mock/mock_gen.go b/mock/mock_gen.go index 00c0ab87..82c0614d 100644 --- a/mock/mock_gen.go +++ b/mock/mock_gen.go @@ -2070,6 +2070,21 @@ func (mr *MockUserStoreMockRecorder) List(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockUserStore)(nil).List), arg0) } +// ListRange mocks base method +func (m *MockUserStore) ListRange(arg0 context.Context, arg1 core.UserParams) ([]*core.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListRange", arg0, arg1) + ret0, _ := ret[0].([]*core.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListRange indicates an expected call of ListRange +func (mr *MockUserStoreMockRecorder) ListRange(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRange", reflect.TypeOf((*MockUserStore)(nil).ListRange), arg0, arg1) +} + // Update mocks base method func (m *MockUserStore) Update(arg0 context.Context, arg1 *core.User) error { m.ctrl.T.Helper() diff --git a/store/user/user.go b/store/user/user.go index 741f5f5e..32c51a5f 100644 --- a/store/user/user.go +++ b/store/user/user.go @@ -16,6 +16,7 @@ package user import ( "context" + "fmt" "github.com/drone/drone/core" "github.com/drone/drone/store/shared/db" @@ -91,6 +92,31 @@ func (s *userStore) List(ctx context.Context) ([]*core.User, error) { return out, err } +// ListRange returns a list of users from the datastore. +func (s *userStore) ListRange(ctx context.Context, params core.UserParams) ([]*core.User, error) { + var out []*core.User + err := s.db.View(func(queryer db.Queryer, binder db.Binder) error { + // this query breaks a rule and uses sprintf to inject parameters + // into the query. Normally this should be avoided, however, in this + // case the parameters are set by the internal system and can + // be considered safe. + query := queryRange + switch { + case params.Sort: + query = fmt.Sprintf(query, "user_login", params.Size, params.Page) + default: + query = fmt.Sprintf(query, "user_id", params.Size, params.Page) + } + rows, err := queryer.Query(query) + if err != nil { + return err + } + out, err = scanRows(s.enc, rows) + return err + }) + return out, err +} + // Create persists a new user to the datastore. func (s *userStore) Create(ctx context.Context, user *core.User) error { if s.db.Driver() == db.Postgres { @@ -235,6 +261,13 @@ FROM users ORDER BY user_login ` +const queryRange = queryBase + ` +FROM users +ORDER BY %s +LIMIT %d +OFFSET %d +` + const stmtUpdate = ` UPDATE users SET