Examples of PySaml2 usage
This commit is contained in:
201
example/idp/idp.app.py
Executable file
201
example/idp/idp.app.py
Executable file
@@ -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('<table border=%s bordercolor="black">\n' % width)
|
||||
for prop, valarr in ava.items():
|
||||
txt.append("<tr>\n")
|
||||
if isinstance(valarr, basestring):
|
||||
txt.append("<th>%s</th>\n" % str(prop))
|
||||
try:
|
||||
txt.append("<td>%s</td>\n" % valarr.encode("utf8"))
|
||||
except AttributeError:
|
||||
txt.append("<td>%s</td>\n" % valarr)
|
||||
elif isinstance(valarr, list):
|
||||
i = 0
|
||||
n = len(valarr)
|
||||
for val in valarr:
|
||||
if i == 0:
|
||||
txt.append("<th rowspan=%d>%s</td>\n" % (len(valarr),prop))
|
||||
else:
|
||||
txt.append("<tr>\n")
|
||||
if isinstance(val, dict):
|
||||
txt.append("<td>\n")
|
||||
txt.extend(dict_to_table(val, lev+1, width-1))
|
||||
txt.append("</td>\n")
|
||||
else:
|
||||
try:
|
||||
txt.append("<td>%s</td>\n" % val.encode("utf8"))
|
||||
except AttributeError:
|
||||
txt.append("<td>%s</td>\n" % val)
|
||||
if n > 1:
|
||||
txt.append("</tr>\n")
|
||||
n -= 1
|
||||
i += 1
|
||||
elif isinstance(valarr, dict):
|
||||
txt.append("<th>%s</th>\n" % prop)
|
||||
txt.append("<td>\n")
|
||||
txt.extend(dict_to_table(valarr, lev+1, width-1))
|
||||
txt.append("</td>\n")
|
||||
txt.append("</tr>\n")
|
||||
txt.append('</table>\n')
|
||||
return txt
|
||||
|
||||
REPOZE_ID_EQUIVALENT = "uid"
|
||||
FORM_SPEC = """<form name="myform" method="post" action="%s">
|
||||
<input type="hidden" name="SAMLResponse" value="%s" />
|
||||
<input type="hidden" name="RelayState" value="%s" />
|
||||
</form>"""
|
||||
|
||||
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("<head>")
|
||||
response.append("<title>SAML 2.0 POST</title>")
|
||||
response.append("</head><body>")
|
||||
#login_url = location + '?spentityid=' + "lingon.catalogix.se"
|
||||
response.append(FORM_SPEC % (consumer,
|
||||
base64.b64encode("".join(authn_resp)),"/"))
|
||||
response.append("""<script type="text/javascript" language="JavaScript">""")
|
||||
response.append(" document.myform.submit();")
|
||||
response.append("""</script>""")
|
||||
response.append("</body>")
|
||||
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( "<application> 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()
|
||||
12
example/idp/idp.conf
Normal file
12
example/idp/idp.conf
Normal file
@@ -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"],
|
||||
}
|
||||
}
|
||||
34
example/idp/metadata.xml
Normal file
34
example/idp/metadata.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<ns0:EntitiesDescriptor name="urn:mace:umu.se:saml:test" validUntil="2009-12-04T17:31:07Z" xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata"><ns0:EntityDescriptor entityID="urn:mace:umu.se:saml:roland:sp"><ns0:SPSSODescriptor AuthnRequestsSigned="False" WantAssertionsSigned="True" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:KeyDescriptor><ns1:KeyInfo xmlns:ns1="http://www.w3.org/2000/09/xmldsig#"><ns1:X509Data><ns1:X509Certificate>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=
|
||||
</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="0" /></ns0:SPSSODescriptor><ns0:Organization><ns0:OrganizationURL>http://www.umu.se/</ns0:OrganizationURL><ns0:OrganizationName>Umea University</ns0:OrganizationName></ns0:Organization><ns0:ContactPerson><ns0:GivenName>Roland</ns0:GivenName><ns0:SurName>Hedberg</ns0:SurName><ns0:EmailAddress>roland.hedberg@adm.umu.se</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><ns1:KeyInfo xmlns:ns1="http://www.w3.org/2000/09/xmldsig#"><ns1:X509Data><ns1:X509Certificate>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=
|
||||
</ns1:X509Certificate></ns1:X509Data></ns1: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:OrganizationURL>http://www.umu.se/</ns0:OrganizationURL><ns0:OrganizationName>Umea University</ns0:OrganizationName></ns0:Organization><ns0:ContactPerson><ns0:GivenName>Roland</ns0:GivenName><ns0:SurName>Hedberg</ns0:SurName><ns0:EmailAddress>roland.hedberg@adm.umu.se</ns0:EmailAddress></ns0:ContactPerson></ns0:EntityDescriptor></ns0:EntitiesDescriptor>
|
||||
18
example/idp/mycert.pem
Normal file
18
example/idp/mycert.pem
Normal file
@@ -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-----
|
||||
15
example/idp/mykey.pem
Normal file
15
example/idp/mykey.pem
Normal file
@@ -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-----
|
||||
55
example/idp/who.ini
Normal file
55
example/idp/who.ini
Normal file
@@ -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
|
||||
120
example/sp/application.py
Executable file
120
example/sp/application.py
Executable file
@@ -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('<table border=%s bordercolor="black">\n' % width)
|
||||
for prop, valarr in ava.items():
|
||||
txt.append("<tr>\n")
|
||||
if isinstance(valarr, basestring):
|
||||
txt.append("<th>%s</th>\n" % str(prop))
|
||||
try:
|
||||
txt.append("<td>%s</td>\n" % valarr.encode("utf8"))
|
||||
except AttributeError:
|
||||
txt.append("<td>%s</td>\n" % valarr)
|
||||
elif isinstance(valarr, list):
|
||||
i = 0
|
||||
n = len(valarr)
|
||||
for val in valarr:
|
||||
if i == 0:
|
||||
txt.append("<th rowspan=%d>%s</td>\n" % (len(valarr),prop))
|
||||
else:
|
||||
txt.append("<tr>\n")
|
||||
if isinstance(val, dict):
|
||||
txt.append("<td>\n")
|
||||
txt.extend(dict_to_table(val, lev+1, width-1))
|
||||
txt.append("</td>\n")
|
||||
else:
|
||||
try:
|
||||
txt.append("<td>%s</td>\n" % val.encode("utf8"))
|
||||
except AttributeError:
|
||||
txt.append("<td>%s</td>\n" % val)
|
||||
if n > 1:
|
||||
txt.append("</tr>\n")
|
||||
n -= 1
|
||||
i += 1
|
||||
elif isinstance(valarr, dict):
|
||||
txt.append("<th>%s</th>\n" % prop)
|
||||
txt.append("<td>\n")
|
||||
txt.extend(dict_to_table(valarr, lev+1, width-1))
|
||||
txt.append("</td>\n")
|
||||
txt.append("</tr>\n")
|
||||
txt.append('</table>\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( "<application> 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()
|
||||
18
example/sp/mycert.pem
Normal file
18
example/sp/mycert.pem
Normal file
@@ -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-----
|
||||
15
example/sp/mykey.pem
Normal file
15
example/sp/mykey.pem
Normal file
@@ -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-----
|
||||
24
example/sp/sp_conf.py
Normal file
24
example/sp/sp_conf.py
Normal file
@@ -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
|
||||
}]
|
||||
}
|
||||
54
example/sp/who.ini
Normal file
54
example/sp/who.ini
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user