Пример #1
0
def entities_descriptor(eds, valid_for, name, ident, sign, secc):
    entities = md.EntitiesDescriptor(entity_descriptor=eds)
    if valid_for:
        entities.valid_until = in_a_while(hours=valid_for)
    if name:
        entities.name = name
    if ident:
        entities.id = ident

    if sign:
        if not ident:
            ident = sid()

        if not secc.key_file:
            raise SAMLError("If you want to do signing you should define " +
                            "a key to sign with")

        if not secc.my_cert:
            raise SAMLError("If you want to do signing you should define " +
                            "where your public key are")

        entities.signature = pre_signature_part(ident, secc.my_cert, 1)
        entities.id = ident
        xmldoc = secc.sign_statement("%s" % entities, class_name(entities))
        entities = md.entities_descriptor_from_string(xmldoc)
    return entities
Пример #2
0
    def imp(self, spec):
        # This serves as a backwards compatibility
        if type(spec) is dict:
            # Old style...
            for key, vals in spec.items():
                for val in vals:
                    if isinstance(val, dict):
                        if not self.check_validity:
                            val["check_validity"] = False
                        self.load(key, **val)
                    else:
                        self.load(key, val)
        else:
            for item in spec:
                try:
                    key = item['class']
                except (KeyError, AttributeError):
                    raise SAMLError("Misconfiguration in metadata %s" % item)
                mod, clas = key.rsplit('.', 1)
                try:
                    mod = importlib.import_module(mod)
                    MDloader = getattr(mod, clas)
                except (ImportError, AttributeError):
                    raise SAMLError("Unknown metadata loader %s" % key)

                # Separately handle MDExtern
                if MDloader == MetaDataExtern:
                    kwargs = {
                        'http': self.http,
                        'security': self.security
                    }
                else:
                    kwargs = {}

                if self.filter:
                    kwargs["filter"] = self.filter

                for key in item['metadata']:
                    # Separately handle MetaDataFile and directory
                    if MDloader == MetaDataFile and os.path.isdir(key[0]):
                        files = [f for f in os.listdir(key[0]) if
                                 isfile(join(key[0], f))]
                        for fil in files:
                            _fil = join(key[0], fil)
                            _md = MetaDataFile(self.attrc, _fil)
                            _md.load()
                            self.metadata[_fil] = _md
                            if _md.to_old:
                                self.to_old[_fil] = _md.to_old
                        return

                    if len(key) == 2:
                        kwargs["cert"] = key[1]

                    _md = MDloader(self.attrc, key[0], **kwargs)
                    _md.load()
                    self.metadata[key[0]] = _md
                    if _md.to_old:
                        self.to_old[key[0]] = _md.to_old
Пример #3
0
    def operation(self, url, idp_entity_id, op, **opargs):
        """
        This is the method that should be used by someone that wants
        to authenticate using SAML ECP

        :param url: The page that access is sought for
        :param idp_entity_id: The entity ID of the IdP that should be
            used for authentication
        :param op: Which HTTP operation (GET/POST/PUT/DELETE)
        :param opargs: Arguments to the HTTP call
        :return: The page
        """
        if url not in opargs:
            url = self._sp

        # ********************************************
        # Phase 1 - First conversation with the SP
        # ********************************************
        # headers needed to indicate to the SP that I'm ECP enabled

        opargs["headers"] = self.add_paos_headers(opargs["headers"])

        response = self.send(url, op, **opargs)
        logger.debug("[Op] SP response: %s" % response)

        if response.status_code != 200:
            raise SAMLError("Request to SP failed: %s" % response.error)

        # The response might be a AuthnRequest instance in a SOAP envelope
        # body. If so it's the start of the ECP conversation
        # Two SOAP header blocks; paos:Request and ecp:Request
        # may also contain a ecp:RelayState SOAP header block
        # If channel-binding was part of the PAOS header any number of
        # <cb:ChannelBindings> header blocks may also be present
        # if 'holder-of-key' option then one or more <ecp:SubjectConfirmation>
        # header blocks may also be present
        try:
            respdict = self.parse_soap_message(response.text)

            self.ecp_conversation(respdict, idp_entity_id)

            # should by now be authenticated so this should go smoothly
            response = self.send(url, op, **opargs)
        except (soap.XmlParseError, AssertionError, KeyError):
            pass

        #print "RESP",response, self.http.response

        if response.status_code != 404:
            raise SAMLError("Error performing operation: %s" %
                            (response.error, ))

        return response
