Example #1
0
def test_crypto_backend():
    idpc = IdPConfig()
    idpc.load(IDP_XMLSECURITY)

    assert idpc.crypto_backend == 'XMLSecurity'
    sec = security_context(idpc)
    assert isinstance(sec.crypto, CryptoBackendXMLSecurity)
Example #2
0
 def dispatch(self, request, *args, **kwargs):
     """ Construct IDP server with config from settings dict
     """
     conf = IdPConfig()
     conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG))
     self.IDP = Server(config=conf)
     return super(IdPHandlerViewMixin, self).dispatch(request, *args, **kwargs)
Example #3
0
def test_crypto_backend():
    idpc = IdPConfig()
    idpc.load(IDP_XMLSECURITY)

    assert idpc.crypto_backend == 'XMLSecurity'
    sec = security_context(idpc)
    assert isinstance(sec.crypto, CryptoBackendXMLSecurity)
Example #4
0
def metadata(request):
    """ Returns an XML with the SAML 2.0 metadata for this Idp.
        The metadata is constructed on-the-fly based on the config dict in the django settings.
    """
    conf = IdPConfig()
    conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG))
    metadata = entity_descriptor(conf)
    return HttpResponse(content=text_type(metadata).encode('utf-8'), content_type="text/xml; charset=utf8")
Example #5
0
class SamlServer(object):
    """
    SAML Wrapper around pysaml2.

    Implements SAML2 Identity Provider functionality for Flask.
    """
    def __init__(self, config, attribute_map=None):
        """Initialize SAML Identity Provider.

        Args:
            config (dict): Identity Provider config info in dict form
            attribute_map (dict): Mapping of attribute keys to user data
        """
        self._config = IdPConfig()
        self._config.load(config)
        self._server = Server(config=self._config)
        self.attribute_map = {}
        if attribute_map is not None:
            self.attribute_map = attribute_map

    def handle_authn_request(self, request, login_form_cb):
        """Handles authentication request.

        TODO: create default login_form_cb, with unstyled login form?

        Args:
            request (Request): Flask request object for this HTTP transaction.
            login_form_cb (function): Function that displays login form with 
                username and password fields. Takes a single parameter which
                is the service_provider_id so the form may be styled accordingly.
        """
        if 'SAMLRequest' in request.values:
            details = self._server.parse_authn_request(request.details,
                                                       BINDING_HTTP_REDIRECT)
            # TODO: check session for already authenticated user
            # and send authn_response immediately.
            # TODO: otherwise render login form login_form_cb(service_provider_id)
        else:
            pass  # TODO: bad request?

    def get_service_provider_id(self, request):
        # TODO: pull service_provider_id from session
        pass

    def authn_response(self, userid):
        service_provider_id = get_service_provider_id()
        # TODO: send authn_response
        pass

    def get_metadata(self):
        """Returns SAML Identity Provider Metadata"""
        edesc = entity_descriptor(self._config, 24)
        if self._config.key_file:
            edesc = sign_entity_descriptor(edesc, 24, None,
                                           security_context(self._config))
        response = make_response(str(edesc))
        response.headers['Content-type'] = 'text/xml; charset=utf-8'
        return response
Example #6
0
def test_idp_1():
    c = IdPConfig().load(IDP1)
    c.context = "idp"

    print c
    assert c.endpoint("single_sign_on_service")[0] == 'http://localhost:8088/'

    attribute_restrictions = c.getattr("policy","idp").get_attribute_restriction("")
    assert attribute_restrictions["eduPersonAffiliation"][0].match("staff")
Example #7
0
 def metadata(cls) -> str:
     """ Get the IDP metadata as a string. """
     conf = IdPConfig()
     try:
         conf.load(cls.construct_metadata())
         metadata = entity_descriptor(conf)
     except Exception as e:
         raise ImproperlyConfigured(_('Could not instantiate IDP metadata based on the SAML_IDP_CONFIG settings and configured ServiceProviders: {}').format(str(e)))
     return str(metadata)
Example #8
0
def test_idp_1():
    c = IdPConfig().load(IDP1)
    c.context = "idp"

    print c
    assert c.endpoint("single_sign_on_service")[0] == 'http://localhost:8088/'

    attribute_restrictions = c.policy.get_attribute_restriction("")
    assert attribute_restrictions["eduPersonAffiliation"][0].match("staff")
 def metadata(cls) -> str:
     conf = IdPConfig()
     try:
         conf.load(cls.construct_metadata())
         metadata = entity_descriptor(conf)
     except:
         pass
     return str(metadata)
             
