From 88ef1c340afd06375577595f7b2a0114998f27f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 12 Nov 2024 18:15:15 +0100 Subject: [PATCH] Initial support for encrypted password --- rust/agama-lib/src/users/client.rs | 6 +++++- rust/agama-lib/src/users/proxies.rs | 3 +++ rust/agama-lib/src/users/settings.rs | 4 ++++ rust/agama-lib/src/users/store.rs | 6 +++++- rust/agama-server/src/users/web.rs | 3 ++- service/lib/agama/dbus/users.rb | 11 ++++++----- service/lib/agama/users.rb | 10 ++++++++-- web/src/components/users/FirstUserForm.tsx | 4 ++++ web/src/types/users.ts | 1 + 9 files changed, 38 insertions(+), 10 deletions(-) diff --git a/rust/agama-lib/src/users/client.rs b/rust/agama-lib/src/users/client.rs index ee85124b3c..9747619236 100644 --- a/rust/agama-lib/src/users/client.rs +++ b/rust/agama-lib/src/users/client.rs @@ -35,6 +35,8 @@ pub struct FirstUser { pub user_name: String, /// First user's password (in clear text) pub password: String, + /// Whether the password is encrypted (true) or is plain text (false) + pub encrypted_password: bool, /// Whether auto-login should enabled or not pub autologin: bool, } @@ -46,7 +48,8 @@ impl FirstUser { full_name: data.0, user_name: data.1, password: data.2, - autologin: data.3, + encrypted_password: data.3, + autologin: data.4, }) } } @@ -107,6 +110,7 @@ impl<'a> UsersClient<'a> { &first_user.full_name, &first_user.user_name, &first_user.password, + first_user.encrypted_password, first_user.autologin, std::collections::HashMap::new(), ) diff --git a/rust/agama-lib/src/users/proxies.rs b/rust/agama-lib/src/users/proxies.rs index 73cba89ce5..b3743701de 100644 --- a/rust/agama-lib/src/users/proxies.rs +++ b/rust/agama-lib/src/users/proxies.rs @@ -47,6 +47,7 @@ use zbus::proxy; /// * full name /// * user name /// * password +/// * encrypted_password (true = encrypted, false = plain text) /// * auto-login (enabled or not) /// * some optional and additional data // NOTE: Manually added to this file. @@ -55,6 +56,7 @@ pub type FirstUser = ( String, String, bool, + bool, std::collections::HashMap, ); @@ -77,6 +79,7 @@ pub trait Users1 { full_name: &str, user_name: &str, password: &str, + encrypted_password: bool, auto_login: bool, data: std::collections::HashMap<&str, &zbus::zvariant::Value<'_>>, ) -> zbus::Result<(bool, Vec)>; diff --git a/rust/agama-lib/src/users/settings.rs b/rust/agama-lib/src/users/settings.rs index 54bdaf282e..f5e2bc05e5 100644 --- a/rust/agama-lib/src/users/settings.rs +++ b/rust/agama-lib/src/users/settings.rs @@ -43,6 +43,8 @@ pub struct FirstUserSettings { pub user_name: Option, /// First user's password (in clear text) pub password: Option, + /// Whether the password is encrypted or is plain text + pub encrypted_password: Option, /// Whether auto-login should enabled or not pub autologin: Option, } @@ -56,6 +58,8 @@ pub struct RootUserSettings { /// Root's password (in clear text) #[serde(skip_serializing)] pub password: Option, + /// Whether the password is encrypted or is plain text + pub encrypted_password: Option, /// Root SSH public key pub ssh_public_key: Option, } diff --git a/rust/agama-lib/src/users/store.rs b/rust/agama-lib/src/users/store.rs index f9ec93cecd..1c0635dca0 100644 --- a/rust/agama-lib/src/users/store.rs +++ b/rust/agama-lib/src/users/store.rs @@ -47,6 +47,7 @@ impl UsersStore { autologin: Some(first_user.autologin), full_name: Some(first_user.full_name), password: Some(first_user.password), + encrypted_password: Some(first_user.encrypted_password), }; let mut root_user = RootUserSettings::default(); let ssh_public_key = self.users_client.root_ssh_key().await?; @@ -77,6 +78,7 @@ impl UsersStore { full_name: settings.full_name.clone().unwrap_or_default(), autologin: settings.autologin.unwrap_or_default(), password: settings.password.clone().unwrap_or_default(), + encrypted_password: settings.encrypted_password.clone().unwrap_or_default(), ..Default::default() }; self.users_client.set_first_user(&first_user).await?; @@ -84,9 +86,11 @@ impl UsersStore { } async fn store_root_user(&self, settings: &RootUserSettings) -> Result<(), ServiceError> { + let encrypted_password = settings.encrypted_password.clone().unwrap_or_default(); + if let Some(root_password) = &settings.password { self.users_client - .set_root_password(root_password, false) + .set_root_password(root_password, encrypted_password) .await?; } diff --git a/rust/agama-server/src/users/web.rs b/rust/agama-server/src/users/web.rs index 4394c2b605..b66b0ccb11 100644 --- a/rust/agama-server/src/users/web.rs +++ b/rust/agama-server/src/users/web.rs @@ -90,7 +90,8 @@ async fn first_user_changed_stream( full_name: user.0, user_name: user.1, password: user.2, - autologin: user.3, + encrypted_password: user.3, + autologin: user.4, }; return Some(Event::FirstUserChanged(user_struct)); } diff --git a/service/lib/agama/dbus/users.rb b/service/lib/agama/dbus/users.rb index d9632a4e34..099ee5740e 100644 --- a/service/lib/agama/dbus/users.rb +++ b/service/lib/agama/dbus/users.rb @@ -58,7 +58,7 @@ def issues USERS_INTERFACE = "org.opensuse.Agama.Users1" private_constant :USERS_INTERFACE - FUSER_SIG = "in FullName:s, in UserName:s, in Password:s, in AutoLogin:b, in data:a{sv}" + FUSER_SIG = "in FullName:s, in UserName:s, in Password:s, in EncryptedPassword:b, in AutoLogin:b, in data:a{sv}" private_constant :FUSER_SIG dbus_interface USERS_INTERFACE do @@ -66,7 +66,7 @@ def issues dbus_reader :root_ssh_key, "s", dbus_name: "RootSSHKey" - dbus_reader :first_user, "(sssba{sv})" + dbus_reader :first_user, "(sssbba{sv})" dbus_method :SetRootPassword, "in Value:s, in Encrypted:b, out result:u" do |value, encrypted| @@ -97,9 +97,9 @@ def issues dbus_method :SetFirstUser, # It returns an Struct with the first field with the result of the operation as a boolean # and the second parameter as an array of issues found in case of failure - FUSER_SIG + ", out result:(bas)" do |full_name, user_name, password, auto_login, data| + FUSER_SIG + ", out result:(bas)" do |full_name, user_name, password, encrypted_password, auto_login, data| logger.info "Setting first user #{full_name}" - user_issues = backend.assign_first_user(full_name, user_name, password, auto_login, data) + user_issues = backend.assign_first_user(full_name, user_name, password, encrypted_password, auto_login, data) if user_issues.empty? dbus_properties_changed(USERS_INTERFACE, { "FirstUser" => first_user }, []) @@ -133,12 +133,13 @@ def root_ssh_key def first_user user = backend.first_user - return ["", "", "", false, {}] unless user + return ["", "", "", false, false, {}] unless user [ user.full_name, user.name, user.password_content || "", + user.password&.value&.encrypted? || false, backend.autologin?(user), {} ] diff --git a/service/lib/agama/users.rb b/service/lib/agama/users.rb index c294b3e819..84c17cef65 100644 --- a/service/lib/agama/users.rb +++ b/service/lib/agama/users.rb @@ -99,15 +99,21 @@ def remove_root_password # @param full_name [String] # @param user_name [String] # @param password [String] + # @param encrypted_password [Boolean] true = encrypted password, false = plain text password # @param auto_login [Boolean] # @param _data [Hash] # @return [Array] the list of fatal issues found - def assign_first_user(full_name, user_name, password, auto_login, _data) + def assign_first_user(full_name, user_name, password, encrypted_password, auto_login, _data) remove_first_user user = Y2Users::User.new(user_name) user.gecos = [full_name] - user.password = Y2Users::Password.create_plain(password) + user.password = if encrypted_password + Y2Users::Password.create_encrypted(password) + else + Y2Users::Password.create_plain(password) + end + fatal_issues = user.issues.map.select(&:error?) return fatal_issues.map(&:message) unless fatal_issues.empty? diff --git a/web/src/components/users/FirstUserForm.tsx b/web/src/components/users/FirstUserForm.tsx index 48b945a934..f0acb0adb1 100644 --- a/web/src/components/users/FirstUserForm.tsx +++ b/web/src/components/users/FirstUserForm.tsx @@ -134,6 +134,10 @@ export default function FirstUserForm() { if (!changePassword) { delete user.password; + } else { + // the web UI only supports plain text passwords, this resets the flag if an + // encrypted password was previously set from CLI + user.encryptedPassword = false; } delete user.passwordConfirmation; user.autologin = !!user.autologin; diff --git a/web/src/types/users.ts b/web/src/types/users.ts index 4714669f06..45a41df011 100644 --- a/web/src/types/users.ts +++ b/web/src/types/users.ts @@ -24,6 +24,7 @@ type FirstUser = { fullName: string; userName: string; password: string; + encryptedPassword: boolean; autologin: boolean; };