Пример #4
0
    def load(self, typ, *args, **kwargs):
        if typ == "local":
            key = args[0]
            _md = MetaDataFile(self.onts, self.attrc, args[0])
        elif typ == "inline":
            self.ii += 1
            key = self.ii
            _md = MetaData(self.onts, self.attrc, args[0], **kwargs)
        elif typ == "remote":
            key = kwargs["url"]
            _args = {}
            for _key in ["node_name", "check_validity"]:
                try:
                    _args[_key] = kwargs[_key]
                except KeyError:
                    pass

            _md = MetaDataExtern(self.onts, self.attrc, kwargs["url"],
                                 self.security, kwargs["cert"], self.http,
                                 **_args)
        elif typ == "mdfile":
            key = args[0]
            _md = MetaDataMD(self.onts, self.attrc, args[0])
        elif typ == "loader":
            key = args[0]
            _md = MetaDataLoader(self.onts, self.attrc, args[0])
        else:
            raise SAMLError("Unknown metadata type '%s'" % typ)

        _md.load()
        self.metadata[key] = _md
Пример #5
0
    def parse_sp_ecp_response(respdict):
        if respdict is None:
            raise SAMLError("Unexpected reply from the SP")

        logger.debug("[P1] SP response dict: %s", respdict)

        # AuthnRequest in the body or not
        authn_request = respdict["body"]

        expected_tag = "AuthnRequest"
        if authn_request.c_tag != expected_tag:
            raise ValueError(
                "Invalid AuthnRequest tag '{invalid}' should be '{valid}'".
                format(invalid=authn_request.c_tag, valid=expected_tag))

        # ecp.RelayState among headers
        _relay_state = None
        _paos_request = None
        for item in respdict["header"]:
            if item.c_tag == "RelayState" and item.c_namespace == ecp.NAMESPACE:
                _relay_state = item
            if item.c_tag == "Request" and item.c_namespace == paos.NAMESPACE:
                _paos_request = item

        if _paos_request is None:
            raise BadRequest("Missing request")

        _rc_url = _paos_request.response_consumer_url

        return {
            "authn_request": authn_request,
            "rc_url": _rc_url,
            "relay_state": _relay_state,
        }
Пример #6
0
    def parse_sp_ecp_response(respdict):
        if respdict is None:
            raise SAMLError("Unexpected reply from the SP")

        logger.debug("[P1] SP response dict: %s", respdict)

        # AuthnRequest in the body or not
        authn_request = respdict["body"]
        assert authn_request.c_tag == "AuthnRequest"

        # ecp.RelayState among headers
        _relay_state = None
        _paos_request = None
        for item in respdict["header"]:
            if item.c_tag == "RelayState" and item.c_namespace == ecp.NAMESPACE:
                _relay_state = item
            if item.c_tag == "Request" and item.c_namespace == paos.NAMESPACE:
                _paos_request = item

        if _paos_request is None:
            raise BadRequest("Missing request")

        _rc_url = _paos_request.response_consumer_url

        return {
            "authn_request": authn_request,
            "rc_url": _rc_url,
            "relay_state": _relay_state
        }
Пример #7
0
    def __init__(self,
                 attrc,
                 url=None,
                 security=None,
                 cert=None,
                 http=None,
                 **kwargs):
        """
        :params attrc:
        :params url: Location of the metadata
        :params security: SecurityContext()
        :params cert: CertificMDloaderate used to sign the metadata
        :params http:
        """
        super(MetaDataExtern, self).__init__(attrc, **kwargs)
        if not url:
            raise SAMLError('URL not specified.')
        else:
            self.url = url

        # No cert is only an error if the metadata is unsigned
        self.cert = cert

        self.security = security
        self.http = http
Пример #8
0
    def get_nameid(self, userid, nformat, sp_name_qualifier, name_qualifier):
        if nformat == NAMEID_FORMAT_PERSISTENT:
            nameid = self.match_local_id(userid, sp_name_qualifier,
                                         name_qualifier)
            if nameid:
                logger.debug(
                    "Found existing persistent NameId {nid} for user {uid}".
                    format(nid=nameid, uid=userid))
                return nameid

        _id = self.create_id(nformat, name_qualifier, sp_name_qualifier)

        if nformat == NAMEID_FORMAT_EMAILADDRESS:
            if not self.domain:
                raise SAMLError("Can't issue email nameids, unknown domain")

            _id = "%s@%s" % (_id, self.domain)

        nameid = NameID(
            format=nformat,
            sp_name_qualifier=sp_name_qualifier,
            name_qualifier=name_qualifier,
            text=_id,
        )

        self.store(userid, nameid)
        return nameid
