Match groups

This commit is contained in:
Tiago Augusto Pimenta 2018-10-02 18:54:00 -03:00
parent 551ee05177
commit 05f005707c
4 changed files with 85 additions and 30 deletions

View file

@ -14,16 +14,14 @@ data:
bindDN: uid=seviceaccount,cn=users,dc=example,dc=com
bindPW: password
user:
baseDN: cn=users,dc=example,dc=com
filter: "(objectClass=person)"
userAttr: uid
baseDN: ou=users,dc=example,dc=com
filter: "(cn={0})"
requiredGroups:
- appAdmin
group:
baseDN: cn=groups,dc=freeipa,dc=example,dc=com
filter: "(objectClass=group)"
userAttr: uid
groupAttr: member
baseDN: ou=groups,dc=example,dc=com
groupAttr: cn
filter: "(member={0})"
timeout:
success: 24h
wrong: 5m

View file

@ -10,15 +10,13 @@ type AuthConfig struct {
type UserConfig struct {
BaseDN string `yaml:"baseDN"`
Filter string `yaml:"filter"`
UserAttr string `yaml:"userAttr"`
RequiredGroups []string `yaml:"requiredGroups"`
}
type GroupConfig struct {
BaseDN string `yaml:"baseDN"`
Filter string `yaml:"filter"`
UserAttr string `yaml:"userAttr"`
GroupAttr string `yaml:"groupAttr"`
Filter string `yaml:"filter"`
}
type TimeoutConfig struct {

View file

@ -2,22 +2,29 @@ package main
import (
"crypto/tls"
"errors"
"fmt"
"math/rand"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"time"
ldap "gopkg.in/ldap.v2"
)
var conn *ldap.Conn
var (
conn *ldap.Conn
admin bool
lock sync.Mutex
)
func setupLDAP() error {
size := len(config.Servers)
if size == 0 {
return fmt.Errorf("No LDAP server available on %+v", config)
return errors.New("No LDAP server available!")
}
r := rand.New(rand.NewSource(time.Now().Unix()))
@ -79,22 +86,28 @@ func setupLDAP() error {
}
}
if config.Auth.BindDN != "" || config.Auth.BindPW != "" {
err = conn.Bind(config.Auth.BindDN, config.Auth.BindPW)
if err != nil {
return err
}
admin = false
return auth()
}
func auth() error {
if admin || config.Auth.BindDN == "" && config.Auth.BindPW == "" {
return nil
}
err := conn.Bind(config.Auth.BindDN, config.Auth.BindPW)
if err == nil {
admin = true
}
return nil
}
func ldapLogin(username, password string) (bool, error) {
// TODO: lock
if config.Auth.BindDN != "" || config.Auth.BindPW != "" {
err := conn.Bind(config.Auth.BindDN, config.Auth.BindPW)
if err != nil {
return false, err
}
lock.Lock()
defer lock.Unlock()
err := auth()
if err != nil {
return false, err
}
req := ldap.NewSearchRequest(
@ -105,12 +118,11 @@ func ldapLogin(username, password string) (bool, error) {
0,
false,
strings.Replace(config.User.Filter, "{0}", username, -1),
[]string{config.User.UserAttr},
nil,
nil,
)
res, err := conn.Search(req)
if err != nil {
return false, err
}
@ -119,6 +131,51 @@ func ldapLogin(username, password string) (bool, error) {
return false, nil
}
admin = false
err = conn.Bind(res.Entries[0].DN, password)
return err == nil, nil
if err != nil {
return false, nil
}
err = auth()
if err != nil {
return false, err
}
if len(config.User.RequiredGroups) == 0 {
return true, nil
}
req = ldap.NewSearchRequest(
config.Group.BaseDN,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0,
0,
false,
strings.Replace(config.Group.Filter, "{0}", res.Entries[0].DN, -1),
[]string{config.Group.GroupAttr},
nil,
)
res, err = conn.Search(req)
if err != nil {
return false, err
}
groups := []string{}
for _, entry := range res.Entries {
groups = append(groups, entry.GetAttributeValue(config.Group.GroupAttr))
}
sort.Strings(groups)
for _, group := range config.User.RequiredGroups {
pos := sort.SearchStrings(groups, group)
if pos >= len(groups) || groups[pos] != group {
return false, nil
}
}
return true, nil
}

View file

@ -19,11 +19,11 @@ var (
Web: "0.0.0.0:5555",
Path: "/",
User: UserConfig{
UserAttr: "uid",
Filter: "(cn={0})",
},
Group: GroupConfig{
UserAttr: "uid",
GroupAttr: "member",
Filter: "(member={0})",
GroupAttr: "cn",
},
Timeout: TimeoutConfig{
Success: 24 * time.Hour,
@ -45,7 +45,7 @@ func main() {
log.Fatalf("Error on parse config: %v\n", err)
}
fmt.Printf("Loaded config: %+v\n", config)
fmt.Printf("Loaded config \"%s\".\n", *configFile)
err = setupLDAP()
if err != nil {
@ -73,6 +73,8 @@ func handler(w http.ResponseWriter, r *http.Request) {
secret := strings.SplitN(string(decoded), ":", 2)
if len(secret) == 2 && validate(secret[0], secret[1]) {
// TODO: match by header, e.g: X-Original-URL X-Original-Method X-Sent-From X-Auth-Request-Redirect
w.WriteHeader(http.StatusOK)
return
}