Example #1
0
    def lookup_challenges(context, serial=None, transid=None):
        """
        database lookup to find all challenges belonging to a token and or
        if exist with a transaction state

        :param context:
        :param serial:   serial of the token
        :param transid:  transaction id, if None, all will be retrieved
        :return:         return a list of challenge dict
        """
        log.debug("serial %r: transactionid %r", serial, transid)

        if transid is None and serial is None:
            log.debug("Called without serial or transid! Returning all challenges")

        conditions = ()

        if transid:
            transid_len = int(context.get("Config").get("TransactionIdLength", 12) or 12)

            if len(transid) == transid_len:
                conditions += (and_(Challenge.transid == transid),)
            else:
                conditions += (and_(Challenge.transid.startswith(transid)),)

        if serial:
            conditions += (and_(Challenge.tokenserial == serial),)

        # SQLAlchemy requires the conditions in one arg as tupple
        condition = and_(*conditions)
        challenges = Session.query(Challenge).filter(condition).order_by(desc(Challenge.id)).all()

        log.debug("%r", challenges)

        return challenges
Example #2
0
    def delete_challenges(serial, challenges):
        """
        delete some challenges of a token

        :param serial: the serial number of the token
        :param challenges: list of (dict|int|str|challenge objects)
        :return: result of the delete operation
        """

        challenge_ids = []
        for challenge in challenges:
            if type(challenge) == dict:
                if 'id' in challenge:
                    challenge_id = challenge.get('id')
            elif type(challenge) == Challenge:
                challenge_id = challenge.get('id')
            elif type(challenge) in (unicode, str, int):
                challenge_id = challenge

            try:
                challenge_ids.append(int(challenge_id))
            except ValueError:
                # ignore
                log.warning("failed to convert the challengeId %r to int()" %
                            challenge_id)

        res = 1
        # gather all challenges with one sql 'in' statement
        if len(challenge_ids) > 0:
            conditions = ()
            if serial:
                conditions += (and_(Challenge.tokenserial == serial),)

            conditions += (and_(Challenge.id.in_(challenge_ids)),)

            # SQLAlchemy requires the conditions in one arg as tupple
            condition = and_(*conditions)
            del_challes = Session.query(Challenge).filter(condition).all()

            # and delete them via session
            for dell in del_challes:
                Session.delete(dell)

        return res
Example #3
0
    def delete_challenges(serial, challenges):
        """
        delete some challenges of a token

        :param serial: the serial number of the token
        :param challenges: list of (dict|int|str|challenge objects)
        :return: result of the delete operation
        """

        challenge_ids = []
        for challenge in challenges:
            if type(challenge) == dict:
                if 'id' in challenge:
                    challenge_id = challenge.get('id')
            elif type(challenge) == Challenge:
                challenge_id = challenge.get('id')
            elif type(challenge) in (unicode, str, int):
                challenge_id = challenge

            try:
                challenge_ids.append(int(challenge_id))
            except ValueError:
                # ignore
                log.warning("failed to convert the challenge id %r to integer",
                            challenge_id)

        res = 1
        # gather all challenges with one sql 'in' statement
        if len(challenge_ids) > 0:
            conditions = ()
            if serial:
                conditions += (and_(Challenge.tokenserial == serial),)

            conditions += (and_(Challenge.id.in_(challenge_ids)),)

            # SQLAlchemy requires the conditions in one arg as tupple
            condition = and_(*conditions)
            del_challes = Session.query(Challenge).filter(condition).all()

            # and delete them via session
            for dell in del_challes:
                Session.delete(dell)

        return res
