1 from ldaptor.protocols.ldap import proxy
2 from ldaptor.protocols.ldap import ldapsyntax, ldaperrors
3 from ldaptor.protocols import pureldap
4 import datetime
5
7 """
8 An LDAP proxy that handles non-anonymous bind requests specially.
9
10 BindRequests are intercepted and authentication is attempted
11 against each configured service. This authentication is performed
12 against a separate LDAP entry, found by searching for entries with
13
14 - objectClass: serviceSecurityObject
15
16 - owner: the DN of the original bind attempt
17
18 - cn: the service name.
19
20 starting at the identity-base as configured in the config file.
21
22 Finally, if the authentication does not succeed against any of the
23 configured services, the proxy can fallback to passing the bind
24 request to the real server.
25 """
26
27 services = []
28
29 fallback = False
30
31 - def __init__(self,
32 services=None,
33 fallback=None,
34 *a,
35 **kw):
36 """
37 Initialize the object.
38
39 @param services: List of service names to try to bind against.
40
41 @param fallback: If none of the attempts to authenticate
42 against a specific service succeeded, whether to fall back to
43 the normal LDAP bind mechanism.
44 """
45
46 proxy.Proxy.__init__(self, *a, **kw)
47 if services is not None:
48 self.services = list(services)
49 if fallback is not None:
50 self.fallback = fallback
51
60
73
75 now = datetime.datetime.now()
76 return now.strftime('%Y%m%d%H%M%SZ')
77
78 - def _tryService(self, services, baseEntry, request, controls, reply):
79 try:
80 serviceName = services.pop(0)
81 except IndexError:
82 return None
83 timestamp = self.timestamp()
84 d = baseEntry.search(filterObject=pureldap.LDAPFilter_and([
85 pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('objectClass'),
86 assertionValue=pureldap.LDAPAssertionValue('serviceSecurityObject')),
87 pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('owner'),
88 assertionValue=pureldap.LDAPAssertionValue(request.dn)),
89 pureldap.LDAPFilter_equalityMatch(attributeDesc=pureldap.LDAPAttributeDescription('cn'),
90 assertionValue=pureldap.LDAPAssertionValue(serviceName)),
91
92 pureldap.LDAPFilter_or([
93
94 pureldap.LDAPFilter_not(pureldap.LDAPFilter_present('validFrom')),
95
96 pureldap.LDAPFilter_lessOrEqual(attributeDesc=pureldap.LDAPAttributeDescription('validFrom'),
97 assertionValue=pureldap.LDAPAssertionValue(timestamp)),
98 ]),
99
100 pureldap.LDAPFilter_or([
101
102 pureldap.LDAPFilter_not(pureldap.LDAPFilter_present('validUntil')),
103
104 pureldap.LDAPFilter_greaterOrEqual(attributeDesc=pureldap.LDAPAttributeDescription('validUntil'),
105 assertionValue=pureldap.LDAPAssertionValue(timestamp)),
106 ]),
107
108 ]),
109 attributes=('1.1',))
110
111 def _gotEntries(entries):
112 if not entries:
113 return None
114 assert len(entries)==1
115 e = entries[0]
116 d = e.bind(request.auth)
117 return d
118 d.addCallback(_gotEntries)
119 d.addCallbacks(
120 callback=self._loopIfNone,
121 callbackArgs=(services, baseEntry,
122 request, controls, reply),
123 errback=self._loopIfBindError,
124 errbackArgs=(services, baseEntry,
125 request, controls, reply))
126 return d
127
129 if r is None:
130 d = self._tryService(*a, **kw)
131 return d
132 else:
133 return r
134
139
140 fail_LDAPBindRequest = pureldap.LDAPBindResponse
141
156
157
158 if __name__ == '__main__':
159 """
160 Demonstration LDAP proxy; passes all requests to localhost:389.
161 """
162 from twisted.internet import reactor, protocol
163 from twisted.python import log
164 import sys
165 log.startLogging(sys.stderr)
166 from ldaptor import config
167
168 factory = protocol.ServerFactory()
169 cfg = config.LDAPConfig(serviceLocationOverrides={
170 '': ('localhost', 389),
171 })
172 factory.protocol = lambda : ServiceBindingProxy(config=cfg,
173 services=[
174 'svc1',
175 'svc2',
176 'svc3',
177 ],
178 fallback=True,
179 )
180 reactor.listenTCP(10389, factory)
181 reactor.run()
182