Browse Source

User registration

master
Damien Nicolas 8 years ago
parent
commit
3514238c2f
15 changed files with 154 additions and 7 deletions
  1. +2
    -0
      .gitignore
  2. +8
    -1
      codular/__init__.py
  3. +4
    -0
      codular/forms/__init__.py
  4. +25
    -0
      codular/forms/csrf.py
  5. +21
    -0
      codular/forms/registration.py
  6. +2
    -2
      codular/models/user.py
  7. +4
    -1
      codular/templates/home/home.jinja2
  8. +2
    -1
      codular/templates/users/new.jinja2
  9. +2
    -1
      codular/views/home.py
  10. +61
    -0
      codular/views/user.py
  11. +0
    -0
      data/sessions/data/.git_dir
  12. +0
    -0
      data/sessions/lock/.git_dir
  13. +11
    -0
      development.ini
  14. +9
    -0
      production.ini
  15. +3
    -1
      setup.py

+ 2
- 0
.gitignore View File

@ -6,3 +6,5 @@ codular.egg-info/
codular.sqlite
codular/__pycache__/
codular/scripts/__pycache__/
data/sessions/data/
data/sessions/lock/

+ 8
- 1
codular/__init__.py View File

@ -1,5 +1,6 @@
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
from pyramid_beaker import session_factory_from_settings
from .models.meta import (DBSession, Base)
from .views import (ViewHome, ViewUsers)
@ -7,10 +8,13 @@ from .views import (ViewHome, ViewUsers)
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
# pyramid_beaker add-on
session_factory = session_factory_from_settings(settings)
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
config = Configurator(settings=settings)
config = Configurator(settings=settings, session_factory=session_factory)
config.include('pyramid_chameleon')
config.include('pyramid_jinja2')
config.add_jinja2_search_path("templates")
@ -39,3 +43,6 @@ def add_routes(config):
config.add_route('delete_user', '/users/{id}/delete', request_method='GET')
config.add_route('edit_user', '/users/{id}/edit', request_method='GET')
config.add_route('user', '/users/{id}', request_method='GET')
# aliases
config.add_route('register', '/register')

+ 4
- 0
codular/forms/__init__.py View File

@ -0,0 +1,4 @@
# Package
from .registration import Registration

+ 25
- 0
codular/forms/csrf.py View File

@ -0,0 +1,25 @@
import colander
import deform
@colander.deferred
def deferred_csrf_default(node, kw):
request = kw.get('request')
csrf_token = request.session.get_csrf_token()
return csrf_token
@colander.deferred
def deferred_csrf_validator(node, kw):
def validate_csrf(node, value):
request = kw.get('request')
csrf_token = request.session.get_csrf_token()
if value != csrf_token:
raise colander.Invalid(node, 'Bad CSRF token')
return validate_csrf
class CSRFSchema(colander.Schema):
csrf = colander.SchemaNode(
colander.String(),
default = deferred_csrf_default,
validator = deferred_csrf_validator,
widget = deform.widget.HiddenWidget(),
)

+ 21
- 0
codular/forms/registration.py View File

@ -0,0 +1,21 @@
import colander
import deform
from .csrf import CSRFSchema
username_regex = '^[a-z0-9\-]+$'
class Registration(CSRFSchema):
username = colander.SchemaNode(colander.String(),
validator=colander.Regex(username_regex,
'Your username can only contain '\
+'lowercase alphanumerical characters or dashes'))
name = colander.SchemaNode(colander.String())
# TODO: write an anti-duplicates validator
email = colander.SchemaNode(colander.String(),
validator=colander.Email())
password = colander.SchemaNode(colander.String(),
validator=colander.Length(min=8),
widget=deform.widget.PasswordWidget(),
description='Enter a password')

+ 2
- 2
codular/models/user.py View File

@ -20,7 +20,7 @@ crypt = cryptacular.bcrypt.BCRYPTPasswordManager()
Return a unicode hash of the password, using BCrypt from cryptacular
"""
def hash_password(password):
return unicode(crypt.encode(password))
return str(crypt.encode(password))
class User(Base):
"""
@ -58,4 +58,4 @@ class User(Base):
user = cls.get_by_username(username)
if not user:
return False
return crypt.check(user.password, password)
return crypt.check(user.password, password)

+ 4
- 1
codular/templates/home/home.jinja2 View File

