Git Product home page Git Product logo

horus's Introduction

Introduction to horus

horus provides generic user registration for the Pyramid web framework, if your web app uses sqlalchemy.

It is a pluggable web application that provides user registration, login, logout and change password functionality. horus follows a policy of minimal interference, so your app can mostly keep its existing models.

Minimal integration

  • Create a virtualenv and activate it. Install pyramid and create your pyramid project.
  • Edit your setup.py to add "horus" to the dependencies in the install_requires list.
  • Run python setup.py develop on your project to install all dependencies into your virtualenv.
  • Create your SQLAlchemy declarative initialization.
  • Create models inheriting from horus' abstract models. Find an example in the file horus/tests/models.py.

    Alternatively, use the horus scaffold script:

    horus_scaffold development.ini > your_app/auth_models.py

    Then all you need to do is tell the class where to find your declarative base you and are good to go!

  • Include horus inside your main() function like this:

    # Tell horus which SQLAlchemy scoped session to use:
    from hem.interfaces import IDBSession
    registry = config.registry
    registry.registerUtility(my_sqlalchemy_scoped_session, IDBSession)
    
    config.include('horus')
    config.scan_horus(auth_models_package_or_module)

    With the above config.scan_horus() call, you need to edit your .ini configuration file and tell horus which model classes to use like this:

    horus.user_class = my_app.models:User horus.activation_class = my_app.models:Activation

    As an alternative to config.scan_horus() plus that configuration, you can register the classes explicitly if you so prefer. This must be done above config.include('horus'):

    # Tell horus which models to use:
    from horus.interfaces import IUserClass, IActivationClass
    registry.registerUtility(User, IUserClass)
    registry.registerUtility(Activation, IActivationClass)
    
    config.include('horus')
  • Configure horus.login_redirect and horus.logout_redirect (in your .ini configuration file) to set the redirection routes.
  • If you haven't done so yet, configure an HTTP session factory according to the Sessions chapter of the Pyramid documentation.
  • Create your database and tables. Maybe even an initial user.
  • Be sure to pass an authentication_policy argument in the config = Configurator(...) call. Refer to Pyramid docs for details.
  • By now the login form should appear at /login, but /register shouldn't.
  • Include the package pyramid_mailer for the validation e-mail and "forgot password" e-mail:

    config.include('pyramid_mailer')
  • The /register form should appear, though ugly. Now you have a choice regarding user activation by email:
    • You may just disable it by setting, in your .ini file:

      horus.require_activation = False
    • Otherwise, configure pyramid_mailer according to its documentation and test the registration page.
  • If you are using pyramid_tm or the ZopeTransactionManager, your minimal integration is done. (The pages are ugly, but working. Keep reading...)

Need to session.commit()?

horus does not require pyramid_tm or the ZopeTransactionManager with your session but if you do not use them you do have to take one extra step. We don't commit transactions for you because that just wouldn't be nice!

All you have to do is subscribe to the extension events and commit the session yourself. This also gives you the chance to do some extra processing:

from horus.events import (
    PasswordResetEvent, NewRegistrationEvent,
    RegistrationActivatedEvent, ProfileUpdatedEvent)

def handle_request(event):
    request = event.request
    session = request.registry.getUtility(IDBSession)
    session.commit()

self.config.add_subscriber(handle_request, PasswordResetEvent)
self.config.add_subscriber(handle_request, NewRegistrationEvent)
self.config.add_subscriber(handle_request, RegistrationActivatedEvent)
self.config.add_subscriber(handle_request, ProfileUpdatedEvent)

Changing the forms

If you would like to modify any of the forms, you just need to register the new deform class to be used.

The interfaces you have available to override from horus.interfaces are:

  • IHorusLoginForm
  • IHorusRegisterForm
  • IHorusForgotPasswordForm
  • IHorusResetPasswordForm
  • IHorusProfileForm

This is how you would do it (MyForm being a custom deform Form class):

config.registry.registerUtility(MyForm, IHorusLoginForm)

Changing the templates

If you would like to substitute the templates you can use pyramid's override_asset:

config.override_asset(to_override='horus:templates/template.mako',
    override_with='your_package:templates/anothertemplate.mako')

The templates you have available to override are:

  • login.mako
  • register.mako
  • forgot_password.mako
  • reset_password.mako
  • profile.mako

If you would like to override the templates with Jinja2, or any other templating language, just override the view configuration:

config.add_view('horus.views.AuthController', attr='login',
    route_name='login', renderer='yourapp:templates/login.jinja2')
config.add_view('horus.views.ForgotPasswordController',
    attr='forgot_password', route_name='forgot_password',
    renderer='yourapp:templates/forgot_password.jinja2')
config.add_view('horus.views.ForgotPasswordController',
    attr='reset_password', route_name='reset_password',
    renderer='yourapp:templates/reset_password.jinja2')
config.add_view('horus.views.RegisterController', attr='register',
    route_name='register', renderer='yourapp:templates/register.jinja2')
config.add_view('horus.views.ProfileController', attr='profile',
    route_name='profile', renderer='yourapp:templates/profile.jinja2')

Changing strings

