source: trunk/src/allmydata/frontends/auth.py

Last change on this file was 1cfe843d, checked in by Alexandre Detiste <alexandre.detiste@…>, at 2024-02-22T23:40:25Z

more python2 removal

  • Property mode set to 100644
File size: 3.6 KB
Line 
1"""
2Authentication for frontends.
3"""
4
5from zope.interface import implementer
6from twisted.internet import defer
7from twisted.cred import checkers, credentials
8from twisted.conch.ssh import keys
9from twisted.conch.checkers import SSHPublicKeyChecker, InMemorySSHKeyDB
10
11from allmydata.util.dictutil import BytesKeyDict
12from allmydata.util.fileutil import abspath_expanduser_unicode
13
14
15class NeedRootcapLookupScheme(Exception):
16    """Accountname+Password-based access schemes require some kind of
17    mechanism to translate name+passwd pairs into a rootcap, either a file of
18    name/passwd/rootcap tuples, or a server to do the translation."""
19
20class FTPAvatarID(object):
21    def __init__(self, username, rootcap):
22        self.username = username
23        self.rootcap = rootcap
24
25@implementer(checkers.ICredentialsChecker)
26class AccountFileChecker(object):
27    credentialInterfaces = (credentials.ISSHPrivateKey,)
28
29    def __init__(self, client, accountfile):
30        self.client = client
31        path = abspath_expanduser_unicode(accountfile)
32        with open_account_file(path) as f:
33            self.rootcaps, pubkeys = load_account_file(f)
34        self._pubkeychecker = SSHPublicKeyChecker(InMemorySSHKeyDB(pubkeys))
35
36    def _avatarId(self, username):
37        return FTPAvatarID(username, self.rootcaps[username])
38
39    def requestAvatarId(self, creds):
40        if credentials.ISSHPrivateKey.providedBy(creds):
41            d = defer.maybeDeferred(self._pubkeychecker.requestAvatarId, creds)
42            d.addCallback(self._avatarId)
43            return d
44        raise NotImplementedError()
45
46def open_account_file(path):
47    """
48    Open and return the accounts file at the given path.
49    """
50    return open(path, "rt", encoding="utf-8")
51
52def load_account_file(lines):
53    """
54    Load credentials from an account file.
55
56    :param lines: An iterable of account lines to load.
57
58    :return: See ``create_account_maps``.
59    """
60    return create_account_maps(
61        parse_accounts(
62            content_lines(
63                lines,
64            ),
65        ),
66    )
67
68def content_lines(lines):
69    """
70    Drop empty and commented-out lines (``#``-prefixed) from an iterator of
71    lines.
72
73    :param lines: An iterator of lines to process.
74
75    :return: An iterator of lines including only those from ``lines`` that
76        include content intended to be loaded.
77    """
78    for line in lines:
79        line = line.strip()
80        if line and not line.startswith("#"):
81            yield line
82
83def parse_accounts(lines):
84    """
85    Parse account lines into their components (name, key, rootcap).
86    """
87    for line in lines:
88        name, passwd, rest = line.split(None, 2)
89        if not passwd.startswith("ssh-"):
90            raise ValueError(
91                "Password-based authentication is not supported; "
92                "configure key-based authentication instead."
93            )
94
95        bits = rest.split()
96        keystring = " ".join([passwd] + bits[:-1])
97        key = keys.Key.fromString(keystring)
98        rootcap = bits[-1]
99        yield (name, key, rootcap)
100
101def create_account_maps(accounts):
102    """
103    Build mappings from account names to keys and rootcaps.
104
105    :param accounts: An iterator if (name, key, rootcap) tuples.
106
107    :return: A tuple of two dicts.  The first maps account names to rootcaps.
108        The second maps account names to public keys.
109    """
110    rootcaps = BytesKeyDict()
111    pubkeys = BytesKeyDict()
112    for (name, key, rootcap) in accounts:
113        name_bytes = name.encode("utf-8")
114        rootcaps[name_bytes] = rootcap.encode("utf-8")
115        pubkeys[name_bytes] = [key]
116    return rootcaps, pubkeys
Note: See TracBrowser for help on using the repository browser.