Пример #9
0
    def __init__(self, url=None, security=None, cert=None,
                 entity_transform=None, **kwargs):
        """
        :params url: mdx service url
        :params security: SecurityContext()
        :params cert: certificate used to check signature of signed metadata
        :params entity_transform: function transforming (e.g. base64,
        sha1 hash or URL quote
        hash) the entity id. It is applied to the entity id before it is
        concatenated with the request URL sent to the MDX server. Defaults to
        sha1 transformation.
        """
        super(MetaDataMDX, self).__init__(None, **kwargs)
        if not url:
            raise SAMLError('URL for MDQ server not specified.')

        self.url = url.rstrip('/')

        if entity_transform:
            self.entity_transform = entity_transform
        else:
            self.entity_transform = MetaDataMDX.sha1_entity_transform

        self.cert = cert
        self.security = security

        # We assume that the MDQ server will return a single entity
        # described by a single <EntityDescriptor> element. The protocol
        # does allow multiple entities to be returned in an
        # <EntitiesDescriptor> element but we will not currently support
        # that use case since it is unlikely to be leveraged for most
        # flows.
        self.node_name = "%s:%s" % (md.EntityDescriptor.c_namespace,
                                      md.EntityDescriptor.c_tag)
Пример #10
0
    def pick_binding(self, service, bindings=None, descr_type="", request=None,
                     entity_id=""):
        if request and not entity_id:
            entity_id = request.issuer.text.strip()

        sfunc = getattr(self.metadata, service)

        if bindings is None:
            bindings = self.config.preferred_binding[service]

        if not descr_type:
            if self.entity_type == "sp":
                descr_type = "idpsso"
            else:
                descr_type = "spsso"

        for binding in bindings:
            try:
                srvs = sfunc(entity_id, binding, descr_type)
                if srvs:
                    return binding, destinations(srvs)[0]
            except UnsupportedBinding:
                pass

        logger.error("Failed to find consumer URL: %s, %s, %s" % (entity_id,
                                                                  bindings,
                                                                  descr_type))
        #logger.error("Bindings: %s" % bindings)
        #logger.error("Entities: %s" % self.metadata)

        raise SAMLError("Unkown entity or unsupported bindings")
Пример #11
0
    def nim_args(self, local_policy=None, sp_name_qualifier="",
                 name_id_policy=None, name_qualifier=""):
        """

        :param local_policy:
        :param sp_name_qualifier:
        :param name_id_policy:
        :param name_qualifier:
        :return:
        """

        logger.debug("local_policy: %s, name_id_policy: %s" % (local_policy,
                                                               name_id_policy))

        if name_id_policy and name_id_policy.sp_name_qualifier:
            sp_name_qualifier = name_id_policy.sp_name_qualifier
        else:
            sp_name_qualifier = sp_name_qualifier

        if name_id_policy and name_id_policy.format:
            nameid_format = name_id_policy.format
        elif local_policy:
            nameid_format = local_policy.get_nameid_format(sp_name_qualifier)
        else:
            raise SAMLError("Unknown NameID format")

        if not name_qualifier:
            name_qualifier = self.name_qualifier

        return {"nformat": nameid_format,
                "sp_name_qualifier": sp_name_qualifier,
                "name_qualifier": name_qualifier}
Пример #12
0
    def parse_authn_request_response(self, xmlstr, binding, outstanding=None):
        """ Deal with an AuthnResponse

        :param xmlstr: The reply as a xml string
        :param binding: Which binding that was used for the transport
        :param outstanding: A dictionary with session IDs as keys and
            the original web request from the user before redirection
            as values.
        :return: An response.AuthnResponse or None
        """

        try:
            _ = self.config.entityid
        except KeyError:
            raise SAMLError("Missing entity_id specification")

        resp = None
        if xmlstr:
            kwargs = {
                "outstanding_queries": outstanding,
                "allow_unsolicited": self.allow_unsolicited,
                "return_addr": self.service_url(),
                "entity_id": self.config.entityid,
                "attribute_converters": self.config.attribute_converters
            }
            try:
                resp = self._parse_response(xmlstr, AuthnResponse,
                                            "assertion_consumer_service",
                                            binding, **kwargs)
            except StatusError, err:
                logger.error("SAML status error: %s" % err)
                raise
            except UnravelError:
                return None
Пример #13
0
def parse_cookie(name, seed, kaka):
    """Parses and verifies a cookie value

    :param seed: A seed used for the HMAC signature
    :param kaka: The cookie
    :return: A tuple consisting of (payload, timestamp)
    """
    if not kaka:
        return None

    cookie_obj = SimpleCookie(kaka)
    morsel = cookie_obj.get(name)

    if morsel:
        parts = morsel.value.split("|")
        if len(parts) != 3:
            return None
            # verify the cookie signature
        sig = cookie_signature(seed, parts[0], parts[1])
        if sig != parts[2]:
            raise SAMLError("Invalid cookie signature")

        try:
            return parts[0].strip(), parts[1]
        except KeyError:
            return None
    else:
        return None
