Ejemplo n.º 1
0
    def set(self, name_id, entity_id, info, not_on_or_after=0):
        """ Stores session information in the cache. Assumes that the name_id
        is unique within the context of the Service Provider.

        :param name_id: The subject identifier, a NameID instance
        :param entity_id: The identifier of the entity_id/receiver of an
            assertion
        :param info: The session info, the assertion is part of this
        :param not_on_or_after: A time after which the assertion is not valid.
        """
        info = dict(info)
        if 'name_id' in info and not isinstance(info['name_id'],
                                                six.string_types):
            # make friendly to (JSON) serialization
            info['name_id'] = code(name_id)

        cni = code(name_id)
        if cni not in self._db:
            self._db[cni] = {}

        self._db[cni][entity_id] = (not_on_or_after, info)
        if self._sync:
            try:
                self._db.sync()
            except AttributeError:
                pass
Ejemplo n.º 2
0
    def set(self, name_id, entity_id, info, not_on_or_after=0):
        """ Stores session information in the cache. Assumes that the name_id
        is unique within the context of the Service Provider.

        :param name_id: The subject identifier, a NameID instance
        :param entity_id: The identifier of the entity_id/receiver of an
            assertion
        :param info: The session info, the assertion is part of this
        :param not_on_or_after: A time after which the assertion is not valid.
        """
        info = dict(info)
        if 'name_id' in info and not isinstance(info['name_id'], six.string_types):
            # make friendly to (JSON) serialization
            info['name_id'] = code(name_id)

        cni = code(name_id)
        if cni not in self._db:
            self._db[cni] = {}

        self._db[cni][entity_id] = (not_on_or_after, info)
        if self._sync:
            try:
                self._db.sync()
            except AttributeError:
                pass
Ejemplo n.º 3
0
 def store_assertion(self, assertion, to_sign):
     self.assertion[assertion.id] = (assertion, to_sign)
     key = sha1(code(assertion.subject.name_id)).hexdigest()
     try:
         self.authn[key].append(assertion.authn_statement)
     except KeyError:
         self.authn[key] = [assertion.authn_statement]
Ejemplo n.º 4
0
def login_action(session_info, user):
    """
    Upon successful login in the IdP, store login info in the session
    and redirect back to the app that asked for authn.

    :param session_info: the SAML session info
    :type session_info: dict

    :param user: the authenticated user
    :type user: eduid_userdb.User
    """
    current_app.logger.info("User {!r} logging in.".format(user))
    session['_saml2_session_name_id'] = code(session_info['name_id'])
    session['eduPersonPrincipalName'] = user.eppn
    session['user_eppn'] = user.eppn
    loa = get_loa(current_app.config.get('AVAILABLE_LOA'), session_info)
    session['eduPersonAssurance'] = loa
    session.persist()

    # redirect the user to the view where he came from
    relay_state = request.form.get('RelayState', '/')
    current_app.logger.debug('Redirecting to the RelayState: ' + relay_state)
    response = redirect(location=relay_state)
    session.set_cookie(response)
    current_app.logger.info('Redirecting user {!r} to {!r}'.format(
        user, relay_state))
    return response
Ejemplo n.º 5
0
 def store_assertion(self, assertion, to_sign):
     self.assertion[assertion.id] = (assertion, to_sign)
     key = sha1(code(assertion.subject.name_id)).hexdigest()
     try:
         self.authn[key].append(assertion.authn_statement)
     except KeyError:
         self.authn[key] = [assertion.authn_statement]
Ejemplo n.º 6
0
    def get_assertions_by_subject(self,
                                  name_id=None,
                                  session_index=None,
                                  requested_context=None):
        """

        :param name_id: One of name_id or key can be used to get the authn
            statement
        :param session_index: If match against a session index should be done
        :param requested_context: Authn statements should match a specific
            authn context
        :return:
        """
        result = []
        key = sha1(code(name_id)).hexdigest()
        for item in self.assertion.find({"name_id_key": key}):
            assertion = from_dict(item["assertion"], ONTS, True)
            if session_index or requested_context:
                for statement in assertion.authn_statement:
                    if session_index:
                        if statement.session_index == session_index:
                            result.append(assertion)
                            break
                    if requested_context:
                        if context_match(requested_context,
                                         statement.authn_context):
                            result.append(assertion)
                            break
            else:
                result.append(assertion)
        return result
Ejemplo n.º 7
0
    def get_assertions_by_subject(self, name_id=None, session_index=None,
                                  requested_context=None):
        """

        :param name_id: One of name_id or key can be used to get the authn
            statement
        :param session_index: If match against a session index should be done
        :param requested_context: Authn statements should match a specific
            authn context
        :return:
        """
        result = []
        key = sha1(code(name_id)).hexdigest()
        for item in self.assertion.find({"name_id_key": key}):
            assertion = from_dict(item["assertion"], ONTS, True)
            if session_index or requested_context:
                for statement in assertion.authn_statement:
                    if session_index:
                        if statement.session_index == session_index:
                            result.append(assertion)
                            break
                    if requested_context:
                        if context_match(requested_context,
                                         statement.authn_context):
                            result.append(assertion)
                            break
            else:
                result.append(assertion)
        return result
Ejemplo n.º 8
0
    def get_authn_statements(self, name_id=None, key="", session_index=None,
                             requested_context=None):
        """

        :param name_id: One of name_id or key can be used to get the authn
            statement
        :param key:
        :param session_index:
        :param requested_context:
        :return:
        """
        result = []
        key = sha1(code(name_id)).hexdigest()
        try:
            statements = [from_dict(t, ONTS, True) for t in self.authn[key]]
        except KeyError:
            logger.info("Unknown subject %s" % name_id)
            return []

        for statement in statements:
            if session_index:
                if statement.session_index != session_index:
                    continue
            if requested_context:
                if not context_match(requested_context,
                                     statement.authn_context):
                    continue
            result.append(statement)

        return result
