def createMergedServer(self, *responses): """ Create an MergedLDAP server for testing. Initialize with len(responses) clients. :param responses: The responses to initialize the `LDAPClientTestDrives`. :type responses: args of lists of lists :return a deferred, fires when server finished connecting """ def createClient(factory): factory.doStart() proto = factory.buildProtocol(addr=None) proto.connectionMade() clients = [] for r in responses: clients.append(testutil.LDAPClientTestDriver(*r)) conf = config.LDAPConfig(serviceLocationOverrides={"": createClient}) server = MergedLDAPServer([conf for _ in clients], [False for _ in clients]) self.clients = clients * 1 server.protocol = lambda: clients.pop() server.transport = proto_helpers.StringTransport() server.connectionMade() d = server._whenConnected(lambda: server) return d
def createServer(proto, *responses, **kw): """ Create an LDAP server for testing. :param proto: The server protocol factory (e.g. `ProxyBase`). :param responses: The responses to initialize the `LDAPClientTestDrive`. :param proto_args: Optional mapping passed as keyword args to protocol factory. """ if "proto_args" in kw: proto_args = kw["proto_args"] del kw["proto_args"] else: proto_args = {} def createClient(factory): factory.doStart() proto = factory.buildProtocol(addr=None) proto.connectionMade() overrides = kw.setdefault("serviceLocationOverrides", {}) overrides.setdefault("", createClient) conf = config.LDAPConfig(**kw) server = proto(conf, **proto_args) clientTestDriver = LDAPClientTestDriver(*responses) server.protocol = lambda: clientTestDriver server.clientTestDriver = clientTestDriver server.transport = proto_helpers.StringTransport() server.connectionMade() return server
def testGetIdentitySearchNoSection(self): """ When the configuration file does not contains the `authentication` section it will use a default expression. """ sut = config.LDAPConfig() result = sut.getIdentitySearch("foo") self.assertEqual("(|(cn=foo)(uid=foo))", result)
def testgetIdentitySearchFromInitArguments(self): """ When data is provided at LDAPConfig initialization it is used as the backend data. """ sut = config.LDAPConfig(identitySearch="(&(bar=thud)(quux=%(name)s))") result = sut.getIdentitySearch("foo") self.assertEqual("(&(bar=thud)(quux=foo))", result)
def console_script(): try: opts = MyOptions() opts.parseOptions() except usage.UsageError as ue: sys.stderr.write("{}: {}\n".format(sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig(baseDN=opts["base"], serviceLocationOverrides=opts["service-location"]) main(cfg, opts["filter"])
def testGetIdentityBaseSectionSection(self): """ When the configuration does not contains the `[authentication]` section it will return the configured Base DN. """ reloadFromContent(self, b"[ldap]\n" b"basE=dc=test,dc=net\n") sut = config.LDAPConfig() result = sut.getIdentityBaseDN() self.assertEqual("dc=test,dc=net", result)
def testGetBaseDNOK(self): """ It will return the base DN found in the configuration in the [ldap] section as `base` option. """ reloadFromContent(self, b"[ldap]\nbase=dc=test,dc=net\n") sut = config.LDAPConfig() result = sut.getBaseDN() self.assertEqual("dc=test,dc=net", result)
def setUp(self): self.dir = self.mktemp() os.mkdir(self.dir) self.f1 = os.path.join(self.dir, 'one.cfg') writeFile( self.f1, """\ [authentication] identity-search = (something=%(name)s) """) self.cfg = config.loadConfig(configFiles=[self.f1], reload=True) self.config = config.LDAPConfig()
def testGetBaseDNNoSection(self): """ It raise an exception when the the configuration has no [ldap] section. """ reloadFromContent(self, b"[other]\nbase=dc=test,dc=net\n") sut = config.LDAPConfig() self.assertRaises( config.MissingBaseDNError, sut.getBaseDN, )
def testGetIdentitySearchNoOption(self): """ When the configuration file contains the `authentication` section but without the identity search option, it will use a default expression. """ reloadFromContent(self, b"[authentication]\nother_key=value") sut = config.LDAPConfig() result = sut.getIdentitySearch("foo") self.assertEqual("(|(cn=foo)(uid=foo))", result)
def testGetBaseDNNoOption(self): """ It raise an exception when the the configuration has [ldap] section but no `base` option. """ reloadFromContent(self, b'[ldap]\nbaseless=dc=test,dc=net\n') sut = config.LDAPConfig() self.assertRaises( config.MissingBaseDNError, sut.getBaseDN, )
def testCopy(self): """ It returns a copy of the configuration. """ sut = config.LDAPConfig() copied = sut.copy(identitySearch="(&(bar=baz)(quux=%(name)s))") self.assertIsInstance(copied, config.LDAPConfig) result = copied.getIdentitySearch("foo") self.assertEqual("(&(bar=baz)(quux=foo))", result)
def testGetIdentityBaseDNOK(self): """ It will return the value found in the configuration in the [authentication] section as `identity-base` option. """ reloadFromContent( self, b"[authentication]\n" b"identity-base=ou=users,dc=test,dc=net\n") sut = config.LDAPConfig() result = sut.getIdentityBaseDN() self.assertEqual("ou=users,dc=test,dc=net", result)
def testGetIdentitySearchOK(self): """ It will use the value from to configuration for its return value. """ reloadFromContent( self, """[authentication] identity-search = (something=%(name)s) """) sut = config.LDAPConfig() result = sut.getIdentitySearch('foo') self.assertEqual('(something=foo)', result)
def console_script(): from twisted.python import log log.startLogging(sys.stderr, setStdout=0) try: opts = MyOptions() opts.parseOptions() except usage.UsageError as ue: sys.stderr.write("{}: {}\n".format(sys.argv[0], ue)) sys.exit(1) cfg = config.LDAPConfig(baseDN=opts["base"], serviceLocationOverrides=opts["service-location"]) main(cfg)
def createServer(proto, *responses, **kw): def createClient(factory): factory.doStart() #TODO factory.startedConnecting(c) proto = factory.buildProtocol(addr=None) proto.connectionMade() cfg = config.loadConfig(configFiles=[], reload=True) overrides = kw.setdefault('serviceLocationOverrides', {}) overrides.setdefault('', createClient) conf = config.LDAPConfig(**kw) server = proto(conf) server.protocol = lambda: LDAPClientTestDriver(*responses) server.transport = proto_helpers.StringTransport() server.connectionMade() return server
def test_cannot_connect_to_proxied_server_no_pending_requests(self): """ When making a request and the proxy cannot connect to the proxied server, the connection is terminated. """ conf = config.LDAPConfig( serviceLocationOverrides={'': ('localhost', 8080)}) server = proxybase.ProxyBase(conf, reactor_=Clock()) server.transport = proto_helpers.StringTransport() server.clientCreator = makeClientCreatorFactory(0) server.connectionMade() server.clientCreator = WontConnectClientCreator server.dataReceived( str(pureldap.LDAPMessage(pureldap.LDAPBindRequest(), id=4))) server.reactor.advance(1) self.assertEquals(server.transport.value(), "")
def testGetIdentityBaseNoOption(self): """ When the configuration does not contains the `identity-base` option inside the `[authentication]` section it will return the configured Base DN. """ reloadFromContent( self, b'[ldap]\n' b'BASE=dc=test,dc=net\n' b'[authentication]\n' b'no-identity-base=dont care\n') sut = config.LDAPConfig() result = sut.getIdentityBaseDN() self.assertEqual('dc=test,dc=net', result)
def test_cannot_connect_to_proxied_server_pending_requests(self): """ When making a request and the proxy cannot connect to the proxied server, pending BIND and startTLS requests are replied to and the connection is closed. """ conf = config.LDAPConfig( serviceLocationOverrides={'': ('localhost', 8080)}) server = proxybase.ProxyBase(conf, reactor_=Clock()) server.transport = proto_helpers.StringTransport() server.clientCreator = makeClientCreatorFactory(2) server.connectionMade() server.dataReceived( str(pureldap.LDAPMessage(pureldap.LDAPBindRequest(), id=4))) server.reactor.advance(2) self.assertEquals( server.transport.value(), str( pureldap.LDAPMessage(pureldap.LDAPBindResponse(resultCode=52), id=4)))
def http_cred_checker(self): # Check whether we've been configured with the attribute forming the # relative DN for identity entries within the directory and use the # appropriate LDAP credential checker object. rdn = self.vars.get("identityrdn") if rdn: return LDAPAuthedBindingChecker( LDAPAuthedConfig(','.join(self.vars.get("basedn")), { ','.join(self.vars.get("basedn")): (self.vars.get("ldaphost"), int(self.vars.get("ldapport", 389))) }, identityRelativeDN=rdn)) else: return checkers.LDAPBindingChecker( config.LDAPConfig( ','.join(self.vars.get("basedn")), { ','.join(self.vars.get("basedn")): (self.vars.get("ldaphost"), int(self.vars.get("ldapport", 389))) }))
def console_script(): try: opts = MyOptions() opts.parseOptions() except usage.UsageError as ue: sys.stderr.write("{}: {}\n".format(sys.argv[0], ue)) sys.exit(1) from twisted.python import log log.startLogging(sys.stderr, setStdout=0) cfg = config.LDAPConfig(serviceLocationOverrides=opts["service-location"]) bindPassword = None if opts["bind-auth-fd"]: f = os.fdopen(opts["bind-auth-fd"]) bindPassword = f.readline() assert bindPassword[-1] == "\n" bindPassword = bindPassword[:-1] f.close() main(cfg, opts["from"], opts["to"], opts["binddn"], bindPassword)
def getObjshPortal(protocol_class, acl_options): realm = ObjshRealm(protocol_class) portal = cred_portal.Portal(realm) # ssh client's public key sshclient_pubkey_folders = acl_options.get('client_publickeys') if sshclient_pubkey_folders: del acl_options['client_publickeys'] client_keydb = {} loaded_folders = [] for sshclient_pubkey_folder in sshclient_pubkey_folders: if sshclient_pubkey_folder in loaded_folders: continue if not os.path.exists(sshclient_pubkey_folder): continue loaded_folders.append(sshclient_pubkey_folder) for file in os.listdir(sshclient_pubkey_folder): username, ext = os.path.splitext(file) if username.startswith('.') or ext != '.pub': continue try: client_keydb[username].append( ssh_keys.Key.fromFile( os.path.join(sshclient_pubkey_folder, file))) log.msg('load key-based ssh user ' + username) except KeyError: try: client_keydb[username] = [ ssh_keys.Key.fromFile( os.path.join(sshclient_pubkey_folder, file)) ] log.msg('load key-based ssh user ' + username) except ssh_keys.BadKeyError: log.msg('Warning! failed to load sshkey of ' + username) sshDB = SSHPublicKeyChecker(InMemorySSHKeyDB(client_keydb)) # this should register to portal to make it work portal.registerChecker(sshDB) # username, password based authentication cascading_checker = CascadingChecker() for acl_name, acl_settings in acl_options.items(): if acl_settings['kind'] == 'LDAP': if LDAPBindingChecker is None: log.msg( 'Warning: package ldaptor is not installed, LDAP authentication ignored' ) continue #basedn = 'dc=example,dc=com' basedn = acl_settings['basedn'] query = '(%s=%%(name)s)' % acl_settings['uid_attrname'] cfg = ldaptor_config.LDAPConfig(basedn, { basedn: (acl_settings['host'], acl_settings.get('port', 389)) }, None, query) checker = LDAPBindingChecker(acl_settings['uid_attrname'], cfg) cascading_checker.registerChecker(checker) elif acl_settings['kind'] == 'PAM': # system accounts cascading_checker.registerChecker( PamPasswordDatabase(acl_settings['usernames'], acl_settings['username_prefix'])) elif acl_settings['kind'] == 'IN_MEMORY_ACCOUNTS': # in memory accounts passwdDB = InMemoryUsernamePasswordDatabaseDontUse() for username, password in acl_settings.get('accounts', {}).items(): username = username.strip() # do not allow empty username password = password.strip() # do not allow empty password if password and username: passwdDB.addUser(username, password) log.msg('Warning! in memory account: %s' % (username)) cascading_checker.registerChecker(passwdDB) else: raise ValueError('Acl kind of %s is unknown' % acl_name) portal.registerChecker(cascading_checker) return portal
reply) return d if __name__ == "__main__": """ Demonstration LDAP proxy; passes all requests to localhost:389. """ from twisted.internet import reactor, protocol from twisted.python import log import sys log.startLogging(sys.stderr) from ldaptor import config factory = protocol.ServerFactory() cfg = config.LDAPConfig(serviceLocationOverrides={ "": ("localhost", 389), }) factory.protocol = lambda: ServiceBindingProxy( config=cfg, services=[ "svc1", "svc2", "svc3", ], fallback=True, ) reactor.listenTCP(10389, factory) reactor.run()
def testInitArg(self): conf = config.LDAPConfig(identitySearch='(&(bar=thud)(quux=%(name)s))') self.assertEquals(conf.getIdentitySearch('foo'), '(&(bar=thud)(quux=foo))')
d = self._whenConnected(self._startSearch, request, controls, reply) return d if __name__ == '__main__': """ Demonstration LDAP proxy; passes all requests to localhost:389. """ from twisted.internet import reactor, protocol from twisted.python import log import sys log.startLogging(sys.stderr) from ldaptor import config factory = protocol.ServerFactory() cfg = config.LDAPConfig(serviceLocationOverrides={ '': ('localhost', 389), }) factory.protocol = lambda : ServiceBindingProxy(config=cfg, services=[ 'svc1', 'svc2', 'svc3', ], fallback=True, ) reactor.listenTCP(10389, factory) reactor.run()