Skip to content

Commit

Permalink
support ldap authentication
Browse files Browse the repository at this point in the history
Signed-off-by: Lin Qing <[email protected]>
  • Loading branch information
summerQLin committed Sep 2, 2015
1 parent bdd0d52 commit d7d0918
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 2 deletions.
146 changes: 146 additions & 0 deletions auth_server/authn/ldap_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
Copyright 2015 Cesanta Software Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package authn

import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"strings"

"github.com/go-ldap/ldap"
"github.com/golang/glog"
)

type LdapAuthConfig struct {
Domain string `yaml:"domain,omitempty"`
Port uint16 `yaml:"port,omitempty"`
StartTLS bool `yaml:"startTLS,omitempty"`
BaseDN string `yaml:"baseDN,omitempty"`
LoginAttributes []string `yaml:"loginAttribute,omitempty"`
GroupBaseDN string `yaml:"groupBaseDN,omitempty"`
GroupAttribute string `yaml:"groupAttribute,omitempty"`
}

type LdapAuth struct {
config *LdapAuthConfig
}

func NewLdapAuth(c *LdapAuthConfig) (*LdapAuth, error) {
return &LdapAuth{
config: c,
}, nil
}

func (la *LdapAuth) Authenticate(user string, password PasswordString) (bool, error) {
if user == "" {
return true, nil
}
l, err := la.ldapConnection()
if err != nil {
return false, err
}
defer l.Close()
//l.Debug = true
filter := la.getLoginFilter(user)
userEntryDN, uSearchErr := la.ldapSearch(l, &la.config.BaseDN, &filter, &[]string{})
if uSearchErr != nil {
return false, uSearchErr
}
if len(userEntryDN) > 0 {
err := l.Bind(userEntryDN, string(password))
if err != nil {
return false, err
}
}
return true, nil
}

func (la *LdapAuth) Name() string {
return "Ldap"
}

func (la *LdapAuth) ldapConnection() (*ldap.Conn, error) {
glog.V(2).Infof("Dial: starting...%s:%d", la.config.Domain, la.config.Port)
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", la.config.Domain, la.config.Port))
if err != nil {
return nil, err
}
if la.config.StartTLS {
glog.V(2).Infof("StartTLS...")
err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
return nil, err
}
}
return l, nil
}

//make filter by login attributes, e.g. login in by ['cn', 'uid']
//the filter will be '(|(cn=account)(uid=account))'
func (la *LdapAuth) getLoginFilter(user string) string {
var buffer bytes.Buffer
buffer.WriteString("(|")
for _, attr := range la.config.LoginAttributes {
buffer.WriteString(fmt.Sprintf("(%s=%s)", attr, user))
}
buffer.WriteString(")")
return fmt.Sprintf(buffer.String())
}

//ldap search and return required attributes' value from searched entries
//default return entry's DN value if you leave attrs array empty
func (la *LdapAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (string, error) {
if l == nil {
return "", errors.New("No ldap connection!")
}
glog.V(2).Infof("Searching...basedDN:%s, filter:%s", *baseDN, *filter)
searchRequest := ldap.NewSearchRequest(
*baseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
*filter,
*attrs,
nil)
sr, err := l.Search(searchRequest)
if err != nil {
return "", err
}

if len(sr.Entries) != 1 {
return "", errors.New("Error...Search Result number != 1\n")
}

var buffer bytes.Buffer
for _, entry := range sr.Entries {
if len(*attrs) == 0 {
glog.V(2).Infof("Entry DN = %s", entry.DN)
buffer.WriteString(entry.DN)
} else {
for _, attr := range *attrs {
values := strings.Join(entry.GetAttributeValues(attr), " ")
glog.V(2).Infof("Entry %s = %s", attr, values)
buffer.WriteString(values)
}
}
}

return buffer.String(), nil
}

func (la *LdapAuth) Stop() {
}
4 changes: 2 additions & 2 deletions auth_server/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type Config struct {
Token TokenConfig `yaml:"token"`
Users map[string]*authn.Requirements `yaml:"users,omitempty"`
GoogleAuth *authn.GoogleAuthConfig `yaml:"google_auth,omitempty"`
LdapAuth *authn.LdapAuthConfig `yaml:"ldap_auth,omitempty"`
ACL authz.ACL `yaml:"acl"`
}

Expand Down Expand Up @@ -68,8 +69,7 @@ func validate(c *Config) error {
if c.Token.Expiration <= 0 {
return fmt.Errorf("expiration must be positive, got %d", c.Token.Expiration)
}

if c.Users == nil && c.GoogleAuth == nil {
if c.Users == nil && c.GoogleAuth == nil && c.LdapAuth == nil {
return errors.New("no auth methods are configured, this is probably a mistake. Use an empty user map if you really want to deny everyone.")
}
if gac := c.GoogleAuth; gac != nil {
Expand Down
10 changes: 10 additions & 0 deletions auth_server/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type AuthServer struct {
authenticators []authn.Authenticator
authorizers []authz.Authorizer
ga *authn.GoogleAuth
la *authn.LdapAuth
}

func NewAuthServer(c *Config) (*AuthServer, error) {
Expand All @@ -66,6 +67,14 @@ func NewAuthServer(c *Config) (*AuthServer, error) {
as.authenticators = append(as.authenticators, ga)
as.ga = ga
}
if c.LdapAuth != nil {
la, err := authn.NewLdapAuth(c.LdapAuth)
if err != nil {
return nil, err
}
as.authenticators = append(as.authenticators, la)
as.la = la
}
return as, nil
}

Expand Down Expand Up @@ -119,6 +128,7 @@ func (as *AuthServer) Authenticate(ar *AuthRequest) (bool, error) {
func (as *AuthServer) Authorize(ar *AuthRequest) ([]string, error) {
for i, a := range as.authorizers {
result, err := a.Authorize(&ar.ai)

glog.V(2).Infof("Authz %s %s -> %s, %s", a.Name(), ar.ai, result, err)
if err != nil {
if err == authz.NoMatch {
Expand Down
22 changes: 22 additions & 0 deletions examples/ldap_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
server:
addr: :5001
certificate: /path/to/server.pem
key: /path/to/server.key
token:
issuer: Acme auth server
expiration: 900
ldap_auth:
domain: ldap.example.com
port: 389
startTLS: true
baseDN: o=example.com
loginAttribute:
- uid
- cn
acl:
- match:
account: ""
actions: [pull]
- match:
account: /.+/
actions: ['*']

0 comments on commit d7d0918

Please sign in to comment.