def _action(self) -> None: assert user.id is not None users = userdb.load_users(lock=True) user_spec = users[user.id] language = request.get_ascii_input_mandatory("language", "") # Set the users language if requested to set it explicitly if language != "_default_": user_spec["language"] = language user.language = language set_language_cookie(request, response, language) else: if "language" in user_spec: del user_spec["language"] user.reset_language() # load the new language localize(user.language) if user.may("general.edit_notifications") and user_spec.get( "notifications_enabled"): value = forms.get_input(get_vs_flexible_notifications(), "notification_method") user_spec["notification_method"] = value # Custom attributes if user.may("general.edit_user_attributes"): for name, attr in userdb.get_user_attributes(): if not attr.user_editable(): continue perm_name = attr.permission() if perm_name and not user.may(perm_name): continue vs = attr.valuespec() value = vs.from_html_vars("ua_" + name) vs.validate_value(value, "ua_" + name) # TODO: Dynamically fiddling around with a TypedDict is a bit questionable user_spec[name] = value # type: ignore[literal-required] userdb.save_users(users) flash(_("Successfully updated user profile.")) # In distributed setups with remote sites where the user can login, start the # user profile replication now which will redirect the user to the destination # page after completion. Otherwise directly open up the destination page. if user.authorized_login_sites(): back_url = "user_profile_replicate.py?back=user_profile.py" else: back_url = "user_profile.py" # Ensure theme changes are applied without additional user interaction html.reload_whole_page(back_url) html.footer() raise FinalizeRequest(code=200)
def _action(self) -> None: assert user.id is not None users = userdb.load_users(lock=True) user_spec = users[user.id] cur_password = request.get_str_input_mandatory("cur_password") password = request.get_str_input_mandatory("password") password2 = request.get_str_input_mandatory("password2", "") # Force change pw mode if not cur_password: raise MKUserError("cur_password", _("You need to provide your current password.")) if not password: raise MKUserError("password", _("You need to change your password.")) if cur_password == password: raise MKUserError("password", _("The new password must differ from your current one.")) if userdb.check_credentials(user.id, cur_password) is False: raise MKUserError("cur_password", _("Your old password is wrong.")) if password2 and password != password2: raise MKUserError("password2", _("The both new passwords do not match.")) watolib.verify_password_policy(password) user_spec["password"] = hash_password(password) user_spec["last_pw_change"] = int(time.time()) # In case the user was enforced to change it's password, remove the flag try: del user_spec["enforce_pw_change"] except KeyError: pass # Increase serial to invalidate old authentication cookies if "serial" not in user_spec: user_spec["serial"] = 1 else: user_spec["serial"] += 1 userdb.save_users(users) flash(_("Successfully changed password.")) # Set the new cookie to prevent logout for the current user login.update_auth_cookie(user.id) # In distributed setups with remote sites where the user can login, start the # user profile replication now which will redirect the user to the destination # page after completion. Otherwise directly open up the destination page. origtarget = request.get_str_input_mandatory("_origtarget", "user_change_pw.py") if user.authorized_login_sites(): raise redirect( makeuri_contextless( request, [("back", origtarget)], filename="user_profile_replicate.py" ) ) raise redirect(origtarget)
def edit_users(changed_users): all_users = userdb.load_users(lock=True) new_users_info = [] modified_users_info = [] for user_id, settings in changed_users.items(): user_attrs = settings.get("attributes") is_new_user = settings.get("is_new_user", True) _validate_user_attributes(all_users, user_id, user_attrs, is_new_user=is_new_user) if is_new_user: new_users_info.append(user_id) else: modified_users_info.append(user_id) all_users[user_id] = user_attrs if new_users_info: add_change("edit-users", _("Created new user: %s") % ", ".join(new_users_info)) if modified_users_info: add_change("edit-users", _("Modified user: %s") % ", ".join(modified_users_info)) userdb.save_users(all_users)
def edit_users(changed_users): all_users = userdb.load_users(lock=True) new_users_info = [] modified_users_info = [] for user_id, settings in changed_users.items(): user_attrs = settings.get("attributes") is_new_user = settings.get("is_new_user", True) _validate_user_attributes(all_users, user_id, user_attrs, is_new_user=is_new_user) if is_new_user: new_users_info.append(user_id) else: modified_users_info.append(user_id) if is_new_user: add_internal_attributes(user_attrs) old_object = make_user_audit_log_object(all_users.get(user_id, {})) log_audit(action="edit-user", message=(_("Created new user: %s") % user_id if is_new_user else _("Modified user: %s") % user_id), diff_text=make_diff_text(old_object, make_user_audit_log_object(user_attrs)), object_ref=make_user_object_ref(user_id)) all_users[user_id] = user_attrs if new_users_info: add_change("edit-users", _("Created new users: %s") % ", ".join(new_users_info)) if modified_users_info: add_change("edit-users", _("Modified users: %s") % ", ".join(modified_users_info)) userdb.save_users(all_users)
def _automation_push_profile(self): site_id = html.request.var("siteid") if not site_id: raise MKGeneralException(_("Missing variable siteid")) user_id = html.request.var("user_id") if not user_id: raise MKGeneralException(_("Missing variable user_id")) our_id = config.omd_site() if our_id is not None and our_id != site_id: raise MKGeneralException( _("Site ID mismatch. Our ID is '%s', but you are saying we are '%s'." ) % (our_id, site_id)) profile = html.request.var("profile") if not profile: raise MKGeneralException( _('Invalid call: The profile is missing.')) users = userdb.load_users(lock=True) users[UserId(user_id)] = watolib.mk_eval(profile) userdb.save_users(users) return True
def delete_users(users_to_delete): user.need_permission("wato.users") user.need_permission("wato.edit") if user.id in users_to_delete: raise MKUserError(None, _("You cannot delete your own account!")) all_users = userdb.load_users(lock=True) deleted_users = [] for entry in users_to_delete: if entry in all_users: # Silently ignore not existing users deleted_users.append(entry) del all_users[entry] else: raise MKUserError(None, _("Unknown user: %s") % entry) if deleted_users: for user_id in deleted_users: log_audit( "edit-user", _("Deleted user: %s") % user_id, object_ref=make_user_object_ref(user_id), ) add_change("edit-users", _("Deleted user: %s") % ", ".join(deleted_users)) userdb.save_users(all_users, datetime.now())
def _action(self) -> None: assert config.user.id is not None users = userdb.load_users(lock=True) user = users[config.user.id] language = html.request.get_ascii_input_mandatory('language', "") # Set the users language if requested to set it explicitly if language != "_default_": user['language'] = language config.user.language = language html.set_language_cookie(language) else: if 'language' in user: del user['language'] config.user.reset_language() # load the new language cmk.gui.i18n.localize(config.user.language) if config.user.may('general.edit_notifications') and user.get( "notifications_enabled"): value = forms.get_input(watolib.get_vs_flexible_notifications(), "notification_method") user["notification_method"] = value # Custom attributes if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if not attr.user_editable(): continue if attr.permission() and not config.user.may( attr.permission()): continue vs = attr.valuespec() value = vs.from_html_vars('ua_' + name) vs.validate_value(value, "ua_" + name) user[name] = value userdb.save_users(users) flash(_("Successfully updated user profile.")) # In distributed setups with remote sites where the user can login, start the # user profile replication now which will redirect the user to the destination # page after completion. Otherwise directly open up the destination page. if config.user.authorized_login_sites(): back_url = "user_profile_replicate.py?back=user_profile.py" else: back_url = "user_profile.py" # Ensure theme changes are applied without additional user interaction html.reload_whole_page(back_url) html.footer() raise FinalizeRequest(code=200)
def _rename_user_role(self, uid, new_id): users = userdb.load_users(lock=True) for user in users.values(): if uid in user["roles"]: user["roles"].remove(uid) if new_id: user["roles"].append(new_id) userdb.save_users(users)
def test_check_credentials_managed_wrong_customer_user_is_denied( with_user: tuple[UserId, str]) -> None: user_id, password = with_user now = datetime.now() users = _load_users_uncached(lock=True) users[user_id]["customer"] = "wrong-customer" userdb.save_users(users, now) assert userdb.check_credentials(user_id, password, now) is False
def make_cme_wrong_customer_user(user_id: UserId) -> None: if not is_managed_repo(): pytest.skip("not relevant") users = _load_users_uncached(lock=True) users[user_id]["customer"] = "wrong-customer" userdb.save_users(users)
def make_cme_global_user(user_id): if not is_managed_repo(): pytest.skip("not relevant") import cmk.gui.cme.managed as managed # pylint: disable=no-name-in-module users = _load_users_uncached(lock=True) users[user_id]["customer"] = managed.SCOPE_GLOBAL userdb.save_users(users)
def rename_host_in_event_rules(oldname, newname): actions = [] def rename_in_event_rules(rules): num_changed = 0 for rule in rules: for key in ["match_hosts", "match_exclude_hosts"]: if rule.get(key): if watolib.rename_host_in_list(rule[key], oldname, newname): num_changed += 1 return num_changed users = userdb.load_users(lock=True) some_user_changed = False for user in users.values(): if user.get("notification_rules"): rules = user["notification_rules"] num_changed = rename_in_event_rules(rules) if num_changed: actions += ["notify_user"] * num_changed some_user_changed = True rules = load_notification_rules() num_changed = rename_in_event_rules(rules) if num_changed: actions += ["notify_global"] * num_changed save_notification_rules(rules) if alert_handling: rules = alert_handling.load_alert_handler_rules() if rules: num_changed = rename_in_event_rules(rules) if num_changed: actions += ["alert_rules"] * num_changed alert_handling.save_alert_handler_rules(rules) # Notification channels of flexible notifications also can have host conditions for user in users.values(): method = user.get("notification_method") if method and isinstance(method, tuple) and method[0] == "flexible": channels_changed = 0 for channel in method[1]: if channel.get("only_hosts"): num_changed = watolib.rename_host_in_list( channel["only_hosts"], oldname, newname) if num_changed: channels_changed += 1 some_user_changed = True if channels_changed: actions += ["notify_flexible"] * channels_changed if some_user_changed: userdb.save_users(users) return actions
def test_check_credentials_managed_global_user_is_allowed( with_user: tuple[UserId, str]) -> None: user_id, password = with_user now = datetime.now() import cmk.gui.cme.managed as managed # pylint: disable=no-name-in-module users = _load_users_uncached(lock=True) users[user_id]["customer"] = managed.SCOPE_GLOBAL userdb.save_users(users, now) assert userdb.check_credentials(user_id, password, now) == user_id
def test_check_credentials_local_user_disallow_locked(with_user): user_id, password = with_user assert userdb.check_credentials(user_id, password) == user_id users = _load_users_uncached(lock=True) users[user_id]["locked"] = True userdb.save_users(users) assert userdb.check_credentials(user_id, password) is False
def execute(self, api_request): user_profiles = api_request.user_profiles if not user_profiles: raise MKGeneralException(_('Invalid call: No profiles set.')) users = userdb.load_users(lock=True) for user_id, profile in user_profiles.items(): users[user_id] = profile userdb.save_users(users) return True
def _set_user_scheme_serial(self): """Set attribute to detect with what cmk version the user was created. We start that with 2.0""" users = load_users(lock=True) for user_id in users: # pre 2.0 user if users[user_id].get("user_scheme_serial") is None: _set_show_mode(users, user_id) # here you could set attributes based on the current scheme users[user_id]["user_scheme_serial"] = USER_SCHEME_SERIAL save_users(users)
def test_check_credentials_local_user_disallow_locked( with_user: tuple[UserId, str]) -> None: now = datetime.now() user_id, password = with_user assert userdb.check_credentials(user_id, password, now) == user_id users = _load_users_uncached(lock=True) users[user_id]["locked"] = True userdb.save_users(users, now) assert userdb.check_credentials(user_id, password, now) is False
def _action(self) -> bool: assert config.user.id is not None users = userdb.load_users(lock=True) user = users[config.user.id] cur_password = html.request.get_str_input_mandatory('cur_password') password = html.request.get_str_input_mandatory('password') password2 = html.request.get_str_input_mandatory('password2', '') # Force change pw mode if not cur_password: raise MKUserError("cur_password", _("You need to provide your current password.")) if not password: raise MKUserError("password", _("You need to change your password.")) if cur_password == password: raise MKUserError( "password", _("The new password must differ from your current one.")) if userdb.check_credentials(config.user.id, cur_password) is False: raise MKUserError("cur_password", _("Your old password is wrong.")) if password2 and password != password2: raise MKUserError("password2", _("The both new passwords do not match.")) watolib.verify_password_policy(password) user['password'] = hash_password(password) user['last_pw_change'] = int(time.time()) # In case the user was enforced to change it's password, remove the flag try: del user['enforce_pw_change'] except KeyError: pass # Increase serial to invalidate old authentication cookies if 'serial' not in user: user['serial'] = 1 else: user['serial'] += 1 userdb.save_users(users) # Set the new cookie to prevent logout for the current user login.update_auth_cookie(config.user.id) return True
def delete_users(users_to_delete): if config.user.id in users_to_delete: raise MKUserError(None, _("You cannot delete your own account!")) all_users = userdb.load_users(lock=True) deleted_users = [] for entry in users_to_delete: if entry in all_users: # Silently ignore not existing users deleted_users.append(entry) del all_users[entry] else: raise MKUserError(None, _("Unknown user: %s") % entry) if deleted_users: add_change("edit-users", _("Deleted user: %s") % ", ".join(deleted_users)) userdb.save_users(all_users)
def _action(self) -> bool: assert config.user.id is not None users = userdb.load_users(lock=True) user = users[config.user.id] language = html.request.get_ascii_input_mandatory('language', "") # Set the users language if requested to set it explicitly if language != "_default_": user['language'] = language config.user.language = language html.set_language_cookie(language) else: if 'language' in user: del user['language'] config.user.reset_language() # load the new language cmk.gui.i18n.localize(config.user.language) if config.user.may('general.edit_notifications') and user.get( "notifications_enabled"): value = forms.get_input(watolib.get_vs_flexible_notifications(), "notification_method") user["notification_method"] = value # Custom attributes if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if not attr.user_editable(): continue if attr.permission() and not config.user.may( attr.permission()): continue vs = attr.valuespec() value = vs.from_html_vars('ua_' + name) vs.validate_value(value, "ua_" + name) user[name] = value userdb.save_users(users) return True
for user in users.values(): method = user.get("notification_method") if method and isinstance(method, tuple) and method[0] == "flexible": channels_changed = 0 for channel in method[1]: if channel.get("only_hosts"): num_changed = rename_host_in_list(channel["only_hosts"], oldname, newname) if num_changed: channels_changed += 1 some_user_changed = True if channels_changed: actions += ["notify_flexible"] * channels_changed if some_user_changed: userdb.save_users(users) return actions def _rename_host_in_multisite(oldname, newname): # State of Multisite --------------------------------------- # Favorites of users and maybe other settings. We simply walk through # all directories rather then through the user database. That way we # are sure that also currently non-existant users are being found and # also only users that really have a profile. users_changed = 0 total_changed = 0 for profile_path in cmk.utils.paths.profile_dir.iterdir(): if not profile_path.is_dir(): continue
def _show_page_user_profile(change_pw): start_async_replication = False if not config.user.id: raise MKUserError(None, _('Not logged in.')) if not config.user.may('general.edit_profile') and not config.user.may( 'general.change_password'): raise MKAuthException( _("You are not allowed to edit your user profile.")) if not config.wato_enabled: raise MKAuthException( _('User profiles can not be edited (WATO is disabled).')) success = None if html.request.has_var('_save') and html.check_transaction(): users = userdb.load_users(lock=True) try: # Profile edit (user options like language etc.) if config.user.may('general.edit_profile'): if not change_pw: set_lang = html.get_checkbox('_set_lang') language = html.request.var('language') # Set the users language if requested if set_lang: if language == '': language = None # Set custom language users[config.user.id]['language'] = language config.user.language = language html.set_language_cookie(language) else: # Remove the customized language if 'language' in users[config.user.id]: del users[config.user.id]['language'] config.user.reset_language() # load the new language cmk.gui.i18n.localize(config.user.language) user = users.get(config.user.id) if config.user.may('general.edit_notifications' ) and user.get("notifications_enabled"): value = forms.get_input( watolib.get_vs_flexible_notifications(), "notification_method") users[config.user.id]["notification_method"] = value # Custom attributes if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): if not attr.permission() or config.user.may( attr.permission()): vs = attr.valuespec() value = vs.from_html_vars('ua_' + name) vs.validate_value(value, "ua_" + name) users[config.user.id][name] = value # Change the password if requested password_changed = False if config.user.may('general.change_password'): cur_password = html.request.var('cur_password') password = html.request.var('password') password2 = html.request.var('password2', '') if change_pw: # Force change pw mode if not cur_password: raise MKUserError( "cur_password", _("You need to provide your current password.")) if not password: raise MKUserError( "password", _("You need to change your password.")) if cur_password == password: raise MKUserError( "password", _("The new password must differ from your current one." )) if cur_password and password: if userdb.hook_login(config.user.id, cur_password) is False: raise MKUserError("cur_password", _("Your old password is wrong.")) if password2 and password != password2: raise MKUserError( "password2", _("The both new passwords do not match.")) watolib.verify_password_policy(password) users[config.user.id]['password'] = hash_password(password) users[config.user.id]['last_pw_change'] = int(time.time()) if change_pw: # Has been changed, remove enforcement flag del users[config.user.id]['enforce_pw_change'] # Increase serial to invalidate old cookies if 'serial' not in users[config.user.id]: users[config.user.id]['serial'] = 1 else: users[config.user.id]['serial'] += 1 password_changed = True # Now, if in distributed environment where users can login to remote sites, # set the trigger for pushing the new auth information to the slave sites # asynchronous if config.user.authorized_login_sites(): start_async_replication = True userdb.save_users(users) if password_changed: # Set the new cookie to prevent logout for the current user login.set_auth_cookie(config.user.id) success = True except MKUserError as e: html.add_user_error(e.varname, e) else: users = userdb.load_users() watolib.init_wato_datastructures(with_wato_lock=True) # When in distributed setup, display the replication dialog instead of the normal # profile edit dialog after changing the password. if start_async_replication: user_profile_async_replication_page() return if change_pw: title = _("Change Password") else: title = _("Edit User Profile") html.header(title) # Rule based notifications: The user currently cannot simply call the according # WATO module due to WATO permission issues. So we cannot show this button # right now. if not change_pw: rulebased_notifications = watolib.load_configuration_settings().get( "enable_rulebased_notifications") if rulebased_notifications and config.user.may( 'general.edit_notifications'): html.begin_context_buttons() url = "wato.py?mode=user_notifications_p" html.context_button(_("Notifications"), url, "notifications") html.end_context_buttons() else: reason = html.request.var('reason') if reason == 'expired': html.p( _('Your password is too old, you need to choose a new password.' )) else: html.p( _('You are required to change your password before proceeding.' )) if success: html.reload_sidebar() if change_pw: html.show_message(_("Your password has been changed.")) raise HTTPRedirect(html.request.var('_origtarget', 'index.py')) else: html.show_message(_("Successfully updated user profile.")) # Ensure theme changes are applied without additional user interaction html.immediate_browser_redirect(0.5, html.makeuri([])) if html.has_user_errors(): html.show_user_errors() user = users.get(config.user.id) if user is None: html.show_warning(_("Sorry, your user account does not exist.")) html.footer() return # Returns true if an attribute is locked and should be read only. Is only # checked when modifying an existing user locked_attributes = userdb.locked_attributes(user.get('connector')) def is_locked(attr): return attr in locked_attributes html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") forms.header(_("Personal Settings")) if not change_pw: forms.section(_("Name"), simple=True) html.write_text(user.get("alias", config.user.id)) if config.user.may( 'general.change_password') and not is_locked('password'): forms.section(_("Current Password")) html.password_input('cur_password', autocomplete="new-password") forms.section(_("New Password")) html.password_input('password', autocomplete="new-password") forms.section(_("New Password Confirmation")) html.password_input('password2', autocomplete="new-password") if not change_pw and config.user.may('general.edit_profile'): select_language(user) # Let the user configure how he wants to be notified if not rulebased_notifications \ and config.user.may('general.edit_notifications') \ and user.get("notifications_enabled"): forms.section(_("Notifications")) html.help( _("Here you can configure how you want to be notified about host and service problems and " "other monitoring events.")) watolib.get_vs_flexible_notifications().render_input( "notification_method", user.get("notification_method")) if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): vs = attr.valuespec() forms.section(_u(vs.title())) value = user.get(name, vs.default_value()) if not attr.permission() or config.user.may( attr.permission()): vs.render_input("ua_" + name, value) html.help(_u(vs.help())) else: html.write(vs.value_to_text(value)) # Save button forms.end() html.button("_save", _("Save")) html.close_div() html.hidden_fields() html.end_form() html.footer()
for user in users.values(): method = user.get("notification_method") if method and isinstance(method, tuple) and method[0] == "flexible": channels_changed = 0 for channel in method[1]: if channel.get("only_hosts"): num_changed = rename_host_in_list(channel["only_hosts"], oldname, newname) if num_changed: channels_changed += 1 some_user_changed = True if channels_changed: actions += ["notify_flexible"] * channels_changed if some_user_changed: userdb.save_users(users, datetime.now()) return actions def _rename_host_in_multisite(oldname, newname): # State of Multisite --------------------------------------- # Favorites of users and maybe other settings. We simply walk through # all directories rather then through the user database. That way we # are sure that also currently non-existant users are being found and # also only users that really have a profile. users_changed = 0 total_changed = 0 for profile_path in cmk.utils.paths.profile_dir.iterdir(): if not profile_path.is_dir(): continue