diff --git a/docs/frontends/FTP-and-SFTP.rst b/docs/frontends/FTP-and-SFTP.rst index ee6371812..4c87b0bc4 100644 --- a/docs/frontends/FTP-and-SFTP.rst +++ b/docs/frontends/FTP-and-SFTP.rst @@ -78,33 +78,6 @@ start with "ssh-". Now add an ``accounts.file`` directive to your ``tahoe.cfg`` file, as described in the next sections. -Running An Account Server (accounts.url) -======================================== - -The accounts.url directive allows access requests to be controlled by an -HTTP-based login service, useful for centralized deployments. This was used -by AllMyData to provide web-based file access, where the service used a -simple PHP script and database lookups to map an account email address and -password to a Tahoe-LAFS directory cap. The service will receive a -multipart/form-data POST, just like one created with a
and -fields, with three parameters: - -• action: "authenticate" (this is a static string) -• email: USERNAME (Tahoe-LAFS has no notion of email addresses, but the - authentication service uses them as account names, so the interface - presents this argument as "email" rather than "username"). -• passwd: PASSWORD - -It should return a single string that either contains a Tahoe-LAFS directory -cap (URI:DIR2:...), or "0" to indicate a login failure. - -Tahoe-LAFS recommends the service be secure, preferably localhost-only. This -makes it harder for attackers to brute force the password or use DNS -poisoning to cause the Tahoe-LAFS gateway to talk with the wrong server, -thereby revealing the usernames and passwords. - -Public key authentication is not supported when an account server is used. - Configuring SFTP Access ======================= diff --git a/newsfragments/3652.removed b/newsfragments/3652.removed new file mode 100644 index 000000000..a3e964702 --- /dev/null +++ b/newsfragments/3652.removed @@ -0,0 +1 @@ +Removed support for the Account Server frontend authentication type. diff --git a/src/allmydata/client.py b/src/allmydata/client.py index 3bf976fe5..a6c45643f 100644 --- a/src/allmydata/client.py +++ b/src/allmydata/client.py @@ -116,7 +116,6 @@ _client_config = configutil.ValidConfiguration( ), "sftpd": ( "accounts.file", - "accounts.url", "enabled", "host_privkey_file", "host_pubkey_file", @@ -1042,13 +1041,12 @@ class _Client(node.Node, pollmixin.PollMixin): accountfile = self.config.get_config("sftpd", "accounts.file", None) if accountfile: accountfile = self.config.get_config_path(accountfile) - accounturl = self.config.get_config("sftpd", "accounts.url", None) sftp_portstr = self.config.get_config("sftpd", "port", "tcp:8022") pubkey_file = self.config.get_config("sftpd", "host_pubkey_file") privkey_file = self.config.get_config("sftpd", "host_privkey_file") from allmydata.frontends import sftpd - s = sftpd.SFTPServer(self, accountfile, accounturl, + s = sftpd.SFTPServer(self, accountfile, sftp_portstr, pubkey_file, privkey_file) s.setServiceParent(self) diff --git a/src/allmydata/frontends/auth.py b/src/allmydata/frontends/auth.py index 7f81572fe..f2ac99b8f 100644 --- a/src/allmydata/frontends/auth.py +++ b/src/allmydata/frontends/auth.py @@ -1,14 +1,10 @@ -import os - from zope.interface import implementer -from twisted.web.client import getPage from twisted.internet import defer from twisted.cred import error, checkers, credentials from twisted.conch.ssh import keys from twisted.conch.checkers import SSHPublicKeyChecker, InMemorySSHKeyDB from allmydata.util.dictutil import BytesKeyDict -from allmydata.util import base32 from allmydata.util.fileutil import abspath_expanduser_unicode @@ -86,54 +82,3 @@ class AccountFileChecker(object): d = defer.maybeDeferred(creds.checkPassword, correct) d.addCallback(self._cbPasswordMatch, str(creds.username)) return d - - -@implementer(checkers.ICredentialsChecker) -class AccountURLChecker(object): - credentialInterfaces = (credentials.IUsernamePassword,) - - def __init__(self, client, auth_url): - self.client = client - self.auth_url = auth_url - - def _cbPasswordMatch(self, rootcap, username): - return FTPAvatarID(username, rootcap) - - def post_form(self, username, password): - sepbase = base32.b2a(os.urandom(4)) - sep = "--" + sepbase - form = [] - form.append(sep) - fields = {"action": "authenticate", - "email": username, - "passwd": password, - } - for name, value in fields.iteritems(): - form.append('Content-Disposition: form-data; name="%s"' % name) - form.append('') - assert isinstance(value, str) - form.append(value) - form.append(sep) - form[-1] += "--" - body = "\r\n".join(form) + "\r\n" - headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase, - } - return getPage(self.auth_url, method="POST", - postdata=body, headers=headers, - followRedirect=True, timeout=30) - - def _parse_response(self, res): - rootcap = res.strip() - if rootcap == "0": - raise error.UnauthorizedLogin - return rootcap - - def requestAvatarId(self, credentials): - # construct a POST to the login form. While this could theoretically - # be done with something like the stdlib 'email' package, I can't - # figure out how, so we just slam together a form manually. - d = self.post_form(credentials.username, credentials.password) - d.addCallback(self._parse_response) - d.addCallback(self._cbPasswordMatch, str(credentials.username)) - return d - diff --git a/src/allmydata/frontends/sftpd.py b/src/allmydata/frontends/sftpd.py index bc7196de6..17eca993e 100644 --- a/src/allmydata/frontends/sftpd.py +++ b/src/allmydata/frontends/sftpd.py @@ -1983,7 +1983,7 @@ class ShellSession(PrefixingLogMixin): components.registerAdapter(ShellSession, SFTPUserHandler, ISession) -from allmydata.frontends.auth import AccountURLChecker, AccountFileChecker, NeedRootcapLookupScheme +from allmydata.frontends.auth import AccountFileChecker, NeedRootcapLookupScheme @implementer(portal.IRealm) class Dispatcher(object): @@ -2000,7 +2000,7 @@ class Dispatcher(object): class SFTPServer(service.MultiService): name = "frontend:sftp" - def __init__(self, client, accountfile, accounturl, + def __init__(self, client, accountfile, sftp_portstr, pubkey_file, privkey_file): precondition(isinstance(accountfile, (str, type(None))), accountfile) precondition(isinstance(pubkey_file, str), pubkey_file) @@ -2013,12 +2013,9 @@ class SFTPServer(service.MultiService): if accountfile: c = AccountFileChecker(self, accountfile) p.registerChecker(c) - if accounturl: - c = AccountURLChecker(self, accounturl) - p.registerChecker(c) - if not accountfile and not accounturl: + if not accountfile: # we could leave this anonymous, with just the /uri/CAP form - raise NeedRootcapLookupScheme("must provide an account file or URL") + raise NeedRootcapLookupScheme("must provide an account file") pubkey = keys.Key.fromFile(pubkey_file.encode(get_filesystem_encoding())) privkey = keys.Key.fromFile(privkey_file.encode(get_filesystem_encoding()))