/
ldapauth.go
83 lines (72 loc) · 1.97 KB
/
ldapauth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package fiber_ldapauth
import (
"errors"
"strings"
"github.com/go-ldap/ldap/v3"
"github.com/gofiber/fiber/v2"
)
// New creates a new middleware handler.
func New(config Config) fiber.Handler {
// Set default config
cfg := configDefault(config)
return func(c *fiber.Ctx) error {
// Do not execute middleware if Next returns true.
if cfg.Next != nil && cfg.Next(c) {
return c.Next()
}
l, err := ldap.DialURL(cfg.URL)
if err != nil {
return errorHandler(c, err, cfg.ErrorCallback)
}
defer l.Close()
if cfg.TLSConfig != nil {
err = l.StartTLS(cfg.TLSConfig)
if err != nil {
return errorHandler(c, err, cfg.ErrorCallback)
}
}
// First bind with a read only user
if cfg.BindCredentials == "" {
err = l.UnauthenticatedBind(cfg.BindDN)
} else {
err = l.Bind(cfg.BindDN, cfg.BindCredentials)
}
if err != nil {
return errorHandler(c, err, cfg.ErrorCallback)
}
// Search user
if cfg.SearchBase != "" || cfg.SearchFilter != "" {
username, password, err := cfg.CredentialsLookup(c, cfg.UsernameField, cfg.PasswordField)
if err != nil {
return errorHandler(c, err, cfg.ErrorCallback)
}
sf := strings.Replace(cfg.SearchFilter, "{{username}}", ldap.EscapeFilter(username), -1)
sreq := ldap.NewSearchRequest(
cfg.SearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
sf,
cfg.SearchAttributes,
nil,
)
sres, err := l.Search(sreq)
if err != nil {
return errorHandler(c, err, cfg.ErrorCallback)
}
if len(sres.Entries) != 1 {
return errorHandler(c, errors.New("user does not exist or too many entries returned"), cfg.ErrorCallback)
}
userDN := sres.Entries[0].DN
// Bind as the user to verify their password
err = l.Bind(userDN, password)
if err != nil {
return errorHandler(c, err, cfg.ErrorCallback)
}
}
// Continue stack
if cfg.SuccessCallback != nil {
return cfg.SuccessCallback(c)
} else {
panic("SuccessCallback must not be nil")
}
}
}