import shutil
import socket
import sys
+import base64
HTTPDCONFD = '/etc/httpd/conf.d'
path = None
if not args['saml_no_httpd']:
path = os.path.join(SAML2_HTTPDIR, args['hostname'])
+ if os.path.exists(path):
+ raise Exception('Service Provider is already configured')
os.makedirs(path, 0750)
else:
path = os.getcwd()
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')
m.set_entity_id(url_sp)
m.add_certs(c)
m.add_service(SAML2_SERVICE_MAP['logout-redirect'], url_logout)
- m.add_service(SAML2_SERVICE_MAP['response-post'], url_post, index="0")
+ 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", 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)
"Error: [%s]" % e)
raise
+ sp_image = None
+ if args['saml_sp_image']:
+ try:
+ # FIXME: limit size
+ with open(args['saml_sp_image']) as f:
+ sp_image = f.read()
+ sp_image = base64.b64encode(sp_image)
+ except Exception as e: # pylint: disable=broad-except
+ logger.error("Failed to read SP Image file!\n" +
+ "Error: [%s]" % e)
+
# Register the SP
try:
saml2_register_sp(args['saml_idp_url'], args['admin_user'],
admin_password, args['saml_sp_name'],
- sp_metadata)
+ sp_metadata, args['saml_sp_description'],
+ args['saml_sp_visible'], sp_image)
except Exception as e: # pylint: disable=broad-except
logger.error("Failed to register SP with IDP!\n" +
"Error: [%s]" % e)
' configure your Service Provider')
-def saml2_register_sp(url, user, password, sp_name, sp_metadata):
+def saml2_register_sp(url, user, password, sp_name, sp_metadata,
+ sp_description, sp_visible, sp_image):
s = requests.Session()
# Authenticate to the IdP
sp_url = '%s/rest/providers/saml2/SPS/%s' % (url.rstrip('/'), sp_name)
sp_headers = {'Content-type': 'application/x-www-form-urlencoded',
'Referer': sp_url}
- sp_data = urlencode({'metadata': sp_metadata})
+ sp_data = {'metadata': sp_metadata}
+ if sp_description:
+ sp_data['description'] = sp_description
+ if sp_visible:
+ sp_data['visible'] = sp_visible
+ if sp_image:
+ if sp_image:
+ sp_data['imagefile'] = sp_image
+ sp_data = urlencode(sp_data)
r = s.post(sp_url, headers=sp_headers, data=sp_data)
if r.status_code != 201:
def saml2_uninstall():
- try:
- shutil.rmtree(os.path.join(SAML2_HTTPDIR, args['hostname']))
- except Exception, e: # pylint: disable=broad-except
- log_exception(e)
- try:
- os.remove(SAML2_CONFFILE)
- except Exception, e: # pylint: disable=broad-except
- log_exception(e)
+ path = os.path.join(SAML2_HTTPDIR, args['hostname'])
+ if os.path.exists(path):
+ try:
+ shutil.rmtree(path)
+ except Exception, e: # pylint: disable=broad-except
+ log_exception(e)
+
+ if os.path.exists(SAML2_CONFFILE):
+ try:
+ os.remove(SAML2_CONFFILE)
+ except Exception, e: # pylint: disable=broad-except
+ log_exception(e)
def uninstall():
if g in globals():
globals()[g] = val
else:
- for k in globals().keys():
+ for k in globals():
if k.lower() == g.lower():
globals()[k] = val
break
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")
parser.add_argument('--saml-secure-setup', action='store_true',
default=True, help="Turn on all security checks")
parser.add_argument('--saml-nameid', default='unspecified',
help="SAML NameID format to use")
parser.add_argument('--saml-sp-name', default=None,
help="The SP name to register with the IdP")
+ parser.add_argument('--saml-sp-description', default=None,
+ help="The description of the SP to display on the " +
+ "portal")
+ parser.add_argument('--saml-sp-visible', action='store_false',
+ default=True,
+ help="The SP is visible in the portal")
+ parser.add_argument('--saml-sp-image', default=None,
+ help="Image to display for this SP on the portal")
parser.add_argument('--debug', action='store_true', default=False,
help="Turn on script debugging")
parser.add_argument('--config-profile', default=None,
# 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.' %
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('/'),