Changed to use the changes in PySAML2

This commit is contained in:
Roland Hedberg
2010-10-17 18:44:59 +02:00
parent 89dcf15ef4
commit 804cd31caa
6 changed files with 141 additions and 6 deletions

View File

@@ -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": {

View File

@@ -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)

View File

@@ -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>

View File

@@ -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"],

View File

@@ -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),
]
# ----------------------------------------------------------------------------

View File

@@ -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