1 | import sys |
---|
2 | try: |
---|
3 | from hashlib import sha1 |
---|
4 | except ImportError: |
---|
5 | sys.exit('ImportError: no module named hashlib\nIf you are on python2.4 this library is not part of python. Please install it. Example: easy_install hashlib') |
---|
6 | import os |
---|
7 | from datetime import datetime |
---|
8 | |
---|
9 | from sqlalchemy import Table, ForeignKey, Column |
---|
10 | from sqlalchemy.types import String, Unicode, UnicodeText, Integer, DateTime, \ |
---|
11 | Boolean, Float |
---|
12 | from sqlalchemy.orm import relation, backref, synonym |
---|
13 | |
---|
14 | from sipbmp3web.model import DeclarativeBase, metadata, DBSession |
---|
15 | |
---|
16 | |
---|
17 | # This is the association table for the many-to-many relationship between |
---|
18 | # groups and permissions. |
---|
19 | group_permission_table = Table('tg_group_permission', metadata, |
---|
20 | Column('group_id', Integer, ForeignKey('tg_group.group_id', |
---|
21 | onupdate="CASCADE", ondelete="CASCADE")), |
---|
22 | Column('permission_id', Integer, ForeignKey('tg_permission.permission_id', |
---|
23 | onupdate="CASCADE", ondelete="CASCADE")) |
---|
24 | ) |
---|
25 | |
---|
26 | # This is the association table for the many-to-many relationship between |
---|
27 | # groups and members - this is, the memberships. |
---|
28 | user_group_table = Table('tg_user_group', metadata, |
---|
29 | Column('user_id', Integer, ForeignKey('tg_user.user_id', |
---|
30 | onupdate="CASCADE", ondelete="CASCADE")), |
---|
31 | Column('group_id', Integer, ForeignKey('tg_group.group_id', |
---|
32 | onupdate="CASCADE", ondelete="CASCADE")) |
---|
33 | ) |
---|
34 | |
---|
35 | # auth model |
---|
36 | |
---|
37 | class Group(DeclarativeBase): |
---|
38 | """An ultra-simple group definition. |
---|
39 | """ |
---|
40 | __tablename__ = 'tg_group' |
---|
41 | |
---|
42 | group_id = Column(Integer, autoincrement=True, primary_key=True) |
---|
43 | group_name = Column(Unicode(16), unique=True, nullable=False) |
---|
44 | display_name = Column(Unicode(255)) |
---|
45 | created = Column(DateTime, default=datetime.now) |
---|
46 | users = relation('User', secondary=user_group_table, backref='groups') |
---|
47 | |
---|
48 | def __repr__(self): |
---|
49 | return '<Group: name=%s>' % self.group_name |
---|
50 | |
---|
51 | def __unicode__(self): |
---|
52 | return self.group_name |
---|
53 | |
---|
54 | # |
---|
55 | # The 'info' argument we're passing to the email_address and password columns |
---|
56 | # contain metadata that Rum (http://python-rum.org/) can use generate an |
---|
57 | # admin interface for your models. |
---|
58 | # |
---|
59 | class User(DeclarativeBase): |
---|
60 | """Reasonably basic User definition. Probably would want additional |
---|
61 | attributes. |
---|
62 | """ |
---|
63 | __tablename__ = 'tg_user' |
---|
64 | |
---|
65 | user_id = Column(Integer, autoincrement=True, primary_key=True) |
---|
66 | user_name = Column(Unicode(16), unique=True, nullable=False) |
---|
67 | email_address = Column(Unicode(255), unique=True, nullable=False, |
---|
68 | info={'rum': {'field':'Email'}}) |
---|
69 | display_name = Column(Unicode(255)) |
---|
70 | _password = Column('password', Unicode(80), |
---|
71 | info={'rum': {'field':'Password'}}) |
---|
72 | created = Column(DateTime, default=datetime.now) |
---|
73 | |
---|
74 | def __repr__(self): |
---|
75 | return '<User: email="%s", display name="%s">' % ( |
---|
76 | self.email_address, self.display_name) |
---|
77 | |
---|
78 | def __unicode__(self): |
---|
79 | return self.display_name or self.user_name |
---|
80 | |
---|
81 | @property |
---|
82 | def permissions(self): |
---|
83 | perms = set() |
---|
84 | for g in self.groups: |
---|
85 | perms = perms | set(g.permissions) |
---|
86 | return perms |
---|
87 | |
---|
88 | @classmethod |
---|
89 | def by_email_address(cls, email): |
---|
90 | """A class method that can be used to search users |
---|
91 | based on their email addresses since it is unique. |
---|
92 | """ |
---|
93 | return DBSession.query(cls).filter(cls.email_address==email).first() |
---|
94 | |
---|
95 | @classmethod |
---|
96 | def by_user_name(cls, username): |
---|
97 | """A class method that permits to search users |
---|
98 | based on their user_name attribute. |
---|
99 | """ |
---|
100 | return DBSession.query(cls).filter(cls.user_name==username).first() |
---|
101 | |
---|
102 | |
---|
103 | def _set_password(self, password): |
---|
104 | """Hash password on the fly.""" |
---|
105 | hashed_password = password |
---|
106 | |
---|
107 | if isinstance(password, unicode): |
---|
108 | password_8bit = password.encode('UTF-8') |
---|
109 | else: |
---|
110 | password_8bit = password |
---|
111 | |
---|
112 | salt = sha1() |
---|
113 | salt.update(os.urandom(60)) |
---|
114 | hash = sha1() |
---|
115 | hash.update(password_8bit + salt.hexdigest()) |
---|
116 | hashed_password = salt.hexdigest() + hash.hexdigest() |
---|
117 | |
---|
118 | # make sure the hased password is an UTF-8 object at the end of the |
---|
119 | # process because SQLAlchemy _wants_ a unicode object for Unicode columns |
---|
120 | if not isinstance(hashed_password, unicode): |
---|
121 | hashed_password = hashed_password.decode('UTF-8') |
---|
122 | |
---|
123 | self._password = hashed_password |
---|
124 | |
---|
125 | def _get_password(self): |
---|
126 | """returns password |
---|
127 | """ |
---|
128 | return self._password |
---|
129 | |
---|
130 | password = synonym('_password', descriptor=property(_get_password, |
---|
131 | _set_password)) |
---|
132 | |
---|
133 | def validate_password(self, password): |
---|
134 | """Check the password against existing credentials. |
---|
135 | |
---|
136 | :param password: the password that was provided by the user to |
---|
137 | try and authenticate. This is the clear text version that we will |
---|
138 | need to match against the hashed one in the database. |
---|
139 | :type password: unicode object. |
---|
140 | :return: Whether the password is valid. |
---|
141 | :rtype: bool |
---|
142 | |
---|
143 | """ |
---|
144 | hashed_pass = sha1() |
---|
145 | hashed_pass.update(password + self.password[:40]) |
---|
146 | return self.password[40:] == hashed_pass.hexdigest() |
---|
147 | |
---|
148 | |
---|
149 | class Permission(DeclarativeBase): |
---|
150 | """A relationship that determines what each Group can do |
---|
151 | """ |
---|
152 | __tablename__ = 'tg_permission' |
---|
153 | |
---|
154 | permission_id = Column(Integer, autoincrement=True, primary_key=True) |
---|
155 | permission_name = Column(Unicode(16), unique=True, nullable=False) |
---|
156 | description = Column(Unicode(255)) |
---|
157 | groups = relation(Group, secondary=group_permission_table, |
---|
158 | backref='permissions') |
---|
159 | |
---|
160 | def __unicode__(self): |
---|
161 | return self.permission_name |
---|