2015-05-02 05:06:58 -05:00
package middleware
import (
2016-02-23 07:22:28 -06:00
"fmt"
2018-06-28 08:43:33 -05:00
"net"
2018-03-22 16:02:34 -05:00
"net/mail"
2018-05-07 03:39:16 -05:00
"reflect"
2016-02-23 07:22:28 -06:00
"strings"
"time"
2015-05-02 05:06:58 -05:00
"github.com/grafana/grafana/pkg/bus"
2015-08-21 00:49:49 -05:00
"github.com/grafana/grafana/pkg/log"
2016-02-23 07:22:28 -06:00
"github.com/grafana/grafana/pkg/login"
2015-05-02 05:06:58 -05:00
m "github.com/grafana/grafana/pkg/models"
2018-03-06 16:59:45 -06:00
"github.com/grafana/grafana/pkg/services/session"
2015-05-02 05:06:58 -05:00
"github.com/grafana/grafana/pkg/setting"
)
2019-01-23 05:41:15 -06:00
var (
AUTH_PROXY_SESSION_VAR = "authProxyHeaderValue"
)
2018-03-22 16:02:34 -05:00
2018-03-22 16:13:46 -05:00
func initContextWithAuthProxy ( ctx * m . ReqContext , orgID int64 ) bool {
2015-05-02 05:06:58 -05:00
if ! setting . AuthProxyEnabled {
return false
}
proxyHeaderValue := ctx . Req . Header . Get ( setting . AuthProxyHeaderName )
2015-05-02 05:30:53 -05:00
if len ( proxyHeaderValue ) == 0 {
2015-05-02 05:06:58 -05:00
return false
}
2016-02-23 07:22:28 -06:00
// if auth proxy ip(s) defined, check if request comes from one of those
2018-06-28 08:43:33 -05:00
if err := checkAuthenticationProxy ( ctx . Req . RemoteAddr , proxyHeaderValue ) ; err != nil {
2016-02-23 07:22:28 -06:00
ctx . Handle ( 407 , "Proxy authentication required" , err )
return true
}
2018-03-22 16:02:34 -05:00
// initialize session
if err := ctx . Session . Start ( ctx . Context ) ; err != nil {
2018-08-28 15:26:47 -05:00
log . Error ( 3 , "Failed to start session. error %v" , err )
2018-03-22 16:02:34 -05:00
return false
}
query := & m . GetSignedInUserQuery { OrgId : orgID }
// if this session has already been authenticated by authProxy just load the user
sessProxyValue := ctx . Session . Get ( AUTH_PROXY_SESSION_VAR )
if sessProxyValue != nil && sessProxyValue . ( string ) == proxyHeaderValue && getRequestUserId ( ctx ) > 0 {
2018-04-16 15:17:01 -05:00
// if we're using ldap, sync user periodically
if setting . LdapEnabled {
syncQuery := & m . LoginUserQuery {
ReqContext : ctx ,
Username : proxyHeaderValue ,
}
if err := syncGrafanaUserWithLdapUser ( syncQuery ) ; err != nil {
if err == login . ErrInvalidCredentials {
ctx . Handle ( 500 , "Unable to authenticate user" , err )
return false
}
ctx . Handle ( 500 , "Failed to sync user" , err )
return false
}
}
2018-03-22 16:02:34 -05:00
query . UserId = getRequestUserId ( ctx )
2018-04-16 15:17:01 -05:00
// if we're using ldap, pass authproxy login name to ldap user sync
} else if setting . LdapEnabled {
2019-01-22 09:16:32 -06:00
ctx . Session . Delete ( session . SESS_KEY_LASTLDAPSYNC ) //makes sure we always sync with ldap if session if we only have last sync info in session but not user.
2018-04-17 16:48:56 -05:00
2018-04-16 15:17:01 -05:00
syncQuery := & m . LoginUserQuery {
ReqContext : ctx ,
Username : proxyHeaderValue ,
}
if err := syncGrafanaUserWithLdapUser ( syncQuery ) ; err != nil {
if err == login . ErrInvalidCredentials {
ctx . Handle ( 500 , "Unable to authenticate user" , err )
return false
}
ctx . Handle ( 500 , "Failed to sync user" , err )
return false
2015-05-02 05:06:58 -05:00
}
2018-04-16 15:17:01 -05:00
if syncQuery . User == nil {
ctx . Handle ( 500 , "Failed to sync user" , nil )
return false
}
query . UserId = syncQuery . User . Id
// no ldap, just use the info we have
2018-03-22 16:02:34 -05:00
} else {
2018-03-23 14:50:07 -05:00
extUser := & m . ExternalUserInfo {
2018-03-22 16:02:34 -05:00
AuthModule : "authproxy" ,
AuthId : proxyHeaderValue ,
2015-05-02 05:06:58 -05:00
}
2018-03-22 16:13:46 -05:00
2018-03-22 16:02:34 -05:00
if setting . AuthProxyHeaderProperty == "username" {
extUser . Login = proxyHeaderValue
// only set Email if it can be parsed as an email address
emailAddr , emailErr := mail . ParseAddress ( proxyHeaderValue )
if emailErr == nil {
extUser . Email = emailAddr . Address
}
} else if setting . AuthProxyHeaderProperty == "email" {
extUser . Email = proxyHeaderValue
extUser . Login = proxyHeaderValue
} else {
ctx . Handle ( 500 , "Auth proxy header property invalid" , nil )
2018-03-23 10:58:38 -05:00
return true
2018-03-22 16:13:46 -05:00
}
2018-05-07 03:39:16 -05:00
for _ , field := range [ ] string { "Name" , "Email" , "Login" } {
if setting . AuthProxyHeaders [ field ] == "" {
continue
}
if val := ctx . Req . Header . Get ( setting . AuthProxyHeaders [ field ] ) ; val != "" {
reflect . ValueOf ( extUser ) . Elem ( ) . FieldByName ( field ) . SetString ( val )
}
}
2018-03-22 16:02:34 -05:00
// add/update user in grafana
2018-03-23 10:16:11 -05:00
cmd := & m . UpsertUserCommand {
2018-03-23 14:50:07 -05:00
ReqContext : ctx ,
ExternalUser : extUser ,
2018-03-22 16:02:34 -05:00
SignupAllowed : setting . AuthProxyAutoSignUp ,
}
2018-03-23 14:50:07 -05:00
err := bus . Dispatch ( cmd )
2018-03-22 16:02:34 -05:00
if err != nil {
ctx . Handle ( 500 , "Failed to login as user specified in auth proxy header" , err )
2018-03-22 16:13:46 -05:00
return true
}
2018-03-22 16:02:34 -05:00
2018-03-23 10:16:11 -05:00
query . UserId = cmd . Result . Id
2018-04-16 15:17:01 -05:00
}
2018-03-22 16:02:34 -05:00
2018-04-16 15:17:01 -05:00
if err := bus . Dispatch ( query ) ; err != nil {
ctx . Handle ( 500 , "Failed to find user" , err )
return true
2016-02-23 07:22:28 -06:00
}
2018-04-16 15:17:01 -05:00
// Make sure that we cannot share a session between different users!
if getRequestUserId ( ctx ) > 0 && getRequestUserId ( ctx ) != query . Result . UserId {
// remove session
if err := ctx . Session . Destory ( ctx . Context ) ; err != nil {
2018-08-28 15:26:47 -05:00
log . Error ( 3 , "Failed to destroy session. error: %v" , err )
2016-02-23 07:22:28 -06:00
}
2018-04-16 15:17:01 -05:00
// initialize a new session
if err := ctx . Session . Start ( ctx . Context ) ; err != nil {
2018-08-28 15:26:47 -05:00
log . Error ( 3 , "Failed to start session. error: %v" , err )
2018-04-16 15:17:01 -05:00
}
2016-02-23 07:22:28 -06:00
}
2018-04-16 15:17:01 -05:00
ctx . Session . Set ( AUTH_PROXY_SESSION_VAR , proxyHeaderValue )
2015-05-02 05:06:58 -05:00
ctx . SignedInUser = query . Result
ctx . IsSignedIn = true
2018-03-06 16:59:45 -06:00
ctx . Session . Set ( session . SESS_KEY_USERID , ctx . UserId )
2015-08-21 00:49:49 -05:00
2019-01-23 05:41:15 -06:00
if err := ctx . Session . Release ( ) ; err != nil {
ctx . Logger . Error ( "failed to save session data" , "error" , err )
}
2015-05-02 05:06:58 -05:00
return true
}
2018-04-16 15:17:01 -05:00
var syncGrafanaUserWithLdapUser = func ( query * m . LoginUserQuery ) error {
2018-03-22 16:13:46 -05:00
expireEpoch := time . Now ( ) . Add ( time . Duration ( - setting . AuthProxyLdapSyncTtl ) * time . Minute ) . Unix ( )
2016-02-23 07:22:28 -06:00
2018-03-22 16:13:46 -05:00
var lastLdapSync int64
2018-04-16 15:17:01 -05:00
if lastLdapSyncInSession := query . ReqContext . Session . Get ( session . SESS_KEY_LASTLDAPSYNC ) ; lastLdapSyncInSession != nil {
2018-03-22 16:13:46 -05:00
lastLdapSync = lastLdapSyncInSession . ( int64 )
}
2016-02-23 07:22:28 -06:00
2018-03-22 16:13:46 -05:00
if lastLdapSync < expireEpoch {
ldapCfg := login . LdapCfg
2016-02-23 07:22:28 -06:00
2018-04-16 15:17:01 -05:00
if len ( ldapCfg . Servers ) < 1 {
return fmt . Errorf ( "No LDAP servers available" )
}
2018-03-22 16:13:46 -05:00
for _ , server := range ldapCfg . Servers {
author := login . NewLdapAuthenticator ( server )
2018-04-16 15:17:01 -05:00
if err := author . SyncUser ( query ) ; err != nil {
2018-03-22 16:13:46 -05:00
return err
}
2016-02-23 07:22:28 -06:00
}
2018-03-22 16:13:46 -05:00
2018-04-16 15:17:01 -05:00
query . ReqContext . Session . Set ( session . SESS_KEY_LASTLDAPSYNC , time . Now ( ) . Unix ( ) )
2016-02-23 07:22:28 -06:00
}
return nil
}
2019-01-23 05:41:15 -06:00
func getRequestUserId ( c * m . ReqContext ) int64 {
userID := c . Session . Get ( session . SESS_KEY_USERID )
if userID != nil {
return userID . ( int64 )
}
return 0
}
2018-03-21 12:28:56 -05:00
func checkAuthenticationProxy ( remoteAddr string , proxyHeaderValue string ) error {
2018-03-22 16:13:46 -05:00
if len ( strings . TrimSpace ( setting . AuthProxyWhitelist ) ) == 0 {
return nil
}
2018-03-21 12:28:56 -05:00
2018-06-15 06:40:42 -05:00
proxies := strings . Split ( setting . AuthProxyWhitelist , "," )
2018-12-17 23:43:14 -06:00
var proxyObjs [ ] * net . IPNet
for _ , proxy := range proxies {
proxyObjs = append ( proxyObjs , coerceProxyAddress ( proxy ) )
2018-06-28 08:43:33 -05:00
}
2018-06-15 06:40:42 -05:00
2018-12-17 23:43:14 -06:00
sourceIP , _ , _ := net . SplitHostPort ( remoteAddr )
sourceObj := net . ParseIP ( sourceIP )
for _ , proxyObj := range proxyObjs {
if proxyObj . Contains ( sourceObj ) {
2018-03-21 12:28:56 -05:00
return nil
2016-02-23 07:22:28 -06:00
}
2018-03-22 16:13:46 -05:00
}
2018-06-28 08:43:33 -05:00
return fmt . Errorf ( "Request for user (%s) from %s is not from the authentication proxy" , proxyHeaderValue , sourceIP )
2016-02-23 07:22:28 -06:00
}
2018-12-17 23:43:14 -06:00
func coerceProxyAddress ( proxyAddr string ) * net . IPNet {
proxyAddr = strings . TrimSpace ( proxyAddr )
if ! strings . Contains ( proxyAddr , "/" ) {
proxyAddr = strings . Join ( [ ] string { proxyAddr , "32" } , "/" )
}
_ , network , err := net . ParseCIDR ( proxyAddr )
if err != nil {
fmt . Println ( err )
}
return network
}