Example #10
0
class SamlServer(object):
    """
    SAML Wrapper around pysaml2.

    Implements SAML2 Identity Provider functionality for Flask.
    """
    def __init__(self, config, attribute_map=None):
        """Initialize SAML Identity Provider.

        Args:
            config (dict): Identity Provider config info in dict form
            attribute_map (dict): Mapping of attribute keys to user data
        """
        self._config = IdPConfig()
        self._config.load(config)
        self._server = Server(config=self._config)
        self.attribute_map = {}
        if attribute_map is not None:
            self.attribute_map = attribute_map

    def handle_authn_request(self, request, login_form_cb):
        """Handles authentication request.

        TODO: create default login_form_cb, with unstyled login form?

        Args:
            request (Request): Flask request object for this HTTP transaction.
            login_form_cb (function): Function that displays login form with 
                username and password fields. Takes a single parameter which
                is the service_provider_id so the form may be styled accordingly.
        """
        if 'SAMLRequest' in request.values:
            details = self._server.parse_authn_request(request.details,
                BINDING_HTTP_REDIRECT)
            # TODO: check session for already authenticated user
            # and send authn_response immediately.
            # TODO: otherwise render login form login_form_cb(service_provider_id)
        else:
            pass # TODO: bad request?

    def get_service_provider_id(self, request):
        # TODO: pull service_provider_id from session
        pass

    def authn_response(self, userid):
        service_provider_id = get_service_provider_id()
        # TODO: send authn_response
        pass

    def get_metadata(self):
        """Returns SAML Identity Provider Metadata"""
        edesc = entity_descriptor(self._config, 24)
        if self._config.key_file:
            edesc = sign_entity_descriptor(edesc, 24, None, security_context(self._config))
        response = make_response(str(edesc))
        response.headers['Content-type'] = 'text/xml; charset=utf-8'
        return response
Example #11
0
 def load(cls, force_refresh:bool = False) -> Server:
     if cls._server_instance is None or force_refresh:
         conf = IdPConfig() #
         md = cls.construct_metadata() #
         try:
             conf.load(md)
             cls._server_instance = Server(config=conf)
         except:
             pass
     return cls._server_instance
Example #12
0
def test_idp_2():
    c = IdPConfig().load(IDP2)
    c.context = "idp"

    print(c)
    assert c.endpoint("single_logout_service", BINDING_SOAP) == []
    assert c.endpoint("single_logout_service", BINDING_HTTP_REDIRECT) == ["http://localhost:8088/"]

    attribute_restrictions = c.getattr("policy", "idp").get_attribute_restrictions("")
    assert attribute_restrictions["edupersonaffiliation"][0].match("staff")
Example #13
0
 def dispatch(self, request, *args, **kwargs):
     """ Construct IDP server with config from settings dict
     """
     conf = IdPConfig()
     try:
         conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG))
         self.IDP = Server(config=conf)
     except Exception as e:
         return self.handle_error(request, exception=e)
     return super().dispatch(request, *args, **kwargs)
Example #14
0
 def dispatch(self, request, *args, **kwargs):
     """ Construct IDP server with config from settings dict
     """
     conf = IdPConfig()
     try:
         conf.load(copy.deepcopy(get_idp_config()))
         self.IDP = Server(config=conf)
     except Exception as e:
         return self.handle_error(request, exception=e)
     return super(IdPHandlerViewMixin,
                  self).dispatch(request, *args, **kwargs)
Example #15
0
def test_assertion_consumer_service():
    c = IdPConfig()
    c.load_file(dotname("idp_conf"))
    c.context = "idp"

    c.metadata.load("local", full_path("InCommon-metadata.xml"))

    entity_id = "https://www.zimride.com/shibboleth"
    acs = c.metadata.assertion_consumer_service(entity_id)
    assert len(acs) == 1
    assert acs[0]["location"] == 'https://www.zimride.com/Shibboleth.sso/SAML2/POST'
Example #16
0
def test_idp_2():
    c = IdPConfig().load(IDP2)
    c.context = "idp"

    print c
    assert c.endpoint("single_logout_service",
                      BINDING_SOAP) == []
    assert c.endpoint("single_logout_service",
                        BINDING_HTTP_REDIRECT) == ["http://localhost:8088/"]

    attribute_restrictions = c.policy.get_attribute_restriction("")
    assert attribute_restrictions["eduPersonAffiliation"][0].match("staff")
Example #17
0
def test_assertion_consumer_service():
    c = IdPConfig()
    c.load_file(dotname("idp_conf"))
    c.context = "idp"

    c.metadata.load("local", full_path("InCommon-metadata.xml"))

    entity_id = "https://www.zimride.com/shibboleth"
    acs = c.metadata.assertion_consumer_service(entity_id)
    assert len(acs) == 1
    assert acs[0][
        "location"] == 'https://www.zimride.com/Shibboleth.sso/SAML2/POST'
