Skip to content

Commit

Permalink
Merge pull request #1194 from twisted/cred-docs-9724
Browse files Browse the repository at this point in the history
Author: twm

Reviewer: glyph

Fixes: ticket:9724

The documentation of the twisted.cred.checkers module has been extended and corrected.
  • Loading branch information
glyph committed Nov 9, 2019
2 parents 1f9d14b + 359e1d7 commit 9c88278
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 19 deletions.
99 changes: 82 additions & 17 deletions src/twisted/cred/checkers.py
Expand Up @@ -2,6 +2,12 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
Basic credential checkers
@var ANONYMOUS: An empty tuple used to represent the anonymous avatar ID.
"""

from __future__ import division, absolute_import

import os
Expand All @@ -14,24 +20,29 @@
from twisted.cred import error, credentials



class ICredentialsChecker(Interface):
"""
An object that can check sub-interfaces of ICredentials.
An object that can check sub-interfaces of L{ICredentials}.
"""

credentialInterfaces = Attribute(
'A list of sub-interfaces of ICredentials which specifies which I may check.')
credentialInterfaces = Attribute((
'A list of sub-interfaces of L{ICredentials} which specifies which I '
'may check.'
))


def requestAvatarId(credentials):
"""
Validate credentials and produce an avatar ID.
@param credentials: something which implements one of the interfaces in
self.credentialInterfaces.
C{credentialInterfaces}.
@return: a Deferred which will fire a string which identifies an
avatar, an empty tuple to specify an authenticated anonymous user
(provided as checkers.ANONYMOUS) or fire a Failure(UnauthorizedLogin).
Alternatively, return the result itself.
@return: a L{Deferred} which will fire with a L{bytes} that identifies
an avatar, an empty tuple to specify an authenticated anonymous user
(provided as L{twisted.cred.checkers.ANONYMOUS}) or fail with
L{UnauthorizedLogin}. Alternatively, return the result itself.
@see: L{twisted.cred.credentials}
"""
Expand All @@ -51,11 +62,22 @@ def requestAvatarId(credentials):
ANONYMOUS = ()



@implementer(ICredentialsChecker)
class AllowAnonymousAccess:
"""
A credentials checker that unconditionally grants anonymous access.
@cvar credentialInterfaces: Tuple containing L{IAnonymous}.
"""
credentialInterfaces = credentials.IAnonymous,

def requestAvatarId(self, credentials):
"""
Succeed with the L{ANONYMOUS} avatar ID.
@return: L{Deferred} that fires with L{twisted.cred.checkers.ANONYMOUS}
"""
return defer.succeed(ANONYMOUS)


Expand All @@ -71,15 +93,45 @@ class InMemoryUsernamePasswordDatabaseDontUse(object):
You really don't want to use this for anything else. It is, at best, a
toy. If you need a simple credentials checker for a real application,
see L{FilePasswordDB}.
@cvar credentialInterfaces: Tuple of L{IUsernamePassword} and
L{IUsernameHashedPassword}.
@ivar users: Mapping of usernames to passwords.
@type users: L{dict} mapping L{bytes} to L{bytes}
"""
credentialInterfaces = (credentials.IUsernamePassword,
credentials.IUsernameHashedPassword)

def __init__(self, **users):
self.users = {x.encode('ascii'):y for x, y in users.items()}
"""
Initialize the in-memory database.
For example::
db = InMemoryUsernamePasswordDatabaseDontUse(
user1=b'sesame',
user2=b'hunter2',
)
@param users: Usernames and passwords to seed the database with.
Each username given as a keyword is encoded to L{bytes} as ASCII.
Passwords must be given as L{bytes}.
@type users: L{dict} of L{str} to L{bytes}
"""
self.users = {x.encode('ascii'): y for x, y in users.items()}


def addUser(self, username, password):
"""
Set a user's password.
@param username: Name of the user.
@type username: L{bytes}
@param password: Password to associate with the username.
@type password: L{bytes}
"""
self.users[username] = password


Expand Down Expand Up @@ -111,9 +163,9 @@ class FilePasswordDB:
by this string, as does the password. Both fields are specifiable. If
the passwords are not stored plaintext, a hash function must be supplied
to convert plaintext passwords to the form stored on disk and this
CredentialsChecker will only be able to check IUsernamePassword
CredentialsChecker will only be able to check L{IUsernamePassword}
credentials. If the passwords are stored plaintext,
IUsernameHashedPassword credentials will be checkable as well.
L{IUsernameHashedPassword} credentials will be checkable as well.
"""

cache = False
Expand All @@ -124,22 +176,22 @@ class FilePasswordDB:
def __init__(self, filename, delim=b':', usernameField=0, passwordField=1,
caseSensitive=True, hash=None, cache=False):
"""
@type filename: C{str}
@type filename: L{str}
@param filename: The name of the file from which to read username and
password information.
@type delim: C{str}
@type delim: L{bytes}
@param delim: The field delimiter used in the file.
@type usernameField: C{int}
@type usernameField: L{int}
@param usernameField: The index of the username after splitting a
line on the delimiter.
@type passwordField: C{int}
@type passwordField: L{int}
@param passwordField: The index of the password after splitting a
line on the delimiter.
@type caseSensitive: C{bool}
@type caseSensitive: L{bool}
@param caseSensitive: If true, consider the case of the username when
performing a lookup. Ignore it otherwise.
Expand All @@ -151,7 +203,7 @@ def __init__(self, filename, delim=b':', usernameField=0, passwordField=1,
version of the password. If the return value compares equal to the
version stored on disk, the credentials are accepted.
@type cache: C{bool}
@type cache: L{bool}
@param cache: If true, maintain an in-memory cache of the
contents of the password file. On lookups, the mtime of the
file will be checked, and the file will only be re-parsed if
Expand Down Expand Up @@ -225,6 +277,19 @@ def _loadCredentials(self):


def getUser(self, username):
"""
Look up the credentials for a username.
@param username: The username to look up.
@type username: L{bytes}
@returns: Two-tuple of the canonicalicalized username (i.e. lowercase
if the database is not case sensitive) and the associated password
value, both L{bytes}.
@rtype: L{tuple}
@raises KeyError: When lookup of the username fails.
"""
if not self.caseSensitive:
username = username.lower()

Expand Down
4 changes: 3 additions & 1 deletion src/twisted/cred/credentials.py
Expand Up @@ -33,7 +33,7 @@ class ICredentials(Interface):
"""
I check credentials.
Implementors _must_ specify which sub-interfaces of ICredentials
Implementors I{must} specify the sub-interfaces of ICredentials
to which it conforms, using L{zope.interface.declarations.implementer}.
"""

Expand Down Expand Up @@ -121,6 +121,8 @@ def checkPassword(password):
class IAnonymous(ICredentials):
"""
I am an explicitly anonymous request for access.
@see: L{twisted.cred.checkers.AllowAnonymousAccess}
"""


Expand Down
2 changes: 1 addition & 1 deletion src/twisted/internet/testing.py
Expand Up @@ -635,7 +635,7 @@ def adoptDatagramPort(self, fileno, addressFamily, protocol,
maxPacketSize=8192):
"""
Fake L{IReactorSocket.adoptDatagramPort}, that logs the call and
returns a fake L{IListeningPort}.
returns a fake L{IListeningPort}.
@see: L{twisted.internet.interfaces.IReactorSocket.adoptDatagramPort}
"""
Expand Down
1 change: 1 addition & 0 deletions src/twisted/newsfragments/9724.doc
@@ -0,0 +1 @@
The documentation of the twisted.cred.checkers module has been extended and corrected.

0 comments on commit 9c88278

Please sign in to comment.