Пример #14
0
 def __call__(self, **kwargs):
     if not self.methods:
         raise SAMLError("No authentication methods defined")
     elif len(self.methods) == 1:
         return self.methods[0]
     else:
         pass  # TODO
Пример #15
0
    def load(self, typ, *args, **kwargs):
        if typ == "local":
            key = args[0]
            _md = MetaDataFile(self.onts, self.attrc, args[0])
        elif typ == "inline":
            self.ii += 1
            key = self.ii
            _md = MetaData(self.onts, self.attrc, args[0], **kwargs)
        elif typ == "remote":
            key = kwargs["url"]
            _md = MetaDataExtern(self.onts, self.attrc,
                                 kwargs["url"], self.security,
                                 kwargs["cert"], self.http,
                                 node_name=kwargs.get('node_name'))
        elif typ == "mdfile":
            key = args[0]
            _md = MetaDataMD(self.onts, self.attrc, args[0])
        elif typ == "loader":
            key = args[0]
            _md = MetaDataLoader(self.onts, self.attrc, args[0])
        else:
            raise SAMLError("Unknown metadata type '%s'" % typ)

        _md.load()
        self.metadata[key] = _md
Пример #16
0
    def __init__(self, entity_type, config=None, config_file="",
                 virtual_organization=""):
        self.entity_type = entity_type
        self.users = None

        if config:
            self.config = config
        elif config_file:
            self.config = config_factory(entity_type, config_file)
        else:
            raise SAMLError("Missing configuration")

        for item in ["cert_file", "key_file", "ca_certs"]:
            _val = getattr(self.config, item, None)
            if not _val:
                continue

            if _val.startswith("http"):
                r = requests.request("GET", _val)
                if r.status_code == 200:
                    _, filename = make_temp(r.text, ".pem", False)
                    setattr(self.config, item, filename)
                else:
                    raise Exception(
                        "Could not fetch certificate from %s" % _val)

        try:
            self.signkey = RSA.importKey(
                open(self.config.getattr("key_file", ""), 'r').read())
        except (KeyError, TypeError):
            self.signkey = None

        HTTPBase.__init__(self, self.config.verify_ssl_cert,
                          self.config.ca_certs, self.config.key_file,
                          self.config.cert_file)

        if self.config.vorg:
            for vo in self.config.vorg.values():
                vo.sp = self

        self.metadata = self.config.metadata
        self.config.setup_logger()
        self.debug = self.config.debug

        self.sec = security_context(self.config)

        if virtual_organization:
            if isinstance(virtual_organization, six.string_types):
                self.vorg = self.config.vorg[virtual_organization]
            elif isinstance(virtual_organization, VirtualOrg):
                self.vorg = virtual_organization
        else:
            self.vorg = None

        self.artifact = {}
        if self.metadata:
            self.sourceid = self.metadata.construct_source_id()
        else:
            self.sourceid = {}
Пример #17
0
    def parse_authn_request_response(self, xmlstr, binding, outstanding=None,
                                     outstanding_certs=None, conv_info=None):
        """ Deal with an AuthnResponse

        :param xmlstr: The reply as a xml string
        :param binding: Which binding that was used for the transport
        :param outstanding: A dictionary with session IDs as keys and
            the original web request from the user before redirection
            as values.
        :param outstanding_certs:
        :param conv_info: Information about the conversation.
        :return: An response.AuthnResponse or None
        """

        if not getattr(self.config, 'entityid', None):
            raise SAMLError("Missing entity_id specification")

        if not xmlstr:
            return None

        kwargs = {
            "outstanding_queries": outstanding,
            "outstanding_certs": outstanding_certs,
            "allow_unsolicited": self.allow_unsolicited,
            "want_assertions_signed": self.want_assertions_signed,
            "want_assertions_or_response_signed": self.want_assertions_or_response_signed,
            "want_response_signed": self.want_response_signed,
            "return_addrs": self.service_urls(binding=binding),
            "entity_id": self.config.entityid,
            "attribute_converters": self.config.attribute_converters,
            "allow_unknown_attributes":
                self.config.allow_unknown_attributes,
            'conv_info': conv_info
        }

        try:
            resp = self._parse_response(xmlstr, AuthnResponse,
                                        "assertion_consumer_service",
                                        binding, **kwargs)
        except StatusError as err:
            logger.error("SAML status error: %s", err)
            raise
        except UnravelError:
            return None
        except Exception as err:
            logger.error("XML parse error: %s", err)
            raise

        if not isinstance(resp, AuthnResponse):
            logger.error("Response type not supported: %s",
                         saml2.class_name(resp))
            return None

        if (resp.assertion and len(resp.response.encrypted_assertion) == 0 and
                resp.assertion.subject.name_id):
            self.users.add_information_about_person(resp.session_info())
            logger.info("--- ADDED person info ----")

        return resp
