connector: add a group interface and an LDAP implementation
This commit is contained in:
parent
af6aade6d0
commit
731dadb29d
2 changed files with 70 additions and 27 deletions
|
@ -112,6 +112,7 @@ type LDAPConnector struct {
|
|||
searchScope int
|
||||
searchBindDN string
|
||||
searchBindPw string
|
||||
searchGroupFilter string
|
||||
|
||||
bindTemplate string
|
||||
|
||||
|
@ -212,6 +213,7 @@ func (cfg *LDAPConnectorConfig) Connector(ns url.URL, lf oidc.LoginFunc, tpls *t
|
|||
emailAttribute: cfg.EmailAttribute,
|
||||
searchBeforeAuth: cfg.SearchBeforeAuth,
|
||||
searchFilter: cfg.SearchFilter,
|
||||
searchGroupFilter: cfg.SearchGroupFilter,
|
||||
searchScope: searchScope,
|
||||
searchBindDN: cfg.SearchBindDN,
|
||||
searchBindPw: cfg.SearchBindPw,
|
||||
|
@ -433,12 +435,47 @@ func invalidBindCredentials(err error) bool {
|
|||
|
||||
func (c *LDAPConnector) formatDN(template, username string) string {
|
||||
result := template
|
||||
result = strings.Replace(result, "%u", username, -1)
|
||||
result = strings.Replace(result, "%u", ldap.EscapeFilter(username), -1)
|
||||
result = strings.Replace(result, "%b", c.baseDN, -1)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *LDAPConnector) Groups(fullUserID string) ([]string, error) {
|
||||
if !c.searchBeforeAuth {
|
||||
return nil, fmt.Errorf("cannot search without service account")
|
||||
}
|
||||
if c.searchGroupFilter == "" {
|
||||
return nil, fmt.Errorf("no group filter specified")
|
||||
}
|
||||
|
||||
var groups []string
|
||||
err := c.ldapPool.Do(func(conn *ldap.Conn) error {
|
||||
if err := conn.Bind(c.searchBindDN, c.searchBindPw); err != nil {
|
||||
if !invalidBindCredentials(err) {
|
||||
log.Errorf("failed to connect to LDAP for search bind: %v", err)
|
||||
}
|
||||
return fmt.Errorf("failed to bind: %v", err)
|
||||
}
|
||||
|
||||
req := &ldap.SearchRequest{
|
||||
BaseDN: c.baseDN,
|
||||
Scope: c.searchScope,
|
||||
Filter: c.formatDN(c.searchGroupFilter, fullUserID),
|
||||
}
|
||||
resp, err := conn.Search(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("search failed: %v", err)
|
||||
}
|
||||
groups = make([]string, len(resp.Entries))
|
||||
for i, entry := range resp.Entries {
|
||||
groups[i] = entry.DN
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return groups, err
|
||||
}
|
||||
|
||||
func (c *LDAPConnector) Identity(username, password string) (*oidc.Identity, error) {
|
||||
var (
|
||||
identity *oidc.Identity
|
||||
|
@ -447,8 +484,10 @@ func (c *LDAPConnector) Identity(username, password string) (*oidc.Identity, err
|
|||
if c.searchBeforeAuth {
|
||||
err = c.ldapPool.Do(func(conn *ldap.Conn) error {
|
||||
if err := conn.Bind(c.searchBindDN, c.searchBindPw); err != nil {
|
||||
// Don't wrap error as it may be a specific LDAP error.
|
||||
return err
|
||||
if !invalidBindCredentials(err) {
|
||||
log.Errorf("failed to connect to LDAP for search bind: %v", err)
|
||||
}
|
||||
return fmt.Errorf("failed to bind: %v", err)
|
||||
}
|
||||
|
||||
filter := c.formatDN(c.searchFilter, username)
|
||||
|
@ -491,8 +530,10 @@ func (c *LDAPConnector) Identity(username, password string) (*oidc.Identity, err
|
|||
err = c.ldapPool.Do(func(conn *ldap.Conn) error {
|
||||
userBindDN := c.formatDN(c.bindTemplate, username)
|
||||
if err := conn.Bind(userBindDN, password); err != nil {
|
||||
// Don't wrap error as it may be a specific LDAP error.
|
||||
return err
|
||||
if !invalidBindCredentials(err) {
|
||||
log.Errorf("failed to connect to LDAP for search bind: %v", err)
|
||||
}
|
||||
return fmt.Errorf("failed to bind: %v", err)
|
||||
}
|
||||
|
||||
req := &ldap.SearchRequest{
|
||||
|
@ -522,11 +563,7 @@ func (c *LDAPConnector) Identity(username, password string) (*oidc.Identity, err
|
|||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if !invalidBindCredentials(err) {
|
||||
log.Errorf("failed to connect to LDAP for search bind: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return identity, nil
|
||||
|
|
|
@ -60,6 +60,12 @@ type ConnectorConfig interface {
|
|||
Connector(ns url.URL, loginFunc oidc.LoginFunc, tpls *template.Template) (Connector, error)
|
||||
}
|
||||
|
||||
// GroupsConnector is a strategy for mapping a user to a set of groups. This is optionally
|
||||
// implemented by some connectors.
|
||||
type GroupsConnector interface {
|
||||
Groups(fullUserID string) ([]string, error)
|
||||
}
|
||||
|
||||
type ConnectorConfigRepo interface {
|
||||
All() ([]ConnectorConfig, error)
|
||||
GetConnectorByID(repo.Transaction, string) (ConnectorConfig, error)
|
||||
|
|
Reference in a new issue