Example #18
0
    def __init__(self, config, attribute_map=None):
        """Initialize SAML Identity Provider.

        Args:
            config (dict): Identity Provider config info in dict form
            attribute_map (dict): Mapping of attribute keys to user data
        """
        self._config = IdPConfig()
        self._config.load(config)
        self._server = Server(config=self._config)
        self.attribute_map = {}
        if attribute_map is not None:
            self.attribute_map = attribute_map
Example #19
0
def run():
    '''配置IdP,生成证书、元数据文件'''
    if not os.path.exists(BASEDIR+'/djangosaml2idp/certificates/mycert.pem') or not \
        os.path.exists(BASEDIR+'/djangosaml2idp/certificates/mykey.pem'):
        create_self_signed_cert()

    conf = IdPConfig()  # pylint: disable=invalid-name
    conf.load(copy.deepcopy(idpsettings.SAML_IDP_CONFIG))
    meta_data = entity_descriptor(conf)  # pylint: disable=invalid-name
    content = text_type(meta_data).encode('utf-8')
    with open(BASEDIR + '/djangosaml2idp/saml2_config/idp_metadata.xml',
              'wb') as f:
        f.write(content)
Example #20
0
 def load(cls, force_refresh: bool = False) -> Server:
     """ Instantiate a IDP Server instance based on the config defined in the SAML_IDP_CONFIG settings.
         Throws an ImproperlyConfigured exception if it could not do so for any reason.
     """
     if cls._server_instance is None or force_refresh:
         conf = IdPConfig()
         md = cls.construct_metadata()
         try:
             conf.load(md)
             cls._server_instance = Server(config=conf)
         except Exception as e:
             raise ImproperlyConfigured(_('Could not instantiate an IDP based on the SAML_IDP_CONFIG settings and configured ServiceProviders: {}').format(str(e)))
     return cls._server_instance
Example #21
0
def test_assertion_consumer_service():
    c = IdPConfig()
    c.load_file("idp_conf")
    c.context = "idp"

    xml_src = open("inCommon-metadata.xml").read()
    # A trick so outdated data is allowed
    c.metadata.import_metadata(xml_src, "-")

    print c.metadata.entity.keys()
    entity_id = "https://www.zimride.com/shibboleth"
    acs = c.assertion_consumer_services(entity_id)
    assert len(acs) == 1
    assert acs[0].location == 'https://www.zimride.com/Shibboleth.sso/SAML2/POST'
Example #22
0
    def dispatch(self, request, *args, **kwargs):
        """
        Construct IDP server with config from settings dict
        """
        conf = IdPConfig()
        try:
            SAML_IDP_CONFIG = {  # pylint: disable=invalid-name
                'debug': settings.DEBUG,
                'xmlsec_binary': get_xmlsec_binary(['/opt/local/bin', '/usr/bin/xmlsec1']),
                'entityid': '%s/saml/metadata/' % settings.BASE_URL,
                'description': 'longguikeji IdP setup',

                'service': {
                    'idp': {
                        'name': 'Django localhost IdP',
                        'endpoints': {
                            'single_sign_on_service': [
                                ('%s/saml/sso/post/' % settings.BASE_URL, BINDING_HTTP_POST),
                                ('%s/saml/sso/redirect/' % settings.BASE_URL, BINDING_HTTP_REDIRECT),
                            ],
                        },
                        'name_id_format': [NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED],
                        'sign_response': True,
                        'sign_assertion': True,
                    },
                },

                'metadata': {
                    'local': [os.path.join(os.path.join(os.path.join(BASEDIR, 'djangosaml2idp'), \
                                                        'saml2_config'), f) for f in
                              os.listdir(BASEDIR + '/djangosaml2idp/saml2_config/') \
                              if f.split('.')[-1] == 'xml'],
                },
                # Signing
                'key_file': BASEDIR + '/djangosaml2idp/certificates/mykey.pem',
                'cert_file': BASEDIR + '/djangosaml2idp/certificates/mycert.pem',
                # Encryption
                'encryption_keypairs': [{
                    'key_file': BASEDIR + '/djangosaml2idp/certificates/mykey.pem',
                    'cert_file': BASEDIR + '/djangosaml2idp/certificates/mycert.pem',
                }],
                'valid_for': 365 * 24,
            }

            conf.load(copy.copy(SAML_IDP_CONFIG))
            self.IDP = Server(config=conf)  # pylint: disable=invalid-name
        except Exception as e:  # pylint: disable=invalid-name, broad-except
            return self.handle_error(request, exception=e)
        return super(IdPHandlerViewMixin,
                     self).dispatch(request, *args, **kwargs)
