create_ory.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package user
  2. import (
  3. "errors"
  4. "net/http"
  5. "github.com/porter-dev/porter/internal/analytics"
  6. "github.com/porter-dev/porter/internal/telemetry"
  7. "gorm.io/gorm"
  8. "github.com/porter-dev/porter/api/server/shared/apierrors"
  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 user creation triggered by an ory action
  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. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusForbidden))
  44. return
  45. }
  46. if oryActionCookie.Value != u.Config().OryActionKey {
  47. err = telemetry.Error(ctx, span, nil, "cookie does not match")
  48. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusForbidden))
  49. return
  50. }
  51. request := &CreateOryUserRequest{}
  52. ok := u.DecodeAndValidate(w, r, request)
  53. if !ok {
  54. err = telemetry.Error(ctx, span, nil, "invalid request")
  55. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  56. return
  57. }
  58. telemetry.WithAttributes(span,
  59. telemetry.AttributeKV{Key: "email", Value: request.Email},
  60. telemetry.AttributeKV{Key: "ory-id", Value: request.OryId},
  61. telemetry.AttributeKV{Key: "referral", Value: request.Referral},
  62. )
  63. if request.Email == "" {
  64. err = telemetry.Error(ctx, span, nil, "email is required")
  65. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  66. return
  67. }
  68. if request.OryId == "" {
  69. err = telemetry.Error(ctx, span, nil, "ory_id is required")
  70. u.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  71. return
  72. }
  73. user := &models.User{
  74. Model: gorm.Model{},
  75. Email: request.Email,
  76. EmailVerified: false,
  77. AuthProvider: models.AuthProvider_Ory,
  78. ExternalId: request.OryId,
  79. }
  80. existingUser, err := u.Repo().User().ReadUserByEmail(user.Email)
  81. if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  82. err = telemetry.Error(ctx, span, err, "error reading user by email")
  83. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  84. return
  85. }
  86. if existingUser == nil || existingUser.ID == 0 {
  87. user, err = u.Repo().User().CreateUser(user)
  88. if err != nil {
  89. err = telemetry.Error(ctx, span, err, "error creating user")
  90. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  91. return
  92. }
  93. _ = u.Config().AnalyticsClient.Identify(analytics.CreateSegmentIdentifyUser(user))
  94. _ = u.Config().AnalyticsClient.Track(analytics.UserCreateTrack(&analytics.UserCreateTrackOpts{
  95. UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
  96. Email: user.Email,
  97. FirstName: user.FirstName,
  98. LastName: user.LastName,
  99. CompanyName: user.CompanyName,
  100. ReferralMethod: request.Referral,
  101. }))
  102. } else {
  103. existingUser.AuthProvider = models.AuthProvider_Ory
  104. existingUser.ExternalId = request.OryId
  105. _, err = u.Repo().User().UpdateUser(existingUser)
  106. if err != nil {
  107. err = telemetry.Error(ctx, span, err, "error updating user")
  108. u.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  109. return
  110. }
  111. }
  112. }