1 import logging
2 from datetime import datetime
3
4 from turbogears import config, identity
5 from turbogears.database import PackageHub
6 from turbogears.util import load_class
7 from turbojson.jsonify import jsonify_sqlobject, jsonify
8
9 from sqlobject import (SQLObject, SQLObjectNotFound, RelatedJoin,
10 DateTimeCol, IntCol, StringCol, UnicodeCol)
11 from sqlobject.dberrors import DuplicateEntryError
12
13
14 log = logging.getLogger('turbogears.identity.soprovider')
15
16 hub = PackageHub('turbogears.identity')
17 __connection__ = hub
21 if not isinstance(s, basestring) and hasattr(s, '__unicode__'):
22 s = unicode(s)
23 if isinstance(s, unicode):
24 s = s.encode(encoding)
25 return s
26
27
28
29
30 user_class = None
31 group_class = None
32 permission_class = None
33 visit_class = None
37 """Identity that uses a model from a database (via SQLObject)."""
38
39 - def __init__(self, visit_key=None, user=None):
45
46 @property
48 """Get user instance for this identity."""
49 try:
50 return self._user
51 except AttributeError:
52
53 pass
54
55
56 visit = self.visit_link
57 if visit:
58 try:
59 self._user = user_class.get(visit.user_id)
60 except SQLObjectNotFound:
61 log.warning("No such user with ID: %s", visit.user_id)
62 self._user = None
63 else:
64 self._user = None
65 return self._user
66
67 @property
69 """Get user name of this identity."""
70 if not self.user:
71 return None
72 return self.user.user_name
73
74 @property
76 """Get user id of this identity."""
77 if not self.user:
78 return None
79 return self.user.id
80
81 @property
83 """Return true if not logged in."""
84 return not self.user
85
86 @property
88 """Get set of permission names of this identity."""
89 try:
90 return self._permissions
91 except AttributeError:
92
93 pass
94 if not self.user:
95 self._permissions = frozenset()
96 else:
97 self._permissions = frozenset(
98 p.permission_name for p in self.user.permissions)
99 return self._permissions
100
101 @property
103 """Get set of group names of this identity."""
104 try:
105 return self._groups
106 except AttributeError:
107
108 pass
109 if not self.user:
110 self._groups = frozenset()
111 else:
112 self._groups = frozenset(g.group_name for g in self.user.groups)
113 return self._groups
114
115 @property
117 """Get set of group IDs of this identity."""
118 try:
119 return self._group_ids
120 except AttributeError:
121
122 pass
123 if not self.user:
124 self._group_ids = frozenset()
125 else:
126 self._group_ids = frozenset(g.id for g in self.user.groups)
127 return self._group_ids
128
129 @property
138
139 @property
143
158
166
169 """IdentityProvider that uses a model from a database (via SQLObject)."""
170
172 super(SqlObjectIdentityProvider, self).__init__()
173 glob_ns = globals()
174
175 for classname in ('user', 'group', 'permission', 'visit'):
176 default_classname = '.TG_' + (classname == 'visit'
177 and 'VisitIdentity' or classname.capitalize())
178 class_path = config.get('identity.soprovider.model.%s' % classname,
179 __name__ + default_classname)
180 class_ = load_class(class_path)
181 if class_:
182 log.info('Successfully loaded "%s".', class_path)
183 glob_ns['%s_class' % classname] = class_
184 else:
185 log.error('Could not load class "%s".'
186 ' Check identity.soprovider.model.%s setting',
187 class_path, classname)
188 try:
189 encoding = glob_ns[
190 'user_class'].sqlmeta.columns['user_name'].dbEncoding
191 except (KeyError, AttributeError):
192 encoding = None
193 self.user_class_db_encoding = encoding or 'utf-8'
194
199
201 """Create the database tables if they don't already exist."""
202 try:
203 hub.begin()
204 user_class.createTable(ifNotExists=True)
205 group_class.createTable(ifNotExists=True)
206 permission_class.createTable(ifNotExists=True)
207 visit_class.createTable(ifNotExists=True)
208 hub.commit()
209 hub.end()
210 except KeyError:
211 log.warning("No database is configured:"
212 " SqlObjectIdentityProvider is disabled.")
213 return
214
238
240 """Check the user_name and password against existing credentials.
241
242 Note: user_name is not used here, but is required by external
243 password validation schemes that might override this method.
244 If you use SqlObjectIdentityProvider, but want to check the passwords
245 against an external source (i.e. PAM, a password file, Windows domain),
246 subclass SqlObjectIdentityProvider, and override this method.
247
248 """
249 return user.password == self.encrypt_password(password)
250
252 """Lookup the principal represented by user_name.
253
254 Return None if there is no principal for the given user ID.
255
256 Must return an object with the following properties:
257 user_name: original user name
258 user: a provider dependent object (TG_User or similar)
259 groups: a set of group names
260 permissions: a set of permission names
261
262 """
263 return SqlObjectIdentity(visit_key)
264
266 """Return anonymous identity.
267
268 Must return an object with the following properties:
269 user_name: original user name
270 user: a provider dependent object (TG_User or similar)
271 groups: a set of group names
272 permissions: a set of permission names
273
274 """
275 return SqlObjectIdentity()
276
280
283 """Reasonably basic User definition."""
284
285 user_name = UnicodeCol(length=16, alternateID=True,
286 alternateMethodName='by_user_name')
287 email_address = UnicodeCol(length=255, alternateID=True,
288 alternateMethodName='by_email_address')
289 display_name = UnicodeCol(length=255)
290 password = UnicodeCol(length=40)
291 created = DateTimeCol(default=datetime.now)
292
293
294 groups = RelatedJoin('TG_Group', intermediateTable='user_group',
295 joinColumn='user_id', otherColumn='group_id')
296
298 perms = set()
299 for g in self.groups:
300 perms = perms | set(g.permissions)
301 return perms
302
317
319 """Save the password as-is to the database."""
320 self._SO_set_password(password)
321
322 @jsonify.when('isinstance(obj, TG_User)')
323 -def jsonify_user(obj):
324 """Convert user to JSON."""
325 result = jsonify_sqlobject(obj)
326 result.pop('password', None)
327 result['groups'] = [g.group_name for g in obj.groups]
328 result['permissions'] = [p.permission_name for p in obj.permissions]
329 return result
330
333 """An ultra-simple group definition."""
334
335 group_name = UnicodeCol(length=16, alternateID=True,
336 alternateMethodName='by_group_name')
337 display_name = UnicodeCol(length=255)
338 created = DateTimeCol(default=datetime.now)
339
340
341 users = RelatedJoin('TG_User', intermediateTable='user_group',
342 joinColumn='group_id', otherColumn='user_id')
343
344
345 permissions = RelatedJoin('TG_Permission', joinColumn='group_id',
346 intermediateTable='group_permission',
347 otherColumn='permission_id')
348
349 @jsonify.when('isinstance(obj, TG_Group)')
350 -def jsonify_group(obj):
351 """Convert group to JSON."""
352 result = jsonify_sqlobject(obj)
353 result['users'] = [u.user_name for u in obj.users]
354 result['permissions'] = [p.permission_name for p in obj.permissions]
355 return result
356
359 """Permissions for a given group."""
360
363
364 permission_name = UnicodeCol(length=16, alternateID=True,
365 alternateMethodName='by_permission_name')
366 description = UnicodeCol(length=255)
367
368 groups = RelatedJoin('TG_Group', intermediateTable='group_permission',
369 joinColumn='permission_id', otherColumn='group_id')
370
373 """Convert permissions to JSON."""
374 result = jsonify_sqlobject(obj)
375 result['groups'] = [g.group_name for g in obj.groups]
376 return result
377
380 """A visit to your website."""
381
384
385 visit_key = StringCol(length=40, alternateID=True,
386 alternateMethodName='by_visit_key')
387 user_id = IntCol()
388
403