From 704da47afc0372432f6dd679ca842c3e52263451 Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Fri, 29 Mar 2019 17:50:51 +0100 Subject: [PATCH] configstore: createuser also create linked account when requested Useful to future user registration to create user and linked account in a unique atomic call. --- internal/services/configstore/api/client.go | 8 +-- internal/services/configstore/api/user.go | 24 ++++++- .../services/configstore/command/command.go | 63 +++++++++++++++++-- internal/services/gateway/command/user.go | 4 +- 4 files changed, 86 insertions(+), 13 deletions(-) diff --git a/internal/services/configstore/api/client.go b/internal/services/configstore/api/client.go index 2aa4cbc..99748ae 100644 --- a/internal/services/configstore/api/client.go +++ b/internal/services/configstore/api/client.go @@ -314,14 +314,14 @@ func (c *Client) GetUserByLinkedAccount(ctx context.Context, linkedAccountID str return users[0], resp, err } -func (c *Client) CreateUser(ctx context.Context, user *types.User) (*types.User, *http.Response, error) { - uj, err := json.Marshal(user) +func (c *Client) CreateUser(ctx context.Context, req *CreateUserRequest) (*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", "/users", nil, jsonContent, bytes.NewReader(uj), user) + user := new(types.User) + resp, err := c.getParsedResponse(ctx, "PUT", "/users", nil, jsonContent, bytes.NewReader(reqj), user) return user, resp, err } diff --git a/internal/services/configstore/api/user.go b/internal/services/configstore/api/user.go index b707b75..f294b1d 100644 --- a/internal/services/configstore/api/user.go +++ b/internal/services/configstore/api/user.go @@ -103,6 +103,12 @@ func (h *UserByNameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +type CreateUserRequest struct { + UserName string `json:"user_name"` + + CreateUserLARequest *CreateUserLARequest `json:"create_user_la_request"` +} + type CreateUserHandler struct { log *zap.SugaredLogger ch *command.CommandHandler @@ -115,14 +121,28 @@ func NewCreateUserHandler(logger *zap.Logger, ch *command.CommandHandler) *Creat func (h *CreateUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - var req types.User + var req *CreateUserRequest d := json.NewDecoder(r.Body) if err := d.Decode(&req); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - user, err := h.ch.CreateUser(ctx, &req) + creq := &command.CreateUserRequest{ + UserName: req.UserName, + } + if req.CreateUserLARequest != nil { + creq.CreateUserLARequest = &command.CreateUserLARequest{ + RemoteSourceName: req.CreateUserLARequest.RemoteSourceName, + RemoteUserID: req.CreateUserLARequest.RemoteUserID, + RemoteUserName: req.CreateUserLARequest.RemoteUserName, + Oauth2AccessToken: req.CreateUserLARequest.Oauth2AccessToken, + Oauth2RefreshToken: req.CreateUserLARequest.Oauth2RefreshToken, + UserAccessToken: req.CreateUserLARequest.UserAccessToken, + } + } + + user, err := h.ch.CreateUser(ctx, creq) if httpError(w, err) { h.log.Errorf("err: %+v", err) return diff --git a/internal/services/configstore/command/command.go b/internal/services/configstore/command/command.go index e77b912..9189287 100644 --- a/internal/services/configstore/command/command.go +++ b/internal/services/configstore/command/command.go @@ -239,13 +239,20 @@ func (s *CommandHandler) DeleteProject(ctx context.Context, projectRef string) e return err } -func (s *CommandHandler) CreateUser(ctx context.Context, user *types.User) (*types.User, error) { - if user.UserName == "" { +type CreateUserRequest struct { + UserName string + + CreateUserLARequest *CreateUserLARequest +} + +func (s *CommandHandler) CreateUser(ctx context.Context, req *CreateUserRequest) (*types.User, error) { + if req.UserName == "" { return nil, util.NewErrBadRequest(errors.Errorf("user name required")) } var cgt *wal.ChangeGroupsUpdateToken - cgNames := []string{user.UserName} + cgNames := []string{req.UserName} + var rs *types.RemoteSource // must do all the check in a single transaction to avoid concurrent changes err := s.readDB.Do(func(tx *db.Tx) error { @@ -256,20 +263,58 @@ func (s *CommandHandler) CreateUser(ctx context.Context, user *types.User) (*typ } // check duplicate user name - u, err := s.readDB.GetUserByName(tx, user.UserName) + 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.UserName)) } + + if req.CreateUserLARequest != nil { + rs, err = s.readDB.GetRemoteSourceByName(tx, req.CreateUserLARequest.RemoteSourceName) + if err != nil { + return err + } + if rs == nil { + return util.NewErrBadRequest(errors.Errorf("remote source %q doesn't exist", req.CreateUserLARequest.RemoteSourceName)) + } + user, err := s.readDB.GetUserByLinkedAccountRemoteUserIDandSource(tx, req.CreateUserLARequest.RemoteUserID, rs.ID) + if err != nil { + return errors.Wrapf(err, "failed to get user for remote user id %q and remote source %q", req.CreateUserLARequest.RemoteUserID, rs.ID) + } + if user != nil { + return util.NewErrBadRequest(errors.Errorf("user for remote user id %q for remote source %q already exists", req.CreateUserLARequest.RemoteUserID, req.CreateUserLARequest.RemoteSourceName)) + } + } return nil }) if err != nil { return nil, err } - user.ID = uuid.NewV4().String() + user := &types.User{ + ID: uuid.NewV4().String(), + UserName: req.UserName, + } + if req.CreateUserLARequest != nil { + if user.LinkedAccounts == nil { + user.LinkedAccounts = make(map[string]*types.LinkedAccount) + } + + la := &types.LinkedAccount{ + ID: uuid.NewV4().String(), + RemoteSourceID: rs.ID, + RemoteUserID: req.CreateUserLARequest.RemoteUserID, + RemoteUserName: req.CreateUserLARequest.RemoteUserName, + UserAccessToken: req.CreateUserLARequest.UserAccessToken, + Oauth2AccessToken: req.CreateUserLARequest.Oauth2AccessToken, + Oauth2RefreshToken: req.CreateUserLARequest.Oauth2RefreshToken, + } + + user.LinkedAccounts[la.ID] = la + } + userj, err := json.Marshal(user) if err != nil { return nil, errors.Wrapf(err, "failed to marshal user") @@ -392,6 +437,14 @@ func (s *CommandHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequ if rs == nil { return util.NewErrBadRequest(errors.Errorf("remote source %q doesn't exist", req.RemoteSourceName)) } + + user, err := s.readDB.GetUserByLinkedAccountRemoteUserIDandSource(tx, req.RemoteUserID, rs.ID) + if err != nil { + return errors.Wrapf(err, "failed to get user for remote user id %q and remote source %q", req.RemoteUserID, rs.ID) + } + if user != nil { + return util.NewErrBadRequest(errors.Errorf("user for remote user id %q for remote source %q already exists", req.RemoteUserID, req.RemoteSourceName)) + } return nil }) if err != nil { diff --git a/internal/services/gateway/command/user.go b/internal/services/gateway/command/user.go index 4b2cad1..22ae7f4 100644 --- a/internal/services/gateway/command/user.go +++ b/internal/services/gateway/command/user.go @@ -39,12 +39,12 @@ func (c *CommandHandler) CreateUser(ctx context.Context, req *CreateUserRequest) return nil, errors.Errorf("invalid user name %q", req.UserName) } - u := &types.User{ + creq := &csapi.CreateUserRequest{ UserName: req.UserName, } c.log.Infof("creating user") - u, _, err := c.configstoreClient.CreateUser(ctx, u) + u, _, err := c.configstoreClient.CreateUser(ctx, creq) if err != nil { return nil, errors.Wrapf(err, "failed to create user") }