experimental saml2 support
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user