diff --git a/edbob/db/__init__.py b/edbob/db/__init__.py index e19e88f..8779a20 100644 --- a/edbob/db/__init__.py +++ b/edbob/db/__init__.py @@ -74,7 +74,7 @@ def init(config): from edbob.db import enum # from edbob.db.model import get_metadata # from edbob.db.mappers import make_mappers - # from edbob.db.extensions import extend_framework + from edbob.db.extensions import extend_framework # global inited, engines, engine, metadata global inited, engines, engine @@ -103,7 +103,7 @@ def init(config): # metadata = get_metadata(bind=engine) # make_mappers(metadata) - # extend_framework() + extend_framework() edbob.graft(edbob, edbob.db) # edbob.graft(edbob, classes) diff --git a/edbob/db/auth.py b/edbob/db/auth.py index f988c4e..eaa3664 100644 --- a/edbob/db/auth.py +++ b/edbob/db/auth.py @@ -32,7 +32,6 @@ from sqlalchemy.orm import object_session import edbob from edbob.db import needs_session -from edbob.db.classes import Permission, Role, User class BcryptAuthenticator(edbob.Object): @@ -55,13 +54,13 @@ def authenticate_user(session, username, password): returns the :class:`edbob.User` instance; otherwise returns ``None``. """ - user = session.query(User).filter_by(username=username).first() - if not user: - return None - auth = BcryptAuthenticator() - if not auth.authenticate_user(user, password): - return None - return user + q = session.query(edbob.User) + q = q.filter(edbob.User.username == username) + user = q.first() + if user: + auth = BcryptAuthenticator() + if auth.authenticate_user(user, password): + return user def administrator_role(session): @@ -103,3 +102,26 @@ def has_permission(obj, perm): if permission == perm: return True return False + + +def init_database(engine, session): + """ + Initialize the auth system within an ``edbob`` database. + """ + + # Create 'admin' user with full rights. + admin = edbob.User() + admin.username = 'admin' + set_user_password(admin, 'admin') + # admin.roles.append(administrator_role(session)) + session.add(admin) + session.flush() + + +def set_user_password(user, password): + """ + Sets the password for the given :class:`edbob.User` instance. + """ + + auth = BcryptAuthenticator() + auth.populate_user(user, password) diff --git a/edbob/db/extensions/__init__.py b/edbob/db/extensions/__init__.py index 29fea1a..e71e4e5 100644 --- a/edbob/db/extensions/__init__.py +++ b/edbob/db/extensions/__init__.py @@ -93,36 +93,43 @@ class Extension(edbob.Object): # """ # pass - def add_class(self, cls): + # def add_class(self, cls): + # """ + # Convenience method for use in :meth:`extend_classes()`. + # """ + + # from edbob.db import classes + + # name = cls.__name__ + # edbob.graft(classes, {name:cls}, name) + + # def extend_classes(self): + # """ + # Any extra classes provided by the extension should be added to the ORM + # whenever this method is called. + + # Note that the :meth:`add_class()` convenience method is designed to be + # used when adding classes. + # """ + # pass + + def extend_framework(self): """ - Convenience method for use in :meth:`extend_classes()`. + Extends the framework... """ - from edbob.db import classes + edbob.graft(edbob, self.get_model_module()) - name = cls.__name__ - edbob.graft(classes, {name:cls}, name) + # def extend_mappers(self, metadata): + # """ + # All SQLAlchemy mapping to be done by the extension should be done + # within this method. - def extend_classes(self): - """ - Any extra classes provided by the extension should be added to the ORM - whenever this method is called. - - Note that the :meth:`add_class()` convenience method is designed to be - used when adding classes. - """ - pass - - def extend_mappers(self, metadata): - """ - All SQLAlchemy mapping to be done by the extension should be done - within this method. - - Any extra classes the extension provides will typically be mapped here. - Any manipulation the extension needs to perform on the ``edbob`` core - ORM should be done here as well. - """ - pass + # Any extra classes the extension provides will typically be mapped here. + # Any manipulation the extension needs to perform on the ``edbob`` core + # ORM should be done here as well. + # """ + # pass def get_metadata(self, recurse=False): """ @@ -173,25 +180,25 @@ class Extension(edbob.Object): if isinstance(obj, type) and issubclass(obj, model.Base): obj.__table__.tometadata(metadata) - def remove_class(self, name): - """ - Convenience method for use in :meth:`restore_classes()`. - """ + # def remove_class(self, name): + # """ + # Convenience method for use in :meth:`restore_classes()`. + # """ - from edbob.db import classes + # from edbob.db import classes - if name in classes.__all__: - classes.__all__.remove(name) - if hasattr(classes, name): - del classes.__dict__[name] + # if name in classes.__all__: + # classes.__all__.remove(name) + # if hasattr(classes, name): + # del classes.__dict__[name] - def restore_classes(self): - """ - This method should remove any extra classes which were added within - :meth:`extend_classes()`. Note that there is a :meth:`remove_class()` - method for convenience in doing so. - """ - pass + # def restore_classes(self): + # """ + # This method should remove any extra classes which were added within + # :meth:`extend_classes()`. Note that there is a :meth:`remove_class()` + # method for convenience in doing so. + # """ + # pass def activate_extension(extension, engine=None): @@ -233,6 +240,7 @@ def activate_extension(extension, engine=None): # merge_extension_metadata(extension) # extension.extend_classes() # extension.extend_mappers(Base.metadata) + extension.extend_framework() # Add extension to in-memory active extensions tracker. active_extensions(engine).append(extension.name) @@ -318,23 +326,24 @@ def extend_framework(): session = Session() try: - active_extensions = session.query(ActiveExtension).all() + active = session.query(ActiveExtension).all() except sqlalchemy.exc.ProgrammingError: session.close() return extensions = {} - for ext in active_extensions: + for ext in active: extensions[ext.name] = get_extension(ext.name) session.close() for name in sorted(extensions, extension_sorter(extensions)): - ext = extensions[name] log.info("Applying active extension: %s" % name) - merge_extension_metadata(ext) - ext.extend_classes() - ext.extend_mappers(rattail.metadata) - active_extensions[name] = ext + ext = extensions[name] + # merge_extension_metadata(ext) + # ext.extend_classes() + # ext.extend_mappers(rattail.metadata) + ext.extend_framework() + _active_extensions[name] = ext def extension_active(extension, engine=None): diff --git a/edbob/db/extensions/auth/__init__.py b/edbob/db/extensions/auth/__init__.py index c49f400..f6e2674 100644 --- a/edbob/db/extensions/auth/__init__.py +++ b/edbob/db/extensions/auth/__init__.py @@ -26,10 +26,11 @@ ``edbob.db.extensions.auth`` -- 'auth' Extension """ -from sqlalchemy import MetaData - from edbob.db.extensions import Extension +from edbob.db.extensions.auth.model import * +from edbob.db.extensions.auth.model import __all__ + class AuthExtension(Extension): diff --git a/edbob/db/extensions/auth/model.py b/edbob/db/extensions/auth/model.py index 9491908..7bf25fb 100644 --- a/edbob/db/extensions/auth/model.py +++ b/edbob/db/extensions/auth/model.py @@ -28,9 +28,10 @@ from sqlalchemy import * from sqlalchemy.orm import relationship +from sqlalchemy.ext.associationproxy import association_proxy import edbob -from edbob.db.model import Base +from edbob.db.model import Base, uuid_column __all__ = ['Person', 'User'] @@ -52,7 +53,7 @@ class Person(Base): __tablename__ = 'people' - uuid = Column(String(32), primary_key=True, default=edbob.get_uuid) + uuid = uuid_column() first_name = Column(String(50)) last_name = Column(String(50)) display_name = Column(String(100), default=get_person_display_name) @@ -72,11 +73,14 @@ class User(Base): __tablename__ = 'users' - uuid = Column(String(32), primary_key=True, default=edbob.get_uuid) + uuid = uuid_column() username = Column(String(25), nullable=False, unique=True) + password = Column(String(60)) + salt = Column(String(29)) person_uuid = Column(String(32), ForeignKey('people.uuid')) person = relationship(Person, backref='user') + # display_name = association_proxy('person', 'display_name') # roles = association_proxy('_roles', 'role', # creator=lambda x: UserRole(role=x), @@ -87,3 +91,13 @@ class User(Base): def __str__(self): return str(self.username or '') + + @property + def display_name(self): + """ + Returns the user's ``person.display_name``, if present, otherwise the + ``username``. + """ + if self.person and self.person.display_name: + return self.person.display_name + return self.username diff --git a/edbob/pyramid/static/css/edbob.css b/edbob/pyramid/static/css/edbob.css index c0b6261..2e98bf3 100644 --- a/edbob/pyramid/static/css/edbob.css +++ b/edbob/pyramid/static/css/edbob.css @@ -77,7 +77,7 @@ table.wrapper td.right { } #login { - margin: 8px auto auto 20px; + margin: 8px 20px auto auto; } #user-menu { diff --git a/edbob/pyramid/subscribers.py b/edbob/pyramid/subscribers.py index 13315e8..0bf8527 100644 --- a/edbob/pyramid/subscribers.py +++ b/edbob/pyramid/subscribers.py @@ -32,6 +32,7 @@ from pyramid.security import authenticated_userid import edbob from edbob.db.auth import has_permission from edbob.pyramid import helpers +from edbob.pyramid import Session def before_render(event): @@ -62,9 +63,9 @@ def context_found(event): def has_perm_func(request): def has_perm(perm): - if not request.current_user: + if not request.user: return False - return has_permission(request.current_user, perm) + return has_permission(request.user, perm) return has_perm request = event.request @@ -73,7 +74,7 @@ def context_found(event): uuid = authenticated_userid(request) if uuid: - request.user = get_session().query(rattail.User).get(uuid) + request.user = Session.query(edbob.User).get(uuid) def includeme(config): diff --git a/edbob/pyramid/templates/edbob/base.mako b/edbob/pyramid/templates/edbob/base.mako index 4882d2d..83adf93 100644 --- a/edbob/pyramid/templates/edbob/base.mako +++ b/edbob/pyramid/templates/edbob/base.mako @@ -30,13 +30,12 @@