123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- # -*- test-case-name: twisted.cred.test.test_strcred -*-
- #
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- #
-
- """
- Support for resolving command-line strings that represent different
- checkers available to cred.
-
- Examples:
- - passwd:/etc/passwd
- - memory:admin:asdf:user:lkj
- - unix
- """
-
-
- import sys
- from typing import Optional, Sequence, Type
-
- from zope.interface import Attribute, Interface
-
- from twisted.plugin import getPlugins
- from twisted.python import usage
-
-
- class ICheckerFactory(Interface):
- """
- A factory for objects which provide
- L{twisted.cred.checkers.ICredentialsChecker}.
-
- It's implemented by twistd plugins creating checkers.
- """
-
- authType = Attribute("A tag that identifies the authentication method.")
-
- authHelp = Attribute(
- "A detailed (potentially multi-line) description of precisely "
- "what functionality this CheckerFactory provides."
- )
-
- argStringFormat = Attribute(
- "A short (one-line) description of the argument string format."
- )
-
- credentialInterfaces = Attribute(
- "A list of credentials interfaces that this factory will support."
- )
-
- def generateChecker(argstring):
- """
- Return an L{twisted.cred.checkers.ICredentialsChecker} provider using the supplied
- argument string.
- """
-
-
- class StrcredException(Exception):
- """
- Base exception class for strcred.
- """
-
-
- class InvalidAuthType(StrcredException):
- """
- Raised when a user provides an invalid identifier for the
- authentication plugin (known as the authType).
- """
-
-
- class InvalidAuthArgumentString(StrcredException):
- """
- Raised by an authentication plugin when the argument string
- provided is formatted incorrectly.
- """
-
-
- class UnsupportedInterfaces(StrcredException):
- """
- Raised when an application is given a checker to use that does not
- provide any of the application's supported credentials interfaces.
- """
-
-
- # This will be used to warn the users whenever they view help for an
- # authType that is not supported by the application.
- notSupportedWarning = "WARNING: This authType is not supported by " "this application."
-
-
- def findCheckerFactories():
- """
- Find all objects that implement L{ICheckerFactory}.
- """
- return getPlugins(ICheckerFactory)
-
-
- def findCheckerFactory(authType):
- """
- Find the first checker factory that supports the given authType.
- """
- for factory in findCheckerFactories():
- if factory.authType == authType:
- return factory
- raise InvalidAuthType(authType)
-
-
- def makeChecker(description):
- """
- Returns an L{twisted.cred.checkers.ICredentialsChecker} based on the
- contents of a descriptive string. Similar to
- L{twisted.application.strports}.
- """
- if ":" in description:
- authType, argstring = description.split(":", 1)
- else:
- authType = description
- argstring = ""
- return findCheckerFactory(authType).generateChecker(argstring)
-
-
- class AuthOptionMixin:
- """
- Defines helper methods that can be added on to any
- L{usage.Options} subclass that needs authentication.
-
- This mixin implements three new options methods:
-
- The opt_auth method (--auth) will write two new values to the
- 'self' dictionary: C{credInterfaces} (a dict of lists) and
- C{credCheckers} (a list).
-
- The opt_help_auth method (--help-auth) will search for all
- available checker plugins and list them for the user; it will exit
- when finished.
-
- The opt_help_auth_type method (--help-auth-type) will display
- detailed help for a particular checker plugin.
-
- @cvar supportedInterfaces: An iterable object that returns
- credential interfaces which this application is able to support.
-
- @cvar authOutput: A writeable object to which this options class
- will send all help-related output. Default: L{sys.stdout}
- """
-
- supportedInterfaces: Optional[Sequence[Type[Interface]]] = None
- authOutput = sys.stdout
-
- def supportsInterface(self, interface):
- """
- Returns whether a particular credentials interface is supported.
- """
- return self.supportedInterfaces is None or interface in self.supportedInterfaces
-
- def supportsCheckerFactory(self, factory):
- """
- Returns whether a checker factory will provide at least one of
- the credentials interfaces that we care about.
- """
- for interface in factory.credentialInterfaces:
- if self.supportsInterface(interface):
- return True
- return False
-
- def addChecker(self, checker):
- """
- Supply a supplied credentials checker to the Options class.
- """
- # First figure out which interfaces we're willing to support.
- supported = []
- if self.supportedInterfaces is None:
- supported = checker.credentialInterfaces
- else:
- for interface in checker.credentialInterfaces:
- if self.supportsInterface(interface):
- supported.append(interface)
- if not supported:
- raise UnsupportedInterfaces(checker.credentialInterfaces)
- # If we get this far, then we know we can use this checker.
- if "credInterfaces" not in self:
- self["credInterfaces"] = {}
- if "credCheckers" not in self:
- self["credCheckers"] = []
- self["credCheckers"].append(checker)
- for interface in supported:
- self["credInterfaces"].setdefault(interface, []).append(checker)
-
- def opt_auth(self, description):
- """
- Specify an authentication method for the server.
- """
- try:
- self.addChecker(makeChecker(description))
- except UnsupportedInterfaces as e:
- raise usage.UsageError("Auth plugin not supported: %s" % e.args[0])
- except InvalidAuthType as e:
- raise usage.UsageError("Auth plugin not recognized: %s" % e.args[0])
- except Exception as e:
- raise usage.UsageError("Unexpected error: %s" % e)
-
- def _checkerFactoriesForOptHelpAuth(self):
- """
- Return a list of which authTypes will be displayed by --help-auth.
- This makes it a lot easier to test this module.
- """
- for factory in findCheckerFactories():
- for interface in factory.credentialInterfaces:
- if self.supportsInterface(interface):
- yield factory
- break
-
- def opt_help_auth(self):
- """
- Show all authentication methods available.
- """
- self.authOutput.write("Usage: --auth AuthType[:ArgString]\n")
- self.authOutput.write("For detailed help: --help-auth-type AuthType\n")
- self.authOutput.write("\n")
- # Figure out the right width for our columns
- firstLength = 0
- for factory in self._checkerFactoriesForOptHelpAuth():
- if len(factory.authType) > firstLength:
- firstLength = len(factory.authType)
- formatString = " %%-%is\t%%s\n" % firstLength
- self.authOutput.write(formatString % ("AuthType", "ArgString format"))
- self.authOutput.write(formatString % ("========", "================"))
- for factory in self._checkerFactoriesForOptHelpAuth():
- self.authOutput.write(
- formatString % (factory.authType, factory.argStringFormat)
- )
- self.authOutput.write("\n")
- raise SystemExit(0)
-
- def opt_help_auth_type(self, authType):
- """
- Show help for a particular authentication type.
- """
- try:
- cf = findCheckerFactory(authType)
- except InvalidAuthType:
- raise usage.UsageError("Invalid auth type: %s" % authType)
- self.authOutput.write("Usage: --auth %s[:ArgString]\n" % authType)
- self.authOutput.write("ArgString format: %s\n" % cf.argStringFormat)
- self.authOutput.write("\n")
- for line in cf.authHelp.strip().splitlines():
- self.authOutput.write(" %s\n" % line.rstrip())
- self.authOutput.write("\n")
- if not self.supportsCheckerFactory(cf):
- self.authOutput.write(" %s\n" % notSupportedWarning)
- self.authOutput.write("\n")
- raise SystemExit(0)
|