Fixes #710: manage storage backends and implement sql one
WARNING: this commit brake sql migrations
This commit is contained in:
parent
6b1788299a
commit
e0ea574b04
48
README.rst
48
README.rst
|
@ -52,32 +52,29 @@ You must install the following packages to use Mandaye
|
|||
* sqlalchemy-migrate:: http://pypi.python.org/pypi/sqlalchemy-migrate
|
||||
|
||||
|
||||
You can install all those dependencies quickly using pip::
|
||||
|
||||
pip install poster SQLAlchemy Beaker Mako lxml gunicorn sqlalchemy-migrate xtraceback
|
||||
|
||||
or easy_install::
|
||||
|
||||
easy_install poster SQLAlchemy Beaker Mako lxml gunicorn sqlalchemy-migrate xtraceback
|
||||
|
||||
or apt-get (Debian based distributions)::
|
||||
|
||||
apt-get install gunicorn python-poster python-sqlalchemy python-beaker python-mako python-lxml python-setuptools
|
||||
|
||||
It's recommanded to install the following modules
|
||||
|
||||
* PyCrypto >= 2.3:: http://pypi.python.org/pypi/pycrypto
|
||||
* Static >= 0.4:: http://pypi.python.org/pypi/static
|
||||
|
||||
You can install this Python modules with pip::
|
||||
|
||||
pip install pycrypto static
|
||||
|
||||
Quick installation
|
||||
------------------
|
||||
|
||||
Install at least Python >=2.5 and setuptools or distribute and enter this command in a shell::
|
||||
Install at least Python >=2.5 and pip in your system.
|
||||
For example with Debian or a Debian based distribution::
|
||||
|
||||
sudo apt-get install python python-pip
|
||||
|
||||
Then install virtualenv ::
|
||||
|
||||
pip install virtualenv
|
||||
|
||||
Create your virtualenv activate it::
|
||||
|
||||
virtualenv mandaye
|
||||
source mandaye/bin/activate
|
||||
pip install -U pip
|
||||
|
||||
Install mandaye::
|
||||
|
||||
$ tar xfvz mandaye-VERSION.tar.gz
|
||||
$ cd mandaye-VERSION
|
||||
$ pip install -r requirements.txt
|
||||
$ python setup.py install
|
||||
|
||||
If you want to develop use this command line::
|
||||
|
@ -88,12 +85,9 @@ If you want to develop use this command line::
|
|||
Quick Start
|
||||
-----------
|
||||
|
||||
Configure MANDAYE_PATH/mandaye/config.py with your own preferences.
|
||||
You must configure the database uri and the log file.
|
||||
First step is to create a mandaye project::
|
||||
|
||||
First create your database::
|
||||
|
||||
$ mandaye_admin.py --createdb
|
||||
$ mandaye_admin.py --newproject
|
||||
|
||||
Launch mandaye server::
|
||||
|
||||
|
|
|
@ -16,19 +16,20 @@ from lxml.html import fromstring
|
|||
from urlparse import parse_qs
|
||||
|
||||
from mandaye import config, VERSION
|
||||
from mandaye.db import sql_session
|
||||
from mandaye.exceptions import MandayeException
|
||||
from mandaye.models import Site, ExtUser, LocalUser
|
||||
from mandaye.log import logger
|
||||
from mandaye.http import HTTPResponse, HTTPHeader, HTTPRequest
|
||||
from mandaye.response import _500, _302, _401
|
||||
from mandaye.response import template_response
|
||||
from mandaye.server import get_response
|
||||
|
||||
from mandaye.config.backend import ManagerIDPUser, ManagerSPUser,\
|
||||
ManagerServiceProvider
|
||||
|
||||
try:
|
||||
from Crypto.Cipher import AES
|
||||
except ImportError:
|
||||
config.encrypt_ext_password = False
|
||||
config.encrypt_sp_password = False
|
||||
|
||||
class AuthForm(object):
|
||||
|
||||
|
@ -72,7 +73,7 @@ a password_field key if you want to encode a password.")
|
|||
|
||||
def _encrypt_pwd(self, post_values):
|
||||
""" This method allows you to encrypt a password
|
||||
To use this feature you muste set encrypt_ext_password to True
|
||||
To use this feature you muste set encrypt_sp_password to True
|
||||
in your configuration and set a secret in encrypt_secret
|
||||
post_values: containt the post values
|
||||
return None and modify post_values
|
||||
|
@ -98,7 +99,7 @@ a password_field key if you want to encode a password.")
|
|||
def _decrypt_pwd(self, post_values):
|
||||
""" This method allows you to dencrypt a password encrypt with
|
||||
_encrypt_pwd method. To use this feature you muste set
|
||||
encrypt_ext_password to True in your configuration and
|
||||
encrypt_sp_password to True in your configuration and
|
||||
set a secret in encrypt_secret
|
||||
post_values: containt the post values
|
||||
return None and modify post_values
|
||||
|
@ -120,7 +121,7 @@ a password_field key if you want to encode a password.")
|
|||
|
||||
def _get_password(self, post_values):
|
||||
if self.form_values.has_key('password_field'):
|
||||
if config.encrypt_ext_password:
|
||||
if config.encrypt_sp_password:
|
||||
return self._encrypt_pwd(
|
||||
post_values[self.form_values['password_field']]
|
||||
)
|
||||
|
@ -182,54 +183,36 @@ a password_field key if you want to encode a password.")
|
|||
request = HTTPRequest(cookies, headers, "POST", params)
|
||||
return get_response(env, request, action, cj)
|
||||
|
||||
def _save_association(self, env, local_login, post_values):
|
||||
def _save_association(self, env, unique_id, post_values):
|
||||
""" save an association in the database
|
||||
env: wsgi environment
|
||||
local_login: the Mandaye login
|
||||
unique_id: idp uinique id
|
||||
post_values: dict with the post values
|
||||
"""
|
||||
ext_username = post_values[self.form_values['username_field']]
|
||||
if config.encrypt_ext_password:
|
||||
sp_login = post_values[self.form_values['username_field']]
|
||||
if config.encrypt_sp_password:
|
||||
self._encrypt_pwd(post_values)
|
||||
site = sql_session().query(Site).\
|
||||
filter_by(name=self.site_name).first()
|
||||
if not site:
|
||||
logger.info('Add %s site in the database' % self.site_name)
|
||||
site = Site(self.site_name)
|
||||
sql_session().add(site)
|
||||
local_user = sql_session().query(LocalUser).\
|
||||
filter_by(login=local_login).first()
|
||||
if not local_user:
|
||||
logger.debug('Add user %s in the database' % local_login)
|
||||
local_user = LocalUser(login=local_login)
|
||||
sql_session().add(local_user)
|
||||
ext_user = sql_session().query(ExtUser).\
|
||||
join(LocalUser).\
|
||||
filter(LocalUser.login==local_login).\
|
||||
filter(ExtUser.login==ext_username).\
|
||||
first()
|
||||
if not ext_user:
|
||||
ext_user = ExtUser()
|
||||
sql_session().add(ext_user)
|
||||
logger.info('New association: %s with %s on site %s' % \
|
||||
(ext_username, local_login, self.site_name))
|
||||
ext_user.login = ext_username
|
||||
ext_user.post_values = post_values
|
||||
ext_user.local_user = local_user
|
||||
ext_user.last_connection = datetime.now()
|
||||
ext_user.site = site
|
||||
sql_session().commit()
|
||||
env['beaker.session']['login'] = local_login
|
||||
env['beaker.session'][self.site_name] = ext_user.id
|
||||
service_provider = ManagerServiceProvider.get_or_create(self.site_name)
|
||||
idp_user = ManagerIDPUser.get_or_create(unique_id)
|
||||
sp_user = ManagerSPUser.get_or_create(sp_login, post_values,
|
||||
idp_user, service_provider)
|
||||
sp_user.login = sp_login
|
||||
sp_user.post_values = post_values
|
||||
sp_user.idp_user = idp_user
|
||||
sp_user.last_connection = datetime.now()
|
||||
sp_user.service_provider = service_provider
|
||||
ManagerSPUser.save()
|
||||
env['beaker.session']['unique_id'] = unique_id
|
||||
env['beaker.session'][self.site_name] = sp_user.id
|
||||
env['beaker.session'].save()
|
||||
|
||||
def associate_submit(self, env, values, condition, request, response):
|
||||
""" Associate your login / password into your database
|
||||
"""
|
||||
logger.debug("Trying to associate a user")
|
||||
login = env['beaker.session'].get('login')
|
||||
unique_id = env['beaker.session'].get('unique_id')
|
||||
if request.msg:
|
||||
if not login:
|
||||
if not unique_id:
|
||||
logger.warning("Association failed: user isn't login on Mandaye")
|
||||
return _302(values.get('connection_url'))
|
||||
post = parse_qs(request.msg.read(), request)
|
||||
|
@ -247,7 +230,7 @@ a password_field key if you want to encode a password.")
|
|||
response = self.replay(env, post_values)
|
||||
if eval(condition):
|
||||
logger.debug("Replay works: save the association")
|
||||
self._save_association(env, login, post_values)
|
||||
self._save_association(env, unique_id, post_values)
|
||||
if qs.has_key('next_url'):
|
||||
return _302(qs['next_url'], response.cookies)
|
||||
return response
|
||||
|
@ -256,20 +239,20 @@ a password_field key if you want to encode a password.")
|
|||
qs['type'] = 'badlogin'
|
||||
return _302(values.get('associate_url') + "?%s" % urllib.urlencode(qs))
|
||||
|
||||
def _login_ext_user(self, ext_user, env, condition, values):
|
||||
""" Log in an external user
|
||||
def _login_sp_user(self, sp_user, env, condition, values):
|
||||
""" Log in sp user
|
||||
"""
|
||||
if not ext_user.login:
|
||||
if not sp_user.login:
|
||||
return _500(env['PATH_INFO'],
|
||||
'Invalid values for AuthFormDispatcher.login')
|
||||
post_values = copy.deepcopy(ext_user.post_values)
|
||||
if config.encrypt_ext_password:
|
||||
post_values = copy.deepcopy(sp_user.post_values)
|
||||
if config.encrypt_sp_password:
|
||||
self._decrypt_pwd(post_values)
|
||||
response = self.replay(env, post_values)
|
||||
if condition and eval(condition):
|
||||
ext_user.last_connection = datetime.now()
|
||||
sql_session().commit()
|
||||
env['beaker.session'][self.site_name] = ext_user.id
|
||||
sp_user.last_connection = datetime.now()
|
||||
ManagerSPUser.save()
|
||||
env['beaker.session'][self.site_name] = sp_user.id
|
||||
env['beaker.session'].save()
|
||||
return response
|
||||
else:
|
||||
|
@ -279,29 +262,27 @@ a password_field key if you want to encode a password.")
|
|||
""" Automatic login on a site with a form
|
||||
"""
|
||||
logger.debug('Trying to login on Mandaye')
|
||||
login = self.get_current_login(env)
|
||||
if not login:
|
||||
# Specific method to get current idp unique id
|
||||
unique_id = self.get_current_unique_id(env)
|
||||
if not unique_id:
|
||||
return _401('Access denied: invalid token')
|
||||
|
||||
# FIXME: hack to force beaker to generate an id
|
||||
# somtimes beaker doesn't do it by himself
|
||||
env['beaker.session'].regenerate_id()
|
||||
|
||||
env['beaker.session']['login'] = login
|
||||
env['beaker.session']['unique_id'] = unique_id
|
||||
env['beaker.session'].save()
|
||||
logger.debug('User %s successfully login' % env['beaker.session']['login'])
|
||||
|
||||
ext_user = sql_session().query(ExtUser).\
|
||||
join(LocalUser).\
|
||||
join(Site).\
|
||||
filter(LocalUser.login==login).\
|
||||
filter(Site.name==self.site_name).\
|
||||
order_by(ExtUser.last_connection.desc()).\
|
||||
first()
|
||||
if not ext_user:
|
||||
logger.debug('User %s is not associate' % env['beaker.session']['login'])
|
||||
logger.debug('User %s successfully login' % env['beaker.session']['unique_id'])
|
||||
|
||||
idp_user = ManagerIDPUser.get(unique_id)
|
||||
service_provider = ManagerServiceProvider.get(self.site_name)
|
||||
sp_user = ManagerSPUser.get_last_connected(idp_user, service_provider)
|
||||
if not sp_user:
|
||||
logger.debug('User %s is not associate' % env['beaker.session']['unique_id'])
|
||||
return _302(values.get('associate_url') + "?type=first")
|
||||
return self._login_ext_user(ext_user, env, condition, values)
|
||||
return self._login_sp_user(sp_user, env, condition, values)
|
||||
|
||||
def logout(self, env, values, request, response):
|
||||
""" Destroy the Beaker session
|
||||
|
@ -317,19 +298,17 @@ a password_field key if you want to encode a password.")
|
|||
This method must have a query string with a username parameter
|
||||
"""
|
||||
# TODO: need to logout the first
|
||||
login = env['beaker.session']['login']
|
||||
unique_id = env['beaker.session']['unique_id']
|
||||
qs = parse_qs(env['QUERY_STRING'])
|
||||
if not login or not qs.has_key('id'):
|
||||
return _401('Access denied: beaker session invalid or not qs id')
|
||||
id = qs['id'][0]
|
||||
ext_user = sql_session().query(ExtUser).\
|
||||
join(LocalUser).\
|
||||
filter(LocalUser.login==login).\
|
||||
filter(ExtUser.id==id).\
|
||||
first()
|
||||
if not ext_user:
|
||||
service_provider = ManagerServiceProvider.get(self.site_name)
|
||||
idp_user = ManagerServiceProvider.get(unique_id)
|
||||
sp_user = ManagerSPUser.get_last_connected(idp_user, service_provider)
|
||||
if not sp_user:
|
||||
return _302(values.get('associate_url'))
|
||||
return self._login_ext_user(ext_user, env, 'response.code==302', values)
|
||||
return self._login_sp_user(sp_user, env, 'response.code==302', values)
|
||||
|
||||
def disassociate(self, env, values, request, response):
|
||||
""" Multi accounts feature
|
||||
|
@ -345,15 +324,9 @@ a password_field key if you want to encode a password.")
|
|||
if not login or not qs.has_key('id'):
|
||||
return _401('Access denied: beaker session invalid or not id')
|
||||
id = qs['id'][0]
|
||||
ext_user = sql_session().query(ExtUser).\
|
||||
join(LocalUser).\
|
||||
filter(LocalUser.login==login).\
|
||||
filter(ExtUser.id==id).\
|
||||
first()
|
||||
if ext_user:
|
||||
logger.debug('Disassociate account %s' % ext_user.login)
|
||||
sql_session().delete(ext_user)
|
||||
sql_session().commit()
|
||||
sp_user = ManagerSPUser.get_by_id(id)
|
||||
if sp_user:
|
||||
ManagerSPUser.delete(sp_user)
|
||||
if qs.has_key('logout'):
|
||||
self.logout(env, values, request, response)
|
||||
return _302(values.get('next_url'))
|
||||
|
|
|
@ -38,8 +38,8 @@ class VincennesAuth(AuthForm):
|
|||
res[keyvalue[0]] = keyvalue[1]
|
||||
return res
|
||||
|
||||
def get_current_login(self, env):
|
||||
""" Return the current Vincennes pseudo
|
||||
def get_current_unique_id(self, env):
|
||||
""" Return the current Vincennes unique id
|
||||
"""
|
||||
from mandaye import config
|
||||
# TODO: test time validity
|
||||
|
@ -97,7 +97,7 @@ class VincennesAuth(AuthForm):
|
|||
if not login:
|
||||
logger.debug('Auto login failed because the user is not connected on vincennes.fr')
|
||||
return _302(path, request.cookies)
|
||||
env['beaker.session']['login'] = login
|
||||
env['beaker.session']['unique_id'] = unique_id
|
||||
env['beaker.session'].save()
|
||||
ext_user = sql_session().query(ExtUser).\
|
||||
join(LocalUser).\
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
class DefaultManagerIDPUser:
|
||||
|
||||
@staticmethod
|
||||
def get(unique_id, idp_id='default'):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def create(unique_id, idp_id='default'):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_or_create(unique_id, idp_id='default'):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def delete(idp_user):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def save(idp_user):
|
||||
pass
|
||||
|
||||
class DefaultManagerSPUser:
|
||||
|
||||
@staticmethod
|
||||
def get(login, idp_user, service_provider):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(id):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_last_connected(idp_user, service_provider):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def create(login, post_values, idp_user, service_provider):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_or_create(login, post_values, idp_user, service_provider):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def delete(sp_user):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def save(sp_user):
|
||||
pass
|
||||
|
||||
class DefaultServiceProvider:
|
||||
|
||||
@staticmethod
|
||||
def get(name):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def create(name):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_or_create(name):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def delete(service_provider):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def save(service_provider):
|
||||
pass
|
||||
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
|
||||
from datetime import datetime
|
||||
|
||||
from mandaye.db import sql_session
|
||||
from mandaye.models import IDPUser, SPUser, ServiceProvider
|
||||
|
||||
class ManagerIDPUserSQL:
|
||||
|
||||
@staticmethod
|
||||
def get(unique_id, idp_id='default'):
|
||||
idp_user = sql_session().query(IDPUser).\
|
||||
filter_by(unique_id=unique_id,
|
||||
idp_id='default')
|
||||
if len(idp_user) > 1:
|
||||
logger.critical('ManagerIDPUserSQL.get %s not unique' % unique_id)
|
||||
raise MandayeException(
|
||||
'ManagerIDPUserSQL.get : %s is not unique' % unique_id)
|
||||
if idp_user:
|
||||
return idp_user.first()
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def create(unique_id, idp_id='default'):
|
||||
idp_user = IDPUser(
|
||||
unique_id=unique_id,
|
||||
idp_id=idp_id)
|
||||
sql_session().add(idp_user)
|
||||
return idp_user
|
||||
|
||||
@staticmethod
|
||||
def get_or_create(unique_id, idp_id='default'):
|
||||
if ManagerIDPUserSQL.get(**kwargs):
|
||||
return user
|
||||
else:
|
||||
return ManagerIDPUserSQL.create(**kwargs)
|
||||
|
||||
@staticmethod
|
||||
def delete(idp_user):
|
||||
sql_session().delete(idp_user)
|
||||
sql_session().commit()
|
||||
|
||||
@staticmethod
|
||||
def save():
|
||||
sql_session().commit()
|
||||
|
||||
class ManagerSPUserSQL:
|
||||
|
||||
@staticmethod
|
||||
def get(login, idp_user, service_provider):
|
||||
sp_user = sql_session().query(SPPUser).\
|
||||
join(IDPUser).\
|
||||
join(ServiceProvider).\
|
||||
filter_by(login=login,
|
||||
idp_user=idp_user,
|
||||
service_provider=service_provider)
|
||||
if sp_user:
|
||||
return sp_user.first()
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(id):
|
||||
return sql_session().query(SPUser).\
|
||||
filter(id==id).first()
|
||||
|
||||
@staticmethod
|
||||
def get_last_connected(idp_user, service_provider):
|
||||
return sql_session().query(SPPUser).\
|
||||
join(IDPUser).\
|
||||
join(ServiceProvider).\
|
||||
filter(idp_user=idp_user).\
|
||||
filer(service_provider=service_provider).\
|
||||
order_by(SPUser.last_connection.desc()).\
|
||||
first()
|
||||
@staticmethod
|
||||
def get_sp_users(idp_unique_id, service_provider_name):
|
||||
return sql_session().query(SPUser).\
|
||||
join(IDPUser).\
|
||||
join(ServiceProvider).\
|
||||
filter(IDPUser.unique_id==idp_unique_id).\
|
||||
filter(ServiceProvider.name==service_provider_name).\
|
||||
order_by(SPUser.last_connection.desc()).\
|
||||
all()
|
||||
|
||||
@staticmethod
|
||||
def create(login, post_values, idp_user, service_provider):
|
||||
sp_user = SPUser(
|
||||
login=login,
|
||||
post_values=post_values,
|
||||
idp_id=idp_id,
|
||||
service_provider = service_provider
|
||||
)
|
||||
logger.info('New association: %s with %s on site %s' % \
|
||||
(login, idp_user.unique_id, service_provider.name))
|
||||
sql_session().add(sp_user)
|
||||
sql_session().commit()
|
||||
return idp_user
|
||||
|
||||
@staticmethod
|
||||
def get_or_create(login, post_values, idp_user, service_provider):
|
||||
sp_user = ManagerSPUserSQL.get(login, idp_user, service_provider)
|
||||
if sp_user:
|
||||
return sp_user
|
||||
else:
|
||||
return ManagerSPUserSQL.create(login, post_values,
|
||||
idp_user, service_provider)
|
||||
@staticmethod
|
||||
def all():
|
||||
return sql_session().query(SPUser).all()
|
||||
|
||||
@staticmethod
|
||||
def delete(sp_user):
|
||||
logger.debug('Disassociate account %s' % sp_user.login)
|
||||
sql_session().delete(sp_user)
|
||||
sql_session().commit()
|
||||
|
||||
@staticmethod
|
||||
def save():
|
||||
sql_session().commit()
|
||||
|
||||
class ManagerServiceProviderSQL:
|
||||
|
||||
@staticmethod
|
||||
def get(name):
|
||||
sp = sql_session().query(ServiceProvider).\
|
||||
filter_by(name=name)
|
||||
if sp:
|
||||
return sp.first()
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def create(name):
|
||||
logger.info('Add %s service provider into the database' % name)
|
||||
sp = ServiceProvider(name=name)
|
||||
sql_session().add(sp)
|
||||
sql_session().commit()
|
||||
return sp
|
||||
|
||||
@staticmethod
|
||||
def get_or_create(name):
|
||||
sp = ManagerServiceProviderSQL.get(name)
|
||||
if sp:
|
||||
return sp
|
||||
else:
|
||||
return ManagerServiceProviderSQL.create(name)
|
||||
|
||||
@staticmethod
|
||||
def delete(service_provider):
|
||||
logger.debug('Delete service provider %s' % service_provider.name)
|
||||
sql_session().delete(service_provider)
|
||||
sql_session().commit()
|
||||
|
||||
@staticmethod
|
||||
def save():
|
||||
sql_session().commit()
|
||||
|
||||
ManagerServiceProvider = ManagerServiceProviderSQL
|
||||
ManagerSPUser = ManagerSPUserSQL
|
||||
ManagerServiceProvider = ManagerServiceProviderSQL
|
||||
|
|
@ -1,6 +1,14 @@
|
|||
import logging
|
||||
from mandaye.exceptions import ImproperlyConfigured
|
||||
|
||||
# Database configuration
|
||||
# rfc 1738 http://rfc.net/rfc1738.html
|
||||
db_url = 'sqlite:///test.db'
|
||||
|
||||
# Default local backend
|
||||
import mandaye.backends.sql
|
||||
backend = mandaye.backends.sql
|
||||
|
||||
# Needed if ssl is activated
|
||||
ssl = False
|
||||
keyfile = ''
|
||||
|
@ -11,6 +19,7 @@ debug = False
|
|||
syslog = False
|
||||
log_file = '/var/log/mandaye/mandaye.log'
|
||||
log_level = logging.INFO
|
||||
|
||||
# Log rotation
|
||||
# W[0-6] : weekly (0: Monday), D: day, ... (python doc)
|
||||
log_when = 'W6'
|
||||
|
@ -24,9 +33,6 @@ template_directory = 'mandaye/templates'
|
|||
# Static folder
|
||||
static_root = 'mandaye/static'
|
||||
|
||||
# Database configuration
|
||||
# rfc 1738 http://rfc.net/rfc1738.html
|
||||
db_url = 'sqlite:///test.db'
|
||||
|
||||
# Email notification configuration
|
||||
email_notification = False
|
||||
|
@ -42,9 +48,9 @@ use_long_trace = True
|
|||
# Decompress response only if you load a filter
|
||||
auto_decompress = True
|
||||
|
||||
# Encrypt external passwords with a secret
|
||||
# Encrypt service provider passwords with a secret
|
||||
# You should install pycypto to use this feature
|
||||
encrypt_ext_password = False
|
||||
encrypt_sp_password = False
|
||||
# Must be a 16, 24, or 32 bytes long
|
||||
encrypt_secret = ''
|
||||
|
||||
|
|
|
@ -9,3 +9,4 @@ class ImproperlyConfigured(Exception):
|
|||
class MandayeException(Exception):
|
||||
"Mandaye generic exception"
|
||||
pass
|
||||
|
||||
|
|
|
@ -6,12 +6,13 @@ from urlparse import parse_qs
|
|||
from BeautifulSoup import BeautifulSoup
|
||||
import lxml.html
|
||||
|
||||
from mandaye.db import sql_session
|
||||
from mandaye.log import logger
|
||||
from mandaye.models import Site, ExtUser, LocalUser
|
||||
from mandaye.response import _302, _401
|
||||
from mandaye.template import serve_template
|
||||
|
||||
from mandaye.config.backend import ManagerSPUser
|
||||
|
||||
def get_associate_form(env, values):
|
||||
""" Return association template content
|
||||
"""
|
||||
|
@ -37,8 +38,7 @@ def get_current_account(env, values):
|
|||
""" Return the current Mandaye user """
|
||||
site_name = values.get('site_name')
|
||||
if env['beaker.session'].get(site_name):
|
||||
return sql_session().query(ExtUser).\
|
||||
get(env['beaker.session'].get(site_name))
|
||||
return ManagerSPUser.get_by_id(env['beaker.session'].get(site_name))
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -46,18 +46,13 @@ def get_current_account(env, values):
|
|||
def get_multi_template(env, values, current_account):
|
||||
""" return the content of the multi account template
|
||||
"""
|
||||
login = env['beaker.session'].get('login')
|
||||
unique_id = env['beaker.session'].get('unique_id')
|
||||
if login:
|
||||
ext_users = sql_session().query(ExtUser).\
|
||||
join(LocalUser).\
|
||||
join(Site).\
|
||||
filter(LocalUser.login==login).\
|
||||
filter(Site.name==values.get('site_name')).\
|
||||
order_by(ExtUser.last_connection.desc()).\
|
||||
all()
|
||||
accounts = {}
|
||||
for ext_user in ext_users:
|
||||
accounts[ext_user.id] = ext_user.login
|
||||
sp_users = ManagerSPUser.get_sp_users(unique_id,
|
||||
values.get('site_name'))
|
||||
for sp_user in sp_users:
|
||||
accounts[sp_user.id] = sp_user.login
|
||||
if current_account:
|
||||
current_login = current_account.login
|
||||
else:
|
||||
|
@ -113,7 +108,7 @@ class Biblio:
|
|||
""" Modify response html to support multi accounts
|
||||
"""
|
||||
if response.msg and '<h2><div>Mon compte</div></h2>' in response.msg:
|
||||
if env['beaker.session'].get('login'):
|
||||
if env['beaker.session'].get('unique_id'):
|
||||
current_account = get_current_account(env, values)
|
||||
template = get_multi_template(env, values, current_account)
|
||||
if current_account:
|
||||
|
@ -177,7 +172,7 @@ class EspaceFamille:
|
|||
"""
|
||||
if response.msg and\
|
||||
'<!-- Navigation -->' in response.msg:
|
||||
login = env['beaker.session'].get('login')
|
||||
login = env['beaker.session'].get('unique_id')
|
||||
current_account = get_current_account(env, values)
|
||||
if login and current_account:
|
||||
disassociate = serve_template('famille/disassociate.html',
|
||||
|
@ -225,7 +220,7 @@ class Duonet:
|
|||
"""
|
||||
if response.msg \
|
||||
and 'font-weight:bold;">Conservatoire de Vincennes' in response.msg:
|
||||
login = env['beaker.session'].get('login')
|
||||
login = env['beaker.session'].get('unique_id')
|
||||
site_name = values.get('site_name')
|
||||
current_account = get_current_account(env, values)
|
||||
if login and current_account:
|
||||
|
|
|
@ -1,18 +1,118 @@
|
|||
import collections
|
||||
import json
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import *
|
||||
from migrate import *
|
||||
from sqlalchemy import Column, Integer, String, DateTime
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.ext.mutable import Mutable
|
||||
from sqlalchemy.orm import column_property, relationship, backref
|
||||
from sqlalchemy.types import TypeDecorator, VARCHAR
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class JSONEncodedDict(TypeDecorator):
|
||||
"Represents an immutable structure as a json-encoded string."
|
||||
|
||||
impl = VARCHAR
|
||||
|
||||
def process_bind_param(self, value, dialect):
|
||||
if value is not None:
|
||||
value = json.dumps(value)
|
||||
return value
|
||||
|
||||
def process_result_value(self, value, dialect):
|
||||
if value is not None:
|
||||
value = json.loads(value)
|
||||
return value
|
||||
|
||||
class MutationDict(Mutable, dict):
|
||||
|
||||
@classmethod
|
||||
def coerce(cls, key, value):
|
||||
""" Convert plain dictionaries to MutationDict. """
|
||||
if not isinstance(value, MutationDict):
|
||||
if isinstance(value, dict):
|
||||
return MutationDict(value)
|
||||
# this call will raise ValueError
|
||||
return Mutable.coerce(key, value)
|
||||
else:
|
||||
return value
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
""" Detect dictionary set events and emit change events. """
|
||||
dict.__setitem__(self, key, value)
|
||||
self.changed()
|
||||
|
||||
def __delitem__(self, key):
|
||||
""" Detect dictionary del events and emit change events. """
|
||||
dict.__delitem__(self, key)
|
||||
self.changed()
|
||||
|
||||
MutationDict.associate_with(JSONEncodedDict)
|
||||
|
||||
class ServiceProvider(Base):
|
||||
|
||||
__tablename__ = 'service_provider'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(50), unique=True, nullable=False)
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "<ServiceProvider('%s')>" % (self.name)
|
||||
|
||||
class IDPUser(Base):
|
||||
|
||||
__tablename__ = 'idp_user'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
# Nameid, pseudo, email, ...
|
||||
unique_id = Column(String(150), nullable=False)
|
||||
# Entityid
|
||||
idp_id = Column(String(150), nullable=False)
|
||||
sp_users = relationship("SPUser", backref=backref('idp_user'))
|
||||
|
||||
def __init__(self, unique_id=None, idp_id=None, sp_users=None):
|
||||
self.unique_id = unique_id
|
||||
self.idp_id = idp_id
|
||||
self.sp_users = sp_users
|
||||
|
||||
def __repr__(self):
|
||||
return "<IDPUser %d '%s'>" % (self.id, self.unique_id)
|
||||
|
||||
|
||||
class SPUser(Base):
|
||||
|
||||
__tablename__ = 'sp_user'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
post_values = Column(JSONEncodedDict, nullable=False)
|
||||
creation_date = Column(DateTime, default=datetime.now(), nullable=False)
|
||||
last_connection = Column(DateTime, default=datetime.now())
|
||||
|
||||
idp_user_id = Column(Integer, ForeignKey('idp_user.id'))
|
||||
service_provider_id = Column(Integer, ForeignKey('service_provider.id'),
|
||||
nullable=False)
|
||||
service_provider = relationship("ServiceProvider", backref=backref('users'))
|
||||
|
||||
def __init__(self, post_values=None):
|
||||
self.post_values = post_values
|
||||
|
||||
def __repr__(self):
|
||||
return "<SPUser '%d'>" % (self.id)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = MetaData(bind=migrate_engine)
|
||||
ext_users = Table('ext_users', meta, autoload=True)
|
||||
creation_date = Column("creation_date", DateTime, default=datetime.now())
|
||||
creation_date.create(ext_users)
|
||||
ServiceProvider.__table__.create(migrate_engine)
|
||||
SPUser.__table__.create(migrate_engine)
|
||||
IDPUser.__table__.create(migrate_engine)
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta = MetaData(bind=migrate_engine)
|
||||
ext_users = Table('ext_users', meta, autoload=True)
|
||||
ext_users.c.creation_date.drop()
|
||||
ServiceProvider.__table__.drop(migrate_engine)
|
||||
SPUser.__table__.drop(migrate_engine)
|
||||
IDPUser.__table__.drop(migrate_engine)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
""" This models is used if you are using form replay authentification
|
||||
"""
|
||||
import collections
|
||||
import json
|
||||
|
||||
|
@ -11,6 +12,7 @@ from sqlalchemy.ext.mutable import Mutable
|
|||
from sqlalchemy.orm import column_property, relationship, backref
|
||||
from sqlalchemy.types import TypeDecorator, VARCHAR
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class JSONEncodedDict(TypeDecorator):
|
||||
"Represents an immutable structure as a json-encoded string."
|
||||
|
@ -51,10 +53,10 @@ class MutationDict(Mutable, dict):
|
|||
self.changed()
|
||||
|
||||
MutationDict.associate_with(JSONEncodedDict)
|
||||
Base = declarative_base()
|
||||
|
||||
class Site(Base):
|
||||
__tablename__ = 'sites'
|
||||
class ServiceProvider(Base):
|
||||
|
||||
__tablename__ = 'service_provider'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(50), unique=True, nullable=False)
|
||||
|
@ -63,52 +65,46 @@ class Site(Base):
|
|||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "<Site('%s')>" % (self.name)
|
||||
return "<ServiceProvider('%s')>" % (self.name)
|
||||
|
||||
class LocalUser(Base):
|
||||
""" Mandaye's user
|
||||
"""
|
||||
__tablename__ = 'local_users'
|
||||
class IDPUser(Base):
|
||||
|
||||
__tablename__ = 'idp_user'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
login = Column(String(150), nullable=False, unique=True)
|
||||
password = Column(String(25), nullable=True)
|
||||
firstname = Column(String(150), nullable=True)
|
||||
lastname = Column(String(150), nullable=True)
|
||||
fullname = column_property(firstname + " " + lastname)
|
||||
# Nameid, pseudo, email, ...
|
||||
unique_id = Column(String(150), nullable=False)
|
||||
# Entityid
|
||||
idp_id = Column(String(150), nullable=False)
|
||||
sp_users = relationship("SPUser", backref=backref('idp_user'))
|
||||
|
||||
creation_date = Column(DateTime, default=datetime.now(), nullable=False)
|
||||
last_connection = Column(DateTime, default=datetime.now())
|
||||
|
||||
def __init__(self, login=None, password=None, fullname=None):
|
||||
self.login = login
|
||||
self.password = password
|
||||
self.fullname = fullname
|
||||
def __init__(self, unique_id=None, idp_id=None, sp_users=None):
|
||||
self.unique_id = unique_id
|
||||
self.idp_id = idp_id
|
||||
self.sp_users = sp_users
|
||||
|
||||
def __repr__(self):
|
||||
return "<LocalUser('%d %s')>" % (self.id, self.fullname)
|
||||
return "<IDPUser %d '%s'>" % (self.id, self.unique_id)
|
||||
|
||||
class ExtUser(Base):
|
||||
""" User of externals applications
|
||||
"""
|
||||
__tablename__ = 'ext_users'
|
||||
|
||||
class SPUser(Base):
|
||||
|
||||
__tablename__ = 'sp_user'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
login = Column(String(150), nullable=False)
|
||||
post_values = Column(JSONEncodedDict)
|
||||
post_values = Column(JSONEncodedDict, nullable=False)
|
||||
creation_date = Column(DateTime, default=datetime.now(), nullable=False)
|
||||
last_connection = Column(DateTime, default=datetime.now())
|
||||
|
||||
local_user_id = Column(Integer, ForeignKey('local_users.id'), nullable=False)
|
||||
site_id = Column(Integer, ForeignKey('sites.id'), nullable=False)
|
||||
local_user = relationship("LocalUser", backref=backref('ext_users'))
|
||||
site = relationship("Site", backref=backref('users'))
|
||||
idp_user_id = Column(Integer, ForeignKey('idp_user.id'))
|
||||
service_provider_id = Column(Integer, ForeignKey('service_provider.id'),
|
||||
nullable=False)
|
||||
service_provider = relationship("ServiceProvider", backref=backref('users'))
|
||||
|
||||
def __init__(self, login=None, post_values=None):
|
||||
self.login = login
|
||||
def __init__(self, post_values=None):
|
||||
self.post_values = post_values
|
||||
|
||||
def __repr__(self):
|
||||
return "<ExtUser '%d'>" % (self.id)
|
||||
|
||||
return "<SPUser '%d'>" % (self.id)
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ def _500(path, msg, exception=None, env=None):
|
|||
tb_str = _get_traceback()
|
||||
if email:
|
||||
try:
|
||||
email.sent('Mandaye internal server error',
|
||||
email.sent('Mandaye internal server error %s' % exception,
|
||||
_get_text_error(tb_str, path, env=env))
|
||||
except Exception as detail:
|
||||
logger.warning('Sent mail failed with error: %s' % detail)
|
||||
|
|
|
@ -10,12 +10,9 @@ from optparse import OptionParser
|
|||
|
||||
from mandaye import config
|
||||
from mandaye.log import logger
|
||||
from mandaye.models import ExtUser
|
||||
from mandaye.db import sql_session
|
||||
|
||||
|
||||
def get_cmd_options():
|
||||
usage = "usage: %prog --createdb|--cryptpwd"
|
||||
usage = "usage: %prog --newproject|--createdb|--cryptpwd"
|
||||
parser = OptionParser(usage=usage)
|
||||
parser.add_option("--createdb",
|
||||
dest="createdb",
|
||||
|
@ -29,6 +26,12 @@ def get_cmd_options():
|
|||
action="store_true",
|
||||
help="Crypt external password in Mandaye's database"
|
||||
)
|
||||
parser.add_option("--newproject",
|
||||
dest="cryptpwd",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Crypt external password in Mandaye's database"
|
||||
)
|
||||
(options, args) = parser.parse_args()
|
||||
return options
|
||||
|
||||
|
@ -56,19 +59,17 @@ def main():
|
|||
if config.db_url:
|
||||
import migrate.versioning.api
|
||||
import mandaye.migration
|
||||
from sqlalchemy import create_engine
|
||||
from mandaye.models import Base
|
||||
engine = create_engine(config.db_url)
|
||||
Base.metadata.create_all(engine)
|
||||
migrate.versioning.api.version_control(url=config.db_url,
|
||||
repository=mandaye.migration.__path__[0])
|
||||
migrate.versioning.api.upgrade(url=config.db_url,
|
||||
repository=mandaye.migration.__path__[0])
|
||||
logger.info("Database created")
|
||||
if options.cryptpwd:
|
||||
for user in sql_session().query(ExtUser).all():
|
||||
from mandaye.config.backend import ManagerSPUserSQL
|
||||
for user in ManagerSPUserSQL.all():
|
||||
user.password = encrypt_pwd(user.password)
|
||||
sql_session().commit()
|
||||
ManagerSPUserSQL.save()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
Beaker==1.5.4
|
||||
Mako==0.4.2
|
||||
MarkupSafe==0.15
|
||||
SQLAlchemy==0.7.2
|
||||
distribute==0.6.10
|
||||
gevent==0.13.6
|
||||
greenlet==0.3.1
|
||||
importlib==1.0.2
|
||||
poster==0.8.1
|
||||
static==0.4
|
||||
wsgiref==0.1.2
|
||||
xtraceback==0.3
|
|
@ -0,0 +1,7 @@
|
|||
beaker>=1.6
|
||||
poster>=0.8
|
||||
pycrypto>=2.0
|
||||
lxml>=2.0
|
||||
xtraceback>=0.3
|
||||
sqlalchemy>=0.7,<0.8
|
||||
static
|
34
setup.py
34
setup.py
|
@ -7,6 +7,23 @@
|
|||
import mandaye
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from sys import version
|
||||
|
||||
install_requires=[
|
||||
'beaker>=1.6',
|
||||
'gunicorn>=0.13',
|
||||
'mako>=0.3',
|
||||
'poster>=0.8',
|
||||
'pycrypto>=2.0',
|
||||
'sqlalchemy>=0.6',
|
||||
'sqlalchemy-migrate>=0.7.2',
|
||||
'lxml>=2.0',
|
||||
'xtraceback>=0.3',
|
||||
'static',
|
||||
]
|
||||
|
||||
if version < '2.7':
|
||||
install_requires.append('importlib')
|
||||
|
||||
setup(name="mandaye",
|
||||
version=mandaye.VERSION,
|
||||
|
@ -17,20 +34,9 @@ setup(name="mandaye",
|
|||
author_email="info@entrouvert.org",
|
||||
maintainer="Jerome Schneider",
|
||||
maintainer_email="jschneider@entrouvert.com",
|
||||
scripts=['mandaye_server.py', 'mandaye_admin.py', 'mandaye_migrate.py'],
|
||||
scripts=['mandaye_admin.py', 'mandaye_migrate.py'],
|
||||
packages=find_packages(),
|
||||
package_data={},
|
||||
install_requires=[
|
||||
'beaker>=1.6',
|
||||
'gunicorn>=0.13',
|
||||
'importlib',
|
||||
'mako>=0.3',
|
||||
'poster>=0.8',
|
||||
'pycrypto>=2.0',
|
||||
'sqlalchemy>=0.6',
|
||||
'sqlalchemy-migrate>=0.7.2',
|
||||
'lxml>=2.0',
|
||||
'xtraceback>=0.3',
|
||||
'static',
|
||||
],
|
||||
install_requires=install_requires
|
||||
)
|
||||
|
||||
|
|
Reference in New Issue