Example #23
0
    def idp_configure(self, metadata_construction=False):
        sys.path.insert(0, self.args.configpath)
        mod = import_module(self.args.config)
        self.idp_config = IdPConfig().load(mod.CONFIG, metadata_construction)

        if not self.args.insecure:
            self.idp_config.verify_ssl_cert = False
        else:
            if self.args.ca_certs:
                self.idp_config.ca_certs = self.args.ca_certs
            else:
                self.idp_config.ca_certs = "../keys/cacert.pem"
        # hack to change idp cert without config change. TODO: find interface to
        # change IDP cert after __init__
        if self.args.oper == 'sp-04':
            self.idp_config.cert_file = os.path.join(self.args.keysdir,
                                                     "non_md_cert.pem")
            self.idp_config.key_file = os.path.join(self.args.keysdir,
                                                    "non_md_key.pem")
            for f in [self.idp_config.cert_file, self.idp_config.key_file]:
                if not os.path.isfile(f):
                    print "File not found: %s" % os.path.abspath(f)
                    raise

        self.idp = Server(config=self.idp_config)
Example #24
0
    def test_authn_response_no_name_id(self, context, idp_conf, sp_conf):
        response_binding = BINDING_HTTP_REDIRECT

        fakesp_conf = SPConfig().load(sp_conf, metadata_construction=False)
        fakesp = FakeSP(fakesp_conf)

        fakeidp_conf = IdPConfig().load(idp_conf, metadata_construction=False)
        fakeidp = FakeIdP(USERS, config=fakeidp_conf)

        destination, request_params = fakesp.make_auth_req(
            idp_conf["entityid"])

        # Use the fake IdP to mock up an authentication request that has no
        # <NameID> element.
        url, auth_resp = fakeidp.handle_auth_req_no_name_id(
            request_params["SAMLRequest"],
            request_params["RelayState"],
            BINDING_HTTP_REDIRECT,
            "testuser1",
            response_binding=response_binding)

        backend = self.samlbackend

        context.request = auth_resp
        context.state[backend.name] = {
            "relay_state": request_params["RelayState"],
        }
        backend.authn_response(context, response_binding)

        context, internal_resp = backend.auth_callback_func.call_args[0]
        assert_authn_response(internal_resp)
        assert backend.name not in context.state
Example #25
0
def create_authn_response(session_id, identity=dict(), sign=True):
    config = IdPConfig()
    config.load(idp_config)
    idp_server = Server(config=config)
    idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident'))
    authn_response = str(idp_server.authn_response(
        identity=identity,
        in_response_to=session_id,
        destination='https://foo.example.com/sp/acs',
        sp_entity_id='https://foo.example.com/sp/metadata',
        name_id_policy=None,
        userid='Irrelevent',
        sign=sign,
        instance=True))
    response = samlp.response_from_string(authn_response)
    return response.assertion[0].subject.name_id.text, authn_response
Example #26
0
    def test_authn_response(self, context, idp_conf, sp_conf):
        response_binding = BINDING_HTTP_REDIRECT
        fakesp = FakeSP(SPConfig().load(sp_conf, metadata_construction=False))
        fakeidp = FakeIdP(USERS,
                          config=IdPConfig().load(idp_conf,
                                                  metadata_construction=False))
        destination, request_params = fakesp.make_auth_req(
            idp_conf["entityid"])
        url, auth_resp = fakeidp.handle_auth_req(
            request_params["SAMLRequest"],
            request_params["RelayState"],
            BINDING_HTTP_REDIRECT,
            "testuser1",
            response_binding=response_binding)

        context.request = auth_resp
        context.state[self.samlbackend.name] = {
            "relay_state": request_params["RelayState"]
        }
        self.samlbackend.authn_response(context, response_binding)

        context, internal_resp = self.samlbackend.auth_callback_func.call_args[
            0]
        self.assert_authn_response(internal_resp)
        assert self.samlbackend.name not in context.state
Example #27
0
def create_authn_response(session_id, identity=dict(), sign=True):
    config = IdPConfig()
    config.load(idp_config)
    idp_server = Server(config=config)
    idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident'))
    authn_response = str(
        idp_server.authn_response(
            identity=identity,
            in_response_to=session_id,
            destination='https://foo.example.com/sp/acs',
            sp_entity_id='https://foo.example.com/sp/metadata',
            name_id_policy=None,
            userid='Irrelevent',
            sign=sign,
            instance=True))
    response = samlp.response_from_string(authn_response)
    return response.assertion[0].subject.name_id.text, authn_response