Ejemplo n.º 9
0
    def test_extend_person(self):
        session_info = {
            "name_id": nid,
            "issuer": IDP_OTHER,
            "not_on_or_after": in_a_while(minutes=15),
            "ava": {
                "eduPersonEntitlement": "Anka"
            }
        }
        
        self.population.add_information_about_person(session_info)
        
        issuers = self.population.issuers_of_info(nid)
        assert _eq(issuers, [IDP_ONE, IDP_OTHER])
        subjects = [code(c) for c in self.population.subjects()]
        assert subjects == [cnid]
        # Are any of the sources gone stale
        stales = self.population.stale_sources_for_person(nid)
        assert stales == []
        # are any of the possible sources not used or gone stale
        possible = [IDP_ONE, IDP_OTHER]
        stales = self.population.stale_sources_for_person(nid, possible)
        assert stales == []

        (identity, stale) = self.population.get_identity(nid)
        assert stale == []
        assert identity == {'mail': '*****@*****.**', 
                            'givenName': 'Anders', 
                            'surName': 'Andersson',
                            "eduPersonEntitlement": "Anka"}

        info = self.population.get_info_from(nid, IDP_OTHER)
        assert _eq(list(info.keys()), ["not_on_or_after", "name_id", "ava"])
        assert info["name_id"] == nid
        assert info["ava"] == {"eduPersonEntitlement": "Anka"}
Ejemplo n.º 10
0
    def get_authn_statements(self,
                             name_id,
                             session_index=None,
                             requested_context=None):
        """

        :param name_id:
        :param session_index:
        :param requested_context:
        :return:
        """
        result = []
        _id = code(name_id)
        key = sha1(_id.encode()).hexdigest()
        try:
            statements = self.authn[key]
        except KeyError:
            logger.info("Unknown subject %s" % name_id)
            return []

        for statement in statements:
            if session_index:
                if statement.session_index != session_index:
                    continue
            if requested_context:
                if not context_match(requested_context,
                                     statement[0].authn_context):
                    continue
            result.append(statement)

        return result
Ejemplo n.º 11
0
    def get_authn_statements(self, name_id, session_index=None,
                             requested_context=None):
        """

        :param name_id:
        :param session_index:
        :param requested_context:
        :return:
        """
        result = []
        key = sha1(code(name_id)).hexdigest()
        try:
            statements = self.authn[key]
        except KeyError:
            logger.info("Unknown subject %s" % name_id)
            return []

        for statement in statements:
            if session_index:
                if statement.session_index != session_index:
                    continue
            if requested_context:
                if not context_match(requested_context,
                                     statement.authn_context):
                    continue
            result.append(statement)

        return result
Ejemplo n.º 12
0
 def set(self, name_id, entity_id, info, *args, **kwargs):
     try:
         name_id = info['name_id']
     except KeyError:
         pass
     else:
         info = dict(info)
         info['name_id'] = code(name_id)
     return super(IdentityCache, self).set(name_id, entity_id, info, *args, **kwargs)
Ejemplo n.º 13
0
    def entities(self, name_id):
        """ Returns all the entities of assertions for a subject, disregarding
        whether the assertion still is valid or not.

        :param name_id: The subject identifier, a NameID instance
        :return: A possibly empty list of entity identifiers
        """
        cni = code(name_id)
        return list(self._db[cni].keys())
Ejemplo n.º 14
0
    def entities(self, name_id):
        """ Returns all the entities of assertions for a subject, disregarding
        whether the assertion still is valid or not.

        :param name_id: The subject identifier, a NameID instance
        :return: A possibly empty list of entity identifiers
        """
        cni = code(name_id)
        return list(self._db[cni].keys())
Ejemplo n.º 15
0
 def set(self, name_id, entity_id, info, *args, **kwargs):
     try:
         name_id = info['name_id']
     except KeyError:
         pass
     else:
         info = dict(info)
         info['name_id'] = code(name_id)
     return super(IdentityCache, self).set(name_id, entity_id, info, *args,
                                           **kwargs)
Ejemplo n.º 16
0
    def _construct_identity(self, session_info):
        cni = code(session_info["name_id"])
        identity = {
            "login": cni,
            "password": "",
            'repoze.who.userid': cni,
            "user": session_info["ava"],
        }
        logger.debug("Identity: %s" % identity)

        return identity
Ejemplo n.º 17
0
    def _construct_identity(self, session_info):
        cni = code(session_info["name_id"])
        identity = {
            "login": cni,
            "password": "",
            'repoze.who.userid': cni,
            "user": session_info["ava"],
        }
        logger.debug("Identity: %s" % identity)

        return identity
Ejemplo n.º 18
0
def _set_name_id(session, name_id):
    """
    Store SAML2 name id info.

    :param session: The current session object
    :param name_id: saml2.saml.NameID object
    :return: None

    :type name_id: saml2.saml.NameID
    """
    session['_saml2_session_name_id'] = code(name_id)
Ejemplo n.º 19
0
    def store_assertion(self, assertion, to_sign):
        name_id = assertion.subject.name_id
        nkey = sha1(code(name_id)).hexdigest()

        doc = {
            "name_id_key": nkey,
            "assertion_id": assertion.id,
            "assertion": to_dict(assertion, ONTS.values(), True),
            "to_sign": to_sign
        }

        _ = self.assertion.insert(doc)
Ejemplo n.º 20
0
def _reauthn(reason, session_info, user):

    current_app.logger.info("Reauthenticating user {!r} for {!r}.".format(
        user, reason))
    session['_saml2_session_name_id'] = code(session_info['name_id'])
    session[reason] = int(time())
    session.persist()

    # redirect the user to the view where he came from
    relay_state = request.form.get('RelayState', '/')
    current_app.logger.debug('Redirecting to the RelayState: ' + relay_state)
    return redirect(location=relay_state)
Ejemplo n.º 21
0
    def delete(self, name_id):
        """

        :param name_id: The subject identifier, a NameID instance
        """
        del self._db[code(name_id)]

        if self._sync:
            try:
                self._db.sync()
            except AttributeError:
                pass
