def getTokenForUser(user, active=None, exclude_rollout=True): """ should be moved to token.py """ tokenArray = [] log.debug("[getTokenForUser] iterating tokens for user...") log.debug("[getTokenForUser] ...user %s in realm %s.", user.login, user.realm) tokens = getTokens4UserOrSerial(user=user, serial=None, _class=True, active=active) for token in tokens: tok = token.token.get_vars() if tok.get('LinOtp.TokenInfo', None): token_info = json.loads(tok.get('LinOtp.TokenInfo')) # skip the rollout tokens from the selfservice token list path = token_info.get('scope', {}).get('path', []) if len(path) == 1 and path[0] == 'userservice' and exclude_rollout: continue tok['LinOtp.TokenInfo'] = token_info tok['Enrollment'] = token.get_enrollment_status() tokenArray.append(tok) log.debug("[getTokenForUser] found tokenarray: %r" % tokenArray) return tokenArray
def getTokenForUser(user, active=None): """ should be moved to token.py """ tokenArray = [] log.debug("[getTokenForUser] iterating tokens for user...") log.debug("[getTokenForUser] ...user %s in realm %s.", user.login, user.realm) tokens = getTokens4UserOrSerial(user=user, serial=None, _class=True, active=active) for token in tokens: tok = token.token.get_vars() if tok.get('LinOtp.TokenInfo', None): token_info = json.loads(tok.get('LinOtp.TokenInfo')) tok['LinOtp.TokenInfo'] = token_info tok['Enrollment'] = token.get_enrollment_status() tokenArray.append(tok) log.debug("[getTokenForUser] found tokenarray: %r" % tokenArray) return tokenArray
def pair(self): try: # ------------------------------------------------------------------ params = dict(**request.params) enc_response = params.get('pairing_response') if enc_response is None: raise Exception('Parameter missing') # ------------------------------------------------------------------ dec_response = decrypt_pairing_response(enc_response) token_type = dec_response.token_type pairing_data = dec_response.pairing_data if not hasattr(pairing_data, 'serial') or \ pairing_data.serial is None: raise ValidateError( 'Pairing responses with no serial attached ' 'are currently not implemented.') # ------------------------------------------------------------------ # TODO: pairing policy tokens = getTokens4UserOrSerial(None, pairing_data.serial) if not tokens: raise Exception('Invalid serial in pairing response') if len(tokens) > 1: raise Exception('Multiple tokens found. Pairing not possible') token = tokens[0] # ------------------------------------------------------------------ if token.type != token_type: raise Exception('Serial in pairing response doesn\'t match ' 'supplied token_type') # ------------------------------------------------------------------ token.pair(pairing_data) Session.commit() return sendResult(response, False) # ---------------------------------------------------------------------- except Exception: Session.rollback() return sendResult(response, False, 0, status=False) finally: Session.close()
def getTokenForUser(user, active=None): """ should be moved to token.py """ tokenArray = [] log.debug("[getTokenForUser] iterating tokens for user...") log.debug("[getTokenForUser] ...user %s in realm %s.", user.login, user.realm) tokens = getTokens4UserOrSerial(user=user, serial=None, _class=True, active=active) for token in tokens: tok = token.token.get_vars() if tok.get('LinOtp.TokenInfo', None): token_info = json.loads(tok.get('LinOtp.TokenInfo')) # skip the rollout tokens from the selfservice token list path = token_info.get('scope', {}).get('path', []) if len(path) == 1 and path[0] == 'userservice': continue tok['LinOtp.TokenInfo'] = token_info tok['Enrollment'] = token.get_enrollment_status() tokenArray.append(tok) log.debug("[getTokenForUser] found tokenarray: %r" % tokenArray) return tokenArray
def pair(self): try: params = dict(**request.params) enc_response = params.get('pairing_response') if enc_response is None: raise Exception('Parameter missing') dec_response = decrypt_pairing_response(enc_response) if not dec_response.serial: raise ValidateError( 'Pairing responses with no serial attached ' 'are currently not implemented.') serial = dec_response.serial user_public_key = dec_response.user_public_key user_token_id = dec_response.user_token_id user = dec_response.user_login user = getUserFromParam(params, optional) # TODO: pairing policy tokens = getTokens4UserOrSerial(None, serial) if not tokens: raise Exception('Invalid serial in pairing response') if len(tokens) > 1: raise Exception('Multiple tokens found. Pairing not possible') token = tokens[0] if token.type != 'qr': raise Exception('Pairing is only implemented for the qrtoken') token.ensure_state('pairing_url_sent') token.addToTokenInfo('user_token_id', user_token_id) b64_user_public_key = b64encode(user_public_key) token.addToTokenInfo('user_public_key', b64_user_public_key) params['serial'] = serial params['user_public_key'] = user_public_key params['user_token_id'] = user_token_id params['user'] = user params['content_type'] = CONTENT_TYPE_PAIRING params['data'] = serial token.change_state('pairing_response_received') Session.commit() return sendResult(response, False) except Exception: Session.rollback() return sendResult(response, False, 0, status=False) finally: Session.close()
def checkYubikeyPass(self, passw): """ Checks the password of a yubikey in Yubico mode (44,48), where the first 12 or 16 characters are the tokenid :param passw: The password that consist of the static yubikey prefix and the otp :type passw: string :return: True/False and the User-Object of the token owner :rtype: dict """ audit = context['audit'] opt = None res = False tokenList = [] # strip the yubico OTP and the PIN modhex_serial = passw[:-32][-16:] try: hex_serial = modhex_decode(modhex_serial) serialnum = "UBAM" + binascii.unhexlify(hex_serial).decode('utf-8') except TypeError as exx: log.error("Failed to convert serialnumber: %r" % exx) return res, opt # build list of possible yubikey tokens serials = [serialnum] for i in range(1, 3): serials.append("%s_%s" % (serialnum, i)) for serial in serials: tokens = getTokens4UserOrSerial(serial=serial, read_for_update=True) tokenList.extend(tokens) if len(tokenList) == 0: audit['action_detail'] = ('The serial %s could not be found!' % serialnum) return res, opt # FIXME if the Token has set a PIN and the User does not want to enter # the PIN for authentication, we need to do something different here... # and avoid PIN checking in __checkToken. # We could pass an "option" to __checkToken. (res, opt) = self.checkTokenList(tokenList, passw) # Now we need to get the user if res is not False and 'serial' in audit: serial = audit.get('serial', None) if serial is not None: user = get_token_owner(tokenList[0]) audit['user'] = user.login audit['realm'] = user.realm opt = {'user': user.login, 'realm': user.realm} return res, opt
def janitor_to_remove_enrollment_token(valid_tokens): """ remove all enrollment only tokens """ # ------------------------------------------------------------------ -- # get all owners for the valid tokens all_owners = set() for token in valid_tokens: # if the authenticated token is a rollout token, we dont count him path = token.getFromTokenInfo('scope', {}).get('path', []) if len(path) == 1 and path[0] == 'userservice': continue owner = get_token_owner(token) if owner: all_owners.add(owner) # ------------------------------------------------------------------ -- # get all rollout only tokens per owner to_be_removed_tokens = [] for owner in all_owners: # should be purge the tokens of the user? <- defined by policy if not purge_enrollment_token(user=owner): continue user_tokens = getTokens4UserOrSerial(user=owner) # if there is only one token either # - the user has still the rollout: do not delete # - or the user has a new one: we dont delete either if len(user_tokens) < 2: continue for token in user_tokens: path = token.getFromTokenInfo('scope', {}).get('path', []) if len(path) == 1 and path[0] == 'userservice': to_be_removed_tokens.append(token) # ------------------------------------------------------------------ -- # delete all rollout tokens for token in to_be_removed_tokens: remove_token(token) # eof###########################################################################
def checkYubikeyPass(self, passw): """ Checks the password of a yubikey in Yubico mode (44,48), where the first 12 or 16 characters are the tokenid :param passw: The password that consist of the static yubikey prefix and the otp :type passw: string :return: True/False and the User-Object of the token owner :rtype: dict """ audit = context['audit'] opt = None res = False tokenList = [] # strip the yubico OTP and the PIN modhex_serial = passw[:-32][-16:] try: serialnum = "UBAM" + modhex_decode(modhex_serial) except TypeError as exx: log.error("Failed to convert serialnumber: %r" % exx) return res, opt # build list of possible yubikey tokens serials = [serialnum] for i in range(1, 3): serials.append("%s_%s" % (serialnum, i)) for serial in serials: tokens = getTokens4UserOrSerial(serial=serial, read_for_update=True) tokenList.extend(tokens) if len(tokenList) == 0: audit['action_detail'] = ( 'The serial %s could not be found!' % serialnum) return res, opt # FIXME if the Token has set a PIN and the User does not want to enter # the PIN for authentication, we need to do something different here... # and avoid PIN checking in __checkToken. # We could pass an "option" to __checkToken. (res, opt) = self.checkTokenList(tokenList, passw) # Now we need to get the user if res is not False and 'serial' in audit: serial = audit.get('serial', None) if serial is not None: user = get_token_owner(tokenList[0]) audit['user'] = user.login audit['realm'] = user.realm opt = {'user': user.login, 'realm': user.realm} return res, opt
def pair(self): try: params = dict(**request.params) enc_response = params.get('pairing_response') if enc_response is None: raise Exception('Parameter missing') dec_response = decrypt_pairing_response(enc_response) if not dec_response.serial: raise ValidateError('Pairing responses with no serial attached ' 'are currently not implemented.') serial = dec_response.serial user_public_key = dec_response.user_public_key user_token_id = dec_response.user_token_id user = dec_response.user_login user = getUserFromParam(params, optional) # TODO: pairing policy tokens = getTokens4UserOrSerial(None, serial) if not tokens: raise Exception('Invalid serial in pairing response') if len(tokens) > 1: raise Exception('Multiple tokens found. Pairing not possible') token = tokens[0] if token.type != 'qr': raise Exception('Pairing is only implemented for the qrtoken') token.ensure_state('pairing_url_sent') token.addToTokenInfo('user_token_id', user_token_id) b64_user_public_key = b64encode(user_public_key) token.addToTokenInfo('user_public_key', b64_user_public_key) params['serial'] = serial params['user_public_key'] = user_public_key params['user_token_id'] = user_token_id params['user'] = user params['content_type'] = CONTENT_TYPE_PAIRING params['data'] = serial token.change_state('pairing_response_received') Session.commit() return sendResult(response, False) except Exception: Session.rollback() return sendResult(response, False, 0, status=False) finally: Session.close()
def check_by_transactionid(self, transid, passw, options=None): """ check the passw against the open transaction :param transid: the transaction id :param passw: the pass parameter :param options: the additional optional parameters :return: tuple of boolean and detail dict """ reply = {} serials = [] challenges = Challenges.lookup_challenges(transid=transid) for challenge in challenges: serials.append(challenge.tokenserial) if not serials: reply['value'] = False reply['failure'] = ('No challenge for transaction %r found' % transid) return False, reply reply['failcount'] = 0 reply['value'] = False reply['token_type'] = '' for serial in serials: tokens = getTokens4UserOrSerial(serial=serial) if not tokens: raise Exception('tokenmismatch for token serial: %s' % (unicode(serial))) # there could be only one token = tokens[0] owner = linotp.lib.token.get_token_owner(token) (ok, opt) = self.checkTokenList(tokens, passw, user=owner, options=options) if opt: reply.update(opt) reply['token_type'] = token.getType() reply['failcount'] = token.getFailCount() reply['value'] = ok if ok: break return ok, reply
def check_by_transactionid(self, transid, passw, options=None): """ check the passw against the open transaction :param transid: the transaction id :param passw: the pass parameter :param options: the additional optional parameters :return: tuple of boolean and detail dict """ reply = {} serials = [] challenges = Challenges.lookup_challenges(transid=transid) for challenge in challenges: serials.append(challenge.tokenserial) if not serials: reply['value'] = False reply['failure'] = ('No challenge for transaction %r found' % transid) return False, reply reply['failcount'] = 0 reply['value'] = False reply['token_type'] = '' for serial in serials: tokens = getTokens4UserOrSerial(serial=serial) if not tokens: raise Exception('tokenmismatch for token serial: %s' % (unicode(serial))) # there could be only one token = tokens[0] owner = linotp.lib.token.get_token_owner(token) (ok, opt) = self.checkTokenList(tokens, passw, user=owner, options=options) if opt: reply.update(opt) reply['token_type'] = token.getType() reply['failcount'] = token.getFailCount() reply['value'] = ok if ok: break return ok, reply
def checkSerialPass(self, serial, passw, options=None, user=None): """ This function checks the otp for a given serial :attention: the parameter user must be set, as the pin policy==1 will verify the user pin """ token_type = options.get("token_type", None) tokenList = getTokens4UserOrSerial(None, serial, token_type=token_type, read_for_update=True) if passw is None: # other than zero or one token should not happen, as serial is # unique if len(tokenList) == 1: theToken = tokenList[0] tok = theToken.token realms = tok.getRealmNames() if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo( tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass, ) user = User(login=userInfo.get("username"), realm=realm) user.info = userInfo if theToken.is_challenge_request(passw, user, options=options): (res, opt) = Challenges.create_challenge(theToken, options) res = False else: raise ParameterError("Missing parameter: pass", id=905) else: raise Exception("No token found: " "unable to create challenge for %s" % serial) else: (res, opt) = self.checkTokenList(tokenList, passw, user=user, options=options) return (res, opt)
def _getTargetToken(self, forwardSerial): """ helper - to get the target token """ if self.targetToken: return self.targetToken from linotp.lib.token import getTokens4UserOrSerial tokens = getTokens4UserOrSerial(serial=forwardSerial) if not tokens: raise Exception('no target token with serial %r found' % forwardSerial) self.targetToken = tokens[0] return self.targetToken
def _getTargetToken(self, forwardSerial): """ helper - to get the target token """ if self.targetToken: return self.targetToken from linotp.lib.token import getTokens4UserOrSerial tokens = getTokens4UserOrSerial(serial=forwardSerial) if not tokens: raise Exception('no target token with serial %r found' % forwardSerial) self.targetToken = tokens[0] return self.targetToken
def checkSerialPass(self, serial, passw, options=None, user=None): """ This function checks the otp for a given serial :attention: the parameter user must be set, as the pin policy==1 will verify the user pin """ token_type = options.get('token_type', None) tokenList = getTokens4UserOrSerial(None, serial, token_type=token_type, read_for_update=True) if passw is None: # other than zero or one token should not happen, as serial is # unique if len(tokenList) == 1: theToken = tokenList[0] tok = theToken.token realms = tok.getRealmNames() if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo(tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass) user = User(login=userInfo.get('username'), realm=realm) user.info = userInfo if theToken.is_challenge_request(passw, user, options=options): (res, opt) = Challenges.create_challenge(theToken, options) res = False else: raise ParameterError('Missing parameter: pass', id=905) else: raise Exception('No token found: ' 'unable to create challenge for %s' % serial) else: (res, opt) = self.checkTokenList( tokenList, passw, user=user, options=options) return (res, opt)
def getTokenForUser(user): """ should be moved to token.py """ tokenArray = [] log.debug("[getTokenForUser] ...user %s in realm %s." % (user.login, user.realm)) tokens = getTokens4UserOrSerial(user=user, serial=None, _class=False) for token in tokens: tok = token.get_vars() if tok.get('LinOtp.TokenInfo', None): token_info = json.loads(tok.get('LinOtp.TokenInfo')) tok['LinOtp.TokenInfo'] = token_info tokenArray.append(tok) log.debug("[getTokenForUser] found tokenarray: %r" % tokenArray) return tokenArray
def getTokenForUser(user): """ should be moved to token.py """ tokenArray = [] log.debug("[getTokenForUser] iterating tokens for user...") log.debug("[getTokenForUser] ...user %s in realm %s." % (user.login, user.realm)) tokens = getTokens4UserOrSerial(user=user, serial=None, _class=False) for token in tokens: tok = token.get_vars() if tok.get("LinOtp.TokneInfo", None): token_info = json.loads(tok.get("LinOtp.TokneInfo")) tok["LinOtp.TokenInfo"] = token_info tokenArray.append(tok) log.debug("[getTokenForUser] found tokenarray: %r" % tokenArray) return tokenArray
def getTokenForUser(user, active=None, exclude_rollout=True): """ should be moved to token.py """ tokenArray = [] log.debug("[getTokenForUser] iterating tokens for user...") log.debug( "[getTokenForUser] ...user %s in realm %s.", user.login, user.realm ) tokens = getTokens4UserOrSerial( user=user, serial=None, _class=True, active=active ) for token in tokens: tok = token.token.get_vars() if tok.get("LinOtp.TokenInfo", None): token_info = json.loads(tok.get("LinOtp.TokenInfo")) # skip the rollout tokens from the selfservice token list path = token_info.get("scope", {}).get("path", []) if ( set(path) & set(["userservice", "validate"]) and exclude_rollout ): continue tok["LinOtp.TokenInfo"] = token_info tok["Enrollment"] = token.get_enrollment_status() tokenArray.append(tok) log.debug("[getTokenForUser] found tokenarray: %r", tokenArray) return tokenArray
def handle_related_challenge(matching_challenges): """ handle related challenges and close these :param matching_challenges: all challenges that have been correctly answerd """ from linotp.lib.token import getTokens4UserOrSerial to_be_closed_challenges = [] for matching_challenge in matching_challenges: # gather all challenges which are now obsolete # from the token point of view serial = matching_challenge.tokenserial token = getTokens4UserOrSerial(serial=serial)[0] token_challenges = Challenges.lookup_challenges(serial=serial) to_be_closed = token.challenge_janitor([matching_challenge], token_challenges) to_be_closed_challenges.extend(to_be_closed) # gather all challenges which are part of the same transaction transid = matching_challenge.transid if "." in transid: transid = transid.split('.')[0] transid_challenges = Challenges.lookup_challenges(transid=transid) to_be_closed_challenges.extend(transid_challenges) hsm = context['hsm'].get('obj') for challenge in set(to_be_closed_challenges): challenge.close() # and calculate the mac for this token data challenge.signChallenge(hsm) challenge.save() return
def handle_related_challenge(matching_challenges): """ handle related challenges and close these :param matching_challenges: all challenges that have been correctly answered """ from linotp.lib.token import getTokens4UserOrSerial to_be_closed_challenges = [] for matching_challenge in matching_challenges: # gather all challenges which are now obsolete # from the token point of view serial = matching_challenge.tokenserial token = getTokens4UserOrSerial(serial=serial)[0] token_challenges = Challenges.lookup_challenges(serial=serial) to_be_closed = token.challenge_janitor([matching_challenge], token_challenges) to_be_closed_challenges.extend(to_be_closed) # gather all challenges which are part of the same transaction transid = matching_challenge.transid if "." in transid: transid = transid.split('.')[0] transid_challenges = Challenges.lookup_challenges(transid=transid) to_be_closed_challenges.extend(transid_challenges) hsm = context['hsm'].get('obj') for challenge in set(to_be_closed_challenges): challenge.close() # and calculate the mac for this token data challenge.signChallenge(hsm) challenge.save() return
def pair(self): """ validate/pair: for the enrollment of qr and push token """ try: # -------------------------------------------------------------- -- enc_response = self.request_params.get('pairing_response') if enc_response is None: raise Exception('Parameter missing') # -------------------------------------------------------------- -- dec_response = decrypt_pairing_response(enc_response) token_type = dec_response.token_type pairing_data = dec_response.pairing_data if not hasattr(pairing_data, 'serial') or \ pairing_data.serial is None: raise ValidateError('Pairing responses with no serial attached' ' are currently not implemented.') # --------------------------------------------------------------- - # TODO: pairing policy tokens = getTokens4UserOrSerial(None, pairing_data.serial) if not tokens: raise Exception('Invalid serial in pairing response') if len(tokens) > 1: raise Exception('Multiple tokens found. Pairing not possible') token = tokens[0] # prepare some audit entries t_owner = token.getUser() realms = token.getRealms() realm = '' if realms: realm = realms[0] c.audit['user'] = t_owner or '' c.audit['realm'] = realm # --------------------------------------------------------------- -- if token.type != token_type: raise Exception('Serial in pairing response doesn\'t match ' 'supplied token_type') # --------------------------------------------------------------- -- token.pair(pairing_data) c.audit['success'] = 1 Session.commit() return sendResult(response, False) # ------------------------------------------------------------------- -- except Exception as exx: log.exception("validate/pair failed: %r" % exx) c.audit['info'] = unicode(exx) Session.rollback() return sendResult(response, False, 0, status=False) finally: Session.close()
def getotp(self): ''' This function is used to retrieve the current otp value for a given user or a given serial. If the user has more than one token, the list of the tokens is returend. method: gettoken/getotp arguments: user - username / loginname realm - additional realm to match the user to a useridresolver serial - the serial number of the token curTime - used ONLY for internal testing: datetime.datetime object returns: JSON response ''' getotp_active = config.get("GETOTP_ENABLED") if not getotp_active: return sendError(response, "getotp is not activated.", 0) param = self.request_params ret = {} res = -1 otpval = "" passw = "" serials = [] try: serial = getParam(param, "serial", optional) user = getUserFromParam(param) curTime = getParam(param, "curTime", optional) g.audit['user'] = user.login if "" != user.login: g.audit['realm'] = user.realm or getDefaultRealm() if serial: log.debug("[getotp] retrieving OTP value for token %s", serial) elif user.login: log.debug( "[getotp] retrieving OTP value for token for user " "%s@%s", user.login, user.realm) toks = getTokens4UserOrSerial(user, serial) tokennum = len(toks) if tokennum > 1: log.debug("[getotp] The user has more than one token." "Returning the list of serials") res = -3 for token in toks: serials.append(token.getSerial()) elif 1 == tokennum: serial = toks[0].getSerial() log.debug( "[getotp] retrieving OTP for token %s for user" " %s@%s", serial, user.login, user.realm) else: log.debug("[getotp] no token found for user %s@%s", user.login, user.realm) res = -4 else: res = -5 # if a serial was given or a unique serial could be # received from the given user. if serial: max_count = checkPolicyPre('gettoken', 'max_count', param) log.debug("[getmultiotp] max_count policy: %s", max_count) if max_count <= 0: return sendError( response, "The policy forbids receiving" " OTP values for the token %s in " "this realm" % serial, 1) (res, pin, otpval, passw) = getOtp(serial, curTime=curTime) g.audit['success'] = True if int(res) < 0: ret['result'] = False if -1 == otpval: ret['description'] = "No Token with this serial number" if -2 == otpval: ret['description'] = ("This Token does not support the" " getOtp function") if -3 == otpval: ret['description'] = "The user has more than one token" ret['serials'] = serials if -4 == otpval: ret['description'] = "No Token found for this user" if -5 == otpval: ret['description'] = ("you need to provide a user or " "a serial") else: ret['result'] = True ret['otpval'] = otpval ret['pin'] = pin ret['pass'] = passw db.session.commit() return sendResult(response, ret, 0) except PolicyException as pe: log.exception("[getotp] gettoken/getotp policy failed: %r", pe) db.session.rollback() return sendError(response, str(pe), 1) except Exception as exx: log.exception("[getotp] gettoken/getotp failed: %r", exx) db.session.rollback() return sendError(response, "gettoken/getotp failed: %s" % exx, 0)
def _get_user_condition(self, user, valid_realms): ucondition = None log.debug('[TokenIterator::init] start search for username: >%r<' % (user)) if not user or user.isEmpty() or not user.login: return ucondition loginUser = user.login.lower() loginUser = loginUser.replace('"', '') loginUser = loginUser.replace("'", '') searchType = "any" ## search for a 'blank' user if len(loginUser) == 0 and len(user.login) > 0: searchType = "blank" elif loginUser == "/:no user:/" or loginUser == "/:none:/": searchType = "blank" elif loginUser == "/:no user info:/": searchType = "wildcard" elif "*" in loginUser or "." in loginUser: searchType = "wildcard" else: ## no blank and no wildcard search searchType = "exact" if searchType == "blank": log.debug('[TokenIterator::init] search for empty user: >%r<' % (user.login)) ucondition = and_( or_(Token.LinOtpUserid == u'', Token.LinOtpUserid is None)) if searchType == "exact": log.debug('[TokenIterator::init] search for exact user: %r' % (user)) serials = [] users = [] ## if search for a realmuser 'user@realm' we can take the ## realm from the argument if len(user.realm) > 0: users.append(user) else: for realm in valid_realms: users.append(User(user.login, realm)) # resolve the realm with wildcard: # identify all users and add these to the userlist userlist = [] for usr in users: urealm = usr.realm if urealm == '*': # if the realm is set to *, the getUserId # triggers the identification of all resolvers, where the # user might reside: tigger the user resolver lookup (uid, resolver, resolverClass) = getUserId(usr) userlist.extend(usr.getUserPerConf()) else: userlist.append(usr) for usr in userlist: try: tokens = getTokens4UserOrSerial(user=usr, _class=False) for tok in tokens: serials.append(tok.LinOtpTokenSerialnumber) except UserError as ex: ## we get an exception if the user is not found log.debug('[TokenIterator::init] no exact user: %r' % (user)) log.debug('[TokenIterator::init] %r' % ex) if len(serials) > 0: # if tokens found, search for their serials ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: # if no token is found, block search for user # and return nothing ucondition = and_(Token.LinOtpTokenSerialnumber == u'') ## handle case, when nothing found in former cases if searchType == "wildcard": log.debug('[TokenIterator::init] wildcard search: %r' % (user)) serials = [] users = getAllTokenUsers() logRe = None lU = loginUser.replace('*', '.*') #lU = lU.replace('..', '.') logRe = re.compile(lU) for ser in users: userInfo = users.get(ser) tokenUser = userInfo.get('username').lower() try: if logRe.match(u'' + tokenUser) is not None: serials.append(ser) except Exception as e: log.error('error no express %r ' % e) ## to prevent warning, we check is serials are found ## SAWarning: The IN-predicate on ## "Token.LinOtpTokenSerialnumber" was invoked with an ## empty sequence. This results in a contradiction, which ## nonetheless can be expensive to evaluate. Consider ## alternative strategies for improved performance. if len(serials) > 0: ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: ucondition = and_(Token.LinOtpTokenSerialnumber == u'') return ucondition
def checkstatus(self): """ method: ocra/checkstatus description: Methode zur assynchronen Ueberpruefungen eines Challenge Response Valiadation requests arguments: * transactionid: (required one of - string - (hex)) Dies ist eine Transaktions-ID, die bei der Challenge ausgegeben wurde. * serial: (required one of - string) die Serien Nummer des OCRA Token * user: (required one of - string) die Benutzer eines Tokens required is one of (user,serial,transactionid) returns: A JSON response:: { "version": "LinOTP 2.4", "jsonrpc": "2.0", "result": { "status": true, "value": [ { "serial": SERIENNUMMER1, "transactionid": TRANSACTIONID1, "received_tan": true, "valid_tan": true, "failcount": 0 }, { "serial": SERIENNUMMER1, "transactionid": TRANSACTIONID2, "received_tan": false, "valid_tan": false, "failcount": 0 }, { "serial": SERIENNUMMER2, "transactionid": TRANSACTIONID3, "received_tan": true, "valid_tan": false, "failcount": 2 }, ] }, "id": 0 } exception: """ res = {} description = 'ocra/checkstatus: check the token status - for assynchronous verification. Missing parameter: You need to provide one of the parameters "transactionid", "user" or "serial"' try: param = getLowerParams(request.params) log.debug("[checkstatus] check OCRA token status: %r" % param) checkPolicyPre('ocra', "status") transid = getParam(param, 'transactionid' , optional) user = getUserFromParam(param, optional) #user = getParam(param, 'user' ,optional) serial = getParam(param, 'serial' , optional) if transid is None and user.isEmpty() and serial is None: ## raise exception log.exception("[ocra/checkstatus] : missing transactionid, user or serial number for token") raise ParameterError("Usage: %s" % description, id=77) tokens = [] serials = set() status = [] if serial is not None: serials.add(serial) ## if we have a transaction, get serial from this challenge if transid is not None : ocraChallenge = None try: ocraChallenge = OcraTokenClass.getTransaction(transid) except: pass if ocraChallenge is not None: serials.add(ocraChallenge.tokenserial) ## if we have a serial number of token if len(serials) > 0: for serial in serials: tokens.extend(getTokens4UserOrSerial(serial=serial)) ## if we have a user if user.isEmpty() == False: try: tokens.extend(getTokens4UserOrSerial(user=user)) except: log.warning("no token or user %r found!" % user) for token in tokens: if token.getType() == 'ocra': challenges = [] if transid is None: serial = token.getSerial() challenges = OcraTokenClass.getTransactions4serial(serial) else: challenges.append(OcraTokenClass.getTransaction(transid)) for challenge in challenges: stat = token.getStatus(challenge.transid) if stat is not None and len(stat) > 0: status.append(stat) res['values'] = status c.audit['success'] = res Session.commit() return sendResult(response, res, 1) except PolicyException as pe: log.exception("[checkstatus] policy failed: %r" % pe) Session.rollback() return sendError(response, unicode(pe)) except Exception as exx: log.exception("[checkstatus] failed: %r" % exx) Session.rollback() return sendResult(response, unicode(exx), 0) finally: Session.close() log.debug('[ocra/checkstatus] done')
def request(self): """ method: ocra/request description: request a challenge for a user or for a serial number (token). arguments: * serial: (required - string) Serial number of the token, for which a challenge should be generated (either serial or user is required) * user: (required - string) The user for whose token a challenge should be generated If the user has more than one token, an error is returend. (either serial or user is required) * data: (required - String: URLendoced) These are the display data, that can be used to generate the challenge remark: the app will report a wrong qrcode, if the policy:: {'authentication' : qrtanurl=https://localhost } is not defined !! returns: A JSON respone:: { "version": "LinOTP 2.4", "jsonrpc": "2.0", "result": { "status": true, "value": false, }, "detail": { "transactionid" : TRANSAKTIONSID, "data" : DATAOBJECT, } } * transactionid: This is the transaction ID, that is used later for verifying the Return code /TAN. * data: This is an object (URL) which can be used to generate a QR-Code to be displayed to the QRTAN App """ res = {} description = 'ocra/request: request a challenge for a given user or token (serial). You must either provide a parameter "user" or a parameter "serial".' dataobj = "" try: param = getLowerParams(request.params) log.info("[request] saving default configuration: %r" % param) checkPolicyPre('ocra', "request") serial = getParam(param, 'serial', optional) user = getUserFromParam(param, optional) if user.isEmpty() and serial is None: ## raise exception log.exception("[request] user or serial is required") raise ParameterError("Usage: %s" % description, id=77) message = getParam(param, 'data' , optional) if message is None: message = '' ## ocra token tokens = getTokens4UserOrSerial(user, serial) if len(tokens) > 1 : error = ('More than one token found: unable to create challenge ' 'for (u:%r,s:%r)!' % (user, serial)) log.error(error) raise Exception(error) if len(tokens) == 0: error = ('No token found: unable to create challenge for' ' (u:%r,s:%r)!' % (user, serial)) log.error(error) raise Exception(error) ocra = tokens[0] (transId, challenge, res, url) = ocra.challenge(message) u = urlencode({'u':str(url.encode("utf-8"))}) uInfo = {'tr': transId, 'ch' : challenge, 'me': str(message.encode("utf-8")), 'u': u[2:]} detail = {"transactionid" : transId, 'challenge' : challenge, 'message' : str(message.encode("utf-8")), 'url' : str(url.encode("utf-8")), } ## create the app_url from the data''' dataobj = 'lseqr://req?%s' % (str(urlencode(uInfo))) ## append the signature to the url ''' signature = {'si' : ocra.signData(dataobj)} uInfo['si'] = signature dataobj = '%s&%s' % (dataobj, str(urlencode(signature))) detail["data"] = dataobj c.audit['success'] = res #c.audit['info'] += "%s=%s, " % (k, value) Session.commit() qr = getParam(param, 'qr', optional) if qr is not None: param['alt'] = detail return sendQRImageResult(response, dataobj, param) else: return sendResult(response, res, 1, opt=detail) except PolicyException as pe: log.exception("[request] policy failed: %r" % pe) Session.rollback() return sendError(response, unicode(pe)) except Exception as exx: log.exception("[request] failed: %r" % exx) Session.rollback() return sendError(response, unicode(exx)) finally: Session.close() log.debug("[request] done")
def check_status(self, transid=None, user=None, serial=None, password=None, use_offline=False): """ check for open transactions - for polling support :param transid: the transaction id where we request the status from :param user: the token owner user :param serial: or the serial we are searching for :param password: the pin/password for authorization the request :param use_offline: on success the offline info is returned :return: tuple of success and detail dict """ expired, challenges = Challenges.get_challenges(None, transid=transid) # remove all expired challenges if expired: Challenges.delete_challenges(None, expired) if not challenges: return False, None # there is only one challenge per transaction id # if not multiple challenges, where transaction id is the parent one reply = {} pin_policies = get_pin_policies(user) if 1 in pin_policies: pin_match = check_pin(None, password, user=user, options=None) if not pin_match: return False, None involved_tokens = [] transactions = {} for ch in challenges: # only look for challenges that are not compromised if not Challenges.verify_checksum(ch): continue # is the requester authorized serial = ch.getTokenSerial() tokens = getTokens4UserOrSerial(serial=serial) if not tokens: continue involved_tokens.extend(tokens) # as one challenge belongs exactly to only one token, # we take this one as the token token = tokens[0] if 1 not in pin_policies: pin_match = check_pin(token, password, user=user, options=None) if not pin_match: ret = False continue ret = True trans_dict = {} trans_dict['received_count'] = ch.received_count trans_dict['received_tan'] = ch.received_tan trans_dict['valid_tan'] = ch.valid_tan trans_dict['message'] = ch.challenge trans_dict['status'] = ch.getStatus() token_dict = {'serial': serial, 'type': token.type} # 1. check if token supports offline at all supports_offline_at_all = token.supports_offline_mode # 2. check if policy allows to use offline authentication if user is not None and user.login and user.realm: realms = [user.realm] else: realms = token.getRealms() offline_is_allowed = supports_offline(realms, token) if not ch.is_open() and ch.valid_tan and \ supports_offline_at_all and \ offline_is_allowed and \ use_offline: token_dict['offline_info'] = token.getOfflineInfo() trans_dict['token'] = token_dict transactions[ch.transid] = trans_dict if transactions: reply['transactions'] = transactions return ret, reply
def __init__(self, user, serial, page=None, psize=None, filter=None, sort=None, sortdir=None, filterRealm=None, user_fields=None, params=None): ''' constructor of Tokeniterator, which gathers all conditions to build a sqalchemy query - iterator :param user: User object - user provides as well the searchfield entry :type user: User class :param serial: serial number of a token :type serial: string :param page: page number :type page: int :param psize: how many entries per page :type psize: int :param filter: additional condition :type filter: string :param sort: sort field definition :type sort: string :param sortdir: sort direction: ascending or descending :type sortdir: string :param filterRealm: restrict the set of token to those in the filterRealm :type filterRealm: string or list :param user_fields: list of additional fields from the user owner :type user_fields: array :param params: list of additional request parameters - currently not used :type params: dict :return: - nothing / None ''' log.debug('[TokenIterator::init] begin. start creating TokenIterator \ class with parameters: user:%r, serial:%r, page=%r, psize:%r, \ filter:%r, sort:%r, sortdir:%r, filterRealm:%r' % (user, serial, page, psize, filter, sort, sortdir, filterRealm)) if params is None: params = {} self.page = 1 self.pages = 1 self.tokens = 0 self.user_fields = user_fields if self.user_fields == None: self.user_fields = [] condition = None ucondition = None scondition = None r_condition = None if type(filterRealm) in (str, unicode): filterRealm = filterRealm.split(',') if type(filterRealm) in [list]: s_realms = [] for f in filterRealm: # support for multiple realm filtering in the ui # as a coma separated string for s_realm in f.split(','): s_realms.append(s_realm.strip()) filterRealm = s_realms # create a list of all realms, which are allowed to be searched # based on the list of the existing ones valid_realms = [] realms = getRealms().keys() if '*' in filterRealm: valid_realms.append("*") else: for realm in realms: if realm in filterRealm: realm = linotp.lib.crypt.uencode(realm) valid_realms.append(realm) if serial is not None: # check if the requested serial is in the realms of # the admin (filterRealm) log.debug('[TokenIterator::init] start search for serial: >%r<' % (serial)) allowed = False if filterRealm == ['*']: allowed = True else: realms = getTokenRealms(serial) for realm in realms: if realm in filterRealm: allowed = True if allowed == True: if '*' in serial: serial = serial.replace('*', '%') scondition = and_(Token.LinOtpTokenSerialnumber.like(serial)) else: scondition = and_(Token.LinOtpTokenSerialnumber == serial) if user.isEmpty() == False and user is not None: log.debug('[TokenIterator::init] start search for username: >%r<' % (user)) if user.login is not None and (user.login) > 0 : loginUser = user.login.lower() loginUser = loginUser.replace('"', '') loginUser = loginUser.replace("'", '') searchType = "any" # search for a 'blank' user if len(loginUser) == 0 and len(user.login) > 0: searchType = "blank" elif loginUser == "/:no user:/" or loginUser == "/:none:/": searchType = "blank" elif loginUser == "/:no user info:/": searchType = "wildcard" elif "*" in loginUser or "." in loginUser: searchType = "wildcard" else: # no blank and no wildcard search searchType = "exact" if searchType == "blank": log.debug('[TokenIterator::init] search for empty user:'******' >%r<' % (user.login)) ucondition = and_(or_(Token.LinOtpUserid == u'', Token.LinOtpUserid == None)) if searchType == "exact": log.debug('[TokenIterator::init] search for exact user:'******' %r' % (user)) serials = [] users = [] # if search for a realmuser 'user@realm' we can take the # realm from the argument if len(user.realm) > 0: users.append(user) else: for realm in valid_realms: users.append(User(user.login, realm)) # resolve the realm with wildcard: # identify all users and add these to the userlist userlist = [] for usr in users: urealm = usr.realm if urealm == '*': # if the realm is set to *, the getUserId # triggers the identification of all resolvers, # where the user might reside: tigger the user # resolver lookup (uid, resolver, resolverClass) = getUserId(usr) userlist.extend(usr.getUserPerConf()) else: userlist.append(usr) for usr in userlist: try: tokens = getTokens4UserOrSerial(user=usr, _class=False) for tok in tokens: serials.append(tok.LinOtpTokenSerialnumber) except UserError as ex: # we get an exception if the user is not found log.debug('[TokenIterator::init] no exact user: %r' % (user)) log.debug('[TokenIterator::init] %r' % ex) if len(serials) > 0: # if tokens found, search for their serials ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: # if no token is found, block search for user # and return nothing ucondition = and_(Token.LinOtpTokenSerialnumber == u'') # handle case, when nothing found in former cases if searchType == "wildcard": log.debug('[TokenIterator::init] wildcard search: %r' % (user)) serials = [] users = getAllTokenUsers() logRe = None lU = loginUser.replace('*', '.*') # lU = lU.replace('..', '.') logRe = re.compile(lU) for ser in users: userInfo = users.get(ser) tokenUser = userInfo.get('username').lower() try: if logRe.match(u'' + tokenUser) is not None: serials.append(ser) except Exception as e: log.error('error no express %r ' % e) # to prevent warning, we check is serials are found # SAWarning: The IN-predicate on # "Token.LinOtpTokenSerialnumber" was invoked with an # empty sequence. This results in a contradiction, which # nonetheless can be expensive to evaluate. Consider # alternative strategies for improved performance. if len(serials) > 0: ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: ucondition = and_(Token.LinOtpTokenSerialnumber == u'') if filter is None: condition = None elif filter in ['/:active:/', '/:enabled:/', '/:token is active:/', '/:token is enabled:/' ]: condition = and_(Token.LinOtpIsactive == True) elif filter in ['/:inactive:/', '/:disabled:/', '/:token is inactive:/', '/:token is disabled:/']: condition = and_(Token.LinOtpIsactive == False) else: # search in other colums filter = linotp.lib.crypt.uencode(filter) condition = or_(Token.LinOtpTokenDesc.contains(filter), Token.LinOtpIdResClass.contains(filter), Token.LinOtpTokenSerialnumber.contains(filter), Token.LinOtpUserid.contains(filter), Token.LinOtpTokenType.contains(filter)) ################################################################### # The condition for only getting certain realms! if '*' in valid_realms: log.debug("[TokenIterator::init] wildcard for realm '*' found." " Tokens of all realms will be displayed") elif len(valid_realms) > 0: log.debug("[TokenIterator::init] adding filter condition" " for realm %r" % valid_realms) # get all matching realms realm_id_tuples = Session.query(Realm.id).\ filter(Realm.name.in_(valid_realms)).all() realm_ids = set() for realm_tuple in realm_id_tuples: realm_ids.add(realm_tuple[0]) # get all tokenrealm ids token_id_tuples = Session.query(TokenRealm.token_id).\ filter(TokenRealm.realm_id.in_(realm_ids)).all() token_ids = set() for token_tuple in token_id_tuples: token_ids.add(token_tuple[0]) # define the token id condition r_condition = and_(Token.LinOtpTokenId.in_(token_ids)) elif ("''" in filterRealm or '""' in filterRealm or "/:no realm:/" in filterRealm): log.debug("[TokenIterator::init] search for all tokens, which are" " in no realm") # get all tokenrealm ids token_id_tuples = Session.query(TokenRealm.token_id).all() token_ids = set() for token_tuple in token_id_tuples: token_ids.add(token_tuple[0]) # define the token id not condition r_condition = and_(not_(Token.LinOtpTokenId.in_(token_ids))) # create the final condition as AND of all conditions condTuple = () for conn in (condition, ucondition, scondition, r_condition): if type(conn).__name__ != 'NoneType': condTuple += (conn,) condition = and_(*condTuple) order = Token.LinOtpTokenDesc if sort == "TokenDesc": order = Token.LinOtpTokenDesc elif sort == "TokenId": order = Token.LinOtpTokenId elif sort == "TokenType": order = Token.LinOtpTokenType elif sort == "TokenSerialnumber": order = Token.LinOtpTokenSerialnumber elif sort == "TokenType": order = Token.LinOtpTokenType elif sort == "IdResClass": order = Token.LinOtpIdResClass elif sort == "IdResolver": order = Token.LinOtpIdResolver elif sort == "Userid": order = Token.LinOtpUserid elif sort == "FailCount": order = Token.LinOtpFailCount elif sort == "Userid": order = Token.LinOtpUserid elif sort == "Isactive": order = Token.LinOtpIsactive # care for the result sort order if sortdir is not None and sortdir == "desc": order = order.desc() else: order = order.asc() # care for the result pageing if page is None: self.toks = Session.query(Token).filter(condition).order_by(order).distinct() self.tokens = self.toks.count() log.debug("[TokenIterator] DB-Query returned # of objects:" " %i" % self.tokens) self.pagesize = self.tokens self.it = iter(self.toks) return try: if psize is None: pagesize = int(getFromConfig("pagesize", 50)) else: pagesize = int(psize) except: pagesize = 20 try: thePage = int (page) - 1 except: thePage = 0 if thePage < 0: thePage = 0 start = thePage * pagesize stop = (thePage + 1) * pagesize self.toks = Session.query(Token).filter(condition).\ order_by(order).distinct() self.tokens = self.toks.count() log.debug("[TokenIterator::init] DB-Query returned # of objects:" " %i" % self.tokens) self.page = thePage + 1 fpages = float(self.tokens) / float(pagesize) self.pages = int(fpages) if fpages - int(fpages) > 0: self.pages = self.pages + 1 self.pagesize = pagesize self.toks = self.toks.slice(start, stop) self.it = iter(self.toks) log.debug('[TokenIterator::init] end. Token iterator created: %r' % \ (self.it)) return
def check_status(self, transid=None, user=None, serial=None, password=None, use_offline=False): """ check for open transactions - for polling support :param transid: the transaction id where we request the status from :param user: the token owner user :param serial: or the serial we are searching for :param password: the pin/password for authorization the request :param use_offline: on success the offline info is returned :return: tuple of success and detail dict """ expired, challenges = Challenges.get_challenges(token=None, transid=transid) # remove all expired challenges if expired: Challenges.delete_challenges(None, expired) if not challenges: return False, None # there is only one challenge per transaction id # if not multiple challenges, where transaction id is the parent one reply = {} involved_tokens = [] transactions = {} for ch in challenges: # only look for challenges that are not compromised if not Challenges.verify_checksum(ch): continue # is the requester authorized challenge_serial = ch.getTokenSerial() if serial and challenge_serial != serial: continue tokens = getTokens4UserOrSerial(serial=challenge_serial) if not tokens: continue involved_tokens.extend(tokens) # as one challenge belongs exactly to only one token, # we take this one as the token token = tokens[0] owner = get_token_owner(token) if user and user != owner: continue involved_tokens.extend(tokens) # we only check the user password / token pin if the user # paranmeter is given if user and owner: pin_match = check_pin(token, password, user=owner, options=None) else: pin_match = token.checkPin(password) if not pin_match: continue trans_dict = {} trans_dict['received_count'] = ch.received_count trans_dict['received_tan'] = ch.received_tan trans_dict['valid_tan'] = ch.valid_tan trans_dict['message'] = ch.getChallenge() trans_dict['status'] = ch.getStatus() # -------------------------------------------------------------- -- # extend the check status with the accept or deny of a transaction challenge_session = ch.getSession() if challenge_session: challenge_session_dict = json.loads(challenge_session) if 'accept' in challenge_session_dict: trans_dict['accept'] = challenge_session_dict['accept'] if 'reject' in challenge_session_dict: trans_dict['reject'] = challenge_session_dict['reject'] # -------------------------------------------------------------- -- token_dict = {'serial': token.getSerial(), 'type': token.type} # 1. check if token supports offline at all supports_offline_at_all = token.supports_offline_mode # 2. check if policy allows to use offline authentication if user is not None and user.login and user.realm: realms = [user.realm] else: realms = token.getRealms() offline_is_allowed = supports_offline(realms, token) if not ch.is_open() and ch.valid_tan and \ supports_offline_at_all and \ offline_is_allowed and \ use_offline: token_dict['offline_info'] = token.getOfflineInfo() trans_dict['token'] = token_dict transactions[ch.transid] = trans_dict if transactions: reply['transactions'] = transactions return len(reply) > 0, reply
def check_status(self): """ check the status of a transaction - for polling support """ try: param = self.request_params # # we require either state or transactionid as parameter transid = param.get("state", param.get("transactionid", None)) if not transid: raise ParameterError( _( 'Missing required parameter "state" or ' '"transactionid"!' ) ) # # serial is an optional parameter serial = param.get("serial", None) # user is an optional parameter: # if no 'user' in the parameters, the User object will be empty user = getUserFromParam(param) passw = param.get("pass") if passw is None: raise ParameterError(_('Missing required parameter "pass"!')) use_offline = param.get("use_offline", False) va = ValidationHandler() ok, opt = va.check_status( transid=transid, user=user, serial=serial, password=passw, use_offline=use_offline, ) serials = [] types = [] owner = None challenges = Challenges.lookup_challenges(transid=transid) for ch in challenges: tokens = getTokens4UserOrSerial(serial=ch.getTokenSerial()) if not tokens: continue for token in tokens: serials.append(token.getSerial()) types.append(token.getType()) if not owner: owner = get_token_owner(token) if owner: g.audit["user"] = g.audit["user"] or owner.login g.audit["realm"] = g.audit["realm"] or owner.realm g.audit["serial"] = " ".join(serials) g.audit["token_type"] = " ".join(types) g.audit["success"] = ok g.audit["info"] = str(opt) db.session.commit() return sendResult(response, ok, 0, opt=opt) except Exception as exx: log.error("check_status failed: %r", exx) g.audit["info"] = str(exx) db.session.rollback() return sendResult(response, False, 0)
def checkstatus(self): """ method: ocra/checkstatus description: Methode zur assynchronen Ueberpruefungen eines Challenge Response Valiadation requests arguments: * transactionid: (required one of - string - (hex)) Dies ist eine Transaktions-ID, die bei der Challenge ausgegeben wurde. * serial: (required one of - string) die Serien Nummer des OCRA Token * user: (required one of - string) die Benutzer eines Tokens required is one of (user,serial,transactionid) returns: A JSON response:: { "version": "LinOTP 2.4", "jsonrpc": "2.0", "result": { "status": true, "value": [ { "serial": SERIENNUMMER1, "transactionid": TRANSACTIONID1, "received_tan": true, "valid_tan": true, "failcount": 0 }, { "serial": SERIENNUMMER1, "transactionid": TRANSACTIONID2, "received_tan": false, "valid_tan": false, "failcount": 0 }, { "serial": SERIENNUMMER2, "transactionid": TRANSACTIONID3, "received_tan": true, "valid_tan": false, "failcount": 2 }, ] }, "id": 0 } exception: """ res = {} description = ('ocra/checkstatus: check the token status - ' 'for assynchronous verification. Missing parameter: ' 'You need to provide one of the parameters ' '"transactionid", "user" or "serial"') try: param = getLowerParams(request.params) log.debug("[checkstatus] check OCRA token status: %r" % param) checkPolicyPre('ocra', "status") transid = param.get('transactionid') user = getUserFromParam(param) serial = param.get('serial') if transid is None and user.is_empty and serial is None: # raise exception log.exception("[ocra/checkstatus] : missing transactionid, user or serial number for token") raise ParameterError("Usage: %s" % description, id=77) tokens = [] serials = set() status = [] if serial is not None: serials.add(serial) # if we have a transaction, get serial from this challenge if transid is not None: ocraChallenge = None try: ocraChallenge = OcraTokenClass.getTransaction(transid) except: pass if ocraChallenge is not None: serials.add(ocraChallenge.tokenserial) # if we have a serial number of token if len(serials) > 0: for serial in serials: tokens.extend(getTokens4UserOrSerial(serial=serial)) # if we have a user if not user.is_empty: try: tokens.extend(getTokens4UserOrSerial(user=user)) except: log.warning("no token or user %r found!" % user) for token in tokens: if token.getType() == 'ocra': challenges = [] if transid is None: serial = token.getSerial() challenges = OcraTokenClass.getTransactions4serial( serial) else: challenges.append(OcraTokenClass.getTransaction( transid)) for challenge in challenges: stat = token.getStatus(challenge.transid) if stat is not None and len(stat) > 0: status.append(stat) res['values'] = status c.audit['success'] = res Session.commit() return sendResult(response, res, 1) except PolicyException as pe: log.exception("[checkstatus] policy failed: %r" % pe) Session.rollback() return sendError(response, unicode(pe)) except Exception as exx: log.exception("[checkstatus] failed: %r" % exx) Session.rollback() return sendResult(response, unicode(exx), 0) finally: Session.close()
def check_t(self): """ method: ocra/check_t description: verify the response of the ocra token arguments: * transactionid: (required - string) Dies ist eine Transaktions-ID, die bei der Challenge ausgegeben wurde. * pass: (required - string) die response, die der OCRA Token auf Grund der Challenge berechnet hat returns: A JSON response:: { "version": "LinOTP 2.4", "jsonrpc": "2.0", "result": { "status": true, "value": { "failcount" : 3, "result": false } }, "id": 0 } exception: """ res = {} description = 'ocra/check_t: validate a token request.' try: param = getLowerParams(request.params) log.info("[check_t] check OCRA token: %r" % param) # TODO: checkPolicyPre('ocra', "check_t") passw = param.get('pass') if passw is None: # raise exception''' log.exception("[check_t] missing pass ") raise ParameterError("Usage: %s Missing parameter " "'pass'." % description, id=77) transid = param.get('transactionid') if not transid: # raise exception log.exception("[check_t] missing transactionid, user or " "serial number of token") raise ParameterError("Usage: %s Missing parameter " "'transactionid'." % description, id=77) # if we have a transaction, get serial from this challenge value = {} ocraChallenge = OcraTokenClass.getTransaction(transid) if ocraChallenge is not None: serial = ocraChallenge.tokenserial tokens = getTokens4UserOrSerial(serial=serial) if len(tokens) == 0 or len(tokens) > 1: raise Exception('tokenmismatch for token serial: %s' % (unicode(serial))) theToken = tokens[0] tok = theToken.token desc = tok.get() realms = desc.get('LinOtp.RealmNames') if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo(tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass) user = User(login=userInfo.get('username'), realm=realm) vh = ValidationHandler() (ok, _opt) = vh.checkSerialPass( serial, passw, user=user, options={'transactionid': transid}) failcount = theToken.getFailCount() value['result'] = ok value['failcount'] = int(failcount) else: # no challenge found for this transid value['result'] = False value['failure'] = ('No challenge for transaction %r found' % transid) c.audit['success'] = res Session.commit() return sendResult(response, value, 1) except Exception as exx: log.exception("[check_t] failed: %r", exx) Session.rollback() return sendResult(response, unicode(exx), 0) finally: Session.close()
def request(self): """ method: ocra/request description: request a challenge for a user or for a serial number (token). arguments: * serial: (required - string) Serial number of the token, for which a challenge should be generated (either serial or user is required) * user: (required - string) The user for whose token a challenge should be generated If the user has more than one token, an error is returend. (either serial or user is required) * data: (required - String: URLendoced) These are the display data, that can be used to generate the challenge remark: the app will report a wrong qrcode, if the policy:: {'authentication' : qrtanurl=https://localhost } is not defined !! returns: A JSON respone:: { "version": "LinOTP 2.4", "jsonrpc": "2.0", "result": { "status": true, "value": false, }, "detail": { "transactionid" : TRANSAKTIONSID, "data" : DATAOBJECT, } } * transactionid: This is the transaction ID, that is used later for verifying the Return code /TAN. * data: This is an object (URL) which can be used to generate a QR-Code to be displayed to the QRTAN App """ res = {} description = ('ocra/request: request a challenge for a given user or' ' token (serial). You must either provide a parameter ' '"user" or a parameter "serial".') dataobj = "" try: param = getLowerParams(request.params) log.info("[request] saving default configuration: %r" % param) checkPolicyPre('ocra', "request") serial = param.get('serial') user = getUserFromParam(param) if user.is_empty and serial is None: log.exception("[request] user or serial is required") raise ParameterError("Usage: %s" % description, id=77) if not serial: if not user.exists(): raise UserError("getUserId failed: no user >%s< found!" % user.login, id=1205) message = param.get('data') if message is None: message = '' # ocra token tokens = getTokens4UserOrSerial(user, serial) if len(tokens) > 1: error = ('More than one token found: unable to create ' 'challenge for (u:%r,s:%r)!' % (user, serial)) raise Exception(error) if len(tokens) == 0: error = ('No token found: unable to create challenge for' ' (u:%r,s:%r)!' % (user, serial)) raise Exception(error) ocra = tokens[0] (transId, challenge, res, url) = ocra.challenge(message) u = urlencode({'u': str(url.encode("utf-8"))}) uInfo = {'tr': transId, 'ch': challenge, 'me': str(message.encode("utf-8")), 'u': u[2:]} detail = {"transactionid": transId, 'challenge': challenge, 'message': str(message.encode("utf-8")), 'url': str(url.encode("utf-8")), } # create the app_url from the data dataobj = 'lseqr://req?%s' % (str(urlencode(uInfo))) # append the signature to the url signature = {'si': ocra.signData(dataobj)} uInfo['si'] = signature dataobj = '%s&%s' % (dataobj, str(urlencode(signature))) detail["data"] = dataobj c.audit['success'] = res Session.commit() qr = param.get('qr') if qr is not None: param['alt'] = detail return sendQRImageResult(response, dataobj, param) else: return sendResult(response, res, 1, opt=detail) except PolicyException as pe: log.exception("[request] policy failed: %r" % pe) Session.rollback() return sendError(response, unicode(pe)) except Exception as exx: log.exception("[request] failed: %r" % exx) Session.rollback() return sendError(response, unicode(exx)) finally: Session.close()
def check_status(self, transid=None, user=None, serial=None, password=None): """ check for open transactions - for polling support :param transid: the transaction id where we request the status from :param user: the token owner user :param serial: or the serial we are searching for :param password: the pin/password for authorization the request :return: tuple of success and detail dict """ expired, challenges = Challenges.get_challenges(None, transid=transid) # remove all expired challenges if expired: Challenges.delete_challenges(None, expired) if not challenges: return False, None # there is only one challenge per transaction id # if not multiple challenges, where transaction id is the parent one reply = {} pin_policies = linotp.lib.policy.get_pin_policies(user) if 1 in pin_policies: pin_match = check_pin(None, password, user=user, options=None) if not pin_match: return False, None involved_tokens = [] transactions = {} for ch in challenges: # only look for challenges that are not compromised if not Challenges.verify_checksum(ch): continue # is the requester authorized serial = ch.getTokenSerial() tokens = getTokens4UserOrSerial(serial=serial) if not tokens: continue involved_tokens.extend(tokens) if 1 not in pin_policies: pin_match = check_pin(tokens[0], password, user=user, options=None) if not pin_match: ret = False continue ret = True trans_dict = {} trans_dict['transactionid'] = ch.transid trans_dict['received_count'] = ch.received_count trans_dict['received_tan'] = ch.received_tan trans_dict['valid_tan'] = ch.valid_tan trans_dict['linotp_tokenserial'] = serial trans_dict['linotp_tokentype'] = tokens[0].type trans_dict['message'] = ch.challenge transactions[serial] = trans_dict if transactions: reply['transactions'] = transactions return ret, reply
def check_status(self, transid=None, user=None, serial=None, password=None, use_offline=False): """ check for open transactions - for polling support :param transid: the transaction id where we request the status from :param user: the token owner user :param serial: or the serial we are searching for :param password: the pin/password for authorization the request :param use_offline: on success the offline info is returned :return: tuple of success and detail dict """ expired, challenges = Challenges.get_challenges(token=None, transid=transid) # remove all expired challenges if expired: Challenges.delete_challenges(None, expired) if not challenges: return False, None # there is only one challenge per transaction id # if not multiple challenges, where transaction id is the parent one reply = {} involved_tokens = [] transactions = {} for ch in challenges: # only look for challenges that are not compromised if not Challenges.verify_checksum(ch): continue # is the requester authorized challenge_serial = ch.getTokenSerial() if serial and challenge_serial != serial: continue tokens = getTokens4UserOrSerial(serial=challenge_serial) if not tokens: continue involved_tokens.extend(tokens) # as one challenge belongs exactly to only one token, # we take this one as the token token = tokens[0] owner = get_token_owner(token) if user and user != owner: continue involved_tokens.extend(tokens) # we only check the user password / token pin if the user # paranmeter is given if user and owner: pin_match = check_pin(token, password, user=owner, options=None) else: pin_match = token.checkPin(password) if not pin_match: continue trans_dict = {} trans_dict['received_count'] = ch.received_count trans_dict['received_tan'] = ch.received_tan trans_dict['valid_tan'] = ch.valid_tan trans_dict['message'] = ch.challenge trans_dict['status'] = ch.getStatus() # -------------------------------------------------------------- -- # extend the check status with the accept or deny of a transaction challenge_session = ch.getSession() if challenge_session: challenge_session_dict = json.loads(challenge_session) if 'accept' in challenge_session_dict: trans_dict['accept'] = challenge_session_dict['accept'] if 'reject' in challenge_session_dict: trans_dict['reject'] = challenge_session_dict['reject'] # -------------------------------------------------------------- -- token_dict = {'serial': token.getSerial(), 'type': token.type} # 1. check if token supports offline at all supports_offline_at_all = token.supports_offline_mode # 2. check if policy allows to use offline authentication if user is not None and user.login and user.realm: realms = [user.realm] else: realms = token.getRealms() offline_is_allowed = supports_offline(realms, token) if not ch.is_open() and ch.valid_tan and \ supports_offline_at_all and \ offline_is_allowed and \ use_offline: token_dict['offline_info'] = token.getOfflineInfo() trans_dict['token'] = token_dict transactions[ch.transid] = trans_dict if transactions: reply['transactions'] = transactions return len(reply) > 0, reply
def check_t(self): param = {} value = {} ok = False opt = None try: param.update(request.params) passw = getParam(param, "pass", required) transid = param.get('state', None) if transid is not None: param['transactionid'] = transid del param['state'] if transid is None: transid = param.get('transactionid', None) if transid is None: raise Exception("missing parameter: state or transactionid!") serial = get_tokenserial_of_transaction(transId=transid) if serial is None: value['value'] = False value['failure'] = 'No challenge for transaction %r found'\ % transid else: param['serial'] = serial tokens = getTokens4UserOrSerial(serial=serial) if len(tokens) == 0 or len(tokens) > 1: raise Exception('tokenmismatch for token serial: %s' % (unicode(serial))) theToken = tokens[0] tok = theToken.token realms = tok.getRealmNames() if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo(tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass) user = User(login=userInfo.get('username'), realm=realm) (ok, opt) = checkSerialPass(serial, passw, user=user, options=param) value['value'] = ok failcount = theToken.getFailCount() value['failcount'] = int(failcount) c.audit['success'] = ok #c.audit['info'] += "%s=%s, " % (k, value) Session.commit() qr = getParam(param, 'qr', optional) if qr is not None and opt is not None and opt.has_key('message'): try: dataobj = opt.get('message') param['alt'] = "%s" % opt return sendQRImageResult(response, dataobj, param) except Exception as exc: log.warning("failed to send QRImage: %r " % exc) return sendQRImageResult(response, opt, param) else: return sendResult(response, value, 1, opt=opt) except Exception as exx: log.error("[check_t] validate/check_t failed: %r" % exx) log.error("[check_t] %s" % traceback.format_exc()) c.audit['info'] = unicode(exx) Session.rollback() return sendError(response, "validate/check_t failed: %s" % unicode(exx), 0) finally: Session.close() log.debug('[check_t] done')
def getotp(self): ''' This function is used to retrieve the current otp value for a given user or a given serial If the user has more than one token, the list of the tokens is returend. method: gettoken/getotp arguments: user - username / loginname realm - additional realm to match the user to a useridresolver serial - the serial number of the token curTime - used ONLY for internal testing: datetime.datetime object returns: JSON response ''' getotp_active = config.get("linotpGetotp.active") if "True" != getotp_active: return sendError(response, "getotp is not activated.", 0) param = request.params ret = {} res = -1 otpval = "" passw = "" serials = [] try: serial = getParam(param, "serial", optional) user = getUserFromParam(param, optional) curTime = getParam(param, "curTime", optional) c.audit['user'] = user.login if "" != user.login: c.audit['realm'] = user.realm or getDefaultRealm() if serial: log.debug("[getotp] retrieving OTP value for token %s" % serial) elif user.login: log.debug("[getotp] retrieving OTP value for token for user %s@%s" % (user.login, user.realm)) toks = getTokens4UserOrSerial(user, serial) tokennum = len(toks) if tokennum > 1: log.debug("[getotp] The user has more than one token. Returning the list of serials") res = -3 for token in toks: serials.append(token.getSerial()) elif 1 == tokennum: serial = toks[0].getSerial() log.debug("[getotp] retrieving OTP for token %s for user %s@%s" % (serial, user.login, user.realm)) else: log.debug("[getotp] no token found for user %s@%s" % (user.login, user.realm)) res = -4 else: res = -5 # if a serial was given or a unique serial could be received from the given user. if serial: max_count = checkPolicyPre('gettoken', 'max_count', param) log.debug("[getmultiotp] checkpolicypre returned %s" % max_count) if max_count <= 0: return sendError(response, "The policy forbids receiving OTP values for the token %s in this realm" % serial , 1) (res, pin, otpval, passw) = getOtp(serial, curTime=curTime) c.audit['success'] = True if int(res) < 0: ret['result'] = False if -1 == otpval: ret['description'] = "No Token with this serial number" if -2 == otpval: ret['description'] = "This Token does not support the getOtp function" if -3 == otpval: ret['description'] = "The user has more than one token" ret['serials'] = serials if -4 == otpval: ret['description'] = "No Token found for this user" if -5 == otpval: ret['description'] = "you need to provide a user or a serial" else: ret['result'] = True ret['otpval'] = otpval ret['pin'] = pin ret['pass'] = passw Session.commit() return sendResult(response, ret , 0) except PolicyException as pe: log.exception("[getotp] gettoken/getotp policy failed: %r" % pe) Session.rollback() return sendError(response, unicode(pe), 1) except Exception as e: log.exception("[getotp] gettoken/getotp failed: %r" % e) Session.rollback() return sendError(response, "gettoken/getotp failed: %s" % unicode(e), 0) finally: Session.close() log.debug('[getotp] done')
def check_status(self, transid=None, user=None, serial=None, password=None, use_offline=False): """ check for open transactions - for polling support :param transid: the transaction id where we request the status from :param user: the token owner user :param serial: or the serial we are searching for :param password: the pin/password for authorization the request :param use_offline: on success the offline info is returned :return: tuple of success and detail dict """ expired, challenges = Challenges.get_challenges(None, transid=transid) # remove all expired challenges if expired: Challenges.delete_challenges(None, expired) if not challenges: return False, None # there is only one challenge per transaction id # if not multiple challenges, where transaction id is the parent one reply = {} pin_policies = linotp.lib.policy.get_pin_policies(user) if 1 in pin_policies: pin_match = check_pin(None, password, user=user, options=None) if not pin_match: return False, None involved_tokens = [] transactions = {} for ch in challenges: # only look for challenges that are not compromised if not Challenges.verify_checksum(ch): continue # is the requester authorized serial = ch.getTokenSerial() tokens = getTokens4UserOrSerial(serial=serial) if not tokens: continue involved_tokens.extend(tokens) # as one challenge belongs exactly to only one token, # we take this one as the token token = tokens[0] if 1 not in pin_policies: pin_match = check_pin(token, password, user=user, options=None) if not pin_match: ret = False continue ret = True trans_dict = {} trans_dict['received_count'] = ch.received_count trans_dict['received_tan'] = ch.received_tan trans_dict['valid_tan'] = ch.valid_tan trans_dict['message'] = ch.challenge trans_dict['status'] = ch.getStatus() token_dict = {'serial': serial, 'type': token.type} # 1. check if token supports offline at all supports_offline_at_all = token.supports_offline_mode # 2. check if policy allows to use offline authentication if user is not None and user.login and user.realm: realms = [user.realm] else: realms = token.getRealms() offline_is_allowed = supports_offline(realms, token) if not ch.is_open() and ch.valid_tan and \ supports_offline_at_all and \ offline_is_allowed and \ use_offline: token_dict['offline_info'] = token.getOfflineInfo() trans_dict['token'] = token_dict transactions[ch.transid] = trans_dict if transactions: reply['transactions'] = transactions return ret, reply
def check_by_transactionid(self, transid, passw, options=None): """ check the passw against the open transaction :param transid: the transaction id :param passw: the pass parameter :param options: the additional optional parameters :return: tuple of boolean and detail dict """ reply = {} serials = [] challenges = Challenges.lookup_challenges(transid=transid) for challenge in challenges: serials.append(challenge.tokenserial) if not serials: reply["value"] = False reply["failure"] = ("No challenge for transaction %r found" % transid) return False, reply ok = False reply["failcount"] = 0 reply["value"] = False reply["token_type"] = "" token_type = options.get("token_type", None) for serial in serials: tokens = getTokens4UserOrSerial(serial=serial, token_type=token_type, read_for_update=True) if not tokens and token_type: continue if not tokens and not token_type: raise Exception("tokenmismatch for token serial: %r" % serial) # there could be only one token = tokens[0] owner = get_token_owner(token) (ok, opt) = self.checkTokenList(tokens, passw, user=owner, options=options) if opt: reply.update(opt) reply["value"] = ok reply["token_type"] = token.getType() reply["failcount"] = token.getFailCount() reply["serial"] = token.getSerial() if ok: break return ok, reply
def _get_user_condition(self, user, valid_realms): ucondition = None if not user or user.is_empty or not user.login: return ucondition loginUser = user.login.lower() loginUser = loginUser.replace('"', '') loginUser = loginUser.replace("'", '') searchType = "any" ## search for a 'blank' user if len(loginUser) == 0 and len(user.login) > 0: searchType = "blank" elif loginUser == "/:no user:/" or loginUser == "/:none:/": searchType = "blank" elif loginUser == "/:no user info:/": searchType = "wildcard" elif "*" in loginUser: searchType = "wildcard" else: ## no blank and no wildcard search searchType = "exact" if searchType == "blank": ucondition = and_(or_(Token.LinOtpUserid == u'', Token.LinOtpUserid is None)) if searchType == "exact": serials = [] users = [] # if search for a realmuser 'user@realm' we can take the # realm from the argument if len(user.realm) > 0: users.append(user) else: # otherwise we add all users which are possible combinations # from loginname and entry of the valid realms. # In case of a '*' wildcard in the list, we take all available # realms if '*' in valid_realms: valid_realm_list = getRealms().keys() else: valid_realm_list = valid_realms for realm in valid_realm_list: users.append(User(user.login, realm)) # resolve the realm with wildcard: # identify all users and add these to the userlist userlist = [] for usr in users: urealm = usr.realm if urealm == '*': # if the realm is set to *, the getUserId # triggers the identification of all resolvers, where the # user might reside: trigger the user resolver lookup for realm in getRealms().keys(): if realm in valid_realms or '*' in valid_realms: usr.realm = realm try: (_uid, _resolver, _resolverClass) = getUserId(usr) except UserError as exx: log.info('User %r not found in realm %r', usr, realm) continue userlist.extend(usr.getUserPerConf()) else: userlist.append(usr) for usr in userlist: try: tokens = getTokens4UserOrSerial(user=usr, _class=False) for tok in tokens: serials.append(tok.LinOtpTokenSerialnumber) except UserError as ex: ## we get an exception if the user is not found log.debug('[TokenIterator::init] no exact user: %r' % (user)) log.debug('[TokenIterator::init] %r' % ex) if len(serials) > 0: # if tokens found, search for their serials ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: # if no token is found, block search for user # and return nothing ucondition = and_(Token.LinOtpTokenSerialnumber == u'') ## handle case, when nothing found in former cases if searchType == "wildcard": serials = _user_expression_match(loginUser, token_owner_iterator()) # to prevent warning, we check is serials are found # SAWarning: The IN-predicate on # "Token.LinOtpTokenSerialnumber" was invoked with an # empty sequence. This results in a contradiction, which # nonetheless can be expensive to evaluate. Consider # alternative strategies for improved performance. if len(serials) > 0: ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: ucondition = and_(Token.LinOtpTokenSerialnumber == u'') return ucondition
def janitor_to_remove_enrollment_token(valid_tokens): """ remove all enrollment only tokens """ # ------------------------------------------------------------------ -- # get all owners for the valid tokens all_owners = set() for token in valid_tokens: # if the authenticated token is a rollout token, we dont count him path = token.getFromTokenInfo('scope', {}).get('path', []) if len(path) == 1 and path[0] == 'userservice': continue # TODO: get owner sadly throws a genric exception in case of # no intersection beteen token realms and user realms :( try: owner = get_token_owner(token) except Exception: continue if owner: all_owners.add(owner) # ------------------------------------------------------------------ -- # get all rollout only tokens per owner to_be_removed_tokens = [] for owner in all_owners: # should be purge the tokens of the user? <- defined by policy if not purge_enrollment_token(user=owner): continue user_tokens = getTokens4UserOrSerial(user=owner) # if there is only one token either # - the user has still the rollout: do not delete # - or the user has a new one: we dont delete either if len(user_tokens) < 2: continue for token in user_tokens: path = token.getFromTokenInfo('scope', {}).get('path', []) if len(path) == 1 and path[0] == 'userservice': to_be_removed_tokens.append(token) # ------------------------------------------------------------------ -- # delete all rollout tokens serials = [] for token in to_be_removed_tokens: serials.append(token.getSerial()) remove_token(token) # ------------------------------------------------------------------ -- # add info about the purging audit = context['audit'] if serials: audit['info'] += 'purged rollout tokens:' + ', '.join(serials)
def check_t(self): """ method: ocra/check_t description: verify the response of the ocra token arguments: * transactionid: (required - string) Dies ist eine Transaktions-ID, die bei der Challenge ausgegeben wurde. * pass: (required - string) die response, die der OCRA Token auf Grund der Challenge berechnet hat returns: A JSON response:: { "version": "LinOTP 2.4", "jsonrpc": "2.0", "result": { "status": true, "value": { "failcount" : 3, "result": false } }, "id": 0 } exception: """ res = {} description = 'ocra/check_t: validate a token request.' try: param = getLowerParams(request.params) log.info("[check_t] check OCRA token: %r" % param) #checkPolicyPre('ocra', "check_t") passw = getParam(param, 'pass' , optional) if passw is None: ## raise exception''' log.exception("[check_t] missing pass ") raise ParameterError("Usage: %s Missing parameter 'pass'." % description, id=77) transid = getParam(param, 'transactionid', optional) if transid is None: ## raise exception''' log.exception("[check_t] missing transactionid, user or serial number of token") raise ParameterError("Usage: %s Missing parameter 'transactionid'." % description, id=77) ## if we have a transaction, get serial from this challenge value = {} ocraChallenge = OcraTokenClass.getTransaction(transid) if ocraChallenge is not None: serial = ocraChallenge.tokenserial tokens = getTokens4UserOrSerial(serial=serial) if len(tokens) == 0 or len(tokens) > 1: raise Exception('tokenmismatch for token serial: %s' % (unicode(serial))) theToken = tokens[0] tok = theToken.token desc = tok.get() realms = desc.get('LinOtp.RealmNames') if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo(tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass) user = User(login=userInfo.get('username'), realm=realm) vh = ValidationHandler() (ok, opt) = vh.checkSerialPass(serial, passw, user=user, options={'transactionid': transid}) failcount = theToken.getFailCount() value['result'] = ok value['failcount'] = int(failcount) else: ## no challenge found for this transid value['result'] = False value['failure'] = 'No challenge for transaction %r found'\ % transid c.audit['success'] = res #c.audit['info'] += "%s=%s, " % (k, value) Session.commit() return sendResult(response, value, 1) except Exception as e : log.exception("[check_t] failed: %r" % e) Session.rollback() return sendResult(response, unicode(e), 0) finally: Session.close() log.debug("[check_t] done")
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 getotp(self): ''' This function is used to retrieve the current otp value for a given user or a given serial If the user has more than one token, the list of the tokens is returend. method: gettoken/getotp arguments: user - username / loginname realm - additional realm to match the user to a useridresolver serial - the serial number of the token curTime - used ONY for internal testing: datetime.datetime object returns: JSON response ''' getotp_active = config.get("linotpGetotp.active") if "True" != getotp_active: return sendError(response, "getotp is not activated.", 0) param = request.params ret = {} res = -1 otpval = "" passw = "" serials = [] try: serial = getParam(param, "serial", optional) user = getUserFromParam(param, optional) curTime = getParam(param, "curTime", optional) c.audit['user'] = user.login if "" != user.login: c.audit['realm'] = user.realm or getDefaultRealm() if serial: log.debug("[getotp] retrieving OTP value for token %s" % serial) elif user.login: log.debug( "[getotp] retrieving OTP value for token for user %s@%s" % (user.login, user.realm)) toks = getTokens4UserOrSerial(user, serial) tokennum = len(toks) if tokennum > 1: log.debug( "[getotp] The user has more than one token. Returning the list of serials" ) res = -3 for token in toks: serials.append(token.getSerial()) elif 1 == tokennum: serial = toks[0].getSerial() log.debug( "[getotp] retrieving OTP for token %s for user %s@%s" % (serial, user.login, user.realm)) else: log.debug("[getotp] no token found for user %s@%s" % (user.login, user.realm)) res = -4 else: res = -5 # if a serial was given or a unique serial could be received from the given user. if serial: max_count = checkPolicyPre('gettoken', 'max_count', param) log.debug("[getmultiotp] checkpolicypre returned %s" % max_count) if max_count <= 0: return sendError( response, "The policy forbids receiving OTP values for the token %s in this realm" % serial, 1) (res, pin, otpval, passw) = getOtp(serial, curTime=curTime) c.audit['success'] = True if int(res) < 0: ret['result'] = False if -1 == otpval: ret['description'] = "No Token with this serial number" if -2 == otpval: ret['description'] = "This Token does not support the getOtp function" if -3 == otpval: ret['description'] = "The user has more than one token" ret['serials'] = serials if -4 == otpval: ret['description'] = "No Token found for this user" if -5 == otpval: ret['description'] = "you need to provide a user or a serial" else: ret['result'] = True ret['otpval'] = otpval ret['pin'] = pin ret['pass'] = passw Session.commit() return sendResult(response, ret, 0) except PolicyException as pe: log.error("[getotp] gettoken/getotp policy failed: %r" % pe) log.error("[getotp] %s" % traceback.format_exc()) Session.rollback() return sendError(response, unicode(pe), 1) except Exception as e: log.error("[getotp] gettoken/getotp failed: %r" % e) log.error("[getotp] %s" % traceback.format_exc()) Session.rollback() return sendError(response, "gettoken/getotp failed: %s" % unicode(e), 0) finally: Session.close() log.debug('[getotp] done')
def check_s(self): """ This function is used to validate the serial and the otp value/password. method: validate/check_s arguments: * serial: the serial number of the token * pass: the password that consists of a possible fixes password component and the OTP value returns: JSON response """ param = {} param.update(request.params) options = {} options.update(param) for k in ["user", "serial", "pass", "init"]: if k in options: del options[k] if "init" in param: if isSelfTest() == True: options["initTime"] = param.get("init") try: passw = getParam(param, "pass", optional) serial = getParam(param, "serial", optional) if serial is None: user = getParam(param, "user", optional) if user is not None: user = getUserFromParam(param, optional) toks = getTokens4UserOrSerial(user=user) if len(toks) == 0: raise Exception("No token found!") elif len(toks) > 1: raise Exception("More than one token found!") else: tok = toks[0].token desc = tok.get() realms = desc.get("LinOtp.RealmNames") if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo(tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass) user = User(login=userInfo.get("username"), realm=realm) serial = tok.getSerial() c.audit["serial"] = serial if isSelfTest() == True: initTime = getParam(param, "init", optional) if initTime is not None: if options is None: options = {} options["initTime"] = initTime (ok, opt) = checkSerialPass(serial, passw, options=options) c.audit["success"] = ok Session.commit() qr = getParam(param, "qr", optional) if qr is not None and opt is not None and opt.has_key("message"): try: dataobj = opt.get("message") param["alt"] = "%s" % opt return sendQRImageResult(response, dataobj, param) except Exception as exc: log.warning("failed to send QRImage: %r " % exc) return sendQRImageResult(response, opt, param) else: return sendResult(response, ok, 0, opt=opt) except Exception as exx: log.error("[check_s] validate/check_s failed: %r" % exx) log.error("[check_s] %s" % traceback.format_exc()) c.audit["info"] = unicode(exx) Session.rollback() return sendError(response, "validate/check_s failed: %s" % unicode(exx), 0) finally: Session.close() log.debug("[check_s] done")
def _get_user_condition(self, user, valid_realms): ucondition = None if not user or user.is_empty or not user.login: return ucondition loginUser = user.login.lower() loginUser = loginUser.replace('"', '') loginUser = loginUser.replace("'", '') searchType = "any" ## search for a 'blank' user if len(loginUser) == 0 and len(user.login) > 0: searchType = "blank" elif loginUser == "/:no user:/" or loginUser == "/:none:/": searchType = "blank" elif loginUser == "/:no user info:/": searchType = "wildcard" elif "*" in loginUser: searchType = "wildcard" else: ## no blank and no wildcard search searchType = "exact" if searchType == "blank": ucondition = and_(or_(Token.LinOtpUserid == u'', Token.LinOtpUserid is None)) if searchType == "exact": serials = [] users = [] # if search for a realmuser 'user@realm' we can take the # realm from the argument if len(user.realm) > 0: users.append(user) else: # otherwise we add all users which are possible combinations # from loginname and entry of the valid realms. # In case of a '*' wildcard in the list, we take all available # realms if '*' in valid_realms: valid_realm_list = getRealms().keys() else: valid_realm_list = valid_realms for realm in valid_realm_list: users.append(User(user.login, realm)) # resolve the realm with wildcard: # identify all users and add these to the userlist userlist = [] for usr in users: urealm = usr.realm if urealm == '*': # if the realm is set to *, the getUserId # triggers the identification of all resolvers, where the # user might reside: trigger the user resolver lookup for realm in getRealms().keys(): if realm in valid_realms or '*' in valid_realms: usr.realm = realm try: (_uid, _resolver, _resolverClass) = getUserId(usr) except UserError as exx: log.info('User %r not found in realm %r', usr, realm) continue userlist.extend(usr.getUserPerConf()) else: userlist.append(usr) for usr in userlist: try: tokens = getTokens4UserOrSerial(user=usr, _class=False) for tok in tokens: serials.append(tok.LinOtpTokenSerialnumber) except UserError as ex: ## we get an exception if the user is not found log.debug('[TokenIterator::init] no exact user: %r' % (user)) log.debug('[TokenIterator::init] %r' % ex) if len(serials) > 0: # if tokens found, search for their serials ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: # if no token is found, block search for user # and return nothing ucondition = and_(Token.LinOtpTokenSerialnumber == u'') ## handle case, when nothing found in former cases if searchType == "wildcard": serials = _user_expression_match(loginUser, token_owner_iterator()) # to prevent warning, we check is serials are found # SAWarning: The IN-predicate on # "Token.LinOtpTokenSerialnumber" was invoked with an # empty sequence. This results in a contradiction, which # nonetheless can be expensive to evaluate. Consider # alternative strategies for improved performance. if len(serials) > 0: ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: ucondition = and_(Token.LinOtpTokenSerialnumber == u'') return ucondition
def check_s(self): ''' This function is used to validate the serial and the otp value/password. method: validate/check_s arguments: * serial: the serial number of the token * pass: the password that consists of a possible fixes password component and the OTP value returns: JSON response ''' param = {} param.update(request.params) options = {} options.update(param) for k in ['user', 'serial', "pass", "init"]: if k in options: del options[k] if 'init' in param: if isSelfTest() is True: options['initTime'] = param.get('init') try: passw = getParam(param, "pass", optional) serial = getParam(param, 'serial', optional) if serial is None: user = getParam(param, 'user', optional) if user is not None: user = getUserFromParam(param, optional) toks = getTokens4UserOrSerial(user=user) if len(toks) == 0: raise Exception("No token found!") elif len(toks) > 1: raise Exception("More than one token found!") else: tok = toks[0].token desc = tok.get() realms = desc.get('LinOtp.RealmNames') if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo(tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass) user = User(login=userInfo.get('username'), realm=realm) serial = tok.getSerial() c.audit['serial'] = serial if isSelfTest() is True: initTime = getParam(param, "init", optional) if initTime is not None: if options is None: options = {} options['initTime'] = initTime options['scope'] = {"check_s": True} vh = ValidationHandler() (ok, opt) = vh.checkSerialPass(serial, passw, options=options) c.audit['success'] = ok Session.commit() qr = param.get('qr', None) if qr and opt and 'message' in opt: try: dataobj = opt.get('message') param['alt'] = "%s" % opt if 'transactionid' in opt: param['transactionid'] = opt['transactionid'] return sendQRImageResult(response, dataobj, param) except Exception as exc: log.warning("failed to send QRImage: %r " % exc) return sendQRImageResult(response, opt, param) else: return sendResult(response, ok, 0, opt=opt) except Exception as exx: log.exception("[check_s] validate/check_s failed: %r" % exx) c.audit['info'] = unicode(exx) Session.rollback() return sendResult(response, False, id=0, status=False) finally: Session.close() log.debug('[check_s] done')
def check_s(self): ''' This function is used to validate the serial and the otp value/password. method: validate/check_s arguments: * serial: the serial number of the token * pass: the password that consists of a possible fixes password component and the OTP value returns: JSON response ''' param = self.request_params options = {} options.update(param) for k in ['user', 'serial', "pass", "init"]: if k in options: del options[k] if 'init' in param: if isSelfTest() is True: options['initTime'] = param.get('init') try: passw = param.get("pass") serial = param.get('serial') if serial is None: user = param.get('user') if user is not None: user = getUserFromParam(param) toks = getTokens4UserOrSerial(user=user) if len(toks) == 0: raise Exception("No token found!") elif len(toks) > 1: raise Exception("More than one token found!") else: tok = toks[0].token desc = tok.get() realms = desc.get('LinOtp.RealmNames') if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo(tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass) user = User(login=userInfo.get('username'), realm=realm) serial = tok.getSerial() c.audit['serial'] = serial if isSelfTest() is True: initTime = param.get("init") if initTime is not None: if options is None: options = {} options['initTime'] = initTime options['scope'] = {"check_s": True} vh = ValidationHandler() (ok, opt) = vh.checkSerialPass(serial, passw, options=options) c.audit['success'] = ok Session.commit() qr = param.get('qr', None) if qr and opt and 'message' in opt: try: dataobj = opt.get('message') param['alt'] = "%s" % opt if 'transactionid' in opt: param['transactionid'] = opt['transactionid'] return sendQRImageResult(response, dataobj, param) except Exception as exc: log.warning("failed to send QRImage: %r " % exc) return sendQRImageResult(response, opt, param) else: return sendResult(response, ok, 0, opt=opt) except Exception as exx: log.exception("[check_s] validate/check_s failed: %r" % exx) c.audit['info'] = unicode(exx) Session.rollback() return sendResult(response, False, id=0, status=False) finally: Session.close()
def pair(self): """ validate/pair: for the enrollment of qr and push token """ try: # -------------------------------------------------------------- -- enc_response = self.request_params.get('pairing_response') if enc_response is None: raise Exception('Parameter missing') # -------------------------------------------------------------- -- dec_response = decrypt_pairing_response(enc_response) token_type = dec_response.token_type pairing_data = dec_response.pairing_data if not hasattr(pairing_data, 'serial') or \ pairing_data.serial is None: raise ValidateError('Pairing responses with no serial attached' ' are currently not implemented.') # --------------------------------------------------------------- - # TODO: pairing policy tokens = getTokens4UserOrSerial(None, pairing_data.serial) if not tokens: raise Exception('Invalid serial in pairing response') if len(tokens) > 1: raise Exception('Multiple tokens found. Pairing not possible') token = tokens[0] # prepare some audit entries t_owner = token.getUser() realms = token.getRealms() realm = '' if realms: realm = realms[0] c.audit['user'] = t_owner or '' c.audit['realm'] = realm # --------------------------------------------------------------- -- if token.type != token_type: raise Exception('Serial in pairing response doesn\'t match ' 'supplied token_type') # --------------------------------------------------------------- -- token.pair(pairing_data) c.audit['success'] = 1 c.audit['serial'] = token.getSerial() Session.commit() return sendResult(response, False) # ------------------------------------------------------------------- -- except Exception as exx: log.exception("validate/pair failed: %r" % exx) c.audit['info'] = unicode(exx) Session.rollback() return sendResult(response, False, 0, status=False) finally: Session.close()
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 audit = context['audit'] 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(): 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 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: 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(): 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) 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!') 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(u"Missing parameter:pass", id=905) (res, opt) = self.checkTokenList( tokenList, passw, user, options=options) return (res, opt)
def __init__(self, user, serial, page=None, psize=None, filter=None, sort=None, sortdir=None, filterRealm=None, user_fields=None, params=None): ''' constructor of Tokeniterator, which gathers all conditions to build a sqalchemy query - iterator :param user: User object - user provides as well the searchfield entry :type user: User class :param serial: serial number of a token :type serial: string :param page: page number :type page: int :param psize: how many entries per page :type psize: int :param filter: additional condition :type filter: string :param sort: sort field definition :type sort: string :param sortdir: sort direction: ascending or descending :type sortdir: string :param filterRealm: restrict the set of token to those in the filterRealm :type filterRealm: string or list :param user_fields: list of additional fields from the user owner :type user_fields: array :param params: list of additional request parameters - currently not used :type params: dict :return: - nothing / None ''' log.debug('[TokenIterator::init] begin. start creating TokenIterator \ class with parameters: user:%r, serial:%r, page=%r, psize:%r, \ filter:%r, sort:%r, sortdir:%r, filterRealm:%r' % (user, serial, page, psize, filter, sort, sortdir, filterRealm)) if params is None: params = {} self.page = 1 self.pages = 1 self.tokens = 0 self.user_fields = user_fields if self.user_fields == None: self.user_fields = [] condition = None ucondition = None scondition = None r_condition = None if type(filterRealm) in (str, unicode): filterRealm = filterRealm.split(',') if type(filterRealm) in [list]: s_realms = [] for f in filterRealm: # support for multiple realm filtering in the ui # as a coma separated string for s_realm in f.split(','): s_realms.append(s_realm.strip()) filterRealm = s_realms # create a list of all realms, which are allowed to be searched # based on the list of the existing ones valid_realms = [] realms = getRealms().keys() if '*' in filterRealm: valid_realms.append("*") else: for realm in realms: if realm in filterRealm: realm = linotp.lib.crypt.uencode(realm) valid_realms.append(realm) if serial is not None: # check if the requested serial is in the realms of # the admin (filterRealm) log.debug('[TokenIterator::init] start search for serial: >%r<' % (serial)) allowed = False if filterRealm == ['*']: allowed = True else: realms = getTokenRealms(serial) for realm in realms: if realm in filterRealm: allowed = True if allowed == True: if '*' in serial: serial = serial.replace('*', '%') scondition = and_(Token.LinOtpTokenSerialnumber.like(serial)) else: scondition = and_(Token.LinOtpTokenSerialnumber == serial) if user.isEmpty() == False and user is not None: log.debug('[TokenIterator::init] start search for username: >%r<' % (user)) if user.login is not None and (user.login) > 0 : loginUser = user.login.lower() loginUser = loginUser.replace('"', '') loginUser = loginUser.replace("'", '') searchType = "any" # search for a 'blank' user if len(loginUser) == 0 and len(user.login) > 0: searchType = "blank" elif loginUser == "/:no user:/" or loginUser == "/:none:/": searchType = "blank" elif loginUser == "/:no user info:/": searchType = "wildcard" elif "*" in loginUser or "." in loginUser: searchType = "wildcard" else: # no blank and no wildcard search searchType = "exact" if searchType == "blank": log.debug('[TokenIterator::init] search for empty user:'******' >%r<' % (user.login)) ucondition = and_(or_(Token.LinOtpUserid == u'', Token.LinOtpUserid == None)) if searchType == "exact": log.debug('[TokenIterator::init] search for exact user:'******' %r' % (user)) serials = [] users = [] # if search for a realmuser 'user@realm' we can take the # realm from the argument if len(user.realm) > 0: users.append(user) else: for realm in valid_realms: users.append(User(user.login, realm)) # resolve the realm with wildcard: # identify all users and add these to the userlist userlist = [] for usr in users: urealm = usr.realm if urealm == '*': # if the realm is set to *, the getUserId # triggers the identification of all resolvers, # where the user might reside: tigger the user # resolver lookup (uid, resolver, resolverClass) = getUserId(usr) userlist.extend(usr.getUserPerConf()) else: userlist.append(usr) for usr in userlist: try: tokens = getTokens4UserOrSerial(user=usr, _class=False) for tok in tokens: serials.append(tok.LinOtpTokenSerialnumber) except UserError as ex: # we get an exception if the user is not found log.debug('[TokenIterator::init] no exact user: %r' % (user)) log.debug('[TokenIterator::init] %r' % ex) if len(serials) > 0: # if tokens found, search for their serials ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: # if no token is found, block search for user # and return nothing ucondition = and_(or_(Token.LinOtpUserid == u'', Token.LinOtpUserid == None)) # handle case, when nothing found in former cases if searchType == "wildcard": log.debug('[TokenIterator::init] wildcard search: %r' % (user)) serials = [] users = getAllTokenUsers() logRe = None lU = loginUser.replace('*', '.*') # lU = lU.replace('..', '.') logRe = re.compile(lU) for ser in users: userInfo = users.get(ser) tokenUser = userInfo.get('username').lower() try: if logRe.match(u'' + tokenUser) is not None: serials.append(ser) except Exception as e: log.error('error no express %r ' % e) # to prevent warning, we check is serials are found # SAWarning: The IN-predicate on # "Token.LinOtpTokenSerialnumber" was invoked with an # empty sequence. This results in a contradiction, which # nonetheless can be expensive to evaluate. Consider # alternative strategies for improved performance. if len(serials) > 0: ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: ucondition = and_(or_(Token.LinOtpUserid == u'', Token.LinOtpUserid == None)) if filter is None: condition = None elif filter in ['/:active:/', '/:enabled:/', '/:token is active:/', '/:token is enabled:/' ]: condition = and_(Token.LinOtpIsactive == True) elif filter in ['/:inactive:/', '/:disabled:/', '/:token is inactive:/', '/:token is disabled:/']: condition = and_(Token.LinOtpIsactive == False) else: # search in other colums filter = linotp.lib.crypt.uencode(filter) condition = or_(Token.LinOtpTokenDesc.contains(filter), Token.LinOtpIdResClass.contains(filter), Token.LinOtpTokenSerialnumber.contains(filter), Token.LinOtpUserid.contains(filter), Token.LinOtpTokenType.contains(filter)) ################################################################### # The condition for only getting certain realms! if '*' in valid_realms: log.debug("[TokenIterator::init] wildcard for realm '*' found." " Tokens of all realms will be displayed") elif len(valid_realms) > 0: log.debug("[TokenIterator::init] adding filter condition" " for realm %r" % valid_realms) # get all matching realms realm_id_tuples = Session.query(Realm.id).\ filter(Realm.name.in_(valid_realms)).all() realm_ids = set() for realm_tuple in realm_id_tuples: realm_ids.add(realm_tuple[0]) # get all tokenrealm ids token_id_tuples = Session.query(TokenRealm.token_id).\ filter(TokenRealm.realm_id.in_(realm_ids)).all() token_ids = set() for token_tuple in token_id_tuples: token_ids.add(token_tuple[0]) # define the token id condition r_condition = and_(Token.LinOtpTokenId.in_(token_ids)) elif ("''" in filterRealm or '""' in filterRealm or "/:no realm:/" in filterRealm): log.debug("[TokenIterator::init] search for all tokens, which are" " in no realm") # get all tokenrealm ids token_id_tuples = Session.query(TokenRealm.token_id).all() token_ids = set() for token_tuple in token_id_tuples: token_ids.add(token_tuple[0]) # define the token id not condition r_condition = and_(not_(Token.LinOtpTokenId.in_(token_ids))) # create the final condition as AND of all conditions condTuple = () for conn in (condition, ucondition, scondition, r_condition): if type(conn).__name__ != 'NoneType': condTuple += (conn,) condition = and_(*condTuple) order = Token.LinOtpTokenDesc if sort == "TokenDesc": order = Token.LinOtpTokenDesc elif sort == "TokenId": order = Token.LinOtpTokenId elif sort == "TokenType": order = Token.LinOtpTokenType elif sort == "TokenSerialnumber": order = Token.LinOtpTokenSerialnumber elif sort == "TokenType": order = Token.LinOtpTokenType elif sort == "IdResClass": order = Token.LinOtpIdResClass elif sort == "IdResolver": order = Token.LinOtpIdResolver elif sort == "Userid": order = Token.LinOtpUserid elif sort == "FailCount": order = Token.LinOtpFailCount elif sort == "Userid": order = Token.LinOtpUserid elif sort == "Isactive": order = Token.LinOtpIsactive # care for the result sort order if sortdir is not None and sortdir == "desc": order = order.desc() else: order = order.asc() # care for the result pageing if page is None: self.toks = Session.query(Token).filter(condition).order_by(order).distinct() self.tokens = self.toks.count() log.debug("[TokenIterator] DB-Query returned # of objects:" " %i" % self.tokens) self.pagesize = self.tokens self.it = iter(self.toks) return try: if psize is None: pagesize = int(getFromConfig("pagesize", 50)) else: pagesize = int(psize) except: pagesize = 20 try: thePage = int (page) - 1 except: thePage = 0 if thePage < 0: thePage = 0 start = thePage * pagesize stop = (thePage + 1) * pagesize self.toks = Session.query(Token).filter(condition).\ order_by(order).distinct() self.tokens = self.toks.count() log.debug("[TokenIterator::init] DB-Query returned # of objects:" " %i" % self.tokens) self.page = thePage + 1 fpages = float(self.tokens) / float(pagesize) self.pages = int(fpages) if fpages - int(fpages) > 0: self.pages = self.pages + 1 self.pagesize = pagesize self.toks = self.toks.slice(start, stop) self.it = iter(self.toks) log.debug('[TokenIterator::init] end. Token iterator created: %r' % \ (self.it)) return
def check_s(self): """ This function is used to validate the serial and the otp value/password. If the otppin policy is set, the endpoint /validate/check_s does not work. method: validate/check_s arguments: * serial: the serial number of the token * pass: the password that consists of a possible fixes password component and the OTP value returns: JSON response """ param = self.request_params options = {} options.update(param) for k in ["user", "serial", "pass", "init"]: if k in options: del options[k] try: passw = param.get("pass") serial = param.get("serial") if serial is None: user = param.get("user") if user is not None: user = getUserFromParam(param) toks = getTokens4UserOrSerial(user=user) if len(toks) == 0: raise Exception("No token found!") elif len(toks) > 1: raise Exception("More than one token found!") else: tok = toks[0].token desc = tok.get() realms = desc.get("LinOtp.RealmNames") if realms is None or len(realms) == 0: realm = getDefaultRealm() elif len(realms) > 0: realm = realms[0] userInfo = getUserInfo( tok.LinOtpUserid, tok.LinOtpIdResolver, tok.LinOtpIdResClass, ) user = User( login=userInfo.get("username"), realm=realm ) serial = tok.getSerial() g.audit["serial"] = serial options["scope"] = {"check_s": True} vh = ValidationHandler() (ok, opt) = vh.checkSerialPass(serial, passw, options=options) g.audit["success"] = ok db.session.commit() qr = param.get("qr", None) if qr and opt and "message" in opt: try: dataobj = opt.get("message") param["alt"] = "%s" % opt if "transactionid" in opt: param["transactionid"] = opt["transactionid"] return sendQRImageResult(response, dataobj, param) except Exception as exc: log.warning("failed to send QRImage: %r ", exc) return sendQRImageResult(response, opt, param) else: return sendResult(response, ok, 0, opt=opt) except Exception as exx: log.error("[check_s] validate/check_s failed: %r", exx) g.audit["info"] = str(exx) db.session.rollback() return sendResult(response, False, id=0, status=False)
def _get_user_condition(self, user, valid_realms): ucondition = None log.debug('[TokenIterator::init] start search for username: >%r<' % (user)) if not user or user.isEmpty() or not user.login: return ucondition loginUser = user.login.lower() loginUser = loginUser.replace('"', '') loginUser = loginUser.replace("'", '') searchType = "any" ## search for a 'blank' user if len(loginUser) == 0 and len(user.login) > 0: searchType = "blank" elif loginUser == "/:no user:/" or loginUser == "/:none:/": searchType = "blank" elif loginUser == "/:no user info:/": searchType = "wildcard" elif "*" in loginUser or "." in loginUser: searchType = "wildcard" else: ## no blank and no wildcard search searchType = "exact" if searchType == "blank": log.debug('[TokenIterator::init] search for empty user: >%r<' % (user.login)) ucondition = and_(or_(Token.LinOtpUserid == u'', Token.LinOtpUserid is None)) if searchType == "exact": log.debug('[TokenIterator::init] search for exact user: %r' % (user)) serials = [] users = [] ## if search for a realmuser 'user@realm' we can take the ## realm from the argument if len(user.realm) > 0: users.append(user) else: for realm in valid_realms: users.append(User(user.login, realm)) # resolve the realm with wildcard: # identify all users and add these to the userlist userlist = [] for usr in users: urealm = usr.realm if urealm == '*': # if the realm is set to *, the getUserId # triggers the identification of all resolvers, where the # user might reside: tigger the user resolver lookup (uid, resolver, resolverClass) = getUserId(usr) userlist.extend(usr.getUserPerConf()) else: userlist.append(usr) for usr in userlist: try: tokens = getTokens4UserOrSerial(user=usr, _class=False) for tok in tokens: serials.append(tok.LinOtpTokenSerialnumber) except UserError as ex: ## we get an exception if the user is not found log.debug('[TokenIterator::init] no exact user: %r' % (user)) log.debug('[TokenIterator::init] %r' % ex) if len(serials) > 0: # if tokens found, search for their serials ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: # if no token is found, block search for user # and return nothing ucondition = and_(Token.LinOtpTokenSerialnumber == u'') ## handle case, when nothing found in former cases if searchType == "wildcard": log.debug('[TokenIterator::init] wildcard search: %r' % (user)) serials = [] users = getAllTokenUsers() logRe = None lU = loginUser.replace('*', '.*') #lU = lU.replace('..', '.') logRe = re.compile(lU) for ser in users: userInfo = users.get(ser) tokenUser = userInfo.get('username').lower() try: if logRe.match(u'' + tokenUser) is not None: serials.append(ser) except Exception as e: log.error('error no express %r ' % e) ## to prevent warning, we check is serials are found ## SAWarning: The IN-predicate on ## "Token.LinOtpTokenSerialnumber" was invoked with an ## empty sequence. This results in a contradiction, which ## nonetheless can be expensive to evaluate. Consider ## alternative strategies for improved performance. if len(serials) > 0: ucondition = and_(Token.LinOtpTokenSerialnumber.in_(serials)) else: ucondition = and_(Token.LinOtpTokenSerialnumber == u'') return ucondition
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 pair(self): """ validate/pair: for the enrollment of qr and push token """ try: # -------------------------------------------------------------- -- enc_response = self.request_params.get("pairing_response") if enc_response is None: raise Exception("Parameter missing") # -------------------------------------------------------------- -- dec_response = decrypt_pairing_response(enc_response) token_type = dec_response.token_type pairing_data = dec_response.pairing_data if ( not hasattr(pairing_data, "serial") or pairing_data.serial is None ): raise ValidateError( "Pairing responses with no serial attached" " are currently not implemented." ) # --------------------------------------------------------------- - # TODO: pairing policy tokens = getTokens4UserOrSerial(None, pairing_data.serial) if not tokens: raise Exception("Invalid serial in pairing response") if len(tokens) > 1: raise Exception("Multiple tokens found. Pairing not possible") token = tokens[0] # prepare some audit entries t_owner = token.getUser() realms = token.getRealms() realm = "" if realms: realm = realms[0] g.audit["user"] = t_owner or "" g.audit["realm"] = realm # --------------------------------------------------------------- -- if token.type != token_type: raise Exception( "Serial in pairing response doesn't match " "supplied token_type" ) # --------------------------------------------------------------- -- token.pair(pairing_data) g.audit["success"] = 1 g.audit["serial"] = token.getSerial() db.session.commit() return sendResult(response, False) # ------------------------------------------------------------------- -- except Exception as exx: log.error("validate/pair failed: %r", exx) g.audit["info"] = str(exx) db.session.rollback() return sendResult(response, False, 0, status=False)