handler.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package handlers
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "github.com/porter-dev/porter/internal/telemetry"
  7. "github.com/porter-dev/porter/api/server/shared"
  8. "github.com/porter-dev/porter/api/server/shared/apierrors"
  9. "github.com/porter-dev/porter/api/server/shared/config"
  10. "github.com/porter-dev/porter/api/types"
  11. "github.com/porter-dev/porter/internal/models"
  12. "github.com/porter-dev/porter/internal/repository"
  13. )
  14. type PorterHandler interface {
  15. Config() *config.Config
  16. Repo() repository.Repository
  17. HandleAPIError(w http.ResponseWriter, r *http.Request, err apierrors.RequestError)
  18. HandleAPIErrorNoWrite(w http.ResponseWriter, r *http.Request, err apierrors.RequestError)
  19. PopulateOAuthSession(
  20. ctx context.Context,
  21. w http.ResponseWriter,
  22. r *http.Request,
  23. state string,
  24. isProject, isUser bool,
  25. integrationClient types.OAuthIntegrationClient,
  26. integrationID uint,
  27. ) error
  28. }
  29. type PorterHandlerWriter interface {
  30. PorterHandler
  31. shared.ResultWriter
  32. }
  33. type PorterHandlerReader interface {
  34. PorterHandler
  35. shared.RequestDecoderValidator
  36. }
  37. type PorterHandlerReadWriter interface {
  38. PorterHandlerWriter
  39. PorterHandlerReader
  40. }
  41. type DefaultPorterHandler struct {
  42. config *config.Config
  43. decoderValidator shared.RequestDecoderValidator
  44. writer shared.ResultWriter
  45. }
  46. func NewDefaultPorterHandler(
  47. config *config.Config,
  48. decoderValidator shared.RequestDecoderValidator,
  49. writer shared.ResultWriter,
  50. ) PorterHandlerReadWriter {
  51. return &DefaultPorterHandler{config, decoderValidator, writer}
  52. }
  53. func (d *DefaultPorterHandler) Config() *config.Config {
  54. return d.config
  55. }
  56. func (d *DefaultPorterHandler) Repo() repository.Repository {
  57. return d.config.Repo
  58. }
  59. func (d *DefaultPorterHandler) HandleAPIError(w http.ResponseWriter, r *http.Request, err apierrors.RequestError) {
  60. apierrors.HandleAPIError(d.Config().Logger, d.Config().Alerter, w, r, err, true)
  61. }
  62. func (d *DefaultPorterHandler) HandleAPIErrorNoWrite(w http.ResponseWriter, r *http.Request, err apierrors.RequestError) {
  63. apierrors.HandleAPIError(d.Config().Logger, d.Config().Alerter, w, r, err, false)
  64. }
  65. func (d *DefaultPorterHandler) WriteResult(w http.ResponseWriter, r *http.Request, v interface{}) {
  66. d.writer.WriteResult(w, r, v)
  67. }
  68. func (d *DefaultPorterHandler) DecodeAndValidate(w http.ResponseWriter, r *http.Request, v interface{}) bool {
  69. return d.decoderValidator.DecodeAndValidate(w, r, v)
  70. }
  71. func (d *DefaultPorterHandler) DecodeAndValidateNoWrite(r *http.Request, v interface{}) error {
  72. return d.decoderValidator.DecodeAndValidateNoWrite(r, v)
  73. }
  74. func IgnoreAPIError(w http.ResponseWriter, r *http.Request, err apierrors.RequestError) {
  75. return
  76. }
  77. func (d *DefaultPorterHandler) PopulateOAuthSession(
  78. ctx context.Context,
  79. w http.ResponseWriter,
  80. r *http.Request,
  81. state string,
  82. isProject, isUser bool,
  83. integrationClient types.OAuthIntegrationClient,
  84. integrationID uint,
  85. ) error {
  86. ctx, span := telemetry.NewSpan(ctx, "handler-populate-oauth-session")
  87. defer span.End()
  88. session, err := d.Config().Store.Get(r, d.Config().ServerConf.CookieName)
  89. if err != nil {
  90. return telemetry.Error(ctx, span, err, "could not get session")
  91. }
  92. // need state parameter to validate when redirected
  93. session.Values["state"] = state
  94. // check if redirect uri is populated, then overwrite
  95. if redirect := r.URL.Query().Get("redirect_uri"); redirect != "" {
  96. session.Values["redirect_uri"] = redirect
  97. }
  98. if isProject {
  99. project, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  100. if project == nil {
  101. return telemetry.Error(ctx, span, nil, "could not read project")
  102. }
  103. session.Values["project_id"] = project.ID
  104. }
  105. if isUser {
  106. user, _ := r.Context().Value(types.UserScope).(*models.User)
  107. if user == nil {
  108. return telemetry.Error(ctx, span, nil, "could not read user")
  109. }
  110. session.Values["user_id"] = user.ID
  111. }
  112. if integrationID != 0 && len(integrationClient) > 0 {
  113. session.Values["integration_id"] = integrationID
  114. session.Values["integration_client"] = string(integrationClient)
  115. }
  116. if err := session.Save(r, w); err != nil {
  117. return telemetry.Error(ctx, span, err, "could not save session")
  118. }
  119. return nil
  120. }
  121. type Unavailable struct {
  122. config *config.Config
  123. handlerID string
  124. }
  125. func NewUnavailable(config *config.Config, handlerID string) *Unavailable {
  126. return &Unavailable{config, handlerID}
  127. }
  128. func (u *Unavailable) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  129. apierrors.HandleAPIError(u.config.Logger, u.config.Alerter, w, r, apierrors.NewErrPassThroughToClient(
  130. fmt.Errorf("%s not available in community edition", u.handlerID),
  131. http.StatusBadRequest,
  132. ), true, apierrors.ErrorOpts{
  133. Code: types.ErrCodeUnavailable,
  134. })
  135. }