diff options
author | Roland Hedberg <roland.hedberg@adm.umu.se> | 2012-12-19 13:08:02 +0100 |
---|---|---|
committer | Roland Hedberg <roland.hedberg@adm.umu.se> | 2012-12-19 13:08:02 +0100 |
commit | 50459d616f4f5ec52f94d7a0181e4445ca867d1e (patch) | |
tree | af605edbfb88dfcbd017782f7032968bae6fa95f /src/saml2/pack.py | |
parent | f265c3b421b87c951b27306284b1b1725c39c7ab (diff) | |
download | pysaml2-50459d616f4f5ec52f94d7a0181e4445ca867d1e.tar.gz |
Complete rewrite of the metadata handling package.
Switched from using httplib2 to requests.
Diffstat (limited to 'src/saml2/pack.py')
-rw-r--r-- | src/saml2/pack.py | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/src/saml2/pack.py b/src/saml2/pack.py new file mode 100644 index 00000000..01e4cf46 --- /dev/null +++ b/src/saml2/pack.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2010-2011 UmeƄ University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Contains classes and functions that are necessary to implement +different bindings. + +Bindings normally consists of three parts: +- rules about what to send +- how to package the information +- which protocol to use +""" +import urlparse +import saml2 +import base64 +import urllib +from saml2.s_utils import deflate_and_base64_encode +import logging + +logger = logging.getLogger(__name__) + +try: + from xml.etree import cElementTree as ElementTree + if ElementTree.VERSION < '1.3.0': + # cElementTree has no support for register_namespace + # neither _namespace_map, thus we sacrify performance + # for correctness + from xml.etree import ElementTree +except ImportError: + try: + import cElementTree as ElementTree + except ImportError: + from elementtree import ElementTree + +NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" +FORM_SPEC = """<form method="post" action="%s"> + <input type="hidden" name="%s" value="%s" /> + <input type="hidden" name="RelayState" value="%s" /> + <input type="submit" value="Submit" /> +</form>""" + +def http_form_post_message(message, location, relay_state="", typ="SAMLRequest"): + """The HTTP POST binding defines a mechanism by which SAML protocol + messages may be transmitted within the base64-encoded content of a + HTML form control. + + :param message: The message + :param location: Where the form should be posted to + :param relay_state: for preserving and conveying state information + :return: A tuple containing header information and a HTML message. + """ + response = ["<head>", """<title>SAML 2.0 POST</title>""", "</head><body>"] + + if not isinstance(message, basestring): + message = "%s" % (message,) + + response.append(FORM_SPEC % (location, typ, base64.b64encode(message), + relay_state)) + + response.append("""<script type="text/javascript">""") + response.append(" window.onload = function ()") + response.append(" { document.forms[0].submit(); }") + response.append("""</script>""") + response.append("</body>") + + return [("Content-type", "text/html")], response + +#noinspection PyUnresolvedReferences +def http_post_message(message, location, relay_state="", typ="SAMLRequest"): + """ + + :param message: + :param location: + :param relay_state: + :param typ: + :return: + """ + return [("Content-type", "text/xml")], message + +def http_redirect_message(message, location, relay_state="", typ="SAMLRequest"): + """The HTTP Redirect binding defines a mechanism by which SAML protocol + messages can be transmitted within URL parameters. + Messages are encoded for use with this binding using a URL encoding + technique, and transmitted using the HTTP GET method. + + The DEFLATE Encoding is used in this function. + + :param message: The message + :param location: Where the message should be posted to + :param relay_state: for preserving and conveying state information + :return: A tuple containing header information and a HTML message. + """ + + if not isinstance(message, basestring): + message = "%s" % (message,) + + args = {typ: deflate_and_base64_encode(message)} + + if relay_state: + args["RelayState"] = relay_state + + glue_char = "&" if urlparse.urlparse(location).query else "?" + login_url = glue_char.join([location, urllib.urlencode(args)]) + headers = [('Location', login_url)] + body = [""] + + return headers, body + +def make_soap_enveloped_saml_thingy(thingy, header_parts=None): + """ Returns a soap envelope containing a SAML request + as a text string. + + :param thingy: The SAML thingy + :return: The SOAP envelope as a string + """ + envelope = ElementTree.Element('') + envelope.tag = '{%s}Envelope' % NAMESPACE + + if header_parts: + header = ElementTree.Element('') + header.tag = '{%s}Header' % NAMESPACE + envelope.append(header) + for part in header_parts: + part.become_child_element_of(header) + + body = ElementTree.Element('') + body.tag = '{%s}Body' % NAMESPACE + envelope.append(body) + + thingy.become_child_element_of(body) + + return ElementTree.tostring(envelope, encoding="UTF-8") + +def http_soap_message(message): + return ([("Content-type", "application/soap+xml")], + make_soap_enveloped_saml_thingy(message)) + +def http_paos(message, extra=None): + return ([("Content-type", "application/soap+xml")], + make_soap_enveloped_saml_thingy(message, extra)) + +def parse_soap_enveloped_saml(text, body_class, header_class=None): + """Parses a SOAP enveloped SAML thing and returns header parts and body + + :param text: The SOAP object as XML + :return: header parts and body as saml.samlbase instances + """ + envelope = ElementTree.fromstring(text) + assert envelope.tag == '{%s}Envelope' % NAMESPACE + + #print len(envelope) + body = None + header = {} + for part in envelope: + #print ">",part.tag + if part.tag == '{%s}Body' % NAMESPACE: + for sub in part: + try: + body = saml2.create_class_from_element_tree(body_class, sub) + except Exception: + raise Exception( + "Wrong body type (%s) in SOAP envelope" % sub.tag) + elif part.tag == '{%s}Header' % NAMESPACE: + if not header_class: + raise Exception("Header where I didn't expect one") + #print "--- HEADER ---" + for sub in part: + #print ">>",sub.tag + for klass in header_class: + #print "?{%s}%s" % (klass.c_namespace,klass.c_tag) + if sub.tag == "{%s}%s" % (klass.c_namespace, klass.c_tag): + header[sub.tag] = \ + saml2.create_class_from_element_tree(klass, sub) + break + + return body, header + +# ----------------------------------------------------------------------------- +# def send_using_http_get(request, destination, key_file=None, cert_file=None, +# log=None): +# +# +# http = HTTPClient(destination, key_file, cert_file, log) +# if log: log.info("HTTP client initiated") +# +# try: +# response = http.get() +# except Exception, exc: +# if log: log.info("HTTPClient exception: %s" % (exc,)) +# return None +# +# if log: log.info("HTTP request sent and got response: %s" % response) +# +# return response + + + + + +# ----------------------------------------------------------------------------- + +PACKING = { + saml2.BINDING_HTTP_REDIRECT: http_redirect_message, + saml2.BINDING_HTTP_POST: http_form_post_message, + } + +def packager( identifier ): + try: + return PACKING[identifier] + except KeyError: + raise Exception("Unkown binding type: %s" % identifier) |