local_bearer.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package auth
  2. import (
  3. "crypto/subtle"
  4. "strings"
  5. types "github.com/OliveTin/OliveTin/internal/auth/authpublic"
  6. "github.com/OliveTin/OliveTin/internal/config"
  7. log "github.com/sirupsen/logrus"
  8. )
  9. const localBearerScheme = "Bearer"
  10. func constantTimeEqualString(a, b string) bool {
  11. if len(a) != len(b) {
  12. return false
  13. }
  14. return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
  15. }
  16. func bearerTokenFromAuthorizationHeader(authz string) (string, bool) {
  17. idx := strings.IndexByte(authz, ' ')
  18. if idx <= 0 {
  19. return "", false
  20. }
  21. if !strings.EqualFold(authz[:idx], localBearerScheme) {
  22. return "", false
  23. }
  24. token := strings.TrimSpace(authz[idx+1:])
  25. if token == "" {
  26. return "", false
  27. }
  28. return token, true
  29. }
  30. func localUserHasAPIKey(user *config.LocalUser) bool {
  31. return user != nil && user.ApiKey != ""
  32. }
  33. func findLocalUserByAPIKey(cfg *config.Config, token string) *config.LocalUser {
  34. for _, user := range cfg.AuthLocalUsers.Users {
  35. if !localUserHasAPIKey(user) {
  36. continue
  37. }
  38. if constantTimeEqualString(token, user.ApiKey) {
  39. return user
  40. }
  41. }
  42. return nil
  43. }
  44. func localBearerAuthorizationHasEmptyCredential(authz string) bool {
  45. idx := strings.IndexByte(authz, ' ')
  46. return idx > 0 &&
  47. strings.EqualFold(authz[:idx], localBearerScheme) &&
  48. strings.TrimSpace(authz[idx+1:]) == ""
  49. }
  50. func logLocalBearerAPIKeyParseFailure(authz string) {
  51. if strings.TrimSpace(authz) == "" {
  52. return
  53. }
  54. if localBearerAuthorizationHasEmptyCredential(authz) {
  55. log.Debugf("Local bearer API key: rejected (empty credential after Bearer prefix)")
  56. return
  57. }
  58. log.Tracef("Local bearer API key: skipped (Authorization is not a Bearer token)")
  59. }
  60. func checkUserFromLocalBearerApiKey(context *types.AuthCheckingContext) *types.AuthenticatedUser {
  61. if !context.Config.AuthLocalUsers.Enabled {
  62. log.Tracef("Local bearer API key: skipped (authLocalUsers disabled)")
  63. return nil
  64. }
  65. authz := context.Request.Header.Get("Authorization")
  66. token, ok := bearerTokenFromAuthorizationHeader(authz)
  67. if !ok {
  68. logLocalBearerAPIKeyParseFailure(authz)
  69. return nil
  70. }
  71. log.Debugf("Local bearer API key: checking configured local user API keys")
  72. user := findLocalUserByAPIKey(context.Config, token)
  73. if user == nil {
  74. log.Debugf("Local bearer API key: rejected (no matching local user)")
  75. return nil
  76. }
  77. log.WithFields(log.Fields{
  78. "username": user.Username,
  79. "usergroup": user.Usergroup,
  80. }).Debugf("Local bearer API key: authenticated")
  81. return &types.AuthenticatedUser{
  82. Username: user.Username,
  83. UsergroupLine: user.Usergroup,
  84. Provider: "local",
  85. }
  86. }