Commits

Atsushi Odagiri committed f67e333

add auth models.

  • Participants
  • Parent commits bad6a77

Comments (0)

Files changed (5)

 use = egg:alchemyadmin#demo
 sqlalchemy.url = sqlite:///%(here)s/demo.sqlite
 sqlalchemy.echo = true
+admin.use_auth_model = true
+
 
 [server:main]
 use = egg:Paste#http

File src/alchemyadmin/__init__.py

         for m in models:
             self.models[m.__name__.lower()] = m
 
+        if settings['admin.use_auth_model']:
+            from models.auth import User, Group, Permission
+            for m in User, Group, Permission:
+                self.models['auth_' + m.__name__.lower()] = m
+
         urls = Map([
                 Rule('/', endpoint='index'),
                 ])

File src/alchemyadmin/demo/__init__.py

     
 
 def make_app(global_conf, **app_conf):
+
     from alchemyadmin import Application
     conf = global_conf.copy()
     conf.update(app_conf)
     engine = engine_from_config(conf)
+    import alchemyadmin.models.auth
+
+    alchemyadmin.models.auth.metadata.bind = engine
+    alchemyadmin.models.auth.metadata.create_all()
+
     metadata.bind = engine
     metadata.drop_all()
     metadata.create_all()

File src/alchemyadmin/models/__init__.py

+#

File src/alchemyadmin/models/auth.py

+import os
+from datetime import datetime
+import sys
+try:
+    from hashlib import sha1
+except ImportError:
+    sys.exit('ImportError: No module named hashlib\n'
+             'If you are on python2.4 this library is not part of python. '
+             'Please install it. Example: easy_install hashlib')
+
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.types import Unicode, Integer, DateTime
+from sqlalchemy.orm import relation, synonym
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+metadata = Base.metadata
+
+
+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"))
+)
+
+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"))
+)
+
+class Group(Base):
+    __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 '<Group: name=%s>' % self.group_name
+    
+    def __unicode__(self):
+        return self.group_name
+
+class User(Base):
+    """
+    User definition.
+    
+    This is the user definition used by :mod:`repoze.who`, which requires at
+    least the ``user_name`` column.
+    
+    """
+    __tablename__ = 'tg_user'
+    
+    #{ Columns
+
+    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)
+    
+    #{ Special methods
+
+    def __repr__(self):
+        return '<User: email="%s", display name="%s">' % (
+                self.email_address, self.display_name)
+
+    def __unicode__(self):
+        return self.display_name or self.user_name
+    
+    #{ Getters and setters
+
+    @property
+    def permissions(self):
+        """Return a set of strings for the permissions granted."""
+        perms = set()
+        for g in self.groups:
+            perms = perms | set(g.permissions)
+        return perms
+
+    @classmethod
+    def by_email_address(cls, email):
+        """Return the user object whose email address is ``email``."""
+        return DBSession.query(cls).filter(cls.email_address==email).first()
+
+    @classmethod
+    def by_user_name(cls, username):
+        """Return the user object whose user name is ``username``."""
+        return DBSession.query(cls).filter(cls.user_name==username).first()
+
+    def _set_password(self, password):
+        """Hash ``password`` on the fly and store its hashed version."""
+        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 hashed 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):
+        """Return the hashed version of the 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(Base):
+    """
+    Permission definition for :mod:`repoze.what`.
+    
+    Only the ``permission_name`` column is required by :mod:`repoze.what`.
+    
+    """
+    
+    __tablename__ = 'tg_permission'
+    
+    #{ Columns
+
+    permission_id = Column(Integer, autoincrement=True, primary_key=True)
+    
+    permission_name = Column(Unicode(16), unique=True, nullable=False)
+    
+    description = Column(Unicode(255))
+    
+    #{ Relations
+    
+    groups = relation(Group, secondary=group_permission_table,
+                      backref='permissions')
+    
+    #{ Special methods
+    
+    def __repr__(self):
+        return '<Permission: name=%s>' % self.permission_name
+
+    def __unicode__(self):
+        return self.permission_name
+    
+    #}
+