From 9349728997fe51fd5e7d72ea718ef1137f46efcb Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Fri, 3 May 2019 09:53:38 +0200 Subject: [PATCH] configstore: add update user --- internal/services/configstore/api/client.go | 11 +++ internal/services/configstore/api/user.go | 44 ++++++++++ .../services/configstore/command/command.go | 84 +++++++++++++++++++ internal/services/configstore/configstore.go | 2 + 4 files changed, 141 insertions(+) diff --git a/internal/services/configstore/api/client.go b/internal/services/configstore/api/client.go index 82d6536..c9f0a6b 100644 --- a/internal/services/configstore/api/client.go +++ b/internal/services/configstore/api/client.go @@ -323,6 +323,17 @@ func (c *Client) CreateUser(ctx context.Context, req *CreateUserRequest) (*types return user, resp, err } +func (c *Client) UpdateUser(ctx context.Context, userID string, req *UpdateUserRequest) (*types.User, *http.Response, error) { + reqj, err := json.Marshal(req) + if err != nil { + return nil, nil, err + } + + user := new(types.User) + resp, err := c.getParsedResponse(ctx, "PUT", fmt.Sprintf("/users/%s", userID), nil, jsonContent, bytes.NewReader(reqj), user) + return user, resp, err +} + func (c *Client) DeleteUser(ctx context.Context, userName string) (*http.Response, error) { return c.getResponse(ctx, "DELETE", fmt.Sprintf("/users/%s", userName), nil, jsonContent, nil) } diff --git a/internal/services/configstore/api/user.go b/internal/services/configstore/api/user.go index 451755b..9be86ea 100644 --- a/internal/services/configstore/api/user.go +++ b/internal/services/configstore/api/user.go @@ -152,6 +152,50 @@ func (h *CreateUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +type UpdateUserRequest struct { + UserName string `json:"user_name"` + + UpdateUserLARequest *UpdateUserLARequest `json:"create_user_la_request"` +} + +type UpdateUserHandler struct { + log *zap.SugaredLogger + ch *command.CommandHandler +} + +func NewUpdateUserHandler(logger *zap.Logger, ch *command.CommandHandler) *UpdateUserHandler { + return &UpdateUserHandler{log: logger.Sugar(), ch: ch} +} + +func (h *UpdateUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + vars := mux.Vars(r) + userID := vars["userid"] + + var req *UpdateUserRequest + d := json.NewDecoder(r.Body) + if err := d.Decode(&req); err != nil { + httpError(w, util.NewErrBadRequest(err)) + return + } + + creq := &command.UpdateUserRequest{ + UserID: userID, + UserName: req.UserName, + } + + user, err := h.ch.UpdateUser(ctx, creq) + if httpError(w, err) { + h.log.Errorf("err: %+v", err) + return + } + + if err := httpResponse(w, http.StatusCreated, user); err != nil { + h.log.Errorf("err: %+v", err) + } +} + type DeleteUserHandler struct { log *zap.SugaredLogger ch *command.CommandHandler diff --git a/internal/services/configstore/command/command.go b/internal/services/configstore/command/command.go index 2b92db9..bb43b60 100644 --- a/internal/services/configstore/command/command.go +++ b/internal/services/configstore/command/command.go @@ -180,6 +180,20 @@ func (s *CommandHandler) CreateProject(ctx context.Context, project *types.Proje if pg != nil { return util.NewErrBadRequest(errors.Errorf("project group with name %q, path %q already exists", pg.Name, pp)) } + + // check that the linked account matches the remote source + user, err := s.readDB.GetUserByLinkedAccount(tx, project.LinkedAccountID) + if err != nil { + return errors.Wrapf(err, "failed to get user with linked account id %q", project.LinkedAccountID) + } + la, ok := user.LinkedAccounts[project.LinkedAccountID] + if !ok { + return util.NewErrBadRequest(errors.Errorf("linked account id %q for user %q doesn't exist", project.LinkedAccountID, user.Name)) + } + if la.RemoteSourceID != project.RemoteSourceID { + return util.NewErrBadRequest(errors.Errorf("linked account id %q remote source %q different than project remote source %q", project.LinkedAccountID, la.RemoteSourceID, project.RemoteSourceID)) + } + return nil }) if err != nil { @@ -407,6 +421,76 @@ func (s *CommandHandler) DeleteUser(ctx context.Context, userName string) error return err } +type UpdateUserRequest struct { + UserID string + + UserName string +} + +func (s *CommandHandler) UpdateUser(ctx context.Context, req *UpdateUserRequest) (*types.User, error) { + var cgt *datamanager.ChangeGroupsUpdateToken + + cgNames := []string{} + var user *types.User + + // must do all the check in a single transaction to avoid concurrent changes + err := s.readDB.Do(func(tx *db.Tx) error { + var err error + user, err = s.readDB.GetUserByName(tx, req.UserName) + if err != nil { + return err + } + if user == nil { + return util.NewErrBadRequest(errors.Errorf("user %q doesn't exist", req.UserName)) + } + + cgt, err = s.readDB.GetChangeGroupsUpdateTokens(tx, cgNames) + if err != nil { + return err + } + + if req.UserName != "" { + // check duplicate user name + u, err := s.readDB.GetUserByName(tx, req.UserName) + if err != nil { + return err + } + if u != nil { + return util.NewErrBadRequest(errors.Errorf("user with name %q already exists", u.Name)) + } + // changegroup is the username (and in future the email) to ensure no + // concurrent user creation/modification using the same name + cgNames = append(cgNames, util.EncodeSha256Hex("username-"+req.UserName)) + } + + return nil + }) + if err != nil { + return nil, err + } + + if req.UserName != "" { + user.Name = req.UserName + } + + userj, err := json.Marshal(user) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal user") + } + + actions := []*datamanager.Action{ + { + ActionType: datamanager.ActionTypePut, + DataType: string(types.ConfigTypeUser), + ID: user.ID, + Data: userj, + }, + } + + _, err = s.dm.WriteWal(ctx, actions, cgt) + return user, err +} + type CreateUserLARequest struct { UserName string RemoteSourceName string diff --git a/internal/services/configstore/configstore.go b/internal/services/configstore/configstore.go index 4222e46..4a520c6 100644 --- a/internal/services/configstore/configstore.go +++ b/internal/services/configstore/configstore.go @@ -145,6 +145,7 @@ func (s *ConfigStore) Run(ctx context.Context) error { usersHandler := api.NewUsersHandler(logger, s.readDB) userByNameHandler := api.NewUserByNameHandler(logger, s.readDB) createUserHandler := api.NewCreateUserHandler(logger, s.ch) + updateUserHandler := api.NewUpdateUserHandler(logger, s.ch) deleteUserHandler := api.NewDeleteUserHandler(logger, s.ch) createUserLAHandler := api.NewCreateUserLAHandler(logger, s.ch) @@ -196,6 +197,7 @@ func (s *ConfigStore) Run(ctx context.Context) error { apirouter.Handle("/users", usersHandler).Methods("GET") apirouter.Handle("/users", createUserHandler).Methods("POST") apirouter.Handle("/users/{username}", userByNameHandler).Methods("GET") + apirouter.Handle("/users/{userid}", updateUserHandler).Methods("PUT") apirouter.Handle("/users/{username}", deleteUserHandler).Methods("DELETE") apirouter.Handle("/users/{username}/linkedaccounts", createUserLAHandler).Methods("POST")