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 """A visit to your website."""
284
287
288 visit_key = StringCol(length=40, alternateID=True,
289 alternateMethodName='by_visit_key')
290 user_id = IntCol()
291
294 """An ultra-simple group definition."""
295
296 group_name = UnicodeCol(length=16, alternateID=True,
297 alternateMethodName='by_group_name')
298 display_name = UnicodeCol(length=255)
299 created = DateTimeCol(default=datetime.now)
300
301
302 users = RelatedJoin('TG_User', intermediateTable='user_group',
303 joinColumn='group_id', otherColumn='user_id')
304
305
306 permissions = RelatedJoin('TG_Permission', joinColumn='group_id',
307 intermediateTable='group_permission',
308 otherColumn='permission_id')
309
310 @jsonify.when('isinstance(obj, TG_Group)')
311 -def jsonify_group(obj):
312 """Convert group to JSON."""
313 result = jsonify_sqlobject(obj)
314 result['users'] = [u.user_name for u in obj.users]
315 result['permissions'] = [p.permission_name for p in obj.permissions]
316 return result
317
320 """Reasonably basic User definition."""
321
322 user_name = UnicodeCol(length=16, alternateID=True,
323 alternateMethodName='by_user_name')
324 email_address = UnicodeCol(length=255, alternateID=True,
325 alternateMethodName='by_email_address')
326 display_name = UnicodeCol(length=255)
327 password = UnicodeCol(length=40)
328 created = DateTimeCol(default=datetime.now)
329
330
331 groups = RelatedJoin('TG_Group', intermediateTable='user_group',
332 joinColumn='user_id', otherColumn='group_id')
333
335 perms = set()
336 for g in self.groups:
337 perms = perms | set(g.permissions)
338 return perms
339
354
356 """Save the password as-is to the database."""
357 self._SO_set_password(password)
358
359 @jsonify.when('isinstance(obj, TG_User)')
360 -def jsonify_user(obj):
361 """Convert user to JSON."""
362 result = jsonify_sqlobject(obj)
363 del result['password']
364 result['groups'] = [g.group_name for g in obj.groups]
365 result['permissions'] = [p.permission_name for p in obj.permissions]
366 return result
367
370 """Permissions for a given group."""
371
374
375 permission_name = UnicodeCol(length=16, alternateID=True,
376 alternateMethodName='by_permission_name')
377 description = UnicodeCol(length=255)
378
379 groups = RelatedJoin('TG_Group', intermediateTable='group_permission',
380 joinColumn='permission_id', otherColumn='group_id')
381
384 """Convert permissions to JSON."""
385 result = jsonify_sqlobject(obj)
386 result['groups'] = [g.group_name for g in obj.groups]
387 return result
388
403