restapi.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package httpservers
  2. import (
  3. "context"
  4. "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
  5. log "github.com/sirupsen/logrus"
  6. "google.golang.org/grpc/metadata"
  7. "google.golang.org/protobuf/reflect/protoreflect"
  8. "net/http"
  9. "strings"
  10. // apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
  11. config "github.com/OliveTin/OliveTin/internal/config"
  12. )
  13. func parseHttpHeaderForAuth(cfg *config.Config, req *http.Request) (string, string) {
  14. username, ok := req.Header[cfg.AuthHttpHeaderUsername]
  15. if !ok {
  16. log.Warnf("Config has AuthHttpHeaderUsername set to %v, but it was not found", cfg.AuthHttpHeaderUsername)
  17. return "", ""
  18. }
  19. if cfg.AuthHttpHeaderUserGroup != "" {
  20. usergroup, ok := req.Header[cfg.AuthHttpHeaderUserGroup]
  21. if ok {
  22. log.Debugf("HTTP Header Auth found a username and usergroup")
  23. return username[0], usergroup[0]
  24. } else {
  25. log.Warnf("Config has AuthHttpHeaderUserGroup set to %v, but it was not found", cfg.AuthHttpHeaderUserGroup)
  26. }
  27. }
  28. log.Debugf("HTTP Header Auth found a username, but usergroup is not being used")
  29. return username[0], ""
  30. }
  31. //gocyclo:ignore
  32. func parseRequestMetadata(cfg *config.Config, ctx context.Context, req *http.Request) metadata.MD {
  33. username := ""
  34. usergroup := ""
  35. provider := "unknown"
  36. sid := ""
  37. if cfg.AuthJwtHeader != "" {
  38. username, usergroup = parseJwtHeader(cfg, req)
  39. provider = "jwt-header"
  40. }
  41. if cfg.AuthJwtCookieName != "" {
  42. username, usergroup = parseJwtCookie(cfg, req)
  43. provider = "jwt-cookie"
  44. }
  45. if cfg.AuthHttpHeaderUsername != "" && username == "" {
  46. username, usergroup = parseHttpHeaderForAuth(cfg, req)
  47. provider = "http-header"
  48. }
  49. // if len(cfg.AuthOAuth2Providers) > 0 && username == "" {
  50. // username, usergroup, sid = parseOAuth2Cookie(req)
  51. // provider = "oauth2"
  52. // }
  53. if cfg.AuthLocalUsers.Enabled && username == "" {
  54. username, usergroup, sid = parseLocalUserCookie(cfg, req)
  55. provider = "local"
  56. }
  57. md := metadata.New(map[string]string{
  58. "username": username,
  59. "usergroup": usergroup,
  60. "provider": provider,
  61. "sid": sid,
  62. })
  63. log.Tracef("api request metadata: %+v", md)
  64. return md
  65. }
  66. func parseJwtHeader(cfg *config.Config, req *http.Request) (string, string) {
  67. // JWTs in the Authorization header are usually prefixed with "Bearer " which is not part of the JWT token.
  68. return parseJwt(cfg, strings.TrimPrefix(req.Header.Get(cfg.AuthJwtHeader), "Bearer "))
  69. }
  70. func (h *OAuth2Handler) forwardResponseHandler(cfg *config.Config, ctx context.Context, w http.ResponseWriter, msg protoreflect.ProtoMessage) error {
  71. md, ok := runtime.ServerMetadataFromContext(ctx)
  72. if !ok {
  73. log.Warn("Could not get ServerMetadata from context")
  74. return nil
  75. }
  76. forwardResponseHandlerLoginLocalUser(cfg, md.HeaderMD, w)
  77. h.forwardResponseHandlerLogout(cfg, md.HeaderMD, w)
  78. return nil
  79. }
  80. func (h *OAuth2Handler) forwardResponseHandlerLogout(cfg *config.Config, md metadata.MD, w http.ResponseWriter) {
  81. if getMetadataKeyOrEmpty(md, "logout-provider") != "" {
  82. sid := getMetadataKeyOrEmpty(md, "logout-sid")
  83. delete(h.registeredStates, sid)
  84. http.SetCookie(
  85. w,
  86. &http.Cookie{
  87. Name: "olivetin-sid-oauth",
  88. MaxAge: 31556952, // 1 year
  89. Value: "",
  90. HttpOnly: true,
  91. Path: "/",
  92. },
  93. )
  94. deleteLocalUserSession(cfg, "local", sid)
  95. http.SetCookie(
  96. w,
  97. &http.Cookie{
  98. Name: "olivetin-sid-local",
  99. MaxAge: 31556952, // 1 year
  100. Value: "",
  101. HttpOnly: true,
  102. Path: "/",
  103. },
  104. )
  105. w.Header().Set("Content-Type", "text/html")
  106. // We cannot send a HTTP redirect here, because we don't have access to req.
  107. w.Write([]byte("<script>window.location.href = '/';</script>"))
  108. }
  109. }
  110. func getMetadataKeyOrEmpty(md metadata.MD, key string) string {
  111. mdValues := md.Get(key)
  112. if len(mdValues) > 0 {
  113. return mdValues[0]
  114. }
  115. return ""
  116. }