initial import for branch 0.8

This is a complete rewrite of the configuration system

Fixes #4666
This commit is contained in:
Jérôme Schneider 2014-05-21 13:15:40 +02:00
parent 5c65415c46
commit de6e5967b5
25 changed files with 384 additions and 369 deletions

View File

@ -1,4 +1,4 @@
VERSION=0.4
__version__='0.8.0'
import os

View File

@ -15,7 +15,7 @@ from datetime import datetime
from lxml.html import fromstring
from urlparse import parse_qs
from mandaye import config, VERSION
from mandaye import config, __version__
from mandaye.exceptions import MandayeException
from mandaye.log import logger
from mandaye.http import HTTPResponse, HTTPHeader, HTTPRequest
@ -32,45 +32,41 @@ except ImportError:
class AuthForm(object):
def __init__(self, form_values, site_name):
def __init__(self, env, mapper):
"""
form_values: dict example :
{
'login_url': '/login',
'post_url': '/login',
'form_attrs': { 'name': 'form40', },
'username_field': 'user',
'password_field': 'pass',
'post_fields': ['birthdate', 'card_number']
}
login_url, form_attrs, post_fields and username_field are obligatory
site_name: str with the site name
env: WSGI environment
mapper: mapper's module like mandaye.mappers.linuxfr
"""
if not form_values.has_key('form_headers'):
form_values['form_headers'] = {
self.env = env
self.urls = mapper.urls
self.site_name = self.env["mandaye.config"]["site_name"]
self.form_values = mapper.form_values
if not self.form_values.has_key('form_headers'):
self.form_values['form_headers'] = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 Mandaye/%s' % VERSION
'User-Agent': 'Mozilla/5.0 Mandaye/%s' % __version__
}
if not form_values.has_key('post_fields') or \
not form_values.has_key('username_field') or \
not form_values.has_key('login_url'):
if not self.form_values.has_key('post_fields') or \
not self.form_values.has_key('username_field') or \
not self.form_values.has_key('login_url'):
logger.critical("Bad configuration: AuthForm form_values dict must have \
this keys: post_fields and username_field")
raise MandayeException, 'AuthForm bad configuration'
if not form_values.has_key('form_attrs') and \
not form_values.has_key('post_url'):
if not self.form_values.has_key('form_attrs') and \
not self.form_values.has_key('post_url'):
logger.critical("Bad configuration: you must set form_attrs or post_url")
if config.encrypt_secret and not form_values.has_key('password_field'):
if config.encrypt_secret and not self.form_values.has_key('password_field'):
logger.critical("Bad configuration: AuthForm form_values dict must have a \
a password_field key if you want to encode a password.")
raise MandayeException, 'AuthForm bad configuration'
self.login_url = form_values.get('login_url')
self.form_values = form_values
self.login_url = self.form_values.get('login_url')
if not self.form_values.has_key('post_fields'):
self.form_values['post_fields'] = []
self.site_name = site_name
def get_default_mapping(self):
return []
def encrypt_pwd(self, password):
""" This method allows you to encrypt a password
@ -207,7 +203,7 @@ a password_field key if you want to encode a password.")
if request.msg:
if not unique_id:
logger.warning("Association failed: user isn't login on Mandaye")
return _302(values.get('connection_url'))
return _302(self.urls.get('connection_url'))
if type(request.msg) == str:
post = parse_qs(request.msg, request)
else:
@ -221,7 +217,7 @@ a password_field key if you want to encode a password.")
if not post.has_key(field):
logger.info('Association auth failed: form not correctly filled')
qs['type'] = 'badlogin'
return _302(values.get('associate_url') + "?%s" % urllib.urlencode(qs))
return _302(self.urls.get('associate_url') + "?%s" % urllib.urlencode(qs))
post_values[field] = post[field][0]
response = self.replay(env, post_values)
if eval(condition):
@ -232,7 +228,7 @@ a password_field key if you want to encode a password.")
return response
logger.info('Auth failed: Bad password or login')
qs['type'] = 'badlogin'
return _302(values.get('associate_url') + "?%s" % urllib.urlencode(qs))
return _302(self.urls.get('associate_url') + "?%s" % urllib.urlencode(qs))
def _login_sp_user(self, sp_user, env, condition, values):
""" Log in sp user
@ -257,7 +253,7 @@ a password_field key if you want to encode a password.")
else:
return response
else:
return _302(values.get('associate_url') + "?type=failed")
return _302(self.urls.get('associate_url') + "?type=failed")
def login(self, env, values, condition, request, response):
""" Automatic login on a site with a form
@ -282,7 +278,7 @@ a password_field key if you want to encode a password.")
sp_user = backend.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 _302(self.urls.get('associate_url') + "?type=first")
return self._login_sp_user(sp_user, env, condition, values)
def logout(self, env, values, request, response):
@ -333,7 +329,7 @@ a password_field key if you want to encode a password.")
idp_user = backend.ManagerIDPUser.get(unique_id)
sp_user = backend.ManagerSPUser.get_last_connected(idp_user, service_provider)
if not sp_user:
return _302(values.get('associate_url'))
return _302(self.urls.get('associate_url'))
return self._login_sp_user(sp_user, env, 'response.code==302', values)
def disassociate(self, env, values, request, response):

View File

@ -9,87 +9,120 @@ from urlparse import parse_qs
from mandaye import config, utils
from mandaye.saml import saml2utils
from mandaye.auth.authform import AuthForm
from mandaye.exceptions import MandayeSamlException
from mandaye.exceptions import MandayeSamlException, ImproperlyConfigured
from mandaye.response import _302, _401
from mandaye.log import logger
from mandaye.http import HTTPResponse, HTTPHeader, HTTPRequest
from mandaye.server import get_response
"""
Mandaye saml2 authentification support
To use it you must set the following options into your
virtual host :
* saml2_idp_metadata: a link to your idp metadata
* saml2_signature_public_key: a path to your public key
* saml2_signature_private_key: a path to your private key
Optional options :
* saml2_sp_logout_url: the url to logout the service provider
* saml2_sp_logout_method: GET or POST
* saml2_authnresp_binding: only post is supported for now
* saml2_authnreq_http_method: only http_redirect at the moment
* saml2_name_identifier_format: only persistent at the moment
"""
END_POINTS_PATH = {
'metadata': '/mandaye/metadata',
'single_sign_on_post': '/mandaye/singleSignOnPost',
'single_logout': '/mandaye/singleLogout',
'single_logout_return': '/mandaye/singleLogoutReturn',
}
class SAML2Auth(AuthForm):
""" SAML 2 authentification
"""
def __init__(self, form_values, site_name, saml2_config):
def __init__(self, env, mapper):
""" saml2_config: saml 2 config module
env: WSGI environment
mapper: mapper's module like mandaye.mappers.linuxfr
"""
self.config = saml2_config
self.env = env
for param in ('saml2_idp_metadata',
'saml2_signature_public_key',
'saml2_signature_private_key'):
if not self.env['mandaye.config'].has_key(param):
err = 'you must set %s option in vhost : %s' % \
(param, self.env['mandaye.vhost'])
logger.error(err)
raise ImproperlyConfigured, err
public_key = self._get_file_content(
self.env['mandaye.config']['saml2_signature_public_key']
)
private_key = self._get_file_content(
self.env['mandaye.config']['saml2_signature_private_key']
)
self.config = {
'saml2_idp_metadata': self.env['mandaye.config']['saml2_idp_metadata'],
'saml2_signature_public_key': public_key,
'saml2_signature_private_key': private_key,
'saml2_sp_logout_url': None,
'saml2_sp_logout_method': 'GET',
'saml2_authnresp_binding': lasso.SAML2_METADATA_BINDING_POST,
'saml2_authnreq_http_method': lasso.HTTP_METHOD_REDIRECT,
'saml2_name_identifier_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT
}
self.metadata_map = (
('AssertionConsumerService',
lasso.SAML2_METADATA_BINDING_POST ,
self.config.END_POINTS_PATH['single_sign_on_post']
END_POINTS_PATH['single_sign_on_post']
),
('SingleLogoutService',
lasso.SAML2_METADATA_BINDING_REDIRECT,
self.config.END_POINTS_PATH['single_logout'],
self.config.END_POINTS_PATH['single_logout_return']),
END_POINTS_PATH['single_logout'],
END_POINTS_PATH['single_logout_return']),
)
self.metadata_options = { 'key': self.config.SAML_SIGNATURE_PUBLIC_KEY }
super(SAML2Auth, self).__init__(form_values, site_name)
self.metadata_options = { 'key': public_key }
super(SAML2Auth, self).__init__(env, mapper)
def _get_file_content(self, path):
if not os.path.isabs(path):
path = os.path.join(config.config_root,
path)
if not os.path.exists(path):
err = "%s: file %s doesn't exist" % \
(path, self.env['mandaye.vhost'])
logger.error(err)
raise ImproperlyConfigured, err
with open(path, 'r') as f:
content = f.read()
return content
def get_default_mapping(self):
return [
{
'path': r'/mandaye/login$',
'method': 'GET',
'response': [{
'filter': self.login,
'values': {
'associate_url': '/mandaye/associate',
},
'condition': 'response.code==302',
},]
},
{
'path': r'/mandaye/sso$',
'method': 'GET',
'response': [{
'filter': self.sso,
}]
},
{
'path': r'/mandaye/slo$',
'method': 'GET',
'response': [{
'filter': self.slo,
}]
},
{
'path': r'%s$' % self.config.END_POINTS_PATH['metadata'],
'path': r'%s$' % END_POINTS_PATH['metadata'],
'method': 'GET',
'response': [{
'filter': self.metadata,
}]
},
{
'path': r'%s$' % self.config.END_POINTS_PATH['single_sign_on_post'],
'path': r'%s$' % END_POINTS_PATH['single_sign_on_post'],
'method': 'POST',
'response': [{
'filter': self.single_sign_on_post,
'values': {
'login_url': '/mandaye/login',
}
}]
'response': [{'filter': self.single_sign_on_post}]
},
{
'path': r'%s$' % self.config.END_POINTS_PATH['single_logout'],
'path': r'%s$' % END_POINTS_PATH['single_logout'],
'method': 'GET',
'response': [{
'filter': self.single_logout,
}]
},
{
'path': r'%s$' % self.config.END_POINTS_PATH['single_logout_return'],
'path': r'%s$' % END_POINTS_PATH['single_logout_return'],
'method': 'GET',
'response': [{
'filter': self.single_logout_return,
@ -112,7 +145,7 @@ class SAML2Auth(AuthForm):
next_url = values['next_url']
req_cookies = request.cookies
if not self.config.SP_LOGOUT_URL:
if not self.config['saml2_sp_logout_url']:
logger.warning('SP_LOGOUT_URL not set into saml2 configuration only removing cookies')
for cookie in req_cookies.values():
cookie['expires'] = 'Thu, 01 Jan 1970 00:00:01 GMT'
@ -121,12 +154,12 @@ class SAML2Auth(AuthForm):
return _302(next_url, req_cookies)
else:
return _302('/', req_cookies)
if self.config.SP_LOGOUT_METHOD == 'POST':
if self.config['saml2_sp_logout_method'] == 'POST':
headers = HTTPHeader({'Content-Type': ['application/x-www-form-urlencoded']})
else:
headers = HTTPHeader()
request = HTTPRequest(req_cookies, headers, self.config.SP_LOGOUT_METHOD)
response = get_response(env, request, self.config.SP_LOGOUT_URL)
request = HTTPRequest(req_cookies, headers, self.config['saml2_sp_logout_method'])
response = get_response(env, request, self.config['saml2_sp_logout_url'])
if next_url:
return _302(next_url, response.cookies)
else:
@ -134,31 +167,31 @@ class SAML2Auth(AuthForm):
def _get_idp_metadata_file_path(self):
metadata_file_path = None
if self.config.IDP_METADATA:
if self.config['saml2_idp_metadata']:
metadata_file_path = os.path.join(config.data_dir,
self.config.IDP_METADATA.\
self.config['saml2_idp_metadata'].\
replace('://', '_').\
replace('/', '_')
)
if not os.path.isfile(metadata_file_path):
try:
response = urllib2.urlopen(self.config.IDP_METADATA)
response = urllib2.urlopen(self.config['saml2_idp_metadata'])
metadata = response.read()
response.close()
except Exception, e:
logger.error("Unable to fetch metadata %s: %s" % \
(self.config.IDP_METADATA, str(e)))
(self.config['saml2_idp_metadata'], str(e)))
raise MandayeSamlException("Unable to find metadata: %s" % str(e))
metadata_file = open(metadata_file_path, 'a+')
metadata_file = open(metadata_file_path, 'w')
metadata_file.write(metadata)
metadata_file.close()
return metadata_file_path
def _get_metadata(self, env):
url_prefix = env['mandaye.scheme'] + '://' + env['HTTP_HOST']
metadata_path = self.config.END_POINTS_PATH['metadata']
metadata_path = END_POINTS_PATH['metadata']
single_sign_on_post_path = \
self.config.END_POINTS_PATH['single_sign_on_post']
END_POINTS_PATH['single_sign_on_post']
metagen = saml2utils.Saml2Metadata(url_prefix + metadata_path,
url_prefix = url_prefix)
metagen.add_sp_descriptor(self.metadata_map, self.metadata_options)
@ -168,15 +201,15 @@ class SAML2Auth(AuthForm):
if env['beaker.session'].has_key('unique_id'):
return _302('/')
qs = parse_qs(env['QUERY_STRING'])
target_idp = self.config.IDP_METADATA
target_idp = self.config['saml2_idp_metadata']
metadata_file_path = self._get_idp_metadata_file_path()
if not metadata_file_path:
raise MandayeSamlException("sso: unable to load provider")
logger.debug('sso: target_idp is %s' % target_idp)
logger.debug('sso: metadata url is %s' % self.config.IDP_METADATA)
logger.debug('sso: metadata url is %s' % self.config['saml2_idp_metadata'])
logger.debug('sso: mandaye metadata are %s' % self._get_metadata(env))
server = lasso.Server.newFromBuffers(self._get_metadata(env),
self.config.SAML_SIGNATURE_PRIVATE_KEY)
self.config['saml2_signature_private_key'])
if not server:
raise MandayeSamlException("sso: error creating server object.")
logger.debug('sso: mandaye server object created')
@ -184,15 +217,15 @@ class SAML2Auth(AuthForm):
login = lasso.Login(server)
if not login:
raise MandayeSamlException("sso: error creating login object.")
http_method = self.config.AUTHNREQ_HTTP_METHOD
http_method = self.config['saml2_authnreq_http_method']
try:
login.initAuthnRequest(target_idp, http_method)
except lasso.Error, error:
raise MandayeSamlException("sso: Error initiating request %s" % lasso.strError(error[0]))
login.request.nameIDPolicy.format = self.config.SAML2_NAME_IDENTIFIER_FORMAT
login.request.nameIDPolicy.allowCreate = self.config.ALLOW_CREATE
login.request.nameIDPolicy.spNameQualifier = self.config.SP_NAME_QUALIFIER
login.request.protocolBinding = self.config.AUTHNRESP_BINDING
login.request.nameIDPolicy.format = self.config['saml2_name_identifier_format']
login.request.nameIDPolicy.allowCreate = True
login.request.nameIDPolicy.spNameQualifier = None
login.request.protocolBinding = self.config['saml2_authnresp_binding']
if qs.has_key('next_url'):
login.msgRelayState = qs['next_url'][0]
try:
@ -213,7 +246,7 @@ class SAML2Auth(AuthForm):
if not metadata_file_path:
raise MandayeSamlException("single_sign_on_post: Unable to load provider")
server = lasso.Server.newFromBuffers(self._get_metadata(env),
self.config.SAML_SIGNATURE_PRIVATE_KEY)
self.config['saml2_signature_private_key'])
if not server:
raise MandayeSamlException("singleSignOnPost: error creating server object")
server.addProvider(lasso.PROVIDER_ROLE_IDP, metadata_file_path)
@ -230,11 +263,7 @@ class SAML2Auth(AuthForm):
raise MandayeSamlException("singleSignOnPost: Missing response")
message = params[lasso.SAML2_FIELD_RESPONSE][0]
logger.debug('sso: message posted %s' % message)
try:
login.processAuthnResponseMsg(message)
except:
raise MandayeSamlException("singleSignOnPost: Unable to proces authnresponse message")
login.processAuthnResponseMsg(message)
subject_confirmation = utils.get_absolute_uri(env)
if not env['beaker.session'].has_key('request_id'):
@ -268,16 +297,16 @@ class SAML2Auth(AuthForm):
next_url = values['next_url']
if next_url:
return _302("%s?next_url=%s" % (values['login_url'], next_url))
return _302("%s?next_url=%s" % (self.urls.get('login_url'), next_url))
else:
return _302(values['login_url'])
return _302(self.urls.get('login_url'))
def slo(self, env, values, request, response):
"""
Single Logout SP initiated by redirected
"""
logger.debug('slo: new slo request')
target_idp = self.config.IDP_METADATA
target_idp = self.config['saml2_idp_metadata']
metadata_file_path = self._get_idp_metadata_file_path()
if not metadata_file_path:
raise MandayeSamlException("slo: Unable to load provider.")
@ -285,7 +314,7 @@ class SAML2Auth(AuthForm):
logger.debug('slo: metadata file path %s' % metadata_file_path)
server = lasso.Server.newFromBuffers(self._get_metadata(env),
self.config.SAML_SIGNATURE_PRIVATE_KEY)
self.config['saml2_signature_private_key'])
if not server:
raise MandayeSamlException("slo: Error creating server object")
logger.debug('slo: mandaye server object created')
@ -339,7 +368,7 @@ class SAML2Auth(AuthForm):
Redirect without query string")
server = lasso.Server.newFromBuffers(self._get_metadata(env),
self.config.SAML_SIGNATURE_PRIVATE_KEY)
self.config['saml2_signature_private_key'])
if not server:
logger.warning('single_logout: Service provider not configured')
return _401('single_logout: Service provider not configured')
@ -378,7 +407,7 @@ class SAML2Auth(AuthForm):
"""
metadata_file_path = self._get_idp_metadata_file_path()
server = lasso.Server.newFromBuffers(self._get_metadata(env),
self.config.SAML_SIGNATURE_PRIVATE_KEY)
self.config['saml2_signature_private_key'])
server.addProvider(lasso.PROVIDER_ROLE_IDP, metadata_file_path)
if not server:
logger.error('single_logout_return: Error creating server object.')

