def checkResolverType(resolver): """ check if a resolver of the given type exists :param resolver: full qualified resolver name or optional with trailing conf like: ``useridresolver.PasswdIdResolver.IdResolver.etc_resl`` :return: True or False """ res = False ret = False # prepare reso = resolver.strip() reso = reso.replace("\"", "") # the fully qualified resolver if reso in context.get('resolver_clazzes'): res = context.get('resolver_clazzes').get(reso) ret = True else: # if the last argument is the configuration pack = reso.split('.') rtype = ".".join(pack[:-1]) conf = pack[-1] # lookup, if there is a resolver definition if rtype in context.get('resolver_types'): res = "%s.%s" % (rtype, conf) ret = True # # else: # legacy support, where resolver is defined as # "useridresolver.passwdresolver.mrealm" # so we only could rely only on the type definition e.g. # 'passwdresolver' as part of the string for res_id, res_type in context.get('resolver_types').iteritems(): if res_type in reso: res = "%s.%s" % (res_id, conf) ret = True break # is resolver defined in the linotp config try: getResolverObject(res) except Exception as exx: log.warning("Failed to setup resolver %r: %r" % (res, exx)) res = "%r" % exx ret = False return (ret, res)
def getResolverInfo(resolvername): ''' return the resolver info of the given resolvername :param resolvername: the requested resolver :type resolvername: string :return : dict of resolver description ''' resolver_dict = {} typ = "" resolvertypes = get_resolver_types() descr = {} conf = context.get('Config') # conf = getLinotpConfig() for entry in conf: for typ in resolvertypes: # get the typed values of the descriptor! resolver_conf = get_resolver_classConfig(typ) if typ in resolver_conf: descr = resolver_conf.get(typ).get('config', {}) else: descr = resolver_conf if entry.startswith("linotp." + typ) and entry.endswith(resolvername): # the realm might contain dots "." # so take all after the 3rd dot for realm resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break value = conf.get(entry) if resolver[2] in descr: configEntry = resolver[2] if descr.get(configEntry) == 'password': # do we already have the decrypted pass? if 'enc' + entry in conf: value = conf.get('enc' + entry) else: # if no, we take the encpass and decrypt it value = conf.get(entry) try: en = decryptPassword(value) value = en except: log.info("Decryption of resolver passwd failed: compatibility issue?") resolver_dict[ resolver[2] ] = value # Dont check the other resolver types break return {"type": typ, "data": resolver_dict, "resolver": resolvername}
def getResolverList(filter_resolver_type=None): ''' Gets the list of configured resolvers :param filter_resolver_type: Only resolvers of the given type are returned :type filter_resolver_type: string :rtype: Dictionary of the resolvers and their configuration ''' Resolvers = {} resolvertypes = get_resolver_types() conf = context.get('Config') # conf = getLinotpConfig() for entry in conf: for typ in resolvertypes: if entry.startswith("linotp." + typ): # the realm might contain dots "." # so take all after the 3rd dot for realm r = {} resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break r["resolvername"] = resolver[3] r["entry"] = entry r["type"] = typ if (filter_resolver_type is None) or (filter_resolver_type and filter_resolver_type == typ): Resolvers[resolver[3]] = r # Dont check the other resolver types break return Resolvers
def get_resolver_types(): """ get the array of the registred resolvers :return: array of resolvertypes like 'passwdresolver' """ return context.get('resolver_types').values()
def active_users_per_realm(self, realm=None): """ get the number of users which are assigned to an active token in total or per realm and resolver :param realm: name of realm :return: dict with keys: resolvernames values: number of active token users """ realminfo = context.get('Config').getRealms().get(realm) resolver_specs = realminfo.get('useridresolver', '') realmdict = {} for resolver_spec in resolver_specs: __, config_identifier = parse_resolver_spec(resolver_spec) act_users_per_resolver = Session.query(Token.LinOtpUserid, Token.LinOtpIdResolver, Token.LinOtpIdResClass, Token.LinOtpIsactive)\ .join(TokenRealm)\ .join(Realm)\ .filter(and_( Token.LinOtpIsactive == True, Token.LinOtpIdResClass == resolver_spec, Realm.name == realm ))\ .group_by(Token.LinOtpUserid, Token.LinOtpIdResolver, Token.LinOtpIsactive, Token.LinOtpIdResClass) realmdict[config_identifier] = act_users_per_resolver.count() return realmdict
def defineResolver(params): """ set up a new resolver from request parameters :param params: dict of request parameters """ typ = params['type'] conf = params['name'] resolver_clazz = None for clazz_name, clazz_type in context.get('resolver_types').items(): if typ.lower() in clazz_type.lower(): resolver_clazz = clazz_name if not resolver_clazz: raise Exception("no such resolver type '%r' defined!" % typ) resolver = Resolver() resolver.setDefinition(params) res = resolver.saveConfig() getResolverObject(resolver_clazz + '.' + conf) return res
def getResolverClassName(resolver_type, resolver_name): res = "" for clazz_name, clazz_type in context.get('resolver_types').items(): if clazz_type == resolver_type: res = "%s.%s" % (clazz_name, resolver_name) break return res
def setSoPin(self, soPin): """ set the soPin of the token the soPin is encrypted and the encrypte value is stored in the Token model :param soPin: the special so pin """ iv, enc_soPin = SecretObj.encrypt(soPin, hsm=context.get('hsm')) self.token.setSoPin(enc_soPin, iv)
def __init__(self): """ the Migration hanlder relies on a crypto handler, which encrypts or decryptes data. The setup of the cryptohandler is delayed, as at startup, might not all data be available """ self.salt = None self.crypter = None self.hsm = context.get('hsm')
def closeResolvers(): """ hook to close the resolvers at the end of the request """ try: for resolver in context.get('resolvers_loaded').values(): if hasattr(resolver, 'close'): resolver.close() except Exception as exx: log.exception("Failed to close resolver in context %r" % exx) return
def get_cls_identifier(config_identifier): """ Returns the class identifier string for a existing resolver identified by config_identifier (or None, if config_identifier doesn't exist) """ config = context.get('Config') cls_identifiers = context.get('resolver_classes').keys() for config_entry in config: if not config_entry.endswith(config_identifier): continue for cls_identifier in cls_identifiers: if config_entry.startswith('linotp.' + cls_identifier): return cls_identifier return None
def similar_resolver_exists(config_identifier): """ Signifies if a resolver identified by config_identifer exists in the configuration. :remark: matches case insensitive :returns: bool """ config = context.get('Config') cls_identifiers = context.get('resolver_classes').keys() for config_entry in config: for cls_identifier in cls_identifiers: if config_entry.startswith('linotp.' + cls_identifier): __, __, entry_config_identifier = config_entry.rpartition('.') if entry_config_identifier.lower() == config_identifier.lower(): return True return False
def deleteResolver(resolvername): ''' delete a resolver and all related config entries :paramm resolvername: the name of the to be deleted resolver :type resolvername: string :return: sucess or fail :rtype: boelean ''' res = False resolvertypes = get_resolver_types() conf = context.get('Config') # conf = getLinotpConfig() delEntries = [] resolver_specs = set() for entry in conf: rest = entry.split(".", 3) lSplit = len(rest) if lSplit > 3: rConf = rest[lSplit - 1] if rConf == resolvername: if rest[0] == "linotp" or rest[0] == "enclinotp": typ = rest[1] if typ in resolvertypes: delEntries.append(entry) resolver_conf = get_resolver_class_config(typ) resolver_class = resolver_conf.get(typ, {}).get('clazz') fqn = ".".join([resolver_class, resolvername]) resolver_specs.add(fqn) if len(delEntries) > 0: try: for entry in delEntries: res = removeFromConfig(entry) res = True except Exception as e: log.exception("Deleting resolver %s failed. Exception was %r" % (resolvername, e)) res = False if res: # on success we can flush the caches for resolver_spec in resolver_specs: _flush_user_resolver_cache(resolver_spec) _delete_from_resolver_config_cache(resolver_spec) return res
def get_tranactionid_length(): """ get transaction_id length from config and check if it is in range :return: length of transaction id """ transid_len = int( context.get( 'Config', {}).get( 'TransactionIdLength', Challenges.DefaultTransactionIdLength)) if transid_len < 12 or transid_len > 17: raise Exception("TransactionIdLength must be between 12 and 17, " "was %d" % transid_len) return transid_len
def checkOtp(self, anOtpVal, counter, window, options=None): ''' checkOtp - validate the token otp against a given otpvalue :param anOtpVal: the to be verified otpvalue :type anOtpVal: string :param counter: the counter state, that shoule be verified :type counter: int :param window: the counter +window, which should be checked :type window: int :param options: the dict, which could contain token specific info :type options: dict :return: the counter state or -1 :rtype: int ''' log.debug("[checkOtp] begin. start the otp verification with: otpval %r, counter %r , window %r , options %r " % (anOtpVal, counter, window, options)) otplen = self.token.LinOtpOtpLen #otime contains the previous verification time # the new one must be newer than this! otime = self.token.LinOtpCount secObj = self._get_secret_object() window = self.token.LinOtpCountWindow key, iv = self.token.getUserPin() secPinObj = SecretObj(key, iv, hsm=context.get('hsm')) log.debug("[checkOtp] otime %s", otime) mtimeOtp = mTimeOtp(secObj, secPinObj, otime, otplen) res = mtimeOtp.checkOtp(anOtpVal, window, options=options) if (res != -1): res = res - 1 ## later on this will be incremented by 1 if res == -1: msg = "verification failed" else: msg = "verifiction was successful" log.debug("[checkOtp] %s :res %r" % (msg, res)) return res
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 request_cacher(*args, **kwargs): cache_name = func_to_cache.__name__ + "_cache" if not request_context.get(cache_name, None): request_context[cache_name] = {} cache_key = key_generator(*args, **kwargs) log_message = ("[" + func_to_cache.__name__ + "]:" + " getting output values from cache") if cache_key not in request_context[cache_name]: log_message = ( "[" + func_to_cache.__name__ + "]:" + "output values not in cache, getting values from DB") request_context[cache_name][cache_key] = func_to_cache( *args, **kwargs) log.info(log_message) return request_context[cache_name][cache_key]
def __after__(self, action): """ """ try: # finally create the audit entry Audit = request_context['Audit'] audit = request_context.get('audit') c.audit.update(audit) Audit.log(c.audit) Session.commit() return request except Exception as exx: log.exception(exx) Session.rollback() return sendError(response, exx, context='after') finally: Session.close()
def get_cls_identifier(config_identifier): """ Returns the class identifier string for a existing resolver identified by config_identifier (or None, if config_identifier doesn't exist) """ config = context.get('Config') cls_identifiers = resolver_registry.keys() for config_entry in config: if not config_entry.endswith(config_identifier): continue for cls_identifier in cls_identifiers: if config_entry.startswith('linotp.' + cls_identifier): return cls_identifier return None
def deleteResolver(resolvername): ''' delete a resolver and all related config entries :paramm resolvername: the name of the to be deleted resolver :type resolvername: string :return: sucess or fail :rtype: boelean ''' res = False resolvertypes = get_resolver_types() conf = context.get('Config') # conf = getLinotpConfig() delEntries = [] for entry in conf: rest = entry.split(".", 3) lSplit = len(rest) if lSplit > 3: rConf = rest[lSplit - 1] if rConf == resolvername: if rest[0] == "linotp" or rest[0] == "enclinotp" : typ = rest[1] if typ in resolvertypes: delEntries.append(entry) if len(delEntries) > 0: try: for entry in delEntries: res = removeFromConfig(entry) log.debug("[deleteResolver] removing key: %s" % entry) res = True except Exception as e: log.exception("deleteResolver: %r" % e) res = False return res
def deleteResolver(resolvername): ''' delete a resolver and all related config entries :paramm resolvername: the name of the to be deleted resolver :type resolvername: string :return: sucess or fail :rtype: boelean ''' res = False resolvertypes = get_resolver_types() conf = context.get('Config') # conf = getLinotpConfig() delEntries = [] for entry in conf: rest = entry.split(".", 3) lSplit = len(rest) if lSplit > 3: rConf = rest[lSplit - 1] if rConf == resolvername: if rest[0] == "linotp" or rest[0] == "enclinotp": typ = rest[1] if typ in resolvertypes: delEntries.append(entry) if len(delEntries) > 0: try: for entry in delEntries: res = removeFromConfig(entry) log.debug("[deleteResolver] removing key: %s" % entry) res = True except Exception as e: log.exception("deleteResolver: %r" % e) res = False return res
def get_resolver_class(cls_identifier): ''' return the class object for a resolver type :param resolver_type: string specifying the resolver fully qualified or abreviated :return: resolver object class ''' # ## this patch is a bit hacky: # the normal request has a request context, where it retrieves # the resolver info from and preserves the loaded resolvers for reusage # But in case of a authentication request (by a redirect from a 401) # the caller is no std request and the context object is missing :-( # The solution is, to deal with local references, either to the # global context or to local data resolver_classes = context.get('resolver_classes') if resolver_classes is None: glo = getGlobalObject() resolver_classes = copy.deepcopy(glo.getResolverClasses()) return resolver_classes.get(cls_identifier)
def _get_hsm_obj_from_context(hsm=None): """Get the hsm from LinOTP request context If no hsm parameter is given, we get the hsm from the LinOTP request context (var context) which was extended some time ago. :param hsm: hsm security object instance :return: return the hsm object :rtype: """ if hsm: hsm_obj = hsm.get('obj') else: hsm_obj = context.get('hsm', {}).get('obj') if not hsm_obj: raise HSMException('no hsm defined in execution context!') if hsm_obj.isReady() is False: raise HSMException('hsm not ready!') return hsm_obj
def similar_resolver_exists(config_identifier): """ Signifies if a resolver identified by config_identifer exists in the configuration. :remark: matches case insensitive :returns: bool """ config = context.get('Config') cls_identifiers = list(resolver_registry.keys()) for config_entry in config: for cls_identifier in cls_identifiers: if config_entry.startswith('linotp.' + cls_identifier): __, __, entry_config_identifier = config_entry.rpartition('.') if entry_config_identifier.lower() == config_identifier.lower( ): return True return False
def resolverinfo(self, realm): """ get the resolvers for one realm and the number of users per resolver :param realm: the realm to query :return: dict with resolvernames as keys and number of users as value """ realminfo = context.get('Config').getRealms().get(realm) resolver_specs = realminfo.get('useridresolver', '') realmdict = {} for resolver_spec in resolver_specs: __, config_identifier = parse_resolver_spec(resolver_spec) realmdict[config_identifier] = 0 user = getUserFromParam({'realm': realm}, optionalOrRequired=True) users_iters = iterate_users(getUserListIterators({'realm': realm}, user)) for next_one in users_iters: for key in realmdict: if key in next_one: realmdict[key] += 1 return realmdict
def resolverinfo(self, realm): """ get the resolvers for one realm and the number of users per resolver :param realm: the realm to query :return: dict with resolvernames as keys and number of users as value """ realminfo = context.get("Config").getRealms().get(realm) resolver_specs = realminfo.get("useridresolver", "") realmdict = {} for resolver_spec in resolver_specs: __, config_identifier = parse_resolver_spec(resolver_spec) realmdict[config_identifier] = 0 user = getUserFromParam({"realm": realm}) users = getUserList({"realm": realm, "username": "******"}, user) for next_one in users: resolver = next_one["useridresolver"].split(".")[-1] if resolver in realmdict: realmdict[resolver] += 1 return realmdict
def resolverinfo(self, realm): """ get the resolvers for one realm and the number of users per resolver :param realm: the realm to query :return: dict with resolvernames as keys and number of users as value """ realminfo = context.get('Config').getRealms().get(realm) resolver_specs = realminfo.get('useridresolver', '') realmdict = {} for resolver_spec in resolver_specs: __, config_identifier = parse_resolver_spec(resolver_spec) realmdict[config_identifier] = 0 user = getUserFromParam({'realm': realm}) users = getUserList({'realm': realm, 'username': '******'}, user) for next_one in users: resolver = next_one['useridresolver'].split('.')[-1] if resolver in realmdict: realmdict[resolver] += 1 return realmdict
def request_context_test_iterator(): # this will raise an error if it is called # outside of request_context_safety yield request_context.get('foo')
def challenge(self, data, session='', typ='raw', challenge=None): ''' the challenge method is for creating an transaction / challenge object remark: the transaction has a maximum lifetime and a reference to the OcraSuite token (serial) :param data: data, which is the base for the challenge or None :type data: string or None :param session: session support for ocratokens :type session: string :type typ: define, which kind of challenge base should be used could be raw - take the data input as is (extract chars accordind challenge definition Q) or random - will generate a random input or hased - will take the hash of the input data :return: challenge response containing the transcation id and the challenge for the ocrasuite :rtype : tuple of (transId(string), challenge(string)) ''' s_data = 'None' s_session = 'None' s_challenge = 'None' if data is not None: s_data = data if session is not None: s_session = session if challenge is None: s_challenge = challenge secObj = self._get_secret_object() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj) if not data: typ = 'random' if challenge is None: if typ == 'raw': challenge = ocraSuite.data2rawChallenge(data) elif typ == 'random': challenge = ocraSuite.data2randomChallenge(data) elif typ == 'hash': challenge = ocraSuite.data2hashChallenge(data) serial = self.getSerial() counter = self.getOtpCount() # set the pin onyl in the compliant hashed mode pin = '' if ocraSuite.P is not None: key, iv = self.token.getUserPin() secObj = SecretObj(key, iv, hsm=context.get('hsm')) pin = secObj.getKey() try: param = {} param['C'] = counter param['Q'] = challenge param['P'] = pin param['S'] = session if ocraSuite.T is not None: now = datetime.datetime.now() stime = now.strftime("%s") itime = int(stime) param['T'] = itime ''' verify that the data is compliant with the OcraSuitesuite and the client is able to calc the otp ''' c_data = ocraSuite.combineData(**param) ocraSuite.compute(c_data) except Exception as ex: log.exception( "[OcraTokenClass] Failed to create ocrasuite challenge") raise Exception('[OcraTokenClass] Failed to create ocrasuite' 'challenge: %r' % (ex)) # save the object digits = '0123456789' transid = '' transactionIdLen = 12 try: transactionIdLen = int(getFromConfig("OcraDefaultSuite", '12')) except: transactionIdLen = 12 log.debug("[OcraTokenClass] Failed to set transactionId length" " from config - using fallback %d" % (transactionIdLen)) # create a non exisiting challenge try: while True: for _c in range(0, transactionIdLen): transid += urandom.choice(digits) chall = OcraTokenClass.getTransaction(transid) if chall is None: break ddata = '' if data is not None: ddata = data chall = OcraChallenge(transid=transid, tokenserial=serial, challenge=typ + ':' + challenge, data=typ + ':' + ddata) chall.save() except Exception as ex: # this might happen if we have a db problem or # the uniqnes constrain does not fit log.exception("[OcraTokenClass] Failed to create challenge") raise Exception('[OcraTokenClass] Failed to create challenge' ' object: %s' % (ex)) realms = [] tokenrealms = self.token.getRealms() for realm in tokenrealms: realms.append(realm.name) url = get_qrtan_url(realms) return (transid, challenge, True, url)
def checkUserPass(self, user, passw, options=None): """ :param user: the to be identified user :param passw: the identification pass :param options: optional parameters, which are provided to the token checkOTP / checkPass :return: tuple of True/False and optional information """ # the upper layer will catch / at least should ;-) opt = None serial = None resolverClass = None uid = None user_exists = False if user is not None and not user.is_empty: # the upper layer will catch / at least should try: (uid, _resolver, resolverClass) = getUserId( user, check_existance=True) user_exists = True except Exception as _exx: pass_on = context.get('Config').get( 'linotp.PassOnUserNotFound', False) if pass_on and 'true' == pass_on.lower(): g.audit['action_detail'] = ( 'authenticated by PassOnUserNotFound') return (True, opt) else: g.audit['action_detail'] = 'User not found' return (False, opt) # if we have an user, check if we forward the request to another server if user_exists and get_auth_forward_on_no_token(user) is False: servers = get_auth_forward(user) if servers: res, opt = ForwardServerPolicy.do_request(servers, env, user, passw, options) return res, opt # ------------------------------------------------------------------ -- th = TokenHandler() # ------------------------------------------------------------------ -- # auto asignement with otp only if user has no active token auto_assign_otp_return = th.auto_assign_otp_only( otp=passw, user=user, options=options) if auto_assign_otp_return is True: return (True, None) # ------------------------------------------------------------------ -- token_type = None if options: token_type = options.get('token_type', None) # ------------------------------------------------------------------ -- # if there is a serial provided in the parameters, it overwrites the # token selection by user query_user = user if options and 'serial' in options and options['serial']: serial = options['serial'] query_user = None # ------------------------------------------------------------------ -- tokenList = getTokens4UserOrSerial( query_user, serial, token_type=token_type, read_for_update=True ) if len(tokenList) == 0: g.audit['action_detail'] = 'User has no tokens assigned' # here we check if we should to autoassign and try to do it auto_assign_return = th.auto_assignToken(passw, user) if auto_assign_return is True: # We can not check the token, as the OTP value is already used! # but we will auth the user.... return (True, opt) auto_enroll_return, opt = th.auto_enrollToken(passw, user, options=options) if auto_enroll_return is True: # we always have to return a false, as # we have a challenge tiggered return (False, opt) pass_on = context.get('Config').get('linotp.PassOnUserNoToken', False) if pass_on and 'true' == pass_on.lower(): g.audit['action_detail'] = 'authenticated by PassOnUserNoToken' return (True, opt) # Check if there is an authentication policy passthru if get_auth_passthru(user): log.debug('user %r has no token. Checking for ' 'passthru in realm %r' % (user.login, user.realm)) y = getResolverObject(resolverClass) g.audit['action_detail'] = 'Authenticated against Resolver' if y.checkPass(uid, passw): return (True, opt) # Check alternatively if there is an authentication # policy passOnNoToken elif get_auth_passOnNoToken(user): log.info('user %r has not token. PassOnNoToken' ' set - authenticated!') g.audit['action_detail'] = ( 'Authenticated by passOnNoToken policy') return (True, opt) # if we have an user, check if we forward the request to another server elif get_auth_forward_on_no_token(user) is True: servers = get_auth_forward(user) if servers: res, opt = ForwardServerPolicy.do_request( servers, env, user, passw, options) return res, opt return False, opt if passw is None: raise ParameterError("Missing parameter:pass", id=905) (res, opt) = self.checkTokenList( tokenList, passw, user, options=options) return (res, opt)
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 deleteResolver(resolvername): """ delete a resolver and all related config entries :paramm resolvername: the name of the to be deleted resolver :type resolvername: string :return: sucess or fail :rtype: boolean """ if resolvername == current_app.config["ADMIN_RESOLVER_NAME"]: raise DeleteForbiddenError( f"default admin resolver {resolvername} is not allowed to be removed!" ) res = False resolvertypes = get_resolver_types() conf = context.get("Config") delEntries = [] resolver_specs = set() for entry in conf: rest = entry.split(".", 3) lSplit = len(rest) if lSplit > 3: rConf = rest[lSplit - 1] if rConf == resolvername: if rest[0] == "linotp" or rest[0] == "enclinotp": typ = rest[1] if typ in resolvertypes: delEntries.append(entry) resolver_conf = get_resolver_class_config(typ) resolver_class = resolver_conf.get(typ, {}).get( "clazz" ) fqn = ".".join([resolver_class, resolvername]) resolver_specs.add(fqn) if len(delEntries) > 0: try: for entry in delEntries: res = removeFromConfig(entry) res = True except Exception as exx: log.error( "Deleting resolver %s failed. Exception was %r", resolvername, exx, ) res = False if res: # on success we can flush the caches for resolver_spec in resolver_specs: _flush_user_resolver_cache(resolver_spec) _delete_from_resolver_config_cache(resolver_spec) return res
def _check(self, param): """ basic check function, that can be used by different controllers :param param: dict of all caller parameters :type param: dict :return: Tuple of True or False and opt :rtype: Tuple(boolean, opt) """ opt = None options = {} # put everything in the options but the user, pass, init options.update(param) for para in ["pass", "user", "init"]: if para in options: del options[para] passw = param.get("pass") user = getUserFromParam(param) # support for challenge verification challenge = param.get("challenge") if challenge is not None: options = {} options["challenge"] = challenge g.audit["user"] = user.login realm = user.realm or getDefaultRealm() g.audit["realm"] = realm # AUTHORIZATION Pre Check # we need to overwrite the user.realm in case the # user does not exist in the original realm (setrealm-policy) user.realm = set_realm(user.login, realm, exception=True) check_user_authorization(user.login, user.realm, exception=True) vh = ValidationHandler() (ok, opt) = vh.checkUserPass(user, passw, options=options) g.audit.update(request_context.get("audit", {})) g.audit["success"] = ok if ok: # AUTHORIZATION post check check_auth_tokentype(g.audit["serial"], exception=True, user=user) check_auth_serial(g.audit["serial"], exception=True, user=user) # add additional details if is_auth_return(ok, user=user): if opt is None: opt = {} if ok: opt["realm"] = g.audit.get("realm") opt["user"] = g.audit.get("user") opt["tokentype"] = g.audit.get("token_type") opt["serial"] = g.audit.get("serial") else: opt["error"] = g.audit.get("action_detail") return (ok, opt)
def checkOtp(self, passw, counter, window, options=None): ''' checkOtp - standard callback of linotp to verify the token :param passw: the passw / otp, which has to be checked :type passw: string :param counter: the start counter :type counter: int :param window: the window, in which the token is valid :type window: int :param options: options contains the transaction id, eg. if check_t checks one transaction this will support assynchreonous otp checks (when check_t is used) :type options: dict :return: verification counter or -1 :rtype: int (-1) ''' ret = -1 secObj = self._get_secret_object() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj) # if we have no transactionid given through the options, # we have to retrieve the eldest challenge for this ocra token serial = self.getSerial() challenges = [] # set the ocra token pin ocraPin = '' if ocraSuite.P is not None: key, iv = self.token.getUserPin() secObj = SecretObj(key, iv, hsm=context.get('hsm')) ocraPin = secObj.getKey() if ocraPin is None or len(ocraPin) == 0: ocraPin = '' timeShift = 0 if ocraSuite.T is not None: defTimeWindow = int(getFromConfig("ocra.timeWindow", 180)) window = (int(self.getFromTokenInfo( 'timeWindow', defTimeWindow)) / ocraSuite.T) defTimeShift = int(getFromConfig("ocra.timeShift", 0)) timeShift = int(self.getFromTokenInfo("timeShift", defTimeShift)) if options is None: challenges = OcraTokenClass.getTransactions4serial( serial, currentOnly=True) elif options is not None: if type(options).__name__ != 'dict': err = ('[chekOtp] "options" not of type dict! %r' % type(options)) log.error(err) raise Exception(err) if 'transactionid' in options: transid = options.get('transactionid') challenges.append(OcraTokenClass.getTransaction(transid)) elif 'challenge' in options: challenges.append(options) # due to the added options in checkUserPass, we have to extend # the logic here: # if no challenges found in between but we have a serial, we catch # the open challenges by serial (s.o.) if len(challenges) == 0: challenges = OcraTokenClass.getTransactions4serial( serial, currentOnly=True) if len(challenges) == 0: # verify that there has already been a challenge challenges = OcraTokenClass.getTransactions4serial(serial) if len(challenges) > 0: err = 'No current transaction found!' ret = -1 return ret else: err = 'No open transaction found!' log.info(err) if type(options) == dict and 'transactionid' in options: raise Exception(err) ret = -1 return ret for ch in challenges: challenge = {} if isinstance(ch, dict): # transaction less checkOtp self.transId = 0 challenge.update(ch) elif type(ch) == OcraChallenge: # preserve transaction context, so we could use this in # the status callback self.transId = ch.transid challenge['challenge'] = ch.challenge challenge['transid'] = ch.transid challenge['session'] = ch.session ret = ocraSuite.checkOtp(passw, counter, window, challenge, pin=ocraPin, options=options, timeshift=timeShift) if ret != -1: break if -1 == ret: # autosync: test if two consecutive challenges + # it's counter match ret = self.autosync(ocraSuite, passw, challenge) return ret
def getResolverList(filter_resolver_type=None, config=None): ''' Gets the list of configured resolvers :param filter_resolver_type: Only resolvers of the given type are returned :type filter_resolver_type: string :rtype: Dictionary of the resolvers and their configuration ''' Resolvers = {} resolvertypes = get_resolver_types() if not config: conf = context.get('Config') else: conf = config for entry in conf: for typ in resolvertypes: if entry.startswith("linotp." + typ): # the realm might contain dots "." # so take all after the 3rd dot for realm r = {} resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break r["resolvername"] = resolver[3] r["entry"] = entry r["type"] = typ readonly_entry = '.'.join([resolver[0], resolver[1], 'readonly', resolver[3]]) if readonly_entry in conf: readonly = False try: readonly = boolean(conf[readonly_entry]) except Exception as _exx: log.info("Failed to convert 'readonly' attribute" " %r:%r", readonly_entry, conf[readonly_entry]) if readonly: r["readonly"] = True # # this is a patch for a hack: # # as entry, the first found resolver is shown # as the PasswdResolver only has one entry, this always # has been 'fileName', which now as could be 'readonly' # thus we skip the readonly entry: key = resolver[2] if key == "readonly": continue if ((filter_resolver_type is None) or (filter_resolver_type and filter_resolver_type == typ)): Resolvers[resolver[3]] = r # Dont check the other resolver types break return Resolvers
def getResolverInfo(resolvername, passwords=False): ''' return the resolver info of the given resolvername :param resolvername: the requested resolver :type resolvername: string :return : dict of resolver description ''' result = {"type": None, "data": {}, "resolver": resolvername} linotp_config = context.get('Config') resolver_types = get_resolver_types() # --------------------------------------------------------------------- -- # lookup, which resolver type is associated with this resolver name for config_entry in linotp_config: if config_entry.endswith("." + resolvername): # check if this is a resolver definition, starting with linotp. # and continuing with a resolver type part = config_entry.split('.') if (len(part) > 3 and part[0] == 'linotp' and part[1] in resolver_types): resolver_type = part[1] break else: return result # now we can load the resolver config unsing the resolver class resolver_cls = get_resolver_class(resolver_type) if resolver_cls is None: raise Exception("no such resolver type '%r' defined!" % resolver_type) res_conf, _missing = resolver_cls.filter_config(linotp_config, resolvername) # --------------------------------------------------------------------- -- # now prepare the resolver config output, which should contain # # - no global entries, starting with 'linotp.' # - adjusted passwords # - all values as text for key in res_conf.keys(): # suppress global config entries if key.startswith("linotp."): del res_conf[key] continue # should passwords be displayed? if key in resolver_cls.crypted_parameters: if not passwords: res_conf[key] = encryptPassword(res_conf[key]) # as we have in the resolver config typed values, this might # lead to some trouble. so we prepare for output comparison # the string representation if (not isinstance(res_conf[key], str) and not isinstance(res_conf[key], unicode)): res_conf[key] = "%r" % res_conf[key] if 'readonly' in res_conf: readonly = False try: readonly = boolean(res_conf['readonly']) except Exception: log.info("Failed to convert 'readonly' attribute %r:%r", resolvername, res_conf['readonly']) if readonly: result["readonly"] = True result["type"] = resolver_type result["data"] = res_conf return result
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') 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.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.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 = 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 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: log.exception("Failed to create Challenge: %r", ReasonException) log.error("Failed to create or init challenge %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)
def checkUserPass(self, user, passw, options=None): """ :param user: the to be identified user :param passw: the identification pass :param options: optional parameters, which are provided to the token checkOTP / checkPass :return: tuple of True/False and optional information """ # the upper layer will catch / at least should ;-) opt = None serial = None resolverClass = None uid = None user_exists = False if user: # the upper layer will catch / at least should try: (uid, _resolver, resolverClass) = getUserId(user, check_existance=True) user_exists = True except Exception as _exx: pass_on = context.get("Config").get( "linotp.PassOnUserNotFound", False) if pass_on and pass_on.lower() == "true": g.audit[ "action_detail"] = "authenticated by PassOnUserNotFound" return (True, opt) else: g.audit["action_detail"] = "User not found" return (False, opt) # if we have an user, check if we forward the request to another server if user_exists and not get_auth_forward_on_no_token(user): servers = get_auth_forward(user) if servers: log.info("forwarding auth request for user {} to {}".format( user, servers)) res, opt = ForwardServerPolicy.do_request( servers, env, user, passw, options) log.info("result of auth request for user {}: ({}, {})".format( user, res, opt)) g.audit["action_detail"] = "Forwarded, result {}".format(res) return res, opt else: log.info( "NOT forwarding auth request for user {} (no servers)". format(user)) g.audit["action_detail"] = "Not forwarded (no servers)" else: log.info( "NOT forwarding auth request for user {} " "(get_auth_forward_on_no_token returned False)".format(user)) # ------------------------------------------------------------------ -- th = TokenHandler() # ------------------------------------------------------------------ -- # auto asignement with otp only if user has no active token auto_assign_otp_return = th.auto_assign_otp_only(otp=passw, user=user, options=options) if auto_assign_otp_return: return (True, None) # ------------------------------------------------------------------ -- token_type = None if options: token_type = options.get("token_type", None) # ------------------------------------------------------------------ -- # if there is a serial provided in the parameters, it overwrites the # token selection by user query_user = user if options and "serial" in options and options["serial"]: serial = options["serial"] query_user = None # ------------------------------------------------------------------ -- tokenList = getTokens4UserOrSerial(query_user, serial, token_type=token_type, read_for_update=True) if len(tokenList) == 0: g.audit["action_detail"] = "User has no tokens assigned" # here we check if we should to autoassign and try to do it auto_assign_return = th.auto_assignToken(passw, user) if auto_assign_return: # We can not check the token, as the OTP value is already used! # but we will auth the user.... return (True, opt) auto_enroll_return, opt = th.auto_enrollToken(passw, user, options=options) if auto_enroll_return: # we always have to return a false, as # we have a challenge tiggered return (False, opt) pass_on = context.get("Config").get("linotp.PassOnUserNoToken", False) if pass_on and pass_on.lower() == "true": g.audit["action_detail"] = "authenticated by PassOnUserNoToken" return (True, opt) # Check if there is an authentication policy passthru if get_auth_passthru(user): log.debug( "user %r has no token. Checking for passthru in realm %r", user.login, user.realm, ) y = getResolverObject(resolverClass) g.audit["action_detail"] = "Authenticated against Resolver" if y.checkPass(uid, passw): return (True, opt) # Check alternatively if there is an authentication # policy passOnNoToken elif get_auth_passOnNoToken(user): log.info("user %r has not token. PassOnNoToken" " set - authenticated!") g.audit[ "action_detail"] = "Authenticated by passOnNoToken policy" return (True, opt) # if we have an user, check if we forward the request to another # server elif get_auth_forward_on_no_token(user): servers = get_auth_forward(user) if servers: log.info( "forwarding auth request for user {} to {}".format( user, servers)) res, opt = ForwardServerPolicy.do_request( servers, env, user, passw, options) log.info( "result of auth request for user {}: ({}, {})".format( user, res, opt)) g.audit["action_detail"] = "Forwarded, result {}".format( res) return res, opt else: log.info( "NOT forwarding auth request for user {} (no servers)". format(user)) g.audit["action_detail"] = "Not forwarded (no servers)" return False, opt if passw is None: raise ParameterError("Missing parameter:pass", id=905) (res, opt) = self.checkTokenList(tokenList, passw, user, options=options) return (res, opt)
def migrate_resolver(self, src=None, target=None, filter_serials=None): """ support the migration of owned tokens from one resolver to a new one the idea is: - get all tokens from one resolver - for each token, the the owner - from the owner get the login name - with the login name get the uid from the target resolver - update the new_id in the token """ _ = context['translate'] ret = {} if not src or not target: raise Exception("Missing src or target resolver defintion!") audit = context.get('audit') now = datetime.now() stime = now.strftime("%s") audit['action_detail'] = ("migration from %s to %s" % (src['resolvername'], target['resolvername'])) ret['src'] = src ret['target'] = target ret['value'] = False ret['message'] = '' search = getResolverClassName(src['type'], src['resolvername']) target_resolver = getResolverClassName(target['type'], target['resolvername']) # get all tokens of src resolver tokens = self._get_tokens_for_resolver(search, serials=filter_serials) num_migration = 0 serials = set() for token in tokens: serial = token.get('LinOtpTokenSerialnumber') userid = token.get('LinOtpUserid') resolverC = token.get('LinOtpIdResClass') # now do the lookup of the uid in the # src resolver to get the login uInfo = getUserInfo(userid, '', resolverC) login = uInfo.get('username') try: y = getResolverObject(target_resolver) uid = y.getUserId(login) if not uid: log.warning("User %s not found in target resolver %r", login, target_resolver) continue token.LinOtpIdResClass = target_resolver token.LinOtpUserid = uid # TODO: adjust token.LinOtpIdResolver = target['type'] Session.add(token) num_migration += 1 serials.add(serial) except Exception as exx: log.exception("Faild to set new resolver data for token %s: %r" % (serial, exx)) ret['value'] = True ret['message'] = (_("%d tokens of %d migrated") % (num_migration, len(tokens))) log.info(ret['message']) audit['info'] = "[%s] %s" % (stime, ret['message']) audit['serial'] = ",".join(list(serials)) audit['success'] = True context['audit'] = audit return ret
return res ## # AUTOSYNC starts here ## counter = self.token.getOtpCounter() syncWindow = self.token.getSyncWindow() if ocraSuite.T is not None: syncWindow = syncWindow / 10 # set the ocra token pin ocraPin = '' if ocraSuite.P is not None: key, iv = self.token.getUserPin() secObj = SecretObj(key, iv, hsm=context.get('hsm')) ocraPin = secObj.getKey() if ocraPin is None or len(ocraPin) == 0: ocraPin = '' timeShift = 0 if ocraSuite.T is not None: timeShift = int(self.getFromTokenInfo("timeShift", 0)) # timeStepping = int(ocraSuite.T) tinfo = self.getTokenInfo() # autosync does only work, if we have a token info, where the # last challenge and the last sync-counter is stored
def autosync(self, ocraSuite, passw, challenge): ''' try to resync a token automaticaly, if a former and the current request failed :param ocraSuite: the ocraSuite of the current Token :type ocraSuite: ocra object :param passw: ''' res = -1 autosync = False try: confsetting = getFromConfig("AutoResync") if confsetting is None: autosync = False elif "true" == confsetting.lower(): autosync = True elif "false" == confsetting.lower(): autosync = False except Exception as ex: log.exception('Ocra: autosync check undefined %r' % (ex)) return res ' if autosync is not enabled: do nothing ' if autosync is False: return res ## # AUTOSYNC starts here ## counter = self.token.getOtpCounter() syncWindow = self.token.getSyncWindow() if ocraSuite.T is not None: syncWindow = syncWindow / 10 # set the ocra token pin ocraPin = '' if ocraSuite.P is not None: key, iv = self.token.getUserPin() secObj = SecretObj(key, iv, hsm=context.get('hsm')) ocraPin = secObj.getKey() if ocraPin is None or len(ocraPin) == 0: ocraPin = '' timeShift = 0 if ocraSuite.T is not None: timeShift = int(self.getFromTokenInfo("timeShift", 0)) # timeStepping = int(ocraSuite.T) tinfo = self.getTokenInfo() # autosync does only work, if we have a token info, where the # last challenge and the last sync-counter is stored # if no tokeninfo, we start with a autosync request, thus start the # lookup in the sync window if 'lChallenge' not in tinfo: # run checkOtp, with sync window for the current challenge log.info('[OcraToken:autosync] initial sync') count_0 = -1 try: otp0 = passw count_0 = ocraSuite.checkOtp(otp0, counter, syncWindow, challenge, pin=ocraPin, timeshift=timeShift) except Exception as ex: log.exception('Ocra: Error during autosync0: %r' % (ex)) if count_0 != -1: tinfo['lChallenge'] = {'otpc': count_0} self.setTokenInfo(tinfo) log.info('[OcraToken:autosync] initial sync - success: %r' % count_0) res = -1 else: # run checkOtp, with sync window for the current challenge count_1 = -1 try: otp1 = passw count_1 = ocraSuite.checkOtp(otp1, counter, syncWindow, challenge, pin=ocraPin, timeshift=timeShift) except Exception as ex: log.exception('Ocra: Error during autosync1: %r' % (ex)) if count_1 == -1: del tinfo['lChallenge'] self.setTokenInfo(tinfo) log.info('[OcraToken:autosync] sync failed! Not a valid pass' ' in scope (%r)' % (otp1)) res = -1 else: # run checkOtp, with sync window for the old challenge lChallange = tinfo.get('lChallenge') count_0 = lChallange.get('otpc') if ocraSuite.C is not None: # sync the counter based ocra token if count_1 - count_0 < 2: self.setOtpCount(count_1) res = count_1 if ocraSuite.T is not None: # sync the timebased ocra token if count_1 - count_0 < ocraSuite.T * 2: # calc the new timeshift ! log.debug("[autosync] the counter %r matches: %r" % (count_1, datetime.datetime.fromtimestamp(count_1))) currenttime = int(time.time()) new_shift = (count_1 - currenttime) tinfo['timeShift'] = new_shift self.setOtpCount(count_1) res = count_1 # if we came here, the old challenge is not required anymore del tinfo['lChallenge'] self.setTokenInfo(tinfo) return res
def checkUserPass(self, user, passw, options=None): """ :param user: the to be identified user :param passw: the identifiaction pass :param options: optional parameters, which are provided to the token checkOTP / checkPass :return: tuple of True/False and optional information """ log.debug('entering function checkUserPass(%r)' % user.login) # the upper layer will catch / at least should ;-) opt = None serial = None resolverClass = None uid = None audit = context['audit'] user_exists = False if user is not None and (user.isEmpty() is False): # the upper layer will catch / at least should try: (uid, _resolver, resolverClass) = getUserId(user) user_exists = True except: pass_on = context.get('Config').get( 'linotp.PassOnUserNotFound', False) if pass_on and 'true' == pass_on.lower(): audit['action_detail'] = ( 'authenticated by PassOnUserNotFound') return (True, opt) else: audit['action_detail'] = 'User not found' return (False, opt) # if we have an user, check if we forward the request to another server if user_exists: servers = get_auth_forward(user) if servers: res, opt = ForwardServerPolicy.do_request( servers, env, user, passw, options) return res, opt tokenList = linotp.lib.token.getTokens4UserOrSerial(user, serial) if len(tokenList) == 0: audit['action_detail'] = 'User has no tokens assigned' # here we check if we should to autoassign and try to do it log.debug('about to check auto_assigning') th = TokenHandler() auto_assign_return = th.auto_assignToken(passw, user) if auto_assign_return is True: # We can not check the token, as the OTP value is already used! # but we will auth the user.... return (True, opt) auto_enroll_return, opt = th.auto_enrollToken(passw, user, options=options) if auto_enroll_return is True: # we always have to return a false, as # we have a challenge tiggered return (False, opt) pass_on = context.get('Config').get('linotp.PassOnUserNoToken', False) if pass_on and 'true' == pass_on.lower(): audit['action_detail'] = 'authenticated by PassOnUserNoToken' return (True, opt) # Check if there is an authentication policy passthru from linotp.lib.policy import get_auth_passthru if get_auth_passthru(user): log.debug('user %r has no token. Checking for ' 'passthru in realm %r' % (user.login, user.realm)) y = getResolverObject(resolverClass) audit['action_detail'] = 'Authenticated against Resolver' if y.checkPass(uid, passw): return (True, opt) # Check if there is an authentication policy passOnNoToken from linotp.lib.policy import get_auth_passOnNoToken if get_auth_passOnNoToken(user): log.info('user %r has not token. PassOnNoToken' ' set - authenticated!') audit['action_detail'] = ( 'Authenticated by passOnNoToken policy') return (True, opt) return (False, opt) if passw is None: raise ParameterError(u"Missing parameter:pass", id=905) (res, opt) = self.checkTokenList(tokenList, passw, user, options=options) log.debug('return of __checkTokenList: %r ' % (res, )) return (res, opt)
def migrate_resolver(self, src=None, target=None, filter_serials=None): """ support the migration of owned tokens from one resolver to a new one the idea is: - get all tokens from one resolver - for each token, the the owner - from the owner get the login name - with the login name get the uid from the target resolver - update the new_id in the token """ _ = context['translate'] ret = {} if not src or not target: raise Exception("Missing src or target resolver defintion!") audit = context.get('audit') now = datetime.now() stime = now.strftime("%s") audit['action_detail'] = ( "migration from %s to %s" % (src['resolvername'], target['resolvername'])) ret['src'] = src ret['target'] = target ret['value'] = False ret['message'] = '' search = getResolverClassName(src['type'], src['resolvername']) target_resolver = getResolverClassName(target['type'], target['resolvername']) # get all tokens of src resolver tokens = self._get_tokens_for_resolver(search, serials=filter_serials) num_migration = 0 serials = set() for token in tokens: serial = token.get('LinOtpTokenSerialnumber') userid = token.get('LinOtpUserid') resolverC = token.get('LinOtpIdResClass') # now do the lookup of the uid in the # src resolver to get the login uInfo = getUserInfo(userid, '', resolverC) login = uInfo.get('username') try: y = getResolverObject(target_resolver) uid = y.getUserId(login) if not uid: log.warning("User %s not found in target resolver %r", login, target_resolver) continue token.LinOtpIdResClass = target_resolver token.LinOtpUserid = uid # TODO: adjust token.LinOtpIdResolver = target['type'] Session.add(token) num_migration += 1 serials.add(serial) except Exception as exx: log.exception( "Faild to set new resolver data for token %s: %r" % (serial, exx)) ret['value'] = True ret['message'] = (_("%d tokens of %d migrated") % (num_migration, len(tokens))) log.info(ret['message']) audit['info'] = "[%s] %s" % (stime, ret['message']) audit['serial'] = ",".join(list(serials)) audit['success'] = True context['audit'] = audit return ret
def getResolverInfo(resolvername): ''' return the resolver info of the given resolvername :param resolvername: the requested resolver :type resolvername: string :return : dict of resolver description ''' resolver_dict = {} typ = "" resolvertypes = get_resolver_types() descr = {} conf = context.get('Config') # conf = getLinotpConfig() for entry in conf: for typ in resolvertypes: # get the typed values of the descriptor! resolver_conf = get_resolver_class_config(typ) if typ in resolver_conf: descr = resolver_conf.get(typ).get('config', {}) else: descr = resolver_conf if entry.startswith("linotp." + typ) and entry.endswith(resolvername): # the realm might contain dots "." # so take all after the 3rd dot for realm resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break value = conf.get(entry) if resolver[2] in descr: configEntry = resolver[2] if descr.get(configEntry) == 'password': # do we already have the decrypted pass? if 'enc' + entry in conf: value = conf.get('enc' + entry) else: # if no, we take the encpass and decrypt it value = conf.get(entry) try: en = decryptPassword(value) value = en except: log.info( "Decryption of resolver passwd failed: compatibility issue?" ) resolver_dict[resolver[2]] = value # Dont check the other resolver types break return {"type": typ, "data": resolver_dict, "resolver": resolvername}
def _check(self, param): ''' basic check function, that can be used by different controllers :param param: dict of all caller parameters :type param: dict :return: Tuple of True or False and opt :rtype: Tuple(boolean, opt) ''' opt = None options = {} # put everything in the options but the user, pass, init options.update(param) for para in ["pass", "user", "init"]: if options.has_key(para): del options[para] passw = param.get("pass") user = getUserFromParam(param) # support for ocra application challenge verification challenge = param.get("challenge") if challenge is not None: options = {} options['challenge'] = challenge c.audit['user'] = user.login realm = user.realm or getDefaultRealm() c.audit['realm'] = realm # AUTHORIZATION Pre Check # we need to overwrite the user.realm in case the # user does not exist in the original realm (setrealm-policy) user.realm = set_realm(user.login, realm, exception=True) check_user_authorization(user.login, user.realm, exception=True) if isSelfTest() is True: initTime = param.get("init") if initTime is not None: if options is None: options = {} options['initTime'] = initTime vh = ValidationHandler() (ok, opt) = vh.checkUserPass(user, passw, options=options) c.audit.update(request_context.get('audit')) c.audit['success'] = ok if ok: # AUTHORIZATION post check check_auth_tokentype(c.audit['serial'], exception=True, user=user) check_auth_serial(c.audit['serial'], exception=True, user=user) # add additional details if is_auth_return(ok, user=user): if opt is None: opt = {} if ok: opt['realm'] = c.audit.get('realm') opt['user'] = c.audit.get('user') opt['tokentype'] = c.audit.get('token_type') opt['serial'] = c.audit.get('serial') else: opt['error'] = c.audit.get('action_detail') return (ok, opt)
def checkOtp(self, passw, counter, window, options=None): ''' checkOtp - standard callback of linotp to verify the token :param passw: the passw / otp, which has to be checked :type passw: string :param counter: the start counter :type counter: int :param window: the window, in which the token is valid :type window: int :param options: options contains the transaction id, eg. if check_t checks one transaction this will support assynchreonous otp checks (when check_t is used) :type options: dict :return: verification counter or -1 :rtype: int (-1) ''' ret = -1 secObj = self._get_secret_object() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj) # if we have no transactionid given through the options, # we have to retrieve the eldest challenge for this ocra token serial = self.getSerial() challenges = [] # set the ocra token pin ocraPin = '' if ocraSuite.P is not None: key, iv = self.token.getUserPin() secObj = SecretObj(key, iv, hsm=context.get('hsm')) ocraPin = secObj.getKey() if ocraPin is None or len(ocraPin) == 0: ocraPin = '' timeShift = 0 if ocraSuite.T is not None: defTimeWindow = int(getFromConfig("ocra.timeWindow", 180)) window = (int(self.getFromTokenInfo('timeWindow', defTimeWindow)) / ocraSuite.T) defTimeShift = int(getFromConfig("ocra.timeShift", 0)) timeShift = int(self.getFromTokenInfo("timeShift", defTimeShift)) if options is None: challenges = OcraTokenClass.getTransactions4serial( serial, currentOnly=True) elif options is not None: if type(options).__name__ != 'dict': err = ('[chekOtp] "options" not of type dict! %r' % type(options)) log.error(err) raise Exception(err) if 'transactionid' in options: transid = options.get('transactionid') challenges.append(OcraTokenClass.getTransaction(transid)) elif 'challenge' in options: challenges.append(options) # due to the added options in checkUserPass, we have to extend # the logic here: # if no challenges found in between but we have a serial, we catch # the open challenges by serial (s.o.) if len(challenges) == 0: challenges = OcraTokenClass.getTransactions4serial( serial, currentOnly=True) if len(challenges) == 0: # verify that there has already been a challenge challenges = OcraTokenClass.getTransactions4serial(serial) if len(challenges) > 0: err = 'No current transaction found!' ret = -1 return ret else: err = 'No open transaction found!' log.info(err) if type(options) == dict and 'transactionid' in options: raise Exception(err) ret = -1 return ret for ch in challenges: challenge = {} if isinstance(ch, dict): # transaction less checkOtp self.transId = 0 challenge.update(ch) elif type(ch) == OcraChallenge: # preserve transaction context, so we could use this in # the status callback self.transId = ch.transid challenge['challenge'] = ch.challenge challenge['transid'] = ch.transid challenge['session'] = ch.session ret = ocraSuite.checkOtp(passw, counter, window, challenge, pin=ocraPin, options=options, timeshift=timeShift) if ret != -1: break if -1 == ret: # autosync: test if two consecutive challenges + # it's counter match ret = self.autosync(ocraSuite, passw, challenge) return ret
def checkUserPass(self, user, passw, options=None): """ :param user: the to be identified user :param passw: the identifiaction pass :param options: optional parameters, which are provided to the token checkOTP / checkPass :return: tuple of True/False and optional information """ log.debug('entering function checkUserPass(%r)' % user.login) # the upper layer will catch / at least should ;-) opt = None serial = None resolverClass = None uid = None audit = context['audit'] user_exists = False if user is not None and (user.isEmpty() == False): # the upper layer will catch / at least should try: (uid, _resolver, resolverClass) = getUserId(user) user_exists = True except: pass_on = context.get('Config').get( 'linotp.PassOnUserNotFound', False) if pass_on and 'true' == pass_on.lower(): audit['action_detail'] = ( 'authenticated by PassOnUserNotFound') return (True, opt) else: audit['action_detail'] = 'User not found' return (False, opt) # if we have an user, check if we forward the request to another server if user_exists: from linotp.lib.policy import get_auth_forward servers = get_auth_forward(user) if servers: if 'radius://' in servers: rad = RadiusRequest(servers=servers) res, opt = rad.do_request(servers, user, passw, options) return res, opt elif 'http://' in servers or 'https://' in servers: http = HttpRequest(servers=servers) res, opt = http.do_request(user, passw, options) return res, opt tokenList = linotp.lib.token.getTokens4UserOrSerial(user, serial) if len(tokenList) == 0: audit['action_detail'] = 'User has no tokens assigned' # here we check if we should to autoassign and try to do it log.debug('about to check auto_assigning') th = TokenHandler() auto_assign_return = th.auto_assignToken(passw, user) if auto_assign_return is True: # We can not check the token, as the OTP value is already used! # but we will auth the user.... return (True, opt) auto_enroll_return, opt = th.auto_enrollToken(passw, user, options=options) if auto_enroll_return is True: # we always have to return a false, as # we have a challenge tiggered return (False, opt) pass_on = context.get('Config').get('linotp.PassOnUserNoToken', False) if pass_on and 'true' == pass_on.lower(): audit['action_detail'] = 'authenticated by PassOnUserNoToken' return (True, opt) # Check if there is an authentication policy passthru from linotp.lib.policy import get_auth_passthru if get_auth_passthru(user): log.debug('user %r has no token. Checking for ' 'passthru in realm %r' % (user.login, user.realm)) y = getResolverObject(resolverClass) audit['action_detail'] = 'Authenticated against Resolver' if y.checkPass(uid, passw): return (True, opt) # Check if there is an authentication policy passOnNoToken from linotp.lib.policy import get_auth_passOnNoToken if get_auth_passOnNoToken(user): log.info('user %r has not token. PassOnNoToken' ' set - authenticated!') audit['action_detail'] = ( 'Authenticated by passOnNoToken policy') return (True, opt) return (False, opt) if passw is None: raise ParameterError(u"Missing parameter:pass", id=905) (res, opt) = self.checkTokenList( tokenList, passw, user, options=options) log.debug('return of __checkTokenList: %r ' % (res,)) return (res, opt)
def getResolverInfo(resolvername, passwords=False): ''' return the resolver info of the given resolvername :param resolvername: the requested resolver :type resolvername: string :return : dict of resolver description ''' result = {"type": None, "data": {}, "resolver": resolvername} linotp_config = context.get('Config') resolver_types = get_resolver_types() # --------------------------------------------------------------------- -- # lookup, which resolver type is associated with this resolver name for config_entry in linotp_config: if config_entry.endswith("." + resolvername): # check if this is a resolver definition, starting with linotp. # and continuing with a resolver type part = config_entry.split('.') if (len(part) > 3 and part[0] == 'linotp' and part[1] in resolver_types): resolver_type = part[1] break else: return result # now we can load the resolver config unsing the resolver class resolver_cls = get_resolver_class(resolver_type) if resolver_cls is None: raise Exception("no such resolver type '%r' defined!" % resolver_type) res_conf, _missing = resolver_cls.filter_config(linotp_config, resolvername) # --------------------------------------------------------------------- -- # now prepare the resolver config output, which should contain # # - no global entries, starting with 'linotp.' # - adjusted passwords # - all values as text for key in list(res_conf.keys()): # suppress global config entries if key.startswith("linotp."): del res_conf[key] continue # should passwords be displayed? if key in resolver_cls.crypted_parameters: # we have to be sure that we only have encrypted data objects for # secret data if not isinstance(res_conf[key], EncryptedData): raise Exception('Encrypted Data Object expected') # if parameter password is True, we need to unencrypt if passwords: res_conf[key] = res_conf[key].get_unencrypted() # as we have in the resolver config typed values, this might # lead to some trouble. so we prepare for output comparison # the string representation if not isinstance(res_conf[key], str): res_conf[key] = "%r" % res_conf[key] if 'readonly' in res_conf: readonly = False try: readonly = boolean(res_conf['readonly']) except Exception: log.info("Failed to convert 'readonly' attribute %r:%r", resolvername, res_conf['readonly']) if readonly: result["readonly"] = True result["type"] = resolver_type result["data"] = res_conf return result
def getResolverList(filter_resolver_type=None, config=None): """ Gets the list of configured resolvers :param filter_resolver_type: Only resolvers of the given type are returned :type filter_resolver_type: string :rtype: Dictionary of the resolvers and their configuration """ Resolvers = {} resolvertypes = get_resolver_types() local_admin_resolver = current_app.config["ADMIN_RESOLVER_NAME"] admin_resolvers = get_admin_resolvers() if not config: conf = context.get("Config") else: conf = config for entry in conf: for typ in resolvertypes: if entry.startswith("linotp." + typ): # the resolver might contain dots "." so take # all after the 3rd dot for the resolver name r = {} resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break r["resolvername"] = resolver[3] r["entry"] = entry r["type"] = typ # return the resolver spec, which is required to define a realm resolver_cls = get_resolver_class(typ) r["spec"] = resolver_cls.db_prefix + "." + resolver[3] r["admin"] = resolver[3] in admin_resolvers # set the immutable flag if its the local_admin_resolver r["immutable"] = local_admin_resolver == resolver[3] readonly_entry = ".".join( [resolver[0], resolver[1], "readonly", resolver[3]] ) if readonly_entry in conf: readonly = False try: readonly = boolean(conf[readonly_entry]) except Exception as _exx: log.info( "Failed to convert 'readonly' attribute %r:%r", readonly_entry, conf[readonly_entry], ) if readonly: r["readonly"] = True # # this is a patch for a hack: # # as entry, the first found resolver is shown # as the PasswdResolver only has one entry, this always # has been 'fileName', which now as could be 'readonly' # thus we skip the readonly entry: key = resolver[2] if key == "readonly": continue if (filter_resolver_type is None) or ( filter_resolver_type and filter_resolver_type == typ ): Resolvers[resolver[3]] = r # Dont check the other resolver types break return Resolvers
def resync(self, otp1, otp2, options=None): ''' - for the resync to work, we take the last two transactions and their challenges - for each challenge, we search forward the sync window length ''' ret = False challenges = [] o_challenges = OcraTokenClass.getTransactions4serial(self.getSerial()) for challenge in o_challenges: challenges.append(challenge) # check if there are enough challenges around if len(challenges) < 2: return False challenge1 = {} challenge2 = {} if options is None: ch1 = challenges[0] challenge1['challenge'] = ch1.challenge challenge1['transid'] = ch1.transid challenge1['session'] = ch1.session ch2 = challenges[1] challenge2['challenge'] = ch2.challenge challenge2['transid'] = ch2.transid challenge2['session'] = ch2.session else: if 'challenge1' in options: challenge1['challenge'] = options.get('challenge1') if 'challenge2' in options: challenge2['challenge'] = options.get('challenge2') if len(challenge1) == 0 or len(challenge2) == 0: error = "No challenges found!" log.info('[OcraTokenClass:resync] %s' % (error)) raise Exception('[OcraTokenClass:resync] %s' % (error)) secObj = self._get_secret_object() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj) syncWindow = self.token.getSyncWindow() if ocraSuite.T is not None: syncWindow = syncWindow / 10 counter = self.token.getOtpCounter() # set the ocra token pin ocraPin = '' if ocraSuite.P is not None: key, iv = self.token.getUserPin() secObj = SecretObj(key, iv, hsm=context.get('hsm')) ocraPin = secObj.getKey() if ocraPin is None or len(ocraPin) == 0: ocraPin = '' timeShift = 0 if ocraSuite.T is not None: timeShift = int(self.getFromTokenInfo("timeShift", 0)) try: count_1 = ocraSuite.checkOtp(otp1, counter, syncWindow, challenge1, pin=ocraPin, timeshift=timeShift) if count_1 == -1: log.info('Ocra resync: lookup for first otp value failed!') ret = False else: count_2 = ocraSuite.checkOtp(otp2, counter, syncWindow, challenge2, pin=ocraPin, timeshift=timeShift) if count_2 == -1: log.info( 'Ocra resync: lookup for second otp value failed!') ret = False else: if ocraSuite.C is not None: if count_1 + 1 == count_2: self.setOtpCount(count_2) ret = True if ocraSuite.T is not None: if count_1 - count_2 <= ocraSuite.T * 2: # callculate the timeshift date = datetime.datetime.fromtimestamp(count_2) log.info('Ocra resync: Syncing token to new ' 'timestamp %r' % (date)) now = datetime.datetime.now() stime = now.strftime("%s") timeShift = count_2 - int(stime) self.addToTokenInfo('timeShift', timeShift) ret = True except Exception as ex: log.exception('[OcraTokenClass:resync] unknown error: %r' % (ex)) raise Exception('[OcraTokenClass:resync] unknown error: %s' % (ex)) return ret
def check_pin(token, passw, user=None, options=None): ''' check the provided pin w.r.t. the policy definition :param passw: the to be checked pass :param user: if otppin==1, this is the user, which resolver should be checked :param options: the optional request parameters :return: boolean, if pin matched True ''' res = False otppin_mode = _get_otppin_mode(get_pin_policies(user)) if 1 == otppin_mode: # We check the Users Password as PIN log.debug("pin policy=1: checking the users password as pin") # this should not be the case if not options: options = {} selfservice_state = context.get('selfservice', {}).get('state', '') if selfservice_state in ['credentials_verified', 'challenge_triggered']: return True if 'pin_match' not in options: options['pin_match'] = {} hashed_passw = sha256(passw.encode('utf-8')).hexdigest() # if password already found, we can return result again if hashed_passw in options['pin_match']: log.debug("check if password already checked! %r " % options['pin_match'][hashed_passw]) return options['pin_match'][hashed_passw] # if a password already matched, this one will fail if 'found' in options['pin_match']: log.debug("check if password already found but its not this one!") return False if user is None or not user.login: log.info("fail for pin policy == 1 with user = None") res = False else: (uid, _resolver, resolver_class) = getUserId(user) resolver = getResolverObject(resolver_class) if resolver.checkPass(uid, passw): log.debug("Successfully authenticated user %r." % uid) res = True else: log.info("user %r failed to authenticate." % uid) # we register our result key = sha256(passw.encode('utf-8')).hexdigest() options['pin_match'][key] = res # and register the success, to shorten lookups after # already one positive was found if res is True: options['pin_match']['found'] = True return res elif otppin_mode == 2: # NO PIN should be entered atall log.debug("[__checkToken] pin policy=2: checking no pin") return len(passw) == 0 elif otppin_mode == 3: # ignore pin or password log.debug("[__checkToken] pin policy=3: ignoreing pin") if token.type in ['spass']: return token.checkPin(passw, options=options) return True else: # old stuff: We check The fixed OTP PIN log.debug("[__checkToken] pin policy=0: checkin the PIN") return token.checkPin(passw, options=options)
def _check(self, param): ''' basic check function, that can be used by different controllers :param param: dict of all caller parameters :type param: dict :return: Tuple of True or False and opt :rtype: Tuple(boolean, opt) ''' opt = None options = {} ## put everythin in the options but the user, pass, init options.update(param) for para in ["pass", "user", "init"]: if options.has_key(para): del options[para] passw = getParam(param, "pass", optional) user = getUserFromParam(param, optional) # support for ocra application challenge verification challenge = getParam(param, "challenge", optional) if challenge is not None: options = {} options['challenge'] = challenge c.audit['user'] = user.login realm = user.realm or getDefaultRealm() c.audit['realm'] = realm # AUTHORIZATION Pre Check # we need to overwrite the user.realm in case the user does not exist in the original realm (setrealm-policy) user.realm = set_realm(user.login, realm, exception=True) check_user_authorization(user.login, user.realm, exception=True) if isSelfTest() is True: initTime = getParam(param, "init", optional) if initTime is not None: if options is None: options = {} options['initTime'] = initTime vh = ValidationHandler() (ok, opt) = vh.checkUserPass(user, passw, options=options) c.audit.update(request_context.get('audit')) c.audit['success'] = ok if ok: # AUTHORIZATION post check check_auth_tokentype(c.audit['serial'], exception=True, user=user) check_auth_serial(c.audit['serial'], exception=True, user=user) # add additional details if is_auth_return(ok, user=user): if opt is None: opt = {} if ok: opt['realm'] = c.audit.get('realm') opt['user'] = c.audit.get('user') opt['tokentype'] = c.audit.get('token_type') opt['serial'] = c.audit.get('serial') else: opt['error'] = c.audit.get('action_detail') return (ok, opt)
def getResolverList(filter_resolver_type=None, config=None): ''' Gets the list of configured resolvers :param filter_resolver_type: Only resolvers of the given type are returned :type filter_resolver_type: string :rtype: Dictionary of the resolvers and their configuration ''' Resolvers = {} resolvertypes = get_resolver_types() if not config: conf = context.get('Config') else: conf = config for entry in conf: for typ in resolvertypes: if entry.startswith("linotp." + typ): # the realm might contain dots "." # so take all after the 3rd dot for realm r = {} resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break r["resolvername"] = resolver[3] r["entry"] = entry r["type"] = typ readonly_entry = '.'.join( [resolver[0], resolver[1], 'readonly', resolver[3]]) if readonly_entry in conf: readonly = False try: readonly = boolean(conf[readonly_entry]) except Exception as _exx: log.info( "Failed to convert 'readonly' attribute" " %r:%r", readonly_entry, conf[readonly_entry]) if readonly: r["readonly"] = True # # this is a patch for a hack: # # as entry, the first found resolver is shown # as the PasswdResolver only has one entry, this always # has been 'fileName', which now as could be 'readonly' # thus we skip the readonly entry: key = resolver[2] if key == "readonly": continue if ((filter_resolver_type is None) or (filter_resolver_type and filter_resolver_type == typ)): Resolvers[resolver[3]] = r # Dont check the other resolver types break return Resolvers
def resync(self, otp1, otp2, options=None): ''' - for the resync to work, we take the last two transactions and their challenges - for each challenge, we search forward the sync window length ''' ret = False challenges = [] o_challenges = OcraTokenClass.getTransactions4serial(self.getSerial()) for challenge in o_challenges: challenges.append(challenge) # check if there are enough challenges around if len(challenges) < 2: return False challenge1 = {} challenge2 = {} if options is None: ch1 = challenges[0] challenge1['challenge'] = ch1.challenge challenge1['transid'] = ch1.transid challenge1['session'] = ch1.session ch2 = challenges[1] challenge2['challenge'] = ch2.challenge challenge2['transid'] = ch2.transid challenge2['session'] = ch2.session else: if 'challenge1' in options: challenge1['challenge'] = options.get('challenge1') if 'challenge2' in options: challenge2['challenge'] = options.get('challenge2') if len(challenge1) == 0 or len(challenge2) == 0: error = "No challenges found!" log.info('[OcraTokenClass:resync] %s' % (error)) raise Exception('[OcraTokenClass:resync] %s' % (error)) secObj = self._get_secret_object() ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj) syncWindow = self.token.getSyncWindow() if ocraSuite.T is not None: syncWindow = syncWindow / 10 counter = self.token.getOtpCounter() # set the ocra token pin ocraPin = '' if ocraSuite.P is not None: key, iv = self.token.getUserPin() secObj = SecretObj(key, iv, hsm=context.get('hsm')) ocraPin = secObj.getKey() if ocraPin is None or len(ocraPin) == 0: ocraPin = '' timeShift = 0 if ocraSuite.T is not None: timeShift = int(self.getFromTokenInfo("timeShift", 0)) try: count_1 = ocraSuite.checkOtp(otp1, counter, syncWindow, challenge1, pin=ocraPin, timeshift=timeShift) if count_1 == -1: log.info('Ocra resync: lookup for first otp value failed!') ret = False else: count_2 = ocraSuite.checkOtp(otp2, counter, syncWindow, challenge2, pin=ocraPin, timeshift=timeShift) if count_2 == -1: log.info('Ocra resync: lookup for second otp value failed!') ret = False else: if ocraSuite.C is not None: if count_1 + 1 == count_2: self.setOtpCount(count_2) ret = True if ocraSuite.T is not None: if count_1 - count_2 <= ocraSuite.T * 2: # callculate the timeshift date = datetime.datetime.fromtimestamp(count_2) log.info('Ocra resync: Syncing token to new ' 'timestamp %r' % (date)) now = datetime.datetime.now() stime = now.strftime("%s") timeShift = count_2 - int(stime) self.addToTokenInfo('timeShift', timeShift) ret = True except Exception as ex: log.exception('[OcraTokenClass:resync] unknown error: %r' % (ex)) raise Exception('[OcraTokenClass:resync] unknown error: %s' % (ex)) return ret
def __init__(self, page=None, psize=None, sort=None, sortdir=None, realms=None, status=None, date=None): """ constructor of Tokeniterator, which gathers all conditions to build a sqalchemy query - iterator :param page: page number :type page: int :param psize: how many entries per page :type psize: int :param sort: sort field definition :type sort: string :param sortdir: sort direction: ascending or descending :type sortdir: string :param realms: reports from which realms will be shown :type realms: list :param status: filter reports by status like active, unassigned :type status: list :param date: only show entries newer than date :type date: strin gin format 'yyyy-mm-dd' :return: - nothing / None """ self.page = 1 self.pages = 1 if not isinstance(realms, (list, tuple)): realms = realms.split(',') if not isinstance(status, (list, tuple)): status = status.split(',') realm_cond = tuple() for realm in realms: realm_cond += (or_(Reporting.realm == realm), ) status_cond = tuple() for stat in status: status_cond += (or_(Reporting.parameter == stat), ) date_cond = tuple() if date: date_cond += (and_(Reporting.timestamp >= date), ) conds = ( and_(*date_cond), or_(*realm_cond), or_(*status_cond), ) if sort is None: order = Reporting.timestamp elif sort == 'event': order = Reporting.event elif sort == 'realm': order = Reporting.realm elif sort == 'parameter': order = Reporting.parameter elif sort == 'value': order = Reporting.value elif sort == 'count': order = Reporting.count elif sort == 'detail': order = Reporting.detail elif sort == 'description': order = Reporting.description elif sort == 'session': order = Reporting.session else: order = Reporting.timestamp # care for the result sort order if sortdir is not None and sortdir == "desc": order = order.desc() else: order = order.asc() # query database for all reports self.reports = Session.query(Reporting).filter( *conds).order_by(order).distinct() self.report_num = self.reports.count() self.pagesize = self.report_num # care for the result pageing if page is not None: try: if psize is None: pagesize = \ int(request_context.get('Config').get('pagesize', 50)) else: pagesize = int(psize) except Exception as exce: log.debug('Reporting: Problem with pagesize detected. ' 'Exception was: %r' % exce) pagesize = 20 try: the_page = int(page) - 1 except Exception as exce: log.debug('Reporting: Problem with page detected. ' 'Exception was %r' % exce) the_page = 0 if the_page < 0: the_page = 0 start = the_page * pagesize stop = (the_page + 1) * pagesize self.page = the_page + 1 fpages = float(self.report_num) / float(pagesize) self.pages = int(fpages) if fpages - self.pages > 0: self.pages += 1 self.pagesize = pagesize self.reports = self.reports.slice(start, stop)
def getResolverInfo(resolvername, passwords=False): ''' return the resolver info of the given resolvername :param resolvername: the requested resolver :type resolvername: string :return : dict of resolver description ''' result = {"type": None, "data": {}, "resolver": resolvername} resolver_dict = {} descr = {} resolver_entries = {} resolvertypes = get_resolver_types() linotp_config = context.get('Config') for typ in resolvertypes: for config_entry in linotp_config: if (config_entry.startswith("linotp." + typ) and config_entry.endswith(resolvername)): resolver_entries[config_entry] = linotp_config.get( config_entry) if not resolver_entries: return result resolver_parts = resolver_entries.keys()[0].split('.') # # TODO: remove legacy code: An old entry without resolver name # if len(resolver_parts) <= 3: return result # # get the type descriptions for the resolver type # resolver_type = resolver_parts[1] resolver_conf = get_resolver_class_config(resolver_type) if resolver_type in resolver_conf: resolver_descr = resolver_conf.get(resolver_type).get('config', {}) else: resolver_descr = resolver_conf # # build up the resolver dictionary # for key, value in resolver_entries.items(): resolver_key = key.split(".")[2] if resolver_key in resolver_descr: if (resolver_descr.get(resolver_key) == 'password' and passwords is True): # do we already have the decrypted pass? if 'enc%s' % key in linotp_config: value = linotp_config.get('enc%s' % key) else: # if no, we take the entry and try to de crypt it value = linotp_config.get(key) try: value = decryptPassword(value) except Exception as exc: log.exception( "Decryption of resolver entry " "failed: %r", exc) resolver_dict[resolver_key] = value result["type"] = resolver_type result["data"] = resolver_dict return result