Пример #18
0
    def parse_authn_request_response(self,
                                     xmlstr,
                                     binding,
                                     outstanding=None,
                                     outstanding_certs=None):
        """ Deal with an AuthnResponse

        :param xmlstr: The reply as a xml string
        :param binding: Which binding that was used for the transport
        :param outstanding: A dictionary with session IDs as keys and
            the original web request from the user before redirection
            as values.
        :return: An response.AuthnResponse or None
        """

        try:
            _ = self.config.entityid
        except KeyError:
            raise SAMLError("Missing entity_id specification")

        resp = None
        if xmlstr:
            kwargs = {
                "outstanding_queries": outstanding,
                "outstanding_certs": outstanding_certs,
                "allow_unsolicited": self.allow_unsolicited,
                "want_assertions_signed": self.want_assertions_signed,
                "want_response_signed": self.want_response_signed,
                "return_addrs": self.service_urls(),
                "entity_id": self.config.entityid,
                "attribute_converters": self.config.attribute_converters,
                "allow_unknown_attributes":
                self.config.allow_unknown_attributes,
            }
            try:
                resp = self._parse_response(xmlstr, AuthnResponse,
                                            "assertion_consumer_service",
                                            binding, **kwargs)
            except StatusError as err:
                logger.error("SAML status error: %s" % err)
                raise
            except UnravelError:
                return None
            except Exception as exc:
                logger.error("%s" % exc)
                raise

            #logger.debug(">> %s", resp)

            if resp is None:
                return None
            elif isinstance(resp, AuthnResponse):
                self.users.add_information_about_person(resp.session_info())
                logger.info("--- ADDED person info ----")
                pass
            else:
                logger.error("Response type not supported: %s" %
                             (saml2.class_name(resp), ))
        return resp
Пример #19
0
def entity_descriptor(confd):
    mycert = None
    enc_cert = None
    if confd.cert_file is not None:
        mycert = []
        mycert.append("".join(open(confd.cert_file).readlines()[1:-1]))
        if confd.additional_cert_files is not None:
            for _cert_file in confd.additional_cert_files:
                mycert.append("".join(open(_cert_file).readlines()[1:-1]))
    if confd.encryption_keypairs is not None:
        enc_cert = []
        for _encryption in confd.encryption_keypairs:
            enc_cert.append("".join(
                open(_encryption["cert_file"]).readlines()[1:-1]))

    entd = md.EntityDescriptor()
    entd.entity_id = confd.entityid

    if confd.valid_for:
        entd.valid_until = in_a_while(hours=int(confd.valid_for))

    if confd.organization is not None:
        entd.organization = do_organization_info(confd.organization)
    if confd.contact_person is not None:
        entd.contact_person = do_contact_person_info(confd.contact_person)

    if confd.entity_category:
        entd.extensions = md.Extensions()
        ava = [AttributeValue(text=c) for c in confd.entity_category]
        attr = Attribute(attribute_value=ava,
                         name="http://macedir.org/entity-category")
        item = mdattr.EntityAttributes(attribute=attr)
        entd.extensions.add_extension_element(item)

    serves = confd.serves
    if not serves:
        raise SAMLError(
            'No service type ("sp","idp","aa") provided in the configuration')

    if "sp" in serves:
        confd.context = "sp"
        entd.spsso_descriptor = do_spsso_descriptor(confd, mycert, enc_cert)
    if "idp" in serves:
        confd.context = "idp"
        entd.idpsso_descriptor = do_idpsso_descriptor(confd, mycert, enc_cert)
    if "aa" in serves:
        confd.context = "aa"
        entd.attribute_authority_descriptor = do_aa_descriptor(
            confd, mycert, enc_cert)
    if "pdp" in serves:
        confd.context = "pdp"
        entd.pdp_descriptor = do_pdp_descriptor(confd, mycert, enc_cert)
    if "aq" in serves:
        confd.context = "aq"
        entd.authn_authority_descriptor = do_aq_descriptor(
            confd, mycert, enc_cert)

    return entd