View File

@ -1,79 +0,0 @@
import lasso
# END_POINTS
END_POINTS_PATH = {
'metadata': '/mandaye/metadata',
'single_sign_on_post': '/mandaye/singleSignOnPost',
'single_logout': '/mandaye/singleLogout',
'single_logout_return': '/mandaye/singleLogoutReturn',
}
# Now only support for a single IdP
IDP_METADATA = ("http://www.identity-hub.net/idp/saml2/metadata")
# SAML2 SP options
AUTHNREQ_HTTP_METHOD = lasso.HTTP_METHOD_REDIRECT
# Only persistent make sense
SAML2_NAME_IDENTIFIER_FORMAT = lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT
ALLOW_CREATE = True
SP_NAME_QUALIFIER = None
# Only POST is supported for now
AUTHNRESP_BINDING = lasso.SAML2_METADATA_BINDING_POST
## Configure local SP logout
# If None remove cookies to logout
SP_LOGOUT_URL = None
SP_LOGOUT_METHOD = 'GET'
# FIXME: change this keys
SAML_SIGNATURE_PUBLIC_KEY = '''-----BEGIN CERTIFICATE-----
MIIDIzCCAgugAwIBAgIJANUBoick1pDpMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV
BAoTCkVudHJvdXZlcnQwHhcNMTAxMjE0MTUzMzAyWhcNMTEwMTEzMTUzMzAyWjAV
MRMwEQYDVQQKEwpFbnRyb3V2ZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZn9Kqm4Cp
06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrUH8QT8NGh
ABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59xihSqsoFr
kmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9Hri8JRdXi
VT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziazZfvvw/VG
Tm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABo3YwdDAdBgNVHQ4EFgQUeF8ePnu0
fcAK50iBQDgAhHkOu8kwRQYDVR0jBD4wPIAUeF8ePnu0fcAK50iBQDgAhHkOu8mh
GaQXMBUxEzARBgNVBAoTCkVudHJvdXZlcnSCCQDVAaInJNaQ6TAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAy8l3GhUtpPHx0FxzbRHVaaUSgMwYKGPhE
IdGhqekKUJIx8et4xpEMFBl5XQjBNq/mp5vO3SPb2h2PVSks7xWnG3cvEkqJSOeo
fEEhkqnM45b2MH1S5uxp4i8UilPG6kmQiXU2rEUBdRk9xnRWos7epVivTSIv1Ncp
lG6l41SXp6YgIb2ToT+rOKdIGIQuGDlzeR88fDxWEU0vEujZv/v1PE1YOV0xKjTT
JumlBc6IViKhJeo1wiBBrVRIIkKKevHKQzteK8pWm9CYWculxT26TZ4VWzGbo06j
o2zbumirrLLqnt1gmBDvDvlOwC/zAAyL4chbz66eQHTiIYZZvYgy
-----END CERTIFICATE-----'''
SAML_SIGNATURE_PRIVATE_KEY = '''-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZ
n9Kqm4Cp06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrU
H8QT8NGhABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59x
ihSqsoFrkmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9H
ri8JRdXiVT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziaz
Zfvvw/VGTm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABAoIBAQCj8t2iKXya10HG
V6Saaeih8aftoLBV38VwFqqjPU0+iKqDpk2JSXBhjI6s7uFIsaTNJpR2Ga1qvns1
hJQEDMQSLhJvXfBgSkHylRWCpJentr4E3D7mnw5pRsd61Ev9U+uHcdv/WHP4K5hM
xsdiwXNXD/RYd1Q1+6bKrCuvnNJVmWe0/RV+r3T8Ni5xdMVFbRWt/VEoE620XX6c
a9TQPiA5i/LRVyie+js7Yv+hVjGOlArtuLs6ECQsivfPrqKLOBRWcofKdcf+4N2e
3cieUqwzC15C31vcMliD9Hax9c1iuTt9Q3Xzo20fOSazAnQ5YBEExyTtrFBwbfQu
ku6hp81pAoGBAN6bc6iJtk5ipYpsaY4ZlbqdjjG9KEXB6G1MExPU7SHXOhOF0cDH
/pgMsv9hF2my863MowsOj3OryVhdQhwA6RrV263LRh+JU8NyHV71BwAIfI0BuVfj
6r24KudwtUcvMr9pJIrJyMAMaw5ZyNoX7YqFpS6fcisSJYdSBSoxzrzVAoGBANu6
xVeMqGavA/EHSOQP3ipDZ3mnWbkDUDxpNhgJG8Q6lZiwKwLoSceJ8z0PNY3VetGA
RbqtqBGfR2mcxHyzeqVBpLnXZC4vs/Vy7lrzTiHDRZk2SG5EkHMSKFA53jN6S/nJ
JWpYZC8lG8w4OHaUfDHFWbptxdGYCgY4//sjeiuXAoGBANuhurJ99R5PnA8AOgEW
4zD1hLc0b4ir8fvshCIcAj9SUB20+afgayRv2ye3Dted1WkUL4WYPxccVhLWKITi
rRtqB03o8m3pG3kJnUr0LIzu0px5J/o8iH3ZOJOTE3iBa+uI/KHmxygc2H+XPGFa
HGeAxuJCNO2kAN0Losbnz5dlAoGAVsCn94gGWPxSjxA0PC7zpTYVnZdwOjbPr/pO
LDE0cEY9GBq98JjrwEd77KibmVMm+Z4uaaT0jXiYhl8pyJ5IFwUS13juCbo1z/u/
ldMoDvZ8/R/MexTA/1204u/mBecMJiO/jPw3GdIJ5phv2omHe1MSuSNsDfN8Sbap
gmsgaiMCgYB/nrTk89Fp7050VKCNnIt1mHAcO9cBwDV8qrJ5O3rIVmrg1T6vn0aY
wRiVcNacaP+BivkrMjr4BlsUM6yH4MOBsNhLURiiCL+tLJV7U0DWlCse/doWij4U
TKX6tp6oI+7MIJE6ySZ0cBqOiydAkBePZhu57j6ToBkTa0dbHjn1WA==
-----END RSA PRIVATE KEY-----'''

