mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 14:50:55 +03:00
@@ -15,7 +15,10 @@ import (
|
||||
"github.com/drakkan/sftpgo/utils"
|
||||
)
|
||||
|
||||
var connAddrKey = &contextKey{"connection address"}
|
||||
var (
|
||||
connAddrKey = &contextKey{"connection address"}
|
||||
errInvalidToken = errors.New("invalid JWT token")
|
||||
)
|
||||
|
||||
type contextKey struct {
|
||||
name string
|
||||
@@ -32,30 +35,60 @@ func saveConnectionAddress(next http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func jwtAuthenticator(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
token, _, err := jwtauth.FromContext(r.Context())
|
||||
func validateJWTToken(w http.ResponseWriter, r *http.Request, audience tokenAudience) error {
|
||||
token, _, err := jwtauth.FromContext(r.Context())
|
||||
|
||||
if err != nil || token == nil {
|
||||
logger.Debug(logSender, "", "error getting jwt token: %v", err)
|
||||
sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
var redirectPath string
|
||||
if audience == tokenAudienceWebAdmin {
|
||||
redirectPath = webLoginPath
|
||||
} else {
|
||||
redirectPath = webClientLoginPath
|
||||
}
|
||||
|
||||
err = jwt.Validate(token)
|
||||
if err != nil {
|
||||
logger.Debug(logSender, "", "error validating jwt token: %v", err)
|
||||
if err != nil || token == nil {
|
||||
logger.Debug(logSender, "", "error getting jwt token: %v", err)
|
||||
if audience == tokenAudienceAPI {
|
||||
sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
} else {
|
||||
http.Redirect(w, r, redirectPath, http.StatusFound)
|
||||
}
|
||||
if !utils.IsStringInSlice(tokenAudienceAPI, token.Audience()) {
|
||||
logger.Debug(logSender, "", "the token audience is not valid for API usage")
|
||||
return errInvalidToken
|
||||
}
|
||||
|
||||
err = jwt.Validate(token)
|
||||
if err != nil {
|
||||
logger.Debug(logSender, "", "error validating jwt token: %v", err)
|
||||
if audience == tokenAudienceAPI {
|
||||
sendAPIResponse(w, r, err, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
} else {
|
||||
http.Redirect(w, r, redirectPath, http.StatusFound)
|
||||
}
|
||||
return errInvalidToken
|
||||
}
|
||||
if !utils.IsStringInSlice(audience, token.Audience()) {
|
||||
logger.Debug(logSender, "", "the token is not valid for audience %#v", audience)
|
||||
if audience == tokenAudienceAPI {
|
||||
sendAPIResponse(w, r, nil, "Your token audience is not valid", http.StatusUnauthorized)
|
||||
return
|
||||
} else {
|
||||
http.Redirect(w, r, redirectPath, http.StatusFound)
|
||||
}
|
||||
if isTokenInvalidated(r) {
|
||||
logger.Debug(logSender, "", "the token has been invalidated")
|
||||
return errInvalidToken
|
||||
}
|
||||
if isTokenInvalidated(r) {
|
||||
logger.Debug(logSender, "", "the token has been invalidated")
|
||||
if audience == tokenAudienceAPI {
|
||||
sendAPIResponse(w, r, nil, "Your token is no longer valid", http.StatusUnauthorized)
|
||||
} else {
|
||||
http.Redirect(w, r, redirectPath, http.StatusFound)
|
||||
}
|
||||
return errInvalidToken
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func jwtAuthenticatorAPI(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := validateJWTToken(w, r, tokenAudienceAPI); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -64,30 +97,9 @@ func jwtAuthenticator(next http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func jwtAuthenticatorWeb(next http.Handler) http.Handler {
|
||||
func jwtAuthenticatorWebAdmin(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
token, _, err := jwtauth.FromContext(r.Context())
|
||||
|
||||
if err != nil || token == nil {
|
||||
logger.Debug(logSender, "", "error getting web jwt token: %v", err)
|
||||
http.Redirect(w, r, webLoginPath, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
err = jwt.Validate(token)
|
||||
if err != nil {
|
||||
logger.Debug(logSender, "", "error validating web jwt token: %v", err)
|
||||
http.Redirect(w, r, webLoginPath, http.StatusFound)
|
||||
return
|
||||
}
|
||||
if !utils.IsStringInSlice(tokenAudienceWeb, token.Audience()) {
|
||||
logger.Debug(logSender, "", "the token audience is not valid for Web usage")
|
||||
http.Redirect(w, r, webLoginPath, http.StatusFound)
|
||||
return
|
||||
}
|
||||
if isTokenInvalidated(r) {
|
||||
logger.Debug(logSender, "", "the token has been invalidated")
|
||||
http.Redirect(w, r, webLoginPath, http.StatusFound)
|
||||
if err := validateJWTToken(w, r, tokenAudienceWebAdmin); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -96,12 +108,44 @@ func jwtAuthenticatorWeb(next http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func jwtAuthenticatorWebClient(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := validateJWTToken(w, r, tokenAudienceWebClient); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Token is authenticated, pass it through
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func checkClientPerm(perm string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, claims, err := jwtauth.FromContext(r.Context())
|
||||
if err != nil {
|
||||
renderClientBadRequestPage(w, r, err)
|
||||
return
|
||||
}
|
||||
tokenClaims := jwtTokenClaims{}
|
||||
tokenClaims.Decode(claims)
|
||||
// for web client perms are negated and not granted
|
||||
if tokenClaims.hasPerm(perm) {
|
||||
renderClientForbiddenPage(w, r, "You don't have permission for this action")
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func checkPerm(perm string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, claims, err := jwtauth.FromContext(r.Context())
|
||||
if err != nil {
|
||||
if isWebAdminRequest(r) {
|
||||
if isWebRequest(r) {
|
||||
renderBadRequestPage(w, r, err)
|
||||
} else {
|
||||
sendAPIResponse(w, r, err, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
@@ -112,7 +156,7 @@ func checkPerm(perm string) func(next http.Handler) http.Handler {
|
||||
tokenClaims.Decode(claims)
|
||||
|
||||
if !tokenClaims.hasPerm(perm) {
|
||||
if isWebAdminRequest(r) {
|
||||
if isWebRequest(r) {
|
||||
renderForbiddenPage(w, r, "You don't have permission for this action")
|
||||
} else {
|
||||
sendAPIResponse(w, r, nil, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
|
||||
Reference in New Issue
Block a user