Example #28
0
    def test_full_flow(self, satosa_config_dict, oidc_frontend_config, saml_backend_config, idp_conf):
        subject_id = "testuser1"

        # proxy config
        satosa_config_dict["FRONTEND_MODULES"] = [oidc_frontend_config]
        satosa_config_dict["BACKEND_MODULES"] = [saml_backend_config]
        satosa_config_dict["INTERNAL_ATTRIBUTES"]["attributes"] = {attr_name: {"openid": [attr_name],
                                                                               "saml": [attr_name]}
                                                                   for attr_name in USERS[subject_id]}
        _, backend_metadata = create_entity_descriptors(SATOSAConfig(satosa_config_dict))

        # application
        test_client = Client(make_app(SATOSAConfig(satosa_config_dict)), Response)

        # get frontend OP config info
        provider_config = json.loads(test_client.get("/.well-known/openid-configuration").data.decode("utf-8"))

        # create auth req
        claims_request = ClaimsRequest(id_token=Claims(**{k: None for k in USERS[subject_id]}))
        req_args = {"scope": "openid", "response_type": "id_token", "client_id": CLIENT_ID,
                    "redirect_uri": REDIRECT_URI, "nonce": "nonce",
                    "claims": claims_request.to_json()}
        auth_req = urlparse(provider_config["authorization_endpoint"]).path + "?" + urlencode(req_args)

        # make auth req to proxy
        proxied_auth_req = test_client.get(auth_req)
        assert proxied_auth_req.status == "303 See Other"

        # config test IdP
        backend_metadata_str = str(backend_metadata[saml_backend_config["name"]][0])
        idp_conf["metadata"]["inline"].append(backend_metadata_str)
        fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf))

        # create auth resp
        req_params = dict(parse_qsl(urlparse(proxied_auth_req.data.decode("utf-8")).query))
        url, authn_resp = fakeidp.handle_auth_req(
            req_params["SAMLRequest"],
            req_params["RelayState"],
            BINDING_HTTP_REDIRECT,
            subject_id,
            response_binding=BINDING_HTTP_REDIRECT)

        # make auth resp to proxy
        authn_resp_req = urlparse(url).path + "?" + urlencode(authn_resp)
        authn_resp = test_client.get(authn_resp_req)
        assert authn_resp.status == "303 See Other"

        # verify auth resp from proxy
        resp_dict = dict(parse_qsl(urlparse(authn_resp.data.decode("utf-8")).fragment))
        signing_key = RSAKey(key=rsa_load(oidc_frontend_config["config"]["signing_key_path"]),
                             use="sig", alg="RS256")
        id_token_claims = JWS().verify_compact(resp_dict["id_token"], keys=[signing_key])

        assert all(
            (name, values) in id_token_claims.items()
            for name, values in OIDC_USERS[subject_id].items()
        )
Example #29
0
def auth_response(identity, in_response_to, sp_conf):
    """Generates a fresh signed authentication response"""
    sp_entity_id = sp_conf.entityid
    idp_entity_id = sp_conf.idps().keys()[0]
    acs = sp_conf.endpoint('assertion_consumer_service')[0]
    issuer = saml.Issuer(text=idp_entity_id, format=saml.NAMEID_FORMAT_ENTITY)
    response = response_factory(issuer=issuer,
                                in_response_to=in_response_to,
                                destination=acs,
                                status=success_status_factory())
    idp_conf = IdPConfig()
    name_form = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
    idp_conf.load({
        'entityid': idp_entity_id,
        'xmlsec_binary': sp_conf.xmlsec_binary,
        'attribute_map_dir': os.path.join(BASEDIR, 'attribute-maps'),
        'service': {
            'idp': {
                'endpoints': tuple(),
                'policy': {
                    'default': {
                        "lifetime": {
                            "minutes": 15
                        },
                        "attribute_restrictions": None,
                        "name_form": name_form,
                    }
                }
            },
        },
        'key_file': os.path.join(BASEDIR, 'idpcert.key'),
        'cert_file': os.path.join(BASEDIR, 'idpcert.pem'),
        'metadata': {
            'local': [os.path.join(BASEDIR, 'sp_metadata.xml')],
        },
    })
    server = Server("", idp_conf)
    server.ident = Identifier(FakeDb())

    userid = 'irrelevant'
    response = server.authn_response(identity, in_response_to, acs,
                                     sp_entity_id, None, userid)
    return '\n'.join(response)
Example #30
0
def auth_response(identity, in_response_to, sp_conf):
    """Generates a fresh signed authentication response"""
    sp_entity_id = sp_conf.entityid
    idp_entity_id = sp_conf.idps().keys()[0]
    acs = sp_conf.endpoint('assertion_consumer_service')[0]
    issuer = saml.Issuer(text=idp_entity_id, format=saml.NAMEID_FORMAT_ENTITY)
    response = response_factory(issuer=issuer,
                                in_response_to=in_response_to,
                                destination=acs,
                                status=success_status_factory())
    idp_conf = IdPConfig()
    name_form = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
    idp_conf.load({
            'entityid': idp_entity_id,
            'xmlsec_binary': sp_conf.xmlsec_binary,
            'attribute_map_dir': os.path.join(BASEDIR, 'attribute-maps'),
            'service': {
                'idp': {
                    'endpoints': tuple(),
                    'policy':  {
                        'default': {
                            "lifetime": {"minutes": 15},
                            "attribute_restrictions": None,
                            "name_form": name_form,
                            }
                        }
                    },
                },
            'key_file': os.path.join(BASEDIR, 'idpcert.key'),
            'cert_file': os.path.join(BASEDIR, 'idpcert.pem'),
            'metadata': {
                'local': [os.path.join(BASEDIR, 'sp_metadata.xml')],
                },
            })
    server = Server("", idp_conf)
    server.ident = Identifier(FakeDb())

    userid = 'irrelevant'
    response = server.authn_response(identity, in_response_to, acs,
                                     sp_entity_id, None, userid)
    return '\n'.join(response)
