configstore: createuser also create linked account when requested

Useful to future user registration to create user and linked account in a unique
atomic call.
This commit is contained in:
Simone Gotti 2019-03-29 17:50:51 +01:00
parent 3e3a7a0ea5
commit 704da47afc
4 changed files with 86 additions and 13 deletions

View File

@ -314,14 +314,14 @@ func (c *Client) GetUserByLinkedAccount(ctx context.Context, linkedAccountID str
return users[0], resp, err return users[0], resp, err
} }
func (c *Client) CreateUser(ctx context.Context, user *types.User) (*types.User, *http.Response, error) { func (c *Client) CreateUser(ctx context.Context, req *CreateUserRequest) (*types.User, *http.Response, error) {
uj, err := json.Marshal(user) reqj, err := json.Marshal(req)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
user = new(types.User) user := new(types.User)
resp, err := c.getParsedResponse(ctx, "PUT", "/users", nil, jsonContent, bytes.NewReader(uj), user) resp, err := c.getParsedResponse(ctx, "PUT", "/users", nil, jsonContent, bytes.NewReader(reqj), user)
return user, resp, err return user, resp, err
} }

View File

@ -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 { type CreateUserHandler struct {
log *zap.SugaredLogger log *zap.SugaredLogger
ch *command.CommandHandler 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) { func (h *CreateUserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
var req types.User var req *CreateUserRequest
d := json.NewDecoder(r.Body) d := json.NewDecoder(r.Body)
if err := d.Decode(&req); err != nil { if err := d.Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return 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) { if httpError(w, err) {
h.log.Errorf("err: %+v", err) h.log.Errorf("err: %+v", err)
return return

View File

@ -239,13 +239,20 @@ func (s *CommandHandler) DeleteProject(ctx context.Context, projectRef string) e
return err return err
} }
func (s *CommandHandler) CreateUser(ctx context.Context, user *types.User) (*types.User, error) { type CreateUserRequest struct {
if user.UserName == "" { 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")) return nil, util.NewErrBadRequest(errors.Errorf("user name required"))
} }
var cgt *wal.ChangeGroupsUpdateToken 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 // must do all the check in a single transaction to avoid concurrent changes
err := s.readDB.Do(func(tx *db.Tx) error { 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 // check duplicate user name
u, err := s.readDB.GetUserByName(tx, user.UserName) u, err := s.readDB.GetUserByName(tx, req.UserName)
if err != nil { if err != nil {
return err return err
} }
if u != nil { if u != nil {
return util.NewErrBadRequest(errors.Errorf("user with name %q already exists", u.UserName)) 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 return nil
}) })
if err != nil { if err != nil {
return nil, err 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) userj, err := json.Marshal(user)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to marshal user") 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 { if rs == nil {
return util.NewErrBadRequest(errors.Errorf("remote source %q doesn't exist", req.RemoteSourceName)) 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 return nil
}) })
if err != nil { if err != nil {

View File

@ -39,12 +39,12 @@ func (c *CommandHandler) CreateUser(ctx context.Context, req *CreateUserRequest)
return nil, errors.Errorf("invalid user name %q", req.UserName) return nil, errors.Errorf("invalid user name %q", req.UserName)
} }
u := &types.User{ creq := &csapi.CreateUserRequest{
UserName: req.UserName, UserName: req.UserName,
} }
c.log.Infof("creating user") c.log.Infof("creating user")
u, _, err := c.configstoreClient.CreateUser(ctx, u) u, _, err := c.configstoreClient.CreateUser(ctx, creq)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to create user") return nil, errors.Wrapf(err, "failed to create user")
} }