View File

@ -1,4 +1,5 @@
import copy
import re
from urlparse import urlparse
@ -12,21 +13,18 @@ from mandaye.exceptions import ImproperlyConfigured
# TODO: add an external url mapping
def import_mapping(path):
if not path or not '.' in path:
return path
i = path.rfind('.')
module, attr = path[:i], path[i+1:]
def import_mapping(name):
if not name:
return dict()
if not config.mappers.has_key(name):
logger.error("mapper %s not found" % name)
return dict()
module = config.mappers[name]
try:
mod = import_module(module)
mapper = import_module(module)
except ImportError, e:
raise ImproperlyConfigured('Error importing mapping %s: "%s"' % (path, e))
try:
mapping = getattr(mod, attr)
except AttributeError:
raise ImproperlyConfigured('Module "%s" does not define a "%s" mapping' % (module, attr))
return mapping
raise ImproperlyConfigured('Error importing mapping %s: "%s"' % (module, e))
return mapper
class Dispatcher(object):
@ -34,17 +32,27 @@ class Dispatcher(object):
It allows you to launch the right filter on the reqest and the response
"""
def __init__(self, env, target_url, mapping=dict()):
def __init__(self, env, target_url, mapper_name=None):
""" env: wsgi environ
target_url: the full url of your destination
mapping: dict with the site mapping
mepper: python module with the mapper
"""
self.target = urlparse(target_url)
self.env = env
self.env['target'] = self.target
self.mapping = import_mapping(mapping)
self.mapper_name = mapper_name
self.mapper = import_mapping(mapper_name)
self.filter = MandayeFilter(self.env, self.target)
self.req_mapping = self._parse_mapping(self.mapping)
auth_type = self.env['mandaye.config']['auth_type']
path = config.authentifications[auth_type]
i = path.rfind('.')
module, attr = path[:i], path[i+1:]
module = import_module(module)
Auth = getattr(module, attr)
self.auth = Auth(env, self.mapper)
mapping = copy.deepcopy(self.mapper.mapping)
mapping.extend(self.auth.get_default_mapping())
self.req_mapping = self._parse_mapping(mapping)
def __get_mappings_hooks(self, mapper, req_mapping):
""" fill the request mapping with the right hooks
@ -55,7 +63,10 @@ class Dispatcher(object):
for hookname in req_mapping:
if mapper.has_key(hookname):
if isinstance(req_mapping[hookname], list):
req_mapping[hookname].extend(mapper[hookname])
for entry in mapper[hookname]:
if entry.has_key('auth'):
entry['filter'] = getattr(self.auth, entry['auth'])
req_mapping[hookname].append(entry)
else:
req_mapping[hookname] = mapper[hookname]
return req_mapping
@ -67,7 +78,7 @@ class Dispatcher(object):
req_mapping = {
'on_request': [],
'on_response': [],
'response': None,
'response': [],
'target': None,
'redirect': None,
'decompress': config.auto_decompress
@ -75,15 +86,15 @@ class Dispatcher(object):
if not mapping:
return req_mapping
for mapper in mapping:
if mapper.has_key('path'):
if isinstance(mapper['path'], str):
if re.match(mapper['path'], self.env['PATH_INFO']):
req_mapping = self.__get_mappings_hooks(mapper, req_mapping)
for entry in mapping:
if entry.has_key('path'):
if isinstance(entry['path'], str):
if re.match(entry['path'], self.env['PATH_INFO']):
req_mapping = self.__get_mappings_hooks(entry, req_mapping)
else:
for path in mapper['path']:
for path in entry['path']:
if re.match(path, self.env['PATH_INFO']):
req_mapping = self.__get_mappings_hooks(mapper, req_mapping)
req_mapping = self.__get_mappings_hooks(entry, req_mapping)
else:
logger.warning('Config error: you need to specify paths in your mapping')
return req_mapping

View File

@ -41,7 +41,7 @@ class MandayeFilter(object):
if key == 'location':
location = value[0].replace(self.target.geturl(),
"%s://%s" % (self.env['mandaye.scheme'], self.env["HTTP_HOST"]))
response.headers.addheader(key, location)
response.headers['location'] = [location]
for name in blacklist:
if response.headers.has_key(name):
del response.headers[name]

View File

@ -9,13 +9,11 @@ storage_backend = "mandaye.backends.sql"
## SQL Backend config
# Database configuration
# rfc 1738 http://rfc.net/rfc1738.html
# http://docs.sqlalchemy.org/en/rel_0_7/core/engines.html
# rfc 1738 https://tools.ietf.org/html/rfc1738
# dialect+driver://username:password@host:port/database
db_url = 'sqlite:///test.db'
# Needed if ssl is activated
ssl = False
keyfile = ''
certfile = ''
# Log configuration
import logging
@ -34,6 +32,8 @@ log_backup = 52
# Template directory
template_directory = os.path.join(_PROJECT_PATH, 'mandaye/templates')
# Configuration directory
config_root = os.path.join(_PROJECT_PATH, 'conf.d')
# Static path
static_url = '/mandaye/static'
# Static folder
@ -64,11 +64,17 @@ encrypt_sp_password = False
# Must be a 16, 24, or 32 bytes long
encrypt_secret = ''
hosts = {}
alembic_cfg = os.path.join(_PROJECT_PATH, 'mandaye/alembic.ini')
alembic_script_path = os.path.join(_PROJECT_PATH, 'mandaye/alembic')
# supported authentification
authentifications = {
'saml2': 'mandaye.auth.SAML2Auth'
}
# supported mappers
mappers = {}
# beaker session configuration
session_opts = {
'session.type': 'file',
@ -77,3 +83,8 @@ session_opts = {
'session.data_dir': '/var/tmp/beaker'
}
# Needed if ssl is activated
ssl = False
keyfile = ''
certfile = ''

View File

@ -33,10 +33,7 @@ class HTTPHeader(dict):
def addheader(self, name, value):
""" name: field name
value: string with the field value """
if isinstance(value, str):
self[name.lower()] = [value]
else:
raise TypeError('Header value must be a string')
self[name.lower()] = [value]
def delheader(self, name):
""" name: field name

View File

@ -1,22 +1,17 @@
import Cookie
import json
import urllib2
import random
import re
import os
import sys
import time
import traceback
from beaker.middleware import SessionMiddleware
from cgi import escape
from md5 import md5
from urlparse import urlparse
from static import Cling
import mandaye
from mandaye import config
from mandaye.exceptions import ImproperlyConfigured
from mandaye.dispatcher import Dispatcher
from mandaye.log import logger, format_logging_handlers
from mandaye.handlers.default import MandayeRedirectHandler, MandayeErrorHandler
@ -84,7 +79,6 @@ class MandayeApp(object):
self.env = None
self.dispatcher = None
def __call__(self, env, start_response):
""" called by the WSGI server
env: standard WSGI env
@ -106,23 +100,39 @@ class MandayeApp(object):
(self.env['REMOTE_ADDR'], self.env['REQUEST_METHOD'],
self.env['wsgi.url_scheme'], self.env['HTTP_HOST'],
self.env['PATH_INFO']))
if config.hosts.has_key(local_host):
for mapper in config.hosts[local_host]:
if re.match(mapper.get('path'), path_info):
if mapper.get('force_ssl'):
for conf in os.listdir(config.config_root):
conf_file = os.path.join(config.config_root, conf)
if os.path.isfile(conf_file):
with open(conf_file, 'r') as f:
conf = json.loads(f.read())
for param in ['site_name', 'location', 'target', 'server_name', 'mapper', 'auth_type']:
if not conf.has_key(param):
error = 'you must set %s option in vhost : %s' % \
(param, conf_file)
logger.error(error)
raise ImproperlyConfigured, error
if not config.mappers.has_key(conf['mapper']):
err = '%s: mapper %s not found' % \
(conf_file, conf['mapper'])
logger.error(err)
raise ImproperlyConfigured, err
self.env['mandaye.auth_type'] = conf['mapper']
if not config.authentifications.has_key(conf['auth_type']):
err = '%s: authentification %s not found' % \
(conf_file, conf['auth_type'])
logger.error(err)
raise ImproperlyConfigured, err
if local_host in conf['server_name'] and \
re.match(re.compile(conf['location']), path_info):
if conf.get('force_ssl'):
self.env['mandaye.scheme'] = 'https'
if mapper.has_key('target'):
self.dispatcher = Dispatcher(self.env, mapper.get('target'),
mapper.get('mapping'))
elif mapper.has_key('static'):
env['PATH_INFO'] = re.sub('^' + mapper.get('path'), '',
env['PATH_INFO'])
response = Cling(mapper.get('static')).__call__(env, start_response)
self.env['mandaye.config'] = conf
self.env['mandaye.vhost'] = conf_file
self.dispatcher = Dispatcher(self.env, conf['target'],
conf['mapper'])
response = self.on_request(start_response)
if not response:
if self.dispatcher:
response = self.on_request(start_response)
else:
response = self.on_response(start_response, _404(env['PATH_INFO']))
response = self.on_response(start_response, _404(env['PATH_INFO']))
sql_session.commit()
except Exception, e:
sql_session.rollback()
@ -186,7 +196,7 @@ class MandayeApp(object):
self.env['REQUEST_METHOD'] = request.req_method
self.env['wsgi.input'] = request.msg
self.dispatcher = Dispatcher(self.env, self.env['target'].geturl(),
self.dispatcher.mapping)
self.dispatcher.mapper_name)
response = self.dispatcher.get_response(request)
else:
response = get_response(self.env, request, request.target)

