auth_utils.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package httpd
  2. import (
  3. "net/http"
  4. "time"
  5. "github.com/go-chi/jwtauth"
  6. "github.com/lestrrat-go/jwx/jwt"
  7. "github.com/rs/xid"
  8. "github.com/drakkan/sftpgo/dataprovider"
  9. "github.com/drakkan/sftpgo/utils"
  10. )
  11. const (
  12. claimUsernameKey = "username"
  13. claimPermissionsKey = "permissions"
  14. basicRealm = "Basic realm=\"SFTPGo\""
  15. )
  16. var (
  17. tokenDuration = 10 * time.Minute
  18. tokenRefreshMin = 5 * time.Minute
  19. )
  20. type jwtTokenClaims struct {
  21. Username string
  22. Permissions []string
  23. Signature string
  24. }
  25. func (c *jwtTokenClaims) asMap() map[string]interface{} {
  26. claims := make(map[string]interface{})
  27. claims[claimUsernameKey] = c.Username
  28. claims[claimPermissionsKey] = c.Permissions
  29. claims[jwt.SubjectKey] = c.Signature
  30. return claims
  31. }
  32. func (c *jwtTokenClaims) Decode(token map[string]interface{}) {
  33. username := token[claimUsernameKey]
  34. switch v := username.(type) {
  35. case string:
  36. c.Username = v
  37. }
  38. signature := token[jwt.SubjectKey]
  39. switch v := signature.(type) {
  40. case string:
  41. c.Signature = v
  42. }
  43. permissions := token[claimPermissionsKey]
  44. switch v := permissions.(type) {
  45. case []interface{}:
  46. for _, elem := range v {
  47. switch elemValue := elem.(type) {
  48. case string:
  49. c.Permissions = append(c.Permissions, elemValue)
  50. }
  51. }
  52. }
  53. }
  54. func (c *jwtTokenClaims) isCriticalPermRemoved(permissions []string) bool {
  55. if utils.IsStringInSlice(dataprovider.PermAdminAny, permissions) {
  56. return false
  57. }
  58. if (utils.IsStringInSlice(dataprovider.PermAdminManageAdmins, c.Permissions) ||
  59. utils.IsStringInSlice(dataprovider.PermAdminAny, c.Permissions)) &&
  60. !utils.IsStringInSlice(dataprovider.PermAdminManageAdmins, permissions) &&
  61. !utils.IsStringInSlice(dataprovider.PermAdminAny, permissions) {
  62. return true
  63. }
  64. return false
  65. }
  66. func (c *jwtTokenClaims) hasPerm(perm string) bool {
  67. if utils.IsStringInSlice(dataprovider.PermAdminAny, c.Permissions) {
  68. return true
  69. }
  70. return utils.IsStringInSlice(perm, c.Permissions)
  71. }
  72. func (c *jwtTokenClaims) createTokenResponse(tokenAuth *jwtauth.JWTAuth) (map[string]interface{}, error) {
  73. claims := c.asMap()
  74. now := time.Now().UTC()
  75. claims[jwt.JwtIDKey] = xid.New().String()
  76. claims[jwt.NotBeforeKey] = now.Add(-30 * time.Second)
  77. claims[jwt.ExpirationKey] = now.Add(tokenDuration)
  78. token, tokenString, err := tokenAuth.Encode(claims)
  79. if err != nil {
  80. return nil, err
  81. }
  82. response := make(map[string]interface{})
  83. response["access_token"] = tokenString
  84. response["expires_at"] = token.Expiration().Format(time.RFC3339)
  85. return response, nil
  86. }
  87. func (c *jwtTokenClaims) createAndSetCookie(w http.ResponseWriter, r *http.Request, tokenAuth *jwtauth.JWTAuth) error {
  88. resp, err := c.createTokenResponse(tokenAuth)
  89. if err != nil {
  90. return err
  91. }
  92. http.SetCookie(w, &http.Cookie{
  93. Name: "jwt",
  94. Value: resp["access_token"].(string),
  95. Path: webBasePath,
  96. Expires: time.Now().Add(tokenDuration),
  97. HttpOnly: true,
  98. Secure: r.TLS != nil,
  99. })
  100. return nil
  101. }
  102. func (c *jwtTokenClaims) removeCookie(w http.ResponseWriter, r *http.Request) {
  103. http.SetCookie(w, &http.Cookie{
  104. Name: "jwt",
  105. Value: "",
  106. Path: webBasePath,
  107. MaxAge: -1,
  108. HttpOnly: true,
  109. Secure: r.TLS != nil,
  110. })
  111. invalidateToken(r)
  112. }
  113. func isTokenInvalidated(r *http.Request) bool {
  114. isTokenFound := false
  115. token := jwtauth.TokenFromHeader(r)
  116. if token != "" {
  117. isTokenFound = true
  118. if _, ok := invalidatedJWTTokens.Load(token); ok {
  119. return true
  120. }
  121. }
  122. token = jwtauth.TokenFromCookie(r)
  123. if token != "" {
  124. isTokenFound = true
  125. if _, ok := invalidatedJWTTokens.Load(token); ok {
  126. return true
  127. }
  128. }
  129. return !isTokenFound
  130. }
  131. func invalidateToken(r *http.Request) {
  132. tokenString := jwtauth.TokenFromHeader(r)
  133. if tokenString != "" {
  134. invalidatedJWTTokens.Store(tokenString, time.Now().UTC().Add(tokenDuration))
  135. }
  136. tokenString = jwtauth.TokenFromCookie(r)
  137. if tokenString != "" {
  138. invalidatedJWTTokens.Store(tokenString, time.Now().UTC().Add(tokenDuration))
  139. }
  140. }
  141. func getAdminFromToken(r *http.Request) *dataprovider.Admin {
  142. admin := &dataprovider.Admin{}
  143. _, claims, err := jwtauth.FromContext(r.Context())
  144. if err != nil {
  145. return admin
  146. }
  147. tokenClaims := jwtTokenClaims{}
  148. tokenClaims.Decode(claims)
  149. admin.Username = tokenClaims.Username
  150. admin.Permissions = tokenClaims.Permissions
  151. return admin
  152. }