2019-11-23 02:33:31 +03:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2021-06-09 20:53:16 +03:00
package auth
2019-11-23 02:33:31 +03:00
import (
2021-01-05 16:05:40 +03:00
"net/http"
2019-11-23 02:33:31 +03:00
"strings"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2019-11-23 02:33:31 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2022-04-29 22:38:11 +03:00
"code.gitea.io/gitea/modules/util"
2021-05-15 21:33:13 +03:00
"code.gitea.io/gitea/modules/web/middleware"
2021-08-12 10:26:33 +03:00
"code.gitea.io/gitea/services/mailer"
2019-11-23 02:33:31 +03:00
2020-06-18 12:18:44 +03:00
gouuid "github.com/google/uuid"
2019-11-23 02:33:31 +03:00
)
// Ensure the struct implements the interface.
var (
2021-07-24 13:16:34 +03:00
_ Method = & ReverseProxy { }
_ Named = & ReverseProxy { }
2019-11-23 02:33:31 +03:00
)
2021-11-20 18:33:18 +03:00
// ReverseProxyMethodName is the constant name of the ReverseProxy authentication method
const ReverseProxyMethodName = "reverse_proxy"
2021-06-09 20:53:16 +03:00
// ReverseProxy implements the Auth interface, but actually relies on
2019-11-23 02:33:31 +03:00
// a reverse proxy for authentication of users.
// On successful authentication the proxy is expected to populate the username in the
// "setting.ReverseProxyAuthUser" header. Optionally it can also populate the email of the
// user in the "setting.ReverseProxyAuthEmail" header.
2022-01-20 20:46:10 +03:00
type ReverseProxy struct { }
2019-11-23 02:33:31 +03:00
// getUserName extracts the username from the "setting.ReverseProxyAuthUser" header
2021-01-05 16:05:40 +03:00
func ( r * ReverseProxy ) getUserName ( req * http . Request ) string {
webAuthUser := strings . TrimSpace ( req . Header . Get ( setting . ReverseProxyAuthUser ) )
2019-11-23 02:33:31 +03:00
if len ( webAuthUser ) == 0 {
return ""
}
return webAuthUser
}
2021-06-09 20:53:16 +03:00
// Name represents the name of auth method
func ( r * ReverseProxy ) Name ( ) string {
2021-11-20 18:33:18 +03:00
return ReverseProxyMethodName
2021-06-09 20:53:16 +03:00
}
// Verify extracts the username from the "setting.ReverseProxyAuthUser" header
2019-11-23 02:33:31 +03:00
// of the request and returns the corresponding user object for that name.
// Verification of header data is not performed as it should have already been done by
// the revese proxy.
// If a username is available in the "setting.ReverseProxyAuthUser" header an existing
// user object is returned (populated with username or email found in header).
// Returns nil if header is empty.
2021-11-24 12:49:20 +03:00
func ( r * ReverseProxy ) Verify ( req * http . Request , w http . ResponseWriter , store DataStore , sess SessionStore ) * user_model . User {
2021-01-05 16:05:40 +03:00
username := r . getUserName ( req )
2019-11-23 02:33:31 +03:00
if len ( username ) == 0 {
return nil
}
2021-05-09 19:04:53 +03:00
log . Trace ( "ReverseProxy Authorization: Found username: %s" , username )
2019-11-23 02:33:31 +03:00
2022-05-20 17:08:52 +03:00
user , err := user_model . GetUserByName ( req . Context ( ) , username )
2019-11-23 02:33:31 +03:00
if err != nil {
2021-11-24 12:49:20 +03:00
if ! user_model . IsErrUserNotExist ( err ) || ! r . isAutoRegisterAllowed ( ) {
2021-05-15 21:33:13 +03:00
log . Error ( "GetUserByName: %v" , err )
return nil
2019-11-23 02:33:31 +03:00
}
2021-05-15 21:33:13 +03:00
user = r . newUser ( req )
2019-11-23 02:33:31 +03:00
}
2021-05-15 21:33:13 +03:00
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
2021-09-02 18:48:48 +03:00
if ! middleware . IsAPIPath ( req ) && ! isAttachmentDownload ( req ) && ! isGitRawReleaseOrLFSPath ( req ) {
2021-05-31 09:54:16 +03:00
if sess != nil && ( sess . Get ( "uid" ) == nil || sess . Get ( "uid" ) . ( int64 ) != user . ID ) {
2021-05-15 21:33:13 +03:00
handleSignIn ( w , req , sess , user )
}
}
store . GetData ( ) [ "IsReverseProxy" ] = true
2021-05-09 19:04:53 +03:00
log . Trace ( "ReverseProxy Authorization: Logged in user %-v" , user )
2019-11-23 02:33:31 +03:00
return user
}
// isAutoRegisterAllowed checks if EnableReverseProxyAutoRegister setting is true
func ( r * ReverseProxy ) isAutoRegisterAllowed ( ) bool {
return setting . Service . EnableReverseProxyAutoRegister
}
// newUser creates a new user object for the purpose of automatic registration
// and populates its name and email with the information present in request headers.
2021-11-24 12:49:20 +03:00
func ( r * ReverseProxy ) newUser ( req * http . Request ) * user_model . User {
2021-01-05 16:05:40 +03:00
username := r . getUserName ( req )
2019-11-23 02:33:31 +03:00
if len ( username ) == 0 {
return nil
}
2020-06-18 12:18:44 +03:00
email := gouuid . New ( ) . String ( ) + "@localhost"
2019-11-23 02:33:31 +03:00
if setting . Service . EnableReverseProxyEmail {
2021-01-05 16:05:40 +03:00
webAuthEmail := req . Header . Get ( setting . ReverseProxyAuthEmail )
2019-11-23 02:33:31 +03:00
if len ( webAuthEmail ) > 0 {
email = webAuthEmail
}
}
2021-11-24 12:49:20 +03:00
user := & user_model . User {
2022-04-29 22:38:11 +03:00
Name : username ,
Email : email ,
2019-11-23 02:33:31 +03:00
}
2022-04-29 22:38:11 +03:00
overwriteDefault := user_model . CreateUserOverwriteOptions {
IsActive : util . OptionalBoolTrue ,
}
if err := user_model . CreateUser ( user , & overwriteDefault ) ; err != nil {
2019-11-23 02:33:31 +03:00
// FIXME: should I create a system notice?
log . Error ( "CreateUser: %v" , err )
return nil
}
2021-05-15 21:33:13 +03:00
2021-08-12 10:26:33 +03:00
mailer . SendRegisterNotifyMail ( user )
2019-11-23 02:33:31 +03:00
return user
}