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
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
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
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
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
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
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
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)
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)