View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxdbs+ZLkuz0DISpAKhHn
WvNBSW4G0xmlUyZcjUWDQlJH7wC3yxhjioQ2oFpxqcuNf5ft/E1E5KUTqZhcKyX9
i7XCmhPoea/fmYH3Egxbucv7++sM+TyZpUWbA0TZHBYAjcUPR/1HTcEz3bl0SqB0
EdjhN5PpXPu1p4pGDPXc4aIkEpFU3mlK+TlV5SrivEqNS/SI14VA9g2WWdJk4+CK
PgozCfeiFtaiu2zem4uQSmd5AG0f0Av4jzxgut22owFYi9PV+Yl0cWoMOUphAwsR
RE4gckEqbhLYluAy+VglgzfT4YCXBQ6o23EH0Z0tW28KnIYEY4dQkLca9YRAKhHc
ywIDAQAB
-----END PUBLIC KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAxdbs+ZLkuz0DISpAKhHnWvNBSW4G0xmlUyZcjUWDQlJH7wC3
yxhjioQ2oFpxqcuNf5ft/E1E5KUTqZhcKyX9i7XCmhPoea/fmYH3Egxbucv7++sM
+TyZpUWbA0TZHBYAjcUPR/1HTcEz3bl0SqB0EdjhN5PpXPu1p4pGDPXc4aIkEpFU
3mlK+TlV5SrivEqNS/SI14VA9g2WWdJk4+CKPgozCfeiFtaiu2zem4uQSmd5AG0f
0Av4jzxgut22owFYi9PV+Yl0cWoMOUphAwsRRE4gckEqbhLYluAy+VglgzfT4YCX
BQ6o23EH0Z0tW28KnIYEY4dQkLca9YRAKhHcywIDAQABAoIBAHS7XPXhW36zAD64
XEW2bKj4cOQvvG0ga7EFKITeqBUg0XrPFKMMD+eyHT0+QGSsSyAm9+/vc5/pWxGt
aWy4LMMbiug4qOnsAOXljm+ixRh6qIK67Nu+ivW+fTlPjT8KKGd+B4c1hbX2MnE4
NMq3o+TH8BNH/eC0UDm715tcEmk6pUSBH3lq3CG7W1TyVjC3FGJcjBAj/X6J45lE
skJHt9d67KG/MwmzuyoI+U9q2b3jSzoIGzzQQaOItGx3OefRjqWeUyDlUWobuFNV
Lky+XjmOFJC0voQsUiV2mBSJejHmfuLjJfE+W/HrRc3YwftxCp+emaFshs56U4Ob
UWu2F9kCgYEA7livJ1nYhHVyYueX6kWKTkBCzcwQO0agLsuYpspDjKGqgUOlFHXW
9CS+DPi/r086iRYLwmGuaFAnNQJqS3ofjowj9/iZCGD/qe6jj9zMmokWDl1FALYe
jT3Eg1HLfhe8hddA815yheL5uIVw3t34TTaQuokN86nkcv/bJ53SW4UCgYEA1H4v
jk88pCNnADqmAnXNbuhPK+w6llre159vtStgKaJrcCZiTejFVpffpdp1b8hU21S2
lg/FgXHgvrdfwq+uZ+lRNJGyCX3mqe3uXWn6d42A/7tgmRDW4NXtxwelV8MTpwHw
nS4hwmDyLyYMupyBlw5Iv7N3XmDBJu/tsEPMgA8CgYBP5MpRlnxNalD9dkQl80l5
EXFTKqQGOpZXGUgCIKqj6U0OJ26efSGglPBfyMH4McadTRaEAdpEfRmnWzfmNPl+
/trPtDUX6evJOoT5JDoxUuJhzkHjCykSjzHgEvrzOWGoO486BN6+omayw4giLKWe
vDunS2mx07EQG1OK5AwvQQKBgCZY21YwQH5SkTz+WIUrIza3n8oKaIxHu91nvW4R
dNouoHrtwmHS9wHoiIjSwsy4d2/ZetXb5MW2eluQlix5Ld08wtXc0SdbXCwgbxrW
jEfU9omwE/+rhUuv76gyXglXgA1skTKcZ6U/f5U4paVrpwtOnZxS0+DpTxIqzFc5
9QbLAoGAeqLr0vm4SKnvtwK9F/Q784Rc8Ygq56vUcQIZ81yL4BsE0h6fuTHcSq+H
NhO5mQFr+CcitGDE48/CRxfw1HYpk+KOtRzY+EdKGAKEu26sUSh7GNCw3TkOvPTo
E/RgydWsPwjJBDp03z87cITfaoyqoIWLtEmUTeDY8m5dGu0EBzk=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,11 @@
{{
"site_name": "linuxfr",
"server_name": ["linuxfrsaml.local:8000"],
"location": "/",
"target": "https://linuxfr.org",
"mapper": "linuxfr",
"auth_type": "saml2",
"saml2_idp_metadata": "http://www.identity-hub.com/idp/saml2/metadata",
"saml2_signature_public_key": "certs/saml.crt",
"saml2_signature_private_key": "certs/saml.key"
}}

