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 bindDN: uid=seviceaccount,cn=users,dc=example,dc=com
bindPW: password bindPW: password
user: user:
baseDN: cn=users,dc=example,dc=com baseDN: ou=users,dc=example,dc=com
filter: "(objectClass=person)" filter: "(cn={0})"
userAttr: uid
requiredGroups: requiredGroups:
- appAdmin - appAdmin
group: group:
baseDN: cn=groups,dc=freeipa,dc=example,dc=com baseDN: ou=groups,dc=example,dc=com
filter: "(objectClass=group)" groupAttr: cn
userAttr: uid filter: "(member={0})"
groupAttr: member
timeout: timeout:
success: 24h success: 24h
wrong: 5m wrong: 5m

View file

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

View file

@ -2,22 +2,29 @@ package main
import ( import (
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"math/rand" "math/rand"
"regexp" "regexp"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
ldap "gopkg.in/ldap.v2" ldap "gopkg.in/ldap.v2"
) )
var conn *ldap.Conn var (
conn *ldap.Conn
admin bool
lock sync.Mutex
)
func setupLDAP() error { func setupLDAP() error {
size := len(config.Servers) size := len(config.Servers)
if size == 0 { 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())) r := rand.New(rand.NewSource(time.Now().Unix()))
@ -79,22 +86,28 @@ func setupLDAP() error {
} }
} }
if config.Auth.BindDN != "" || config.Auth.BindPW != "" { admin = false
err = conn.Bind(config.Auth.BindDN, config.Auth.BindPW) return auth()
if err != nil { }
return err
} 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 return nil
} }
func ldapLogin(username, password string) (bool, error) { func ldapLogin(username, password string) (bool, error) {
// TODO: lock lock.Lock()
if config.Auth.BindDN != "" || config.Auth.BindPW != "" { defer lock.Unlock()
err := conn.Bind(config.Auth.BindDN, config.Auth.BindPW)
if err != nil { err := auth()
return false, err if err != nil {
} return false, err
} }
req := ldap.NewSearchRequest( req := ldap.NewSearchRequest(
@ -105,12 +118,11 @@ func ldapLogin(username, password string) (bool, error) {
0, 0,
false, false,
strings.Replace(config.User.Filter, "{0}", username, -1), strings.Replace(config.User.Filter, "{0}", username, -1),
[]string{config.User.UserAttr}, nil,
nil, nil,
) )
res, err := conn.Search(req) res, err := conn.Search(req)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -119,6 +131,51 @@ func ldapLogin(username, password string) (bool, error) {
return false, nil return false, nil
} }
admin = false
err = conn.Bind(res.Entries[0].DN, password) 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", Web: "0.0.0.0:5555",
Path: "/", Path: "/",
User: UserConfig{ User: UserConfig{
UserAttr: "uid", Filter: "(cn={0})",
}, },
Group: GroupConfig{ Group: GroupConfig{
UserAttr: "uid", Filter: "(member={0})",
GroupAttr: "member", GroupAttr: "cn",
}, },
Timeout: TimeoutConfig{ Timeout: TimeoutConfig{
Success: 24 * time.Hour, Success: 24 * time.Hour,
@ -45,7 +45,7 @@ func main() {
log.Fatalf("Error on parse config: %v\n", err) 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() err = setupLDAP()
if err != nil { if err != nil {
@ -73,6 +73,8 @@ func handler(w http.ResponseWriter, r *http.Request) {
secret := strings.SplitN(string(decoded), ":", 2) secret := strings.SplitN(string(decoded), ":", 2)
if len(secret) == 2 && validate(secret[0], secret[1]) { 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) w.WriteHeader(http.StatusOK)
return return
} }