initial import for branch 0.8
This is a complete rewrite of the configuration system Fixes #4666
This commit is contained in:
parent
5c65415c46
commit
de6e5967b5
|
@ -1,4 +1,4 @@
|
|||
VERSION=0.4
|
||||
__version__='0.8.0'
|
||||
|
||||
import os
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.')
|
||||
|
|
|
@ -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-----'''
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 = ''
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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"
|
||||
}}
|
|
@ -1 +1 @@
|
|||
__version__="0.1"
|
||||
__version__="0.1.0"
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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())
|
||||
|
|
@ -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"
|
||||
}},
|
||||
]
|
||||
}},
|
||||
]
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
gunicorn>=0.17
|
||||
mandaye>=0.7.1
|
||||
mandaye>=0.8.0
|
||||
whitenoise>=1.0
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -6,4 +6,3 @@ xtraceback>=0.3
|
|||
sqlalchemy>=0.7,<0.8
|
||||
alembic>=0.5.0
|
||||
Mako>=0.4
|
||||
static
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
3
setup.py
3
setup.py
|
@ -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(),
|
||||
|
|
Reference in New Issue