1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """User management facilities.
17
18 Access to this module is primarily through two global variables.
19
20 'database' -- The user database. The database contains objects
21 representing users, accessed via a *user ID*. The user ID is a label
22 uniquely identifying the user in the system.
23
24 The user database also provides a notion of user groups. Each group
25 is identified by a group ID, and contains zero or more user IDs. A
26 user may belong to more than one group. A group may not contain other
27 groups.
28
29 'authenticator' -- The authenticator object by which the application
30 authenticates users who attempt to access it via various channels.
31
32 Access the database object via this interface:
33
34 * Use the database object as a Python map from user IDs to 'User'
35 objects. The 'keys' method returns a sequence of user IDs in the
36 database.
37
38 * Call 'GetGroupIds' to return a sequence of group IDs. Use the
39 'GetGroup' method to retrieve a 'Group' object for a given group ID.
40
41 """
42
43
44
45
46
47 import qm
48 import label
49 import xmlutil
50
51
52
53
54
55 xml_user_database_dtd = "-//Software Carpentry//User Database V0.1//EN"
56
57
58
59
60
63
64
65
68
69
70
73
74
75
77 """A user account record.
78
79 User accounts contain three sets of properties. Each property
80 is a name-value pair. The three sets of properties are:
81
82 informational properties -- General information about the
83 user. This may include the user's real name, contact
84 information, etc. These properties are generally
85 user-visible, and should be modified only at the user's
86 request.
87
88 configuration properties -- Program-specific per-user
89 configuration. This includes user's preferences, such as
90 preferred layout and output options. These properties are
91 typically hidden as implementation details, and are often
92 changed implicitly as part of other operations.
93
94 authentication properties -- Information used to authenticate
95 the user. This may include such things as passwords, PGP
96 keys, and digital certificates. There are no accessors for
97 these properties; they should be used by authenticators only.
98 """
99
100
101 - def __init__(self,
102 user_id,
103 role="user",
104 enabled=1,
105 information_properties={},
106 configuration_properties={},
107 authentication_properties={}):
108 """Create a new user account.
109
110 'user_id' -- The ID for the user.
111
112 'role' -- If "default", this is the default user account (there
113 should be only one). The default account, if provided, is used
114 for otherwise unauthenticated operations. If "admin", this is
115 an administrator account. If "user", this is an ordinary user
116 account.
117
118 'enabled' -- If true, this account is enabled; otherwise,
119 disabled.
120
121 'information_properties' -- A map contianing information
122 properties.
123
124 'configuration_properties' -- A map containing configuration
125 properties.
126
127 'authentication_properties' -- A map containing authentication
128 properties."""
129
130 self.__id = user_id
131 self.__role = role
132 self.__is_enabled = enabled
133
134 self.__authentication = authentication_properties.copy()
135 self.__configuration = configuration_properties.copy()
136 self.__info = information_properties.copy()
137
138
140 """Return the user ID of this user."""
141
142 return self.__id
143
144
146 """Return the role of this user."""
147
148 return self.__role
149
150
152 """Return true if this account is enabled."""
153
154 return self.__is_enabled
155
156
158 """Return a configuration property.
159
160 'name' -- The name of the property.
161
162 'default' -- The value to return if this property is not
163 specified for the user account."""
164
165 return self.__configuration.get(name, default)
166
167
169 """Set the configuration property 'name' to 'value'."""
170
171 self.__configuration[name] = value
172
173
175 """Return an informational property.
176
177 'name' -- The name of the property.
178
179 'default' -- The value to return if this property is not
180 specified for the user account."""
181
182 return self.__info.get(name, default)
183
184
186 """Set the informational property 'name' to 'value'."""
187
188 self.__info[name] = value
189
190
191
193 """A group of users.
194
195 A 'Group' object is treated as an ordinary list of user IDs (except
196 that a user ID may not appear more than once in the list)."""
197
198 - def __init__(self, group_id, user_ids=[]):
199 """Create a new group.
200
201 'group_id' -- The ID of this group.
202
203 'user_ids' -- IDs of users initially in the group."""
204
205 self.__id = group_id
206 self.__user_ids = list(user_ids)
207
208
210 """Return the group_id of this group."""
211
212 return self.__id
213
214
216 return len(self.__user_ids)
217
218
220 return self.__user_ids[index]
221
222
224 self.__user_ids[index] = user_id
225
226 while self.__user_ids.count(user_id) > 1:
227 self.__user_ids.remove(user_id)
228
229
231 del self.__user_ids[index]
232
233
235
236 if user_id not in self.__user_ids:
237 self.__user_ids.append(user_id)
238
239
241 self.__user_ids.remove(user_id)
242
243
244
246 """Base class for authentication classes.
247
248 An 'Authenticator' object is responsible for determining the
249 authenticity of a user. The inputs to an authentication action
250 depend on the mechanism with which the user communicates with the
251 program -- for instance, a web request, command invocation, or email
252 message."""
253
254
256 """Authenticate for the default user, if one is provided.
257
258 returns -- The user ID of the default user."""
259
260 raise NotImplementedError
261
262
264 """Authenticate a login web request.
265
266 'request' -- A web request containing the user's login
267 information.
268
269 returns -- The user ID of the authenticated user."""
270
271 raise NotImplementedError
272
273
274
276 default_user = User("default_user", "default")
277
280
281
284
285
287 default_user_id = self.default_user.GetId()
288 if user_id == default_user_id:
289 return self.default_user
290 else:
291 raise KeyError, user_id
292
293
294 - def get(self, user_id, default=None):
300
301
304
305
307 raise KeyError, "no such group"
308
309
310
312 """Authenticator for only a single user, "default_user"."""
313
316
317
320
321
322
324 """An XML user database.
325
326 An object of this class behaves as a read-only map from user IDs to
327 'User' objects."""
328
330 """Read in the XML user database."""
331
332 document = xmlutil.load_xml_file(database_path)
333 self.__path = database_path
334
335 node = document.documentElement
336 assert node.tagName == "users"
337
338 self.__users = {}
339 self.__groups = {}
340 self.__default_user_id = None
341
342
343 for user_node in node.getElementsByTagName("user"):
344 user = get_user_from_dom(user_node)
345
346 self.__users[user.GetId()] = user
347
348 if user.GetRole() == "default":
349 if self.__default_user_id is not None:
350
351 raise XmlDatabaseError, "multiple default users"
352 self.__default_user_id = user.GetId()
353
354
355 for group_node in node.getElementsByTagName("group"):
356 group = get_group_from_dom(group_node)
357
358
359 for user_id in group:
360 if not self.__users.has_key(user_id):
361 raise XmlDatabaseError, \
362 'user "%s" in group "%s" is unknown' \
363 % (user_id, group.GetId())
364
365 self.__groups[group.GetId()] = group
366
367
368
370 """Return the ID of the default user, or 'None'."""
371
372 return self.__default_user_id
373
374
376 """Return the IDs of user groups."""
377
378 return self.__groups.keys()
379
380
382 """Return the group with ID 'group_id'."""
383
384 return self.__groups[group_id]
385
386
388 """Write out the user database."""
389
390
391 document = xmlutil.create_dom_document(
392 public_id = "User",
393 document_element_tag="users"
394 )
395 document_element = document.documentElement
396
397 for user in self.__users.values():
398 user_element = create_dom_for_user(document, user)
399 document_element.appendChild(user_element)
400
401 for group in self.__groups.values():
402 group_element = create_dom_for_group(document, group)
403 document_element.appendChild(group_element)
404
405 document.writexml(open(self.__path, "w"))
406
407
408
409
411 return self.__users[user_id]
412
413
414 - def get(self, user_id, default=None):
415 return self.__get(user_id, default)
416
417
419 return self.__users.keys()
420
421
422
424 """An authenticator based on contents of the XML user database."""
425
427 """Create a new authenticator.
428
429 Authentication is performed based on information stored in
430 'XmlDatabase' instance 'database'."""
431
432 assert isinstance(database, XmlDatabase)
433 self.__database = database
434
435
441
442
448
449
469
470
471
472
473
474
475
491
492
494 """Construct a 'User' object from a user DOM element.
495
496 'user_node' -- A "user" DOM element.
497
498 returns -- A 'User' object."""
499
500 assert user_node.tagName == "user"
501
502
503 user_id = user_node.getAttribute("id")
504
505 role = user_node.getAttribute("role")
506
507 enabled = user_node.getAttribute("disabled") == "no"
508
509 info_properties = _get_dom_properties(user_node, "info")
510 auth_properties = _get_dom_properties(user_node, "authentication")
511 conf_properties = _get_dom_properties(user_node, "configuration")
512
513 return User(user_id, role, enabled,
514 info_properties, conf_properties, auth_properties)
515
516
518 """Return a dictionary of properties from a user DOM element node.
519
520 'node' -- A "user" DOM element.
521
522 'tag' -- The tag of the child element in which to look for
523 properties.
524
525 returns -- A map from property names to values."""
526
527
528 elements = node.getElementsByTagName(tag)
529 if len(elements) == 0:
530
531 return {}
532
533 assert len(elements) == 1
534 element = elements[0]
535
536 properties = {}
537
538 for property_node in element.getElementsByTagName("property"):
539
540 name = property_node.getAttribute("name")
541
542 value = xmlutil.get_dom_text(property_node)
543 properties[name] = value
544 return properties
545
546
548 """Create a DOM element node for 'user'.
549
550 'document' -- The DOM document object in which to create the
551 element.
552
553 'user' -- A 'User' instance.
554
555 returns -- A DOM element node."""
556
557
558 element = document.createElement("user")
559 element.setAttribute("id", user.GetId())
560 element.setAttribute("role", user.GetRole())
561 if user.IsEnabled():
562 disabled_attribute = "no"
563 else:
564 disabled_attribute = "yes"
565 element.setAttribute("disabled", disabled_attribute)
566
567 info_properties_element = document.createElement("info")
568 _create_dom_properties(info_properties_element, user._User__info)
569 element.appendChild(info_properties_element)
570
571 auth_properties_element = document.createElement("authentication")
572 _create_dom_properties(auth_properties_element, user._User__authentication)
573 element.appendChild(auth_properties_element)
574
575 conf_properties_element = document.createElement("configuration")
576 _create_dom_properties(conf_properties_element, user._User__configuration)
577 element.appendChild(conf_properties_element)
578
579 return element
580
581
583 """Add user properties to a DOM element.
584
585 'element' -- A DOM element node to which properties are to be added
586 as children.
587
588 'properties' -- A map from property names to values."""
589
590 document = element.ownerDocument
591 for name, value in properties.items():
592 prop_element = xmlutil.create_dom_text_element(
593 document, "property", str(value))
594 prop_element.setAttribute("name", name)
595 element.appendChild(prop_element)
596
597
599 """Construct a 'Group' object from a DOM element.
600
601 'group_node' -- A DOM "group" element node.
602
603 returns -- A 'Group' object."""
604
605 assert group_node.tagName == "group"
606
607 group_id = group_node.getAttribute("id")
608 user_ids = xmlutil.get_child_texts(group_node, "user-id")
609
610 return Group(group_id, user_ids)
611
612
614 """Create a DOM element node for 'group'.
615
616 'document' -- The DOM document object in which to create the
617 element.
618
619 'group' -- A 'Group' instance.
620
621 returns -- A DOM element node."""
622
623 element = document.createElement("group")
624 element.setAttribute("id", group.GetId())
625 for user_id in group:
626 user_id_element = xmlutil.create_dom_text_element(
627 document, "user-id", user_id)
628 element.appendChild(user_id_element)
629 return element
630
631
632
633
634
635
636 database = DefaultDatabase()
637 authenticator = DefaultAuthenticator()
638
639
640
641
642
643
644
645