Ejemplo n.º 22
0
    def add_information_about_person(self, session_info):
        """If there already are information from this source in the cache
        this function will overwrite that information"""

        name_id = session_info["name_id"]
        # make friendly to (JSON) serialization
        session_info['name_id'] = code(name_id)
        issuer = session_info["issuer"]
        del session_info["issuer"]
        self.cache.set(name_id, issuer, session_info,
                       session_info["not_on_or_after"])
        return name_id
Ejemplo n.º 23
0
    def delete(self, name_id):
        """

        :param name_id: The subject identifier, a NameID instance
        """
        del self._db[code(name_id)]

        if self._sync:
            try:
                self._db.sync()
            except AttributeError:
                pass
Ejemplo n.º 24
0
    def store_assertion(self, assertion, to_sign):
        name_id = assertion.subject.name_id
        nkey = sha1(code(name_id)).hexdigest()

        doc = {
            "name_id_key": nkey,
            "assertion_id": assertion.id,
            "assertion": to_dict(assertion, ONTS.values(), True),
            "to_sign": to_sign
        }

        _ = self.assertion.insert(doc)
Ejemplo n.º 25
0
    def store_authn_statement(self, authn_statement, name_id):
        """

        :param authn_statement:
        :param name_id:
        :return:
        """
        logger.debug("store authn about: %s" % name_id)
        nkey = sha1(code(name_id)).hexdigest()
        logger.debug("Store authn_statement under key: %s" % nkey)
        try:
            self.authn[nkey].append(authn_statement)
        except KeyError:
            self.authn[nkey] = [authn_statement]

        return nkey
Ejemplo n.º 26
0
def login_action(session_info, user):

    logger.info("User {!r} logging in (eduPersonPrincipalName: {!r})".format(
        user.user_id,
        user.eppn))
    session['_saml2_session_name_id'] = code(session_info['name_id'])
    session['eduPersonPrincipalName'] = user.eppn
    loa = get_loa(current_app.config.get('AVAILABLE_LOA'), session_info)
    session['eduPersonAssurance'] = loa
    session.persist()

    # redirect the user to the view where he came from
    relay_state = request.form.get('RelayState', '/')
    logger.debug('Redirecting to the RelayState: ' + relay_state)
    response = redirect(location=relay_state)
    session.set_cookie(response)
    return response
Ejemplo n.º 27
0
    def get(self, name_id, entity_id, check_not_on_or_after=True):
        """ Get session information about a subject gotten from a
        specified IdP/AA.

        :param name_id: The subject identifier, a NameID instance
        :param entity_id: The identifier of the entity_id
        :param check_not_on_or_after: if True it will check if this
             subject is still valid or if it is too old. Otherwise it
             will not check this. True by default.
        :return: The session information
        """
        cni = code(name_id)
        (timestamp, info) = self._db[cni][entity_id]
        if check_not_on_or_after and time_util.after(timestamp):
            raise ToOld("past %s" % timestamp)

        return info or None
Ejemplo n.º 28
0
    def store_authn_statement(self, authn_statement, name_id):
        """

        :param authn_statement:
        :param name_id:
        :return:
        """
        logger.debug("store authn about: %s" % name_id)
        nkey = sha1(code(name_id)).hexdigest()
        logger.debug("Store authn_statement under key: %s" % nkey)
        _as = to_dict(authn_statement, ONTS.values(), True)
        try:
            self.authn[nkey].append(_as)
        except KeyError:
            self.authn[nkey] = [_as]

        return nkey
Ejemplo n.º 29
0
    def get(self, name_id, entity_id, check_not_on_or_after=True):
        """ Get session information about a subject gotten from a
        specified IdP/AA.

        :param name_id: The subject identifier, a NameID instance
        :param entity_id: The identifier of the entity_id
        :param check_not_on_or_after: if True it will check if this
             subject is still valid or if it is too old. Otherwise it
             will not check this. True by default.
        :return: The session information
        """
        cni = code(name_id)
        (timestamp, info) = self._db[cni][entity_id]
        if check_not_on_or_after and time_util.after(timestamp):
            raise ToOld("past %s" % timestamp)

        return info or None
Ejemplo n.º 30
0
    def active(self, name_id, entity_id):
        """ Returns the status of assertions from a specific entity_id.

        :param name_id: The ID of the subject
        :param entity_id: The entity ID of the entity_id of the assertion
        :return: True or False depending on if the assertion is still
            valid or not.
        """
        try:
            cni = code(name_id)
            (timestamp, info) = self._db[cni][entity_id]
        except KeyError:
            return False

        if not info:
            return False
        else:
            return time_util.not_on_or_after(timestamp)
Ejemplo n.º 31
0
    def active(self, name_id, entity_id):
        """ Returns the status of assertions from a specific entity_id.

        :param name_id: The ID of the subject
        :param entity_id: The entity ID of the entity_id of the assertion
        :return: True or False depending on if the assertion is still
            valid or not.
        """
        try:
            cni = code(name_id)
            (timestamp, info) = self._db[cni][entity_id]
        except KeyError:
            return False

        if not info:
            return False
        else:
            return time_util.not_on_or_after(timestamp)
Ejemplo n.º 32
0
    def test_add_person(self):
        session_info = {
            "name_id": nid,
            "issuer": IDP_ONE,
            "not_on_or_after": in_a_while(minutes=15),
            "ava": {
                "givenName": "Anders",
                "surName": "Andersson",
                "mail": "*****@*****.**"
            }
        }
        self.population.add_information_about_person(session_info)

        issuers = self.population.issuers_of_info(nid)
        assert list(issuers) == [IDP_ONE]
        subjects = [code(c) for c in self.population.subjects()]
        assert subjects == [cnid]
        # Are any of the sources gone stale
        stales = self.population.stale_sources_for_person(nid)
        assert stales == []
        # are any of the possible sources not used or gone stale
        possible = [IDP_ONE, IDP_OTHER]
        stales = self.population.stale_sources_for_person(nid, possible)
        assert stales == [IDP_OTHER]

        (identity, stale) = self.population.get_identity(nid)
        assert stale == []
        assert identity == {
            'mail': '*****@*****.**',
            'givenName': 'Anders',
            'surName': 'Andersson'
        }

        info = self.population.get_info_from(nid, IDP_ONE)
        assert sorted(list(info.keys())) == sorted(
            ["not_on_or_after", "name_id", "ava"])
        assert info["name_id"] == nid
        assert info["ava"] == {
            'mail': '*****@*****.**',
            'givenName': 'Anders',
            'surName': 'Andersson'
        }
