OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.test.test_strcred -*- | |
2 # | |
3 # Copyright (c) 2007-2008 Twisted Matrix Laboratories. | |
4 # See LICENSE for details. | |
5 # | |
6 | |
7 """ | |
8 Support for resolving command-line strings that represent different | |
9 checkers available to cred. | |
10 | |
11 Examples: | |
12 - passwd:/etc/passwd | |
13 - memory:admin:asdf:user:lkj | |
14 - unix | |
15 """ | |
16 | |
17 import sys | |
18 | |
19 from zope.interface import Interface, Attribute | |
20 | |
21 from twisted.plugin import getPlugins | |
22 from twisted.python import usage | |
23 | |
24 | |
25 | |
26 class ICheckerFactory(Interface): | |
27 """ | |
28 A factory for objects which provide | |
29 L{twisted.cred.checkers.ICredentialsChecker}. | |
30 | |
31 It's implemented by twistd plugins creating checkers. | |
32 """ | |
33 | |
34 authType = Attribute( | |
35 'A tag that identifies the authentication method.') | |
36 | |
37 | |
38 authHelp = Attribute( | |
39 'A detailed (potentially multi-line) description of precisely ' | |
40 'what functionality this CheckerFactory provides.') | |
41 | |
42 | |
43 argStringFormat = Attribute( | |
44 'A short (one-line) description of the argument string format.') | |
45 | |
46 | |
47 credentialInterfaces = Attribute( | |
48 'A list of credentials interfaces that this factory will support.') | |
49 | |
50 | |
51 def generateChecker(argstring): | |
52 """ | |
53 Return an L{ICredentialChecker} provider using the supplied | |
54 argument string. | |
55 """ | |
56 | |
57 | |
58 | |
59 class StrcredException(Exception): | |
60 """ | |
61 Base exception class for strcred. | |
62 """ | |
63 | |
64 | |
65 | |
66 class InvalidAuthType(StrcredException): | |
67 """ | |
68 Raised when a user provides an invalid identifier for the | |
69 authentication plugin (known as the authType). | |
70 """ | |
71 | |
72 | |
73 | |
74 class InvalidAuthArgumentString(StrcredException): | |
75 """ | |
76 Raised by an authentication plugin when the argument string | |
77 provided is formatted incorrectly. | |
78 """ | |
79 | |
80 | |
81 | |
82 class UnsupportedInterfaces(StrcredException): | |
83 """ | |
84 Raised when an application is given a checker to use that does not | |
85 provide any of the application's supported credentials interfaces. | |
86 """ | |
87 | |
88 | |
89 | |
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 " | |
93 "this application.") | |
94 | |
95 | |
96 | |
97 def findCheckerFactories(): | |
98 """ | |
99 Find all objects that implement L{ICheckerFactory}. | |
100 """ | |
101 return getPlugins(ICheckerFactory) | |
102 | |
103 | |
104 | |
105 def findCheckerFactory(authType): | |
106 """ | |
107 Find the first checker factory that supports the given authType. | |
108 """ | |
109 for factory in findCheckerFactories(): | |
110 if factory.authType == authType: | |
111 return factory | |
112 raise InvalidAuthType(authType) | |
113 | |
114 | |
115 | |
116 def makeChecker(description): | |
117 """ | |
118 Returns an L{twisted.cred.checkers.ICredentialsChecker} based on the | |
119 contents of a descriptive string. Similar to | |
120 L{twisted.application.strports}. | |
121 """ | |
122 if ':' in description: | |
123 authType, argstring = description.split(':', 1) | |
124 else: | |
125 authType = description | |
126 argstring = '' | |
127 return findCheckerFactory(authType).generateChecker(argstring) | |
128 | |
129 | |
130 | |
131 class AuthOptionMixin: | |
132 """ | |
133 Defines helper methods that can be added on to any | |
134 L{usage.Options} subclass that needs authentication. | |
135 | |
136 This mixin implements three new options methods: | |
137 | |
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). | |
141 | |
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 | |
144 when finished. | |
145 | |
146 The opt_help_auth_type method (--help-auth-type) will display | |
147 detailed help for a particular checker plugin. | |
148 | |
149 @cvar supportedInterfaces: An iterable object that returns | |
150 credential interfaces which this application is able to support. | |
151 | |
152 @cvar authOutput: A writeable object to which this options class | |
153 will send all help-related output. Default: L{sys.stdout} | |
154 """ | |
155 | |
156 supportedInterfaces = None | |
157 authOutput = sys.stdout | |
158 | |
159 | |
160 def supportsInterface(self, interface): | |
161 """ | |
162 Returns whether a particular credentials interface is supported. | |
163 """ | |
164 return (self.supportedInterfaces is None | |
165 or interface in self.supportedInterfaces) | |
166 | |
167 | |
168 def supportsCheckerFactory(self, factory): | |
169 """ | |
170 Returns whether a checker factory will provide at least one of | |
171 the credentials interfaces that we care about. | |
172 """ | |
173 for interface in factory.credentialInterfaces: | |
174 if self.supportsInterface(interface): | |
175 return True | |
176 return False | |
177 | |
178 | |
179 def addChecker(self, checker): | |
180 """ | |
181 Supply a supplied credentials checker to the Options class. | |
182 """ | |
183 # First figure out which interfaces we're willing to support. | |
184 supported = [] | |
185 if self.supportedInterfaces is None: | |
186 supported = checker.credentialInterfaces | |
187 else: | |
188 for interface in checker.credentialInterfaces: | |
189 if self.supportsInterface(interface): | |
190 supported.append(interface) | |
191 if not supported: | |
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) | |
201 | |
202 | |
203 def opt_auth(self, description): | |
204 """ | |
205 Specify an authentication method for the server. | |
206 """ | |
207 try: | |
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]) | |
215 except Exception, e: | |
216 raise usage.UsageError('Unexpected error: %s' % e) | |
217 | |
218 | |
219 def _checkerFactoriesForOptHelpAuth(self): | |
220 """ | |
221 Return a list of which authTypes will be displayed by --help-auth. | |
222 This makes it a lot easier to test this module. | |
223 """ | |
224 for factory in findCheckerFactories(): | |
225 for interface in factory.credentialInterfaces: | |
226 if self.supportsInterface(interface): | |
227 yield factory | |
228 break | |
229 | |
230 | |
231 def opt_help_auth(self): | |
232 """ | |
233 Show all authentication methods available. | |
234 """ | |
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 | |
239 firstLength = 0 | |
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') | |
250 raise SystemExit(0) | |
251 | |
252 | |
253 def opt_help_auth_type(self, authType): | |
254 """ | |
255 Show help for a particular authentication type. | |
256 """ | |
257 try: | |
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') | |
270 raise SystemExit(0) | |
OLD | NEW |