def upgrade(): node = get_privacyidea_node() try: # The ``node`` field is not nullable. Hence, We set the server_default to the current node to ensure that # the ``node`` of all existing rows is set to the current node. op.add_column('clientapplication', sa.Column('node', sa.Unicode(length=255), nullable=False, server_default=node)) op.drop_constraint(u'caix', 'clientapplication', type_='unique') op.create_unique_constraint('caix', 'clientapplication', ['ip', 'clienttype', 'node']) except Exception as exx: print("Failed to add 'node' column to 'clientapplication' table") print(exx)
def reset(counter_name): """ Reset the counter value in the database to 0. If the counter does not exist yet, create the counter. :param counter_name: The name/identifier of the counter :return: """ node = get_privacyidea_node() counters = EventCounter.query.filter_by(counter_name=counter_name).count() if not counters: counter = EventCounter(counter_name, 0, node=node) counter.save() else: _reset_counter_on_all_nodes(counter_name)
def increase(counter_name): """ Increase the counter value in the database. If the counter does not exist yet, create the counter. :param counter_name: The name/identifier of the counter :return: None """ # If there is no table row for the current node, create one. node = get_privacyidea_node() counter = EventCounter.query.filter_by(counter_name=counter_name, node=node).first() if not counter: counter = EventCounter(counter_name, 0, node=node) counter.save() counter.increase()
def save_clientapplication(ip, clienttype): """ Save (or update) the IP and the clienttype to the database table. :param ip: The IP address of the requesting client. :type ip: well formatted string or IPAddress :param clienttype: The type of the client :type ip: basestring :return: None """ node = get_privacyidea_node() # Check for a valid IP address ip = IPAddress(ip) # TODO: resolve hostname app = ClientApplication(ip="{0!s}".format(ip), clienttype=clienttype, node=node) app.save()
def upgrade(): try: # Step 1: Create sequence on Postgres seq = sa.Sequence('eventcounter_seq') try: create_seq(seq) except Exception as _e: pass # Step 2: Create new eventcounter_new table op.create_table('eventcounter_new', sa.Column("id", sa.Integer, sa.Sequence("eventcounter_seq"), primary_key=True), sa.Column("counter_name", sa.Unicode(80), nullable=False), sa.Column("counter_value", sa.Integer, default=0), sa.Column("node", sa.Unicode(255), nullable=False), sa.UniqueConstraint('counter_name', 'node', name='evctr_1'), mysql_row_format='DYNAMIC') # Step 3: Migrate data from eventcounter to eventcounter_new node = get_privacyidea_node() bind = op.get_bind() session = orm.Session(bind=bind) for old_ctr in session.query(OldEventCounter).all(): new_ctr = NewEventCounter(counter_name=old_ctr.counter_name, counter_value=old_ctr.counter_value, node=node) session.add(new_ctr) print("Migrating counter {!r}={} on node={!r} ...".format( new_ctr.counter_name, new_ctr.counter_value, node)) session.commit() # Step 4: Remove eventcounter op.drop_table("eventcounter") op.rename_table("eventcounter_new", "eventcounter") except Exception as exx: session.rollback() print("Could not migrate table 'eventcounter'") print(exx)
def decrease(counter_name, allow_negative=False): """ Decrease the counter value in the database. If the counter does not exist yet, create the counter. Also checks whether the counter is allowed to become negative. :param counter_name: The name/identifier of the counter :param allow_negative: Whether the counter can become negative. Note that even if this flag is not set, the counter may become negative due to concurrent queries. :return: None """ node = get_privacyidea_node() counter = EventCounter.query.filter_by(counter_name=counter_name, node=node).first() if not counter: counter = EventCounter(counter_name, 0, node=node) counter.save() # We are allowed to decrease the current counter object only if the overall # counter value is positive (because individual rows may be negative then), # or if we allow negative values. Otherwise, we need to reset all rows of all nodes. if read(counter_name) > 0 or allow_negative: counter.decrease() else: _reset_counter_on_all_nodes(counter_name)
def get_webui_settings(request, response): """ This decorator is used in the /auth API to add configuration information like the logout_time or the policy_template_url to the response. :param request: flask request object :param response: flask response object :return: the response """ content = response.json # check, if the authentication was successful, then we need to do nothing if content.get("result").get("status") is True: role = content.get("result").get("value").get("role") loginname = content.get("result").get("value").get("username") realm = content.get("result").get("value").get("realm") or get_default_realm() # At this point the logged in user is not necessarily a user object. It can # also be a local admin. logout_time_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.LOGOUTTIME, user=loginname, realm=realm).action_values(unique=True) timeout_action_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.TIMEOUT_ACTION, user=loginname, realm=realm).action_values(unique=True) token_page_size_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.TOKENPAGESIZE, user=loginname, realm=realm).action_values(unique=True) user_page_size_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.USERPAGESIZE, user=loginname, realm=realm).action_values(unique=True) token_wizard_2nd = bool(role == ROLE.USER and Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.TOKENWIZARD2ND, user=loginname, realm=realm).policies()) admin_dashboard = (role == ROLE.ADMIN and Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.ADMIN_DASHBOARD, user=loginname, realm=realm).any()) token_wizard = False dialog_no_token = False if role == ROLE.USER: user_obj = User(loginname, realm) user_token_num = get_tokens(user=user_obj, count=True) token_wizard_pol = Match.user(g, scope=SCOPE.WEBUI, action=ACTION.TOKENWIZARD, user_object=user_obj).any() # We also need to check, if the user has not tokens assigned. # If the user has no tokens, we run the wizard. If the user # already has tokens, we do not run the wizard. token_wizard = token_wizard_pol and (user_token_num == 0) dialog_no_token_pol = Match.user(g, scope=SCOPE.WEBUI, action=ACTION.DIALOG_NO_TOKEN, user_object=user_obj).any() dialog_no_token = dialog_no_token_pol and (user_token_num == 0) user_details_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.USERDETAILS, user=loginname, realm=realm).policies() search_on_enter = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SEARCH_ON_ENTER, user=loginname, realm=realm).policies() hide_welcome = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.HIDE_WELCOME, user=loginname, realm=realm).any() hide_buttons = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.HIDE_BUTTONS, user=loginname, realm=realm).any() default_tokentype_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.DEFAULT_TOKENTYPE, user=loginname, realm=realm).action_values(unique=True) show_seed = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_SEED, user=loginname, realm=realm).any() show_node = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_NODE, realm=realm).any() qr_ios_authenticator = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_IOS_AUTHENTICATOR, user=loginname, realm=realm).any() qr_android_authenticator = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_ANDROID_AUTHENTICATOR, user=loginname, realm=realm).any() qr_custom_authenticator_url = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_CUSTOM_AUTHENTICATOR, user=loginname, realm=realm).action_values(unique=True) qr_image_android = create_img(DEFAULT_ANDROID_APP_URL) if qr_android_authenticator else None qr_image_ios = create_img(DEFAULT_IOS_APP_URL) if qr_ios_authenticator else None qr_image_custom = create_img(list(qr_custom_authenticator_url)[0]) if qr_custom_authenticator_url else None token_page_size = DEFAULT_PAGE_SIZE user_page_size = DEFAULT_PAGE_SIZE default_tokentype = DEFAULT_TOKENTYPE if len(token_page_size_pol) == 1: token_page_size = int(list(token_page_size_pol)[0]) if len(user_page_size_pol) == 1: user_page_size = int(list(user_page_size_pol)[0]) if len(default_tokentype_pol) == 1: default_tokentype = list(default_tokentype_pol)[0] logout_time = DEFAULT_LOGOUT_TIME if len(logout_time_pol) == 1: logout_time = int(list(logout_time_pol)[0]) timeout_action = DEFAULT_TIMEOUT_ACTION if len(timeout_action_pol) == 1: timeout_action = list(timeout_action_pol)[0] policy_template_url_pol = Match.action_only(g, scope=SCOPE.WEBUI, action=ACTION.POLICYTEMPLATEURL).action_values(unique=True) policy_template_url = DEFAULT_POLICY_TEMPLATE_URL if len(policy_template_url_pol) == 1: policy_template_url = list(policy_template_url_pol)[0] indexed_preset_attribute = Match.realm(g, scope=SCOPE.WEBUI, action="indexedsecret_preset_attribute", realm=realm).action_values(unique=True) if len(indexed_preset_attribute) == 1: content["result"]["value"]["indexedsecret_preset_attribute"] = list(indexed_preset_attribute)[0] # This only works for users, because the value of the policy does not change while logged in. if role == ROLE.USER and \ Match.user(g, SCOPE.USER, "indexedsecret_force_attribute", user_obj).action_values(unique=False): content["result"]["value"]["indexedsecret_force_attribute"] = 1 content["result"]["value"]["logout_time"] = logout_time content["result"]["value"]["token_page_size"] = token_page_size content["result"]["value"]["user_page_size"] = user_page_size content["result"]["value"]["policy_template_url"] = policy_template_url content["result"]["value"]["default_tokentype"] = default_tokentype content["result"]["value"]["user_details"] = len(user_details_pol) > 0 content["result"]["value"]["token_wizard"] = token_wizard content["result"]["value"]["token_wizard_2nd"] = token_wizard_2nd content["result"]["value"]["admin_dashboard"] = admin_dashboard content["result"]["value"]["dialog_no_token"] = dialog_no_token content["result"]["value"]["search_on_enter"] = len(search_on_enter) > 0 content["result"]["value"]["timeout_action"] = timeout_action content["result"]["value"]["hide_welcome"] = hide_welcome content["result"]["value"]["hide_buttons"] = hide_buttons content["result"]["value"]["show_seed"] = show_seed content["result"]["value"]["show_node"] = get_privacyidea_node() if show_node else "" content["result"]["value"]["subscription_status"] = subscription_status() content["result"]["value"]["qr_image_android"] = qr_image_android content["result"]["value"]["qr_image_ios"] = qr_image_ios content["result"]["value"]["qr_image_custom"] = qr_image_custom response.set_data(json.dumps(content)) return response
def test_07_node_names(self): node = get_privacyidea_node() self.assertEqual(node, "Node1") nodes = get_privacyidea_nodes() self.assertTrue("Node1" in nodes) self.assertTrue("Node2" in nodes)
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", "fr", "it", "es"], 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) show_node = get_privacyidea_node() \ if Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_NODE).any(write_to_audit_log=False) else "" 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: r = is_remote_user_allowed(request, write_to_audit_log=False) force_remote_user = r == REMOTE_USER.FORCE if r != REMOTE_USER.DISABLE: 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 = "" gdpr_link = Match.action_only(g, action=ACTION.GDPR_LINK, scope=SCOPE.WEBUI) \ .action_values(unique=True, allow_white_space_in_action=True, write_to_audit_log=False) if len(gdpr_link) and list(gdpr_link)[0] and sub_state not in [1, 2]: gdpr_link = list(gdpr_link)[0] else: gdpr_link = "" render_context = { 'instance': instance, 'backendUrl': backend_url, 'browser_lang': browser_lang, 'remote_user': remote_user, 'force_remote_user': force_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, 'show_node': show_node, 'external_links': external_links, 'login_text': login_text, 'gdpr_link': gdpr_link, 'logo': logo, 'page_title': page_title } index_page = current_app.config.get("PI_INDEX_HTML") or "index.html" return send_html(render_template(index_page, **render_context))
def test_07_node_names(self): node = get_privacyidea_node() self.assertEqual(node, "Node1") nodes = get_privacyidea_nodes() self.assertTrue("Node1" in nodes) self.assertTrue("Node2" in nodes)