Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: Add update user password endpoint#1310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
BrunoQuaresma merged 17 commits intomainfrombq/update-user-password
May 6, 2022
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
17 commits
Select commitHold shift + click to select a range
346e5e4
Add UpdateUserHashedPassword query
BrunoQuaresmaMay 4, 2022
4bd7557
chore: Merge branch 'main' of github.com:coder/coder into bq/update-u…
BrunoQuaresmaMay 4, 2022
de39cf5
Add database functions
BrunoQuaresmaMay 5, 2022
2fe1716
Add update user password endpoint
BrunoQuaresmaMay 5, 2022
212020a
Add tests and fixes
BrunoQuaresmaMay 5, 2022
2699445
Remove confirmation and fix lint issues
BrunoQuaresmaMay 5, 2022
355f163
Return hash error as server error
BrunoQuaresmaMay 5, 2022
56b29fd
Update coderd/database/databasefake/databasefake.go
BrunoQuaresmaMay 5, 2022
30b8f15
Improve readbility
BrunoQuaresmaMay 5, 2022
f6be255
Add RBAC
BrunoQuaresmaMay 5, 2022
5df5763
Fix route
BrunoQuaresmaMay 5, 2022
b9dbd64
Merge branch 'bq/update-user-password' of github.com:coder/coder into…
BrunoQuaresmaMay 5, 2022
69af903
Add missing TS types
BrunoQuaresmaMay 5, 2022
d85092b
Update update password request params
BrunoQuaresmaMay 5, 2022
96ce751
Remove confirm password from the API
BrunoQuaresmaMay 5, 2022
4f9f506
Update coderd/users.go
BrunoQuaresmaMay 5, 2022
671c56d
Remove user restriction and refactor tests
BrunoQuaresmaMay 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletionscoderd/coderd.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -240,6 +240,10 @@ func New(options *Options) (http.Handler, func()) {
r.Get("/",api.userByName)
r.Put("/profile",api.putUserProfile)
r.Put("/suspend",api.putUserSuspend)
r.Route("/password",func(r chi.Router) {
r.Use(httpmw.WithRBACObject(rbac.ResourceUserPasswordRole))
r.Put("/",authorize(api.putUserPassword,rbac.ActionUpdate))
})
r.Get("/organizations",api.organizationsByUser)
r.Post("/organizations",api.postOrganizationsByUser)
// These roles apply to the site wide permissions.
Expand Down
19 changes: 10 additions & 9 deletionscoderd/coderdtest/coderdtest.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -174,21 +174,22 @@ func NewProvisionerDaemon(t *testing.T, client *codersdk.Client) io.Closer {
return closer
}

var FirstUserParams = codersdk.CreateFirstUserRequest{
Email: "testuser@coder.com",
Username: "testuser",
Password: "testpass",
OrganizationName: "testorg",
}

// CreateFirstUser creates a user with preset credentials and authenticates
// with the passed in codersdk client.
func CreateFirstUser(t *testing.T, client *codersdk.Client) codersdk.CreateFirstUserResponse {
req := codersdk.CreateFirstUserRequest{
Email: "testuser@coder.com",
Username: "testuser",
Password: "testpass",
OrganizationName: "testorg",
}
resp, err := client.CreateFirstUser(context.Background(), req)
resp, err := client.CreateFirstUser(context.Background(), FirstUserParams)
require.NoError(t, err)

login, err := client.LoginWithPassword(context.Background(), codersdk.LoginWithPasswordRequest{
Email:req.Email,
Password:req.Password,
Email:FirstUserParams.Email,
Password:FirstUserParams.Password,
})
require.NoError(t, err)
client.SessionToken = login.SessionToken
Expand Down
15 changes: 15 additions & 0 deletionscoderd/database/databasefake/databasefake.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1314,6 +1314,21 @@ func (q *fakeQuerier) UpdateUserStatus(_ context.Context, arg database.UpdateUse
return database.User{}, sql.ErrNoRows
}

func (q *fakeQuerier) UpdateUserHashedPassword(_ context.Context, arg database.UpdateUserHashedPasswordParams) error {
q.mutex.Lock()
defer q.mutex.Unlock()

for i, user := range q.users {
if user.ID != arg.ID {
continue
}
user.HashedPassword = arg.HashedPassword
q.users[i] = user
return nil
}
return sql.ErrNoRows
}

func (q *fakeQuerier) InsertWorkspace(_ context.Context, arg database.InsertWorkspaceParams) (database.Workspace, error) {
q.mutex.Lock()
defer q.mutex.Unlock()
Expand Down
1 change: 1 addition & 0 deletionscoderd/database/querier.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

19 changes: 19 additions & 0 deletionscoderd/database/queries.sql.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

10 changes: 9 additions & 1 deletioncoderd/database/queries/users.sql
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,14 @@ WHERE
id = @id
RETURNING *;

-- name: UpdateUserHashedPassword :exec
UPDATE
users
SET
hashed_password = $2
WHERE
id = $1;

-- name: GetUsers :many
SELECT
*
Expand DownExpand Up@@ -133,4 +141,4 @@ FROM
LEFT JOIN organization_members
ON id = user_id
WHERE
id = @user_id;
id = @user_id;
8 changes: 0 additions & 8 deletionscoderd/httpmw/userparam.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,14 +76,6 @@ func ExtractUserParam(db database.Store) func(http.Handler) http.Handler {
}
}

apiKey := APIKey(r)
if apiKey.UserID != user.ID {
Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I removed this because it was "overriding" RBAC roles. I know we don’t have the RBAC in place for all the user routes, but I can try to do that next. Probably I can send a PR on Monday. Thoughts? cc.:@f0ssel

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "getting non-personal users isn't supported yet",
})
return
}