Пример #20
0
    def pick_binding(self,
                     service,
                     bindings=None,
                     descr_type="",
                     request=None,
                     entity_id=""):
        if request and not entity_id:
            entity_id = request.issuer.text.strip()

        sfunc = getattr(self.metadata, service)

        if bindings is None:
            if request and request.protocol_binding:
                bindings = [request.protocol_binding]
            else:
                bindings = self.config.preferred_binding[service]

        if not descr_type:
            if self.entity_type == "sp":
                descr_type = "idpsso"
            else:
                descr_type = "spsso"

        _url = _index = None
        if request:
            try:
                _url = getattr(request, "%s_url" % service)
            except AttributeError:
                _url = None
                try:
                    _index = getattr(request, "%s_index" % service)
                except AttributeError:
                    pass

        for binding in bindings:
            try:
                srvs = sfunc(entity_id, binding, descr_type)
                if srvs:
                    if _url:
                        for srv in srvs:
                            if srv["location"] == _url:
                                return binding, _url
                    elif _index:
                        for srv in srvs:
                            if srv["index"] == _index:
                                return binding, srv["location"]
                    else:
                        return binding, destinations(srvs)[0]
            except UnsupportedBinding:
                pass

        logger.error("Failed to find consumer URL: %s, %s, %s" %
                     (entity_id, bindings, descr_type))
        #logger.error("Bindings: %s" % bindings)
        #logger.error("Entities: %s" % self.metadata)

        raise SAMLError("Unkown entity or unsupported bindings")
Пример #21
0
    def apply_binding(self, binding, msg_str, destination="", relay_state="",
                      response=False, sign=False, **kwargs):
        """
        Construct the necessary HTTP arguments dependent on Binding

        :param binding: Which binding to use
        :param msg_str: The return message as a string (XML) if the message is
            to be signed it MUST contain the signature element.
        :param destination: Where to send the message
        :param relay_state: Relay_state if provided
        :param response: Which type of message this is
        :param kwargs: response type specific arguments
        :return: A dictionary
        """
        # unless if BINDING_HTTP_ARTIFACT
        if response:
            typ = "SAMLResponse"
        else:
            typ = "SAMLRequest"

        if binding == BINDING_HTTP_POST:
            logger.info("HTTP POST")
            # if self.entity_type == 'sp':
            #     info = self.use_http_post(msg_str, destination, relay_state,
            #                               typ)
            #     info["url"] = destination
            #     info["method"] = "POST"
            # else:
            info = self.use_http_form_post(msg_str, destination,
                                           relay_state, typ)
            info["url"] = destination
            info["method"] = "POST"
        elif binding == BINDING_HTTP_REDIRECT:
            logger.info("HTTP REDIRECT")
            if kwargs.get('sigalg', ''):
                signer = self.sec.sec_backend.get_signer(kwargs['sigalg'])
            else:
                signer = None
            info = self.use_http_get(msg_str, destination, relay_state, typ,
                                     signer=signer, **kwargs)
            info["url"] = str(destination)
            info["method"] = "GET"
        elif binding == BINDING_SOAP or binding == BINDING_PAOS:
            info = self.use_soap(msg_str, destination, sign=sign, **kwargs)
        elif binding == BINDING_URI:
            info = self.use_http_uri(msg_str, typ, destination)
        elif binding == BINDING_HTTP_ARTIFACT:
            if response:
                info = self.use_http_artifact(msg_str, destination, relay_state)
                info["method"] = "GET"
                info["status"] = 302
            else:
                info = self.use_http_artifact(msg_str, destination, relay_state)
        else:
            raise SAMLError("Unknown binding type: %s" % binding)

        return info
Пример #22
0
    def load(self, *args, **kwargs):
        if self.filter:
            _args = {"filter": self.filter}
        else:
            _args = {}

        typ = args[0]
        if typ == "local":
            key = args[1]
            # if library read every file in the library
            if os.path.isdir(key):
                files = [f for f in os.listdir(key) if isfile(join(key, f))]
                for fil in files:
                    _fil = join(key, fil)
                    _md = MetaDataFile(self.attrc, _fil, **_args)
                    _md.load()
                    self.metadata[_fil] = _md
                return
            else:
                # else it's just a plain old file so read it
                _md = MetaDataFile(self.attrc, key, **_args)
        elif typ == "inline":
            self.ii += 1
            key = self.ii
            kwargs.update(_args)
            _md = InMemoryMetaData(self.attrc, args[1])
        elif typ == "remote":
            if "url" not in kwargs:
                raise ValueError(
                    "Remote metadata must be structured as a dict containing the key 'url'"
                )
            key = kwargs["url"]
            for _key in ["node_name", "check_validity"]:
                try:
                    _args[_key] = kwargs[_key]
                except KeyError:
                    pass

            if "cert" not in kwargs:
                kwargs["cert"] = ""

            _md = MetaDataExtern(self.attrc, kwargs["url"], self.security,
                                 kwargs["cert"], self.http, **_args)
        elif typ == "mdfile":
            key = args[1]
            _md = MetaDataMD(self.attrc, args[1], **_args)
        elif typ == "loader":
            key = args[1]
            _md = MetaDataLoader(self.attrc, args[1], **_args)
        elif typ == "mdq":
            key = args[1]
            _md = MetaDataMDX(args[1])
        else:
            raise SAMLError("Unknown metadata type '%s'" % typ)
        _md.load()
        self.metadata[key] = _md
