Skip to content

Commit e6151b5

Browse files
author
Roland Hedberg
committed
Merge pull request #295 from rebeckag/its-dirg-changes
Support for fetching extension elements from metadata
2 parents 0b1da5a + dd74977 commit e6151b5

File tree

6 files changed

+117
-74
lines changed

6 files changed

+117
-74
lines changed

doc/howto/config.rst

+17-2
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,23 @@ This directive has as value a dictionary with one or more of the following keys:
452452
* single_logout_service (aa, idp, sp)
453453
* single_sign_on_service (idp)
454454

455-
The values per service is a list of tuples containing endpoint and binding
456-
type.
455+
The values per service is a list of endpoint specifications.
456+
An endpoint specification can either be just the URL::
457+
458+
”http://localhost:8088/A"
459+
460+
or it can be a 2-tuple (URL+binding)::
461+
462+
from saml2 import BINDING_HTTP_POST
463+
(”http://localhost:8087/A”, BINDING_HTTP_POST)
464+
465+
or a 3-tuple (URL+binding+index)::
466+
467+
from saml2 import BINDING_HTTP_POST
468+
(”http://lingon.catalogix.se:8087/A”, BINDING_HTTP_POST, 1)
469+
470+
If no binding is specified, no index can be set.
471+
If no index is specified, the index is set based on the position in the list.
457472

458473
Example::
459474

example/idp2/idp_user.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#from dirg_util.dict import LDAPDict
2-
#ldap_settings = {
1+
# from dirg_util.dict import LDAPDict
2+
# ldap_settings = {
33
# "ldapuri": "ldaps://ldap.test.umu.se",
44
# "base": "dc=umu, dc=se",
55
# "filter_pattern": "(uid=%s)",
@@ -30,9 +30,9 @@
3030
# "exact_match": True,
3131
# "firstonly_len1": True,
3232
# "timeout": 15,
33-
#}
34-
#Uncomment to use a LDAP directory instead.
35-
#USERS = LDAPDict(**ldap_settings)
33+
# }
34+
# Uncomment to use a LDAP directory instead.
35+
# USERS = LDAPDict(**ldap_settings)
3636

3737
USERS = {
3838
"testuser": {
@@ -54,7 +54,9 @@
5454
"email": "[email protected]",
5555
"displayName": "Test Testsson",
5656
"labeledURL": "http://www.example.com/test My homepage",
57-
"norEduPersonNIN": "SE199012315555"
57+
"norEduPersonNIN": "SE199012315555",
58+
"postaladdress": "postaladdress",
59+
"cn": "cn"
5860
},
5961
"roland": {
6062
"sn": "Hedberg",
@@ -67,7 +69,7 @@
6769
"o": "Example Co.",
6870
"ou": "IT",
6971
"initials": "P",
70-
#"schacHomeOrganization": "example.com",
72+
# "schacHomeOrganization": "example.com",
7173
"mail": "[email protected]",
7274
"displayName": "P. Roland Hedberg",
7375
"labeledURL": "http://www.example.com/rohe My homepage",
@@ -91,4 +93,4 @@
9193
"schacGender": "male",
9294
"schacUserPresenceID": "skype:pepe.perez"
9395
}
94-
}
96+
}

example/sp-wsgi/sp.py

+69-59
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,47 @@
11
#!/usr/bin/env python
22
from __future__ import print_function
3-
import logging
4-
import re
3+
54
import argparse
5+
import importlib
6+
import logging
67
import os
8+
import re
9+
import sys
710

8-
from six.moves.http_cookies import SimpleCookie
911
import six
10-
11-
from saml2.extension.pefim import SPCertEnc
12-
from saml2.metadata import create_metadata_string
13-
import service_conf
14-
12+
from six.moves.http_cookies import SimpleCookie
1513
from six.moves.urllib.parse import parse_qs
16-
import sys
1714

15+
import saml2.xmldsig as ds
16+
from saml2 import BINDING_HTTP_ARTIFACT
17+
from saml2 import BINDING_HTTP_POST
1818
from saml2 import BINDING_HTTP_REDIRECT, element_to_extension_element
1919
from saml2 import BINDING_SOAP
20-
from saml2 import time_util
2120
from saml2 import ecp
22-
from saml2 import BINDING_HTTP_ARTIFACT
23-
from saml2 import BINDING_HTTP_POST
21+
from saml2 import time_util
2422
from saml2.client import Saml2Client
2523
from saml2.ecp_client import PAOS_HEADER_INFO
26-
from saml2.httputil import geturl, make_cookie, parse_cookie
27-
from saml2.httputil import get_post
28-
from saml2.httputil import Response
24+
from saml2.extension.pefim import SPCertEnc
2925
from saml2.httputil import BadRequest
30-
from saml2.httputil import ServiceError
31-
from saml2.httputil import SeeOther
32-
from saml2.httputil import Unauthorized
3326
from saml2.httputil import NotFound
34-
from saml2.httputil import Redirect
3527
from saml2.httputil import NotImplemented
28+
from saml2.httputil import Redirect
29+
from saml2.httputil import Response
30+
from saml2.httputil import SeeOther
31+
from saml2.httputil import ServiceError
32+
from saml2.httputil import Unauthorized
33+
from saml2.httputil import get_post
34+
from saml2.httputil import geturl, make_cookie, parse_cookie
35+
from saml2.metadata import create_metadata_string
3636
from saml2.response import StatusError
3737
from saml2.response import VerificationError
3838
from saml2.s_utils import UnknownPrincipal
39-
from saml2.s_utils import decode_base64_and_inflate
4039
from saml2.s_utils import UnsupportedBinding
41-
from saml2.s_utils import sid
40+
from saml2.s_utils import decode_base64_and_inflate
4241
from saml2.s_utils import rndstr
43-
#from srtest import exception_trace
42+
from saml2.s_utils import sid
43+
from saml2.saml import NAMEID_FORMAT_PERSISTENT
4444
from saml2.samlp import Extensions
45-
from saml2 import xmldsig as ds
46-
import saml2.xmldsig as ds
4745

4846
logger = logging.getLogger("")
4947
hdlr = logging.FileHandler('spx.log')
@@ -54,7 +52,6 @@
5452
logger.addHandler(hdlr)
5553
logger.setLevel(logging.INFO)
5654

57-
5855
SP = None
5956
SEED = ""
6057
POLICY = None
@@ -139,7 +136,7 @@ class ECPResponse(object):
139136
def __init__(self, content):
140137
self.content = content
141138

142-
#noinspection PyUnusedLocal
139+
# noinspection PyUnusedLocal
143140
def __call__(self, environ, start_response):
144141
start_response('%s %s' % (self.code, self.title),
145142
[('Content-Type', "text/xml")])
@@ -351,7 +348,7 @@ def do(self, response, binding, relay_state="", mtype="response"):
351348
:param response: The SAML response, transport encoded
352349
:param binding: Which binding the query came in over
353350
"""
354-
#tmp_outstanding_queries = dict(self.outstanding_queries)
351+
# tmp_outstanding_queries = dict(self.outstanding_queries)
355352
if not response:
356353
logger.info("Missing Response")
357354
resp = Unauthorized('Unknown user')
@@ -405,6 +402,7 @@ def verify_attributes(self, ava):
405402

406403
return res
407404

405+
408406
# -----------------------------------------------------------------------------
409407
# REQUESTERS
410408
# -----------------------------------------------------------------------------
@@ -554,7 +552,7 @@ def redirect_to_auth(self, _cli, entity_id, came_from, sigalg=""):
554552
"single_sign_on_service", self.bindings, "idpsso",
555553
entity_id=entity_id)
556554
logger.debug("binding: %s, destination: %s", _binding,
557-
destination)
555+
destination)
558556
# Binding here is the response binding that is which binding the
559557
# IDP should use to return the response.
560558
acs = _cli.config.getattr("endpoints", "sp")[
@@ -565,19 +563,20 @@ def redirect_to_auth(self, _cli, entity_id, came_from, sigalg=""):
565563
extensions = None
566564
cert = None
567565
if _cli.config.generate_cert_func is not None:
568-
cert_str, req_key_str = _cli.config.generate_cert_func()
569-
cert = {
570-
"cert": cert_str,
571-
"key": req_key_str
572-
}
573-
spcertenc = SPCertEnc(x509_data=ds.X509Data(
574-
x509_certificate=ds.X509Certificate(text=cert_str)))
575-
extensions = Extensions(extension_elements=[
576-
element_to_extension_element(spcertenc)])
566+
cert_str, req_key_str = _cli.config.generate_cert_func()
567+
cert = {
568+
"cert": cert_str,
569+
"key": req_key_str
570+
}
571+
spcertenc = SPCertEnc(x509_data=ds.X509Data(
572+
x509_certificate=ds.X509Certificate(text=cert_str)))
573+
extensions = Extensions(extension_elements=[
574+
element_to_extension_element(spcertenc)])
577575

578576
req_id, req = _cli.create_authn_request(destination,
579577
binding=return_binding,
580-
extensions=extensions)
578+
extensions=extensions,
579+
nameid_format=NAMEID_FORMAT_PERSISTENT)
581580
_rstate = rndstr()
582581
self.cache.relay_state[_rstate] = came_from
583582
ht_args = _cli.apply_binding(_binding, "%s" % req, destination,
@@ -636,7 +635,7 @@ def do(self, message, binding, relay_state="", mtype="response"):
636635
try:
637636
txt = decode_base64_and_inflate(message)
638637
is_logout_request = 'LogoutRequest' in txt.split('>', 1)[0]
639-
except: # TODO: parse the XML correctly
638+
except: # TODO: parse the XML correctly
640639
is_logout_request = False
641640

642641
if is_logout_request:
@@ -646,10 +645,11 @@ def do(self, message, binding, relay_state="", mtype="response"):
646645

647646
return finish_logout(self.environ, self.start_response)
648647

648+
649649
# ----------------------------------------------------------------------------
650650

651651

652-
#noinspection PyUnusedLocal
652+
# noinspection PyUnusedLocal
653653
def not_found(environ, start_response):
654654
"""Called if no URL matches."""
655655
resp = NotFound('Not Found')
@@ -659,7 +659,7 @@ def not_found(environ, start_response):
659659
# ----------------------------------------------------------------------------
660660

661661

662-
#noinspection PyUnusedLocal
662+
# noinspection PyUnusedLocal
663663
def main(environ, start_response, sp):
664664
user = CACHE.get_user(environ)
665665

@@ -687,10 +687,11 @@ def disco(environ, start_response, _sp):
687687
resp.headers.append(kaka)
688688
return resp(environ, start_response)
689689

690+
690691
# ----------------------------------------------------------------------------
691692

692693

693-
#noinspection PyUnusedLocal
694+
# noinspection PyUnusedLocal
694695
def logout(environ, start_response, sp):
695696
user = CACHE.get_user(environ)
696697

@@ -737,10 +738,11 @@ def finish_logout(environ, start_response):
737738
cookie = CACHE.delete_cookie(environ)
738739

739740
resp = Response('You are now logged out of this service', headers=[
740-
cookie,
741+
cookie,
741742
])
742743
return resp(environ, start_response)
743744

745+
744746
# ----------------------------------------------------------------------------
745747

746748
# map urls to functions
@@ -768,16 +770,17 @@ def add_urls():
768770
urls.append(("%s/redirect$" % base, (SLO, "redirect", SP)))
769771
urls.append(("%s/redirect/(.*)$" % base, (SLO, "redirect", SP)))
770772

773+
771774
# ----------------------------------------------------------------------------
772775

773776
def metadata(environ, start_response):
774777
try:
775778
path = _args.path
776779
if path is None or len(path) == 0:
777-
path = os.path.dirname(os.path.abspath( __file__ ))
780+
path = os.path.dirname(os.path.abspath(__file__))
778781
if path[-1] != "/":
779782
path += "/"
780-
metadata = create_metadata_string(path+"sp_conf.py", None,
783+
metadata = create_metadata_string(path + "sp_conf.py", None,
781784
_args.valid, _args.cert, _args.keyfile,
782785
_args.id, _args.name, _args.sign)
783786
start_response('200 OK', [('Content-Type', "text/xml")])
@@ -786,6 +789,7 @@ def metadata(environ, start_response):
786789
logger.error("An error occured while creating metadata: %s", ex.message)
787790
return not_found(environ, start_response)
788791

792+
789793
def application(environ, start_response):
790794
"""
791795
The main WSGI application. Dispatch the current request to
@@ -824,23 +828,12 @@ def application(environ, start_response):
824828
resp = BadRequest("%s" % err)
825829
return resp(environ, start_response)
826830
except Exception as err:
827-
#_err = exception_trace("RUN", err)
828-
#logging.error(exception_trace("RUN", _err))
831+
# _err = exception_trace("RUN", err)
832+
# logging.error(exception_trace("RUN", _err))
829833
print(err, file=sys.stderr)
830834
resp = ServiceError("%s" % err)
831835
return resp(environ, start_response)
832836

833-
# ----------------------------------------------------------------------------
834-
835-
HOST = service_conf.HOST
836-
PORT = service_conf.PORT
837-
# ------- HTTPS -------
838-
# These should point to relevant files
839-
SERVER_CERT = service_conf.SERVER_CERT
840-
SERVER_KEY = service_conf.SERVER_KEY
841-
# This is of course the certificate chain for the CA that signed
842-
# your cert and all the way up to the top
843-
CERT_CHAIN = service_conf.CERT_CHAIN
844837

845838
if __name__ == '__main__':
846839
from cherrypy import wsgiserver
@@ -866,7 +859,8 @@ def application(environ, start_response):
866859
_parser.add_argument('-n', dest='name')
867860
_parser.add_argument('-S', dest='sign', action='store_true',
868861
help="sign the metadata")
869-
862+
_parser.add_argument('-C', dest='service_conf_module',
863+
help="service config module")
870864

871865
ARGS = {}
872866
_args = _parser.parse_args()
@@ -882,6 +876,21 @@ def application(environ, start_response):
882876
else:
883877
SEED = "SnabbtInspel"
884878

879+
if _args.service_conf_module:
880+
service_conf = importlib.import_module(_args.service_conf_module)
881+
else:
882+
import service_conf
883+
884+
HOST = service_conf.HOST
885+
PORT = service_conf.PORT
886+
# ------- HTTPS -------
887+
# These should point to relevant files
888+
SERVER_CERT = service_conf.SERVER_CERT
889+
SERVER_KEY = service_conf.SERVER_KEY
890+
# This is of course the certificate chain for the CA that signed
891+
# your cert and all the way up to the top
892+
CERT_CHAIN = service_conf.CERT_CHAIN
893+
885894
SP = Saml2Client(config_file="%s" % CNFBASE)
886895

887896
POLICY = service_conf.POLICY
@@ -904,6 +913,7 @@ def application(environ, start_response):
904913
_https = ""
905914
if service_conf.HTTPS:
906915
from cherrypy.wsgiserver import ssl_pyopenssl
916+
907917
SRV.ssl_adapter = ssl_pyopenssl.pyOpenSSLAdapter(SERVER_CERT,
908918
SERVER_KEY, CERT_CHAIN)
909919
_https = " using SSL/TLS"

src/saml2/client_base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ def create_authn_request(self, destination, vorg="", scoping=None,
243243
del kwargs["assertion_consumer_service_url"]
244244
except KeyError:
245245
try:
246-
args["attribute_consuming_service_index"] = str(kwargs[
247-
"attribute_consuming_service_index"])
248-
del kwargs["attribute_consuming_service_index"]
246+
args["assertion_consumer_service_index"] = str(kwargs[
247+
"assertion_consumer_service_index"])
248+
del kwargs["assertion_consumer_service_index"]
249249
except KeyError:
250250
if service_url_binding is None:
251251
service_urls = self.service_urls(binding)

0 commit comments

Comments
 (0)