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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
package main
import (
"errors"
"fmt"
"net/http"
"github.com/go-ldap/ldap/v3"
)
/* Check credentials against LDAP */
type LdapPreAuth struct {
WithConfig *Config
OnWrongPassword ErrorHandler
OnFailure ErrorHandler
OnCreds CredsHandler
}
func (l LdapPreAuth) WithCreds(username, password string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var e *LdapWrongPasswordError
access_key, secret_key, err := LdapGetS3(l.WithConfig, username, password)
if err == nil {
l.OnCreds.WithCreds(access_key, secret_key).ServeHTTP(w, r)
} else if errors.As(err, &e) {
l.OnWrongPassword.WithError(err).ServeHTTP(w, r)
} else {
l.OnFailure.WithError(e).ServeHTTP(w, r)
}
})
}
/**
* Private logic
*/
type ldapConnector struct {
conn *ldap.Conn
config *Config
userDn string
}
type LdapError struct {
Username string
Err error
}
func (e *LdapError) Error() string { return "ldap error for "+e.Username+": "+e.Err.Error() }
type LdapWrongPasswordError struct { LdapError }
func LdapGetS3(c *Config, username, password string) (access_key, secret_key string, werr error) {
// 1. Connect to the server
conn, err := ldapConnect(c)
if err != nil {
werr = &LdapError { username, err }
return
}
defer conn.Close()
// 2. Authenticate with provided credentials
// @FIXME we should better check the error, it could also be due to an LDAP error
err = conn.auth(username, password)
if err != nil {
werr = &LdapWrongPasswordError { LdapError { username, err } }
return
}
// 3. Fetch user's profile
profile, err := conn.profile()
if err != nil {
werr = &LdapError { username, err }
return
}
// 4. Basic checks upon users' attributes
access_key = profile.GetAttributeValue("garage_s3_access_key")
secret_key = profile.GetAttributeValue("garage_s3_secret_key")
if access_key == "" || secret_key == "" {
err = errors.New(fmt.Sprintf("Either access key or secret key is missing in LDAP for %s", conn.userDn))
werr = &LdapError { username, err }
return
}
// 5. Send fetched credentials to the next middleware
return
}
func ldapConnect(c *Config) (ldapConnector, error) {
ldapSock, err := ldap.Dial("tcp", c.LdapServer)
if err != nil {
return ldapConnector{}, err
}
return ldapConnector{
conn: ldapSock,
config: c,
}, nil
}
func (l *ldapConnector) auth(username, password string) error {
l.userDn = fmt.Sprintf("%s=%s,%s", l.config.UserNameAttr, username, l.config.UserBaseDN)
return l.conn.Bind(l.userDn, password)
}
func (l *ldapConnector) profile() (*ldap.Entry, error) {
searchRequest := ldap.NewSearchRequest(
l.userDn,
ldap.ScopeBaseObject,
ldap.NeverDerefAliases,
0,
0,
false,
"(objectClass=*)",
[]string{"garage_s3_access_key", "garage_s3_secret_key"},
nil)
sr, err := l.conn.Search(searchRequest)
if err != nil {
return nil, err
}
if len(sr.Entries) != 1 {
return nil, errors.New(fmt.Sprintf("Wrong number of LDAP entries, expected 1, got %d", len(sr.Entries)))
}
return sr.Entries[0], nil
}
func (l *ldapConnector) Close() {
if l.conn != nil {
l.conn.Close()
l.conn = nil
}
}
|