Package ldaptor :: Package protocols :: Package ldap :: Module svcbindproxy
[hide private]
[frames] | no frames]

Source Code for Module ldaptor.protocols.ldap.svcbindproxy

  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   
6 -class ServiceBindingProxy(proxy.Proxy):
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
52 - def _startSearch(self, request, controls, reply):
53 services = list(self.services) 54 baseDN = self.config.getIdentityBaseDN() 55 e = ldapsyntax.LDAPEntryWithClient(client=self.client, 56 dn=baseDN) 57 d = self._tryService(services, e, request, controls, reply) 58 d.addCallback(self._maybeFallback, request, controls, reply) 59 return d
60
61 - def _maybeFallback(self, entry, request, controls, reply):
62 if entry is not None: 63 msg = pureldap.LDAPBindResponse( 64 resultCode=ldaperrors.Success.resultCode, 65 matchedDN=request.dn) 66 return msg 67 elif self.fallback: 68 self.handleUnknown(request, controls, reply) 69 else: 70 msg = pureldap.LDAPBindResponse( 71 resultCode=ldaperrors.LDAPInvalidCredentials.resultCode) 72 return msg
73
74 - def timestamp(self):
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 # no time 94 pureldap.LDAPFilter_not(pureldap.LDAPFilter_present('validFrom')), 95 # or already valid 96 pureldap.LDAPFilter_lessOrEqual(attributeDesc=pureldap.LDAPAttributeDescription('validFrom'), 97 assertionValue=pureldap.LDAPAssertionValue(timestamp)), 98 ]), 99 100 pureldap.LDAPFilter_or([ 101 # no time 102 pureldap.LDAPFilter_not(pureldap.LDAPFilter_present('validUntil')), 103 # or still valid 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 #TODO 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
128 - def _loopIfNone(self, r, *a, **kw):
129 if r is None: 130 d = self._tryService(*a, **kw) 131 return d 132 else: 133 return r
134
135 - def _loopIfBindError(self, fail, *a, **kw):
136 fail.trap(ldaperrors.LDAPInvalidCredentials) 137 d = self._tryService(*a, **kw) 138 return d
139 140 fail_LDAPBindRequest = pureldap.LDAPBindResponse 141
142 - def handle_LDAPBindRequest(self, request, controls, reply):
143 if request.version != 3: 144 raise ldaperrors.LDAPProtocolError, \ 145 'Version %u not supported' % request.version 146 147 self.checkControls(controls) 148 149 if request.dn == '': 150 # anonymous bind 151 return self.handleUnknown(request, controls, reply) 152 else: 153 d = self._whenConnected(self._startSearch, 154 request, controls, reply) 155 return d
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