middleware.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. package httpd
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "strings"
  7. "time"
  8. "github.com/go-chi/jwtauth/v5"
  9. "github.com/lestrrat-go/jwx/jwt"
  10. "github.com/rs/xid"
  11. "github.com/sftpgo/sdk"
  12. "github.com/drakkan/sftpgo/v2/common"
  13. "github.com/drakkan/sftpgo/v2/dataprovider"
  14. "github.com/drakkan/sftpgo/v2/logger"
  15. "github.com/drakkan/sftpgo/v2/util"
  16. )
  17. var (
  18. forwardedProtoKey = &contextKey{"forwarded proto"}
  19. errInvalidToken = errors.New("invalid JWT token")
  20. )
  21. type contextKey struct {
  22. name string
  23. }
  24. func (k *contextKey) String() string {
  25. return "context value " + k.name
  26. }
  27. func validateJWTToken(w http.ResponseWriter, r *http.Request, audience tokenAudience) error {
  28. token, _, err := jwtauth.FromContext(r.Context())
  29. var redirectPath string
  30. if audience == tokenAudienceWebAdmin {
  31. redirectPath = webLoginPath
  32. } else {
  33. redirectPath = webClientLoginPath
  34. }
  35. isAPIToken := (audience == tokenAudienceAPI || audience == tokenAudienceAPIUser)
  36. if err != nil || token == nil {
  37. logger.Debug(logSender, "", "error getting jwt token: %v", err)
  38. if isAPIToken {
  39. sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
  40. } else {
  41. http.Redirect(w, r, redirectPath, http.StatusFound)
  42. }
  43. return errInvalidToken
  44. }
  45. err = jwt.Validate(token)
  46. if err != nil {
  47. logger.Debug(logSender, "", "error validating jwt token: %v", err)
  48. if isAPIToken {
  49. sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
  50. } else {
  51. http.Redirect(w, r, redirectPath, http.StatusFound)
  52. }
  53. return errInvalidToken
  54. }
  55. if isTokenInvalidated(r) {
  56. logger.Debug(logSender, "", "the token has been invalidated")
  57. if isAPIToken {
  58. sendAPIResponse(w, r, nil, "Your token is no longer valid", http.StatusUnauthorized)
  59. } else {
  60. http.Redirect(w, r, redirectPath, http.StatusFound)
  61. }
  62. return errInvalidToken
  63. }
  64. // a user with a partial token will be always redirected to the appropriate two factor auth page
  65. if err := checkPartialAuth(w, r, audience, token.Audience()); err != nil {
  66. return err
  67. }
  68. if !util.IsStringInSlice(audience, token.Audience()) {
  69. logger.Debug(logSender, "", "the token is not valid for audience %#v", audience)
  70. if isAPIToken {
  71. sendAPIResponse(w, r, nil, "Your token audience is not valid", http.StatusUnauthorized)
  72. } else {
  73. http.Redirect(w, r, redirectPath, http.StatusFound)
  74. }
  75. return errInvalidToken
  76. }
  77. return nil
  78. }
  79. func validateJWTPartialToken(w http.ResponseWriter, r *http.Request, audience tokenAudience) error {
  80. token, _, err := jwtauth.FromContext(r.Context())
  81. var notFoundFunc func(w http.ResponseWriter, r *http.Request, err error)
  82. if audience == tokenAudienceWebAdminPartial {
  83. notFoundFunc = renderNotFoundPage
  84. } else {
  85. notFoundFunc = renderClientNotFoundPage
  86. }
  87. if err != nil || token == nil || jwt.Validate(token) != nil {
  88. notFoundFunc(w, r, nil)
  89. return errInvalidToken
  90. }
  91. if isTokenInvalidated(r) {
  92. notFoundFunc(w, r, nil)
  93. return errInvalidToken
  94. }
  95. if !util.IsStringInSlice(audience, token.Audience()) {
  96. logger.Debug(logSender, "", "the token is not valid for audience %#v", audience)
  97. notFoundFunc(w, r, nil)
  98. return errInvalidToken
  99. }
  100. return nil
  101. }
  102. func jwtAuthenticatorPartial(audience tokenAudience) func(next http.Handler) http.Handler {
  103. return func(next http.Handler) http.Handler {
  104. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  105. if err := validateJWTPartialToken(w, r, audience); err != nil {
  106. return
  107. }
  108. // Token is authenticated, pass it through
  109. next.ServeHTTP(w, r)
  110. })
  111. }
  112. }
  113. func jwtAuthenticatorAPI(next http.Handler) http.Handler {
  114. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  115. if err := validateJWTToken(w, r, tokenAudienceAPI); err != nil {
  116. return
  117. }
  118. // Token is authenticated, pass it through
  119. next.ServeHTTP(w, r)
  120. })
  121. }
  122. func jwtAuthenticatorAPIUser(next http.Handler) http.Handler {
  123. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  124. if err := validateJWTToken(w, r, tokenAudienceAPIUser); err != nil {
  125. return
  126. }
  127. // Token is authenticated, pass it through
  128. next.ServeHTTP(w, r)
  129. })
  130. }
  131. func jwtAuthenticatorWebAdmin(next http.Handler) http.Handler {
  132. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  133. if err := validateJWTToken(w, r, tokenAudienceWebAdmin); err != nil {
  134. return
  135. }
  136. // Token is authenticated, pass it through
  137. next.ServeHTTP(w, r)
  138. })
  139. }
  140. func jwtAuthenticatorWebClient(next http.Handler) http.Handler {
  141. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  142. if err := validateJWTToken(w, r, tokenAudienceWebClient); err != nil {
  143. return
  144. }
  145. // Token is authenticated, pass it through
  146. next.ServeHTTP(w, r)
  147. })
  148. }
  149. func checkHTTPUserPerm(perm string) func(next http.Handler) http.Handler {
  150. return func(next http.Handler) http.Handler {
  151. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  152. _, claims, err := jwtauth.FromContext(r.Context())
  153. if err != nil {
  154. if isWebRequest(r) {
  155. renderClientBadRequestPage(w, r, err)
  156. } else {
  157. sendAPIResponse(w, r, err, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  158. }
  159. return
  160. }
  161. tokenClaims := jwtTokenClaims{}
  162. tokenClaims.Decode(claims)
  163. // for web client perms are negated and not granted
  164. if tokenClaims.hasPerm(perm) {
  165. if isWebRequest(r) {
  166. renderClientForbiddenPage(w, r, "You don't have permission for this action")
  167. } else {
  168. sendAPIResponse(w, r, nil, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  169. }
  170. return
  171. }
  172. next.ServeHTTP(w, r)
  173. })
  174. }
  175. }
  176. func checkPerm(perm string) func(next http.Handler) http.Handler {
  177. return func(next http.Handler) http.Handler {
  178. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  179. _, claims, err := jwtauth.FromContext(r.Context())
  180. if err != nil {
  181. if isWebRequest(r) {
  182. renderBadRequestPage(w, r, err)
  183. } else {
  184. sendAPIResponse(w, r, err, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  185. }
  186. return
  187. }
  188. tokenClaims := jwtTokenClaims{}
  189. tokenClaims.Decode(claims)
  190. if !tokenClaims.hasPerm(perm) {
  191. if isWebRequest(r) {
  192. renderForbiddenPage(w, r, "You don't have permission for this action")
  193. } else {
  194. sendAPIResponse(w, r, nil, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  195. }
  196. return
  197. }
  198. next.ServeHTTP(w, r)
  199. })
  200. }
  201. }
  202. func verifyCSRFHeader(next http.Handler) http.Handler {
  203. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  204. tokenString := r.Header.Get(csrfHeaderToken)
  205. token, err := jwtauth.VerifyToken(csrfTokenAuth, tokenString)
  206. if err != nil || token == nil {
  207. logger.Debug(logSender, "", "error validating CSRF header: %v", err)
  208. sendAPIResponse(w, r, err, "Invalid token", http.StatusForbidden)
  209. return
  210. }
  211. if !util.IsStringInSlice(tokenAudienceCSRF, token.Audience()) {
  212. logger.Debug(logSender, "", "error validating CSRF header audience")
  213. sendAPIResponse(w, r, errors.New("the token is not valid"), "", http.StatusForbidden)
  214. return
  215. }
  216. next.ServeHTTP(w, r)
  217. })
  218. }
  219. func checkAPIKeyAuth(tokenAuth *jwtauth.JWTAuth, scope dataprovider.APIKeyScope) func(next http.Handler) http.Handler {
  220. return func(next http.Handler) http.Handler {
  221. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  222. apiKey := r.Header.Get("X-SFTPGO-API-KEY")
  223. if apiKey == "" {
  224. next.ServeHTTP(w, r)
  225. return
  226. }
  227. keyParams := strings.SplitN(apiKey, ".", 3)
  228. if len(keyParams) < 2 {
  229. logger.Debug(logSender, "", "invalid api key %#v", apiKey)
  230. sendAPIResponse(w, r, errors.New("the provided api key is not valid"), "", http.StatusBadRequest)
  231. return
  232. }
  233. keyID := keyParams[0]
  234. key := keyParams[1]
  235. apiUser := ""
  236. if len(keyParams) > 2 {
  237. apiUser = keyParams[2]
  238. }
  239. k, err := dataprovider.APIKeyExists(keyID)
  240. if err != nil {
  241. logger.Debug(logSender, "invalid api key %#v: %v", apiKey, err)
  242. sendAPIResponse(w, r, errors.New("the provided api key is not valid"), "", http.StatusBadRequest)
  243. return
  244. }
  245. if err := k.Authenticate(key); err != nil {
  246. logger.Debug(logSender, "unable to authenticate api key %#v: %v", apiKey, err)
  247. sendAPIResponse(w, r, fmt.Errorf("the provided api key cannot be authenticated"), "", http.StatusUnauthorized)
  248. return
  249. }
  250. if scope == dataprovider.APIKeyScopeAdmin {
  251. if k.Admin != "" {
  252. apiUser = k.Admin
  253. }
  254. if err := authenticateAdminWithAPIKey(apiUser, keyID, tokenAuth, r); err != nil {
  255. logger.Debug(logSender, "", "unable to authenticate admin %#v associated with api key %#v: %v",
  256. apiUser, apiKey, err)
  257. sendAPIResponse(w, r, fmt.Errorf("the admin associated with the provided api key cannot be authenticated"),
  258. "", http.StatusUnauthorized)
  259. return
  260. }
  261. } else {
  262. if k.User != "" {
  263. apiUser = k.User
  264. }
  265. if err := authenticateUserWithAPIKey(apiUser, keyID, tokenAuth, r); err != nil {
  266. logger.Debug(logSender, "", "unable to authenticate user %#v associated with api key %#v: %v",
  267. apiUser, apiKey, err)
  268. code := http.StatusUnauthorized
  269. if errors.Is(err, common.ErrInternalFailure) {
  270. code = http.StatusInternalServerError
  271. }
  272. sendAPIResponse(w, r, errors.New("the user associated with the provided api key cannot be authenticated"),
  273. "", code)
  274. return
  275. }
  276. }
  277. dataprovider.UpdateAPIKeyLastUse(&k) //nolint:errcheck
  278. next.ServeHTTP(w, r)
  279. })
  280. }
  281. }
  282. func forbidAPIKeyAuthentication(next http.Handler) http.Handler {
  283. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  284. claims, err := getTokenClaims(r)
  285. if err != nil || claims.Username == "" {
  286. sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
  287. return
  288. }
  289. if claims.APIKeyID != "" {
  290. sendAPIResponse(w, r, nil, "API key authentication is not allowed", http.StatusForbidden)
  291. return
  292. }
  293. next.ServeHTTP(w, r)
  294. })
  295. }
  296. func authenticateAdminWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTAuth, r *http.Request) error {
  297. if username == "" {
  298. return errors.New("the provided key is not associated with any admin and no username was provided")
  299. }
  300. admin, err := dataprovider.AdminExists(username)
  301. if err != nil {
  302. return err
  303. }
  304. if !admin.Filters.AllowAPIKeyAuth {
  305. return fmt.Errorf("API key authentication disabled for admin %#v", admin.Username)
  306. }
  307. if err := admin.CanLogin(util.GetIPFromRemoteAddress(r.RemoteAddr)); err != nil {
  308. return err
  309. }
  310. c := jwtTokenClaims{
  311. Username: admin.Username,
  312. Permissions: admin.Permissions,
  313. Signature: admin.GetSignature(),
  314. APIKeyID: keyID,
  315. }
  316. resp, err := c.createTokenResponse(tokenAuth, tokenAudienceAPI)
  317. if err != nil {
  318. return err
  319. }
  320. r.Header.Set("Authorization", fmt.Sprintf("Bearer %v", resp["access_token"]))
  321. dataprovider.UpdateAdminLastLogin(&admin)
  322. return nil
  323. }
  324. func authenticateUserWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTAuth, r *http.Request) error {
  325. ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
  326. if username == "" {
  327. err := errors.New("the provided key is not associated with any user and no username was provided")
  328. updateLoginMetrics(&dataprovider.User{BaseUser: sdk.BaseUser{Username: username}}, ipAddr, err)
  329. return err
  330. }
  331. if err := common.Config.ExecutePostConnectHook(ipAddr, common.ProtocolHTTP); err != nil {
  332. return err
  333. }
  334. user, err := dataprovider.UserExists(username)
  335. if err != nil {
  336. updateLoginMetrics(&dataprovider.User{BaseUser: sdk.BaseUser{Username: username}}, ipAddr, err)
  337. return err
  338. }
  339. if !user.Filters.AllowAPIKeyAuth {
  340. err := fmt.Errorf("API key authentication disabled for user %#v", user.Username)
  341. updateLoginMetrics(&user, ipAddr, err)
  342. return err
  343. }
  344. if err := user.CheckLoginConditions(); err != nil {
  345. updateLoginMetrics(&user, ipAddr, err)
  346. return err
  347. }
  348. connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, xid.New().String())
  349. if err := checkHTTPClientUser(&user, r, connectionID); err != nil {
  350. updateLoginMetrics(&user, ipAddr, err)
  351. return err
  352. }
  353. lastLogin := util.GetTimeFromMsecSinceEpoch(user.LastLogin)
  354. diff := -time.Until(lastLogin)
  355. if diff < 0 || diff > 10*time.Minute {
  356. defer user.CloseFs() //nolint:errcheck
  357. err = user.CheckFsRoot(connectionID)
  358. if err != nil {
  359. updateLoginMetrics(&user, ipAddr, common.ErrInternalFailure)
  360. return common.ErrInternalFailure
  361. }
  362. }
  363. c := jwtTokenClaims{
  364. Username: user.Username,
  365. Permissions: user.Filters.WebClient,
  366. Signature: user.GetSignature(),
  367. APIKeyID: keyID,
  368. }
  369. resp, err := c.createTokenResponse(tokenAuth, tokenAudienceAPIUser)
  370. if err != nil {
  371. updateLoginMetrics(&user, ipAddr, common.ErrInternalFailure)
  372. return err
  373. }
  374. r.Header.Set("Authorization", fmt.Sprintf("Bearer %v", resp["access_token"]))
  375. dataprovider.UpdateLastLogin(&user)
  376. updateLoginMetrics(&user, ipAddr, nil)
  377. return nil
  378. }
  379. func checkPartialAuth(w http.ResponseWriter, r *http.Request, audience string, tokenAudience []string) error {
  380. if audience == tokenAudienceWebAdmin && util.IsStringInSlice(tokenAudienceWebAdminPartial, tokenAudience) {
  381. http.Redirect(w, r, webAdminTwoFactorPath, http.StatusFound)
  382. return errInvalidToken
  383. }
  384. if audience == tokenAudienceWebClient && util.IsStringInSlice(tokenAudienceWebClientPartial, tokenAudience) {
  385. http.Redirect(w, r, webClientTwoFactorPath, http.StatusFound)
  386. return errInvalidToken
  387. }
  388. return nil
  389. }