2024-03-19 07:16:19 +00:00
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package shared
import (
"net/http"
2025-03-27 19:40:14 +00:00
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
"forgejo.org/routers/common"
"forgejo.org/services/auth"
"forgejo.org/services/context"
2024-03-19 07:16:19 +00:00
"github.com/go-chi/cors"
)
func Middlewares ( ) ( stack [ ] any ) {
stack = append ( stack , securityHeaders ( ) )
if setting . CORSConfig . Enabled {
stack = append ( stack , cors . Handler ( cors . Options {
AllowedOrigins : setting . CORSConfig . AllowDomain ,
AllowedMethods : setting . CORSConfig . Methods ,
AllowCredentials : setting . CORSConfig . AllowCredentials ,
AllowedHeaders : append ( [ ] string { "Authorization" , "X-Gitea-OTP" , "X-Forgejo-OTP" } , setting . CORSConfig . Headers ... ) ,
MaxAge : int ( setting . CORSConfig . MaxAge . Seconds ( ) ) ,
} ) )
}
return append ( stack ,
context . APIContexter ( ) ,
checkDeprecatedAuthMethods ,
// Get user from session if logged in.
apiAuth ( buildAuthGroup ( ) ) ,
verifyAuthWithOptions ( & common . VerifyOptions {
SignInRequired : setting . Service . RequireSignInView ,
} ) ,
)
}
func buildAuthGroup ( ) * auth . Group {
group := auth . NewGroup (
& auth . OAuth2 { } ,
& auth . HTTPSign { } ,
& auth . Basic { } , // FIXME: this should be removed once we don't allow basic auth in API
)
if setting . Service . EnableReverseProxyAuthAPI {
group . Add ( & auth . ReverseProxy { } )
}
return group
}
func apiAuth ( authMethod auth . Method ) func ( * context . APIContext ) {
return func ( ctx * context . APIContext ) {
ar , err := common . AuthShared ( ctx . Base , nil , authMethod )
if err != nil {
ctx . Error ( http . StatusUnauthorized , "APIAuth" , err )
return
}
ctx . Doer = ar . Doer
ctx . IsSigned = ar . Doer != nil
ctx . IsBasicAuth = ar . IsBasicAuth
}
}
// verifyAuthWithOptions checks authentication according to options
func verifyAuthWithOptions ( options * common . VerifyOptions ) func ( ctx * context . APIContext ) {
return func ( ctx * context . APIContext ) {
// Check prohibit login users.
if ctx . IsSigned {
if ! ctx . Doer . IsActive && setting . Service . RegisterEmailConfirm {
ctx . Data [ "Title" ] = ctx . Tr ( "auth.active_your_account" )
ctx . JSON ( http . StatusForbidden , map [ string ] string {
"message" : "This account is not activated." ,
} )
return
}
if ! ctx . Doer . IsActive || ctx . Doer . ProhibitLogin {
log . Info ( "Failed authentication attempt for %s from %s" , ctx . Doer . Name , ctx . RemoteAddr ( ) )
ctx . Data [ "Title" ] = ctx . Tr ( "auth.prohibit_login" )
ctx . JSON ( http . StatusForbidden , map [ string ] string {
"message" : "This account is prohibited from signing in, please contact your site administrator." ,
} )
return
}
if ctx . Doer . MustChangePassword {
ctx . JSON ( http . StatusForbidden , map [ string ] string {
"message" : "You must change your password. Change it at: " + setting . AppURL + "/user/change_password" ,
} )
return
}
}
// Redirect to dashboard if user tries to visit any non-login page.
if options . SignOutRequired && ctx . IsSigned && ctx . Req . URL . RequestURI ( ) != "/" {
ctx . Redirect ( setting . AppSubURL + "/" )
return
}
if options . SignInRequired {
if ! ctx . IsSigned {
// Restrict API calls with error message.
ctx . JSON ( http . StatusForbidden , map [ string ] string {
"message" : "Only signed in user is allowed to call APIs." ,
} )
return
} else if ! ctx . Doer . IsActive && setting . Service . RegisterEmailConfirm {
ctx . Data [ "Title" ] = ctx . Tr ( "auth.active_your_account" )
ctx . JSON ( http . StatusForbidden , map [ string ] string {
"message" : "This account is not activated." ,
} )
return
}
}
if options . AdminRequired {
if ! ctx . Doer . IsAdmin {
ctx . JSON ( http . StatusForbidden , map [ string ] string {
"message" : "You have no permission to request for this." ,
} )
return
}
}
}
}
// check for and warn against deprecated authentication options
func checkDeprecatedAuthMethods ( ctx * context . APIContext ) {
if ctx . FormString ( "token" ) != "" || ctx . FormString ( "access_token" ) != "" {
ctx . Resp . Header ( ) . Set ( "Warning" , "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization." )
}
}
func securityHeaders ( ) func ( http . Handler ) http . Handler {
return func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( resp http . ResponseWriter , req * http . Request ) {
// CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers
// http://stackoverflow.com/a/3146618/244009
resp . Header ( ) . Set ( "x-content-type-options" , "nosniff" )
next . ServeHTTP ( resp , req )
} )
}
}