From 75cb3cb2bd4fab1e8044aa4fc90d5f8da434bb76 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 7 Dec 2022 19:24:50 +0100 Subject: [PATCH] Add a virtual setting to enable passwordless sudo for admins --- locales/en.json | 1 + share/config_global.toml | 5 +++++ src/settings.py | 27 ++++++++++++++++++--------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/locales/en.json b/locales/en.json index 36b2f41ad..d47447e14 100644 --- a/locales/en.json +++ b/locales/en.json @@ -393,6 +393,7 @@ "firewall_reloaded": "Firewall reloaded", "firewall_rules_cmd_failed": "Some firewall rule commands have failed. More info in log.", "global_settings_reset_success": "Reset global settings", + "global_settings_setting_passwordless_sudo": "Allow admins to use 'sudo' without re-typing their passwords", "global_settings_setting_admin_strength": "Admin password strength requirements", "global_settings_setting_admin_strength_help": "These requirements are only enforced when initializing or changing the password", "global_settings_setting_backup_compress_tar_archives": "Compress backups", diff --git a/share/config_global.toml b/share/config_global.toml index 27f8d47dc..51754166c 100644 --- a/share/config_global.toml +++ b/share/config_global.toml @@ -22,6 +22,11 @@ name = "Security" choices.4 = "ditto, but also require at least 12 chars" default = "1" + [security.password.passwordless_sudo] + type = "boolean" + # The actual value is dynamically computed by checking the sudoOption of cn=admins,ou=sudo + default = false + [security.ssh] name = "SSH" [security.ssh.ssh_compatibility] diff --git a/src/settings.py b/src/settings.py index fa5021f7a..5fed8a8a3 100644 --- a/src/settings.py +++ b/src/settings.py @@ -120,6 +120,7 @@ class SettingsConfigPanel(ConfigPanel): entity_type = "global" save_path_tpl = SETTINGS_PATH save_mode = "diff" + virtual_settings = ["root_password", "root_password_confirm", "passwordless_sudo"] def __init__(self, config_path=None, save_path=None, creation=False): super().__init__("settings") @@ -128,17 +129,12 @@ class SettingsConfigPanel(ConfigPanel): root_password = self.new_values.pop("root_password", None) root_password_confirm = self.new_values.pop("root_password_confirm", None) + passwordless_sudo = self.new_values.pop("passwordless_sudo", None) - if "root_password" in self.values: - del self.values["root_password"] - if "root_password_confirm" in self.values: - del self.values["root_password_confirm"] - if "root_password" in self.new_values: - del self.new_values["root_password"] - if "root_password_confirm" in self.new_values: - del self.new_values["root_password_confirm"] + self.values = {k: v for k, v in self.values.items() if k not in self.virtual_settings} + self.new_values = {k: v for k, v in self.new_values.items() if k not in self.virtual_settings} - assert "root_password" not in self.future_values + assert all(v not in self.future_values for v in self.virtual_settings) if root_password and root_password.strip(): @@ -149,6 +145,11 @@ class SettingsConfigPanel(ConfigPanel): tools_rootpw(root_password, check_strength=True) + if passwordless_sudo is not None: + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + ldap.update("cn=admins,ou=sudo", {"sudoOption": ["!authenticate"] if passwordless_sudo else []}) + super()._apply() settings = { @@ -172,6 +173,14 @@ class SettingsConfigPanel(ConfigPanel): self.values["root_password"] = "" self.values["root_password_confirm"] = "" + # Specific logic for virtual setting "passwordless_sudo" + try: + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + self.values["passwordless_sudo"] = "!authenticate" in ldap.search("ou=sudo", "cn=admins", ["sudoOption"])[0].get("sudoOption", []) + except: + self.values["passwordless_sudo"] = False + def get(self, key="", mode="classic"): result = super().get(key=key, mode=mode)