Initial work on ldap support, #1450

This commit is contained in:
Torkel Ödegaard 2015-06-04 09:34:42 +02:00
parent 2c7d33cdfa
commit eb793f7feb
8 changed files with 204 additions and 14 deletions

View File

@ -174,6 +174,18 @@ header_name = X-WEBAUTH-USER
header_property = username
auto_sign_up = true
#################################### Auth LDAP ##########################
[auth.ldap]
enabled = true
hosts = ldap://localhost.com:389
use_ssl = false
base_dn = dc=grafana,dc=org
bind_path = cn=%username%,dc=grafana,dc=org
attr_username = cn
attr_name = cn
attr_surname = sn
attr_email = email
#################################### Logging ##########################
[log]
# Either "console", "file", default is "console"

View File

@ -19,7 +19,7 @@ func Register(r *macaron.Macaron) {
// not logged in views
r.Get("/", reqSignedIn, Index)
r.Get("/logout", Logout)
r.Post("/login", bind(dtos.LoginCommand{}), LoginPost)
r.Post("/login", bind(dtos.LoginCommand{}), wrap(LoginPost))
r.Get("/login/:name", OAuthLogin)
r.Get("/login", LoginView)

View File

View File

@ -0,0 +1,56 @@
package ldapauth
import (
"errors"
"fmt"
"net/url"
"github.com/gogits/gogs/modules/ldap"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/setting"
)
var (
ErrInvalidCredentials = errors.New("Invalid Username or Password")
)
func Login(username, password string) error {
url, err := url.Parse(setting.LdapUrls[0])
if err != nil {
return err
}
log.Info("Host: %v", url.Host)
conn, err := ldap.Dial("tcp", url.Host)
if err != nil {
return err
}
defer conn.Close()
bindFormat := "cn=%s,dc=grafana,dc=org"
nx := fmt.Sprintf(bindFormat, username)
err = conn.Bind(nx, password)
if err != nil {
if ldapErr, ok := err.(*ldap.Error); ok {
if ldapErr.ResultCode == 49 {
return ErrInvalidCredentials
}
}
return err
}
return nil
// search := ldap.NewSearchRequest(url.Path,
// ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
// fmt.Sprintf(ls.Filter, name),
// []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
// nil)
// sr, err := l.Search(search)
// if err != nil {
// log.Debug("LDAP Authen OK but not in filter %s", name)
// return "", "", "", "", false
// }
}

View File

@ -4,6 +4,8 @@ import (
"net/url"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/ldapauth"
"github.com/grafana/grafana/pkg/auth"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
@ -86,21 +88,28 @@ func LoginApiPing(c *middleware.Context) {
c.JsonOK("Logged in")
}
func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) {
userQuery := m.GetUserByLoginQuery{LoginOrEmail: cmd.User}
err := bus.Dispatch(&userQuery)
if err != nil {
c.JsonApiErr(401, "Invalid username or password", err)
return
func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) Response {
sourcesQuery := auth.GetAuthSourcesQuery{}
if err := bus.Dispatch(&sourcesQuery); err != nil {
return ApiError(500, "Could not get login sources", err)
}
user := userQuery.Result
var err error
var user *m.User
passwordHashed := util.EncodePassword(cmd.Password, user.Salt)
if passwordHashed != user.Password {
c.JsonApiErr(401, "Invalid username or password", err)
return
for _, authSource := range sourcesQuery.Sources {
user, err = authSource.AuthenticateUser(cmd.User, cmd.Password)
if err == nil {
break
}
// handle non invalid credentials error, otherwise try next auth source
if err != auth.ErrInvalidCredentials {
return ApiError(500, "Error while trying to authenticate user", err)
}
}
if err != nil {
return ApiError(401, "Invalid username or password", err)
}
loginUserWithUser(user, c)
@ -116,7 +125,20 @@ func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) {
metrics.M_Api_Login_Post.Inc(1)
c.JSON(200, result)
return Json(200, result)
}
func LoginUsingLdap(c *middleware.Context, cmd dtos.LoginCommand) Response {
err := ldapauth.Login(cmd.User, cmd.Password)
if err != nil {
if err == ldapauth.ErrInvalidCredentials {
return ApiError(401, "Invalid username or password", err)
}
return ApiError(500, "Ldap login failed", err)
}
return Empty(401)
}
func loginUserWithUser(user *m.User, c *middleware.Context) {

73
pkg/auth/auth.go Normal file
View File

@ -0,0 +1,73 @@
package auth
import (
"errors"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
var (
ErrInvalidCredentials = errors.New("Invalid Username or Password")
)
type LoginSettings struct {
LdapEnabled bool
}
type LdapFilterToOrg struct {
Filter string
OrgId int
OrgRole string
}
type LdapSettings struct {
Enabled bool
Hosts []string
UseSSL bool
BindDN string
AttrUsername string
AttrName string
AttrSurname string
AttrMail string
Filters []LdapFilterToOrg
}
type AuthSource interface {
AuthenticateUser(username, password string) (*m.User, error)
}
type GetAuthSourcesQuery struct {
Sources []AuthSource
}
func init() {
bus.AddHandler("auth", GetAuthSources)
}
func GetAuthSources(query *GetAuthSourcesQuery) error {
query.Sources = []AuthSource{&GrafanaDBAuthSource{}}
return nil
}
type GrafanaDBAuthSource struct {
}
func (s *GrafanaDBAuthSource) AuthenticateUser(username, password string) (*m.User, error) {
userQuery := m.GetUserByLoginQuery{LoginOrEmail: username}
err := bus.Dispatch(&userQuery)
if err != nil {
return nil, ErrInvalidCredentials
}
user := userQuery.Result
passwordHashed := util.EncodePassword(password, user.Salt)
if passwordHashed != user.Password {
return nil, ErrInvalidCredentials
}
return user, nil
}

View File

@ -114,6 +114,10 @@ var (
ReportingEnabled bool
GoogleAnalyticsId string
// LDAP
LdapEnabled bool
LdapUrls []string
)
type CommandLineArgs struct {
@ -406,6 +410,10 @@ func NewConfigContext(args *CommandLineArgs) {
ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)
GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
ldapSec := Cfg.Section("auth.ldap")
LdapEnabled = ldapSec.Key("enabled").MustBool(false)
LdapUrls = ldapSec.Key("urls").Strings(" ")
readSessionConfig()
}

View File

@ -0,0 +1,19 @@
package setting
type LdapFilterToOrg struct {
Filter string
OrgId int
OrgRole string
}
type LdapSettings struct {
Enabled bool
Hosts []string
UseSSL bool
BindDN string
AttrUsername string
AttrName string
AttrSurname string
AttrMail string
Filters []LdapFilterToOrg
}