· 8 years ago · Jan 14, 2018, 07:18 AM
1"""\
2This module provides an implementation of an SQLAlchemy 0.4 driver for use with
3the SQLAlchemyManager middleware.
4
5Ordinarily you will probably want to use the ``authkit.users.sqlalchemy_driver``
6module which is designed to be used with Pylons 0.9.7 and automatically uses the
7correct driver for the version of SQLAlchemy you are using.
8"""
9
10from paste.util.import_string import eval_import
11from authkit.users import *
12from sqlalchemy import types, ForeignKey, Table, Column, types
13from sqlalchemy.orm import mapper, relation
14
15def setup_model(model, metadata, **p):
16 class User(object):
17 def __init__(
18 self,
19 username,
20 uid=None,
21 password=None,
22 group_uid=None,
23 ):
24 self.uid = uid
25 self.username = username
26 self.password = password
27 self.group_uid = group_uid
28 def __repr__(self):
29 return "User(%(username)s)" % self.__dict__
30 model.User = User
31
32 class Group(object):
33 def __init__(self, name=None):
34 self.name = name
35 def __repr__(self):
36 return "Group(%(name)s)" % self.__dict__
37 model.Group = Group
38
39 class Role(object):
40 def __init__(self, name=None):
41 self.name = name
42 def __repr__(self):
43 return "Role(%(name)s)" % self.__dict__
44 model.Role = Role
45
46 # Tables
47 model.groups_table = Table(
48 "groups",
49 metadata,
50 Column("uid", types.Integer, primary_key=True),
51 Column("name", types.String(255), unique=True, nullable=False),
52 )
53 model.roles_table = Table(
54 "roles",
55 metadata,
56 Column("uid", types.Integer, primary_key=True),
57 Column("name", types.String(255), unique=True, nullable=False),
58 )
59 model.users_table = Table(
60 "users",
61 metadata,
62 Column("uid", types.Integer, primary_key=True),
63 Column("username", types.String(255), unique=True, nullable=False),
64 Column("password", types.String(255), nullable=False),
65 Column("group_uid", types.Integer, ForeignKey("groups.uid")),
66 )
67 model.users_roles_table = Table( # many:many relation table
68 "users_roles",
69 metadata,
70 Column("user_uid", types.Integer, ForeignKey("users.uid")),
71 Column("role_uid", types.Integer, ForeignKey("roles.uid")),
72 )
73
74 model.groups_mapper = mapper(
75 Group,
76 model.groups_table,
77 properties={
78 "users": relation(User)
79 }
80 )
81 model.users_mapper = mapper(
82 User,
83 model.users_table,
84 properties={
85 "roles": relation(Role, lazy=True, secondary=model.users_roles_table),
86 "group": relation(Group),
87 }
88 )
89 model.roles_mapper = mapper(
90 Role,
91 model.roles_table,
92 properties={
93 "users": relation(User, lazy=True, secondary=model.users_roles_table)
94 }
95 )
96
97class UsersFromDatabase(Users):
98 """
99 Database Version
100 """
101 api_version = 0.4
102
103 def __init__(self, environ, data=None, encrypt=None):
104 if encrypt is None:
105 def encrypt(password):
106 return password
107 self.encrypt = encrypt
108 self.environ = environ
109 if not environ.has_key('sqlalchemy.model') or not environ.has_key('sqlalchemy.session'):
110 raise AuthKitError("The required SQLAlchemyManager middleware is not present")
111 self.model = environ['sqlalchemy.model']
112 # This object is set up on each request so we can assign the session
113 self.session = environ['sqlalchemy.session']
114 self.autoflush = False
115
116 # Create Methods
117 def user_create(self, username, password, group=None):
118 """
119 Create a new user with the username, password and group name specified.
120 """
121 if ' ' in username:
122 raise AuthKitError("Usernames cannot contain space characters")
123 if self.user_exists(username):
124 raise AuthKitError("User %r already exists"%username)
125 if group is None:
126 new_user = self.model.User(
127 username=username.lower(),
128 password=self.encrypt(password)
129 )
130 else:
131 if not self.group_exists(group):
132 raise AuthKitNoSuchGroupError(
133 "There is no such group %r"%group
134 )
135 new_user = self.model.User(
136 username=username.lower(),
137 password=self.encrypt(password),
138 group_uid=self.session.query(self.model.Group).filter_by(name=group.lower()).one().uid
139 )
140 self.session.save(new_user)
141 if self.autoflush:
142 self.session.flush()
143
144 def role_create(self, role):
145 """
146 Add a new role to the system
147 """
148 if ' ' in role:
149 raise AuthKitError("Roles cannot contain space characters")
150 if self.role_exists(role):
151 raise AuthKitError("Role %r already exists"%role)
152 new_role = self.model.Role(role.lower())
153 self.session.save(new_role)
154 if self.autoflush:
155 self.session.flush()
156
157 #~ new_role2 = self.model.Role("test")
158 #~ #new_role2.flush()
159 #~ from pysqlite2 import dbapi2 as sqlite
160 #~ conn = sqlite.connect("mydb.db")
161 #~ cur = conn.cursor()
162 #~ cur.execute("SELECT * FROM roles;")
163 #~ raise Exception(cur.fetchall(), self.model.Role.get(1))
164 #~ raise Exception([repr(obj) for obj in self.model.ctx.current], self.model.ctx.current.identity_map.values())
165 #~ self.model.ctx.current.flush()
166 #~ raise Exception([u.name for u in self.model.Role.select(order_by=self.model.Role.c.name)])
167 #new_role.flush()
168
169 def group_create(self, group):
170 """
171 Add a new group to the system
172 """
173 if ' ' in group:
174 raise AuthKitError("Groups cannot contain space characters")
175 if self.group_exists(group):
176 raise AuthKitError("Group %r already exists"%group)
177 new_group = self.model.Group(group.lower())
178 self.session.save(new_group)
179 if self.autoflush:
180 self.session.flush()
181
182 # Delete Methods
183 def user_delete(self, username):
184 """
185 Remove the user with the specified username
186 """
187 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
188 if not user:
189 raise AuthKitNoSuchUserError("There is no such user %r"%username)
190 else:
191 self.session.delete(user)
192 if self.autoflush:
193 session.flush()
194
195 def role_delete(self, role):
196 """
197 Remove the role specified. Rasies an exception if the role is still in use.
198 To delete the role and remove it from all existing users use
199 ``role_delete_cascade()``
200 """
201 role = self.session.query(self.model.Role).filter_by(name=role.lower()).one()
202 if not role:
203 raise AuthKitNoRoleUserError("There is no such role %r"%role)
204 else:
205 self.session.delete(role)
206 if self.autoflush:
207 self.session.flush()
208
209 def group_delete(self, group):
210 """
211 Remove the group specified. Rasies an exception if the group is still in use.
212 To delete the group and remove it from all existing users use ``group_delete_cascade()``
213 """
214 group = self.session.query(self.model.Group).filter_by(name=group.lower()).one()
215 if not group:
216 raise AuthKitNoGroupUserError("There is no such group %r"%group)
217 else:
218 self.session.delete(group)
219 if self.autoflush:
220 session.flush()
221
222 #~ # Delete Cascade Methods
223 #~ def role_delete_cascade(self, role):
224 #~ """
225 #~ Remove the role specified and remove the role from any users who used it
226 #~ """
227 #~ raise AuthKitNotSupportedError(
228 #~ "The %s implementation of the User Management API doesn't support this method"%(
229 #~ self.__class__.__name__
230 #~ )
231 #~ )
232
233 #~ def group_delete_cascade(self, group):
234 #~ """
235 #~ Remove the group specified and remove the group from any users who used it
236 #~ """
237 #~ raise AuthKitNotSupportedError(
238 #~ "The %s implementation of the User Management API doesn't support this method"%(
239 #~ self.__class__.__name__
240 #~ )
241 #~ )
242
243 # Existence Methods
244 def user_exists(self, username):
245 """
246 Returns ``True`` if a user exists with the given username, ``False``
247 otherwise. Usernames are case insensitive.
248 """
249 user = self.session.query(self.model.User).filter_by(username=username.lower()).all()
250 if user:
251 return True
252 return False
253
254 def role_exists(self, role):
255 """
256 Returns ``True`` if the role exists, ``False`` otherwise. Roles are
257 case insensitive.
258 """
259 role = self.session.query(self.model.Role).filter_by(name=role.lower()).all()
260 if role:
261 return True
262 return False
263
264 def group_exists(self, group):
265 """
266 Returns ``True`` if the group exists, ``False`` otherwise. Groups
267 are case insensitive.
268 """
269 group = self.session.query(self.model.Group).filter_by(name=group.lower()).all()
270 if group:
271 return True
272 return False
273
274 # List Methods
275 def list_roles(self):
276 """
277 Returns a lowercase list of all roll names ordered alphabetically
278 """
279 return [r.name for r in self.session.query(self.model.Role).order_by(self.model.roles_table.c.name).all()]
280
281 def list_users(self):
282 """
283 Returns a lowecase list of all usernames ordered alphabetically
284 """
285 return [u.username for u in self.session.query(self.model.User).order_by(self.model.users_table.c.username).all()]
286
287 def list_groups(self):
288 """
289 Returns a lowercase list of all groups ordered alphabetically
290 """
291 return [g.name for g in self.session.query(self.model.Group).order_by(self.model.groups_table.c.name).all()]
292
293 # User Methods
294 def user(self, username):
295 """
296 Returns a dictionary in the following format:
297
298 .. code-block :: Python
299
300 {
301 'username': username,
302 'group': group,
303 'password': password,
304 'roles': [role1,role2,role3... etc]
305 }
306
307 The role names are ordered alphabetically
308 Raises an exception if the user doesn't exist.
309 """
310 if not self.user_exists(username.lower()):
311 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
312 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
313 roles = [r.name for r in user.roles]
314 roles.sort()
315 return {
316 'username': user.username,
317 'group': user.group and user.group.name or None,
318 'password': user.password,
319 'roles': roles
320 }
321
322 def user_roles(self, username):
323 """
324 Returns a list of all the role names for the given username ordered
325 alphabetically. Raises an exception if the username doesn't exist.
326 """
327 if not self.user_exists(username.lower()):
328 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
329 roles = [r.name for r in self.session.query(self.model.User).filter_by(username=username.lower()).one().roles]
330 roles.sort()
331 return roles
332
333 def user_group(self, username):
334 """
335 Returns the group associated with the user or ``None`` if no group is
336 associated. Raises an exception is the user doesn't exist.
337 """
338 if not self.user_exists(username.lower()):
339 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
340 return self.session.query(self.model.User).filter_by(username=username.lower()).one().group.name
341
342 def user_password(self, username):
343 """
344 Returns the password associated with the user or ``None`` if no
345 password exists. Raises an exception is the user doesn't exist.
346 """
347 if not self.user_exists(username.lower()):
348 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
349 return self.session.query(self.model.User).filter_by(username=username.lower()).one().password
350
351 def user_has_role(self, username, role):
352 """
353 Returns ``True`` if the user has the role specified, ``False``
354 otherwise. Raises an exception if the user doesn't exist.
355 """
356 if not self.user_exists(username.lower()):
357 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
358 if not self.role_exists(role.lower()):
359 raise AuthKitNoSuchRoleError("No such role %r"%role.lower())
360 for role_ in self.session.query(self.model.User).filter_by(username=username.lower()).one().roles:
361 if role_.name == role.lower():
362 return True
363 return False
364
365 def user_has_group(self, username, group):
366 """
367 Returns ``True`` if the user has the group specified, ``False``
368 otherwise. The value for ``group`` can be ``None`` to test that
369 the user doesn't belong to a group. Raises an exception if the
370 user doesn't exist.
371 """
372 if not self.user_exists(username.lower()):
373 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
374 if group is not None and not self.group_exists(group.lower()):
375 raise AuthKitNoSuchGroupError("No such group %r"%group.lower())
376 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
377 if user.group is None:
378 if group == None:
379 return True
380 else:
381 if group is not None and user.group.name == group.lower():
382 return True
383 return False
384
385 def user_has_password(self, username, password):
386 """
387 Returns ``True`` if the user has the password specified, ``False``
388 otherwise. Passwords are case sensitive. Raises an exception if the
389 user doesn't exist.
390 """
391 if not self.user_exists(username.lower()):
392 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
393 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
394 if user.password == self.encrypt(password):
395 return True
396 return False
397
398 def user_set_username(self, username, new_username):
399 """
400 Sets the user's username to the lowercase of new_username.
401 Raises an exception if the user doesn't exist or if there is already
402 a user with the username specified by ``new_username``.
403 """
404 if not self.user_exists(username.lower()):
405 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
406 if self.user_exists(new_username.lower()):
407 raise AuthKitError(
408 "A user with the username %r already exists"%username.lower()
409 )
410 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
411 user.username = new_username.lower()
412 if self.autoflush:
413 self.session.flush()
414
415 def user_set_password(self, username, new_password):
416 """
417 Sets the user's password. Should be plain text, will be encrypted using self.encrypt
418 Raises an exception if the user doesn't exist.
419 """
420 if not self.user_exists(username.lower()):
421 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
422 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
423 user.password = self.encrypt(new_password)
424 if self.autoflush:
425 self.session.flush()
426
427 def user_set_group(self, username, group, auto_add_group=False):
428 """
429 Sets the user's group to the lowercase of ``group`` or ``None``. If
430 the group doesn't exist and ``add_if_necessary`` is ``True`` the
431 group will also be added. Otherwise an ``AuthKitNoSuchGroupError``
432 will be raised. Raises an exception if the user doesn't exist.
433 """
434 if not self.user_exists(username.lower()):
435 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
436 if not self.group_exists(group.lower()):
437 if auto_add_group:
438 self.group_create(group.lower())
439 else:
440 raise AuthKitNoSuchGroupError("No such group %r"%group.lower())
441 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
442 user.group = self.session.query(self.model.Group).filter_by(name=group.lower()).one()
443 if self.autoflush:
444 self.session.flush()
445
446 def user_add_role(self, username, role, auto_add_role=False):
447 """
448 Sets the user's role to the lowercase of ``role``. If the role doesn't
449 exist and ``add_if_necessary`` is ``True`` the role will also be
450 added. Otherwise an ``AuthKitNoSuchRoleError`` will be raised. Raises
451 an exception if the user doesn't exist.
452 """
453 if not self.user_exists(username.lower()):
454 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
455 if not self.role_exists(role.lower()):
456 if auto_add_role:
457 self.role_create(role.lower())
458 else:
459 raise AuthKitNoSuchRoleError("No such role %r"%role.lower())
460 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
461 role = self.session.query(self.model.Role).filter_by(name=role.lower()).one()
462 user.roles.append(role)
463 if self.autoflush:
464 self.session.flush()
465
466 def user_remove_group(self, username):
467 """
468 Sets the group to ``None`` for the user specified by ``username``.
469 Raises an exception if the user doesn't exist.
470 """
471 if not self.user_exists(username.lower()):
472 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
473 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
474 user.group = None
475 if self.autoflush:
476 self.session.flush()
477
478 def user_remove_role(self, username, role):
479 """
480 Removes the role from the user specified by ``username``. Raises
481 an exception if the user doesn't exist.
482 """
483 if not self.user_exists(username.lower()):
484 raise AuthKitNoSuchUserError("No such user %r"%username.lower())
485 if not self.role_exists(role.lower()):
486 raise AuthKitNoSuchRoleError("No such role %r"%role.lower())
487 user = self.session.query(self.model.User).filter_by(username=username.lower()).one()
488 for role_ in user.roles:
489 if role_.name == role.lower():
490 user.roles.pop(user.roles.index(role_))
491 if self.autoflush:
492 self.session.flush()
493 return
494 raise AuthKitError(
495 "No role %r found for user %r"%(role.lower(), username.lower())
496 )