Example #4
0
    def delete_challenges(serial, challenges):
        """
        delete some challenges of a token

        :param serial: the serial number of the token
        :param challenges: list of (dict|int|str|challenge objects)
        :return: result of the delete operation
        """

        challenge_ids = []
        for challenge in challenges:
            if type(challenge) == dict:
                if "id" in challenge:
                    challenge_id = challenge.get("id")
            elif type(challenge) == Challenge:
                challenge_id = challenge.get("id")
            elif type(challenge) in (unicode, str, int):
                challenge_id = challenge

            try:
                challenge_ids.append(int(challenge_id))
            except ValueError:
                # ignore
                log.warning("failed to convert the challengeId %r to int()" % challenge_id)

        res = 1
        # gather all challenges with one sql 'in' statement
        if len(challenge_ids) > 0:
            del_challes = (
                Session.query(Challenge)
                .filter(Challenge.tokenserial == serial)
                .filter(Challenge.id.in_(challenge_ids))
                .all()
            )

            # and delete them via session
            for dell in del_challes:
                Session.delete(dell)

        return res
Example #5
0
    def lookup_challenges(serial=None, transid=None, filter_open=False,
                          read_for_update=False):
        """
        database lookup to find all challenges belonging to a token and or
        if exist with a transaction state

        :param serial:   serial of the token
        :param transid:  transaction id, if None, all will be retrieved
        :param filter_open: check only for those challenges, which have not
                            been verified before
        :param read_for_update: if True the Select challenges command will
                                set a lock for getting the challenges. So
                                a second request will wait until the first
                                is complete.
        :return:         return a list of challenge dict
        """
        log.debug('lookup_challenges: serial %r: transactionid %r',
                  serial, transid)

        if transid is None and serial is None:
            log.debug('lookup_challenges was called without serial or '
                      'transid! Returning all challenges')

        conditions = ()

        if transid:
            transid_len = Challenges.get_tranactionid_length()

            if len(transid) == transid_len:
                conditions += (and_(Challenge.transid == transid),)
            else:
                conditions += (and_(Challenge.transid.startswith(transid)),)

        if serial:
            conditions += (and_(Challenge.tokenserial == serial),)

        if filter_open is True:
            conditions += (and_(Challenge.session.like('%"status": "open"%')),)

        # SQLAlchemy requires the conditions in one arg as tuple
        condition = and_(*conditions)
        challenges_query = Session.query(Challenge).\
            filter(condition).order_by(desc(Challenge.id))

        if read_for_update:
            challenges_query = challenges_query.with_lockmode('update')

        challenges = challenges_query.all()
        log.debug('lookup_challenges: founnd challenges: %r', challenges)

        return challenges
Example #6
0
    def lookup_challenges(serial=None, transid=None, filter_open=False):
        """
        database lookup to find all challenges belonging to a token and or
        if exist with a transaction state

        :param serial:   serial of the token
        :param transid:  transaction id, if None, all will be retrieved
        :param filter_open: check only for those challenges, which have not
                            been verified before
        :return:         return a list of challenge dict
        """
        log.debug('lookup_challenges: serial %r: transactionid %r', serial,
                  transid)

        if transid is None and serial is None:
            log.debug('lookup_challenges was called without serial or '
                      'transid! Returning all challenges')

        conditions = ()

        if transid:
            transid_len = int(
                context.get('Config').get('TransactionIdLength', 12) or 12)

            if len(transid) == transid_len:
                conditions += (and_(Challenge.transid == transid), )
            else:
                conditions += (and_(Challenge.transid.startswith(transid)), )

        if serial:
            conditions += (and_(Challenge.tokenserial == serial), )

        if filter_open is True:
            conditions += (and_(
                Challenge.session.like('%"status": "open"%')), )

        # SQLAlchemy requires the conditions in one arg as tupple
        condition = and_(*conditions)
        challenges = Session.query(Challenge).\
            filter(condition).order_by(desc(Challenge.id)).all()

        log.debug('lookup_challenges: founnd challenges: %r', challenges)

        return challenges
