1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """Base classes for PyXMPP SASL implementation.
18
19 Normative reference:
20 - `RFC 2222 <http://www.ietf.org/rfc/rfc2222.txt>`__
21 """
22 __revision__="$Id: core.py 647 2006-08-26 18:27:39Z jajcus $"
23 __docformat__="restructuredtext en"
24
25 import random
26 import logging
27 from binascii import b2a_base64
28
30 """Base class for password managers.
31
32 Password manager is an object responsible for providing or verification
33 of authentication credentials.
34
35 All the methods of `PasswordManager` class may be overriden in derived
36 classes for specific authentication and authorization policy."""
38 """Initialize a `PasswordManager` object."""
39 pass
40
41 - def get_password(self,username,realm=None,acceptable_formats=("plain",)):
42 """Get the password for user authentication.
43
44 [both client or server]
45
46 By default returns (None, None) providing no password. Should be
47 overriden in derived classes.
48
49 :Parameters:
50 - `username`: the username for which the password is requested.
51 - `realm`: the authentication realm for which the password is
52 requested.
53 - `acceptable_formats`: a sequence of acceptable formats of the
54 password data. Could be "plain", "md5:user:realm:password" or any
55 other mechanism-specific encoding. This allows non-plain-text
56 storage of passwords. But only "plain" format will work with
57 all password authentication mechanisms.
58 :Types:
59 - `username`: `unicode`
60 - `realm`: `unicode`
61 - `acceptable_formats`: sequence of `str`
62
63 :return: the password and its encoding (format).
64 :returntype: `unicode`,`str` tuple."""
65 _unused, _unused, _unused = username, realm, acceptable_formats
66 return None,None
67
69 """Check the password validity.
70
71 [server only]
72
73 Used by plain-text authentication mechanisms.
74
75 Retrieve a "plain" password for the `username` and `realm` using
76 `self.get_password` and compare it with the password provided.
77
78 May be overrided e.g. to check the password against some external
79 authentication mechanism (PAM, LDAP, etc.).
80
81 :Parameters:
82 - `username`: the username for which the password verification is
83 requested.
84 - `password`: the password to verify.
85 - `realm`: the authentication realm for which the password
86 verification is requested.
87 :Types:
88 - `username`: `unicode`
89 - `password`: `unicode`
90 - `realm`: `unicode`
91
92 :return: `True` if the password is valid.
93 :returntype: `bool`"""
94 pw,format=self.get_password(username,realm,("plain",))
95 if pw and format=="plain" and pw==password:
96 return True
97 return False
98
100 """Get available realms list.
101
102 [server only]
103
104 :return: a list of realms available for authentication. May be empty --
105 the client may choose its own realm then or use no realm at all.
106 :returntype: `list` of `unicode`"""
107 return []
108
110 """Choose an authentication realm from the list provided by the server.
111
112 [client only]
113
114 By default return the first realm from the list or `None` if the list
115 is empty.
116
117 :Parameters:
118 - `realm_list`: the list of realms provided by a server.
119 :Types:
120 - `realm_list`: sequence of `unicode`
121
122 :return: the realm chosen.
123 :returntype: `unicode`"""
124 if realm_list:
125 return realm_list[0]
126 else:
127 return None
128
130 """Check if the authenticated entity is allowed to use given
131 authorization id.
132
133 [server only]
134
135 By default return `True` if the `authzid` is `None` or empty or it is
136 equal to extra_info["username"] (if the latter is present).
137
138 :Parameters:
139 - `authzid`: an authorization id.
140 - `extra_info`: information about an entity got during the
141 authentication process. This is a mapping with arbitrary,
142 mechanism-dependent items. Common keys are 'username' or
143 'realm'.
144 :Types:
145 - `authzid`: `unicode`
146 - `extra_info`: mapping
147
148 :return: `True` if the authenticated entity is authorized to use
149 the provided authorization id.
150 :returntype: `bool`"""
151 if not extra_info:
152 extra_info={}
153 return (not authzid
154 or extra_info.has_key("username")
155 and extra_info["username"]==authzid)
156
158 """Return the service type for DIGEST-MD5 'digest-uri' field.
159
160 Should be overriden in derived classes.
161
162 :return: the service type ("unknown" by default)"""
163 return "unknown"
164
166 """Return the host name for DIGEST-MD5 'digest-uri' field.
167
168 Should be overriden in derived classes.
169
170 :return: the host name ("unknown" by default)"""
171 return "unknown"
172
174 """Return the service name for DIGEST-MD5 'digest-uri' field.
175
176 Should be overriden in derived classes.
177
178 :return: the service name or `None` (which is the default)."""
179 return None
180
182 """Generate a random string for digest authentication challenges.
183
184 The string should be cryptographicaly secure random pattern.
185
186 :return: the string generated.
187 :returntype: `str`"""
188
189 r1=str(random.random())[2:]
190 r2=str(random.random())[2:]
191 return r1+r2
192
194 """Base class for SASL authentication reply objects.
195
196 :Ivariables:
197 - `data`: optional reply data.
198 :Types:
199 - `data`: `str`"""
201 """Initialize the `Reply` object.
202
203 :Parameters:
204 - `data`: optional reply data.
205 :Types:
206 - `data`: `str`"""
207 self.data=data
208
210 """Base64-encode the data contained in the reply.
211
212 :return: base64-encoded data.
213 :returntype: `str`"""
214 if self.data is not None:
215 ret=b2a_base64(self.data)
216 if ret[-1]=='\n':
217 ret=ret[:-1]
218 return ret
219 else:
220 return None
221
223 """The challenge SASL message (server's challenge for the client)."""
225 """Initialize the `Challenge` object."""
226 Reply.__init__(self,data)
228 return "<sasl.Challenge: %r>" % (self.data,)
229
231 """The response SASL message (clients's reply the the server's challenge)."""
233 """Initialize the `Response` object."""
234 Reply.__init__(self,data)
236 return "<sasl.Response: %r>" % (self.data,)
237
239 """The failure SASL message.
240
241 :Ivariables:
242 - `reason`: the failure reason.
243 :Types:
244 - `reason`: unicode."""
246 """Initialize the `Failure` object.
247
248 :Parameters:
249 - `reason`: the failure reason.
250 :Types:
251 - `reason`: unicode."""
252 Reply.__init__(self,"")
253 self.reason=reason
255 return "<sasl.Failure: %r>" % (self.reason,)
256
258 """The success SASL message (sent by the server on authentication success)."""
259 - def __init__(self,username,realm=None,authzid=None,data=None):
260 """Initialize the `Success` object.
261
262 :Parameters:
263 - `username`: authenticated username (authentication id).
264 - `realm`: authentication realm used.
265 - `authzid`: authorization id.
266 - `data`: the success data to be sent to the client.
267 :Types:
268 - `username`: `unicode`
269 - `realm`: `unicode`
270 - `authzid`: `unicode`
271 - `data`: `str`
272 """
273 Reply.__init__(self,data)
274 self.username=username
275 self.realm=realm
276 self.authzid=authzid
278 return "<sasl.Success: authzid: %r data: %r>" % (self.authzid,self.data)
279
281 """Base class for client authenticators.
282
283 A client authenticator class is a client-side implementation of a SASL
284 mechanism. One `ClientAuthenticator` object may be used for one
285 client authentication process."""
286
288 """Initialize a `ClientAuthenticator` object.
289
290 :Parameters:
291 - `password_manager`: a password manager providing authentication
292 credentials.
293 :Types:
294 - `password_manager`: `PasswordManager`"""
295 self.password_manager=password_manager
296 self.__logger=logging.getLogger("pyxmpp.sasl.ClientAuthenticator")
297
298 - def start(self,username,authzid):
299 """Start the authentication process.
300
301 :Parameters:
302 - `username`: the username (authentication id).
303 - `authzid`: the authorization id requester.
304 :Types:
305 - `username`: `unicode`
306 - `authzid`: `unicode`
307
308 :return: the initial response to send to the server or a failuer
309 indicator.
310 :returntype: `Response` or `Failure`"""
311 _unused, _unused = username, authzid
312 return Failure("Not implemented")
313
315 """Process the server's challenge.
316
317 :Parameters:
318 - `challenge`: the challenge.
319 :Types:
320 - `challenge`: `str`
321
322 :return: the response or a failure indicator.
323 :returntype: `Response` or `Failure`"""
324 _unused = challenge
325 return Failure("Not implemented")
326
328 """Handle authentication succes information from the server.
329
330 :Parameters:
331 - `data`: the optional additional data returned with the success.
332 :Types:
333 - `data`: `str`
334
335 :return: success or failure indicator.
336 :returntype: `Success` or `Failure`"""
337 _unused = data
338 return Failure("Not implemented")
339
341 """Base class for server authenticators.
342
343 A server authenticator class is a server-side implementation of a SASL
344 mechanism. One `ServerAuthenticator` object may be used for one
345 client authentication process."""
346
348 """Initialize a `ServerAuthenticator` object.
349
350 :Parameters:
351 - `password_manager`: a password manager providing authentication
352 credential verfication.
353 :Types:
354 - `password_manager`: `PasswordManager`"""
355 self.password_manager=password_manager
356 self.__logger=logging.getLogger("pyxmpp.sasl.ServerAuthenticator")
357
358 - def start(self,initial_response):
359 """Start the authentication process.
360
361 :Parameters:
362 - `initial_response`: the initial response send by the client with
363 the authentication request.
364
365 :Types:
366 - `initial_response`: `str`
367
368 :return: a challenge, a success or a failure indicator.
369 :returntype: `Challenge` or `Failure` or `Success`"""
370 _unused = initial_response
371 return Failure("not-authorized")
372
374 """Process a response from a client.
375
376 :Parameters:
377 - `response`: the response from the client to our challenge.
378 :Types:
379 - `response`: `str`
380
381 :return: a challenge, a success or a failure indicator.
382 :returntype: `Challenge` or `Success` or `Failure`"""
383 _unused = response
384 return Failure("not-authorized")
385
386
387