ctx := context.WithValue(r.Context(), userParamContextKey{}, user)
next.ServeHTTP(rw, r.WithContext(ctx))
})
Expand Down
4 changes: 4 additions & 0 deletionscoderd/rbac/object.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,6 +24,10 @@ var (
Type: "user_role",
}

ResourceUserPasswordRole = Object{
Type: "user_password",
}

// ResourceWildcard represents all resource types
ResourceWildcard = Object{
Type: WildcardSymbol,
Expand Down
31 changes: 30 additions & 1 deletioncoderd/users.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -360,6 +360,36 @@ func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser, organizations))
}

func (api *api) putUserPassword(rw http.ResponseWriter, r *http.Request) {
var (
user = httpmw.UserParam(r)
params codersdk.UpdateUserPasswordRequest
)
if !httpapi.Read(rw, r, &params) {
return
}

hashedPassword, err := userpassword.Hash(params.Password)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("hash password: %s", err.Error()),
})
return
}
err = api.Database.UpdateUserHashedPassword(r.Context(), database.UpdateUserHashedPasswordParams{
ID: user.ID,
HashedPassword: []byte(hashedPassword),
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("put user password: %s", err.Error()),
})
return
}

httpapi.Write(rw, http.StatusNoContent, nil)
}

func (api *api) userRoles(rw http.ResponseWriter, r *http.Request) {
user := httpmw.UserParam(r)

Expand DownExpand Up@@ -577,7 +607,6 @@ func (api *api) postLogin(rw http.ResponseWriter, r *http.Request) {
}

// If the user doesn't exist, it will be a default struct.

equal, err := userpassword.Compare(string(user.HashedPassword), loginWithPassword.Password)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Expand Down
38 changes: 38 additions & 0 deletionscoderd/users_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -287,6 +287,44 @@ func TestUpdateUserProfile(t *testing.T) {
})
}

func TestUpdateUserPassword(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Let's also add some tests to make sure the rbac is working here, I'd like to ensure that the user itself cannot perform this action, and neither can other non-admin users.

Copy link
CollaboratorAuthor

@BrunoQuaresmaBrunoQuaresmaMay 5, 2022
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

So from what I'm understanding we want to test:

  • A non-admin user can't update any password
  • An admin can update another user's password

Is that?

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Updated!

t.Parallel()

t.Run("MemberCantUpdateAdminPassword", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
admin := coderdtest.CreateFirstUser(t, client)
member := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
err := member.UpdateUserPassword(context.Background(), admin.UserID, codersdk.UpdateUserPasswordRequest{
Password: "newpassword",
})
require.Error(t, err, "member should not be able to update admin password")
})