@ -3,4 +3,7 @@
<p>
Home page {{ one }}
</p>
{% endblock %}
<p>
{{ user }}
</p>
{% endblock %}

+ 2
- 1
codular/templates/users/new.jinja2 View File

@ -3,4 +3,5 @@
<p>
Create new user
</p>
{% endblock %}
{{ form|safe }}
{% endblock %}

+ 2
- 1
codular/views/home.py View File

@ -3,6 +3,7 @@ Home view
"""
from pyramid.response import Response
from pyramid.view import view_config
from pyramid.security import authenticated_userid
from sqlalchemy.exc import DBAPIError
@ -22,4 +23,4 @@ class ViewHome(object):
@view_config(route_name='home', renderer='home/home.jinja2')
def home_get(self):
template_vars = {'menu_active': 'home', 'one': "the one", 'project': 'codular'}
return template_vars
return template_vars

+ 61
- 0
codular/views/user.py View File

@ -0,0 +1,61 @@
"""
User view
"""
from pyramid.response import Response
from pyramid.view import view_config
from pyramid.security import authenticated_userid
from pyramid.httpexceptions import HTTPFound
from deform import Form
from deform.exception import ValidationFailure
from ..forms import Registration
from sqlalchemy.exc import DBAPIError
from ..models import (
DBSession,
User,
)
"""
Is : User edit action
Route : /users
"""
@view_config(route_name='update_user', request_method='POST', renderer='users/new.jinja2')
def update_user(request):
form = Form(Registration().bind(request=request), buttons=('submit',),
action=request.route_url('update_user'))
if 'submit' in request.POST: # detect that the submit button was clicked
controls = request.POST.items() # get the form controls
try:
form_data = form.validate(controls) # call validate
except ValidationFailure as e: # catch the exception
return {'form':form.render()} # re-render the form with an exception
# if user is logged, we update his profile. Else we create a new user.
userid = authenticated_userid(request)
if userid:
user = User.by_id(userid)
else:
del(form_data['csrf'])
user = User(**form_data)
DBSession.add(user)
url = request.route_url('home')
return HTTPFound(location=url)
return {'form': form.render()}
"""
Is : Registration form
Route : /users/new
"""
@view_config(route_name='new_user', renderer='users/new.jinja2')
@view_config(route_name='register', renderer='users/new.jinja2')
def new_users(request):
form = Form(Registration().bind(request=request), buttons=('submit',),
action=request.route_url('update_user'))
return {'form': form.render()}

+ 0
- 0
data/sessions/data/.git_dir View File


+ 0
- 0
data/sessions/lock/.git_dir View File


+ 11
- 0
development.ini View File

@ -15,6 +15,8 @@ pyramid.includes =
pyramid_debugtoolbar
pyramid_tm
pyramid_jinja2
deform_bootstrap
pyramid_beaker
sqlalchemy.url = sqlite:///%(here)s/codular.sqlite
@ -27,6 +29,14 @@ jinja2.filters =
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1
session.type = file
session.data_dir = %(here)s/data/sessions/data
session.lock_dir = %(here)s/data/sessions/lock
session.key = customerskey
session.secret = customerssecret
session.cookie_on_exception = true
###
# wsgi server configuration
###
@ -75,3 +85,4 @@ formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s

+ 9
- 0
production.ini View File

@ -14,6 +14,8 @@ pyramid.default_locale_name = en
pyramid.includes =
pyramid_tm
pyramid_jinja2
deform_bootstrap
pyramid_beaker
sqlalchemy.url = sqlite:///%(here)s/codular.sqlite
@ -22,6 +24,13 @@ jinja2.filters =
route_url = pyramid_jinja2.filters:route_url_filter
static_url = pyramid_jinja2.filters:static_url_filter
session.type = file
session.data_dir = %(here)s/data/sessions/data
session.lock_dir = %(here)s/data/sessions/lock
session.key = customerskey
session.secret = customerssecret
session.cookie_on_exception = true
[server:main]
use = egg:waitress#main
host = 0.0.0.0


+ 3
- 1
setup.py View File

@ -18,7 +18,9 @@ requires = [
'transaction',
'zope.sqlalchemy',
'waitress',
'cryptacular'
'cryptacular',
'deform_bootstrap',
'pyramid_beaker',
]
setup(name='codular',


Loading…
Cancel
Save