2015-05-02 18:06:58 +08:00
|
|
|
package middleware
|
|
|
|
|
|
|
|
|
|
import (
|
2016-02-23 21:22:28 +08:00
|
|
|
"fmt"
|
2018-06-28 21:43:33 +08:00
|
|
|
"net"
|
2018-03-23 05:02:34 +08:00
|
|
|
"net/mail"
|
2018-05-07 16:39:16 +08:00
|
|
|
"reflect"
|
2016-02-23 21:22:28 +08:00
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2015-05-02 18:06:58 +08:00
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
2019-04-08 19:31:46 +08:00
|
|
|
"github.com/grafana/grafana/pkg/infra/remotecache"
|
2016-02-23 21:22:28 +08:00
|
|
|
"github.com/grafana/grafana/pkg/login"
|
2015-05-02 18:06:58 +08:00
|
|
|
m "github.com/grafana/grafana/pkg/models"
|
|
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
|
|
|
)
|
|
|
|
|
|
2019-04-08 19:31:46 +08:00
|
|
|
const (
|
|
|
|
|
|
|
|
|
|
// cachePrefix is a prefix for the cache key
|
|
|
|
|
cachePrefix = "auth-proxy-sync-ttl:%s"
|
2019-01-23 19:41:15 +08:00
|
|
|
)
|
2018-03-23 05:02:34 +08:00
|
|
|
|
2019-04-08 19:31:46 +08:00
|
|
|
func initContextWithAuthProxy(store *remotecache.RemoteCache, ctx *m.ReqContext, orgID int64) bool {
|
2015-05-02 18:06:58 +08:00
|
|
|
if !setting.AuthProxyEnabled {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proxyHeaderValue := ctx.Req.Header.Get(setting.AuthProxyHeaderName)
|
2015-05-02 18:30:53 +08:00
|
|
|
if len(proxyHeaderValue) == 0 {
|
2015-05-02 18:06:58 +08:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-23 21:22:28 +08:00
|
|
|
// if auth proxy ip(s) defined, check if request comes from one of those
|
2018-06-28 21:43:33 +08:00
|
|
|
if err := checkAuthenticationProxy(ctx.Req.RemoteAddr, proxyHeaderValue); err != nil {
|
2016-02-23 21:22:28 +08:00
|
|
|
ctx.Handle(407, "Proxy authentication required", err)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-23 05:02:34 +08:00
|
|
|
query := &m.GetSignedInUserQuery{OrgId: orgID}
|
2019-04-08 19:31:46 +08:00
|
|
|
cacheKey := fmt.Sprintf(cachePrefix, proxyHeaderValue)
|
|
|
|
|
userID, err := store.Get(cacheKey)
|
|
|
|
|
inCache := err == nil
|
2018-03-23 05:02:34 +08:00
|
|
|
|
2019-04-08 19:31:46 +08:00
|
|
|
// load the user if we have them
|
|
|
|
|
if inCache {
|
|
|
|
|
query.UserId = userID.(int64)
|
2018-04-17 04:17:01 +08:00
|
|
|
|
|
|
|
|
// if we're using ldap, pass authproxy login name to ldap user sync
|
|
|
|
|
} else 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
|
|
|
|
|
}
|
2015-05-02 18:06:58 +08:00
|
|
|
}
|
2018-04-17 04:17:01 +08: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-23 05:02:34 +08:00
|
|
|
} else {
|
2018-03-24 03:50:07 +08:00
|
|
|
extUser := &m.ExternalUserInfo{
|
2018-03-23 05:02:34 +08:00
|
|
|
AuthModule: "authproxy",
|
|
|
|
|
AuthId: proxyHeaderValue,
|
2015-05-02 18:06:58 +08:00
|
|
|
}
|
2018-03-23 05:13:46 +08:00
|
|
|
|
2018-03-23 05:02:34 +08: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 23:58:38 +08:00
|
|
|
return true
|
2018-03-23 05:13:46 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-07 16:39:16 +08: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-23 05:02:34 +08:00
|
|
|
// add/update user in grafana
|
2018-03-23 23:16:11 +08:00
|
|
|
cmd := &m.UpsertUserCommand{
|
2018-03-24 03:50:07 +08:00
|
|
|
ReqContext: ctx,
|
|
|
|
|
ExternalUser: extUser,
|
2018-03-23 05:02:34 +08:00
|
|
|
SignupAllowed: setting.AuthProxyAutoSignUp,
|
|
|
|
|
}
|
2018-03-24 03:50:07 +08:00
|
|
|
err := bus.Dispatch(cmd)
|
2018-03-23 05:02:34 +08:00
|
|
|
if err != nil {
|
|
|
|
|
ctx.Handle(500, "Failed to login as user specified in auth proxy header", err)
|
2018-03-23 05:13:46 +08:00
|
|
|
return true
|
|
|
|
|
}
|
2018-03-23 05:02:34 +08:00
|
|
|
|
2018-03-23 23:16:11 +08:00
|
|
|
query.UserId = cmd.Result.Id
|
2018-04-17 04:17:01 +08:00
|
|
|
}
|
2018-03-23 05:02:34 +08:00
|
|
|
|
2018-04-17 04:17:01 +08:00
|
|
|
if err := bus.Dispatch(query); err != nil {
|
|
|
|
|
ctx.Handle(500, "Failed to find user", err)
|
|
|
|
|
return true
|
2016-02-23 21:22:28 +08:00
|
|
|
}
|
2019-04-08 19:31:46 +08:00
|
|
|
ctx.SignedInUser = query.Result
|
|
|
|
|
ctx.IsSignedIn = true
|
2016-02-23 21:22:28 +08:00
|
|
|
|
2019-04-08 19:31:46 +08:00
|
|
|
expiration := time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute
|
|
|
|
|
value := query.UserId
|
2016-02-23 21:22:28 +08:00
|
|
|
|
2019-04-08 19:31:46 +08:00
|
|
|
// This <if> is here to make sure we do not
|
|
|
|
|
// rewrite the expiration all the time
|
|
|
|
|
if inCache == false {
|
|
|
|
|
if err = store.Set(cacheKey, value, expiration); err != nil {
|
|
|
|
|
ctx.Handle(500, "Couldn't write a user in cache key", err)
|
|
|
|
|
return true
|
2018-04-17 04:17:01 +08:00
|
|
|
}
|
2016-02-23 21:22:28 +08:00
|
|
|
}
|
|
|
|
|
|
2015-05-02 18:06:58 +08:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 04:17:01 +08:00
|
|
|
var syncGrafanaUserWithLdapUser = func(query *m.LoginUserQuery) error {
|
2019-04-08 19:31:46 +08:00
|
|
|
ldapCfg := login.LdapCfg
|
|
|
|
|
if len(ldapCfg.Servers) < 1 {
|
|
|
|
|
return fmt.Errorf("No LDAP servers available")
|
2018-03-23 05:13:46 +08:00
|
|
|
}
|
2016-02-23 21:22:28 +08:00
|
|
|
|
2019-04-08 19:31:46 +08:00
|
|
|
for _, server := range ldapCfg.Servers {
|
|
|
|
|
author := login.NewLdapAuthenticator(server)
|
|
|
|
|
if err := author.SyncUser(query); err != nil {
|
|
|
|
|
return err
|
2018-04-17 04:17:01 +08:00
|
|
|
}
|
2016-02-23 21:22:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-22 01:28:56 +08:00
|
|
|
func checkAuthenticationProxy(remoteAddr string, proxyHeaderValue string) error {
|
2018-03-23 05:13:46 +08:00
|
|
|
if len(strings.TrimSpace(setting.AuthProxyWhitelist)) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-03-22 01:28:56 +08:00
|
|
|
|
2018-06-15 19:40:42 +08:00
|
|
|
proxies := strings.Split(setting.AuthProxyWhitelist, ",")
|
2018-12-18 13:43:14 +08:00
|
|
|
var proxyObjs []*net.IPNet
|
|
|
|
|
for _, proxy := range proxies {
|
|
|
|
|
proxyObjs = append(proxyObjs, coerceProxyAddress(proxy))
|
2018-06-28 21:43:33 +08:00
|
|
|
}
|
2018-06-15 19:40:42 +08:00
|
|
|
|
2018-12-18 13:43:14 +08:00
|
|
|
sourceIP, _, _ := net.SplitHostPort(remoteAddr)
|
|
|
|
|
sourceObj := net.ParseIP(sourceIP)
|
|
|
|
|
|
|
|
|
|
for _, proxyObj := range proxyObjs {
|
|
|
|
|
if proxyObj.Contains(sourceObj) {
|
2018-03-22 01:28:56 +08:00
|
|
|
return nil
|
2016-02-23 21:22:28 +08:00
|
|
|
}
|
2018-03-23 05:13:46 +08:00
|
|
|
}
|
2018-06-28 21:43:33 +08:00
|
|
|
return fmt.Errorf("Request for user (%s) from %s is not from the authentication proxy", proxyHeaderValue, sourceIP)
|
2016-02-23 21:22:28 +08:00
|
|
|
}
|
2018-12-18 13:43:14 +08: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
|
|
|
|
|
}
|