View File

@ -1 +1 @@
__version__="0.1"
__version__="0.1.0"

View File

@ -3,15 +3,12 @@ import os
_PROJECT_PATH = os.path.join(os.path.dirname(__file__), '..')
## Virtual hosts configuration
hosts = {{}}
## SQL Backend config
# Database configuration
# http://docs.sqlalchemy.org/en/rel_0_7/core/engines.html
# rfc 1738 https://tools.ietf.org/html/rfc1738
# dialect+driver://username:password@host:port/database
db_url = 'sqlite:///' + os.path.join(_PROJECT_PATH, 'test.db')
db_url = 'sqlite:///' + os.path.join(_PROJECT_PATH, '{project_name}.db')
## Log configuration
debug = False
@ -29,6 +26,8 @@ log_backup = 52
## PATH
# Template directory
template_directory = os.path.join(_PROJECT_PATH, '{project_name}/templates')
# Configuration directory
config_root = os.path.join(_PROJECT_PATH, 'conf.d')
# Static url
static_url = '/mandaye/static'
# Static folder
@ -38,15 +37,12 @@ data_dir = os.path.join(_PROJECT_PATH, 'data')
# Email notification configuration
email_notification = False
email_prefix = '[Mandaye CAM]'
email_prefix = '[Mandaye {project_name}]'
smtp_host = 'localhost'
smtp_port = 25
email_from = 'traceback@entrouvert.com'
email_to = ['admin@localhost']
# platform : should be prod, recette or dev
platform = "prod"
# Use long traceback with xtraceback
use_long_trace = True
@ -60,6 +56,16 @@ encrypt_sp_password = False
# Must be a 16, 24, or 32 bytes long
encrypt_secret = ''
# Supported authentification
authentifications = {{
'saml2': 'mandaye.auth.saml2.SAML2Auth'
}}
# sp mappers
mappers = {{
'linuxfr': '{project_name}.mappers.linuxfr_example'
}}
# Beaker session configuration
session_opts = {{
'session.type': 'file',
@ -72,59 +78,6 @@ session_opts = {{
# Only mandaye.backends.sql at the moment
storage_backend = "mandaye.backends.sql"
# Needed if ssl is activated
ssl = False
keyfile = ''
certfile = ''
SAML_SIGNATURE_PUBLIC_KEY = '''-----BEGIN CERTIFICATE-----
MIIDIzCCAgugAwIBAgIJANUBoick1pDpMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV
BAoTCkVudHJvdXZlcnQwHhcNMTAxMjE0MTUzMzAyWhcNMTEwMTEzMTUzMzAyWjAV
MRMwEQYDVQQKEwpFbnRyb3V2ZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZn9Kqm4Cp
06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrUH8QT8NGh
ABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59xihSqsoFr
kmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9Hri8JRdXi
VT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziazZfvvw/VG
Tm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABo3YwdDAdBgNVHQ4EFgQUeF8ePnu0
fcAK50iBQDgAhHkOu8kwRQYDVR0jBD4wPIAUeF8ePnu0fcAK50iBQDgAhHkOu8mh
GaQXMBUxEzARBgNVBAoTCkVudHJvdXZlcnSCCQDVAaInJNaQ6TAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAy8l3GhUtpPHx0FxzbRHVaaUSgMwYKGPhE
IdGhqekKUJIx8et4xpEMFBl5XQjBNq/mp5vO3SPb2h2PVSks7xWnG3cvEkqJSOeo
fEEhkqnM45b2MH1S5uxp4i8UilPG6kmQiXU2rEUBdRk9xnRWos7epVivTSIv1Ncp
lG6l41SXp6YgIb2ToT+rOKdIGIQuGDlzeR88fDxWEU0vEujZv/v1PE1YOV0xKjTT
JumlBc6IViKhJeo1wiBBrVRIIkKKevHKQzteK8pWm9CYWculxT26TZ4VWzGbo06j
o2zbumirrLLqnt1gmBDvDvlOwC/zAAyL4chbz66eQHTiIYZZvYgy
-----END CERTIFICATE-----'''
SAML_SIGNATURE_PRIVATE_KEY = '''-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZ
n9Kqm4Cp06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrU
H8QT8NGhABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59x
ihSqsoFrkmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9H
ri8JRdXiVT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziaz
Zfvvw/VGTm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABAoIBAQCj8t2iKXya10HG
V6Saaeih8aftoLBV38VwFqqjPU0+iKqDpk2JSXBhjI6s7uFIsaTNJpR2Ga1qvns1
hJQEDMQSLhJvXfBgSkHylRWCpJentr4E3D7mnw5pRsd61Ev9U+uHcdv/WHP4K5hM
xsdiwXNXD/RYd1Q1+6bKrCuvnNJVmWe0/RV+r3T8Ni5xdMVFbRWt/VEoE620XX6c
a9TQPiA5i/LRVyie+js7Yv+hVjGOlArtuLs6ECQsivfPrqKLOBRWcofKdcf+4N2e
3cieUqwzC15C31vcMliD9Hax9c1iuTt9Q3Xzo20fOSazAnQ5YBEExyTtrFBwbfQu
ku6hp81pAoGBAN6bc6iJtk5ipYpsaY4ZlbqdjjG9KEXB6G1MExPU7SHXOhOF0cDH
/pgMsv9hF2my863MowsOj3OryVhdQhwA6RrV263LRh+JU8NyHV71BwAIfI0BuVfj
6r24KudwtUcvMr9pJIrJyMAMaw5ZyNoX7YqFpS6fcisSJYdSBSoxzrzVAoGBANu6
xVeMqGavA/EHSOQP3ipDZ3mnWbkDUDxpNhgJG8Q6lZiwKwLoSceJ8z0PNY3VetGA
RbqtqBGfR2mcxHyzeqVBpLnXZC4vs/Vy7lrzTiHDRZk2SG5EkHMSKFA53jN6S/nJ
JWpYZC8lG8w4OHaUfDHFWbptxdGYCgY4//sjeiuXAoGBANuhurJ99R5PnA8AOgEW
4zD1hLc0b4ir8fvshCIcAj9SUB20+afgayRv2ye3Dted1WkUL4WYPxccVhLWKITi
rRtqB03o8m3pG3kJnUr0LIzu0px5J/o8iH3ZOJOTE3iBa+uI/KHmxygc2H+XPGFa
HGeAxuJCNO2kAN0Losbnz5dlAoGAVsCn94gGWPxSjxA0PC7zpTYVnZdwOjbPr/pO
LDE0cEY9GBq98JjrwEd77KibmVMm+Z4uaaT0jXiYhl8pyJ5IFwUS13juCbo1z/u/
ldMoDvZ8/R/MexTA/1204u/mBecMJiO/jPw3GdIJ5phv2omHe1MSuSNsDfN8Sbap
gmsgaiMCgYB/nrTk89Fp7050VKCNnIt1mHAcO9cBwDV8qrJ5O3rIVmrg1T6vn0aY
wRiVcNacaP+BivkrMjr4BlsUM6yH4MOBsNhLURiiCL+tLJV7U0DWlCse/doWij4U
TKX6tp6oI+7MIJE6ySZ0cBqOiydAkBePZhu57j6ToBkTa0dbHjn1WA==
-----END RSA PRIVATE KEY-----'''
# Import local config
try:
from ..{project_name}.local_config import *

View File

@ -1,49 +0,0 @@
from {project_name}.auth.example import MyAuthSAML
from {project_name}.filters.example import ReplayFilter
from mandaye.configs import saml2 as saml2_config
form_values = {{
'login_url': '/compte/connexion',
'form_attrs': {{ 'id': 'new_account' }},
'post_fields': ['account[login]', 'account[password]'],
'username_field': 'account[login]',
'password_field': 'account[password]',
}}
auth = MyAuthSAML(form_values, 'linuxfr', saml2_config)
linuxfr_mapping = [
{{
'path': r'/mandaye/associate$',
'method': 'GET',
'on_response': [{{
'filter': ReplayFilter.associate,
'values': {{
'action': '/mandaye/associate',
'template': 'associate.html',
'sp_name': 'Linux FR',
'login_name': form_values['username_field'],
'password_name': form_values['password_field'],
}},
}},]
}},
{{
'path': r'/mandaye/associate$',
'method': 'POST',
'response': [
{{
'filter': auth.associate_submit,
'values': {{
'connection_url': '/mandaye/sso',
'associate_url': '/mandaye/associate',
}},
'condition': "response.code==302"
}},
]
}},
]
linuxfr_mapping.extend(auth.get_default_mapping())

View File

@ -0,0 +1,88 @@
"""
You need to defined 3 variables :
* form_values (defined the login form values):
form_values = {{
'login_url': '/login',
'post_url': '/login',
'form_attrs': {{ 'name': 'form40', }},
'username_field': 'user',
'password_field': 'pass',
'post_fields': ['birthdate', 'card_number']
}}
login_url, form_attrs, post_fields and username_field are obligatory
* urls (a dictionnary with urls) :
urls = {{
'associate_url': '/mandaye/associate',
'connection_url': '/mandaye/sso',
'login_url': '/mandaye/login'
}}
* mapping
"""
from {project_name}.filters.example import ReplayFilter
form_values = {{
'login_url': '/compte/connexion',
'form_attrs': {{ 'id': 'new_account' }},
'post_fields': ['account[login]', 'account[password]'],
'username_field': 'account[login]',
'password_field': 'account[password]',
}}
urls = {{
'associate_url': '/mandaye/associate',
'connection_url': '/mandaye/sso',
'login_url': '/mandaye/login'
}}
mapping = [
{{
'path': r'/mandaye/login$',
'method': 'GET',
'response': [{{
'auth': 'login',
'condition': 'response.code==302',
}},]
}},
{{
'path': r'/mandaye/sso$',
'method': 'GET',
'response': [{{
'auth': 'sso',
}}]
}},
{{
'path': r'/mandaye/slo$',
'method': 'GET',
'response': [{{
'auth': 'slo',
}}]
}},
{{
'path': r'/mandaye/associate$',
'method': 'GET',
'on_response': [{{
'filter': ReplayFilter.associate,
'values': {{
'action': urls['associate_url'],
'template': 'associate.html',
'sp_name': 'Linux FR',
'login_name': form_values['username_field'],
'password_name': form_values['password_field'],
}},
}},]
}},
{{
'path': r'/mandaye/associate$',
'method': 'POST',
'response': [
{{
'auth': 'associate_submit',
'condition': "response.code==302"
}},
]
}},
]

View File

@ -5,9 +5,13 @@ from mandaye.server import MandayeApp
from {project_name} import config
from beaker.middleware import SessionMiddleware
from whitenoise import WhiteNoise
os.environ['MANDAYE_CONFIG_MODULE'] = '{project_name}.config'
os.environ['MANDAYE_CONFIG_MODULE'] = 'test.config'
from mandaye import config
application = SessionMiddleware(MandayeApp(), config.session_opts)
application_dev = WhiteNoise(application, root=config.static_root, prefix=config.static_url)

View File

@ -1,2 +1,3 @@
gunicorn>=0.17
mandaye>=0.7.1
mandaye>=0.8.0
whitenoise>=1.0

View File

@ -15,8 +15,8 @@ from gunicorn.app.wsgiapp import WSGIApplication
class MandayeWSGIApplication(WSGIApplication):
def init(self, parser, opts, args):
self.cfg.set("default_proc_name", "{project_name}.wsgi:application")
self.app_uri = "{project_name}.wsgi:application"
self.cfg.set("default_proc_name", "{project_name}.wsgi:application_dev")
self.app_uri = "{project_name}.wsgi:application_dev"
def main():
""" The ``gunicorn`` command line runner for launcing Gunicorn with

View File

@ -14,7 +14,8 @@ import {project_name}
install_requires=[
'gunicorn>=0.17',
'mandaye>=0.7.1',
'mandaye>=0.8.0',
'whitenoise>=1.0'
]
def get_version():

View File

@ -6,4 +6,3 @@ xtraceback>=0.3
sqlalchemy>=0.7,<0.8
alembic>=0.5.0
Mako>=0.4
static

View File

@ -47,14 +47,11 @@ def main():
shutil.copytree(skel, project_name)
for root, dirs, files in os.walk(project_name):
if not "templates" in root and not "static" in root:
print root
for filename in files:
file_path = os.path.join(root, filename)
print file_path
with open(file_path, "r") as f:
content = f.read()
with open(file_path, "w") as f:
print content.format(project_name=project_name)
f.write(content.format(project_name=project_name))
shutil.move(modue_example, module)

View File

@ -18,7 +18,6 @@ install_requires=[
'sqlalchemy>=0.7.3',
'lxml>=2.0',
'xtraceback>=0.3',
'static',
]
if version < '2.7':
@ -37,7 +36,7 @@ def get_version():
version = result.split()[0][1:]
return version.replace('-','.')
import mandaye
return mandaye.VERSION
return mandaye.__version__
setup(name="mandaye",
version=get_version(),