experimental saml2 support

This commit is contained in:
mdipierro
2014-08-20 21:05:03 -05:00
parent c7beeaf5c5
commit 9ad30aa64d
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This file is part of web2py Web Framework (Copyrighted, 2007-2014).
Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu>.
License: LGPL v3
Login will be done via Web2py's CAS application, instead of web2py's
login form.
Include in your model (eg db.py)::
auth.define_tables(username=True)
from gluon.contrib.login_methods.saml2_auth import Saml2Auth
auth.settings.login_form=Saml2Auth(
config_file = os.path.join(request.folder,'private','sp_conf'),
maps=dict(
username=lambda v: v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
email=lambda v: v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
user_id=lambda v: v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0]))
you must have private/sp_conf.py, the pysaml2 sp configuration file
"""
from saml2 import BINDING_HTTP_REDIRECT
from saml2.client import Saml2Client
from gluon.utils import web2py_uuid, obj2dict
from gluon import current, redirect, URL
import os
def saml2_handler(session, request, config_filename = None):
config_filename = config_filename or os.path.join(request.folder,'private','sp_conf')
client = Saml2Client(config_file = config_filename)
idps = client.metadata.with_descriptor("idpsso")
entityid = idps.keys()[0]
bindings = [BINDING_HTTP_REDIRECT]
binding, destination = client.pick_binding(
"single_sign_on_service", bindings, "idpsso", entity_id=entityid)
binding = BINDING_HTTP_REDIRECT
if not request.vars.SAMLResponse:
req_id, req = client.create_authn_request(destination, binding=binding)
relay_state = web2py_uuid().replace('-','')
session.saml_outstanding_queries = {req_id: request.url}
session.saml_req_id = req_id
http_args = client.apply_binding(binding, str(req), destination,
relay_state=relay_state)
return {'url':dict(http_args["headers"])['Location']}
else:
relay_state = request.vars.RelayState
req_id = session.saml_req_id
unquoted_response = request.vars.SAMLResponse
res = {}
try:
data = client.parse_authn_request_response(
unquoted_response, binding, session.saml_outstanding_queries)
res['response'] = data if data else {}
except Exception, e:
res['error'] = str(e)
return res
class Saml2Auth(object):
def __init__(self, config_file=None, maps=dict(
username=lambda v:v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
email=lambda v:v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
user_id=lambda v:v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
)):
self.config_file = config_file
self.maps = maps
def login_url(self, next="/"):
d = saml2_handler(current.session,current.request)
if 'url' in d:
redirect(d['url'])
elif 'error' in d:
current.response.flash = d['error']
elif 'response' in d:
# a['assertions'][0]['attribute_statement'][0]['attribute']
# is list of
# {'name': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname', 'name_format': None, 'text': None, 'friendly_name': None, 'attribute_value': [{'text': 'CAA\\dev-mdp', 'extension_attributes': "{'{http://www.w3.org/2001/XMLSchema-instance}type': 'xs:string'}", 'extension_elements': []}], 'extension_elements': [], 'extension_attributes': '{}'}
attributes = d['response'].assertions[0].attribute_statement[0].attribute
current.session.saml2_info = dict(
(a.name, [i.text for i in a.attribute_value]) for a in attributes)
return next
def logout_url(self, next="/"):
current.session.saml2_info = None
return next
def get_user(self):
user = current.session.saml2_info
if user:
d = {'source': 'web2py saml2'}
for key in self.maps:
d[key] = self.maps[key](user)
return d
return None