vigiboard / dashboard / model / auth.py @ 70c5f034
History | View | Annotate | Download (6.63 KB)
1 | 805cc54a | Thomas ANDREJAK | # -*- coding: utf-8 -*-
|
---|---|---|---|
2 | """
|
||
3 | Auth* related model.
|
||
4 |
|
||
5 | This is where the models used by :mod:`repoze.who` and :mod:`repoze.what` are
|
||
6 | defined.
|
||
7 |
|
||
8 | It's perfectly fine to re-use this definition in the dashboard application,
|
||
9 | though.
|
||
10 |
|
||
11 | """
|
||
12 | import os |
||
13 | from datetime import datetime |
||
14 | import sys |
||
15 | try:
|
||
16 | from hashlib import sha1 |
||
17 | except ImportError: |
||
18 | sys.exit('ImportError: No module named hashlib\n'
|
||
19 | 'If you are on python2.4 this library is not part of python. '
|
||
20 | 'Please install it. Example: easy_install hashlib')
|
||
21 | |||
22 | from sqlalchemy import Table, ForeignKey, Column |
||
23 | from sqlalchemy.types import Unicode, Integer, DateTime |
||
24 | from sqlalchemy.orm import relation, synonym |
||
25 | |||
26 | from dashboard.model import DeclarativeBase, metadata, DBSession |
||
27 | |||
28 | __all__ = ['User', 'Group', 'Permission'] |
||
29 | |||
30 | |||
31 | #{ Association tables
|
||
32 | |||
33 | |||
34 | # This is the association table for the many-to-many relationship between
|
||
35 | # groups and permissions. This is required by repoze.what.
|
||
36 | group_permission_table = Table('tg_group_permission', metadata,
|
||
37 | Column('group_id', Integer, ForeignKey('tg_group.group_id', |
||
38 | onupdate="CASCADE", ondelete="CASCADE")), |
||
39 | Column('permission_id', Integer, ForeignKey('tg_permission.permission_id', |
||
40 | onupdate="CASCADE", ondelete="CASCADE")) |
||
41 | ) |
||
42 | |||
43 | # This is the association table for the many-to-many relationship between
|
||
44 | # groups and members - this is, the memberships. It's required by repoze.what.
|
||
45 | user_group_table = Table('tg_user_group', metadata,
|
||
46 | Column('user_id', Integer, ForeignKey('tg_user.user_id', |
||
47 | onupdate="CASCADE", ondelete="CASCADE")), |
||
48 | Column('group_id', Integer, ForeignKey('tg_group.group_id', |
||
49 | onupdate="CASCADE", ondelete="CASCADE")) |
||
50 | ) |
||
51 | |||
52 | |||
53 | #{ The auth* model itself
|
||
54 | |||
55 | |||
56 | class Group(DeclarativeBase): |
||
57 | """
|
||
58 | Group definition for :mod:`repoze.what`.
|
||
59 |
|
||
60 | Only the ``group_name`` column is required by :mod:`repoze.what`.
|
||
61 |
|
||
62 | """
|
||
63 | |||
64 | __tablename__ = 'tg_group'
|
||
65 | |||
66 | #{ Columns
|
||
67 | |||
68 | group_id = Column(Integer, autoincrement=True, primary_key=True) |
||
69 | |||
70 | group_name = Column(Unicode(16), unique=True, nullable=False) |
||
71 | |||
72 | display_name = Column(Unicode(255))
|
||
73 | |||
74 | created = Column(DateTime, default=datetime.now) |
||
75 | |||
76 | #{ Relations
|
||
77 | |||
78 | users = relation('User', secondary=user_group_table, backref='groups') |
||
79 | |||
80 | #{ Special methods
|
||
81 | |||
82 | def __repr__(self): |
||
83 | return '<Group: name=%s>' % self.group_name |
||
84 | |||
85 | def __unicode__(self): |
||
86 | return self.group_name |
||
87 | |||
88 | #}
|
||
89 | |||
90 | |||
91 | # The 'info' argument we're passing to the email_address and password columns
|
||
92 | # contain metadata that Rum (http://python-rum.org/) can use generate an
|
||
93 | # admin interface for your models.
|
||
94 | class User(DeclarativeBase): |
||
95 | """
|
||
96 | User definition.
|
||
97 |
|
||
98 | This is the user definition used by :mod:`repoze.who`, which requires at
|
||
99 | least the ``user_name`` column.
|
||
100 |
|
||
101 | """
|
||
102 | __tablename__ = 'tg_user'
|
||
103 | |||
104 | #{ Columns
|
||
105 | |||
106 | user_id = Column(Integer, autoincrement=True, primary_key=True) |
||
107 | |||
108 | user_name = Column(Unicode(16), unique=True, nullable=False) |
||
109 | |||
110 | email_address = Column(Unicode(255), unique=True, nullable=False, |
||
111 | info={'rum': {'field':'Email'}}) |
||
112 | |||
113 | display_name = Column(Unicode(255))
|
||
114 | |||
115 | _password = Column('password', Unicode(80), |
||
116 | info={'rum': {'field':'Password'}}) |
||
117 | |||
118 | created = Column(DateTime, default=datetime.now) |
||
119 | |||
120 | #{ Special methods
|
||
121 | |||
122 | def __repr__(self): |
||
123 | return '<User: email="%s", display name="%s">' % ( |
||
124 | self.email_address, self.display_name) |
||
125 | |||
126 | def __unicode__(self): |
||
127 | return self.display_name or self.user_name |
||
128 | |||
129 | #{ Getters and setters
|
||
130 | |||
131 | @property
|
||
132 | def permissions(self): |
||
133 | """Return a set of strings for the permissions granted."""
|
||
134 | perms = set()
|
||
135 | for g in self.groups: |
||
136 | perms = perms | set(g.permissions)
|
||
137 | return perms
|
||
138 | |||
139 | @classmethod
|
||
140 | def by_email_address(cls, email): |
||
141 | """Return the user object whose email address is ``email``."""
|
||
142 | return DBSession.query(cls).filter(cls.email_address==email).first()
|
||
143 | |||
144 | @classmethod
|
||
145 | def by_user_name(cls, username): |
||
146 | """Return the user object whose user name is ``username``."""
|
||
147 | return DBSession.query(cls).filter(cls.user_name==username).first()
|
||
148 | |||
149 | def _set_password(self, password): |
||
150 | """Hash ``password`` on the fly and store its hashed version."""
|
||
151 | hashed_password = password |
||
152 | |||
153 | if isinstance(password, unicode): |
||
154 | password_8bit = password.encode('UTF-8')
|
||
155 | else:
|
||
156 | password_8bit = password |
||
157 | |||
158 | salt = sha1() |
||
159 | salt.update(os.urandom(60))
|
||
160 | hash = sha1() |
||
161 | hash.update(password_8bit + salt.hexdigest())
|
||
162 | hashed_password = salt.hexdigest() + hash.hexdigest()
|
||
163 | |||
164 | # Make sure the hashed password is an UTF-8 object at the end of the
|
||
165 | # process because SQLAlchemy _wants_ a unicode object for Unicode
|
||
166 | # columns
|
||
167 | if not isinstance(hashed_password, unicode): |
||
168 | hashed_password = hashed_password.decode('UTF-8')
|
||
169 | |||
170 | self._password = hashed_password
|
||
171 | |||
172 | def _get_password(self): |
||
173 | """Return the hashed version of the password."""
|
||
174 | return self._password |
||
175 | |||
176 | password = synonym('_password', descriptor=property(_get_password, |
||
177 | _set_password)) |
||
178 | |||
179 | #}
|
||
180 | |||
181 | def validate_password(self, password): |
||
182 | """
|
||
183 | Check the password against existing credentials.
|
||
184 |
|
||
185 | :param password: the password that was provided by the user to
|
||
186 | try and authenticate. This is the clear text version that we will
|
||
187 | need to match against the hashed one in the database.
|
||
188 | :type password: unicode object.
|
||
189 | :return: Whether the password is valid.
|
||
190 | :rtype: bool
|
||
191 |
|
||
192 | """
|
||
193 | hashed_pass = sha1() |
||
194 | hashed_pass.update(password + self.password[:40]) |
||
195 | return self.password[40:] == hashed_pass.hexdigest() |
||
196 | |||
197 | |||
198 | class Permission(DeclarativeBase): |
||
199 | """
|
||
200 | Permission definition for :mod:`repoze.what`.
|
||
201 |
|
||
202 | Only the ``permission_name`` column is required by :mod:`repoze.what`.
|
||
203 |
|
||
204 | """
|
||
205 | |||
206 | __tablename__ = 'tg_permission'
|
||
207 | |||
208 | #{ Columns
|
||
209 | |||
210 | permission_id = Column(Integer, autoincrement=True, primary_key=True) |
||
211 | |||
212 | permission_name = Column(Unicode(16), unique=True, nullable=False) |
||
213 | |||
214 | description = Column(Unicode(255))
|
||
215 | |||
216 | #{ Relations
|
||
217 | |||
218 | groups = relation(Group, secondary=group_permission_table, |
||
219 | backref='permissions')
|
||
220 | |||
221 | #{ Special methods
|
||
222 | |||
223 | def __repr__(self): |
||
224 | return '<Permission: name=%s>' % self.permission_name |
||
225 | |||
226 | def __unicode__(self): |
||
227 | return self.permission_name |
||
228 | |||
229 | #}
|
||
230 | |||
231 | |||
232 | #} |