t.Run("AdminCanUpdateMemberPassword", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
admin := coderdtest.CreateFirstUser(t, client)
member, err := client.CreateUser(context.Background(), codersdk.CreateUserRequest{
Email: "coder@coder.com",
Username: "coder",
Password: "password",
OrganizationID: admin.OrganizationID,
})
require.NoError(t, err, "create member")
err = client.UpdateUserPassword(context.Background(), member.ID, codersdk.UpdateUserPasswordRequest{
Password: "newpassword",
})
require.NoError(t, err, "admin should be able to update member password")
// Check if the member can login using the new password
_, err = client.LoginWithPassword(context.Background(), codersdk.LoginWithPasswordRequest{
Email: "coder@coder.com",
Password: "newpassword",
})
require.NoError(t, err, "member should login successfully with the new password")
})
}

func TestGrantRoles(t *testing.T) {
t.Parallel()
t.Run("UpdateIncorrectRoles", func(t *testing.T) {
Expand Down
18 changes: 18 additions & 0 deletionscodersdk/users.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,6 +72,10 @@ type UpdateUserProfileRequest struct {
Username string `json:"username" validate:"required,username"`
}

type UpdateUserPasswordRequest struct {
Password string `json:"password" validate:"required"`
}

type UpdateRoles struct {
Roles []string `json:"roles" validate:"required"`
}
Expand DownExpand Up@@ -181,6 +185,20 @@ func (c *Client) SuspendUser(ctx context.Context, userID uuid.UUID) (User, error
return user, json.NewDecoder(res.Body).Decode(&user)
}

// UpdateUserPassword updates a user password.
// It calls PUT /users/{user}/password
func (c *Client) UpdateUserPassword(ctx context.Context, userID uuid.UUID, req UpdateUserPasswordRequest) error {
res, err := c.request(ctx, http.MethodPut, fmt.Sprintf("/api/v2/users/%s/password", uuidOrMe(userID)), req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusNoContent {
return readBodyAsError(res)
}
return nil
}

// UpdateUserRoles grants the userID the specified roles.
// Include ALL roles the user has.
func (c *Client) UpdateUserRoles(ctx context.Context, userID uuid.UUID, req UpdateRoles) (User, error) {
Expand Down
19 changes: 12 additions & 7 deletionssite/src/api/typesGenerated.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@ export interface AgentGitSSHKey {
readonly private_key: string
}

// From codersdk/users.go:105:6
// From codersdk/users.go:109:6
export interface AuthMethods {
readonly password: boolean
readonly github: boolean
Expand DownExpand Up@@ -44,7 +44,7 @@ export interface CreateFirstUserResponse {
readonly organization_id: string
}

// From codersdk/users.go:100:6
// From codersdk/users.go:104:6
export interface CreateOrganizationRequest {
readonly name: string
}
Expand DownExpand Up@@ -101,7 +101,7 @@ export interface CreateWorkspaceRequest {
readonly parameter_values: CreateParameterRequest[]
}

// From codersdk/users.go:96:6
// From codersdk/users.go:100:6
export interface GenerateAPIKeyResponse {
readonly key: string
}
Expand All@@ -119,13 +119,13 @@ export interface GoogleInstanceIdentityToken {
readonly json_web_token: string
}

// From codersdk/users.go:85:6
// From codersdk/users.go:89:6
export interface LoginWithPasswordRequest {
readonly email: string
readonly password: string
}

// From codersdk/users.go:91:6
// From codersdk/users.go:95:6
export interface LoginWithPasswordResponse {
readonly session_token: string
}
Expand DownExpand Up@@ -255,11 +255,16 @@ export interface UpdateActiveTemplateVersion {
readonly id: string
}

// From codersdk/users.go:75:6
// From codersdk/users.go:79:6
export interface UpdateRoles {
readonly roles: string[]
}

// From codersdk/users.go:75:6
export interface UpdateUserPasswordRequest {
readonly password: string
}

// From codersdk/users.go:70:6
export interface UpdateUserProfileRequest {
readonly email: string
Expand DownExpand Up@@ -291,7 +296,7 @@ export interface User {
readonly organization_ids: string[]
}

// From codersdk/users.go:79:6
// From codersdk/users.go:83:6
export interface UserRoles {
readonly roles: string[]
readonly organization_roles: Record<string, string[]>
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp