create_ory.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package user
  2. import (
  3. "errors"
  4. "net/http"
  5. "github.com/porter-dev/porter/internal/telemetry"
  6. "gorm.io/gorm"
  7. "github.com/porter-dev/porter/api/server/shared/apierrors"
  8. "github.com/porter-dev/porter/internal/analytics"
  9. "github.com/porter-dev/porter/internal/models"
  10. "github.com/porter-dev/porter/api/server/handlers"
  11. "github.com/porter-dev/porter/api/server/shared"
  12. "github.com/porter-dev/porter/api/server/shared/config"
  13. )
  14. // OryUserCreateHandler is the handler for user creation triggered by an ory action
  15. type OryUserCreateHandler struct {
  16. handlers.PorterHandlerReadWriter
  17. }
  18. // NewOryUserCreateHandler generates a new OryUserCreateHandler
  19. func NewOryUserCreateHandler(
  20. config *config.Config,
  21. decoderValidator shared.RequestDecoderValidator,
  22. writer shared.ResultWriter,
  23. ) *OryUserCreateHandler {
  24. return &OryUserCreateHandler{
  25. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  26. }
  27. }
  28. // CreateOryUserRequest is the expected request body for creating a user
  29. type CreateOryUserRequest struct {
  30. OryId string `json:"ory_id"`
  31. Email string `json:"email"`
  32. Referral string `json:"referral"`
  33. }
  34. // ServeHTTP handles the user creation triggered by an ory action
  35. func (u *OryUserCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  36. ctx, span := telemetry.NewSpan(r.Context(), "serve-create-ory-user")
  37. defer span.End()
  38. // this endpoint is not authenticated through middleware; instead, we check
  39. // for the presence of an ory action cookie that matches env
  40. oryActionCookie, err := r.Cookie("ory_action")
  41. if err != nil {
  42. err = telemetry.Error(ctx, span, err, "invalid ory action cookie")
  43. reqErr := apierrors.NewErrForbidden(err)
  44. apierrors.HandleAPIError(u.Config().Logger, u.Config().Alerter, w, r, reqErr, true)
  45. return
  46. }
  47. if oryActionCookie.Value != u.Config().OryActionKey {
  48. err = telemetry.Error(ctx, span, nil, "cookie does not match")
  49. reqErr := apierrors.NewErrForbidden(err)
  50. apierrors.HandleAPIError(u.Config().Logger, u.Config().Alerter, w, r, reqErr, true)
  51. return
  52. }
  53. request := &CreateOryUserRequest{}
  54. ok := u.DecodeAndValidate(w, r, request)
  55. if !ok {
  56. err = telemetry.Error(ctx, span, nil, "invalid request")
  57. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  58. return
  59. }
  60. telemetry.WithAttributes(span,
  61. telemetry.AttributeKV{Key: "email", Value: request.Email},
  62. telemetry.AttributeKV{Key: "ory-id", Value: request.OryId},
  63. telemetry.AttributeKV{Key: "referral", Value: request.Referral},
  64. )
  65. if request.Email == "" {
  66. err = telemetry.Error(ctx, span, nil, "email is required")
  67. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  68. return
  69. }
  70. if request.OryId == "" {
  71. err = telemetry.Error(ctx, span, nil, "ory_id is required")
  72. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  73. return
  74. }
  75. user := &models.User{
  76. Model: gorm.Model{},
  77. Email: request.Email,
  78. EmailVerified: false,
  79. AuthProvider: models.AuthProvider_Ory,
  80. ExternalId: request.OryId,
  81. }
  82. existingUser, err := u.Repo().User().ReadUserByEmail(user.Email)
  83. if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  84. err = telemetry.Error(ctx, span, err, "error reading user by email")
  85. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  86. return
  87. }
  88. if existingUser == nil || existingUser.ID == 0 {
  89. user, err = u.Repo().User().CreateUser(user)
  90. if err != nil {
  91. err = telemetry.Error(ctx, span, err, "error creating user")
  92. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  93. return
  94. }
  95. } else {
  96. existingUser.AuthProvider = models.AuthProvider_Ory
  97. existingUser.ExternalId = request.UserId
  98. _, err = u.Repo().User().UpdateUser(existingUser)
  99. if err != nil {
  100. err = telemetry.Error(ctx, span, err, "error updating user")
  101. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  102. return
  103. }
  104. }
  105. u.Config().AnalyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
  106. u.Config().AnalyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
  107. UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
  108. Email: user.Email,
  109. FirstName: user.FirstName,
  110. LastName: user.LastName,
  111. CompanyName: user.CompanyName,
  112. ReferralMethod: request.Referral,
  113. }))
  114. }