Example #31
0
 def register_endpoints(self, backend_names):
     """
     See super class satosa.frontends.base.FrontendModule
     :type backend_names: list[str]
     :rtype: list[(str, ((satosa.context.Context, Any) -> satosa.response.Response, Any))]
     """
     self.idp_config = self._build_idp_config_endpoints(
         self.config[self.KEY_IDP_CONFIG], backend_names)
     # Create the idp
     idp_config = IdPConfig().load(copy.deepcopy(self.idp_config))
     self.idp = Server(config=idp_config)
     return self._register_endpoints(backend_names)
Example #32
0
 def register_endpoints(self, providers):
     """
     See super class satosa.frontends.base.FrontendModule
     :type providers: list[str]
     :rtype: list[(str, ((satosa.context.Context, Any) -> satosa.response.Response, Any))]
     """
     self._validate_providers(providers)
     self.config = self._build_idp_config_endpoints(self.config, providers)
     # Create the idp
     idp_config = IdPConfig().load(copy.deepcopy(self.config), metadata_construction=False)
     self.idp = Server(config=idp_config)
     return self._register_endpoints(providers)
Example #33
0
def _parse_metadata_dict_to_inline(metadata):
    """Convert any metadata included as dict to PySAML2's `inline` type.

    Currently PySAML supports remote, local files, and string IdP metadata to
    be included in the SP config dict as XML. It is also possible to pull your
    IdP metadata from local JSON files (the format of the JSON is nearly
    unparsable for any normal human).

    This function adds the ability to include the IdP metadata directly in the
    SP config as a dict of IdP attributes by hacking around this PySAML2
    limitation and converting the dict into XML via PySAML2's IdPConfig class.

    Note: In the process of trying to find an alternative which will allow us
        to NOT be hacking around PySAML so rudely in order to load IdP metadata
        from a Python dict. https://github.com/rohe/pysaml2/issues/172

    Args:
        metadata (dict): The IdP metadata this SP is configured for.

    Returns:
        (dict) config where any metadata `inline_dict` data has been
            converted to `inline` XML.
    """
    if metadata.get('inline_dict', None):
        metadata['inline'] = metadata.get('inline', [])
        for _idp in metadata.get('inline_dict'):
            idp_config = IdPConfig()
            idp_config.load(_idp)
            entity_desc = entity_descriptor(idp_config)
            # Hack for supporting multiple certificates.
            if _idp.get('certs'):
                # `certs` config directive overrides `cert_file`.
                entity_desc.idpsso_descriptor.key_descriptor = \
                    _parse_key_descriptors(_idp['certs'])
            idp_metadata_str = str(entity_desc)
            LOGGER.debug("IdP XML Metadata for %s: %s",
                         _idp['entityid'], idp_metadata_str)
            metadata['inline'].append(idp_metadata_str)
        del metadata['inline_dict']
    return metadata
Example #34
0
def _parse_metadata_dict_to_inline(metadata):
    """Convert any metadata included as dict to PySAML2's `inline` type.

    Currently PySAML supports remote, local files, and string IdP metadata to
    be included in the SP config dict as XML. It is also possible to pull your
    IdP metadata from local JSON files (the format of the JSON is nearly
    unparsable for any normal human).

    This function adds the ability to include the IdP metadata directly in the
    SP config as a dict of IdP attributes by hacking around this PySAML2
    limitation and converting the dict into XML via PySAML2's IdPConfig class.

    Note: In the process of trying to find an alternative which will allow us
        to NOT be hacking around PySAML so rudely in order to load IdP metadata
        from a Python dict. https://github.com/rohe/pysaml2/issues/172

    Args:
        metadata (dict): The IdP metadata this SP is configured for.

    Returns:
        (dict) config where any metadata `inline_dict` data has been
            converted to `inline` XML.
    """
    if metadata.get('inline_dict', None):
        metadata['inline'] = metadata.get('inline', [])
        for _idp in metadata.get('inline_dict'):
            idp_config = IdPConfig()
            idp_config.load(_idp)
            entity_desc = entity_descriptor(idp_config)
            # Hack for supporting multiple certificates.
            if _idp.get('certs'):
                # `certs` config directive overrides `cert_file`.
                entity_desc.idpsso_descriptor.key_descriptor = \
                    _parse_key_descriptors(_idp['certs'])
            idp_metadata_str = str(entity_desc)
            LOGGER.debug("IdP XML Metadata for %s: %s", _idp['entityid'],
                         idp_metadata_str)
            metadata['inline'].append(idp_metadata_str)
        del metadata['inline_dict']
    return metadata