Ejemplo n.º 33
0
    def set(self, name_id, entity_id, info, not_on_or_after=0):
        """ Stores session information in the cache. Assumes that the name_id
        is unique within the context of the Service Provider.

        :param name_id: The subject identifier, a NameID instance
        :param entity_id: The identifier of the entity_id/receiver of an
            assertion
        :param info: The session info, the assertion is part of this
        :param not_on_or_after: A time after which the assertion is not valid.
        """
        cni = code(name_id)
        if cni not in self._db:
            self._db[cni] = {}

        self._db[cni][entity_id] = (not_on_or_after, info)
        if self._sync:
            try:
                self._db.sync()
            except AttributeError:
                pass
Ejemplo n.º 34
0
    def set(self, name_id, entity_id, info, not_on_or_after=0):
        """ Stores session information in the cache. Assumes that the name_id
        is unique within the context of the Service Provider.

        :param name_id: The subject identifier, a NameID instance
        :param entity_id: The identifier of the entity_id/receiver of an
            assertion
        :param info: The session info, the assertion is part of this
        :param not_on_or_after: A time after which the assertion is not valid.
        """
        cni = code(name_id)
        if cni not in self._db:
            self._db[cni] = {}

        self._db[cni][entity_id] = (not_on_or_after, info)
        if self._sync:
            try:
                self._db.sync()
            except AttributeError:
                pass
Ejemplo n.º 35
0
def update_user_session(session_info, user):
    """
    Store login info in the session

    :param session_info: the SAML session info
    :param user: the authenticated user

    :type session_info: dict
    :type user: eduid_userdb.User

    :return: None
    :rtype: None
    """
    session['_saml2_session_name_id'] = code(session_info['name_id'])
    session['eduPersonPrincipalName'] = user.eppn
    session['user_eppn'] = user.eppn  # TODO: Remove when we have deployed and IdP that sets user_eppn
    session['user_is_logged_in'] = True
    loa = get_loa(current_app.config.get('AVAILABLE_LOA'), session_info)
    session['eduPersonAssurance'] = loa
    session['eduidIdPCredentialsUsed'] = get_saml_attribute(session_info, 'eduidIdPCredentialsUsed')
Ejemplo n.º 36
0
    def test_add_another_person(self):
        session_info = {
            "name_id": nida,
            "issuer": IDP_ONE,
            "not_on_or_after": in_a_while(minutes=15),
            "ava": {
                "givenName": "Bertil",
                "surName": "Bertilsson",
                "mail": "*****@*****.**"
            }
        }
        self.population.add_information_about_person(session_info)

        issuers = self.population.issuers_of_info(nida)
        assert issuers == [IDP_ONE]
        subjects = [code(c) for c in self.population.subjects()]
        assert _eq(subjects, [cnid, cnida])

        stales = self.population.stale_sources_for_person(nida)
        assert stales == []
        # are any of the possible sources not used or gone stale
        possible = [IDP_ONE, IDP_OTHER]
        stales = self.population.stale_sources_for_person(nida, possible)
        assert stales == [IDP_OTHER]

        (identity, stale) = self.population.get_identity(nida)
        assert stale == []
        assert identity == {
            "givenName": "Bertil",
            "surName": "Bertilsson",
            "mail": "*****@*****.**"
        }

        info = self.population.get_info_from(nida, IDP_ONE)
        assert info.keys() == ["not_on_or_after", "name_id", "ava"]
        assert info["name_id"] == nida
        assert info["ava"] == {
            "givenName": "Bertil",
            "surName": "Bertilsson",
            "mail": "*****@*****.**"
        }
Ejemplo n.º 37
0
    def test_add_another_person(self):
        session_info = {
            "name_id": nida,
            "issuer": IDP_ONE,
            "not_on_or_after": in_a_while(minutes=15),
            "ava": {
                "givenName": "Bertil",
                "surName": "Bertilsson",
                "mail": "*****@*****.**"
            }
        }
        self.population.add_information_about_person(session_info)

        issuers = self.population.issuers_of_info(nida)
        assert list(issuers) == [IDP_ONE]
        subjects = [code(c) for c in self.population.subjects()]
        assert _eq(subjects, [cnid, cnida])
        
        stales = self.population.stale_sources_for_person(nida)
        assert stales == []
        # are any of the possible sources not used or gone stale
        possible = [IDP_ONE, IDP_OTHER]
        stales = self.population.stale_sources_for_person(nida, possible)
        assert stales == [IDP_OTHER]

        (identity, stale) = self.population.get_identity(nida)
        assert stale == []
        assert identity == {"givenName": "Bertil",
                            "surName": "Bertilsson",
                            "mail": "*****@*****.**"
                            }

        info = self.population.get_info_from(nida, IDP_ONE)
        assert sorted(list(info.keys())) == sorted(["not_on_or_after",
                                                    "name_id", "ava"])
        assert info["name_id"] == nida
        assert info["ava"] == {"givenName": "Bertil",
                                "surName": "Bertilsson",
                                "mail": "*****@*****.**"
                                }
