import sys try: from hashlib import sha1 except ImportError: 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') import os from datetime import datetime from sqlalchemy import Table, ForeignKey, Column from sqlalchemy.types import String, Unicode, UnicodeText, Integer, DateTime, \ Boolean, Float from sqlalchemy.orm import relation, backref, synonym from sipbmp3web.model import DeclarativeBase, metadata, DBSession # This is the association table for the many-to-many relationship between # groups and permissions. group_permission_table = Table('tg_group_permission', metadata, Column('group_id', Integer, ForeignKey('tg_group.group_id', onupdate="CASCADE", ondelete="CASCADE")), Column('permission_id', Integer, ForeignKey('tg_permission.permission_id', onupdate="CASCADE", ondelete="CASCADE")) ) # This is the association table for the many-to-many relationship between # groups and members - this is, the memberships. user_group_table = Table('tg_user_group', metadata, Column('user_id', Integer, ForeignKey('tg_user.user_id', onupdate="CASCADE", ondelete="CASCADE")), Column('group_id', Integer, ForeignKey('tg_group.group_id', onupdate="CASCADE", ondelete="CASCADE")) ) # auth model class Group(DeclarativeBase): """An ultra-simple group definition. """ __tablename__ = 'tg_group' group_id = Column(Integer, autoincrement=True, primary_key=True) group_name = Column(Unicode(16), unique=True, nullable=False) display_name = Column(Unicode(255)) created = Column(DateTime, default=datetime.now) users = relation('User', secondary=user_group_table, backref='groups') def __repr__(self): return '' % self.group_name def __unicode__(self): return self.group_name # # The 'info' argument we're passing to the email_address and password columns # contain metadata that Rum (http://python-rum.org/) can use generate an # admin interface for your models. # class User(DeclarativeBase): """Reasonably basic User definition. Probably would want additional attributes. """ __tablename__ = 'tg_user' user_id = Column(Integer, autoincrement=True, primary_key=True) user_name = Column(Unicode(16), unique=True, nullable=False) email_address = Column(Unicode(255), unique=True, nullable=False, info={'rum': {'field':'Email'}}) display_name = Column(Unicode(255)) _password = Column('password', Unicode(80), info={'rum': {'field':'Password'}}) created = Column(DateTime, default=datetime.now) def __repr__(self): return '' % ( self.email_address, self.display_name) def __unicode__(self): return self.display_name or self.user_name @property def permissions(self): perms = set() for g in self.groups: perms = perms | set(g.permissions) return perms @classmethod def by_email_address(cls, email): """A class method that can be used to search users based on their email addresses since it is unique. """ return DBSession.query(cls).filter(cls.email_address==email).first() @classmethod def by_user_name(cls, username): """A class method that permits to search users based on their user_name attribute. """ return DBSession.query(cls).filter(cls.user_name==username).first() def _set_password(self, password): """Hash password on the fly.""" hashed_password = password if isinstance(password, unicode): password_8bit = password.encode('UTF-8') else: password_8bit = password salt = sha1() salt.update(os.urandom(60)) hash = sha1() hash.update(password_8bit + salt.hexdigest()) hashed_password = salt.hexdigest() + hash.hexdigest() # make sure the hased password is an UTF-8 object at the end of the # process because SQLAlchemy _wants_ a unicode object for Unicode columns if not isinstance(hashed_password, unicode): hashed_password = hashed_password.decode('UTF-8') self._password = hashed_password def _get_password(self): """returns password """ return self._password password = synonym('_password', descriptor=property(_get_password, _set_password)) def validate_password(self, password): """Check the password against existing credentials. :param password: the password that was provided by the user to try and authenticate. This is the clear text version that we will need to match against the hashed one in the database. :type password: unicode object. :return: Whether the password is valid. :rtype: bool """ hashed_pass = sha1() hashed_pass.update(password + self.password[:40]) return self.password[40:] == hashed_pass.hexdigest() class Permission(DeclarativeBase): """A relationship that determines what each Group can do """ __tablename__ = 'tg_permission' permission_id = Column(Integer, autoincrement=True, primary_key=True) permission_name = Column(Unicode(16), unique=True, nullable=False) description = Column(Unicode(255)) groups = relation(Group, secondary=group_permission_table, backref='permissions') def __unicode__(self): return self.permission_name