Example #7
0
    def lookup_challenges(serial=None, transid=None, filter_open=False):
        """
        database lookup to find all challenges belonging to a token and or
        if exist with a transaction state

        :param serial:   serial of the token
        :param transid:  transaction id, if None, all will be retrieved
        :param filter_open: check only for those challenges, which have not
                            been verified before
        :return:         return a list of challenge dict
        """
        log.debug('serial %r: transactionid %r', serial, transid)

        if transid is None and serial is None:
            log.debug(
                'Called without serial or transid! Returning all challenges')

        conditions = ()

        if transid:
            transid_len = int(
                context.get('Config').get('TransactionIdLength', 12) or 12)

            if len(transid) == transid_len:
                conditions += (and_(Challenge.transid == transid),)
            else:
                conditions += (and_(Challenge.transid.startswith(transid)),)

        if serial:
            conditions += (and_(Challenge.tokenserial == serial),)

        if filter_open is True:
            conditions += (and_(Challenge.session.like('%"status": "open"%')),)

        # SQLAlchemy requires the conditions in one arg as tupple
        condition = and_(*conditions)
        challenges = Session.query(Challenge).\
            filter(condition).order_by(desc(Challenge.id)).all()

        log.debug('%r', challenges)

        return challenges
Example #8
0
    def create_challenge(token, options=None, challenge_id=None, id_postfix=''):
        """
        dedicated method to create a challenge to support the implementation
        of challenge policies in future

        :param options: optional parameters for token specific tokens
                        eg. request a signed challenge
        :return: a tuple of  (boolean, and a dict, which contains the
                 {'challenge' : challenge} description)
        """

        # response dict, describing the challenge reply
        challenge = {}
        # the allocated db challenge object
        challenge_obj = None
        retry_counter = 0
        reason = None

        hsm = context['hsm'].get('obj')

        id_length = int(
            context.get('Config', None).get('TransactionIdLength', 12)) - \
                        len(id_postfix)

        while True:
            try:
                if not challenge_id:
                    transactionid = "%s%s" % (
                    Challenge.createTransactionId(length=id_length), id_postfix)
                else:
                    transactionid = challenge_id

                num_challenges = Session.query(Challenge). \
                    filter(Challenge.transid == transactionid).count()

                if num_challenges == 0:
                    challenge_obj = Challenge(transid=transactionid,
                                              tokenserial=token.getSerial())
                if challenge_obj is not None:
                    break

            except Exception as exce:
                log.info("Failed to create Challenge: %r", exce)
                reason = exce

            # prevent an unlimited loop
            retry_counter = retry_counter + 1
            if retry_counter > 100:
                log.info(
                    "Failed to create Challenge for %d times: %r -quiting!",
                    retry_counter, reason)
                raise Exception('Failed to create challenge %r' % reason)

        expired_challenges, valid_challenges = Challenges.get_challenges(token)

        # carefully create a new challenge
        try:

            # we got a challenge object allocated and initialize the challenge
            (res, open_transactionid, message, attributes) = \
                token.initChallenge(transactionid,
                                    challenges=valid_challenges,
                                    options=options)

            if res is False:
                # if a different transid is returned, this indicates, that there
                # is already an outstanding challenge we can refere to
                if open_transactionid != transactionid:
                    transactionid = open_transactionid

            else:
                # in case the init was successfull, we preserve no the
                # challenge data to support the implementation of a blocking
                # based on the previous stored data
                challenge_obj.setChallenge(message)
                challenge_obj.save()

                (res, message, data, attributes) = \
                    token.createChallenge(transactionid, options=options)

                if res is True:
                    # persist the final challenge data + message
                    challenge_obj.setChallenge(message)
                    challenge_obj.setData(data)
                    challenge_obj.signChallenge(hsm)
                    challenge_obj.save()
                else:
                    transactionid = ''
                    reason = Exception(message)

        except Exception as exce:
            reason = exce
            res = False

        # if something goes wrong with the challenge, remove it
        if res is False and challenge_obj is not None:
            try:
                log.debug("deleting session")
                Session.delete(challenge_obj)
                Session.commit()
            except Exception as exx:
                log.debug("deleting session failed: %r" % exx)
                try:
                    Session.expunge(challenge_obj)
                    Session.commit()
                except Exception as exx:
                    log.debug("expunge session failed: %r" % exx)

        # in case that create challenge fails, we must raise this reason
        if reason is not None:
            message = "%r" % reason
            log.error("Failed to create or init challenge %r " % reason)
            raise reason

        # prepare the response for the user
        if transactionid is not None:
            challenge['transactionid'] = transactionid

        if message is not None:
            challenge['message'] = message

        if attributes is not None and type(attributes) == dict:
            challenge.update(attributes)

        return (res, challenge)