Ejemplo n.º 38
0
    def get_identity(self, name_id, entities=None,
                     check_not_on_or_after=True):
        """ Get all the identity information that has been received and
        are still valid about the subject.

        :param name_id: The subject identifier, a NameID instance
        :param entities: The identifiers of the entities whoes assertions are
            interesting. If the list is empty all entities are interesting.
        :return: A 2-tuple consisting of the identity information (a
            dictionary of attributes and values) and the list of entities
            whoes information has timed out.
        """
        if not entities:
            try:
                cni = code(name_id)
                entities = self._db[cni].keys()
            except KeyError:
                return {}, []

        res = {}
        oldees = []
        for entity_id in entities:
            try:
                info = self.get(name_id, entity_id, check_not_on_or_after)
            except ToOld:
                oldees.append(entity_id)
                continue

            if not info:
                oldees.append(entity_id)
                continue

            for key, vals in info["ava"].items():
                try:
                    tmp = set(res[key]).union(set(vals))
                    res[key] = list(tmp)
                except KeyError:
                    res[key] = vals
        return res, oldees
Ejemplo n.º 39
0
    def get_identity(self, name_id, entities=None,
                     check_not_on_or_after=True):
        """ Get all the identity information that has been received and
        are still valid about the subject.

        :param name_id: The subject identifier, a NameID instance
        :param entities: The identifiers of the entities whoes assertions are
            interesting. If the list is empty all entities are interesting.
        :return: A 2-tuple consisting of the identity information (a
            dictionary of attributes and values) and the list of entities
            whoes information has timed out.
        """
        if not entities:
            try:
                cni = code(name_id)
                entities = self._db[cni].keys()
            except KeyError:
                return {}, []

        res = {}
        oldees = []
        for entity_id in entities:
            try:
                info = self.get(name_id, entity_id, check_not_on_or_after)
            except ToOld:
                oldees.append(entity_id)
                continue

            if not info:
                oldees.append(entity_id)
                continue

            for key, vals in info["ava"].items():
                try:
                    tmp = set(res[key]).union(set(vals))
                    res[key] = list(tmp)
                except KeyError:
                    res[key] = vals
        return res, oldees
Ejemplo n.º 40
0
def update_user_session(session_info, user):
    """
    Store login info in the session

    :param session_info: the SAML session info
    :param user: the authenticated user

    :type session_info: dict
    :type user: eduid_userdb.User

    :return: None
    :rtype: None
    """
    session['_saml2_session_name_id'] = code(session_info['name_id'])
    session['eduPersonPrincipalName'] = user.eppn
    session[
        'user_eppn'] = user.eppn  # TODO: Remove when we have deployed and IdP that sets user_eppn
    session['user_is_logged_in'] = True
    loa = get_loa(current_app.config.available_loa, session_info)
    session['eduPersonAssurance'] = loa
    session['eduidIdPCredentialsUsed'] = get_saml_attribute(
        session_info, 'eduidIdPCredentialsUsed')
Ejemplo n.º 41
0
    def test_modify_person(self):
        session_info = {
            "name_id": nid,
            "issuer": IDP_ONE,
            "not_on_or_after": in_a_while(minutes=15),
            "ava": {
                "givenName": "Arne",
                "surName": "Andersson",
                "mail": "*****@*****.**"
            }
        }
        self.population.add_information_about_person(session_info)

        issuers = self.population.issuers_of_info(nid)
        assert _eq(issuers, [IDP_ONE, IDP_OTHER])
        subjects = [code(c) for c in self.population.subjects()]
        assert _eq(subjects, [cnid, cnida])
        # Are any of the sources gone stale
        stales = self.population.stale_sources_for_person(nid)
        assert stales == []
        # are any of the possible sources not used or gone stale
        possible = [IDP_ONE, IDP_OTHER]
        stales = self.population.stale_sources_for_person(nid, possible)
        assert stales == []

        (identity, stale) = self.population.get_identity(nid)
        assert stale == []
        assert identity == {
            'mail': '*****@*****.**',
            'givenName': 'Arne',
            'surName': 'Andersson',
            "eduPersonEntitlement": "Anka"
        }

        info = self.population.get_info_from(nid, IDP_OTHER)
        assert info.keys() == ["not_on_or_after", "name_id", "ava"]
        assert info["name_id"] == nid
        assert info["ava"] == {"eduPersonEntitlement": "Anka"}
Ejemplo n.º 42
0
    def get(self, name_id, entity_id, check_not_on_or_after=True):
        """ Get session information about a subject gotten from a
        specified IdP/AA.

        :param name_id: The subject identifier, a NameID instance
        :param entity_id: The identifier of the entity_id
        :param check_not_on_or_after: if True it will check if this
             subject is still valid or if it is too old. Otherwise it
             will not check this. True by default.
        :return: The session information
        """
        cni = code(name_id)
        try:
            (timestamp, info) = self._db[cni][entity_id]
        except KeyError:
            return None
        info = info.copy()
        if check_not_on_or_after and time_util.after(timestamp):
            raise TooOld("past %s" % str(timestamp))

        if 'name_id' in info and isinstance(info['name_id'], six.string_types):
            info['name_id'] = decode(info['name_id'])
        return info or None