Take a look at this class. This is where we store all the strings in horus. If you'd like to change one or two messages, simply subclass this, then do:

from horus.interfaces import IUIStrings
config.registry.registerUtility(MyStringsClass, IUIStrings)

Changing the primary key column name

If you wish to override the primary key attribute name, you can do so by creating a new mixin class:

class NullPkMixin(Base):
    abstract = True
    _idAttribute = 'pk'

    @declared_attr
    def pk(self):
        return Base.pk

    @declared_attr
    def id(self):
        return None

class User(NullPkMixin, UserMixin):
    pass

horus development

See https://github.com/eventray/horus

If you would like to help make any changes to horus, you can run its unit tests with py.test:

py.test

To check test coverage:

py.test --cov-report term-missing --cov horus

The tests can also be run in parallel:

py.test -n4

We are using this build server: http://travis-ci.org/#!/eventray/horus

horus's People

Contributors

aharonp avatar ccomb avatar dobrite avatar jkoelker avatar nandoflorestan avatar patreeceeo avatar seut avatar sontek avatar tilgovi avatar vigrond avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

horus's Issues

Finish "admin" functionality

I see in the code, there are already views defined for an admin panel, but there are no tests for it. Activating it in my application, I get an error on json() when trying to edit a user from the list.

What is the status on this feature? Is there a roadmap for the functionality?

I will have to build out this functionality for my application, including the feature requested in #35. I can send a pull request when I am done, but I wanted to check with the developers first.

Thanks!
James

Profile security

nandoflorestan> Hey John, I notice in horus anyone can edit anyone's profile (email and password) right now.
sontek> I must have accidentally removed the acl
nandoflorestan> I think the url for editing the profile should not include the profile id -- only the active user would be editable.
sontek> I think the ID in the profile URL for viewing other profiles and for admins to edit, but there should definitely be a group permission with an acl for editing
sontek> Maybe the default edit URL can be without ID
sontek> but still have viewing with an ID?
sontek> what do you think?
nandoflorestan> Is there really a use case for admins editing other people's emails and passwords?
sontek> In private/admin type apps, like internal business to business applications, but not on consumer products
nandoflorestan> Viewing with an ID is useful for some apps, not for others (mine for instance).
sontek> Thats the hard part of balancing flexibility with defaults
sontek> But I could probably make it all configurable with a few settings
sontek> I'll do that. By default lets go with more secure
nandoflorestan> Cool. Let's fix the ACL bug first
sontek> So no IDs in URLs and add acl, but with a flag in .ini you can trigger the other URLs to be added
nandoflorestan> Nice idea.

Support for manual activation (by administrator)

Hi,

We are using Horus at hypothes.is

We would like to have an option in Horus that makes it possible to send all registration requests through a configured [group of] admin[s].

So, while horus.require_activation enables automatic activation mails, we would like some thing like horus.require_admin_action, which would send a mail to admin, to allow/deny the new registration.

Thank you.

hard-coded class names

Currently I am integrating horus into my python app. I really like the flexibility of your package, but I stumbled upon a little problem. I already have an User class and table in my application. For testing I wanted to rename the horus User class. After doing this, SQLAlchemy initialization fails, because it's not able to find the User class. I had a look at the source code and saw, that the GroupMixin has a hard-coded "User" in the Group to User relationship.

    @declared_attr
    def users(self):
        """Relationship for users belonging to this group"""
        return sa.orm.relationship(
            'User',
            secondary=UserGroupMixin.__tablename__,
            # order_by='%s.user.username' % UserMixin.__tablename__,
            passive_deletes=True,
            passive_updates=True,
            backref=pluralize(GroupMixin.__tablename__),
        )

Same goes for Activation to User

    @declared_attr
    def activation(self):
        return sa.orm.relationship(
            'Activation',
            backref='user'
        )

I don't know if that is intended. It's not that hard to fix (I just rename my own class), but I think this makes it harder to integrade horus in an existing model.

Creating the initial tables

I see that horus is being used by hypothesis to do the authentication, it inherits the model straight up. But I do not see anywhere where horus utilize sqlalchemy to initialize the table in the database. Is this step supposed to be manual?

Forgot password doesn't check for user existence

Result is a traceback that NoneType has no attribute 'activation'.

@sontek two questions:

  1. Should I fix this, or is the intention to let this repo die and move everything to Pylons/horus?
  2. If I fix it, what error do we want to raise (if any)? Are we concerned about leaking information about which email addresses have accounts?

The "index" route should be called "home"

The "index" route should be called "home". Not because "index" is better, but because when one starts a new Pyramid project, the scaffold creates a "home" route and a "home" view.

I can make this change for you guys, but I want you to approve the idea first. Anyone have anything against this?

Compilation error while python setup.py develop

After adding horus in requires list and executing python setup.py develop I got a compilation error saying that
"cryptacular/bcrypt/_bcrypt.c:26:20: fatal error: Python.h: No such file or directory
compilation terminated.
error: Setup script exited with error: command 'gcc' failed with exit status 1
"

Package Dependencies

Please note in your setup.py the dependencies this packages has, namely: colander, cryptacular, deform, pyramid_deform, maybe pyramid_mailer, and maybe pystache.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.