Example #35
0
def test_do_idp_sso_descriptor():
    conf = IdPConfig().load(IDP)
    idpsso = metadata.do_idpsso_descriptor(conf)

    assert isinstance(idpsso, md.IDPSSODescriptor)
    assert _eq(idpsso.keyswv(), [
        'protocol_support_enumeration', 'single_sign_on_service',
        'want_authn_requests_signed', "extensions"
    ])
    exts = idpsso.extensions.extension_elements
    assert len(exts) == 2
    print(exts)
    inst = saml2.extension_element_to_element(exts[0],
                                              shibmd.ELEMENT_FROM_STRING,
                                              namespace=shibmd.NAMESPACE)
    assert isinstance(inst, shibmd.Scope)
    assert inst.text == "example.org"
    assert inst.regexp == "false"

    uiinfo = saml2.extension_element_to_element(exts[1],
                                                mdui.ELEMENT_FROM_STRING,
                                                namespace=mdui.NAMESPACE)

    assert uiinfo
    assert _eq(uiinfo.keyswv(), [
        'display_name', 'description', 'information_url',
        'privacy_statement_url', 'keywords', 'logo'
    ])

    assert len(uiinfo.privacy_statement_url) == 1
    assert uiinfo.privacy_statement_url[
        0].text == "http://example.com/saml2/privacyStatement.html"
    assert len(uiinfo.description) == 1
    assert uiinfo.description[0].text == "Exempel bolag"
    assert uiinfo.description[0].lang == "se"

    res = extension_elements_to_elements(exts, [shibmd, mdui])

    assert len(res) == 2
    # one is a shibmd.Scope instance and the other a mdui.UIInfo instance
    if isinstance(res[0], shibmd.Scope):
        assert isinstance(res[1], mdui.UIInfo)
    elif isinstance(res[1], shibmd.Scope):
        assert isinstance(res[0], mdui.UIInfo)

    found = idpsso.extensions.find_extensions(mdui.UIInfo.c_tag,
                                              mdui.NAMESPACE)
    assert len(found) == 1

    elem = idpsso.extensions.extensions_as_elements(mdui.UIInfo.c_tag, mdui)
    assert len(elem) == 1
    assert isinstance(elem[0], mdui.UIInfo)
Example #36
0
def create_logout_response(subject_id, destination, issuer_entity_id,
        req_entity_id, sign=True):
    config = IdPConfig()
    config.load(idp_config)
    idp_server = Server(config=config)
    # construct a request
    logout_request = create_logout_request(
        subject_id=subject_id,
        destination=destination,
        issuer_entity_id=issuer_entity_id,
        req_entity_id=req_entity_id)
    #idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident'))
    resp, headers, message = idp_server.logout_response(
        request=logout_request,
        bindings=[BINDING_HTTP_REDIRECT],
        sign=sign)
    location = dict(headers).get('Location')
    url = urlparse.urlparse(location)
    params = urlparse.parse_qs(url.query)
    logout_response_xml = decode_base64_and_inflate(params['SAMLResponse'][0])
    response = samlp.logout_response_from_string(logout_response_xml)
    return response.in_response_to, logout_response_xml
Example #37
0
def get_idp_config(saml_idp_config=settings.SAML_IDP_CONFIG):
    conf = IdPConfig()
    idp_config = copy.deepcopy(saml_idp_config)

    # this is only used for merge DB metadatastores configurations
    db_mdstores = MetadataStore.as_pysaml_mdstore_dict()
    for k,v in db_mdstores.items():
        if not idp_config['metadata'].get(k):
            idp_config['metadata'][k] = []
        for endpoint in v:
            if endpoint not in idp_config['metadata'][k]:
                idp_config['metadata'][k].append(endpoint)
    # end DB metadatastores configurations
    try:
        conf.load(idp_config)
    except FileNotFoundError as e: # pragma: no cover
        raise MetadataNotFound(e)
    except xml.etree.ElementTree.ParseError as e: # pragma: no cover
        raise SPConfigurationMissing(e)
    except Exception as e: # pragma: no cover
        raise Exception(e)
    return Server(config=conf)
Example #38
0
 def setup(self):
     """
     Initiates the test.
     :return: None
     """
     self.sp = FakeSP(None,
                      config=SPConfig().load(
                          TestConfiguration.get_instance().fake_sp_config,
                          metadata_construction=False))
     self.idp = FakeIdP(
         USERS,
         IdPConfig().load(TestConfiguration.get_instance().fake_idp_config,
                          metadata_construction=False))
