create.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package user
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "gorm.io/gorm"
  7. "github.com/porter-dev/porter/internal/telemetry"
  8. "github.com/porter-dev/porter/api/server/authn"
  9. "github.com/porter-dev/porter/api/server/handlers"
  10. "github.com/porter-dev/porter/api/server/shared"
  11. "github.com/porter-dev/porter/api/server/shared/apierrors"
  12. "github.com/porter-dev/porter/api/server/shared/config"
  13. "github.com/porter-dev/porter/api/types"
  14. "github.com/porter-dev/porter/internal/analytics"
  15. "github.com/porter-dev/porter/internal/models"
  16. "github.com/porter-dev/porter/internal/repository"
  17. "golang.org/x/crypto/bcrypt"
  18. )
  19. type UserCreateHandler struct {
  20. handlers.PorterHandlerReadWriter
  21. }
  22. func NewUserCreateHandler(
  23. config *config.Config,
  24. decoderValidator shared.RequestDecoderValidator,
  25. writer shared.ResultWriter,
  26. ) *UserCreateHandler {
  27. return &UserCreateHandler{
  28. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  29. }
  30. }
  31. func (u *UserCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  32. ctx, span := telemetry.NewSpan(r.Context(), "serve-user-create")
  33. defer span.End()
  34. r = r.Clone(ctx)
  35. request := &types.CreateUserRequest{}
  36. ok := u.DecodeAndValidate(w, r, request)
  37. if !ok {
  38. err := telemetry.Error(ctx, span, nil, "error decoding and validating request")
  39. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  40. return
  41. }
  42. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "email", Value: request.Email})
  43. if request.Email == "" {
  44. err := fmt.Errorf("email is required")
  45. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  46. return
  47. }
  48. newUser := &models.User{
  49. Email: request.Email,
  50. Password: request.Password,
  51. FirstName: request.FirstName,
  52. LastName: request.LastName,
  53. CompanyName: request.CompanyName,
  54. AuthProvider: request.AuthProvider,
  55. ExternalId: request.ExternalId,
  56. }
  57. if request.AuthProvider != "" {
  58. telemetry.WithAttributes(span,
  59. telemetry.AttributeKV{Key: "auth-provider", Value: request.AuthProvider},
  60. telemetry.AttributeKV{Key: "external-id", Value: request.ExternalId},
  61. )
  62. user, err := u.Repo().User().ReadUserByAuthProvider(request.AuthProvider, request.ExternalId)
  63. if err != nil {
  64. if !errors.Is(err, gorm.ErrRecordNotFound) {
  65. telemetry.Error(ctx, span, err, "error reading user by auth provider")
  66. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  67. return
  68. }
  69. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "new-user", Value: true})
  70. newUser, err = u.Repo().User().CreateUser(newUser)
  71. if err != nil {
  72. telemetry.Error(ctx, span, err, "error creating user")
  73. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  74. return
  75. }
  76. user = newUser
  77. }
  78. u.Config().AnalyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
  79. u.Config().AnalyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
  80. UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
  81. Email: user.Email,
  82. FirstName: user.FirstName,
  83. LastName: user.LastName,
  84. CompanyName: user.CompanyName,
  85. ReferralMethod: request.ReferralMethod,
  86. }))
  87. u.WriteResult(w, r, user.ToUserType())
  88. }
  89. // check if user exists
  90. doesExist := doesUserExist(u.Repo().User(), newUser)
  91. if doesExist {
  92. err := fmt.Errorf("email already taken")
  93. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  94. return
  95. }
  96. if err := checkUserRestrictions(u.Config().ServerConf, request.Email); err != nil {
  97. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  98. return
  99. }
  100. // hash the password using bcrypt
  101. hashedPw, err := bcrypt.GenerateFromPassword([]byte(newUser.Password), 8)
  102. if err != nil {
  103. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  104. return
  105. }
  106. newUser.Password = string(hashedPw)
  107. // write the user to the db
  108. newUser, err = u.Repo().User().CreateUser(newUser)
  109. if err != nil {
  110. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  111. return
  112. }
  113. err = addUserToDefaultProject(u.Config(), newUser)
  114. if err != nil {
  115. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  116. return
  117. }
  118. // save the user as authenticated in the session
  119. redirect, err := authn.SaveUserAuthenticated(w, r, u.Config(), newUser)
  120. if err != nil {
  121. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  122. return
  123. }
  124. // non-fatal send email verification
  125. if !newUser.EmailVerified {
  126. err = startEmailVerification(u.Config(), w, r, newUser)
  127. if err != nil {
  128. u.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
  129. }
  130. }
  131. u.Config().AnalyticsClient.Identify(analytics.CreateSegmentIdentifyUser(newUser))
  132. u.Config().AnalyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
  133. UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(newUser.ID),
  134. Email: newUser.Email,
  135. FirstName: newUser.FirstName,
  136. LastName: newUser.LastName,
  137. CompanyName: newUser.CompanyName,
  138. ReferralMethod: request.ReferralMethod,
  139. }))
  140. if redirect != "" {
  141. http.Redirect(w, r, redirect, http.StatusFound)
  142. return
  143. }
  144. u.WriteResult(w, r, newUser.ToUserType())
  145. }
  146. func doesUserExist(userRepo repository.UserRepository, user *models.User) bool {
  147. user, err := userRepo.ReadUserByEmail(user.Email)
  148. return user != nil && err == nil
  149. }
  150. // addUserToDefaultProject adds the created user to any default projects if required by
  151. // config variables.
  152. func addUserToDefaultProject(config *config.Config, user *models.User) error {
  153. if config.ServerConf.InitInCluster {
  154. // if this is the first user, add the user to the default project
  155. if user.ID == 1 {
  156. // read the default project
  157. project, err := config.Repo.Project().ReadProject(1)
  158. if err != nil {
  159. return err
  160. }
  161. // create a new Role with the user as the admin
  162. _, err = config.Repo.Project().CreateProjectRole(project, &models.Role{
  163. Role: types.Role{
  164. UserID: user.ID,
  165. ProjectID: project.ID,
  166. Kind: types.RoleAdmin,
  167. },
  168. })
  169. if err != nil {
  170. return err
  171. }
  172. }
  173. }
  174. return nil
  175. }