Пример #23
0
    def response_args(self, message, bindings=None, descr_type=""):
        """

        :param message: The message to which a reply is constructed
        :param bindings: Which bindings can be used.
        :param descr_type: Type of descriptor (spssp, idpsso, )
        :return: Dictionary
        """
        info = {"in_response_to": message.id}

        if isinstance(message, AuthnRequest):
            rsrv = "assertion_consumer_service"
            descr_type = "spsso"
            info["sp_entity_id"] = message.issuer.text
            info["name_id_policy"] = message.name_id_policy
        elif isinstance(message, LogoutRequest):
            rsrv = "single_logout_service"
        elif isinstance(message, AttributeQuery):
            info["sp_entity_id"] = message.issuer.text
            rsrv = "attribute_consuming_service"
            descr_type = "spsso"
        elif isinstance(message, ManageNameIDRequest):
            rsrv = "manage_name_id_service"
        # The once below are solely SOAP so no return destination needed
        elif isinstance(message, AssertionIDRequest):
            rsrv = ""
        elif isinstance(message, ArtifactResolve):
            rsrv = ""
        elif isinstance(message, AssertionIDRequest):
            rsrv = ""
        elif isinstance(message, NameIDMappingRequest):
            rsrv = ""
        else:
            raise SAMLError("No support for this type of query")

        if bindings == [BINDING_SOAP]:
            info["binding"] = BINDING_SOAP
            info["destination"] = ""
            return info

        if rsrv:
            if not descr_type:
                if self.entity_type == "sp":
                    descr_type = "idpsso"
                else:
                    descr_type = "spsso"

            binding, destination = self.pick_binding(rsrv,
                                                     bindings,
                                                     descr_type=descr_type,
                                                     request=message)
            info["binding"] = binding
            info["destination"] = destination

        return info
Пример #24
0
    def create_logout_request(self, destination, issuer_entity_id,
                              subject_id=None, name_id=None,
                              reason=None, expire=None, message_id=0,
                              consent=None, extensions=None, sign=False,
                              session_indexes=None, sign_alg=None,
                              digest_alg=None):
        """ Constructs a LogoutRequest

        :param destination: Destination of the request
        :param issuer_entity_id: The entity ID of the IdP the request is
            target at.
        :param subject_id: The identifier of the subject
        :param name_id: A NameID instance identifying the subject
        :param reason: An indication of the reason for the logout, in the
            form of a URI reference.
        :param expire: The time at which the request expires,
            after which the recipient may discard the message.
        :param message_id: Request identifier
        :param consent: Whether the principal have given her consent
        :param extensions: Possible extensions
        :param sign: Whether the query should be signed or not.
        :param session_indexes: SessionIndex instances or just values
        :return: A LogoutRequest instance
        """

        if subject_id:
            if self.entity_type == "idp":
                name_id = NameID(text=self.users.get_entityid(subject_id,
                                                              issuer_entity_id,
                                                              False))
            else:
                name_id = NameID(text=subject_id)

        if not name_id:
            raise SAMLError("Missing subject identification")

        args = {}
        if session_indexes:
            sis = []
            for si in session_indexes:
                if isinstance(si, SessionIndex):
                    sis.append(si)
                else:
                    sis.append(SessionIndex(text=si))
            args["session_index"] = sis

        return self._message(LogoutRequest, destination, message_id,
                             consent, extensions, sign, name_id=name_id,
                             reason=reason, not_on_or_after=expire,
                             issuer=self._issuer(), sign_alg=sign_alg,
                             digest_alg=digest_alg, **args)
Пример #25
0
    def get_nameid(self, userid, nformat, sp_name_qualifier, name_qualifier):
        _id = self.create_id(nformat, name_qualifier, sp_name_qualifier)

        if nformat == NAMEID_FORMAT_EMAILADDRESS:
            if not self.domain:
                raise SAMLError("Can't issue email nameids, unknown domain")

            _id = "%s@%s" % (_id, self.domain)

        nameid = NameID(format=nformat, sp_name_qualifier=sp_name_qualifier,
                        name_qualifier=name_qualifier, text=_id)

        self.store(userid, nameid)
        return nameid
Пример #26
0
    def artifact2message(self, artifact, descriptor):
        """

        :param artifact: The Base64 encoded SAML artifact as sent over the net
        :param descriptor: The type of entity on the other side
        :return: A SAML message (request/response)
        """

        destination = self.artifact2destination(artifact, descriptor)

        if not destination:
            raise SAMLError("Missing endpoint location")

        _sid = sid()
        msg = self.create_artifact_resolve(artifact, destination, _sid)
        return self.send_using_soap(msg, destination)