Ejemplo n.º 43
0
    def do_logout(
        self,
        name_id,
        entity_ids,
        reason,
        expire,
        sign=None,
        expected_binding=None,
        sign_alg=None,
        digest_alg=None,
        **kwargs,
    ):
        """

        :param name_id: Identifier of the Subject (a NameID instance)
        :param entity_ids: List of entity ids for the IdPs that have provided
            information concerning the subject
        :param reason: The reason for doing the logout
        :param expire: Try to logout before this time.
        :param sign: Whether to sign the request or not
        :param expected_binding: Specify the expected binding then not try it
            all
        :param kwargs: Extra key word arguments.
        :return:
        """
        # check time
        if not not_on_or_after(expire):  # I've run out of time
            # Do the local logout anyway
            self.local_logout(name_id)
            return 0, "504 Gateway Timeout", [], []

        not_done = entity_ids[:]
        responses = {}

        bindings_slo_preferred = self.config.preferred_binding[
            "single_logout_service"]

        for entity_id in entity_ids:
            logger.debug("Logout from '%s'", entity_id)

            bindings_slo_supported = self.metadata.single_logout_service(
                entity_id=entity_id, typ="idpsso")
            bindings_slo_preferred_and_supported = (
                binding for binding in bindings_slo_preferred
                if binding in bindings_slo_supported)
            bindings_slo_choices = filter(lambda x: x, (
                expected_binding,
                *bindings_slo_preferred_and_supported,
                *bindings_slo_supported,
            ))
            binding = next(bindings_slo_choices, None)
            if not binding:
                logger.info({
                    "message": "Entity does not support SLO",
                    "entity": entity_id,
                })
                continue

            service_info = bindings_slo_supported[binding]
            service_location = next(locations(service_info), None)
            if not service_location:
                logger.info({
                    "message": "Entity SLO service does not have a location",
                    "entity": entity_id,
                    "service_location": service_location,
                })
                continue

            session_info = self.users.get_info_from(name_id, entity_id, False)
            session_index = session_info.get('session_index')
            session_indexes = [session_index] if session_index else None

            sign = sign if sign is not None else self.logout_requests_signed
            sign_post = sign and (binding == BINDING_HTTP_POST
                                  or binding == BINDING_SOAP)
            sign_redirect = sign and binding == BINDING_HTTP_REDIRECT

            log_report = {
                "message": "Invoking SLO on entity",
                "entity": entity_id,
                "binding": binding,
                "location": service_location,
                "session_indexes": session_indexes,
                "sign": sign,
            }
            logger.info(log_report)

            req_id, request = self.create_logout_request(
                service_location,
                entity_id,
                name_id=name_id,
                reason=reason,
                expire=expire,
                session_indexes=session_indexes,
                sign=sign_post,
                sign_alg=sign_alg,
                digest_alg=digest_alg,
            )
            relay_state = self._relay_state(req_id)
            http_info = self.apply_binding(
                binding,
                str(request),
                service_location,
                relay_state,
                sign=sign_redirect,
                sigalg=sign_alg,
            )

            if binding == BINDING_SOAP:
                response = self.send(**http_info)
                if response and response.status_code == 200:
                    not_done.remove(entity_id)
                    response_text = response.text
                    log_report_response = {
                        **log_report,
                        "message": "Response from SLO service",
                        "response_text": response_text,
                    }
                    logger.debug(log_report_response)
                    res = self.parse_logout_request_response(
                        response_text, binding)
                    responses[entity_id] = res
                else:
                    log_report_response = {
                        **log_report,
                        "message": "Bad status_code response from SLO service",
                        "status_code": (response and response.status_code),
                    }
                    logger.info(log_report_response)
            else:
                self.state[req_id] = {
                    "entity_id": entity_id,
                    "operation": "SLO",
                    "entity_ids": entity_ids,
                    "name_id": code(name_id),
                    "reason": reason,
                    "not_on_or_after": expire,
                    "sign": sign,
                }
                responses[entity_id] = (binding, http_info)
                not_done.remove(entity_id)

        if not_done:
            # upstream should try later
            raise LogoutError("%s" % (entity_ids, ))

        return responses
Ejemplo n.º 44
0
    def do_logout(self,
                  name_id,
                  entity_ids,
                  reason,
                  expire,
                  sign=None,
                  expected_binding=None):
        """

        :param name_id: Identifier of the Subject (a NameID instance)
        :param entity_ids: List of entity ids for the IdPs that have provided
            information concerning the subject
        :param reason: The reason for doing the logout
        :param expire: Try to logout before this time.
        :param sign: Whether to sign the request or not
        :param expected_binding: Specify the expected binding then not try it
            all
        :return:
        """
        # check time
        if not not_on_or_after(expire):  # I've run out of time
            # Do the local logout anyway
            self.local_logout(name_id)
            return 0, "504 Gateway Timeout", [], []

        not_done = entity_ids[:]
        responses = {}

        for entity_id in entity_ids:
            logger.debug("Logout from '%s'" % entity_id)
            # for all where I can use the SOAP binding, do those first
            for binding in [
                    BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
            ]:
                if expected_binding and binding != expected_binding:
                    continue
                try:
                    srvs = self.metadata.single_logout_service(
                        entity_id, binding, "idpsso")
                except:
                    srvs = None

                if not srvs:
                    logger.debug("No SLO '%s' service" % binding)
                    continue

                destination = destinations(srvs)[0]
                logger.info("destination to provider: %s" % destination)
                req_id, request = self.create_logout_request(destination,
                                                             entity_id,
                                                             name_id=name_id,
                                                             reason=reason,
                                                             expire=expire)

                #to_sign = []
                if binding.startswith("http://"):
                    sign = True

                if sign is None:
                    sign = self.logout_requests_signed

                if sign:
                    srequest = self.sign(request)
                else:
                    srequest = "%s" % request

                relay_state = self._relay_state(req_id)

                http_info = self.apply_binding(binding, srequest, destination,
                                               relay_state)

                if binding == BINDING_SOAP:
                    response = self.send(**http_info)

                    if response and response.status_code == 200:
                        not_done.remove(entity_id)
                        response = response.text
                        logger.info("Response: %s" % response)
                        res = self.parse_logout_request_response(response)
                        responses[entity_id] = res
                    else:
                        logger.info("NOT OK response from %s" % destination)

                else:
                    self.state[req_id] = {
                        "entity_id": entity_id,
                        "operation": "SLO",
                        "entity_ids": entity_ids,
                        "name_id": code(name_id),
                        "reason": reason,
                        "not_on_of_after": expire,
                        "sign": sign
                    }

                    responses[entity_id] = (binding, http_info)
                    not_done.remove(entity_id)

                # only try one binding
                break

        if not_done:
            # upstream should try later
            raise LogoutError("%s" % (entity_ids, ))

        return responses
Ejemplo n.º 45
0
from saml2.saml import NAMEID_FORMAT_TRANSIENT, NameID

from saml2.population import Population
from saml2.time_util import in_a_while

IDP_ONE = "urn:mace:example.com:saml:one:idp"
IDP_OTHER = "urn:mace:example.com:saml:other:idp"

nid = NameID(name_qualifier="foo",
             format=NAMEID_FORMAT_TRANSIENT,
             text="123456")
nida = NameID(name_qualifier="foo",
              format=NAMEID_FORMAT_TRANSIENT,
              text="abcdef")

