* LDAP: Add API endpoint to query the LDAP server(s) status| This endpoint returns the current status(es) of the configured LDAP server(s). The status of each server is verified by dialling and if no error is returned we assume the server is operational. This is the last piece I'll produce as an API before moving into #18759 and see the view come to life.
208 lines
4.4 KiB
Go
208 lines
4.4 KiB
Go
package multildap
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/grafana/grafana/pkg/models"
|
|
"github.com/grafana/grafana/pkg/services/ldap"
|
|
)
|
|
|
|
// GetConfig gets LDAP config
|
|
var GetConfig = ldap.GetConfig
|
|
|
|
// IsEnabled checks if LDAP is enabled
|
|
var IsEnabled = ldap.IsEnabled
|
|
|
|
// newLDAP return instance of the single LDAP server
|
|
var newLDAP = ldap.New
|
|
|
|
// ErrInvalidCredentials is returned if username and password do not match
|
|
var ErrInvalidCredentials = ldap.ErrInvalidCredentials
|
|
|
|
// ErrCouldNotFindUser is returned when username hasn't been found (not username+password)
|
|
var ErrCouldNotFindUser = ldap.ErrCouldNotFindUser
|
|
|
|
// ErrNoLDAPServers is returned when there is no LDAP servers specified
|
|
var ErrNoLDAPServers = errors.New("No LDAP servers are configured")
|
|
|
|
// ErrDidNotFindUser if request for user is unsuccessful
|
|
var ErrDidNotFindUser = errors.New("Did not find a user")
|
|
|
|
// ServerStatus holds the LDAP server status
|
|
type ServerStatus struct {
|
|
Host string
|
|
Port int
|
|
Available bool
|
|
Error error
|
|
}
|
|
|
|
// IMultiLDAP is interface for MultiLDAP
|
|
type IMultiLDAP interface {
|
|
Ping() ([]*ServerStatus, error)
|
|
Login(query *models.LoginUserQuery) (
|
|
*models.ExternalUserInfo, error,
|
|
)
|
|
|
|
Users(logins []string) (
|
|
[]*models.ExternalUserInfo, error,
|
|
)
|
|
|
|
User(login string) (
|
|
*models.ExternalUserInfo, ldap.ServerConfig, error,
|
|
)
|
|
}
|
|
|
|
// MultiLDAP is basic struct of LDAP authorization
|
|
type MultiLDAP struct {
|
|
configs []*ldap.ServerConfig
|
|
}
|
|
|
|
// New creates the new LDAP auth
|
|
func New(configs []*ldap.ServerConfig) IMultiLDAP {
|
|
return &MultiLDAP{
|
|
configs: configs,
|
|
}
|
|
}
|
|
|
|
// Ping dials each of the LDAP servers and returns their status. If the server is unavailable, it also returns the error.
|
|
func (multiples *MultiLDAP) Ping() ([]*ServerStatus, error) {
|
|
|
|
if len(multiples.configs) == 0 {
|
|
return nil, ErrNoLDAPServers
|
|
}
|
|
|
|
serverStatuses := []*ServerStatus{}
|
|
for _, config := range multiples.configs {
|
|
|
|
status := &ServerStatus{}
|
|
|
|
status.Host = config.Host
|
|
status.Port = config.Port
|
|
|
|
server := newLDAP(config)
|
|
err := server.Dial()
|
|
|
|
if err == nil {
|
|
status.Available = true
|
|
serverStatuses = append(serverStatuses, status)
|
|
} else {
|
|
status.Available = false
|
|
status.Error = err
|
|
serverStatuses = append(serverStatuses, status)
|
|
}
|
|
|
|
defer server.Close()
|
|
}
|
|
|
|
return serverStatuses, nil
|
|
}
|
|
|
|
// Login tries to log in the user in multiples LDAP
|
|
func (multiples *MultiLDAP) Login(query *models.LoginUserQuery) (
|
|
*models.ExternalUserInfo, error,
|
|
) {
|
|
|
|
if len(multiples.configs) == 0 {
|
|
return nil, ErrNoLDAPServers
|
|
}
|
|
|
|
for _, config := range multiples.configs {
|
|
server := newLDAP(config)
|
|
|
|
if err := server.Dial(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer server.Close()
|
|
|
|
user, err := server.Login(query)
|
|
if user != nil {
|
|
return user, nil
|
|
}
|
|
|
|
// Continue if we couldn't find the user
|
|
if err == ErrCouldNotFindUser {
|
|
continue
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Return invalid credentials if we couldn't find the user anywhere
|
|
return nil, ErrInvalidCredentials
|
|
}
|
|
|
|
// User attempts to find an user by login/username by searching into all of the configured LDAP servers. Then, if the user is found it returns the user alongisde the server it was found.
|
|
func (multiples *MultiLDAP) User(login string) (
|
|
*models.ExternalUserInfo,
|
|
ldap.ServerConfig,
|
|
error,
|
|
) {
|
|
|
|
if len(multiples.configs) == 0 {
|
|
return nil, ldap.ServerConfig{}, ErrNoLDAPServers
|
|
}
|
|
|
|
search := []string{login}
|
|
for _, config := range multiples.configs {
|
|
server := newLDAP(config)
|
|
|
|
if err := server.Dial(); err != nil {
|
|
return nil, *config, err
|
|
}
|
|
|
|
defer server.Close()
|
|
|
|
if err := server.Bind(); err != nil {
|
|
return nil, *config, err
|
|
}
|
|
|
|
users, err := server.Users(search)
|
|
if err != nil {
|
|
return nil, *config, err
|
|
}
|
|
|
|
if len(users) != 0 {
|
|
return users[0], *config, nil
|
|
}
|
|
}
|
|
|
|
return nil, ldap.ServerConfig{}, ErrDidNotFindUser
|
|
}
|
|
|
|
// Users gets users from multiple LDAP servers
|
|
func (multiples *MultiLDAP) Users(logins []string) (
|
|
[]*models.ExternalUserInfo,
|
|
error,
|
|
) {
|
|
var result []*models.ExternalUserInfo
|
|
|
|
if len(multiples.configs) == 0 {
|
|
return nil, ErrNoLDAPServers
|
|
}
|
|
|
|
for _, config := range multiples.configs {
|
|
server := newLDAP(config)
|
|
|
|
if err := server.Dial(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer server.Close()
|
|
|
|
if err := server.Bind(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
users, err := server.Users(logins)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result = append(result, users...)
|
|
}
|
|
|
|
return result, nil
|
|
}
|