Changed to use the changes in PySAML2
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
"name" : "Rolands IdP",
|
||||
"endpoints" : {
|
||||
"single_sign_on_service" : ["http://localhost:8088/sso"],
|
||||
"single_logout_service" : [("http://localhost:8088/logout",
|
||||
'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect')],
|
||||
},
|
||||
"policy": {
|
||||
"default": {
|
||||
|
||||
@@ -4,7 +4,17 @@ import re
|
||||
import base64
|
||||
from cgi import parse_qs
|
||||
from saml2 import server
|
||||
|
||||
from saml2 import BINDING_HTTP_REDIRECT, BINDING_HTTP_POST
|
||||
from saml2 import time_util
|
||||
from Cookie import SimpleCookie
|
||||
|
||||
def _expiration(timeout, format=None):
|
||||
if timeout == "now":
|
||||
return time_util.instant(format)
|
||||
else:
|
||||
# validity time should match lifetime of assertions
|
||||
return time_util.in_a_while(minutes=timeout, format=format)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def dict_to_table(ava, lev=0, width=1):
|
||||
txt = []
|
||||
@@ -124,6 +134,57 @@ def not_authn(environ, start_response, logger):
|
||||
start_response('401 Unauthorized', [('Content-Type', 'text/plain')])
|
||||
return ['Unknown user']
|
||||
|
||||
def slo(environ, start_response, user, logger):
|
||||
""" Expects a HTTP-redirect logout request """
|
||||
|
||||
if "QUERY_STRING" in environ:
|
||||
logger and logger.info("Query string: %s" % environ["QUERY_STRING"])
|
||||
query = parse_qs(environ["QUERY_STRING"])
|
||||
|
||||
try:
|
||||
req_info = IDP.parse_logout_request(query["SAMLRequest"][0],
|
||||
BINDING_HTTP_REDIRECT)
|
||||
logger.info("LOGOUT request parsed OK")
|
||||
logger.info("REQ_INFO: %s" % req_info.message)
|
||||
except KeyError, exc:
|
||||
logger and logger.info("logout request error: %s" % (exc,))
|
||||
# return error reply
|
||||
|
||||
# look for the subject
|
||||
subject = req_info.subject_id()
|
||||
subject = subject.text.strip()
|
||||
sp_entity_id = req_info.message.issuer.text.strip()
|
||||
logger.info("Logout subject: %s" % (subject,))
|
||||
logger.info("local identifier: %s" % IDP.ident.local_name(sp_entity_id,
|
||||
subject))
|
||||
# remove the authentication
|
||||
|
||||
status = None
|
||||
|
||||
# Either HTTP-Post or HTTP-redirect is possible
|
||||
bindings = [BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]
|
||||
(resp, headers, message) = IDP.logout_response(req_info.message, bindings)
|
||||
#headers.append(session.cookie(expire="now"))
|
||||
logger.info("Response code: %s" % (resp,))
|
||||
logger.info("Header: %s" % (headers,))
|
||||
delco = delete_cookie(environ, "pysaml2idp")
|
||||
if delco:
|
||||
headers.append(delco)
|
||||
start_response(resp, headers)
|
||||
return message
|
||||
|
||||
def delete_cookie(environ, name):
|
||||
kaka = environ.get("HTTP_COOKIE", '')
|
||||
if kaka:
|
||||
cookie_obj = SimpleCookie(kaka)
|
||||
morsel = cookie_obj.get(name, None)
|
||||
cookie = SimpleCookie()
|
||||
cookie[name] = morsel
|
||||
cookie[name]["expires"] = \
|
||||
_expiration("now", "%a, %d-%b-%Y %H:%M:%S CET")
|
||||
return tuple(cookie.output().split(": ", 1))
|
||||
return None
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# map urls to functions
|
||||
@@ -132,6 +193,8 @@ URLS = [
|
||||
(r'whoami/(.*)$', whoami),
|
||||
(r'sso$', sso),
|
||||
(r'sso/(.*)$', sso),
|
||||
(r'logout$', slo),
|
||||
(r'logout/(.*)$', slo),
|
||||
]
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
@@ -151,12 +214,14 @@ def application(environ, start_response):
|
||||
:return: The response as a list of lines
|
||||
"""
|
||||
user = environ.get("REMOTE_USER", "")
|
||||
kaka = environ.get("HTTP_COOKIE", '')
|
||||
if not user:
|
||||
user = environ.get("repoze.who.identity", "")
|
||||
|
||||
|
||||
path = environ.get('PATH_INFO', '').lstrip('/')
|
||||
logger = environ.get('repoze.who.logger')
|
||||
logger and logger.info( "<application> PATH: %s" % path)
|
||||
logger and logger.info("<application> PATH: %s" % path)
|
||||
logger and logger.info("Cookie: %s" % (kaka,))
|
||||
for regex, callback in URLS:
|
||||
if user:
|
||||
match = re.search(regex, path)
|
||||
|
||||
@@ -15,7 +15,7 @@ AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
|
||||
BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO
|
||||
zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN
|
||||
+vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI=
|
||||
</ns1:X509Certificate></ns1:X509Data></ns1:KeyInfo></ns0:KeyDescriptor><ns0:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8087/" index="1" /><ns0:AttributeConsumingService index="1"><ns0:ServiceName xml:lang="en">Rolands SP</ns0:ServiceName><ns0:RequestedAttribute FriendlyName="surname" Name="urn:oid:2.5.4.4" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="givenname" Name="urn:oid:2.5.4.42" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="edupersonaffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="title" Name="urn:oid:2.5.4.12" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false" /></ns0:AttributeConsumingService></ns0:SPSSODescriptor><ns0:Organization><ns0:OrganizationName xml:lang="en">Exempel AB</ns0:OrganizationName><ns0:OrganizationDisplayName xml:lang="se">Exempel AB</ns0:OrganizationDisplayName><ns0:OrganizationDisplayName xml:lang="en">Example Co.</ns0:OrganizationDisplayName><ns0:OrganizationURL xml:lang="en">http://www.example.com/roland</ns0:OrganizationURL></ns0:Organization><ns0:ContactPerson contactType="technical"><ns0:GivenName>John</ns0:GivenName><ns0:SurName>Smith</ns0:SurName><ns0:EmailAddress>john.smith@example.com</ns0:EmailAddress></ns0:ContactPerson></ns0:EntityDescriptor><ns0:EntityDescriptor entityID="urn:mace:umu.se:saml:roland:idp"><ns0:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:KeyDescriptor><ns2:KeyInfo xmlns:ns2="http://www.w3.org/2000/09/xmldsig#"><ns2:X509Data><ns2:X509Certificate>MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV
|
||||
</ns1:X509Certificate></ns1:X509Data></ns1:KeyInfo></ns0:KeyDescriptor><ns0:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8087/slo" /><ns0:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8087/" index="1" /><ns0:AttributeConsumingService index="1"><ns0:ServiceName xml:lang="en">Rolands SP</ns0:ServiceName><ns0:RequestedAttribute FriendlyName="surname" Name="urn:oid:2.5.4.4" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="givenname" Name="urn:oid:2.5.4.42" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="edupersonaffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="title" Name="urn:oid:2.5.4.12" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false" /></ns0:AttributeConsumingService></ns0:SPSSODescriptor><ns0:Organization><ns0:OrganizationName xml:lang="en">Exempel AB</ns0:OrganizationName><ns0:OrganizationDisplayName xml:lang="se">Exempel AB</ns0:OrganizationDisplayName><ns0:OrganizationDisplayName xml:lang="en">Example Co.</ns0:OrganizationDisplayName><ns0:OrganizationURL xml:lang="en">http://www.example.com/roland</ns0:OrganizationURL></ns0:Organization><ns0:ContactPerson contactType="technical"><ns0:GivenName>John</ns0:GivenName><ns0:SurName>Smith</ns0:SurName><ns0:EmailAddress>john.smith@example.com</ns0:EmailAddress></ns0:ContactPerson></ns0:EntityDescriptor><ns0:EntityDescriptor entityID="urn:mace:umu.se:saml:roland:idp"><ns0:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:KeyDescriptor><ns2:KeyInfo xmlns:ns2="http://www.w3.org/2000/09/xmldsig#"><ns2:X509Data><ns2:X509Certificate>MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV
|
||||
BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx
|
||||
EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz
|
||||
MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l
|
||||
@@ -31,4 +31,4 @@ AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
|
||||
BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO
|
||||
zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN
|
||||
+vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI=
|
||||
</ns2:X509Certificate></ns2:X509Data></ns2:KeyInfo></ns0:KeyDescriptor><ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8088/sso" /></ns0:IDPSSODescriptor><ns0:Organization><ns0:OrganizationName xml:lang="en">Rolands Identiteter</ns0:OrganizationName><ns0:OrganizationDisplayName xml:lang="en">Rolands Identiteter</ns0:OrganizationDisplayName><ns0:OrganizationURL xml:lang="en">http://www.example.com</ns0:OrganizationURL></ns0:Organization></ns0:EntityDescriptor></ns0:EntitiesDescriptor>
|
||||
</ns2:X509Certificate></ns2:X509Data></ns2:KeyInfo></ns0:KeyDescriptor><ns0:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8088/logout" /><ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8088/sso" /></ns0:IDPSSODescriptor><ns0:Organization><ns0:OrganizationName xml:lang="en">Rolands Identiteter</ns0:OrganizationName><ns0:OrganizationDisplayName xml:lang="en">Rolands Identiteter</ns0:OrganizationDisplayName><ns0:OrganizationURL xml:lang="en">http://www.example.com</ns0:OrganizationURL></ns0:Organization></ns0:EntityDescriptor></ns0:EntitiesDescriptor>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
"name" : "Rolands SP",
|
||||
"endpoints":{
|
||||
"assertion_consumer_service": ["http://localhost:8087/"],
|
||||
"single_logout_service" : [("http://localhost:8087/slo",
|
||||
'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect')],
|
||||
},
|
||||
"required_attributes": ["surname", "givenname", "edupersonaffiliation"],
|
||||
"optional_attributes": ["title"],
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
import re
|
||||
from cgi import escape
|
||||
from cgi import parse_qs
|
||||
import urllib
|
||||
from saml2 import BINDING_HTTP_REDIRECT
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def dict_to_table(ava, width=1):
|
||||
@@ -48,10 +50,13 @@ def dict_to_table(ava, width=1):
|
||||
|
||||
|
||||
def whoami(environ, start_response, user, logger):
|
||||
start_response('200 OK', [('Content-Type', 'text/html')])
|
||||
identity = environ["repoze.who.identity"]["user"]
|
||||
if not identity:
|
||||
return not_authn(environ, start_response)
|
||||
response = ["<h2>Your identity are supposed to be</h2>"]
|
||||
response.extend(dict_to_table(identity))
|
||||
response.extend("<a href='logout'>Logout</a>")
|
||||
start_response('200 OK', [('Content-Type', 'text/html')])
|
||||
return response[:]
|
||||
|
||||
def not_found(environ, start_response):
|
||||
@@ -62,12 +67,71 @@ def not_found(environ, start_response):
|
||||
def not_authn(environ, start_response):
|
||||
start_response('401 Unauthorized', [('Content-Type', 'text/plain')])
|
||||
return ['Unknown user']
|
||||
|
||||
def slo(environ, start_response, user, logger):
|
||||
# so here I might get either a LogoutResponse or a LogoutRequest
|
||||
client = environ['repoze.who.plugins']["saml2auth"]
|
||||
if "QUERY_STRING" in environ:
|
||||
query = parse_qs(environ["QUERY_STRING"])
|
||||
logger and logger.info("query: %s" % query)
|
||||
try:
|
||||
(sids, code, head, message) = client.saml_client.logout_response(
|
||||
query["SAMLResponse"][0],
|
||||
log=logger,
|
||||
binding=BINDING_HTTP_REDIRECT)
|
||||
logger.info("LOGOUT reponse parsed OK")
|
||||
except KeyError:
|
||||
# return error reply
|
||||
pass
|
||||
|
||||
if sids == 0:
|
||||
start_response("302 Found", [("Location", "/done")])
|
||||
return ["Successfull Logout"]
|
||||
|
||||
def logout(environ, start_response, user, logger):
|
||||
client = environ['repoze.who.plugins']["saml2auth"]
|
||||
subject_id = environ["repoze.who.identity"]['repoze.who.userid']
|
||||
logger.info("[logout] subject_id: '%s'" % (subject_id,))
|
||||
target = "/done"
|
||||
# What if more than one
|
||||
tmp = client.saml_client.global_logout(subject_id, log=logger,
|
||||
return_to=target)
|
||||
logger.info("[logout] global_logout > %s" % (tmp,))
|
||||
(session_id, code, header, result) = tmp
|
||||
|
||||
if session_id:
|
||||
#redir = [v for (a,v) in header if a == "Location"]
|
||||
# remove cookie
|
||||
start_response(code, header)
|
||||
return result
|
||||
else: # All was done using SOAP
|
||||
if result:
|
||||
start_response("302 Found", [("Location", target)])
|
||||
return ["Successfull Logout"]
|
||||
else:
|
||||
start_response("500 Internal Server Error")
|
||||
return ["Failed to logout from identity services"]
|
||||
|
||||
def done(environ, start_response, user, logger):
|
||||
# remove cookie and stored info
|
||||
logger.info("[done] environ: %s" % environ)
|
||||
subject_id = environ["repoze.who.identity"]['repoze.who.userid']
|
||||
client = environ['repoze.who.plugins']["saml2auth"]
|
||||
logger.info("[logout done] remaining subjects: %s" % (
|
||||
client.saml_client.users.subjects(),))
|
||||
|
||||
start_response('200 OK', [('Content-Type', 'text/html')])
|
||||
return ["<h3>You are now logged out from this service</h3>"]
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# map urls to functions
|
||||
urls = [
|
||||
(r'whoami$', whoami),
|
||||
(r'logout$', logout),
|
||||
(r'done$', done),
|
||||
(r'slo$', slo),
|
||||
(r'^$', whoami),
|
||||
]
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
@@ -16,6 +16,8 @@ use = s2repoze.plugins.sp:make_plugin
|
||||
saml_conf = sp.conf
|
||||
rememberer_name = auth_tkt
|
||||
debug = 1
|
||||
sid_store = outstanding
|
||||
identity_cache = identities
|
||||
|
||||
[general]
|
||||
request_classifier = s2repoze.plugins.challenge_decider:my_request_classifier
|
||||
|
||||
Reference in New Issue
Block a user