Пример #27
0
    def ecp_conversation(self, respdict, idp_entity_id=None):
        """

        :param respdict:
        :param idp_entity_id:
        :return:
        """

        args = self.parse_sp_ecp_response(respdict)

        # **********************
        # Phase 2 - talk to the IdP
        # **********************

        idp_response = self.phase2(idp_entity_id=idp_entity_id, **args)

        # **********************************
        # Phase 3 - back to the SP
        # **********************************

        ht_args = self.use_soap(idp_response, args["rc_url"],
                                [args["relay_state"]])

        logger.debug("[P3] Post to SP: %s" % ht_args["data"])

        ht_args["headers"].append(('Content-Type', 'application/vnd.paos+xml'))

        # POST the package from the IdP to the SP
        response = self.send(args["rc_url"], "POST", **ht_args)

        if response.status_code == 302:
            # ignore where the SP is redirecting us to and go for the
            # url I started off with.
            pass
        else:
            print((response.error))
            raise SAMLError("Error POSTing package to SP: %s" % response.error)

        logger.debug("[P3] SP response: %s" % response.text)

        self.done_ecp = True
        logger.debug("Done ECP")

        return None
Пример #28
0
    def __init__(self,
                 entity_type,
                 config=None,
                 config_file="",
                 virtual_organization=""):
        self.entity_type = entity_type
        self.users = None

        if config:
            self.config = config
        elif config_file:
            self.config = config_factory(entity_type, config_file)
        else:
            raise SAMLError("Missing configuration")

        HTTPBase.__init__(self, self.config.verify_ssl_cert,
                          self.config.ca_certs, self.config.key_file,
                          self.config.cert_file)

        if self.config.vorg:
            for vo in list(self.config.vorg.values()):
                vo.sp = self

        self.metadata = self.config.metadata
        self.config.setup_logger()
        self.debug = self.config.debug
        self.seed = rndbytes(32)

        self.sec = security_context(self.config)

        if virtual_organization:
            if isinstance(virtual_organization, str):
                self.vorg = self.config.vorg[virtual_organization]
            elif isinstance(virtual_organization, VirtualOrg):
                self.vorg = virtual_organization
        else:
            self.vorg = None

        self.artifact = {}
        if self.metadata:
            self.sourceid = self.metadata.construct_source_id()
        else:
            self.sourceid = {}
Пример #29
0
    def load(self, typ, *args, **kwargs):
        if typ == "local":
            key = args[0]
            md = MetaDataFile(self.onts, self.attrc, args[0])
        elif typ == "inline":
            self.ii += 1
            key = self.ii
            md = MetaData(self.onts, self.attrc, args[0])
        elif typ == "remote":
            key = kwargs["url"]
            md = MetaDataExtern(self.onts, self.attrc,
                                kwargs["url"], self.security,
                                kwargs["cert"], self.http)
        elif typ == "mdfile":
            key = args[0]
            md = MetaDataMD(self.onts, self.attrc, args[0])
        else:
            raise SAMLError("Unknown metadata type '%s'" % typ)

        md.load()
        self.metadata[key] = md
Пример #30
0
 def load(self, typ, *args, **kwargs):
     if typ == "local":
         key = args[0]
         # if library read every file in the library
         if os.path.isdir(key):
             files = [f for f in listdir(key) if isfile(join(key, f))]
             for fil in files:
                 _fil = join(key, fil)
                 _md = MetaDataFile(self.onts, self.attrc, _fil)
                 _md.load()
                 self.metadata[_fil] = _md
             return
         else:
             # else it's just a plain old file so read it
             _md = MetaDataFile(self.onts, self.attrc, key)
     elif typ == "inline":
         self.ii += 1
         key = self.ii
         _md = MetaData(self.onts, self.attrc, args[0], **kwargs)
     elif typ == "remote":
         key = kwargs["url"]
         _args = {}
         for _key in ["node_name", "check_validity"]:
             try:
                 _args[_key] = kwargs[_key]
             except KeyError:
                 pass
         _md = MetaDataExtern(self.onts, self.attrc,
                              kwargs["url"], self.security,
                              kwargs["cert"], self.http, **_args)
     elif typ == "mdfile":
         key = args[0]
         _md = MetaDataMD(self.onts, self.attrc, args[0])
     elif typ == "loader":
         key = args[0]
         _md = MetaDataLoader(self.onts, self.attrc, args[0])
     else:
         raise SAMLError("Unknown metadata type '%s'" % typ)
     _md.load()
     self.metadata[key] = _md