From: Brian Warner Date: Mon, 6 Oct 2008 22:51:24 +0000 (-0700) Subject: ftpd: add ftp.accounts checker, remove InMemoryPasswordChecker X-Git-Url: https://git.rkrishnan.org/specifications/install.html?a=commitdiff_plain;h=daf4af6699ec6ea9326f2af0c570e3e037aee980;p=tahoe-lafs%2Ftahoe-lafs.git ftpd: add ftp.accounts checker, remove InMemoryPasswordChecker --- diff --git a/src/allmydata/ftpd.py b/src/allmydata/ftpd.py index e19af52b..0182126e 100644 --- a/src/allmydata/ftpd.py +++ b/src/allmydata/ftpd.py @@ -6,9 +6,7 @@ from twisted.application import service, strports from twisted.internet import defer from twisted.internet.interfaces import IConsumer from twisted.protocols import ftp -from twisted.cred import portal, checkers -from twisted.vfs import pathutils -from twisted.vfs.backends import osfs +from twisted.cred import error, portal, checkers, credentials from twisted.python import log from allmydata.interfaces import IDirectoryNode, ExistingChildError @@ -322,43 +320,67 @@ class Handler: return d -from twisted.vfs.adapters import ftp as vftp # register the IFTPShell adapter -_hush_pyflakes = [vftp] -fs = pathutils.FileSystem(osfs.OSDirectory("/tmp/ftp-test")) +class FTPAvatarID: + def __init__(self, username, rootcap): + self.username = username + self.rootcap = rootcap + +class AccountFileChecker: + implements(checkers.ICredentialsChecker) + credentialInterfaces = (credentials.IUsernamePassword, + credentials.IUsernameHashedPassword) + def __init__(self, client, accountfile): + self.client = client + self.passwords = {} + self.rootcaps = {} + for line in open(os.path.expanduser(accountfile), "r"): + line = line.strip() + if line.startswith("#") or not line: + continue + name, passwd, rootcap = line.split() + self.passwords[name] = passwd + self.rootcaps[name] = rootcap + + def _cbPasswordMatch(self, matched, username): + if matched: + return FTPAvatarID(username, self.rootcaps[username]) + raise error.UnauthorizedLogin + + def requestAvatarId(self, credentials): + if credentials.username in self.passwords: + d = defer.maybeDeferred(credentials.checkPassword, + self.passwords[credentials.username]) + d.addCallback(self._cbPasswordMatch, str(credentials.username)) + return d + return defer.fail(error.UnauthorizedLogin()) class Dispatcher: implements(portal.IRealm) - def __init__(self, client, accounts): + def __init__(self, client): self.client = client - self.accounts = accounts + def requestAvatar(self, avatarID, mind, interface): assert interface == ftp.IFTPShell print "REQUEST", avatarID, mind, interface - pw, rootcap = self.accounts[avatarID] - rootnode = self.client.create_node_from_uri(rootcap) + rootnode = self.client.create_node_from_uri(avatarID.rootcap) convergence = self.client.convergence - s = Handler(self.client, rootnode, avatarID, convergence) + s = Handler(self.client, rootnode, avatarID.username, convergence) return (interface, s, None) class FTPServer(service.MultiService): def __init__(self, client, portstr, accountfile, accounturl): service.MultiService.__init__(self) - self.client = client - assert not accounturl, "Not implemented yet" - assert accountfile, "Need accountfile" - self.accounts = {} - c = checkers.InMemoryUsernamePasswordDatabaseDontUse() - for line in open(os.path.expanduser(accountfile), "r"): - line = line.strip() - if line.startswith("#") or not line: - continue - name, passwd, rootcap = line.split() - self.accounts[name] = (passwd,rootcap) - c.addUser(name, passwd) + if accountfile: + c = AccountFileChecker(self, accountfile) + elif accounturl: + raise NotImplementedError("account URL not yet implemented") + else: + # we could leave this anonymous, with just the /uri/CAP form + raise RuntimeError("must provide some translation") - r = Dispatcher(client, self.accounts) + r = Dispatcher(client) p = portal.Portal(r) p.registerChecker(c) f = ftp.FTPFactory(p)