mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: implement api for "forgot password?" flow (#14915)
Relates to https://github.com/coder/coder/issues/14232 This implements two endpoints (names subject to change): - `/api/v2/users/otp/request` - `/api/v2/users/otp/change-password`
This commit is contained in:
@@ -3628,6 +3628,14 @@ func (q *querier) UpdateUserGithubComUserID(ctx context.Context, arg database.Up
|
||||
return q.db.UpdateUserGithubComUserID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateUserHashedOneTimePasscode(ctx context.Context, arg database.UpdateUserHashedOneTimePasscodeParams) error {
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.db.UpdateUserHashedOneTimePasscode(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateUserHashedPassword(ctx context.Context, arg database.UpdateUserHashedPasswordParams) error {
|
||||
user, err := q.db.GetUserByID(ctx, arg.ID)
|
||||
if err != nil {
|
||||
|
||||
@@ -1187,6 +1187,12 @@ func (s *MethodTestSuite) TestUser() {
|
||||
ID: u.ID,
|
||||
}).Asserts(u, policy.ActionUpdatePersonal).Returns()
|
||||
}))
|
||||
s.Run("UpdateUserHashedOneTimePasscode", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
check.Args(database.UpdateUserHashedOneTimePasscodeParams{
|
||||
ID: u.ID,
|
||||
}).Asserts(rbac.ResourceSystem, policy.ActionUpdate).Returns()
|
||||
}))
|
||||
s.Run("UpdateUserQuietHoursSchedule", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
check.Args(database.UpdateUserQuietHoursScheduleParams{
|
||||
|
||||
@@ -9077,6 +9077,26 @@ func (q *FakeQuerier) UpdateUserGithubComUserID(_ context.Context, arg database.
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateUserHashedOneTimePasscode(_ context.Context, arg database.UpdateUserHashedOneTimePasscodeParams) error {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for i, user := range q.users {
|
||||
if user.ID != arg.ID {
|
||||
continue
|
||||
}
|
||||
user.HashedOneTimePasscode = arg.HashedOneTimePasscode
|
||||
user.OneTimePasscodeExpiresAt = arg.OneTimePasscodeExpiresAt
|
||||
q.users[i] = user
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateUserHashedPassword(_ context.Context, arg database.UpdateUserHashedPasswordParams) error {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return err
|
||||
@@ -9090,6 +9110,8 @@ func (q *FakeQuerier) UpdateUserHashedPassword(_ context.Context, arg database.U
|
||||
continue
|
||||
}
|
||||
user.HashedPassword = arg.HashedPassword
|
||||
user.HashedOneTimePasscode = nil
|
||||
user.OneTimePasscodeExpiresAt = sql.NullTime{}
|
||||
q.users[i] = user
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2307,6 +2307,13 @@ func (m metricsStore) UpdateUserGithubComUserID(ctx context.Context, arg databas
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateUserHashedOneTimePasscode(ctx context.Context, arg database.UpdateUserHashedOneTimePasscodeParams) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpdateUserHashedOneTimePasscode(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("UpdateUserHashedOneTimePasscode").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateUserHashedPassword(ctx context.Context, arg database.UpdateUserHashedPasswordParams) error {
|
||||
start := time.Now()
|
||||
err := m.s.UpdateUserHashedPassword(ctx, arg)
|
||||
|
||||
@@ -4861,6 +4861,20 @@ func (mr *MockStoreMockRecorder) UpdateUserGithubComUserID(arg0, arg1 any) *gomo
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserGithubComUserID", reflect.TypeOf((*MockStore)(nil).UpdateUserGithubComUserID), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateUserHashedOneTimePasscode mocks base method.
|
||||
func (m *MockStore) UpdateUserHashedOneTimePasscode(arg0 context.Context, arg1 database.UpdateUserHashedOneTimePasscodeParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateUserHashedOneTimePasscode", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateUserHashedOneTimePasscode indicates an expected call of UpdateUserHashedOneTimePasscode.
|
||||
func (mr *MockStoreMockRecorder) UpdateUserHashedOneTimePasscode(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserHashedOneTimePasscode", reflect.TypeOf((*MockStore)(nil).UpdateUserHashedOneTimePasscode), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateUserHashedPassword mocks base method.
|
||||
func (m *MockStore) UpdateUserHashedPassword(arg0 context.Context, arg1 database.UpdateUserHashedPasswordParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DELETE FROM notification_templates WHERE id = '62f86a30-2330-4b61-a26d-311ff3b608cf';
|
||||
@@ -0,0 +1,4 @@
|
||||
INSERT INTO notification_templates (id, name, title_template, body_template, "group", actions)
|
||||
VALUES ('62f86a30-2330-4b61-a26d-311ff3b608cf', 'One-Time Passcode', E'Your One-Time Passcode for Coder.',
|
||||
E'Hi {{.UserName}},\n\nA request to reset the password for your Coder account has been made. Your one-time passcode is:\n\n**{{.Labels.one_time_passcode}}**\n\nIf you did not request to reset your password, you can ignore this message.',
|
||||
'User Events', '[]'::jsonb);
|
||||
@@ -457,6 +457,7 @@ type sqlcQuerier interface {
|
||||
UpdateUserAppearanceSettings(ctx context.Context, arg UpdateUserAppearanceSettingsParams) (User, error)
|
||||
UpdateUserDeletedByID(ctx context.Context, id uuid.UUID) error
|
||||
UpdateUserGithubComUserID(ctx context.Context, arg UpdateUserGithubComUserIDParams) error
|
||||
UpdateUserHashedOneTimePasscode(ctx context.Context, arg UpdateUserHashedOneTimePasscodeParams) error
|
||||
UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error
|
||||
UpdateUserLastSeenAt(ctx context.Context, arg UpdateUserLastSeenAtParams) (User, error)
|
||||
UpdateUserLink(ctx context.Context, arg UpdateUserLinkParams) (UserLink, error)
|
||||
|
||||
@@ -10528,11 +10528,34 @@ func (q *sqlQuerier) UpdateUserGithubComUserID(ctx context.Context, arg UpdateUs
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUserHashedOneTimePasscode = `-- name: UpdateUserHashedOneTimePasscode :exec
|
||||
UPDATE
|
||||
users
|
||||
SET
|
||||
hashed_one_time_passcode = $2,
|
||||
one_time_passcode_expires_at = $3
|
||||
WHERE
|
||||
id = $1
|
||||
`
|
||||
|
||||
type UpdateUserHashedOneTimePasscodeParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
HashedOneTimePasscode []byte `db:"hashed_one_time_passcode" json:"hashed_one_time_passcode"`
|
||||
OneTimePasscodeExpiresAt sql.NullTime `db:"one_time_passcode_expires_at" json:"one_time_passcode_expires_at"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateUserHashedOneTimePasscode(ctx context.Context, arg UpdateUserHashedOneTimePasscodeParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateUserHashedOneTimePasscode, arg.ID, arg.HashedOneTimePasscode, arg.OneTimePasscodeExpiresAt)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUserHashedPassword = `-- name: UpdateUserHashedPassword :exec
|
||||
UPDATE
|
||||
users
|
||||
SET
|
||||
hashed_password = $2
|
||||
hashed_password = $2,
|
||||
hashed_one_time_passcode = NULL,
|
||||
one_time_passcode_expires_at = NULL
|
||||
WHERE
|
||||
id = $1
|
||||
`
|
||||
|
||||
@@ -117,7 +117,9 @@ RETURNING *;
|
||||
UPDATE
|
||||
users
|
||||
SET
|
||||
hashed_password = $2
|
||||
hashed_password = $2,
|
||||
hashed_one_time_passcode = NULL,
|
||||
one_time_passcode_expires_at = NULL
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
@@ -289,3 +291,13 @@ RETURNING id, email, last_seen_at;
|
||||
-- AllUserIDs returns all UserIDs regardless of user status or deletion.
|
||||
-- name: AllUserIDs :many
|
||||
SELECT DISTINCT id FROM USERS;
|
||||
|
||||
-- name: UpdateUserHashedOneTimePasscode :exec
|
||||
UPDATE
|
||||
users
|
||||
SET
|
||||
hashed_one_time_passcode = $2,
|
||||
one_time_passcode_expires_at = $3
|
||||
WHERE
|
||||
id = $1
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user