cnid = code(nid)
cnida = code(nida)


def _eq(l1, l2):
    return set(l1) == set(l2)


class TestPopulationMemoryBased():
    def setup_class(self):
        self.population = Population()

    def test_add_person(self):
        session_info = {
            "name_id": nid,
            "issuer": IDP_ONE,
Ejemplo n.º 46
0
    def construct_message(self):
        _cli = self.conv.entity
        _entity_id = self.req_args['entity_id']
        _name_id = self.req_args['name_id']

        sls_args = {
            'entity_id': _entity_id, 'binding': self.binding, 'typ': 'idpsso'}

        try:
            srvs = _cli.metadata.single_logout_service(**sls_args)
        except:
            msg = "No SLO '{}' service".format(self.binding)
            raise UnknownBinding(msg)

        destination = destinations(srvs)[0]
        logger.info("destination to provider: %s", destination)
        self.conv.destination = destination

        try:
            session_info = _cli.users.get_info_from(_name_id, _entity_id, False)
            session_indexes = [session_info['session_index']]
        except KeyError:
            session_indexes = None

        try:
            expire = self.req_args['expire']
        except KeyError:
            expire = in_a_while(minutes=5)

        req_id, request = _cli.create_logout_request(
            destination, _entity_id, name_id=_name_id,
            reason=self.req_args['reason'],
            expire=expire, session_indexes=session_indexes)

        self.conv.events.store(EV_REQUEST_ARGS, self.req_args,
                               sender=self.__class__, sub='construct_message')
        self.conv.events.store(EV_PROTOCOL_REQUEST, request,
                               sender=self.__class__, sub='construct_message')

        # to_sign = []
        if self.binding.startswith("http://"):
            sign = True
        else:
            try:
                sign = self.req_args['sign']
            except KeyError:
                sign = _cli.logout_requests_signed

        sigalg = None
        key = None
        if sign:
            if self.binding == BINDING_HTTP_REDIRECT:
                try:
                    sigalg = self.req_args["sigalg"]
                except KeyError:
                    sigalg = ds.sig_default
                try:
                    key = self.req_args["key"]
                except KeyError:
                    key = _cli.signkey

                srequest = str(request)
            else:
                srequest = _cli.sign(request)
        else:
            srequest = str(request)

        relay_state = _cli._relay_state(req_id)

        http_info = _cli.apply_binding(self.binding, srequest, destination,
                                       relay_state, sigalg=sigalg,
                                       key=key)

        if self.binding != BINDING_SOAP:
            _cli.state[req_id] = {
                "entity_id": _entity_id, "operation": "SLO",
                "name_id": code(_name_id), "reason": self.req_args['reason'],
                "not_on_of_after": expire, "sign": sign}

        self.conv.events.store(EV_HTTP_ARGS, http_info, sender=self.__class__,
                               sub='construct_message')

        return http_info, req_id
Ejemplo n.º 47
0
 def remove_authn_statements(self, name_id):
     logger.debug("remove authn about: %s" % name_id)
     key = sha1(code(name_id)).hexdigest()
     for item in self.assertion.find({"name_id_key": key}):
         self.assertion.remove(item["_id"])
Ejemplo n.º 48
0
    def remove_authn_statements(self, name_id):
        logger.debug("remove authn about: %s" % name_id)
        nkey = sha1(code(name_id)).hexdigest()

        del self.authn[nkey]
Ejemplo n.º 49
0
def _set_subject_id(session, subject_id):
    session['_saml2_subject_id'] = code(subject_id)
Ejemplo n.º 50
0
def nid_eq(l1, l2):
    return _eq([code(c) for c in l1], [code(c) for c in l2])
Ejemplo n.º 51
0
from saml2.ident import code
from saml2.saml import NAMEID_FORMAT_TRANSIENT, NameID

from saml2.population import Population
from saml2.time_util import in_a_while

IDP_ONE = "urn:mace:example.com:saml:one:idp"
IDP_OTHER = "urn:mace:example.com:saml:other:idp"

nid = NameID(name_qualifier="foo", format=NAMEID_FORMAT_TRANSIENT, 
             text="123456")

nida = NameID(name_qualifier="foo", format=NAMEID_FORMAT_TRANSIENT,
              text="abcdef")

cnid = code(nid)
cnida = code(nida)

def _eq(l1, l2):
    return set(l1) == set(l2)

class TestPopulationMemoryBased():
    def setup_class(self):
        self.population = Population()
        
    def test_add_person(self):
        session_info = {
            "name_id": nid,
            "issuer": IDP_ONE,
            "not_on_or_after": in_a_while(minutes=15),
            "ava": {
Ejemplo n.º 52
0
    def do_logout(self, name_id, entity_ids, reason, expire, sign=None,
                  expected_binding=None, **kwargs):
        """

        :param name_id: Identifier of the Subject (a NameID instance)
        :param entity_ids: List of entity ids for the IdPs that have provided
            information concerning the subject
        :param reason: The reason for doing the logout
        :param expire: Try to logout before this time.
        :param sign: Whether to sign the request or not
        :param expected_binding: Specify the expected binding then not try it
            all
        :param kwargs: Extra key word arguments.
        :return:
        """
        # check time
        if not not_on_or_after(expire):  # I've run out of time
            # Do the local logout anyway
            self.local_logout(name_id)
            return 0, "504 Gateway Timeout", [], []

        not_done = entity_ids[:]
        responses = {}

        for entity_id in entity_ids:
            logger.debug("Logout from '%s'", entity_id)
            # for all where I can use the SOAP binding, do those first
            for binding in [BINDING_SOAP, BINDING_HTTP_POST,
                            BINDING_HTTP_REDIRECT]:
                if expected_binding and binding != expected_binding:
                    continue
                try:
                    srvs = self.metadata.single_logout_service(entity_id,
                                                               binding,
                                                               "idpsso")
                except:
                    srvs = None

                if not srvs:
                    logger.debug("No SLO '%s' service", binding)
                    continue

                destination = destinations(srvs)[0]
                logger.info("destination to provider: %s", destination)
                try:
                    session_info = self.users.get_info_from(name_id,
                                                            entity_id,
                                                            False)
                    session_indexes = [session_info['session_index']]
                except KeyError:
                    session_indexes = None
                req_id, request = self.create_logout_request(
                    destination, entity_id, name_id=name_id, reason=reason,
                    expire=expire, session_indexes=session_indexes)

                # to_sign = []
                if binding.startswith("http://"):
                    sign = True

                if sign is None:
                    sign = self.logout_requests_signed

                sigalg = None
                key = None
                if sign:
                    if binding == BINDING_HTTP_REDIRECT:
                        sigalg = kwargs.get("sigalg", ds.sig_default)
                        key = kwargs.get("key", self.signkey)
                        srequest = str(request)
                    else:
                        srequest = self.sign(request)
                else:
                    srequest = str(request)

                relay_state = self._relay_state(req_id)

                http_info = self.apply_binding(binding, srequest, destination,
                                               relay_state, sigalg=sigalg,
                                               key=key)

                if binding == BINDING_SOAP:
                    response = self.send(**http_info)

                    if response and response.status_code == 200:
                        not_done.remove(entity_id)
                        response = response.text
                        logger.info("Response: %s", response)
                        res = self.parse_logout_request_response(response,
                                                                 binding)
                        responses[entity_id] = res
                    else:
                        logger.info("NOT OK response from %s", destination)

                else:
                    self.state[req_id] = {"entity_id": entity_id,
                                          "operation": "SLO",
                                          "entity_ids": entity_ids,
                                          "name_id": code(name_id),
                                          "reason": reason,
                                          "not_on_of_after": expire,
                                          "sign": sign}

                    responses[entity_id] = (binding, http_info)
                    not_done.remove(entity_id)

                # only try one binding
                break

        if not_done:
            # upstream should try later
            raise LogoutError("%s" % (entity_ids,))

        return responses
Ejemplo n.º 53
0
    def do_logout(
        self,
        name_id,
        entity_ids,
        reason,
        expire,
        sign=None,
        expected_binding=None,
        sign_alg=None,
        digest_alg=None,
        **kwargs,
    ):
        """

        :param name_id: Identifier of the Subject (a NameID instance)
        :param entity_ids: List of entity ids for the IdPs that have provided
            information concerning the subject
        :param reason: The reason for doing the logout
        :param expire: Try to logout before this time.
        :param sign: Whether to sign the request or not
        :param expected_binding: Specify the expected binding then not try it
            all
        :param kwargs: Extra key word arguments.
        :return:
        """
        # check time
        if not not_on_or_after(expire):  # I've run out of time
            # Do the local logout anyway
            self.local_logout(name_id)
            return 0, "504 Gateway Timeout", [], []

        not_done = entity_ids[:]
        responses = {}

        for entity_id in entity_ids:
            logger.debug("Logout from '%s'", entity_id)
            # for all where I can use the SOAP binding, do those first
            for binding in [BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]:
                if expected_binding and binding != expected_binding:
                    continue

                try:
                    srvs = self.metadata.single_logout_service(
                        entity_id, binding, "idpsso"
                    )
                except:
                    srvs = None

                if not srvs:
                    logger.debug("No SLO '%s' service", binding)
                    continue

                destination = next(locations(srvs), None)
                logger.info("destination to provider: %s", destination)

                try:
                    session_info = self.users.get_info_from(
                        name_id, entity_id, False
                    )
                    session_indexes = [session_info['session_index']]
                except KeyError:
                    session_indexes = None

                sign_post = False if binding == BINDING_HTTP_REDIRECT else sign
                sign_redirect = False if binding == BINDING_HTTP_POST and sign else sign

                req_id, request = self.create_logout_request(
                    destination,
                    entity_id,
                    name_id=name_id,
                    reason=reason,
                    expire=expire,
                    session_indexes=session_indexes,
                    sign=sign_post,
                    sign_alg=sign_alg,
                    digest_alg=digest_alg,
                )

                relay_state = self._relay_state(req_id)
                http_info = self.apply_binding(
                    binding,
                    str(request),
                    destination,
                    relay_state,
                    sign=sign_redirect,
                    sigalg=sign_alg,
                )

                if binding == BINDING_SOAP:
                    response = self.send(**http_info)
                    if response and response.status_code == 200:
                        not_done.remove(entity_id)
                        response = response.text
                        logger.info("Response: %s", response)
                        res = self.parse_logout_request_response(response, binding)
                        responses[entity_id] = res
                    else:
                        logger.info("NOT OK response from %s", destination)
                else:
                    self.state[req_id] = {
                        "entity_id": entity_id,
                        "operation": "SLO",
                        "entity_ids": entity_ids,
                        "name_id": code(name_id),
                        "reason": reason,
                        "not_on_or_after": expire,
                        "sign": sign,
                    }
                    responses[entity_id] = (binding, http_info)
                    not_done.remove(entity_id)

                # only try one binding
                break

        if not_done:
            # upstream should try later
            raise LogoutError("%s" % (entity_ids,))

        return responses
Ejemplo n.º 54
0
def _set_subject_id(session, subject_id):
    session["_saml2_subject_id"] = code(subject_id)
Ejemplo n.º 55
0
def nid_eq(l1, l2):
    return _eq([code(c) for c in l1], [code(c) for c in l2])
Ejemplo n.º 56
0
 def remove_authn_statements(self, name_id):
     logger.debug("remove authn about: %s" % name_id)
     key = sha1(code(name_id)).hexdigest()
     for item in self.assertion.find({"name_id_key": key}):
         self.assertion.remove(item["_id"])
Ejemplo n.º 57
0
def _set_subject_id(session, subject_id):
    session['_saml2_subject_id'] = code(subject_id)
Ejemplo n.º 58
0
    def remove_authn_statements(self, name_id):
        logger.debug("remove authn about: %s" % name_id)
        nkey = sha1(code(name_id)).hexdigest()

        del self.authn[nkey]