Example #39
0
def create_logout_response(subject_id,
                           destination,
                           issuer_entity_id,
                           req_entity_id,
                           sign=True):
    config = IdPConfig()
    config.load(idp_config)
    idp_server = Server(config=config)
    # construct a request
    logout_request = create_logout_request(subject_id=subject_id,
                                           destination=destination,
                                           issuer_entity_id=issuer_entity_id,
                                           req_entity_id=req_entity_id)
    #idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident'))
    resp, headers, message = idp_server.logout_response(
        request=logout_request, bindings=[BINDING_HTTP_REDIRECT], sign=sign)
    location = dict(headers).get('Location')
    url = urlparse.urlparse(location)
    params = urlparse.parse_qs(url.query)
    logout_response_xml = decode_base64_and_inflate(params['SAMLResponse'][0])
    response = samlp.logout_response_from_string(logout_response_xml)
    return response.in_response_to, logout_response_xml
Example #40
0
    def __init__(self, config, attribute_map=None):
        """Initialize SAML Service Provider.

        Args:
            config (dict): Service Provider config info in dict form
            attribute_map (dict): Mapping of attribute keys to user data
        """
        self._config = SPConfig()
        self._config.load(config)
        if config['metadata'].get('config'):
            # Hacked in a way to get the IdP metadata from a python dict
            # rather than having to resort to loading XML from file or http.
            idp_config = IdPConfig()
            idp_config.load(config['metadata']['config'][0])
            idp_entityid = config['metadata']['config'][0]['entityid']
            idp_metadata_str = str(entity_descriptor(idp_config, 24))
            LOGGER.debug('IdP XML Metadata for %s: %s' % (
                idp_entityid, idp_metadata_str))
            self._config.metadata.import_metadata(
                idp_metadata_str, idp_entityid)
        self.attribute_map = {}
        if attribute_map is not None:
            self.attribute_map = attribute_map
def saml_redirect(request, sp_name, ms):
    '''
    Redirect to a saml sp acs
    '''
    # ** Init SAML IDP
    setting = get_saml_setting(sp_name)

    conf = IdPConfig()
    conf.load(copy.deepcopy(setting))

    IDP = server.Server(config=conf, cache=Cache())
    IDP.ticket = {}

    # ** Get sp entity id from sp.xml
    entity_id = IDP.metadata.keys()[0]

    # ** Get binding and acs destination
    # pass bindings=None, correct?
    binding, destination = IDP.pick_binding("assertion_consumer_service", entity_id=entity_id)

    authn = {'class_ref': 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password'}

    # ** Prepare attributes
    attribute_setting = ms.get('attributes')
    parsed_data = {}
    for attr in attribute_setting:
        if not attr['name']:
            continue

        mapped_name = attr['map'] if 'map' in attr else attr['name']
        value = None

        try:
            if attr['name'] == "email":
                value = request.user.email
            if attr['name'] == "first_name":
                value = request.user.first_name
            elif attr['name'] == "last_name":
                value = request.user.last_name
            elif attr['name'] == "username":
                value = request.user.username
            elif attr['name'] == "state":
                value = request.user.profile.district.state.name
            elif attr['name'] == "district":
                value = request.user.profile.district.name
            elif attr['name'] == "school":
                value = request.user.profile.school.name
            elif attr['name'] == "grades":
                value = request.user.profile.grade_level_id
            elif attr['name'] == "bio":
                value = request.user.profile.bio
            elif attr['name'] == "internal_id":
                value = str(request.user.id)
            elif attr['name'] == "avatar":
                value = request.build_absolute_uri(reverse('user_photo', args=[request.user.id]))
        except:
            value = None
        if value is not None:
            parsed_data[mapped_name] = [value]
        else:
            parsed_data[mapped_name] = ['']


    # ** Get the X509Certificate string from sp.xml
    sign = IDP.metadata.certs(entity_id, "any", "signing")

    # ** Create authn response
    identity = parsed_data
    resp = IDP.create_authn_response(
        issuer=setting.get('entityid'),  # "https://localhost:8088/idp.xml",
        identity=identity,
        sign_response=sign,
        sign_assertion=sign,
        in_response_to=None,
        destination=destination,
        sp_entity_id=entity_id,
        name_id_policy=None,             # "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
        authn=authn,
        encrypt_cert="",
        encrypt_assertion="",
        # userid="%s" % request.user.id,
        )

    # ** Translate to http response
    http_args = IDP.apply_binding(
        binding=binding,
        msg_str=resp,
        destination=destination,
        relay_state="",
        response=True)

    resp = "\n".join(http_args["data"])
    resp = resp.replace("<body>", "<body style='display:none'>")
    return HttpResponse(resp)