def search_policy(param, only_active=True): """ migration stub for the new policy engine """ use_new_one = boolean(context['Config'].get( 'NewPolicyEvaluation', False)) compare = boolean(context['Config'].get( 'NewPolicyEvaluation.compare', False)) if use_new_one or compare: pols_new = new_search_policy(param, only_active=only_active) if not use_new_one or compare: pols_old = legacy_getPolicy(param, only_active=only_active) if use_new_one: return_policies = pols_new else: return_policies = pols_old if not compare: return return_policies if not are_the_same(pols_old, pols_new): LOG.error('PolicyEvaluation is not the same for params %r', param) LOG.error('old: new %r <> %r', pols_old, pols_new) return return_policies
def _getAuthorization(scope, action): """ migration stub for the new policy engine """ use_new_one = boolean(context['Config'].get( 'NewPolicyEvaluation', False)) compare = boolean(context['Config'].get( 'NewPolicyEvaluation.compare', False)) if use_new_one or compare: new_pols = new_getAuthorization(scope, action) if not use_new_one or compare: old_pols = legacy_getAuthorization(scope, action) if use_new_one: return_policies = new_pols else: return_policies = old_pols if not compare: return return_policies if not are_the_same(old_pols, new_pols): LOG.error('PolicyEvaluation is not the same for params %r,%r', scope, action) LOG.error('old: new %r <> %r', old_pols, new_pols) return return_policies
def has_client_policy(client, scope=None, action=None, realm=None, user=None, find_resolver=True, userObj=None, active_only=True): """ migration stub for the new policy engine Remark: has_client_policy is different to the method get_client_policy as the filters for the has_client_policy are reseted after usage """ use_new_one = boolean(context['Config'].get( 'NewPolicyEvaluation', False)) compare = boolean(context['Config'].get( 'NewPolicyEvaluation.compare', False)) if use_new_one or compare: new_pols = new_has_client_policy(client, scope=scope, action=action, realm=realm, user=user, find_resolver=find_resolver, userObj=userObj, active_only=active_only) if not use_new_one or compare: old_pols = legacy_get_client_policy(client, scope=scope, action=action, realm=realm, user=user, find_resolver=find_resolver, userObj=userObj) if use_new_one: return_policies = new_pols else: return_policies = old_pols if not compare: return return_policies if not are_the_same(old_pols, new_pols): LOG.error('PolicyEvaluation is not the same for params %r', client) LOG.error('old: new %r <> %r', old_pols, new_pols) return return_policies
def get_client_policy(client, scope=None, action=None, realm=None, user=None, find_resolver=True, userObj=None, active_only=True): """ migration stub for the new policy engine """ use_new_one = boolean(context['Config'].get( 'NewPolicyEvaluation', False)) compare = boolean(context['Config'].get( 'NewPolicyEvaluation.compare', False)) if use_new_one or compare: pols_new = new_get_client_policy(client, scope=scope, action=action, realm=realm, user=user, find_resolver=find_resolver, userObj=userObj, active_only=active_only) if not use_new_one or compare: pols_old = legacy_get_client_policy(client, scope=scope, action=action, realm=realm, user=user, find_resolver=find_resolver, userObj=userObj) if use_new_one: return_policies = pols_new else: return_policies = pols_old if not compare: return return_policies if not are_the_same(pols_old, pols_new): LOG.error('PolicyEvaluation is not the same for params %r', client) LOG.error('old: new %r <> %r', pols_old, pols_new) return return_policies
def __before__(self, action, **params): try: c.audit = request_context['audit'] c.audit['success'] = False c.audit['client'] = get_client(request) c.version = get_version() c.version_ref = base64.encodestring(c.version)[:6] c.licenseinfo = get_copyright_info() c.polDefs = getPolicyDefinitions() c.display_provider = boolean( request_context['Config'].get('display_provider', True)) # -------------------------------------------------------------- -- # check for support of setting admin password c.admin_can_change_password = False if ('linotpadmin.user' in config and 'linotpadmin.password' in config): c.admin_can_change_password = True # -------------------------------------------------------------- -- # Session handling for the functions, that show data: # Also exclude custom-style.css, since the CSRF check # will always fail and return a HTTP 401 anyway. # A HTTP 404 makes more sense. if request.path.lower() in ['/manage/', '/manage', '/manage/logout', '/manage/audittrail', '/manage/policies', '/manage/tokenview', '/manage/userview', '/manage/help', '/manage/custom-style.css']: pass else: check_session(request) except Exception as exx: log.exception("[__before__::%r] exception %r" % (action, exx)) Session.rollback() Session.close() return sendError(response, exx, context='before') finally: log.debug("[__before__::%r] done" % (action))
def import_users(self): """ import users from a csv file into an dedicated sql resolver """ try: params = {} params.update(request.POST) # -------------------------------------------------------------- -- # processing required arguments try: data_file = request.POST['file'] resolver_name = params['resolver'] except KeyError as exx: log.exception("Missing parameter: %r", exx) raise ParameterError("Missing parameter: %r" % exx) groupid = resolver_name # process file upload data data = data_file # -- ----------------------------------------------------------- -- # In case of form post requests, it is a "instance" of FieldStorage # i.e. the Filename is selected in the browser and the data is # transferred in an iframe. # see: http://jquery.malsup.com/form/#sample4 # -- ----------------------------------------------------------- -- if isinstance(data_file, FieldStorage): data = data_file.value # -------------------------------------------------------------- -- # process the other arguments dryrun = boolean(params.get('dryrun', False)) passwords_in_plaintext = boolean(params.get( 'passwords_in_plaintext', False)) file_format = params.get('format', "csv") if file_format in ('password', 'passwd'): column_mapping = { "userid": 2, "username": 0, "phone": 8, "mobile": 7, "email": 9, "surname": 5, "givenname": 4, "password": 1} format_reader = PasswdFormatReader() elif file_format in ('csv'): skip_header = boolean(params.get('skip_header', False)) if skip_header: data = '\n'.join(data.split('\n')[1:]) column_mapping = { "username": 0, "userid": 1, "surname": 2, "givenname": 3, "email": 4, "phone": 5, "mobile": 6, "password": 7} delimiter = str(params.get('delimiter', ",")) quotechar = str(params.get('quotechar', '"')) format_reader = DefaultFormatReader() format_reader.delimiter = delimiter format_reader.quotechar = quotechar column_mapping = params.get('column_mapping', column_mapping) else: raise Exception('unspecified file foramt') # we have to convert the column_mapping back into an dict if (isinstance(column_mapping, str) or isinstance(column_mapping, unicode)): column_mapping = json.loads(column_mapping) # prevent overwrite of existing unmanaged resolver resolvers = getResolverList() if resolver_name in resolvers: if not resolvers[resolver_name].get('readonly', False): raise Exception("Unmanged resolver with same name: %r" " already exists!" % resolver_name) # -------------------------------------------------------------- -- # feed the engine :) # use a LinOTP Database context for Sessions and Engine db_context = LinOTP_DatabaseContext( SqlSession=Session, SqlEngine=linotp.model.meta.engine) # define the import into an SQL database + resolver import_handler = SQLImportHandler( groupid=groupid, resolver_name=resolver_name, database_context=db_context) # create the UserImporter with the required mapping user_import = UserImport(import_handler) user_import.set_mapping(column_mapping) # and run the data processing result = user_import.import_csv_users( data, dryrun=dryrun, format_reader=format_reader, passwords_in_plaintext=passwords_in_plaintext ) if dryrun: return sendResult(response, result) # -------------------------------------------------------------- -- # create / extend target realm for the resolver resolver_spec = import_handler.get_resolver_spec() Session.commit() return sendResult(response, result) except PolicyException as pexx: log.exception("Error during user import: %r", pexx) Session.rollback() return sendError(response, "%r" % pexx, 1) except Exception as exx: log.exception("Error during user import: %r" % exx) Session.rollback() return sendError(response, "%r" % exx) finally: Session.close() log.debug('done')
def import_users(self): """ import users from a csv file into an dedicated sql resolver """ try: params = self.request_params # -------------------------------------------------------------- -- # processing required arguments try: data_file = request.files["file"] resolver_name = params["resolver"] except KeyError as exx: log.error("Missing parameter: %r", exx) raise ParameterError("Missing parameter: %r" % exx) if resolver_name == current_app.config["ADMIN_RESOLVER_NAME"]: raise DeleteForbiddenError( f"default admin resolver {resolver_name} is not allowed " "to be overwritten!") groupid = resolver_name # process file upload data data = data_file # -- ----------------------------------------------------------- -- # In case of form post requests, it is a "instance" of FileStorage # i.e. the Filename is selected in the browser and the data is # transferred in an iframe. # see: http://jquery.malsup.com/form/#sample4 # -- ----------------------------------------------------------- -- if isinstance(data_file, FileStorage): data = data_file.read() data = data.decode() # -------------------------------------------------------------- -- # process the other arguments dryrun = boolean(params.get("dryrun", False)) passwords_in_plaintext = boolean( params.get("passwords_in_plaintext", False)) file_format = params.get("format", "csv") if file_format in ("password", "passwd"): column_mapping = { "userid": 2, "username": 0, "phone": 8, "mobile": 7, "email": 9, "surname": 5, "givenname": 4, "password": 1, } format_reader = PasswdFormatReader() elif file_format in ("csv"): skip_header = boolean(params.get("skip_header", False)) if skip_header: data = "\n".join(data.split("\n")[1:]) column_mapping = { "username": 0, "userid": 1, "surname": 2, "givenname": 3, "email": 4, "phone": 5, "mobile": 6, "password": 7, } delimiter = str(params.get("delimiter", ",")) quotechar = str(params.get("quotechar", '"')) format_reader = DefaultFormatReader() format_reader.delimiter = delimiter format_reader.quotechar = quotechar column_mapping = params.get("column_mapping", column_mapping) else: raise Exception("unspecified file foramt") # we have to convert the column_mapping back into an dict if isinstance(column_mapping, str): column_mapping = json.loads(column_mapping) # prevent overwrite of existing unmanaged resolver checkPolicyPre("system", "setResolver") resolvers = getResolverList() if resolver_name in resolvers: if not resolvers[resolver_name].get("readonly", False): raise Exception("Unmanged resolver with same name: %r" " already exists!" % resolver_name) # -------------------------------------------------------------- -- # feed the engine :) # use a LinOTP Database context for Sessions and Engine db_context = LinOTP_DatabaseContext(SqlSession=db.session, SqlEngine=db.engine) # define the import into an SQL database + resolver import_handler = SQLImportHandler( groupid=groupid, resolver_name=resolver_name, database_context=db_context, ) # create the UserImporter with the required mapping user_import = UserImport(import_handler) user_import.set_mapping(column_mapping) # and run the data processing result = user_import.import_csv_users( data, dryrun=dryrun, format_reader=format_reader, passwords_in_plaintext=passwords_in_plaintext, ) if dryrun: return sendResult(response, result) # -------------------------------------------------------------- -- # create / extend target realm for the resolver resolver_spec = import_handler.get_resolver_spec() db.session.commit() return sendResult(response, result) except PolicyException as pexx: log.error("Error during user import: %r", pexx) db.session.rollback() return sendError(response, "%r" % pexx, 1) except Exception as exx: log.error("Error during user import: %r", exx) db.session.rollback() return sendError(response, exx) finally: log.debug("done")
def import_users(self): """ import users from a csv file into an dedicated sql resolver """ try: params = {} params.update(request.POST) # -------------------------------------------------------------- -- # processing required arguments try: data_file = request.POST['file'] resolver_name = params['resolver'] except KeyError as exx: log.exception("Missing parameter: %r", exx) raise ParameterError("Missing parameter: %r" % exx) groupid = resolver_name # process file upload data data = data_file # -- ----------------------------------------------------------- -- # In case of form post requests, it is a "instance" of FieldStorage # i.e. the Filename is selected in the browser and the data is # transferred in an iframe. # see: http://jquery.malsup.com/form/#sample4 # -- ----------------------------------------------------------- -- if isinstance(data_file, FieldStorage): data = data_file.value # -------------------------------------------------------------- -- # process the other arguments dryrun = boolean(params.get('dryrun', False)) passwords_in_plaintext = boolean( params.get('passwords_in_plaintext', False)) file_format = params.get('format', "csv") if file_format in ('password', 'passwd'): column_mapping = { "userid": 2, "username": 0, "phone": 8, "mobile": 7, "email": 9, "surname": 5, "givenname": 4, "password": 1 } format_reader = PasswdFormatReader() elif file_format in ('csv'): skip_header = boolean(params.get('skip_header', False)) if skip_header: data = '\n'.join(data.split('\n')[1:]) column_mapping = { "username": 0, "userid": 1, "surname": 2, "givenname": 3, "email": 4, "phone": 5, "mobile": 6, "password": 7 } delimiter = str(params.get('delimiter', ",")) quotechar = str(params.get('quotechar', '"')) format_reader = DefaultFormatReader() format_reader.delimiter = delimiter format_reader.quotechar = quotechar column_mapping = params.get('column_mapping', column_mapping) else: raise Exception('unspecified file foramt') # we have to convert the column_mapping back into an dict if (isinstance(column_mapping, str) or isinstance(column_mapping, unicode)): column_mapping = json.loads(column_mapping) # prevent overwrite of existing unmanaged resolver resolvers = getResolverList() if resolver_name in resolvers: if not resolvers[resolver_name].get('readonly', False): raise Exception("Unmanged resolver with same name: %r" " already exists!" % resolver_name) # -------------------------------------------------------------- -- # feed the engine :) # use a LinOTP Database context for Sessions and Engine db_context = LinOTP_DatabaseContext( SqlSession=Session, SqlEngine=linotp.model.meta.engine) # define the import into an SQL database + resolver import_handler = SQLImportHandler(groupid=groupid, resolver_name=resolver_name, database_context=db_context) # create the UserImporter with the required mapping user_import = UserImport(import_handler) user_import.set_mapping(column_mapping) # and run the data processing result = user_import.import_csv_users( data, dryrun=dryrun, format_reader=format_reader, passwords_in_plaintext=passwords_in_plaintext) if dryrun: return sendResult(response, result) # -------------------------------------------------------------- -- # create / extend target realm for the resolver resolver_spec = import_handler.get_resolver_spec() Session.commit() return sendResult(response, result) except PolicyException as pexx: log.exception("Error during user import: %r", pexx) Session.rollback() return sendError(response, "%r" % pexx, 1) except Exception as exx: log.exception("Error during user import: %r" % exx) Session.rollback() return sendError(response, "%r" % exx) finally: Session.close() log.debug('done')
def autosync(self, hmac2Otp, anOtpVal): ''' auto - sync the token based on two otp values - internal method to realize the autosync within the checkOtp method :param hmac2Otp: the hmac object (with reference to the token secret) :type hmac2Otp: hmac object :param anOtpVal: the actual otp value :type anOtpVal: string :return: counter or -1 if otp does not exist :rtype: int ''' if not boolean(getFromConfig("AutoResync", False)): log.info('autosync is not enabled') return -1 info = self.getTokenInfo() syncWindow = self.getSyncWindow() # check if the otpval is valid in the sync scope otp_counter = hmac2Otp.checkOtp(anOtpVal, syncWindow, symetric=True) if otp_counter == -1: log.info('no valid otp in auto resync window') return -1 # ------------------------------------------------------------------ -- # protect against a replay # if the counter belonging to the provided otp is lower than the # stored counter (which is the next expected counter), then we deny # the resync as it might be replay if otp_counter < self.getOtpCount(): log.info('otp before the last verified valid otp!') return -1 # ------------------------------------------------------------------ -- # taken from the tokeninfo: # check if we have the first otp for the auto resync if "otp1c" not in info: info["otp1c"] = otp_counter self.setTokenInfo(info) log.info('preserved the first otp counter for resync') return -1 # ------------------------------------------------------------------ -- # now we have 2 otps - the first from the former request, # the second otp from the current auto sync request otp1c = info["otp1c"] otp2c = otp_counter if otp2c <= otp1c: # the otps are not in right order log.info('OTP values are not in the right order!') return -1 if (otp2c - otp1c) > self.resyncDiffLimit: # assert that the otps are not too far apart log.info('the otps are too far apart for resync!') # if so, we take the new one as the auto sync base info["otp1c"] = otp2c self.setTokenInfo(info) return -1 # reset the resync info self.removeFromTokenInfo("otp1c") return otp_counter
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 = boolean(getFromConfig("linotpGetotp.active", False)) 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.error("[getotp] gettoken/getotp policy failed: %r", pe) db.session.rollback() return sendError(response, pe, 1) except Exception as exx: log.error("[getotp] gettoken/getotp failed: %r", exx) db.session.rollback() return sendError(response, "gettoken/getotp failed: %s" % exx, 0)
def prepare_resolver_parameter(new_resolver_name, param, previous_name=None): """ prepare the create/update/rename of a resolver used in system/setResolver and admin/testresolver :param new_resolver_name: the name of the new/current resolver :param param: the new set of parameters :param previous_name: the previous name of the resolver :return: tuple of set of potential extended parameters and the list of the missing parameters """ primary_key_changed = False resolver_cls = get_resolver_class(param['type']) if resolver_cls is None: raise Exception("no such resolver type '%r' defined!" % param['type']) # for rename and update, we support the merge with previous parameters if previous_name: # get the parameters of the previous resolver previous_resolver = getResolverInfo(previous_name, passwords=True) previous_param = previous_resolver['data'] previous_readonly = boolean(previous_resolver.get('readonly', False)) # get the critical parameters for this resolver type # and check if these parameters have changed is_critical = resolver_cls.is_change_critical( new_params=param, previous_params=previous_param) # if there are no critical changes, we can transfer # the encrypted parameters from previous resolver if not is_critical: merged_params = resolver_cls.merge_crypted_parameters( new_params=param, previous_params=previous_param) param.update(merged_params) # in case of a readonly resolver, no changes beneath a rename # is allowed if previous_readonly: for key, p_value in previous_param.items(): # we inherit the readonly parameter if it is # not provided by the ui if key == 'readonly': param['readonly'] = boolean(p_value) continue if p_value != param.get(key, ''): raise Exception('Readonly Resolver Change not allowed!') # check if the primary key changed - if so, we need # to migrate the resolver primary_key_changed = resolver_cls.primary_key_changed( new_params=param, previous_params=previous_param) # ---------------------------------------------------------- -- # check if all crypted parameters are included missing = resolver_cls.missing_crypted_parameters(param) return param, missing, primary_key_changed
def getResolverInfo(resolvername, passwords=False): """ return the resolver info of the given resolvername :param resolvername: the requested resolver :type resolvername: string :return : dict of resolver description """ result = {"type": None, "data": {}, "resolver": resolvername} linotp_config = context.get("Config") resolver_types = get_resolver_types() local_admin_resolver = current_app.config["ADMIN_RESOLVER_NAME"] # --------------------------------------------------------------------- -- # lookup, which resolver type is associated with this resolver name for config_entry in linotp_config: if config_entry.endswith("." + resolvername): # check if this is a resolver definition, starting with linotp. # and continuing with a resolver type part = config_entry.split(".") if ( len(part) > 3 and part[0] == "linotp" and part[1] in resolver_types ): resolver_type = part[1] break else: return result # now we can load the resolver config unsing the resolver class resolver_cls = get_resolver_class(resolver_type) if resolver_cls is None: raise Exception("no such resolver type '%r' defined!" % resolver_type) result["spec"] = resolver_cls.db_prefix + "." + resolvername res_conf, _missing = resolver_cls.filter_config( linotp_config, resolvername ) # --------------------------------------------------------------------- -- # now prepare the resolver config output, which should contain # # - no global entries, starting with 'linotp.' # - adjusted passwords # - all values as text for key in list(res_conf.keys()): # suppress global config entries if key.startswith("linotp."): del res_conf[key] continue # should passwords be displayed? if key in resolver_cls.crypted_parameters: # we have to be sure that we only have encrypted data objects for # secret data if not isinstance(res_conf[key], EncryptedData): raise Exception("Encrypted Data Object expected") # if parameter password is True, we need to unencrypt if passwords: res_conf[key] = res_conf[key].get_unencrypted() # as we have in the resolver config typed values, this might # lead to some trouble. so we prepare for output comparison # the string representation if not isinstance(res_conf[key], str): res_conf[key] = "%r" % res_conf[key] if "readonly" in res_conf: readonly = False try: readonly = boolean(res_conf["readonly"]) except Exception: log.info( "Failed to convert 'readonly' attribute %r:%r", resolvername, res_conf["readonly"], ) if readonly: result["readonly"] = True result["type"] = resolver_type result["data"] = res_conf result["admin"] = resolvername in get_admin_resolvers() # set the immutable flag if its the local_admin_resolver result["immutable"] = local_admin_resolver == resolvername return result
def getResolverList(filter_resolver_type=None, config=None): """ Gets the list of configured resolvers :param filter_resolver_type: Only resolvers of the given type are returned :type filter_resolver_type: string :rtype: Dictionary of the resolvers and their configuration """ Resolvers = {} resolvertypes = get_resolver_types() local_admin_resolver = current_app.config["ADMIN_RESOLVER_NAME"] admin_resolvers = get_admin_resolvers() if not config: conf = context.get("Config") else: conf = config for entry in conf: for typ in resolvertypes: if entry.startswith("linotp." + typ): # the resolver might contain dots "." so take # all after the 3rd dot for the resolver name r = {} resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break r["resolvername"] = resolver[3] r["entry"] = entry r["type"] = typ # return the resolver spec, which is required to define a realm resolver_cls = get_resolver_class(typ) r["spec"] = resolver_cls.db_prefix + "." + resolver[3] r["admin"] = resolver[3] in admin_resolvers # set the immutable flag if its the local_admin_resolver r["immutable"] = local_admin_resolver == resolver[3] readonly_entry = ".".join( [resolver[0], resolver[1], "readonly", resolver[3]] ) if readonly_entry in conf: readonly = False try: readonly = boolean(conf[readonly_entry]) except Exception as _exx: log.info( "Failed to convert 'readonly' attribute %r:%r", readonly_entry, conf[readonly_entry], ) if readonly: r["readonly"] = True # # this is a patch for a hack: # # as entry, the first found resolver is shown # as the PasswdResolver only has one entry, this always # has been 'fileName', which now as could be 'readonly' # thus we skip the readonly entry: key = resolver[2] if key == "readonly": continue if (filter_resolver_type is None) or ( filter_resolver_type and filter_resolver_type == typ ): Resolvers[resolver[3]] = r # Dont check the other resolver types break return Resolvers
def getResolverList(filter_resolver_type=None): ''' Gets the list of configured resolvers :param filter_resolver_type: Only resolvers of the given type are returned :type filter_resolver_type: string :rtype: Dictionary of the resolvers and their configuration ''' Resolvers = {} resolvertypes = get_resolver_types() conf = context.get('Config') # conf = getLinotpConfig() for entry in conf: for typ in resolvertypes: if entry.startswith("linotp." + typ): # the realm might contain dots "." # so take all after the 3rd dot for realm r = {} resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break r["resolvername"] = resolver[3] r["entry"] = entry r["type"] = typ readonly_entry = '.'.join( [resolver[0], resolver[1], 'readonly', resolver[3]]) if readonly_entry in conf: readonly = False try: readonly = boolean(conf[readonly_entry]) except Exception as _exx: log.info( "Failed to convert 'readonly' attribute" " %r:%r", readonly_entry, conf[readonly_entry]) if readonly: r["readonly"] = True # # this is a patch for a hack: # # as entry, the first found resolver is shown # as the PasswdResolver only has one entry, this always # has been 'fileName', which now as could be 'readonly' # thus we skip the readonly entry: key = resolver[2] if key == "readonly": continue if ((filter_resolver_type is None) or (filter_resolver_type and filter_resolver_type == typ)): Resolvers[resolver[3]] = r # Dont check the other resolver types break return Resolvers
def _get_client_from_request(request=None): ''' This function returns the client as it is passed in the HTTP Request. This is the very HTTP client, that contacts the LinOTP server. ''' client = request.environ.get( 'REMOTE_ADDR', request.environ.get( 'HTTP_REMOTE_ADDR', None)) x_forwarded_for = boolean(config.get( 'client.X_FORWARDED_FOR', getFromConfig( 'client.X_FORWARDED_FOR', 'False'))) if x_forwarded_for: # check, if the request passed by a qualified proxy remote_addr = client x_forwarded_proxies = config.get( 'client.FORWARDED_PROXY', getFromConfig( 'client.FORWARDED_PROXY', '')).split(',') for x_forwarded_proxy in x_forwarded_proxies: if _is_addr_in_network(remote_addr, x_forwarded_proxy): ref_clients = request.environ.get('HTTP_X_FORWARDED_FOR', '') for ref_client in ref_clients.split(','): # the first ip in the list is the originator client = ref_client.strip() break # "Forwarded" Header # # In 2014 RFC 7239 standardized a new Forwarded header with similar purpose # but more features compared to XFF.[28] An example of a Forwarded header # syntax: # # Forwarded: for=192.0.2.60; proto=http; by=203.0.113.43 forwarded = boolean(config.get( 'client.FORWARDED', getFromConfig( 'client.FORWARDED', 'false'))) if forwarded: # check, if the request passed by a qaulified proxy remote_addr = client forwarded_proxies = config.get( 'client.FORWARDED_PROXY', getFromConfig( 'client.FORWARDED_PROXY', '').split(',')) for forwarded_proxy in forwarded_proxies: if _is_addr_in_network(remote_addr, forwarded_proxy): # example is: # "Forwarded: for=192.0.2.43, for=198.51.100.17" entries = request.environ.get( 'HTTP_FORWARDED', request.environ.get( 'Forwarded', '')) forwarded_set = [] entries = entries.replace("Forwarded:", "") for entry in entries.split(','): if entry.lower().startswith('for'): value = entry.split('=')[1] value = value.split(';')[0].strip() if ']' in value: ipvalue = value.split(']')[0].split('[')[1] elif ':' in value: ipvalue = value.split(':')[0] else: ipvalue = value forwarded_set.append(ipvalue.strip('"')) for originator in forwarded_set: client = originator break log.debug("got the client %s" % client) return client
def getResolverInfo(resolvername, passwords=False): ''' return the resolver info of the given resolvername :param resolvername: the requested resolver :type resolvername: string :return : dict of resolver description ''' result = {"type": None, "data": {}, "resolver": resolvername} linotp_config = context.get('Config') resolver_types = get_resolver_types() # --------------------------------------------------------------------- -- # lookup, which resolver type is associated with this resolver name for config_entry in linotp_config: if config_entry.endswith("." + resolvername): # check if this is a resolver definition, starting with linotp. # and continuing with a resolver type part = config_entry.split('.') if (len(part) > 3 and part[0] == 'linotp' and part[1] in resolver_types): resolver_type = part[1] break else: return result # now we can load the resolver config unsing the resolver class resolver_cls = get_resolver_class(resolver_type) if resolver_cls is None: raise Exception("no such resolver type '%r' defined!" % resolver_type) res_conf, _missing = resolver_cls.filter_config(linotp_config, resolvername) # --------------------------------------------------------------------- -- # now prepare the resolver config output, which should contain # # - no global entries, starting with 'linotp.' # - adjusted passwords # - all values as text for key in res_conf.keys(): # suppress global config entries if key.startswith("linotp."): del res_conf[key] continue # should passwords be displayed? if key in resolver_cls.crypted_parameters: if not passwords: res_conf[key] = encryptPassword(res_conf[key]) # as we have in the resolver config typed values, this might # lead to some trouble. so we prepare for output comparison # the string representation if (not isinstance(res_conf[key], str) and not isinstance(res_conf[key], unicode)): res_conf[key] = "%r" % res_conf[key] if 'readonly' in res_conf: readonly = False try: readonly = boolean(res_conf['readonly']) except Exception: log.info("Failed to convert 'readonly' attribute %r:%r", resolvername, res_conf['readonly']) if readonly: result["readonly"] = True result["type"] = resolver_type result["data"] = res_conf return result
def __before__(self, **params): """ __before__ is called before every action :param params: list of named arguments :return: -nothing- or in case of an error a Response created by sendError with the context info 'before' """ action = request_context["action"] try: g.audit["success"] = False g.audit["client"] = get_client(request) c.version = get_version() c.version_ref = base64.encodebytes(c.version.encode())[:6] c.licenseinfo = get_copyright_info() c.polDefs = get_policy_definitions() c.display_provider = boolean( request_context["Config"].get("display_provider", True) ) # -------------------------------------------------------------- -- # check for support of setting admin password c.admin_can_change_password = False if ( "linotpadmin.user" in config and "linotpadmin.password" in config ): c.admin_can_change_password = True # -------------------------------------------------------------- -- # Session handling for the functions, that show data: # Also exclude custom-style.css, since the CSRF check # will always fail and return a HTTP 401 anyway. # A HTTP 404 makes more sense. if request.path.lower() in [ "/manage/", "/manage", "/manage/login", "/manage/audittrail", "/manage/policies", "/manage/tokenview", "/manage/userview", "/manage/help", "/manage/custom-style.css", ]: pass else: check_session(request) except Exception as exx: log.error("[__before__::%r] exception %r", action, exx) db.session.rollback() return sendError(response, exx, context="before") finally: log.debug("[__before__::%r] done", action)
def getmultiotp(self): """ This function is used to retrieve multiple otp values 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/getmultiotp arguments: serial - the serial number of the token count - number of otp values to return curTime - used ONLY for internal testing: datetime.datetime object returns: JSON response """ getotp_active = boolean(getFromConfig("linotpGetotp.active", False)) if not getotp_active: return sendError(response, "getotp is not activated.", 0) param = self.request_params ret = {} try: serial = getParam(param, "serial", required) count = int(getParam(param, "count", required)) curTime = getParam(param, "curTime", optional) view = getParam(param, "view", optional) r1 = checkPolicyPre("admin", "getotp", param) log.debug("[getmultiotp] admin-getotp policy: %s", r1) max_count = checkPolicyPre("gettoken", "max_count", param) log.debug("[getmultiotp] maxcount policy: %s", max_count) if count > max_count: count = max_count log.debug("[getmultiotp] retrieving OTP value for token %s", serial) ret = get_multi_otp(serial, count=int(count), curTime=curTime) ret["serial"] = serial g.audit["success"] = True db.session.commit() if view: c.ret = ret return render("/selfservice/multiotp_view.mako").decode( "utf-8") else: return sendResult(response, ret, 0) except PolicyException as pe: log.error("[getotp] gettoken/getotp policy failed: %r", pe) db.session.rollback() return sendError(response, pe, 1) except Exception as exx: log.error("[getmultiotp] gettoken/getmultiotp failed: %r", exx) db.session.rollback() return sendError(response, "gettoken/getmultiotp failed: %r" % exx, 0)
def getResolverList(filter_resolver_type=None, config=None): ''' Gets the list of configured resolvers :param filter_resolver_type: Only resolvers of the given type are returned :type filter_resolver_type: string :rtype: Dictionary of the resolvers and their configuration ''' Resolvers = {} resolvertypes = get_resolver_types() if not config: conf = context.get('Config') else: conf = config for entry in conf: for typ in resolvertypes: if entry.startswith("linotp." + typ): # the realm might contain dots "." # so take all after the 3rd dot for realm r = {} resolver = entry.split(".", 3) # An old entry without resolver name if len(resolver) <= 3: break r["resolvername"] = resolver[3] r["entry"] = entry r["type"] = typ readonly_entry = '.'.join([resolver[0], resolver[1], 'readonly', resolver[3]]) if readonly_entry in conf: readonly = False try: readonly = boolean(conf[readonly_entry]) except Exception as _exx: log.info("Failed to convert 'readonly' attribute" " %r:%r", readonly_entry, conf[readonly_entry]) if readonly: r["readonly"] = True # # this is a patch for a hack: # # as entry, the first found resolver is shown # as the PasswdResolver only has one entry, this always # has been 'fileName', which now as could be 'readonly' # thus we skip the readonly entry: key = resolver[2] if key == "readonly": continue if ((filter_resolver_type is None) or (filter_resolver_type and filter_resolver_type == typ)): Resolvers[resolver[3]] = r # Dont check the other resolver types break return Resolvers
def _get_client_from_request(request=None): ''' This function returns the client as it is passed in the HTTP Request. This is the very HTTP client, that contacts the LinOTP server. ''' client = request.environ.get('REMOTE_ADDR', request.environ.get('HTTP_REMOTE_ADDR', None)) x_forwarded_for = boolean( config.get('client.X_FORWARDED_FOR', getFromConfig('client.X_FORWARDED_FOR', 'False'))) if x_forwarded_for: # check, if the request passed by a qualified proxy remote_addr = client x_forwarded_proxies = config.get( 'client.FORWARDED_PROXY', getFromConfig('client.FORWARDED_PROXY', '')).split(',') for x_forwarded_proxy in x_forwarded_proxies: if _is_addr_in_network(remote_addr, x_forwarded_proxy): ref_clients = request.environ.get('HTTP_X_FORWARDED_FOR', '') for ref_client in ref_clients.split(','): # the first ip in the list is the originator client = ref_client.strip() break # "Forwarded" Header # # In 2014 RFC 7239 standardized a new Forwarded header with similar purpose # but more features compared to XFF.[28] An example of a Forwarded header # syntax: # # Forwarded: for=192.0.2.60; proto=http; by=203.0.113.43 forwarded = boolean( config.get('client.FORWARDED', getFromConfig('client.FORWARDED', 'false'))) if forwarded: # check, if the request passed by a qaulified proxy remote_addr = client forwarded_proxies = config.get( 'client.FORWARDED_PROXY', getFromConfig('client.FORWARDED_PROXY', '').split(',')) for forwarded_proxy in forwarded_proxies: if _is_addr_in_network(remote_addr, forwarded_proxy): # example is: # "Forwarded: for=192.0.2.43, for=198.51.100.17" entries = request.environ.get( 'HTTP_FORWARDED', request.environ.get('Forwarded', '')) forwarded_set = [] entries = entries.replace("Forwarded:", "") for entry in entries.split(','): if entry.lower().startswith('for'): value = entry.split('=')[1] value = value.split(';')[0].strip() if ']' in value: ipvalue = value.split(']')[0].split('[')[1] elif ':' in value: ipvalue = value.split(':')[0] else: ipvalue = value forwarded_set.append(ipvalue.strip('"')) for originator in forwarded_set: client = originator break log.debug("got the client %s" % client) return client
def getResolverInfo(resolvername, passwords=False): ''' return the resolver info of the given resolvername :param resolvername: the requested resolver :type resolvername: string :return : dict of resolver description ''' result = {"type": None, "data": {}, "resolver": resolvername} linotp_config = context.get('Config') resolver_types = get_resolver_types() # --------------------------------------------------------------------- -- # lookup, which resolver type is associated with this resolver name for config_entry in linotp_config: if config_entry.endswith("." + resolvername): # check if this is a resolver definition, starting with linotp. # and continuing with a resolver type part = config_entry.split('.') if (len(part) > 3 and part[0] == 'linotp' and part[1] in resolver_types): resolver_type = part[1] break else: return result # now we can load the resolver config unsing the resolver class resolver_cls = get_resolver_class(resolver_type) if resolver_cls is None: raise Exception("no such resolver type '%r' defined!" % resolver_type) res_conf, _missing = resolver_cls.filter_config(linotp_config, resolvername) # --------------------------------------------------------------------- -- # now prepare the resolver config output, which should contain # # - no global entries, starting with 'linotp.' # - adjusted passwords # - all values as text for key in res_conf.keys(): # suppress global config entries if key.startswith("linotp."): del res_conf[key] continue # should passwords be displayed? if key in resolver_cls.crypted_parameters: if not passwords: res_conf[key] = encryptPassword(res_conf[key]) # as we have in the resolver config typed values, this might # lead to some trouble. so we prepare for output comparison # the string representation if (not isinstance(res_conf[key], str) and not isinstance(res_conf[key], unicode)): res_conf[key] = "%r" % res_conf[key] if 'readonly' in res_conf: readonly = False try: readonly = boolean(res_conf['readonly']) except Exception: log.info("Failed to convert 'readonly' attribute %r:%r", resolvername, res_conf['readonly']) if readonly: result["readonly"] = True result["type"] = resolver_type result["data"] = res_conf return result
def _submitMessage(self, phone, message): ''' Submits the message for phone to the email gateway. Returns true in case of success ''' ret = False if ('mailserver' not in self.config or 'mailsender' not in self.config or 'mailto' not in self.config): log.error("[submitMessage] incomplete config: %s. mailserver, " "mailsender and mailto needed." % self.config) return ret # prepare the phone number msisdn = 'true' in ("%r" % self.config.get('MSISDN', "false")).lower() if msisdn: phone = self._get_msisdn_phonenumber(phone) # prepare the smtp server connection parameters default_port = 25 start_tls_params = {} start_tls = str(self.config.get("start_tls", False)).lower() == 'true' if start_tls: default_port = 587 start_tls_params_keyfile = self.config.get("keyfile", None) start_tls_params_certfile = self.config.get("certfile", None) use_ssl = str(self.config.get("use_ssl", False)).lower() == 'true' if use_ssl: default_port = 465 server = self.config.get("mailserver") port = int(self.config.get("mailserver_port", default_port)) # support for mailserver syntax like server:port # if port is not explicit defined if "mailserver_port" not in self.config and ':' in server: server, _sep, port = server.rpartition(':') user = self.config.get("mailuser") password = self.config.get("mailpassword") fromaddr = self.config.get("mailsender", "linotp@localhost") toaddr = self.config.get("mailto") subject = self.config.get("subject", "") body = self.config.get("body", "") log.debug("[submitMessage] submitting message %s to %s", message, phone) toaddr = string.replace(toaddr, PHONE_TAG, phone) if not subject: subject = "[LinOTP]" subject = string.replace(subject, PHONE_TAG, phone) subject = string.replace(subject, MSG_TAG, message) if not body: body = "<otp>" body = string.replace(body, PHONE_TAG, phone) body = string.replace(body, MSG_TAG, message) msg = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % (fromaddr, toaddr, subject, body)) serv = None try: # if SSL is defined, we require a different base class if not use_ssl: serv = smtplib.SMTP(server, port) else: serv = smtplib.SMTP_SSL(server, port) serv.set_debuglevel(1) serv.ehlo() if start_tls and not use_ssl: if serv.has_extn('STARTTLS'): serv.starttls(start_tls_params_keyfile, start_tls_params_certfile) serv.ehlo() else: log.error("Start_TLS not supported:") raise Exception("Start_TLS requested but not supported" " by server %r" % server) if user: if serv.has_extn('AUTH'): log.debug("authenticating to mailserver, user: %s, " "pass: %r", user, sha256(password).hexdigest()) serv.login(user, password) else: log.error("AUTH not supported:") data_dict = serv.sendmail(fromaddr, toaddr, msg) log.debug("sendmail: %r", data_dict) (code, response) = serv.quit() log.debug("quit: (%r) %r", code, response) ret = True except smtplib.socket.error as exc: log.exception('Error: could not connect to server') if boolean(self.config.get('raise_exception', True)): raise ProviderNotAvailable('Error: could not connect ' 'to server: %r' % exc) ret = False except Exception as exx: log.exception("[submitMessage] %s", exx) if boolean(self.config.get('raise_exception', False)): raise Exception(exx) ret = False finally: if serv: serv.close() return ret
def prepare_resolver_parameter(new_resolver_name, param, previous_name=None): """ prepare the create/update/rename of a resolver used in system/setResolver and admin/testresolver :param new_resolver_name: the name of the new/current resolver :param param: the new set of parameters :param previous_name: the previous name of the resolver :return: tuple of set of potential extended parameters and the list of the missing parameters """ primary_key_changed = False resolver_cls = get_resolver_class(param['type']) if resolver_cls is None: raise Exception("no such resolver type '%r' defined!" % param['type']) # for rename and update, we support the merge with previous parameters if previous_name: # get the parameters of the previous resolver previous_resolver = getResolverInfo(previous_name, passwords=True) previous_param = previous_resolver['data'] previous_readonly = boolean(previous_resolver.get('readonly', False)) # get the critical parameters for this resolver type # and check if these parameters have changed is_critical = resolver_cls.is_change_critical( new_params=param, previous_params=previous_param) # if there are no critical changes, we can transfer # the encrypted parameters from previous resolver if not is_critical: merged_params = resolver_cls.merge_crypted_parameters( new_params=param, previous_params=previous_param) param.update(merged_params) # in case of a readonly resolver, no changes beneath a rename # is allowed if previous_readonly: for key, p_value in previous_param.items(): # we inherit the readonly parameter if it is # not provided by the ui if key == 'readonly': param['readonly'] = boolean(p_value) continue if p_value != param.get(key, ''): raise Exception('Readonly Resolver Change not allowed!') # check if the primary key changed - if so, we need # to migrate the resolver primary_key_changed = resolver_cls.primary_key_changed( new_params=param, previous_params=previous_param) # ---------------------------------------------------------- -- # check if all crypted parameters are included missing = resolver_cls.missing_crypted_parameters(param) return param, missing, primary_key_changed
def get_cache(cache_name: str, scope: str=None) -> Optional[Cache]: """ load the cache with cache_name and scope Each cache defines the following configuration parameters: linotp.{cache_name}_cache.enabled Whether the cache is enabled. Defaults to True linotp.{cache_name}_cache.expiration How long the entries are cached for in seconds. Defaults to 3 days. :remark: This cache is only enabled, if the configuration entry 'enabled' evaluates to True and the expiration is of a valid format. Expiration format is defined linotp.lib.type_utils :param cache_name: the name of the cache :param scope: there are related caches, which names are extended by scope used for realm specific caches e.g. for users :return: the cache or None if not enabled, wrt to typing the cache is not deterministic as the cache type is returned by the app.getCacheManager() which could be a beaker or something else """ # --------------------------------------------------------------------- -- # evaluate the config lookup keys config = context['Config'] config_basename = "linotp." + cache_name + "_cache" enabled_entry = config_basename + ".enabled" expiration_entry = config_basename + ".expiration" # --------------------------------------------------------------------- -- enabled = boolean(config.get(enabled_entry, True)) if not enabled: return None # --------------------------------------------------------------------- -- # handle expiration format expiration_conf = config.get(expiration_entry, 36 * 3600) try: expiration = get_duration(expiration_conf) except ValueError: log.info("caching is disabled due to a value error for expiration " "definition %r" % expiration_conf) return None # --------------------------------------------------------------------- -- # retrieve the cache from the cache manager cache_manager = current_app.getCacheManager() if not cache_manager: log.info('No Cache Manager found!') return None cache_fullname = cache_name if scope: cache_fullname = "%s::%s" % (cache_name, scope) resolver_config_cache = cache_manager.get_cache( cache_fullname, type="memory", expiretime=expiration) return resolver_config_cache
def initToken(self): """ init one DPW token """ self.createDPWToken("dpw1", "12341234123412341234123412341234") """ curTime = datetime.datetime(2012, 5, 16, 9, 0, 52, 227413) "12-05-22": "202690", "12-05-23": "252315", "12-05-20": "6", "12-05-21": "325819", "12-05-24": "263973", "12-05-25": "321965", "12-05-17": "028193", "12-05-16": "427701", "12-05-19": "167074", "12-05-18": "857788" """ self.createHOTPToken("hotp1", "12341234123412341234123412341234") """ "0": "819132", "1": "301156", "2": "586172", "3": "720026", "4": "062511", "5": "598723", "6": "770725", "7": "596337", "8": "647211", "9": "294016", "10": "161051", "11": "886458" """ self.createTOTPToken("totp1", self.seed, timeStep=30) """ T0=44576428.686175205 (*30) "44576428": "33726427", "44576429": "84341529", "44576430": "35692495", "44576431": "70995873", "44576432": "12048114", "44576433": "06245460", "44576434": "10441015", "44576435": "50389782", "44576436": "78905052", "44576437": "52978758", "44576438": "90386435", "44576439": "76892112" """ response = self.setTokenRealm("dpw1", "mydefrealm") assert '"status": true' in response, response response = self.setTokenRealm("hotp1", "mydefrealm") assert '"status": true' in response, response response = self.setTokenRealm("totp1", "mydefrealm") assert '"status": true' in response, response params = {"user": "******", "serial": "totp1"} response = self.make_admin_request(action="assign", params=params) assert '"status": true' in response, response parameters = {} response = self.make_system_request( action="getRealms", params=parameters ) assert '"status": true' in response, response parameters = { "name": "getmultitoken", "scope": "gettoken", "realm": "mydefrealm", "action": ( "max_count_dpw=10, max_count_hotp=10, max_count_totp=10" ), "user": "******", } response = self.make_system_request( action="setPolicy", params=parameters ) assert '"status": true' in response, response response = self.make_system_request(action="getConfig", params={}) assert response.json["result"]["status"], response config = response.json["result"]["value"] assert "linotpGetotp.active" in config, response assert boolean(config["linotpGetotp.active"]) is True return