Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions rust/agama-lib/src/dbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ pub fn extract_id_from_path(path: &OwnedObjectPath) -> Result<u32, zvariant::Err
})
}

// small helper to convert dbus empty string to None
pub fn optional_string(value: String) -> Option<String> {
if value.is_empty() {
None
} else {
Some(value)
}
}

#[cfg(test)]
mod tests {
use std::collections::HashMap;
Expand Down
28 changes: 27 additions & 1 deletion rust/agama-lib/src/users/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UserSettings {
#[serde(rename = "user")]
#[serde(rename = "user", skip_serializing_if = "Option::is_none")]
pub first_user: Option<FirstUserSettings>,
#[serde(skip_serializing_if = "Option::is_none")]
pub root: Option<RootUserSettings>,
}

Expand All @@ -38,15 +39,34 @@ pub struct UserSettings {
#[serde(rename_all = "camelCase")]
pub struct FirstUserSettings {
/// First user's full name
#[serde(skip_serializing_if = "Option::is_none")]
pub full_name: Option<String>,
/// First user's username
#[serde(skip_serializing_if = "Option::is_none")]
pub user_name: Option<String>,
/// First user's password (in clear text)
#[serde(skip_serializing_if = "Option::is_none")]
pub password: Option<String>,
/// Whether the password is hashed or is plain text
#[serde(skip_serializing_if = "Self::skip_hashed")]
pub hashed_password: Option<bool>,
}

impl FirstUserSettings {
// as dbus provides only boolean it is hard to distinguish
// false and not set. So act for false like not set as it is default.
fn skip_hashed(value: &Option<bool>) -> bool {
*value == Some(false)
}

pub fn skip_export(&self) -> bool {
self.full_name.is_none()
&& self.user_name.is_none()
&& self.password.is_none()
&& Self::skip_hashed(&self.hashed_password)
}
}

/// Root user settings
///
/// Holds the settings for the root user.
Expand All @@ -63,3 +83,9 @@ pub struct RootUserSettings {
#[serde(skip_serializing_if = "Option::is_none")]
pub ssh_public_key: Option<String>,
}

impl RootUserSettings {
pub fn skip_export(&self) -> bool {
self.password.is_none() && self.hashed_password.is_none() && self.ssh_public_key.is_none()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@imobachgs sadly here we have identical problem to first user with hashed password...but here it is even more tricky as password and hashed_password are always skipped during deserializing, so unless ssh_public_key is set, then it is always empty....but on other hand we want somehow indicate user that they need to set it in root user.
So I am not sure how to proceed here. Of course I can change it to something like self.hashed_password == Some(false)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw if hashed_password is set to true, should we really omit serialization of password to profile?

}
}
22 changes: 17 additions & 5 deletions rust/agama-lib/src/users/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

use super::{FirstUser, FirstUserSettings, RootUserSettings, UserSettings, UsersHTTPClient};
use crate::base_http_client::BaseHTTPClient;
use crate::dbus::optional_string;
use crate::error::ServiceError;

/// Loads and stores the users settings from/to the D-Bus service.
Expand All @@ -43,21 +44,32 @@ impl UsersStore {
pub async fn load(&self) -> Result<UserSettings, ServiceError> {
let first_user = self.users_client.first_user().await?;
let first_user = FirstUserSettings {
user_name: Some(first_user.user_name),
full_name: Some(first_user.full_name),
password: Some(first_user.password),
user_name: optional_string(first_user.user_name),
full_name: optional_string(first_user.full_name),
password: optional_string(first_user.password),
// sadly for boolean we do not have on dbus way to set it to not_set
hashed_password: Some(first_user.hashed_password),
};
let first_user = if first_user.skip_export() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could extend the D-Bus API with information about whether the user or the root was set instead of checking many conditions here.

None
} else {
Some(first_user)
};
let root_user = self.users_client.root_user().await?;
let root_user = RootUserSettings {
password: root_user.password,
hashed_password: root_user.hashed_password,
ssh_public_key: root_user.ssh_public_key,
};
let root_user = if root_user.skip_export() {
None
} else {
Some(root_user)
};

Ok(UserSettings {
first_user: Some(first_user),
root: Some(root_user),
first_user: first_user,
root: root_user,
})
}

Expand Down
Loading