254 lines
5.4 KiB
Go
254 lines
5.4 KiB
Go
package ldapserver
|
|
|
|
import (
|
|
"strings"
|
|
|
|
ldap "bottin/goldap"
|
|
)
|
|
|
|
// Constant to LDAP Request protocol Type names
|
|
const (
|
|
SEARCH = "SearchRequest"
|
|
BIND = "BindRequest"
|
|
COMPARE = "CompareRequest"
|
|
ADD = "AddRequest"
|
|
MODIFY = "ModifyRequest"
|
|
DELETE = "DelRequest"
|
|
EXTENDED = "ExtendedRequest"
|
|
ABANDON = "AbandonRequest"
|
|
)
|
|
|
|
// HandlerFunc type is an adapter to allow the use of
|
|
// ordinary functions as LDAP handlers. If f is a function
|
|
// with the appropriate signature, HandlerFunc(f) is a
|
|
// Handler object that calls f.
|
|
type HandlerFunc func(UserState, ResponseWriter, *Message)
|
|
|
|
// RouteMux manages all routes
|
|
type RouteMux struct {
|
|
routes []*route
|
|
notFoundRoute *route
|
|
}
|
|
|
|
type route struct {
|
|
label string
|
|
operation string
|
|
handler HandlerFunc
|
|
exoName string
|
|
sBasedn string
|
|
uBasedn bool
|
|
sFilter string
|
|
uFilter bool
|
|
sScope int
|
|
uScope bool
|
|
sAuthChoice string
|
|
uAuthChoice bool
|
|
}
|
|
|
|
// Match return true when the *Message matches the route
|
|
// conditions
|
|
func (r *route) Match(m *Message) bool {
|
|
if m.ProtocolOpName() != r.operation {
|
|
return false
|
|
}
|
|
|
|
switch v := m.ProtocolOp().(type) {
|
|
case ldap.BindRequest:
|
|
if r.uAuthChoice == true {
|
|
if strings.ToLower(v.AuthenticationChoice()) != r.sAuthChoice {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
|
|
case ldap.ExtendedRequest:
|
|
if string(v.RequestName()) != r.exoName {
|
|
return false
|
|
}
|
|
return true
|
|
|
|
case ldap.SearchRequest:
|
|
if r.uBasedn == true {
|
|
if strings.ToLower(string(v.BaseObject())) != r.sBasedn {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if r.uFilter == true {
|
|
if strings.ToLower(v.FilterString()) != r.sFilter {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if r.uScope == true {
|
|
if int(v.Scope()) != r.sScope {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (r *route) Label(label string) *route {
|
|
r.label = label
|
|
return r
|
|
}
|
|
|
|
func (r *route) BaseDn(dn string) *route {
|
|
r.sBasedn = strings.ToLower(dn)
|
|
r.uBasedn = true
|
|
return r
|
|
}
|
|
|
|
func (r *route) AuthenticationChoice(choice string) *route {
|
|
r.sAuthChoice = strings.ToLower(choice)
|
|
r.uAuthChoice = true
|
|
return r
|
|
}
|
|
|
|
func (r *route) Filter(pattern string) *route {
|
|
r.sFilter = strings.ToLower(pattern)
|
|
r.uFilter = true
|
|
return r
|
|
}
|
|
|
|
func (r *route) Scope(scope int) *route {
|
|
r.sScope = scope
|
|
r.uScope = true
|
|
return r
|
|
}
|
|
|
|
func (r *route) RequestName(name ldap.LDAPOID) *route {
|
|
r.exoName = string(name)
|
|
return r
|
|
}
|
|
|
|
// NewRouteMux returns a new *RouteMux
|
|
// RouteMux implements ldapserver.Handler
|
|
func NewRouteMux() *RouteMux {
|
|
return &RouteMux{}
|
|
}
|
|
|
|
// Handler interface used to serve a LDAP Request message
|
|
type Handler interface {
|
|
ServeLDAP(s UserState, w ResponseWriter, r *Message)
|
|
}
|
|
|
|
// ServeLDAP dispatches the request to the handler whose
|
|
// pattern most closely matches the request request Message.
|
|
func (h *RouteMux) ServeLDAP(s UserState, w ResponseWriter, r *Message) {
|
|
|
|
//find a matching Route
|
|
for _, route := range h.routes {
|
|
|
|
//if the route don't match, skip it
|
|
if route.Match(r) == false {
|
|
continue
|
|
}
|
|
|
|
if route.label != "" {
|
|
Logger.Tracef("")
|
|
Logger.Tracef(" ROUTE MATCH ; %s", route.label)
|
|
Logger.Tracef("")
|
|
}
|
|
|
|
route.handler(s, w, r)
|
|
return
|
|
}
|
|
|
|
// Catch a AbandonRequest not handled by user
|
|
switch v := r.ProtocolOp().(type) {
|
|
case ldap.AbandonRequest:
|
|
// retreive the request to abandon, and send a abort signal to it
|
|
if requestToAbandon, ok := r.Client.GetMessageByID(int(v)); ok {
|
|
requestToAbandon.Abandon()
|
|
}
|
|
}
|
|
|
|
if h.notFoundRoute != nil {
|
|
h.notFoundRoute.handler(s, w, r)
|
|
} else {
|
|
res := NewResponse(LDAPResultUnwillingToPerform)
|
|
res.SetDiagnosticMessage("Operation not implemented by server")
|
|
w.Write(res)
|
|
}
|
|
}
|
|
|
|
// Adds a new Route to the Handler
|
|
func (h *RouteMux) addRoute(r *route) {
|
|
//and finally append to the list of Routes
|
|
//create the Route
|
|
h.routes = append(h.routes, r)
|
|
}
|
|
|
|
func (h *RouteMux) NotFound(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.handler = handler
|
|
h.notFoundRoute = route
|
|
return route
|
|
}
|
|
|
|
func (h *RouteMux) Bind(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.operation = BIND
|
|
route.handler = handler
|
|
h.addRoute(route)
|
|
return route
|
|
}
|
|
|
|
func (h *RouteMux) Search(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.operation = SEARCH
|
|
route.handler = handler
|
|
h.addRoute(route)
|
|
return route
|
|
}
|
|
|
|
func (h *RouteMux) Add(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.operation = ADD
|
|
route.handler = handler
|
|
h.addRoute(route)
|
|
return route
|
|
}
|
|
|
|
func (h *RouteMux) Delete(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.operation = DELETE
|
|
route.handler = handler
|
|
h.addRoute(route)
|
|
return route
|
|
}
|
|
|
|
func (h *RouteMux) Modify(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.operation = MODIFY
|
|
route.handler = handler
|
|
h.addRoute(route)
|
|
return route
|
|
}
|
|
|
|
func (h *RouteMux) Compare(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.operation = COMPARE
|
|
route.handler = handler
|
|
h.addRoute(route)
|
|
return route
|
|
}
|
|
|
|
func (h *RouteMux) Extended(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.operation = EXTENDED
|
|
route.handler = handler
|
|
h.addRoute(route)
|
|
return route
|
|
}
|
|
|
|
func (h *RouteMux) Abandon(handler HandlerFunc) *route {
|
|
route := &route{}
|
|
route.operation = ABANDON
|
|
route.handler = handler
|
|
h.addRoute(route)
|
|
return route
|
|
}
|