def test_01_create_realm(self): rid = save_resolver({"resolver": self.resolvername1, "type": "passwdresolver", "fileName": "/etc/passwd"}) self.assertTrue(rid > 0, rid) rid = save_resolver({"resolver": self.resolvername2, "type": "passwdresolver", "fileName": "/etc/secrets"}) self.assertTrue(rid > 0, rid) (added, failed) = set_realm(self.realm1, [self.resolvername1, self.resolvername2]) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 2) # test the realms realms = get_realms() self.assertTrue(self.realm1 in realms, realms) self.assertTrue(realms.get("realm1").get("default"), realms) # try to create realm with invalid name self.assertRaises(Exception, set_realm, "#####") # update the resolver list: (added, failed) = set_realm(self.realm1, [self.resolvername1, "non exiting"]) self.assertTrue(len(failed) == 1) self.assertTrue(len(added) == 1) self.assertTrue(realm_is_defined(self.realm1)) self.assertTrue(realm_is_defined("non exist") is False)
def test_10_delete_realm(self): delete_realm(self.realm1) delete_realm("realm2") delete_resolver(self.resolvername1) delete_resolver(self.resolvername2) realms = get_realms() self.assertTrue(len(realms) == 0, realms)
def get_config_documentation(): """ returns an restructured text document, that describes the complete configuration. """ P = PolicyClass() config = get_from_config() resolvers = get_resolver_list() realms = get_realms() policies = P.list_policies() admins = get_db_admins() context = { "system": socket.getfqdn(socket.gethostname()), "date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), "systemconfig": config, "appconfig": current_app.config, "resolverconfig": resolvers, "realmconfig": realms, "policyconfig": policies, "admins": admins } g.audit_object.log({"success": True}) # Three or more line breaks will be changed to two. return re.sub("\n{3,}", "\n\n", render_template("documentation.rst", context=context))
def get_config_documentation(): """ returns an restructured text document, that describes the complete configuration. """ P = PolicyClass() config = get_from_config() resolvers = get_resolver_list() realms = get_realms() policies = P.get_policies() admins = get_db_admins() context = {"system": socket.getfqdn(socket.gethostname()), "date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), "systemconfig": config, "appconfig": current_app.config, "resolverconfig": resolvers, "realmconfig": realms, "policyconfig": policies, "admins": admins} g.audit_object.log({"success": True}) # Three or more line breaks will be changed to two. return re.sub("\n{3,}", "\n\n", render_template("documentation.rst", context=context))
def list(): """ list the available realms """ from privacyidea.lib.realm import get_realms realm_list = get_realms() for name, realm_data in realm_list.iteritems(): resolvernames = [x.get("name") for x in realm_data.get("resolver")] print "%16s: %s" % (name, resolvernames)
def single_page_application(): instance = request.script_root if instance == "/": instance = "" # The backend URL should come from the configuration of the system. backend_url = "" # The default theme. We can change this later theme = current_app.config.get("PI_CSS", DEFAULT_THEME) # Get further customizations customization = current_app.config.get("PI_CUSTOMIZATION", "/static/customize/") customization = customization.strip('/') # TODO: we should add the CSS into PI_CUSTOMZATION/css # Enrollment-Wizard: # PI_CUSTOMIZATION/views/includes/token.enroll.pre.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.pre.bottom.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.bottom.html browser_lang = request.accept_languages.best_match(["en", "de"]) # check if login with REMOTE_USER is allowed. remote_user = "" password_reset = False if not hasattr(request, "all_data"): request.all_data = {} # Depending on displaying the realm dropdown, we fill realms or not. policy_object = PolicyClass() realms = "" client_ip = request.access_route[0] if request.access_route else \ request.remote_addr realm_dropdown = policy_object.get_policies(action=ACTION.REALMDROPDOWN, scope=SCOPE.WEBUI, client=client_ip) if realm_dropdown: realms = ",".join(get_realms().keys()) try: if is_remote_user_allowed(request): remote_user = request.remote_user password_reset = is_password_reset() hsm_ready = True except HSMException: hsm_ready = False return render_template("index.html", instance=instance, backendUrl=backend_url, browser_lang=browser_lang, remote_user=remote_user, theme=theme, password_reset=password_reset, hsm_ready=hsm_ready, customization=customization, realms=realms)
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should apply."), "value": realms.keys() }, "tokenrealm": { "type": "multi", "desc": _("The token realm, for which this event should " "apply."), "value": [{"name": r} for r in realms.keys()] }, "tokentype": { "type": "multi", "desc": _("The type of the token."), "value": [{"name": r} for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, "result_value": { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") } } return cond
def single_page_application(): instance = request.script_root if instance == "/": instance = "" # The backend URL should come from the configuration of the system. backend_url = "" # The default theme. We can change this later theme = current_app.config.get("PI_CSS", DEFAULT_THEME) # Get further customizations customization = current_app.config.get("PI_CUSTOMIZATION", "/static/customize/") customization = customization.strip('/') # TODO: we should add the CSS into PI_CUSTOMZATION/css # Enrollment-Wizard: # PI_CUSTOMIZATION/views/includes/token.enroll.pre.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.pre.bottom.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.bottom.html browser_lang = request.accept_languages.best_match(["en", "de"]) # check if login with REMOTE_USER is allowed. remote_user = "" password_reset = False # Depending on displaying the realm dropdown, we fill realms or not. policy_object = PolicyClass() realms = "" client_ip = request.access_route[0] if request.access_route else \ request.remote_addr realm_dropdown = policy_object.get_policies(action=ACTION.REALMDROPDOWN, scope=SCOPE.WEBUI, client=client_ip) if realm_dropdown: realms = ",".join(get_realms().keys()) try: if is_remote_user_allowed(request): remote_user = request.remote_user password_reset = is_password_reset() hsm_ready = True except HSMException: hsm_ready = False return render_template("index.html", instance=instance, backendUrl=backend_url, browser_lang=browser_lang, remote_user=remote_user, theme=theme, password_reset=password_reset, hsm_ready=hsm_ready, customization=customization, realms=realms)
def actions(cls): """ This method returns a dictionary of allowed actions and possible options in this handler module. :return: dict with actions """ realm_list = get_realms().keys() actions = {ACTION_TYPE.SET_TOKENREALM: {"realm": {"type": "str", "required": True, "description": _("set a new realm of the token"), "value": realm_list}, "only_realm": {"type": "bool", "description": _("The new realm will be the only " "realm of the token. I.e. all " "other realms will be removed " "from this token. Otherwise the " "realm will be added to the token.") } }, ACTION_TYPE.DELETE: {}, ACTION_TYPE.UNASSIGN: {}, ACTION_TYPE.DISABLE: {}, ACTION_TYPE.ENABLE: {}, #"assign": {}, ACTION_TYPE.INIT: {"tokentype": {"type": "str", "required": True, "description": _("Token type to create"), "value": get_token_types() }, #"user": {}, "realm": {"type": "str", "required": False, "description": _("Set the realm of the newly " "created token."), "value": realm_list}, } } return actions
def test_01_create_realm(self): rid = save_resolver({ "resolver": self.resolvername1, "type": "passwdresolver", "fileName": "/etc/passwd" }) self.assertTrue(rid > 0, rid) rid = save_resolver({ "resolver": self.resolvername2, "type": "passwdresolver", "fileName": "/etc/secrets" }) self.assertTrue(rid > 0, rid) (added, failed) = set_realm(self.realm1, [self.resolvername1, self.resolvername2]) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 2) (added, failed) = set_realm(self.realm_dot, [self.resolvername1, self.resolvername2]) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 2) # test the realms realms = get_realms() self.assertTrue(self.realm1 in realms, realms) self.assertTrue(realms.get("realm1").get("default"), realms) self.assertTrue(self.realm_dot in realms, realms) # delete dot realm delete_realm(self.realm_dot) # try to create realm with invalid name self.assertRaises(Exception, set_realm, "#####") # update the resolver list: (added, failed) = set_realm(self.realm1, [self.resolvername1, "non exiting"]) self.assertTrue(len(failed) == 1) self.assertTrue(len(added) == 1) self.assertTrue(realm_is_defined(self.realm1)) self.assertTrue(realm_is_defined("non exist") is False)
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should aply."), "value": realms.keys() }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, "result_value": { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") } } return cond
def test_03_get_specific_realm(self): realm = get_realms(self.realm1) self.assertTrue(self.realm1 in realm, realm) self.assertTrue(len(realm) == 1, realm)
def single_page_application(): instance = request.script_root if instance == "/": instance = "" # The backend URL should come from the configuration of the system. backend_url = "" if current_app.config.get("PI_UI_DEACTIVATED"): # Do not provide the UI return render_template("deactivated.html") # The default theme. We can change this later theme = current_app.config.get("PI_CSS", DEFAULT_THEME) # Get further customizations customization = current_app.config.get("PI_CUSTOMIZATION", "/static/customize/") customization = customization.strip('/') # TODO: we should add the CSS into PI_CUSTOMZATION/css # Enrollment-Wizard: # PI_CUSTOMIZATION/views/includes/token.enroll.pre.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.pre.bottom.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.bottom.html # Get the hidden external links external_links = current_app.config.get("PI_EXTERNAL_LINKS", True) # Get the logo file logo = current_app.config.get("PI_LOGO", "privacyIDEA1.png") browser_lang = request.accept_languages.best_match(["en", "de"]) # check if login with REMOTE_USER is allowed. remote_user = "" password_reset = False if not hasattr(request, "all_data"): request.all_data = {} # Depending on displaying the realm dropdown, we fill realms or not. policy_object = PolicyClass() realms = "" client_ip = request.access_route[0] if request.access_route else \ request.remote_addr realm_dropdown = policy_object.get_policies(action=ACTION.REALMDROPDOWN, scope=SCOPE.WEBUI, client=client_ip, active=True) if realm_dropdown: try: realm_dropdown_values = policy_object.get_action_values( action=ACTION.REALMDROPDOWN, scope=SCOPE.WEBUI, client=client_ip) # Use the realms from the policy. realms = ",".join(realm_dropdown_values) except AttributeError as ex: # The policy is still a boolean realm_dropdown action # Thus we display ALL realms realms = ",".join(get_realms().keys()) if realms: realms = "," + realms try: if is_remote_user_allowed(request): remote_user = request.remote_user password_reset = is_password_reset() hsm_ready = True except HSMException: hsm_ready = False # Use policies to determine the customization of menu # and baseline. get_action_values returns an array! sub_state = subscription_status() customization_menu_file = policy_object.get_action_values( allow_white_space_in_action=True, action=ACTION.CUSTOM_MENU, scope=SCOPE.WEBUI, client=client_ip, unique=True) if len(customization_menu_file) and customization_menu_file[0] \ and sub_state not in [1, 2]: customization_menu_file = customization_menu_file[0] else: customization_menu_file = "templates/menu.html" customization_baseline_file = policy_object.get_action_values( allow_white_space_in_action=True, action=ACTION.CUSTOM_BASELINE, scope=SCOPE.WEBUI, client=client_ip, unique=True) if len(customization_baseline_file) and customization_baseline_file[0] \ and sub_state not in [1, 2]: customization_baseline_file = customization_baseline_file[0] else: customization_baseline_file = "templates/baseline.html" return render_template("index.html", instance=instance, backendUrl=backend_url, browser_lang=browser_lang, remote_user=remote_user, theme=theme, password_reset=password_reset, hsm_ready=hsm_ready, customization=customization, customization_menu_file=customization_menu_file, customization_baseline_file=customization_baseline_file, realms=realms, external_links=external_links, logo=logo)
def single_page_application(): instance = request.script_root if instance == "/": instance = "" # The backend URL should come from the configuration of the system. backend_url = "" if current_app.config.get("PI_UI_DEACTIVATED"): # Do not provide the UI return send_html(render_template("deactivated.html")) # The default theme. We can change this later theme = current_app.config.get("PI_CSS", DEFAULT_THEME) theme = theme.strip('/') # Get further customizations customization = current_app.config.get("PI_CUSTOMIZATION", "/static/customize/") customization = customization.strip('/') custom_css = customization + "/css/custom.css" if current_app.config.get("PI_CUSTOM_CSS") else "" # Enrollment-Wizard: # PI_CUSTOMIZATION/views/includes/token.enroll.pre.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.pre.bottom.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.bottom.html # Get the hidden external links external_links = current_app.config.get("PI_EXTERNAL_LINKS", True) # Read the UI translation warning translation_warning = current_app.config.get("PI_TRANSLATION_WARNING", False) # Get the logo file logo = current_app.config.get("PI_LOGO", "privacyIDEA1.png") browser_lang = request.accept_languages.best_match(["en", "de", "de-DE", "nl"], default="en").split("-")[0] # The page title can be configured in pi.cfg page_title = current_app.config.get("PI_PAGE_TITLE", "privacyIDEA Authentication System") # check if login with REMOTE_USER is allowed. remote_user = "" password_reset = False if not hasattr(request, "all_data"): request.all_data = {} # Depending on displaying the realm dropdown, we fill realms or not. realms = "" realm_dropdown = Match.action_only(g, scope=SCOPE.WEBUI, action=ACTION.REALMDROPDOWN)\ .policies(write_to_audit_log=False) if realm_dropdown: try: realm_dropdown_values = Match.action_only(g, scope=SCOPE.WEBUI, action=ACTION.REALMDROPDOWN) \ .action_values(unique=False, write_to_audit_log=False) # Use the realms from the policy. realms = ",".join(realm_dropdown_values) except AttributeError as _e: # The policy is still a boolean realm_dropdown action # Thus we display ALL realms realms = ",".join(get_realms()) try: if is_remote_user_allowed(request): remote_user = request.remote_user password_reset = is_password_reset(g) hsm_ready = True except HSMException: hsm_ready = False # Use policies to determine the customization of menu # and baseline. get_action_values returns an array! sub_state = subscription_status() customization_menu_file = Match.action_only(g, action=ACTION.CUSTOM_MENU, scope=SCOPE.WEBUI)\ .action_values(unique=True, allow_white_space_in_action=True, write_to_audit_log=False) if len(customization_menu_file) and list(customization_menu_file)[0] \ and sub_state not in [1, 2]: customization_menu_file = list(customization_menu_file)[0] else: customization_menu_file = "templates/menu.html" customization_baseline_file = Match.action_only(g, action=ACTION.CUSTOM_BASELINE, scope=SCOPE.WEBUI) \ .action_values(unique=True, allow_white_space_in_action=True, write_to_audit_log=False) if len(customization_baseline_file) and list(customization_baseline_file)[0] \ and sub_state not in [1, 2]: customization_baseline_file = list(customization_baseline_file)[0] else: customization_baseline_file = "templates/baseline.html" login_text = Match.action_only(g, action=ACTION.LOGIN_TEXT, scope=SCOPE.WEBUI) \ .action_values(unique=True, allow_white_space_in_action=True, write_to_audit_log=False) if len(login_text) and list(login_text)[0] and sub_state not in [1, 2]: login_text = list(login_text)[0] else: login_text = "" render_context = { 'instance': instance, 'backendUrl': backend_url, 'browser_lang': browser_lang, 'remote_user': remote_user, 'theme': theme, 'translation_warning': translation_warning, 'password_reset': password_reset, 'hsm_ready': hsm_ready, 'has_job_queue': str(has_job_queue()), 'customization': customization, 'custom_css': custom_css, 'customization_menu_file': customization_menu_file, 'customization_baseline_file': customization_baseline_file, 'realms': realms, 'external_links': external_links, 'login_text': login_text, 'logo': logo, 'page_title': page_title } return send_html(render_template("index.html", **render_context))
def test_21_check_all_resolver(self): # check_all_resolver allows to find a policy for a secondary user # resolver. # We create one realm "realm1" with the resolvers # reso1 (prio 1) # reso2 (prio 2) # reso3 (prio 3) # A user user@realm1 will be identified as user.reso1@realm1. # But we will also match policies for reso2. # no realm and resolver r = get_realms() self.assertEqual(r, {}) r = get_resolver_list() self.assertEqual(r, {}) # create user realm for reso in ["reso1", "resoX", "resoA"]: rid = save_resolver({ "resolver": reso, "type": "passwdresolver", "fileName": PWFILE }) self.assertTrue(rid > 0, rid) # create a realm with reso1 being the resolver with the highest priority (added, failed) = set_realm("realm1", ["reso1", "resoX", "resoA"], priority={ "reso1": 1, "resoX": 2, "resoA": 3 }) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 3) user = User(login="******", realm="realm1") # The user, that is created, is cornelius.reso1@realm1 user_str = "{0!s}".format(user) self.assertEqual(user_str, "<cornelius.reso1@realm1>") # But the user "cornelius" is also contained in other resolves in # this realm r = user.get_ordererd_resolvers() self.assertEqual(r, ["reso1", "resoX", "resoA"]) self.assertFalse(user.is_empty()) self.assertTrue(User().is_empty()) # define a policy with the wrong resolver p = set_policy(name="checkAll", scope=SCOPE.AUTHZ, realm="realm1", resolver="resoX", action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) p = set_policy(name="catchAll", scope=SCOPE.AUTHZ, realm="realm1", action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) P = PolicyClass() pols = P.get_policies(scope=SCOPE.AUTHZ, realm=user.realm, resolver=user.resolver, user=user.login) self.assertEqual(len(pols), 1) # Now we change the policy, so that it uses check_all_resolver, i.e. p = set_policy(name="checkAll", scope=SCOPE.AUTHZ, realm="realm1", resolver="resoX", check_all_resolvers=True, action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) P = PolicyClass() pols = P.get_policies(scope=SCOPE.AUTHZ, realm=user.realm, resolver=user.resolver, user=user.login) self.assertEqual(len(pols), 2) # delete policy delete_policy("checkAll") delete_policy("catchAll") # delete resolvers and realm delete_realm("realm1") for reso in ["reso1", "resoX", "resoA"]: rid = delete_resolver(reso) self.assertTrue(rid > 0, rid)
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should apply."), "value": realms.keys() }, "tokenrealm": { "type": "multi", "desc": _("The token realm, for which this event should " "apply."), "value": [{ "name": r } for r in realms.keys()] }, CONDITION.TOKENTYPE: { "type": "multi", "desc": _("The type of the token."), "value": [{ "name": r } for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, "result_value": { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, CONDITION.TOKEN_HAS_OWNER: { "type": "str", "desc": _("The token has a user assigned."), "value": ("True", "False") }, CONDITION.TOKEN_IS_ORPHANED: { "type": "str", "desc": _("The token has a user assigned, but the user does " "not exist in the userstore anymore."), "value": ("True", "False") }, CONDITION.TOKEN_VALIDITY_PERIOD: { "type": "str", "desc": _("Check if the token is within its validity period."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") }, CONDITION.USER_TOKEN_NUMBER: { "type": "str", "desc": _("Action is triggered, if the user has this number " "of tokens assigned.") }, CONDITION.OTP_COUNTER: { "type": "str", "desc": _("Action is triggered, if the counter of the token " "equals this setting.") }, CONDITION.LAST_AUTH: { "type": "str", "desc": _("Action is triggered, if the last authentication of " "the token is older than 7h, 10d or 1y.") }, CONDITION.COUNT_AUTH: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field 'count_auth' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_SUCCESS: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field " "'count_auth_success' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_FAIL: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the difference between the tokeninfo " "field 'count_auth' and 'count_auth_success is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.TOKENINFO: { "type": "str", "desc": _("This condition can check any arbitrary tokeninfo " "field. You need to enter something like " "'<fieldname> == <fieldvalue>', '<fieldname> > " "<fieldvalue>' or '<fieldname> < <fieldvalue>'") } } return cond
def actions(cls): """ This method returns a dictionary of allowed actions and possible options in this handler module. :return: dict with actions """ realm_list = get_realms().keys() actions = { ACTION_TYPE.SET_TOKENREALM: { "realm": { "type": "str", "required": True, "description": _("set a new realm of the token"), "value": realm_list }, "only_realm": { "type": "bool", "description": _("The new realm will be the only " "realm of the token. I.e. all " "other realms will be removed " "from this token. Otherwise the " "realm will be added to the token.") } }, ACTION_TYPE.DELETE: {}, ACTION_TYPE.UNASSIGN: {}, ACTION_TYPE.DISABLE: {}, ACTION_TYPE.ENABLE: {}, ACTION_TYPE.INIT: { "tokentype": { "type": "str", "required": True, "description": _("Token type to create"), "value": get_token_types() }, "user": { "type": "bool", "description": _("Assign token to user in " "request or to tokenowner.") }, "realm": { "type": "str", "required": False, "description": _("Set the realm of the newly " "created token."), "value": realm_list }, "motppin": { "type": "str", "visibleIf": "tokentype", "visibleValue": "motp", "description": _("Set the MOTP PIN of the MOTP " "token during enrollment. This " "is a required value for " "enrolling MOTP tokens.") } }, ACTION_TYPE.SET_DESCRIPTION: { "description": { "type": "str", "description": _("The new description of the " "token.") } }, ACTION_TYPE.SET_VALIDITY: { VALIDITY.START: { "type": "str", "description": _("The token will be valid starting " "at the given date. Can be a fixed " "date or an offset like +10m, " "+24h, +7d.") }, VALIDITY.END: { "type": "str", "description": _("The token will be valid until " "the given date. Can be a fixed " "date or an offset like +10m, " "+24h, +7d.") } }, ACTION_TYPE.SET_COUNTWINDOW: { "count window": { # TODO: should be "int" but we do not support # this at the moment. "type": "str", "required": True, "description": _("Set the new count window of " "the token.") } }, ACTION_TYPE.SET_TOKENINFO: { "key": { "type": "str", "required": True, "description": _("Set this tokeninfo key.") }, "value": { "type": "str", "description": _("Set the above key the this " "value.") } } } return actions
def single_page_application(): instance = request.script_root if instance == "/": instance = "" # The backend URL should come from the configuration of the system. backend_url = "" if current_app.config.get("PI_UI_DEACTIVATED"): # Do not provide the UI return render_template("deactivated.html") # The default theme. We can change this later theme = current_app.config.get("PI_CSS", DEFAULT_THEME) # Get further customizations customization = current_app.config.get("PI_CUSTOMIZATION", "/static/customize/") customization = customization.strip('/') # TODO: we should add the CSS into PI_CUSTOMZATION/css # Enrollment-Wizard: # PI_CUSTOMIZATION/views/includes/token.enroll.pre.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.pre.bottom.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.bottom.html # Get the hidden external links external_links = current_app.config.get("PI_EXTERNAL_LINKS", True) # Get the logo file logo = current_app.config.get("PI_LOGO", "privacyIDEA1.png") browser_lang = request.accept_languages.best_match( ["en", "de", "de-DE"], default="en").split("-")[0] # The page title can be configured in pi.cfg page_title = current_app.config.get("PI_PAGE_TITLE", "privacyIDEA Authentication System") # check if login with REMOTE_USER is allowed. remote_user = "" password_reset = False if not hasattr(request, "all_data"): request.all_data = {} # Depending on displaying the realm dropdown, we fill realms or not. policy_object = PolicyClass() realms = "" client_ip = get_client_ip(request, get_from_config(SYSCONF.OVERRIDECLIENT)) realm_dropdown = policy_object.get_policies(action=ACTION.REALMDROPDOWN, scope=SCOPE.WEBUI, client=client_ip, active=True) if realm_dropdown: try: realm_dropdown_values = policy_object.get_action_values( action=ACTION.REALMDROPDOWN, scope=SCOPE.WEBUI, client=client_ip) # Use the realms from the policy. realms = ",".join(realm_dropdown_values) except AttributeError as ex: # The policy is still a boolean realm_dropdown action # Thus we display ALL realms realms = ",".join(get_realms()) try: if is_remote_user_allowed(request): remote_user = request.remote_user password_reset = is_password_reset() hsm_ready = True except HSMException: hsm_ready = False # Use policies to determine the customization of menu # and baseline. get_action_values returns an array! sub_state = subscription_status() customization_menu_file = policy_object.get_action_values( allow_white_space_in_action=True, action=ACTION.CUSTOM_MENU, scope=SCOPE.WEBUI, client=client_ip, unique=True) if len(customization_menu_file) and list(customization_menu_file)[0] \ and sub_state not in [1, 2]: customization_menu_file = list(customization_menu_file)[0] else: customization_menu_file = "templates/menu.html" customization_baseline_file = policy_object.get_action_values( allow_white_space_in_action=True, action=ACTION.CUSTOM_BASELINE, scope=SCOPE.WEBUI, client=client_ip, unique=True) if len(customization_baseline_file) and list(customization_baseline_file)[0] \ and sub_state not in [1, 2]: customization_baseline_file = list(customization_baseline_file)[0] else: customization_baseline_file = "templates/baseline.html" login_text = policy_object.get_action_values( allow_white_space_in_action=True, action=ACTION.LOGIN_TEXT, scope=SCOPE.WEBUI, client=client_ip, unique=True) if len(login_text) and list(login_text)[0] and sub_state not in [1, 2]: login_text = list(login_text)[0] else: login_text = "" return render_template( "index.html", instance=instance, backendUrl=backend_url, browser_lang=browser_lang, remote_user=remote_user, theme=theme, password_reset=password_reset, hsm_ready=hsm_ready, has_job_queue=str(has_job_queue()), customization=customization, customization_menu_file=customization_menu_file, customization_baseline_file=customization_baseline_file, realms=realms, external_links=external_links, login_text=login_text, logo=logo, page_title=page_title)
def test_21_check_all_resolver(self): # check_all_resolver allows to find a policy for a secondary user # resolver. # We create one realm "realm1" with the resolvers # reso1 (prio 1) # reso2 (prio 2) # reso3 (prio 3) # A user user@realm1 will be identified as user.reso1@realm1. # But we will also match policies for reso2. # no realm and resolver r = get_realms() self.assertEqual(r, {}) r = get_resolver_list() self.assertEqual(r, {}) # create user realm for reso in ["reso1", "resoX", "resoA"]: rid = save_resolver({"resolver": reso, "type": "passwdresolver", "fileName": PWFILE}) self.assertTrue(rid > 0, rid) # create a realm with reso1 being the resolver with the highest priority (added, failed) = set_realm("realm1", ["reso1", "resoX", "resoA"], priority={"reso1": 1, "resoX": 2, "resoA": 3}) self.assertTrue(len(failed) == 0) self.assertTrue(len(added) == 3) user = User(login="******", realm="realm1") # The user, that is created, is cornelius.reso1@realm1 user_str = "{0!s}".format(user) self.assertEqual(user_str, "<cornelius.reso1@realm1>") # But the user "cornelius" is also contained in other resolves in # this realm r = user.get_ordererd_resolvers() self.assertEqual(r, ["reso1", "resoX", "resoA"]) self.assertFalse(user.is_empty()) self.assertTrue(User().is_empty()) # define a policy with the wrong resolver p = set_policy(name="checkAll", scope=SCOPE.AUTHZ, realm="realm1", resolver="resoX", action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) p = set_policy(name="catchAll", scope=SCOPE.AUTHZ, realm="realm1", action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) P = PolicyClass() pols = P.get_policies(scope=SCOPE.AUTHZ, realm=user.realm, resolver=user.resolver, user=user.login) self.assertEqual(len(pols), 1) # Now we change the policy, so that it uses check_all_resolver, i.e. p = set_policy(name="checkAll", scope=SCOPE.AUTHZ, realm="realm1", resolver="resoX", check_all_resolvers=True, action="{0}=totp".format(ACTION.TOKENTYPE)) self.assertTrue(p > 0) P = PolicyClass() pols = P.get_policies(scope=SCOPE.AUTHZ, realm=user.realm, resolver=user.resolver, user=user.login) self.assertEqual(len(pols), 2) # delete policy delete_policy("checkAll") delete_policy("catchAll") # delete resolvers and realm delete_realm("realm1") for reso in ["reso1", "resoX", "resoA"]: rid = delete_resolver(reso) self.assertTrue(rid > 0, rid)
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() resolvers = get_resolver_list() cond = { CONDITION.ROLLOUT_STATE: { "type": "str", "desc": _("The rollout_state of the token has a certain value like 'clientwait' or 'enrolled'.") }, CONDITION.REALM: { "type": "str", "desc": _("The realm of the user, for which this event should apply."), "value": list(realms) }, CONDITION.RESOLVER: { "type": "str", "desc": _("The resolver of the user, for which this event should apply."), "value": list(resolvers) }, CONDITION.TOKENREALM: { "type": "multi", "desc": _("The realm of the token, for which this event should " "apply."), "value": [{"name": r} for r in realms] }, CONDITION.TOKENRESOLVER: { "type": "multi", "desc": _("The resolver of the token, for which this event should " "apply."), "value": [{"name": r} for r in resolvers] }, CONDITION.TOKENTYPE: { "type": "multi", "desc": _("The type of the token."), "value": [{"name": r} for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, CONDITION.RESULT_VALUE: { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, CONDITION.RESULT_STATUS: { "type": "str", "desc": _("The result.status within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, CONDITION.TOKEN_HAS_OWNER: { "type": "str", "desc": _("The token has a user assigned."), "value": ("True", "False") }, CONDITION.TOKEN_IS_ORPHANED: { "type": "str", "desc": _("The token has a user assigned, but the user does " "not exist in the userstore anymore."), "value": ("True", "False") }, CONDITION.TOKEN_VALIDITY_PERIOD: { "type": "str", "desc": _("Check if the token is within its validity period."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") }, CONDITION.USER_TOKEN_NUMBER: { "type": "str", "desc": _("Action is triggered, if the user has this number " "of tokens assigned.") }, CONDITION.OTP_COUNTER: { "type": "str", "desc": _("Action is triggered, if the counter of the token " "equals this setting. Can also be " "'>100' or '<99' for no exact match.") }, CONDITION.LAST_AUTH: { "type": "str", "desc": _("Action is triggered, if the last authentication of " "the token is older than 7h, 10d or 1y.") }, CONDITION.COUNT_AUTH: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field 'count_auth' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_SUCCESS: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field " "'count_auth_success' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_FAIL: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the difference between the tokeninfo " "field 'count_auth' and 'count_auth_success is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.TOKENINFO: { "type": "str", "desc": _("This condition can check any arbitrary tokeninfo " "field. You need to enter something like " "'<fieldname> == <fieldvalue>', '<fieldname> > " "<fieldvalue>' or '<fieldname> < <fieldvalue>'") }, CONDITION.DETAIL_ERROR_MESSAGE: { "type": "str", "desc": _("Here you can enter a regular expression. The " "condition only applies if the regular expression " "matches the detail->error->message in the response.") }, CONDITION.DETAIL_MESSAGE: { "type": "str", "desc": _("Here you can enter a regular expression. The " "condition only applies if the regular expression " "matches the detail->message in the response.") }, CONDITION.CLIENT_IP: { "type": "str", "desc": _("Trigger the action, if the client IP matches.") } } return cond
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should apply."), "value": list(realms) }, "tokenrealm": { "type": "multi", "desc": _("The token realm, for which this event should " "apply."), "value": [{"name": r} for r in realms.keys()] }, CONDITION.TOKENTYPE: { "type": "multi", "desc": _("The type of the token."), "value": [{"name": r} for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, CONDITION.RESULT_VALUE: { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, CONDITION.RESULT_STATUS: { "type": "str", "desc": _("The result.status within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, CONDITION.TOKEN_HAS_OWNER: { "type": "str", "desc": _("The token has a user assigned."), "value": ("True", "False") }, CONDITION.TOKEN_IS_ORPHANED: { "type": "str", "desc": _("The token has a user assigned, but the user does " "not exist in the userstore anymore."), "value": ("True", "False") }, CONDITION.TOKEN_VALIDITY_PERIOD: { "type": "str", "desc": _("Check if the token is within its validity period."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") }, CONDITION.USER_TOKEN_NUMBER: { "type": "str", "desc": _("Action is triggered, if the user has this number " "of tokens assigned.") }, CONDITION.OTP_COUNTER: { "type": "str", "desc": _("Action is triggered, if the counter of the token " "equals this setting.") }, CONDITION.LAST_AUTH: { "type": "str", "desc": _("Action is triggered, if the last authentication of " "the token is older than 7h, 10d or 1y.") }, CONDITION.COUNT_AUTH: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field 'count_auth' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_SUCCESS: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field " "'count_auth_success' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_FAIL: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the difference between the tokeninfo " "field 'count_auth' and 'count_auth_success is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.TOKENINFO: { "type": "str", "desc": _("This condition can check any arbitrary tokeninfo " "field. You need to enter something like " "'<fieldname> == <fieldvalue>', '<fieldname> > " "<fieldvalue>' or '<fieldname> < <fieldvalue>'") }, CONDITION.DETAIL_ERROR_MESSAGE: { "type": "str", "desc": _("Here you can enter a regular expression. The " "condition only applies if the regular expression " "matches the detail->error->message in the response.") }, CONDITION.DETAIL_MESSAGE: { "type": "str", "desc": _("Here you can enter a regular expression. The " "condition only applies if the regular expression " "matches the detail->message in the response.") } } return cond
def actions(cls): """ This method returns a dictionary of allowed actions and possible options in this handler module. :return: dict with actions """ realm_list = list(get_realms()) actions = {ACTION_TYPE.SET_TOKENREALM: {"realm": {"type": "str", "required": True, "description": _("set a new realm of the token"), "value": realm_list}, "only_realm": {"type": "bool", "description": _("The new realm will be the only " "realm of the token. I.e. all " "other realms will be removed " "from this token. Otherwise the " "realm will be added to the token.") } }, ACTION_TYPE.DELETE: {}, ACTION_TYPE.UNASSIGN: {}, ACTION_TYPE.DISABLE: {}, ACTION_TYPE.ENABLE: {}, ACTION_TYPE.SET_RANDOM_PIN: { "length": {"type": "int", "required": True, "description": _("set the PIN of the token to a random PIN of this length."), "value": list(range(1,32))} }, ACTION_TYPE.INIT: {"tokentype": {"type": "str", "required": True, "description": _("Token type to create"), "value": get_token_types() }, "user": {"type": "bool", "description": _("Assign token to user in " "request or to tokenowner.")}, "realm": {"type": "str", "required": False, "description": _("Set the realm of the newly " "created token."), "value": realm_list}, "dynamic_phone": { "type": "bool", "visibleIf": "tokentype", "visibleValue": "sms", "description": _("Dynamically read the mobile number " "from the user store.") }, "dynamic_email": { "type": "bool", "visibleIf": "tokentype", "visibleValue": "email", "description": _("Dynamically read the email address " "from the user store.") }, "smtp_identifier": { "type": "str", "visibleIf": "tokentype", "visibleValue": "email", "description": _("Use a specific SMTP server configuration for this token."), "value": [server.config.identifier for server in get_smtpservers()] }, "sms_identifier": { "type": "str", "visibleIf": "tokentype", "visibleValue": "sms", "description": _("Use a specific SMS gateway configuration for this token."), "value": [gateway.identifier for gateway in get_smsgateway()] }, "additional_params": { "type": "str", "description": _("A dictionary of additional init parameters.") }, "motppin": { "type": "str", "visibleIf": "tokentype", "visibleValue": "motp", "description": _("Set the MOTP PIN of the MOTP " "token during enrollment. This " "is a required value for " "enrolling MOTP tokens.")} }, ACTION_TYPE.SET_DESCRIPTION: {"description": { "type": "str", "description": _("The new description of the " "token.") } }, ACTION_TYPE.SET_VALIDITY: {VALIDITY.START: { "type": "str", "description": _("The token will be valid starting " "at the given date. Can be a fixed " "date or an offset like +10m, " "+24h, +7d.") }, VALIDITY.END: { "type": "str", "description": _("The token will be valid until " "the given date. Can be a fixed " "date or an offset like +10m, " "+24h, +7d.") } }, ACTION_TYPE.SET_COUNTWINDOW: {"count window": { # TODO: should be "int" but we do not support # this at the moment. "type": "str", "required": True, "description": _("Set the new count window of " "the token.") } }, ACTION_TYPE.SET_FAILCOUNTER: { "fail counter": { "type": "str", "required": True, "description": _("Set the failcounter of " "the token.") } }, ACTION_TYPE.CHANGE_FAILCOUNTER: { "change fail counter": { "type": "str", "required": True, "description": _("Increase or decrease the fail counter of the token. " "Values of +n, -n with n being an integer are accepted.") } }, ACTION_TYPE.SET_TOKENINFO: {"key": { "type": "str", "required": True, "description": _("Set this tokeninfo key.") }, "value": { "type": "str", "description": _("Set the above key the this " "value.") } }, ACTION_TYPE.DELETE_TOKENINFO: {"key": { "type": "str", "required": True, "description": _("Delete this tokeninfo key.") } } } return actions
def single_page_application(): instance = request.script_root if instance == "/": instance = "" # The backend URL should come from the configuration of the system. backend_url = "" if current_app.config.get("PI_UI_DEACTIVATED"): # Do not provide the UI return render_template("deactivated.html") # The default theme. We can change this later theme = current_app.config.get("PI_CSS", DEFAULT_THEME) # Get further customizations customization = current_app.config.get("PI_CUSTOMIZATION", "/static/customize/") customization = customization.strip('/') # TODO: we should add the CSS into PI_CUSTOMZATION/css # Enrollment-Wizard: # PI_CUSTOMIZATION/views/includes/token.enroll.pre.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.pre.bottom.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.top.html # PI_CUSTOMIZATION/views/includes/token.enroll.post.bottom.html # Get the hidden external links external_links = current_app.config.get("PI_EXTERNAL_LINKS", True) # Get the logo file logo = current_app.config.get("PI_LOGO", "privacyIDEA1.png") browser_lang = request.accept_languages.best_match(["en", "de"]) # check if login with REMOTE_USER is allowed. remote_user = "" password_reset = False if not hasattr(request, "all_data"): request.all_data = {} # Depending on displaying the realm dropdown, we fill realms or not. policy_object = PolicyClass() realms = "" client_ip = request.access_route[0] if request.access_route else \ request.remote_addr realm_dropdown = policy_object.get_policies(action=ACTION.REALMDROPDOWN, scope=SCOPE.WEBUI, client=client_ip, active=True) if realm_dropdown: try: realm_dropdown_values = policy_object.get_action_values( action=ACTION.REALMDROPDOWN, scope=SCOPE.WEBUI, client=client_ip) # Use the realms from the policy. realms = ",".join(realm_dropdown_values) except AttributeError as ex: # The policy is still a boolean realm_dropdown action # Thus we display ALL realms realms = ",".join(get_realms().keys()) if realms: realms = "," + realms try: if is_remote_user_allowed(request): remote_user = request.remote_user password_reset = is_password_reset() hsm_ready = True except HSMException: hsm_ready = False return render_template("index.html", instance=instance, backendUrl=backend_url, browser_lang=browser_lang, remote_user=remote_user, theme=theme, password_reset=password_reset, hsm_ready=hsm_ready, customization=customization, realms=realms, external_links=external_links, logo=logo)
def actions(cls): """ This method returns a dictionary of allowed actions and possible options in this handler module. :return: dict with actions """ realm_list = get_realms().keys() actions = {ACTION_TYPE.SET_TOKENREALM: {"realm": {"type": "str", "required": True, "description": _("set a new realm of the token"), "value": realm_list}, "only_realm": {"type": "bool", "description": _("The new realm will be the only " "realm of the token. I.e. all " "other realms will be removed " "from this token. Otherwise the " "realm will be added to the token.") } }, ACTION_TYPE.DELETE: {}, ACTION_TYPE.UNASSIGN: {}, ACTION_TYPE.DISABLE: {}, ACTION_TYPE.ENABLE: {}, ACTION_TYPE.INIT: {"tokentype": {"type": "str", "required": True, "description": _("Token type to create"), "value": get_token_types() }, "user": {"type": "bool", "description": _("Assign token to user in " "request or to tokenowner.")}, "realm": {"type": "str", "required": False, "description": _("Set the realm of the newly " "created token."), "value": realm_list}, "motppin": { "type": "str", "visibleIf": "tokentype", "visibleValue": "motp", "description": _("Set the MOTP PIN of the MOTP " "token during enrollment. This " "is a required value for " "enrolling MOTP tokens.")} }, ACTION_TYPE.SET_DESCRIPTION: {"description": { "type": "str", "description": _("The new description of the " "token.") } }, ACTION_TYPE.SET_VALIDITY: {VALIDITY.START: { "type": "str", "description": _("The token will be valid starting " "at the given date. Can be a fixed " "date or an offset like +10m, " "+24h, +7d.") }, VALIDITY.END: { "type": "str", "description": _("The token will be valid until " "the given date. Can be a fixed " "date or an offset like +10m, " "+24h, +7d.") } }, ACTION_TYPE.SET_COUNTWINDOW: {"count window": { # TODO: should be "int" but we do not support # this at the moment. "type": "str", "required": True, "description": _("Set the new count window of " "the token.") } }, ACTION_TYPE.SET_TOKENINFO: {"key": { "type": "str", "required": True, "description": _("Set this tokeninfo key.") }, "value": { "type": "str", "description": _("Set the above key the this " "value.") } } } return actions
def check_condition(self, options): """ Check if all conditions are met and if the action should be executed. The the conditions are met, we return "True" :return: True """ g = options.get("g") request = options.get("request") response = options.get("response") e_handler_def = options.get("handler_def") if not e_handler_def: # options is the handler definition return True # conditions can be corresponding to the property conditions conditions = e_handler_def.get("conditions") content = self._get_response_content(response) user = self._get_tokenowner(request) serial = request.all_data.get("serial") or \ content.get("detail", {}).get("serial") tokenrealms = [] tokenresolvers = [] tokentype = None token_obj = None if serial: # We have determined the serial number from the request. token_obj_list = get_tokens(serial=serial) else: # We have to determine the token via the user object. But only if # the user has only one token token_obj_list = get_tokens(user=user) if len(token_obj_list) == 1: # There is a token involved, so we determine it's resolvers and realms token_obj = token_obj_list[0] tokenrealms = token_obj.get_realms() tokentype = token_obj.get_tokentype() all_realms = get_realms() for tokenrealm in tokenrealms: resolvers = all_realms.get(tokenrealm, {}).get("resolver", {}) tokenresolvers.extend([r.get("name") for r in resolvers]) tokenresolvers = list(set(tokenresolvers)) if CONDITION.CLIENT_IP in conditions: if g and g.client_ip: ip_policy = [ ip.strip() for ip in conditions.get(CONDITION.CLIENT_IP).split(",") ] found, excluded = check_ip_in_policy(g.client_ip, ip_policy) if not found or excluded: return False if CONDITION.REALM in conditions: if user.realm != conditions.get(CONDITION.REALM): return False if CONDITION.RESOLVER in conditions: if user.resolver != conditions.get(CONDITION.RESOLVER): return False if "logged_in_user" in conditions: # Determine the role of the user try: logged_in_user = g.logged_in_user user_role = logged_in_user.get("role") except Exception: # A non-logged-in-user is a User, not an admin user_role = ROLE.USER if user_role != conditions.get("logged_in_user"): return False if CONDITION.RESULT_VALUE in conditions: condition_value = conditions.get(CONDITION.RESULT_VALUE) result_value = content.get("result", {}).get("value") if is_true(condition_value) != is_true(result_value): return False if CONDITION.RESULT_STATUS in conditions: condition_value = conditions.get(CONDITION.RESULT_STATUS) result_status = content.get("result", {}).get("status") if is_true(condition_value) != is_true(result_status): return False # checking of max-failcounter state of the token if "token_locked" in conditions: if token_obj: locked = token_obj.get_failcount() >= \ token_obj.get_max_failcount() if (conditions.get("token_locked") in ["True", True]) != \ locked: return False else: # check all tokens of the user, if any token is maxfail token_objects = get_tokens(user=user, maxfail=True) if not ','.join([tok.get_serial() for tok in token_objects]): return False if CONDITION.TOKENREALM in conditions and tokenrealms: res = False for trealm in tokenrealms: if trealm in conditions.get(CONDITION.TOKENREALM).split(","): res = True break if not res: return False if CONDITION.TOKENRESOLVER in conditions and tokenresolvers: res = False for tres in tokenresolvers: if tres in conditions.get(CONDITION.TOKENRESOLVER).split(","): res = True break if not res: return False if "serial" in conditions and serial: serial_match = conditions.get("serial") if not bool(re.match(serial_match, serial)): return False if CONDITION.USER_TOKEN_NUMBER in conditions and user: num_tokens = get_tokens(user=user, count=True) if num_tokens != int(conditions.get(CONDITION.USER_TOKEN_NUMBER)): return False if CONDITION.DETAIL_ERROR_MESSAGE in conditions: message = content.get("detail", {}).get("error", {}).get("message") search_exp = conditions.get(CONDITION.DETAIL_ERROR_MESSAGE) m = re.search(search_exp, message) if not bool(m): return False if CONDITION.DETAIL_MESSAGE in conditions: message = content.get("detail", {}).get("message") search_exp = conditions.get(CONDITION.DETAIL_MESSAGE) m = re.search(search_exp, message) if not bool(m): return False # Token specific conditions if token_obj: if CONDITION.TOKENTYPE in conditions: if tokentype not in conditions.get( CONDITION.TOKENTYPE).split(","): return False if CONDITION.TOKEN_HAS_OWNER in conditions: uid = token_obj.get_user_id() check = conditions.get(CONDITION.TOKEN_HAS_OWNER) if uid and check in ["True", True]: res = True elif not uid and check in ["False", False]: res = True else: log.debug("Condition token_has_owner for token {0!r} " "not fulfilled.".format(token_obj)) return False if CONDITION.TOKEN_IS_ORPHANED in conditions: orphaned = token_obj.is_orphaned() check = conditions.get(CONDITION.TOKEN_IS_ORPHANED) if orphaned and check in ["True", True]: res = True elif not orphaned and check in ["False", False]: res = True else: log.debug( "Condition token_is_orphaned for token {0!r} not " "fulfilled.".format(token_obj)) return False if CONDITION.TOKEN_VALIDITY_PERIOD in conditions: valid = token_obj.check_validity_period() if (conditions.get(CONDITION.TOKEN_VALIDITY_PERIOD) in ["True", True]) != valid: return False if CONDITION.OTP_COUNTER in conditions: cond = conditions.get(CONDITION.OTP_COUNTER) if not compare_condition(cond, token_obj.token.count): return False if CONDITION.LAST_AUTH in conditions: if token_obj.check_last_auth_newer( conditions.get(CONDITION.LAST_AUTH)): return False if CONDITION.COUNT_AUTH in conditions: count = token_obj.get_count_auth() cond = conditions.get(CONDITION.COUNT_AUTH) if not compare_condition(cond, count): return False if CONDITION.COUNT_AUTH_SUCCESS in conditions: count = token_obj.get_count_auth_success() cond = conditions.get(CONDITION.COUNT_AUTH_SUCCESS) if not compare_condition(cond, count): return False if CONDITION.COUNT_AUTH_FAIL in conditions: count = token_obj.get_count_auth() c_success = token_obj.get_count_auth_success() c_fail = count - c_success cond = conditions.get(CONDITION.COUNT_AUTH_FAIL) if not compare_condition(cond, c_fail): return False if CONDITION.TOKENINFO in conditions: cond = conditions.get(CONDITION.TOKENINFO) # replace {now} in condition cond, td = parse_time_offset_from_now(cond) s_now = (datetime.datetime.now(tzlocal()) + td).strftime(DATE_FORMAT) cond = cond.format(now=s_now) if len(cond.split("==")) == 2: key, value = [x.strip() for x in cond.split("==")] if not compare_value_value(token_obj.get_tokeninfo(key), "==", value): return False elif len(cond.split(">")) == 2: key, value = [x.strip() for x in cond.split(">")] if not compare_value_value(token_obj.get_tokeninfo(key), ">", value): return False elif len(cond.split("<")) == 2: key, value = [x.strip() for x in cond.split("<")] if not compare_value_value(token_obj.get_tokeninfo(key), "<", value): return False else: # There is a condition, but we do not know it! log.warning("Misconfiguration in your tokeninfo " "condition: {0!s}".format(cond)) return False return True
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should apply."), "value": realms.keys() }, "tokenrealm": { "type": "multi", "desc": _("The token realm, for which this event should " "apply."), "value": [{ "name": r } for r in realms.keys()] }, CONDITION.TOKENTYPE: { "type": "multi", "desc": _("The type of the token."), "value": [{ "name": r } for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, "result_value": { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, CONDITION.TOKEN_HAS_OWNER: { "type": "str", "desc": _("The token has a user assigned."), "value": ("True", "False") }, CONDITION.TOKEN_IS_ORPHANED: { "type": "str", "desc": _("The token has a user assigned, but the user does " "not exist in the userstore anymore."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") }, CONDITION.USER_TOKEN_NUMBER: { "type": "str", "desc": _("Action is triggered, if the user has this number " "of tokens assigned.") }, CONDITION.OTP_COUNTER: { "type": "str", "desc": _("Action is triggered, if the counter of the token " "equals this setting.") } } return cond
def actions(cls): """ This method returns a dictionary of allowed actions and possible options in this handler module. :return: dict with actions """ realm_list = get_realms().keys() actions = {ACTION_TYPE.SET_TOKENREALM: {"realm": {"type": "str", "required": True, "description": _("set a new realm of the token"), "value": realm_list}, "only_realm": {"type": "bool", "description": _("The new realm will be the only " "realm of the token. I.e. all " "other realms will be removed " "from this token. Otherwise the " "realm will be added to the token.") } }, ACTION_TYPE.DELETE: {}, ACTION_TYPE.UNASSIGN: {}, ACTION_TYPE.DISABLE: {}, ACTION_TYPE.ENABLE: {}, ACTION_TYPE.INIT: {"tokentype": {"type": "str", "required": True, "description": _("Token type to create"), "value": get_token_types() }, "user": {"type": "bool", "description": _("Assign token to user in " "request or tokenowner.")}, "realm": {"type": "str", "required": False, "description": _("Set the realm of the newly " "created token."), "value": realm_list}, }, ACTION_TYPE.SET_DESCRIPTION: {"description": { "type": "str", "description": _("The new description of the " "token.") } }, ACTION_TYPE.SET_VALIDITY: {VALIDITY.START: { "type": "str", "description": _("The token will be valid starting " "at the given date. Can be a fixed " "date or an offset like +10m, " "+24h, +7d.") }, VALIDITY.END: { "type": "str", "description": _("The token will be valid until " "the given date. Can be a fixed " "date or an offset like +10m, " "+24h, +7d.") } } } return actions