Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / cred / strcred.py
1 # -*- test-case-name: twisted.test.test_strcred -*-
2 #
3 # Copyright (c) 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)