Example #9
0
    def create_challenge(token, options=None, challenge_id=None, id_postfix=''):
        """
        dedicated method to create a challenge to support the implementation
        of challenge policies in future

        :param options: optional parameters for token specific tokens
                        eg. request a signed challenge
        :return: a tuple of  (boolean, and a dict, which contains the
                 {'challenge' : challenge} description)
        """

        # response dict, describing the challenge reply
        challenge = {}
        # the allocated db challenge object
        challenge_obj = None
        retry_counter = 0

        reason = None
        ReasonException = Exception()

        hsm = context['hsm'].get('obj')

        transid_len = Challenges.get_tranactionid_length()

        id_length = transid_len - len(id_postfix)

        while True:
            try:
                if not challenge_id:
                    transactionid = u"%s%s" % (
                    Challenge.createTransactionId(length=id_length), id_postfix)
                else:
                    transactionid = challenge_id

                num_challenges = Session.query(Challenge). \
                    filter(Challenge.transid == transactionid).count()

                if num_challenges == 0:
                    challenge_obj = Challenge(transid=transactionid,
                                              tokenserial=token.getSerial())
                if challenge_obj is not None:
                    break

            except Exception as exce:
                log.exception("Failed to create challenge: %r", exce)
                reason = "%r" % exce
                ReasonException = exce

            # prevent an unlimited loop
            retry_counter = retry_counter + 1
            if retry_counter > 100:
                log.error(
                    "Failed to create challenge for %d times: %r - quiting!",
                    retry_counter, reason)
                raise Exception('Failed to create challenge %r' % reason)

        expired_challenges, valid_challenges = Challenges.get_challenges(token)

        # carefully create a new challenge
        try:

            # we got a challenge object allocated and initialize the challenge
            (res, open_transactionid, message, attributes) = \
                token.initChallenge(transactionid,
                                    challenges=valid_challenges,
                                    options=options)

            if res is False:
                # if a different transid is returned, this indicates, that there
                # is already an outstanding challenge we can refere to
                if open_transactionid != transactionid:
                    transactionid = open_transactionid

            else:
                # in case the init was successful, we preserve no the
                # challenge data to support the implementation of a blocking
                # based on the previous stored data
                challenge_obj.setChallenge(message)
                challenge_obj.save()

                (res, message, data, attributes) = \
                    token.createChallenge(transactionid, options=options)

                if res is True:
                    # persist the final challenge data + message
                    challenge_obj.setChallenge(message)
                    challenge_obj.setData(data)
                    challenge_obj.signChallenge(hsm)
                    challenge_obj.save()
                else:
                    transactionid = ''
                    reason = message
                    ReasonException = Exception(message)

        except Exception as exce:
            log.exception("Failed to create challenge: %r", exce)
            reason = "%r" % exce
            ReasonException = exce
            res = False

        # if something goes wrong with the challenge, remove it
        if res is False and challenge_obj is not None:
            try:
                log.debug("Deleting challenge from database session, because "
                          "of earlier error")
                Session.delete(challenge_obj)
                Session.commit()
            except Exception as exx:
                log.debug("Deleting challenge from database session failed. "
                          "Retrying with expunge. Exception was: %r", exx)
                try:
                    Session.expunge(challenge_obj)
                    Session.commit()
                except Exception as exx:
                    log.debug("Expunging challenge from database session "
                              "failed. Exception was: %r", exx)

        # in case that create challenge fails, we must raise this reason
        if reason is not None:
            log.error("Failed to create or init challenge. Reason was %r ",
                      reason)
            raise ReasonException

        # prepare the response for the user
        if transactionid is not None:
            challenge['transactionid'] = transactionid

        if message is not None:
            challenge['message'] = message

        if attributes is not None and type(attributes) == dict:
            challenge.update(attributes)

        #
        # add token specific info like tokentype and serial
        #

        challenge["linotp_tokenserial"] = token.getSerial()
        challenge["linotp_tokentype"] = token.type

        return (res, challenge)