Define PAOS AssertionConsumerService in ipsilon-client-install
authorJohn Dennis <jdennis@redhat.com>
Thu, 27 Aug 2015 20:34:40 +0000 (16:34 -0400)
committerPatrick Uiterwijk <puiterwijk@redhat.com>
Wed, 2 Sep 2015 01:26:54 +0000 (03:26 +0200)
A SAML SP will not be able to perform ECP unless a
AssertionConsumerService for the PAOS binding has been defined in it's
metadata. The PAOS AssertionConsumerService participates in the ECP
protocol exchange, specifically it's where the ECP client sends the
IdP Assertion.

If lasso starts to engage in an ECP transaction by trying to generate a
Samlp:AuthnRequest and no PAOS AssertionConsumerService is defined in
the SP metadata it will fail with a unknown provider error.

Note, AssertionConsumerService elements are indexed endpoints, there
may be one per protocol binding. Now that there is more than 1
AssertionConsumerService we set the isDefault flag to True on the
existing post response at index 0. This isn't strictly necessary
because the spec says if the default flag isn't set on any
AssertionConsumerService endpoint then the first one is selected, but
it's good practice anyway.

FWIW, if mod_auth_mellon is not configured with metadata then
mod_auth_mellon will generate it's own metadata which includes the
PAOS AssertionConsumerService. However in ipsilon-client we generate
the SP metadata and were failing to add the PAOS
AssertionConsumerService, something mellon would have done
automatically for us. This is why this bug was only first seen using
ipsilon-client-install.

Ticket: 162
Signed-off-by: John Dennis <jdennis@redhat.com>
Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
ipsilon/install/ipsilon-client-install
ipsilon/tools/saml2metadata.py

index 2c6df8e..1d65b5f 100755 (executable)
@@ -89,6 +89,7 @@ def saml2():
     url_sp = url + args['saml_sp']
     url_logout = url + args['saml_sp_logout']
     url_post = url + args['saml_sp_post']
+    url_paos = url + args['saml_sp_paos']
 
     # Generate metadata
     m = Metadata('sp')
@@ -99,7 +100,10 @@ def saml2():
     m.add_service(SAML2_SERVICE_MAP['logout-redirect'], url_logout)
     if not args['no_saml_soap_logout']:
         m.add_service(SAML2_SERVICE_MAP['slo-soap'], url_logout)
-    m.add_service(SAML2_SERVICE_MAP['response-post'], url_post, index="0")
+    m.add_service(SAML2_SERVICE_MAP['response-post'], url_post,
+                  index="0", isDefault="true")
+    m.add_service(SAML2_SERVICE_MAP['response-paos'], url_paos,
+                  index="1")
     m.add_allowed_name_format(SAML2_NAMEID_MAP[args['saml_nameid']])
     sp_metafile = os.path.join(path, 'metadata.xml')
     m.output(sp_metafile)
@@ -336,6 +340,8 @@ def parse_args():
                         help="Single Logout URL")
     parser.add_argument('--saml-sp-post', default=None,
                         help="Post response URL")
+    parser.add_argument('--saml-sp-paos', default=None,
+                        help="PAOS response URL, used for ECP")
     parser.add_argument('--no-saml-soap-logout', action='store_true',
                         default=False,
                         help="Disable Single Logout over SOAP")
@@ -366,7 +372,7 @@ def parse_args():
 
     # Validate that all path options begin with '/'
     path_args = ['saml_base', 'saml_auth', 'saml_sp', 'saml_sp_logout',
-                 'saml_sp_post']
+                 'saml_sp_post', 'saml_sp_paos']
     for path_arg in path_args:
         if args[path_arg] is not None and not args[path_arg].startswith('/'):
             raise ValueError('--%s must begin with a / character.' %
@@ -377,10 +383,11 @@ def parse_args():
     if not args['saml_sp'].startswith(args['saml_base']):
         raise ValueError('--saml-sp must be a subpath of --saml-base.')
 
-    # The saml_sp_logout and saml_sp_post settings must be subpaths
-    # of saml_sp (the mellon endpoint).
+    # The saml_sp_logout, saml_sp_post and saml_sp_paos settings must
+    # be subpaths of saml_sp (the mellon endpoint).
     path_args = {'saml_sp_logout': 'logout',
-                 'saml_sp_post': 'postResponse'}
+                 'saml_sp_post': 'postResponse',
+                 'saml_sp_paos': 'paosResponse'}
     for path_arg, default_path in path_args.items():
         if args[path_arg] is None:
             args[path_arg] = '%s/%s' % (args['saml_sp'].rstrip('/'),
index 2138777..d1b8e46 100755 (executable)
@@ -32,7 +32,9 @@ SAML2_SERVICE_MAP = {
     'slo-soap': ('SingleLogoutService',
                  lasso.SAML2_METADATA_BINDING_SOAP),
     'response-post': ('AssertionConsumerService',
-                      lasso.SAML2_METADATA_BINDING_POST)
+                      lasso.SAML2_METADATA_BINDING_POST),
+    'response-paos': ('AssertionConsumerService',
+                      lasso.SAML2_METADATA_BINDING_PAOS),
 }
 
 EDESC = '{%s}EntityDescriptor' % lasso.SAML2_METADATA_HREF