diff --git a/example/idp/idp.app.py b/example/idp/idp.app.py
new file mode 100755
index 0000000..ed026a9
--- /dev/null
+++ b/example/idp/idp.app.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+
+import re
+import base64
+from cgi import escape
+import urllib
+import urlparse
+
+from saml2 import server
+from saml2.utils import make_instance, sid, decode_base64_and_inflate
+from saml2 import samlp, saml
+from saml2.time_util import in_a_while, instant
+
+def authn_response(identity, in_response_to, destination, spid):
+ global idp
+ resp = idp.do_sso_response(
+ destination, # consumer_url
+ in_response_to, # in_response_to
+ spid, # sp_entity_id
+ identity # identity as dictionary
+ )
+
+ return ("%s" % resp).split("\n")
+
+# -----------------------------------------------------------------------------
+def dict_to_table(ava, lev=0, width=1):
+ txt = []
+ txt.append('
\n' % width)
+ for prop, valarr in ava.items():
+ txt.append("\n")
+ if isinstance(valarr, basestring):
+ txt.append("| %s | \n" % str(prop))
+ try:
+ txt.append("%s | \n" % valarr.encode("utf8"))
+ except AttributeError:
+ txt.append("%s | \n" % valarr)
+ elif isinstance(valarr, list):
+ i = 0
+ n = len(valarr)
+ for val in valarr:
+ if i == 0:
+ txt.append("%s\n" % (len(valarr),prop))
+ else:
+ txt.append(" |
\n")
+ if isinstance(val, dict):
+ txt.append("| \n")
+ txt.extend(dict_to_table(val, lev+1, width-1))
+ txt.append(" | \n")
+ else:
+ try:
+ txt.append("%s | \n" % val.encode("utf8"))
+ except AttributeError:
+ txt.append("%s | \n" % val)
+ if n > 1:
+ txt.append("
\n")
+ n -= 1
+ i += 1
+ elif isinstance(valarr, dict):
+ txt.append("%s | \n" % prop)
+ txt.append("\n")
+ txt.extend(dict_to_table(valarr, lev+1, width-1))
+ txt.append(" | \n")
+ txt.append("\n")
+ txt.append('
\n')
+ return txt
+
+REPOZE_ID_EQUIVALENT = "uid"
+FORM_SPEC = """"""
+
+def sso(environ, start_response, user, logger):
+ """ Supposted to return a POST """
+ #edict = dict_to_table(environ)
+ logger and logger.info("Environ keys: %s" % environ.keys())
+ if "QUERY_STRING" in environ:
+ logger and logger.info("Query string: %s" % environ["QUERY_STRING"])
+ query = urlparse.parse_qs(environ["QUERY_STRING"])
+ elif "s2repoze.qinfo" in environ:
+ query = environ["s2repoze.qinfo"]
+ # base 64 encoded request
+ (consumer, identifier, policies,
+ spid) = idp.parse_authn_request(query["SAMLRequest"][0])
+ spentityid = query["spentityid"][0]
+ try:
+ relayState = query["RelayState"][0]
+ except (KeyError, AttributeError):
+ relayState = "/"
+ start_response('200 OK', [('Content-Type', 'text/html')])
+ identity = dict(environ["repoze.who.identity"]["user"])
+ if REPOZE_ID_EQUIVALENT:
+ identity[REPOZE_ID_EQUIVALENT] = (
+ environ["repoze.who.identity"]['repoze.who.userid'])
+ authn_resp = authn_response(identity, identifier, consumer, spid)
+ logger and logger.info("AuthNResponse: %s" % authn_resp)
+ response = []
+ response.append("")
+ response.append("SAML 2.0 POST")
+ response.append("")
+ #login_url = location + '?spentityid=' + "lingon.catalogix.se"
+ response.append(FORM_SPEC % (consumer,
+ base64.b64encode("".join(authn_resp)),"/"))
+ response.append("""""")
+ response.append("")
+ return response
+
+def whoami(environ, start_response, user, logger):
+ start_response('200 OK', [('Content-Type', 'text/html')])
+ identity = environ["repoze.who.identity"].copy()
+ for prop in ["login", "password"]:
+ try:
+ del identity[prop]
+ except KeyError:
+ continue
+ response = dict_to_table(identity)
+ return response[:]
+
+def not_found(environ, start_response, logger):
+ """Called if no URL matches."""
+ start_response('404 NOT FOUND', [('Content-Type', 'text/plain')])
+ return ['Not Found']
+
+def not_authn(environ, start_response, logger):
+ if "QUERY_STRING" in environ:
+ query = urlparse.parse_qs(environ["QUERY_STRING"])
+ logger and logger.info("query: %s" % query)
+ start_response('401 Unauthorized', [('Content-Type', 'text/plain')])
+ return ['Unknown user']
+
+# ----------------------------------------------------------------------------
+
+# map urls to functions
+urls = [
+ (r'whoami$', whoami),
+ (r'whoami/(.*)$', whoami),
+ (r'sso$', sso),
+ (r'sso/(.*)$', sso),
+]
+
+# ----------------------------------------------------------------------------
+
+def application(environ, start_response):
+ """
+ The main WSGI application. Dispatch the current request to
+ the functions from above and store the regular expression
+ captures in the WSGI environment as `myapp.url_args` so that
+ the functions from above can access the url placeholders.
+
+ If nothing matches call the `not_found` function.
+
+ :param environ: The HTTP application environment
+ :param start_response: The application to run when the handling of the
+ request is done
+ :return: The response as a list of lines
+ """
+ user = environ.get("REMOTE_USER", "")
+ 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( " PATH: %s" % path)
+ for regex, callback in urls:
+ if user:
+ match = re.search(regex, path)
+ if match is not None:
+ try:
+ environ['myapp.url_args'] = match.groups()[0]
+ except IndexError:
+ environ['myapp.url_args'] = path
+ return callback(environ, start_response, user, logger)
+ else:
+ logger and logger.info("-- No USER --")
+ return not_authn(environ, start_response, logger)
+ return not_found(environ, start_response, logger)
+
+# ----------------------------------------------------------------------------
+
+from repoze.who.config import make_middleware_with_config
+
+app_with_auth = make_middleware_with_config(application, {"here":"."},
+ './who.ini', log_file="idpapp.log")
+
+# ----------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ import sys
+ from wsgiref.simple_server import make_server
+ import logging
+ LOG_FILENAME = "./idp.log"
+ PORT = 8088
+
+ logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)
+
+ idp = server.Server(sys.argv[1], logging)
+ srv = make_server('localhost', PORT, app_with_auth)
+ print "listening on port: %s" % PORT
+ srv.serve_forever()
\ No newline at end of file
diff --git a/example/idp/idp.conf b/example/idp/idp.conf
new file mode 100644
index 0000000..c7c0d55
--- /dev/null
+++ b/example/idp/idp.conf
@@ -0,0 +1,12 @@
+{
+ "entityid" : "urn:mace:umu.se:saml:roland:idp",
+ "service": ["idp"],
+ "my_name" : "Rolands IdP",
+ "debug" : 1,
+ "key_file" : "./mykey.pem",
+ "cert_file" : "./mycert.pem",
+ "xmlsec_binary" : "/opt/local/bin/xmlsec1",
+ "metadata" : {
+ "local": ["metadata.xml"],
+ }
+}
\ No newline at end of file
diff --git a/example/idp/metadata.xml b/example/idp/metadata.xml
new file mode 100644
index 0000000..1766c0a
--- /dev/null
+++ b/example/idp/metadata.xml
@@ -0,0 +1,34 @@
+
+MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV
+BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx
+EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz
+MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l
+YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw
+DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7
+bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC
+FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR
+mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW
+BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9
+o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW
+BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE
+AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
+BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO
+zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN
++vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI=
+http://www.umu.se/Umea UniversityRolandHedbergroland.hedberg@adm.umu.seMIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV
+BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx
+EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz
+MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l
+YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw
+DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7
+bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC
+FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR
+mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW
+BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9
+o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW
+BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE
+AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
+BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO
+zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN
++vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI=
+http://www.umu.se/Umea UniversityRolandHedbergroland.hedberg@adm.umu.se
diff --git a/example/idp/mycert.pem b/example/idp/mycert.pem
new file mode 100644
index 0000000..d4a0873
--- /dev/null
+++ b/example/idp/mycert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV
+BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx
+EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz
+MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l
+YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw
+DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7
+bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC
+FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR
+mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW
+BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9
+o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW
+BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE
+AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
+BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO
+zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN
++vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI=
+-----END CERTIFICATE-----
diff --git a/example/idp/mykey.pem b/example/idp/mykey.pem
new file mode 100644
index 0000000..d9ec5f8
--- /dev/null
+++ b/example/idp/mykey.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDkJWP7bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr
+6/ROgW96ZeQ57fzVy2MCFiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43
+qCfLx+clUlOvtnsoMiiRmo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQAB
+AoGAbx9rKH91DCw/ZEPhHsVXJ6cYHxGcMoAWvnMMC9WUN+bNo4gNL205DLfsxXA1
+jqXFXZj3+38vSFumGPA6IvXrN+Wyp3+Lz3QGc4K5OdHeBtYlxa6EsrxPgvuxYDUB
+vx3xdWPMjy06G/ML+pR9XHnRaPNubXQX3UxGBuLjwNXVmyECQQD2/D84tYoCGWoq
+5FhUBxFUy2nnOLKYC/GGxBTX62iLfMQ3fbQcdg2pJsB5rrniyZf7UL+9FOsAO9k1
+8DO7G12DAkEA7Hkdg1KEw4ZfjnnjEa+KqpyLTLRQ91uTVW6kzR+4zY719iUJ/PXE
+PxJqm1ot7mJd1LW+bWtjLpxs7jYH19V+kQJBAIEpn2JnxdmdMuFlcy/WVmDy09pg
+0z0imdexeXkFmjHAONkQOv3bWv+HzYaVMo8AgCOksfEPHGqN4eUMTfFeuUMCQF+5
+E1JSd/2yCkJhYqKJHae8oMLXByNqRXTCyiFioutK4JPYIHfugJdLfC4QziD+Xp85
+RrGCU+7NUWcIJhqfiJECQAIgUAzfzhdj5AyICaFPaOQ+N8FVMLcTyqeTXP0sIlFk
+JStVibemTRCbxdXXM7OVipz1oW3PBVEO3t/VyjiaGGg=
+-----END RSA PRIVATE KEY-----
diff --git a/example/idp/who.ini b/example/idp/who.ini
new file mode 100644
index 0000000..7ae28e7
--- /dev/null
+++ b/example/idp/who.ini
@@ -0,0 +1,55 @@
+[plugin:form]
+# identificaion and challenge
+use = s2repoze.plugins.formswithhidden:make_plugin
+login_form_qs = __do_login
+rememberer_name = auth_tkt
+#form = %(here)s/login_form.html
+
+[plugin:auth_tkt]
+# identification
+use = repoze.who.plugins.auth_tkt:make_plugin
+secret = cassiopeja
+cookie_name = kustrask
+secure = False
+include_ip = False
+
+[plugin:basicauth]
+# identification and challenge
+use = repoze.who.plugins.basicauth:make_plugin
+realm = 'sample'
+
+[plugin:htpasswd]
+# authentication
+use = repoze.who.plugins.htpasswd:make_plugin
+filename = %(here)s/passwd
+check_fn = repoze.who.plugins.htpasswd:crypt_check
+
+[plugin:ini]
+use = s2repoze.plugins.ini:make_plugin
+ini_file = %(here)s/user.ini
+
+[general]
+request_classifier = repoze.who.classifiers:default_request_classifier
+challenge_decider = repoze.who.classifiers:default_challenge_decider
+remote_user_key = REMOTE_USER
+
+[identifiers]
+# plugin_name;classifier_name:.. or just plugin_name (good for any)
+plugins =
+ form;browser
+ auth_tkt
+ basicauth
+
+[authenticators]
+# plugin_name;classifier_name.. or just plugin_name (good for any)
+plugins =
+ htpasswd
+
+[challengers]
+# plugin_name;classifier_name:.. or just plugin_name (good for any)
+plugins =
+ form;browser
+ basicauth
+
+[mdproviders]
+plugins = ini
diff --git a/example/sp/application.py b/example/sp/application.py
new file mode 100755
index 0000000..542a137
--- /dev/null
+++ b/example/sp/application.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+
+import re
+from cgi import escape
+import urllib
+
+# -----------------------------------------------------------------------------
+def dict_to_table(ava, width=1):
+ txt = []
+ txt.append('\n' % width)
+ for prop, valarr in ava.items():
+ txt.append("\n")
+ if isinstance(valarr, basestring):
+ txt.append("| %s | \n" % str(prop))
+ try:
+ txt.append("%s | \n" % valarr.encode("utf8"))
+ except AttributeError:
+ txt.append("%s | \n" % valarr)
+ elif isinstance(valarr, list):
+ i = 0
+ n = len(valarr)
+ for val in valarr:
+ if i == 0:
+ txt.append("%s\n" % (len(valarr),prop))
+ else:
+ txt.append(" |
\n")
+ if isinstance(val, dict):
+ txt.append("| \n")
+ txt.extend(dict_to_table(val, lev+1, width-1))
+ txt.append(" | \n")
+ else:
+ try:
+ txt.append("%s | \n" % val.encode("utf8"))
+ except AttributeError:
+ txt.append("%s | \n" % val)
+ if n > 1:
+ txt.append("
\n")
+ n -= 1
+ i += 1
+ elif isinstance(valarr, dict):
+ txt.append("%s | \n" % prop)
+ txt.append("\n")
+ txt.extend(dict_to_table(valarr, lev+1, width-1))
+ txt.append(" | \n")
+ txt.append("\n")
+ txt.append('
\n')
+ return txt
+
+
+def whoami(environ, start_response, user, logger):
+ start_response('200 OK', [('Content-Type', 'text/html')])
+ identity = environ["repoze.who.identity"]["user"]
+ response = dict_to_table(identity)
+ return response[:]
+
+def not_found(environ, start_response):
+ """Called if no URL matches."""
+ start_response('404 NOT FOUND', [('Content-Type', 'text/plain')])
+ return ['Not Found']
+
+def not_authn(environ, start_response):
+ start_response('401 Unauthorized', [('Content-Type', 'text/plain')])
+ return ['Unknown user']
+
+# ----------------------------------------------------------------------------
+
+# map urls to functions
+urls = [
+ (r'whoami$', whoami),
+]
+
+# ----------------------------------------------------------------------------
+
+def application(environ, start_response):
+ """
+ The main WSGI application. Dispatch the current request to
+ the functions from above and store the regular expression
+ captures in the WSGI environment as `myapp.url_args` so that
+ the functions from above can access the url placeholders.
+
+ If nothing matches call the `not_found` function.
+
+ :param environ: The HTTP application environment
+ :param start_response: The application to run when the handling of the
+ request is done
+ :return: The response as a list of lines
+ """
+ user = environ.get("REMOTE_USER", "")
+ 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( " PATH: %s" % path)
+ for regex, callback in urls:
+ if user:
+ match = re.search(regex, path)
+ if match is not None:
+ try:
+ environ['myapp.url_args'] = match.groups()[0]
+ except IndexError:
+ environ['myapp.url_args'] = path
+ return callback(environ, start_response, user, logger)
+ else:
+ return not_authn(environ, start_response)
+ return not_found(environ, start_response)
+
+# ----------------------------------------------------------------------------
+
+from repoze.who.config import make_middleware_with_config
+
+app_with_auth = make_middleware_with_config(application, {"here":"."},
+ './who_saml2.ini', log_file="repo.log")
+
+# ----------------------------------------------------------------------------
+
+if __name__ == '__main__':
+ from wsgiref.simple_server import make_server
+ srv = make_server('localhost', 8087, app_with_auth)
+ srv.serve_forever()
\ No newline at end of file
diff --git a/example/sp/mycert.pem b/example/sp/mycert.pem
new file mode 100644
index 0000000..d4a0873
--- /dev/null
+++ b/example/sp/mycert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV
+BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx
+EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz
+MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l
+YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw
+DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7
+bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC
+FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR
+mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW
+BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9
+o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW
+BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE
+AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
+BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO
+zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN
++vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI=
+-----END CERTIFICATE-----
diff --git a/example/sp/mykey.pem b/example/sp/mykey.pem
new file mode 100644
index 0000000..d9ec5f8
--- /dev/null
+++ b/example/sp/mykey.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDkJWP7bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr
+6/ROgW96ZeQ57fzVy2MCFiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43
+qCfLx+clUlOvtnsoMiiRmo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQAB
+AoGAbx9rKH91DCw/ZEPhHsVXJ6cYHxGcMoAWvnMMC9WUN+bNo4gNL205DLfsxXA1
+jqXFXZj3+38vSFumGPA6IvXrN+Wyp3+Lz3QGc4K5OdHeBtYlxa6EsrxPgvuxYDUB
+vx3xdWPMjy06G/ML+pR9XHnRaPNubXQX3UxGBuLjwNXVmyECQQD2/D84tYoCGWoq
+5FhUBxFUy2nnOLKYC/GGxBTX62iLfMQ3fbQcdg2pJsB5rrniyZf7UL+9FOsAO9k1
+8DO7G12DAkEA7Hkdg1KEw4ZfjnnjEa+KqpyLTLRQ91uTVW6kzR+4zY719iUJ/PXE
+PxJqm1ot7mJd1LW+bWtjLpxs7jYH19V+kQJBAIEpn2JnxdmdMuFlcy/WVmDy09pg
+0z0imdexeXkFmjHAONkQOv3bWv+HzYaVMo8AgCOksfEPHGqN4eUMTfFeuUMCQF+5
+E1JSd/2yCkJhYqKJHae8oMLXByNqRXTCyiFioutK4JPYIHfugJdLfC4QziD+Xp85
+RrGCU+7NUWcIJhqfiJECQAIgUAzfzhdj5AyICaFPaOQ+N8FVMLcTyqeTXP0sIlFk
+JStVibemTRCbxdXXM7OVipz1oW3PBVEO3t/VyjiaGGg=
+-----END RSA PRIVATE KEY-----
diff --git a/example/sp/sp_conf.py b/example/sp/sp_conf.py
new file mode 100644
index 0000000..4ce6458
--- /dev/null
+++ b/example/sp/sp_conf.py
@@ -0,0 +1,24 @@
+{
+ "service": ["sp"],
+ "entityid" : "urn:mace:example.com:saml:sp",
+ "service_url" : "http://example.com:8087/",
+ "idp_url" : "https://example.com/saml2/idp/SSOService.php",
+ "my_name" : "My first SP",
+ "debug" : 1,
+ "key_file" : "./mykey.pem",
+ "cert_file" : "./mycert.pem",
+ "xmlsec_binary" : "/opt/local/bin/xmlsec1",
+ "organization": {
+ "name": "Example Co",
+ #display_name
+ "url":"http://www.example.com/",
+ },
+ "contact": [{
+ "given_name":"John",
+ "sur_name": "Smith",
+ "email_address": "john.smith@example.com",
+ #contact_type
+ #company
+ #telephone_number
+ }]
+}
\ No newline at end of file
diff --git a/example/sp/who.ini b/example/sp/who.ini
new file mode 100644
index 0000000..984652f
--- /dev/null
+++ b/example/sp/who.ini
@@ -0,0 +1,54 @@
+[plugin:auth_tkt]
+# identification
+use = repoze.who.plugins.auth_tkt:make_plugin
+secret = kasamark
+cookie_name = pysaml2
+secure = False
+include_ip = False
+timeout = 3600
+reissue_time = 3000
+
+# IDENTIFIER
+# @param :
+# - rememberer_name : name of the plugin for remembering (delegate)
+[plugin:saml2auth]
+use = s2repoze.plugins.sp:make_plugin
+saml_conf = sp_conf.py
+rememberer_name = auth_tkt
+debug = 1
+path_logout = .*/logout.*
+
+
+# CHALLENGE DECIDER
+# @param:
+# - path_login : those regexp indicate which url should be redirected for a challenge
+# e.g. : for SAML2, will be redirected on a "/saml2/login" like url
+[plugin:decider]
+use = repoze.who.plugins.saml2.challenge_decider:make_plugin
+path_login =
+ .*/login$
+
+
+[general]
+request_classifier = repoze.who.classifiers:default_request_classifier
+remote_user_key = REMOTE_USER
+# trick : target the plugin whose name is the same
+challenge_decider = decider
+
+
+[identifiers]
+# plugin_name;classifier_name:.. or just plugin_name (good for any)
+plugins =
+ saml2auth
+ auth_tkt
+
+[authenticators]
+# plugin_name;classifier_name.. or just plugin_name (good for any)
+plugins = saml2auth
+
+[challengers]
+# plugin_name;classifier_name:.. or just plugin_name (good for any)
+plugins = saml2auth
+
+[mdproviders]
+plugins = saml2auth