1 # -*- test-case-name: twisted.test.test_strcred -*-
3 # Copyright (c) Twisted Matrix Laboratories.
4 # See LICENSE for details.
8 Support for resolving command-line strings that represent different
9 checkers available to cred.
13 - memory:admin:asdf:user:lkj
19 from zope.interface import Interface, Attribute
21 from twisted.plugin import getPlugins
22 from twisted.python import usage
26 class ICheckerFactory(Interface):
28 A factory for objects which provide
29 L{twisted.cred.checkers.ICredentialsChecker}.
31 It's implemented by twistd plugins creating checkers.
35 'A tag that identifies the authentication method.')
39 'A detailed (potentially multi-line) description of precisely '
40 'what functionality this CheckerFactory provides.')
43 argStringFormat = Attribute(
44 'A short (one-line) description of the argument string format.')
47 credentialInterfaces = Attribute(
48 'A list of credentials interfaces that this factory will support.')
51 def generateChecker(argstring):
53 Return an L{ICredentialChecker} provider using the supplied
59 class StrcredException(Exception):
61 Base exception class for strcred.
66 class InvalidAuthType(StrcredException):
68 Raised when a user provides an invalid identifier for the
69 authentication plugin (known as the authType).
74 class InvalidAuthArgumentString(StrcredException):
76 Raised by an authentication plugin when the argument string
77 provided is formatted incorrectly.
82 class UnsupportedInterfaces(StrcredException):
84 Raised when an application is given a checker to use that does not
85 provide any of the application's supported credentials interfaces.
90 # This will be used to warn the users whenever they view help for an
91 # authType that is not supported by the application.
92 notSupportedWarning = ("WARNING: This authType is not supported by "
97 def findCheckerFactories():
99 Find all objects that implement L{ICheckerFactory}.
101 return getPlugins(ICheckerFactory)
105 def findCheckerFactory(authType):
107 Find the first checker factory that supports the given authType.
109 for factory in findCheckerFactories():
110 if factory.authType == authType:
112 raise InvalidAuthType(authType)
116 def makeChecker(description):
118 Returns an L{twisted.cred.checkers.ICredentialsChecker} based on the
119 contents of a descriptive string. Similar to
120 L{twisted.application.strports}.
122 if ':' in description:
123 authType, argstring = description.split(':', 1)
125 authType = description
127 return findCheckerFactory(authType).generateChecker(argstring)
131 class AuthOptionMixin:
133 Defines helper methods that can be added on to any
134 L{usage.Options} subclass that needs authentication.
136 This mixin implements three new options methods:
138 The opt_auth method (--auth) will write two new values to the
139 'self' dictionary: C{credInterfaces} (a dict of lists) and
140 C{credCheckers} (a list).
142 The opt_help_auth method (--help-auth) will search for all
143 available checker plugins and list them for the user; it will exit
146 The opt_help_auth_type method (--help-auth-type) will display
147 detailed help for a particular checker plugin.
149 @cvar supportedInterfaces: An iterable object that returns
150 credential interfaces which this application is able to support.
152 @cvar authOutput: A writeable object to which this options class
153 will send all help-related output. Default: L{sys.stdout}
156 supportedInterfaces = None
157 authOutput = sys.stdout
160 def supportsInterface(self, interface):
162 Returns whether a particular credentials interface is supported.
164 return (self.supportedInterfaces is None
165 or interface in self.supportedInterfaces)
168 def supportsCheckerFactory(self, factory):
170 Returns whether a checker factory will provide at least one of
171 the credentials interfaces that we care about.
173 for interface in factory.credentialInterfaces:
174 if self.supportsInterface(interface):
179 def addChecker(self, checker):
181 Supply a supplied credentials checker to the Options class.
183 # First figure out which interfaces we're willing to support.
185 if self.supportedInterfaces is None:
186 supported = checker.credentialInterfaces
188 for interface in checker.credentialInterfaces:
189 if self.supportsInterface(interface):
190 supported.append(interface)
192 raise UnsupportedInterfaces(checker.credentialInterfaces)
193 # If we get this far, then we know we can use this checker.
194 if 'credInterfaces' not in self:
195 self['credInterfaces'] = {}
196 if 'credCheckers' not in self:
197 self['credCheckers'] = []
198 self['credCheckers'].append(checker)
199 for interface in supported:
200 self['credInterfaces'].setdefault(interface, []).append(checker)
203 def opt_auth(self, description):
205 Specify an authentication method for the server.
208 self.addChecker(makeChecker(description))
209 except UnsupportedInterfaces, e:
210 raise usage.UsageError(
211 'Auth plugin not supported: %s' % e.args[0])
212 except InvalidAuthType, e:
213 raise usage.UsageError(
214 'Auth plugin not recognized: %s' % e.args[0])
216 raise usage.UsageError('Unexpected error: %s' % e)
219 def _checkerFactoriesForOptHelpAuth(self):
221 Return a list of which authTypes will be displayed by --help-auth.
222 This makes it a lot easier to test this module.
224 for factory in findCheckerFactories():
225 for interface in factory.credentialInterfaces:
226 if self.supportsInterface(interface):
231 def opt_help_auth(self):
233 Show all authentication methods available.
235 self.authOutput.write("Usage: --auth AuthType[:ArgString]\n")
236 self.authOutput.write("For detailed help: --help-auth-type AuthType\n")
237 self.authOutput.write('\n')
238 # Figure out the right width for our columns
240 for factory in self._checkerFactoriesForOptHelpAuth():
241 if len(factory.authType) > firstLength:
242 firstLength = len(factory.authType)
243 formatString = ' %%-%is\t%%s\n' % firstLength
244 self.authOutput.write(formatString % ('AuthType', 'ArgString format'))
245 self.authOutput.write(formatString % ('========', '================'))
246 for factory in self._checkerFactoriesForOptHelpAuth():
247 self.authOutput.write(
248 formatString % (factory.authType, factory.argStringFormat))
249 self.authOutput.write('\n')
253 def opt_help_auth_type(self, authType):
255 Show help for a particular authentication type.
258 cf = findCheckerFactory(authType)
259 except InvalidAuthType:
260 raise usage.UsageError("Invalid auth type: %s" % authType)
261 self.authOutput.write("Usage: --auth %s[:ArgString]\n" % authType)
262 self.authOutput.write("ArgString format: %s\n" % cf.argStringFormat)
263 self.authOutput.write('\n')
264 for line in cf.authHelp.strip().splitlines():
265 self.authOutput.write(' %s\n' % line.rstrip())
266 self.authOutput.write('\n')
267 if not self.supportsCheckerFactory(cf):
268 self.authOutput.write(' %s\n' % notSupportedWarning)
269 self.authOutput.write('\n')