2019-11-23 01:33:31 +02: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-10 01:53:16 +08:00
package auth
2019-11-23 01:33:31 +02:00
import (
2021-01-05 21:05:40 +08:00
"net/http"
2019-11-23 01:33:31 +02:00
"strings"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2019-11-23 01:33:31 +02:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2022-04-29 21:38:11 +02:00
"code.gitea.io/gitea/modules/util"
2021-05-15 19:33:13 +01:00
"code.gitea.io/gitea/modules/web/middleware"
2021-08-12 08:26:33 +01:00
"code.gitea.io/gitea/services/mailer"
2019-11-23 01:33:31 +02:00
2020-06-18 17:18:44 +08:00
gouuid "github.com/google/uuid"
2019-11-23 01:33:31 +02:00
)
// Ensure the struct implements the interface.
var (
2021-07-24 11:16:34 +01:00
_ Method = & ReverseProxy { }
_ Named = & ReverseProxy { }
2019-11-23 01:33:31 +02:00
)
2021-11-20 15:33:18 +00:00
// ReverseProxyMethodName is the constant name of the ReverseProxy authentication method
const ReverseProxyMethodName = "reverse_proxy"
2021-06-10 01:53:16 +08:00
// ReverseProxy implements the Auth interface, but actually relies on
2019-11-23 01:33:31 +02: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 18:46:10 +01:00
type ReverseProxy struct { }
2019-11-23 01:33:31 +02:00
// getUserName extracts the username from the "setting.ReverseProxyAuthUser" header
2021-01-05 21:05:40 +08:00
func ( r * ReverseProxy ) getUserName ( req * http . Request ) string {
2022-09-27 17:00:15 -07:00
return strings . TrimSpace ( req . Header . Get ( setting . ReverseProxyAuthUser ) )
2019-11-23 01:33:31 +02:00
}
2021-06-10 01:53:16 +08:00
// Name represents the name of auth method
func ( r * ReverseProxy ) Name ( ) string {
2021-11-20 15:33:18 +00:00
return ReverseProxyMethodName
2021-06-10 01:53:16 +08:00
}
2022-09-27 17:00:15 -07:00
// getUserFromAuthUser extracts the username from the "setting.ReverseProxyAuthUser" header
2019-11-23 01:33:31 +02: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
2022-09-27 17:00:15 -07:00
// the reverse proxy.
2019-11-23 01:33:31 +02:00
// 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.
2022-09-27 17:00:15 -07:00
func ( r * ReverseProxy ) getUserFromAuthUser ( req * http . Request ) * user_model . User {
2021-01-05 21:05:40 +08:00
username := r . getUserName ( req )
2019-11-23 01:33:31 +02:00
if len ( username ) == 0 {
return nil
}
2021-05-09 17:04:53 +01:00
log . Trace ( "ReverseProxy Authorization: Found username: %s" , username )
2019-11-23 01:33:31 +02:00
2022-05-20 22:08:52 +08:00
user , err := user_model . GetUserByName ( req . Context ( ) , username )
2019-11-23 01:33:31 +02:00
if err != nil {
2021-11-24 17:49:20 +08:00
if ! user_model . IsErrUserNotExist ( err ) || ! r . isAutoRegisterAllowed ( ) {
2021-05-15 19:33:13 +01:00
log . Error ( "GetUserByName: %v" , err )
return nil
2019-11-23 01:33:31 +02:00
}
2021-05-15 19:33:13 +01:00
user = r . newUser ( req )
2019-11-23 01:33:31 +02:00
}
2022-09-27 17:00:15 -07:00
return user
}
// getEmail extracts the email from the "setting.ReverseProxyAuthEmail" header
func ( r * ReverseProxy ) getEmail ( req * http . Request ) string {
return strings . TrimSpace ( req . Header . Get ( setting . ReverseProxyAuthEmail ) )
}
// getUserFromAuthEmail extracts the username from the "setting.ReverseProxyAuthEmail" header
// of the request and returns the corresponding user object for that email.
// Verification of header data is not performed as it should have already been done by
// the reverse proxy.
// If an email is available in the "setting.ReverseProxyAuthEmail" header an existing
// user object is returned (populated with the email found in header).
// Returns nil if header is empty or if "setting.EnableReverseProxyEmail" is disabled.
func ( r * ReverseProxy ) getUserFromAuthEmail ( req * http . Request ) * user_model . User {
if ! setting . Service . EnableReverseProxyEmail {
return nil
}
email := r . getEmail ( req )
if len ( email ) == 0 {
return nil
}
log . Trace ( "ReverseProxy Authorization: Found email: %s" , email )
user , err := user_model . GetUserByEmail ( email )
if err != nil {
// Do not allow auto-registration, we don't have a username here
if ! user_model . IsErrUserNotExist ( err ) {
log . Error ( "GetUserByEmail: %v" , err )
}
return nil
}
return user
}
// Verify attempts to load a user object based on headers sent by the reverse proxy.
// First it will attempt to load it based on the username (see docs for getUserFromAuthUser),
// and failing that it will attempt to load it based on the email (see docs for getUserFromAuthEmail).
// Returns nil if the headers are empty or the user is not found.
func ( r * ReverseProxy ) Verify ( req * http . Request , w http . ResponseWriter , store DataStore , sess SessionStore ) * user_model . User {
user := r . getUserFromAuthUser ( req )
if user == nil {
user = r . getUserFromAuthEmail ( req )
if user == nil {
return nil
}
}
2019-11-23 01:33:31 +02:00
2021-05-15 19:33:13 +01:00
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
2021-09-02 16:48:48 +01:00
if ! middleware . IsAPIPath ( req ) && ! isAttachmentDownload ( req ) && ! isGitRawReleaseOrLFSPath ( req ) {
2021-05-31 07:54:16 +01:00
if sess != nil && ( sess . Get ( "uid" ) == nil || sess . Get ( "uid" ) . ( int64 ) != user . ID ) {
2021-05-15 19:33:13 +01:00
handleSignIn ( w , req , sess , user )
}
}
store . GetData ( ) [ "IsReverseProxy" ] = true
2021-05-09 17:04:53 +01:00
log . Trace ( "ReverseProxy Authorization: Logged in user %-v" , user )
2019-11-23 01:33:31 +02: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 17:49:20 +08:00
func ( r * ReverseProxy ) newUser ( req * http . Request ) * user_model . User {
2021-01-05 21:05:40 +08:00
username := r . getUserName ( req )
2019-11-23 01:33:31 +02:00
if len ( username ) == 0 {
return nil
}
2020-06-18 17:18:44 +08:00
email := gouuid . New ( ) . String ( ) + "@localhost"
2019-11-23 01:33:31 +02:00
if setting . Service . EnableReverseProxyEmail {
2021-01-05 21:05:40 +08:00
webAuthEmail := req . Header . Get ( setting . ReverseProxyAuthEmail )
2019-11-23 01:33:31 +02:00
if len ( webAuthEmail ) > 0 {
email = webAuthEmail
}
}
2022-08-15 23:30:27 -07:00
var fullname string
if setting . Service . EnableReverseProxyFullName {
fullname = req . Header . Get ( setting . ReverseProxyAuthFullName )
}
2021-11-24 17:49:20 +08:00
user := & user_model . User {
2022-08-15 23:30:27 -07:00
Name : username ,
Email : email ,
FullName : fullname ,
2019-11-23 01:33:31 +02:00
}
2022-04-29 21:38:11 +02:00
overwriteDefault := user_model . CreateUserOverwriteOptions {
IsActive : util . OptionalBoolTrue ,
}
if err := user_model . CreateUser ( user , & overwriteDefault ) ; err != nil {
2019-11-23 01:33:31 +02:00
// FIXME: should I create a system notice?
log . Error ( "CreateUser: %v" , err )
return nil
}
2021-05-15 19:33:13 +01:00
2021-08-12 08:26:33 +01:00
mailer . SendRegisterNotifyMail ( user )
2019-11-23 01:33:31 +02:00
return user
}