Commit 29b1e267 authored by Valéry Febvre's avatar Valéry Febvre

Initial commit

parents
development-*.ini
Passim.egg-info
*~
*.pyc
.*.swp
*.mo
0.0
---
- Initial version
Debian Wheezy Install
---------------------
### Install Apache and Apache WSGI module
aptitude install \
apache2 \
libapache2-mod-wsgi
### Install Postgres
aptitude install \
postgresql \
postgresql-contrib-9.1 \
### Install some Python dependencies
aptitude install -twheezy-backports \
python-pyramid \
python-pyramid-tm \
python-sqlalchemy \
python-sqlalchemy-ext
### install Easter-Eggs's packages
aptitude install -twheezy-backports \
python-waitress \
python-pyramid-debugtoolbar \
python-zope.sqlalchemy
### install generic Python packages via Debian
aptitude install \
python-decorator \
python-mako \
python-pyramid-beaker \
python-psycopg2 \
python-pybabel \
python-transaction \
python-weberror \
python-webhelpers
#### Install Pyramid-Helpers
git clone git.easter-eggs.fr:/home/git/ee/pyramid-helpers.git
cd pyramid-helpers
./setup.py compile_catalog
su -
cd /home/passim/pyramid-helpers
./setup.py develop --no-deps
#### Install Passim
git clone git.easter-eggs.fr:/home/git/mat/passim.git
cd passim
./setup.py compile_catalog
su -
cd /home/passim/passim
python ./setup.py develop --no-deps
exit
initialize_Passim_db development.ini
Database setup (PostgreSQL)
---------------------------
# su - postgres
$ psql
CREATE USER passim WITH PASSWORD 'passim';
CREATE DATABASE passim WITH OWNER passim;
include *.txt *.ini *.cfg *.rst
recursive-include passim *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
Passim
=====
Annuaire de services d'information transport en France
Passim README
==================
Getting Started
---------------
- cd <directory containing this file>
- $VENV/bin/python setup.py develop
- $VENV/bin/initialize_Passim_db development.ini
- $VENV/bin/pserve development.ini
###
# app configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html
###
[app:main]
use = egg:Passim
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
pyramid.includes =
pyramid_debugtoolbar
pyramid_mako
pyramid_tm
mako.directories =
passim:templates
pyramid_helpers:templates
sqlalchemy.url = postgres://passim:passim@localhost:5432/passim
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1
# Fake user for test purpose
passim.auth.fake_user = admin
# Captcha
#recaptcha.private_key =
#recaptcha.public_key =
#email.to =
#email.smtp_server =
###
# wsgi server configuration
###
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
###
# logging configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html
###
[loggers]
keys = root, passim, sqlalchemy
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[logger_passim]
level = DEBUG
handlers =
qualname = passim
[logger_sqlalchemy]
level = INFO
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
# -*- coding: utf-8 -*-
# Passim -- Annuaire de services d'information transport en France
# By: Pierre Arnaud <parnaud@easter-eggs.com>
# Valéry Febvre <vfebvre@easter-eggs.com>
#
# Copyright (C) 2015 Easter-eggs
# @app_url@
#
# This file is part of Passim.
#
# Passim is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Passim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
from passim.models import initialize_model
#from passim.resources import RootResource
from .models import (
DBSession,
Base,
)
def on_before_renderer(event):
global APP_NAME, APP_VERSION
request = event['request']
def main(global_config, **settings):
# Model setup
initialize_model(settings)
# Authorization setup
authorization_policy = ACLAuthorizationPolicy()
config = Configurator(
authorization_policy=authorization_policy,
# root_factory=RootResource,
settings=settings,
)
# Session setup
config.include('pyramid_beaker')
# Subscribers setup
config.add_subscriber(on_before_renderer, 'pyramid.events.BeforeRender')
# Auth setup
config.include('passim.auth')
# I18n setup
config.include('pyramid_helpers.i18n')
#config.add_translation_dirs('passim:locale/')
# Forms setup
config.include('pyramid_helpers.forms')
# Static route
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('index', '/')
config.add_route('accueil', '/accueil')
config.add_route('about', '/about')
config.add_route('contact', '/contact')
config.add_route('contact-email', '/contact-email')
config.add_route('contribute', '/contribute')
config.add_route('contribute-email', '/contribute-email')
config.add_route('data', '/data')
config.add_route('help', '/help')
config.scan('passim.views')
return config.make_wsgi_app()
# -*- coding: utf-8 -*-
# Passim -- Annuaire de services d'information transport en France
# By: Pierre Arnaud <parnaud@easter-eggs.com>
# Valéry Febvre <vfebvre@easter-eggs.com>
#
# Copyright (C) 2015 Easter-eggs
# @app_url@
#
# This file is part of Passim.
#
# Passim is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Passim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.httpexceptions import HTTPFound
from pyramid.security import authenticated_userid
from pyramid.security import has_permission
from passim.models import DBSession
from models import User
log = logging.getLogger(__name__)
def get_user(username):
if username is None:
return None
try:
user = DBSession.query(User).filter_by(username=username).first()
except:
log.exception('Failed to get user with username={0}'.format(username))
user = None
return user
def authentication_callback(userid, request):
""" Called by authentication policy """
user = get_user(userid)
if user is None:
# Invalid user
return None
# Construct effective principals with profile and roles
principals = [
'profile:{0}'.format(user.profile)
]
return principals
def check_credentials(username, password):
user = get_user(username)
return user is not None and user.validate_password(password)
def on_before_renderer(event):
""" Add authenticated_user to renderer context """
request = event['request']
event['authenticated_user'] = request.authenticated_user
def on_new_request(event):
""" Add authenticated_user to request """
request = event.request
username = authenticated_userid(request)
request.authenticated_user = get_user(username)
if not hasattr(request, 'has_permission'):
# Pyramid < 1.5
# Request.has_permission() method has been added in Pyramid 1.5
request.has_permission = lambda permission, context=None: has_permission(permission, context or request.root, request)
def includeme(config):
"""
Set up standard configurator registrations. Use via:
.. code-block:: python
config = Configurator()
config.include('passim.auth')
"""
config.add_subscriber(on_before_renderer, 'pyramid.events.BeforeRender')
config.add_subscriber(on_new_request, 'pyramid.events.NewRequest')
# Authentication Policy
registry = config.registry
settings = registry.settings
secret = settings.get('auth.secret', 'the-big-secret-for-secured-authentication')
authentication_policy = AuthTktAuthenticationPolicy(secret, callback=authentication_callback)
config.set_authentication_policy(authentication_policy)
# -*- coding: utf-8 -*-
# Passim -- Annuaire de services d'information transport en France
# By: Pierre Arnaud <parnaud@easter-eggs.com>
# Valéry Febvre <vfebvre@easter-eggs.com>
#
# Copyright (C) 2015 Easter-eggs
# @app_url@
#
# This file is part of Passim.
#
# Passim is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Passim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Google reCaptcha submit helper"""
import logging
from recaptcha.client import captcha
log = logging.getLogger(__name__)
def submit(request, params):
if not params['recaptcha_challenge_field']:
# An empty recaptcha_challenge_field means there is a problem with Google reCaptcha service
# don't return an error
return captcha.RecaptchaResponse(is_valid=True)
if not params['recaptcha_response_field']:
return captcha.RecaptchaResponse(is_valid=False, error_code='incorrect-captcha-sol')
response = captcha.submit(
params['recaptcha_challenge_field'],
params['recaptcha_response_field'],
request.registry.settings['recaptcha.private_key'],
request.remote_addr)
if not response.is_valid and \
response.error_code in ['invalid-site-private-key', 'invalid-request-cookie', 'recaptcha-not-reachable']:
log.debug('reCaptcha Google service error: {}'.format(response.error_code))
return captcha.RecaptchaResponse(is_valid=True)
return response
# -*- coding: utf-8 -*-
# Passim -- Annuaire de services d'information transport en France
# By: Pierre Arnaud <parnaud@easter-eggs.com>
# Valéry Febvre <vfebvre@easter-eggs.com>
#
# Copyright (C) 2015 Easter-eggs
# @app_url@
#
# This file is part of Passim.
#
# Passim is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Passim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Forms for emails """
import formencode
from formencode import validators
#
# Forms
#
# contact, contribute
class EmailForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
email_from = validators.Email(not_empty=True)
subject = validators.String(not_empty=True, strip=True)
body = validators.String(not_empty=True, strip=True)
recaptcha_challenge_field = validators.String()
recaptcha_response_field = validators.String()
# -*- coding: utf-8 -*-
# Passim -- Annuaire de services d'information transport en France
# By: Pierre Arnaud <parnaud@easter-eggs.com>
# Valéry Febvre <vfebvre@easter-eggs.com>
#
# Copyright (C) 2015 Easter-eggs
# @app_url@
#
# This file is part of Passim.
#
# Passim is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Passim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Database model """
from collections import OrderedDict
import datetime
import hashlib
from sqlalchemy import engine_from_config
from sqlalchemy import Boolean
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Unicode
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension
from pyramid_helpers.i18n import N_
from passim.utils import randstr
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), autoflush=False))
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
PROFILES = OrderedDict([
('guest', N_(u'Guest')),
('user', N_(u'User')),
('admin', N_(u'Administrator')),
])
# Primary key
id = Column(Integer, primary_key=True)
# Attributes
email = Column(Unicode(255), nullable=False)
enabled = Column(Boolean, nullable=False)
password = Column(Unicode(255))
profile = Column(Unicode(255), nullable=False)
username = Column(Unicode(255), unique=True, index=True)
firstname = Column(Unicode(255))
lastname = Column(Unicode(255))
@property
def fullname(self):
fullname = []
if self.firstname:
fullname.append(self.firstname)
if self.lastname:
fullname.append(self.lastname)
if not fullname:
if self.username:
fullname.append(self.username)
return ' '.join(fullname)
def __get_salted_hash(self, secret, salt=None):
if salt is None:
salt = randstr(10)
sha256 = hashlib.sha256(secret)
sha256.update(salt)
return '%s:%s' % (salt, sha256.hexdigest())
def set_password(self, secret):
self.password = self.__get_salted_hash(secret)
def to_dict(self):
return dict(
id=self.id,
email=self.email,
enabled=self.enabled,
profile=self.profile,
username=self.username,
firstname=self.firstname,
lastname=self.lastname,
)
def validate_password(self, secret):
if not self.enabled:
return False
if self.password is None:
return False
salt, salted = self.password.split(':')
if self.__get_salted_hash(secret, salt=salt) == self.password:
self.last_login = datetime.datetime.utcnow()
return True
return False
def initialize_model(settings):
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
from pyramid.security import ALL_PERMISSIONS
from pyramid.security import Allow
from pyramid.security import Authenticated
class PassimResource(object):
__acl__ = [
(Allow, 'profile:admin', ALL_PERMISSIONS),
]
class AdminResource(object):
__parent__ = PassimResource
__acl__ = [
(Allow, Authenticated, 'admin'),
]
def __init__(self, request):
self.request = request
class UsersResource(object):
"""
Resource object for users management
valid permissions for Users are:
list, view, create, delete, modify
"""
__parent__ = PassimResource
__acl__ = [
(Allow, Authenticated, 'list'),
(Allow, Authenticated, 'view'),
]
def __init__(self, request):
self.request = request
# -*- coding: utf-8 -*-
# Passim -- Annuaire de services d'information transport en France
# By: Pierre Arnaud <parnaud@easter-eggs.com>
# Valéry Febvre <vfebvre@easter-eggs.com>
#
# Copyright (C) 2015 Easter-eggs
# @app_url@
#
# This file is part of Passim.
#
# Passim is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Passim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import os
import sys
import transaction