Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
67d2585
Implement initial question to import certificate
jreidinger Apr 11, 2025
31b1e37
Merge remote-tracking branch 'origin/master' into initial_certificates
jreidinger Apr 15, 2025
72873ff
experiment with paragraphs
jreidinger Apr 16, 2025
abf9ff0
add fingerprint storage and use it in registration
jreidinger Apr 16, 2025
65ceb8d
service: add data to certificate question
joseivanlopez Apr 16, 2025
5946bbb
allow registration url be redefined and also add ssl fingerprints
jreidinger Apr 16, 2025
930f816
update dbus doc and fix typo
jreidinger Apr 17, 2025
a57e474
add proxies for security
jreidinger Apr 17, 2025
7b65d88
Add registration url to rust part
jreidinger Apr 17, 2025
6901bae
implement for security in rust part that communicate with dbus
jreidinger Apr 19, 2025
48e5991
service: fix registration certificate question
joseivanlopez Apr 22, 2025
4cb3cf9
web: add question for registration certificate
joseivanlopez Apr 22, 2025
41663b2
Merge remote-tracking branch 'origin/master' into initial_certificates
jreidinger Apr 22, 2025
90d795d
add web part of security
jreidinger Apr 22, 2025
d8c0b16
rename key in profile
jreidinger Apr 23, 2025
9354ceb
apply clippy suggestion
jreidinger Apr 23, 2025
f1b0180
make rubocop happy
jreidinger Apr 23, 2025
6d0d98a
fix rust unit test
jreidinger Apr 23, 2025
64ba358
adjust dbus doc to make ci happy
jreidinger Apr 23, 2025
9214f64
Apply suggestions from code review
jreidinger Apr 23, 2025
caa3559
changes from code review
jreidinger Apr 23, 2025
6696dee
changes from review
jreidinger Apr 24, 2025
22a03c9
cargo fmt
jreidinger Apr 24, 2025
6eb69ae
changes
jreidinger Apr 24, 2025
2fa3f3d
Apply suggestions from code review
jreidinger Apr 24, 2025
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
1 change: 1 addition & 0 deletions doc/dbus/bus/org.opensuse.Agama.Security.bus.xml
1 change: 1 addition & 0 deletions doc/dbus/bus/org.opensuse.Agama.Software1.Product.bus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
</method>
<property type="s" name="RegCode" access="read"/>
<property type="s" name="Email" access="read"/>
<property type="s" name="Url" access="readwrite"/>
<property type="u" name="Requirement" access="read"/>
</interface>
</node>
3 changes: 3 additions & 0 deletions doc/dbus/bus/org.opensuse.Agama.Software1.bus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,7 @@
<property type="aa{sv}" name="All" access="read"/>
<property type="u" name="Current" access="read"/>
</interface>
<interface name="org.opensuse.Agama.Security">
<property type="a(ss)" name="SslFingerprints" access="readwrite"/>
</interface>
</node>
8 changes: 8 additions & 0 deletions doc/dbus/org.opensuse.Agama.Security.doc.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/org/opensuse/Agama/Software1">
<node name="Product"></node>
<node name="Proposal"></node>
<interface name="org.opensuse.Agama.Security">
<property type="a(ss)" name="SslFingerprints" access="readwrite"/>
</interface>
</node>
4 changes: 4 additions & 0 deletions doc/dbus/org.opensuse.Agama1.Registration.doc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
Email used for the registration. Empty if the current product is not registered yet.
-->
<property type="s" name="Email" access="read"/>
<!--
URL used to register against. Empty means use default
-->
<property type="s" name="Url" access="readwrite"/>
<!--
Indicates the registration requirement of the currently selected product.

Expand Down
8 changes: 4 additions & 4 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rust/agama-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ chrono = { version = "0.4.38", default-features = false, features = [
"clock",
] }
home = "0.5.9"
strum = { version = "0.26.3", features = ["derive"] }
strum = { version = "0.27.1", features = ["derive"] }
fs_extra = "1.3.0"
serde_with = "3.12.0"
regex = "1.11.1"
Expand Down
31 changes: 31 additions & 0 deletions rust/agama-lib/share/profile.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,33 @@
}
}
},
"security": {
"title": "Security settings",
"type": "object",
"properties": {
"sslCertificates": {
"title": "List of SSL certificates to add to system",
"type": "array",
"items": {
"type": "object",
"properties": {
"fingerprint": {
"title": "fingerprint of ssl certificate",
"type": "string",
"examples": ["A8:DE:08:B1:57:52:FE:70:DF:D5:31:EA:E3:53:BB:39:EE:01:FF:B9"]
},
"algorithm": {
"title": "Fingerprint algorithm used to compute it",
"type": "string",
"enum": ["SHA1", "SHA256"],
"examples": ["SHA1"]
}
},
"required": ["fingerprint", "algorithm"]
}
}
}
},
"software": {
"title": "Software settings",
"type": "object",
Expand Down Expand Up @@ -139,6 +166,10 @@
"registrationEmail": {
"title": "Product registration email",
"type": "string"
},
"registrationUrl": {
"title": "URL of the registration server",
"type": "string"
}
}
},
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-lib/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ pub enum ServiceError {
// FIXME reroute the error to a better place
#[error("Profile error: {0}")]
Profile(#[from] ProfileError),
#[error("Unsupported SSL Fingeprint algorithm '#{0}'.")]
UnsupportedSSLFingerprintAlgorithm(String),
#[error("Invalid URL: {0}")]
InvalidURL(#[from] url::ParseError),
}
Expand Down
4 changes: 4 additions & 0 deletions rust/agama-lib/src/install_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use crate::bootloader::model::BootloaderSettings;
use crate::files::model::UserFile;
use crate::hostname::model::HostnameSettings;
use crate::security::settings::SecuritySettings;
use crate::{
localization::LocalizationSettings, network::NetworkSettings, product::ProductSettings,
scripts::ScriptsConfig, software::SoftwareSettings, users::UserSettings,
Expand Down Expand Up @@ -57,6 +58,9 @@ pub struct InstallSettings {
pub user: Option<UserSettings>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub security: Option<SecuritySettings>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub software: Option<SoftwareSettings>,
#[serde(default)]
pub product: Option<ProductSettings>,
Expand Down
1 change: 1 addition & 0 deletions rust/agama-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub use store::Store;
pub mod openapi;
pub mod questions;
pub mod scripts;
pub mod security;
pub mod utils;

use crate::error::ServiceError;
Expand Down
10 changes: 10 additions & 0 deletions rust/agama-lib/src/product/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ impl<'a> ProductClient<'a> {
Ok(self.registration_proxy.email().await?)
}

/// URL of the registration server
pub async fn registration_url(&self) -> Result<String, ServiceError> {
Ok(self.registration_proxy.url().await?)
}

/// set registration url
pub async fn set_registration_url(&self, url: &str) -> Result<(), ServiceError> {
Ok(self.registration_proxy.set_url(url).await?)
}

/// list of already registered addons
pub async fn registered_addons(&self) -> Result<Vec<AddonParams>, ServiceError> {
let addons: Vec<AddonParams> = self
Expand Down
6 changes: 6 additions & 0 deletions rust/agama-lib/src/product/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ impl ProductHTTPClient {
self.client.get("/software/registration").await
}

pub async fn set_registration_url(&self, url: &String) -> Result<(), ServiceError> {
self.client
.put_void("/software/registration/url", url)
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks weird to me to have separate endpoints to set the registration code/email and the URL.

Perhaps in the future we might consider to add the URL to the registration params.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeap, but code+email is together registration...not sure if it should be also part of registration method param and expect that all following registration of addons is forced to be on identical url.

.await
}

// get list of registered addons
pub async fn get_registered_addons(&self) -> Result<Vec<AddonSettings>, ServiceError> {
self.client
Expand Down
6 changes: 6 additions & 0 deletions rust/agama-lib/src/product/proxies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ pub trait Registration {
#[zbus(property)]
fn reg_code(&self) -> zbus::Result<String>;

/// Url property
#[zbus(property)]
fn url(&self) -> zbus::Result<String>;
#[zbus(property)]
fn set_url(&self, value: &str) -> zbus::Result<()>;

/// registered addons property, list of tuples (name, version, reg_code))
#[zbus(property)]
fn registered_addons(&self) -> zbus::Result<Vec<(String, String, String)>>;
Expand Down
2 changes: 2 additions & 0 deletions rust/agama-lib/src/product/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@ pub struct ProductSettings {
#[serde(skip_serializing_if = "Option::is_none")]
pub registration_email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub registration_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub addons: Option<Vec<AddonSettings>>,
}
9 changes: 8 additions & 1 deletion rust/agama-lib/src/product/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl ProductStore {
id: Some(product),
registration_code: Self::non_empty_string(registration_info.key),
registration_email: Self::non_empty_string(registration_info.email),
registration_url: Self::non_empty_string(registration_info.url),
addons,
})
}
Expand All @@ -75,6 +76,9 @@ impl ProductStore {
probe = true;
}
}
if let Some(url) = &settings.registration_url {
self.product_client.set_registration_url(url).await?;
}
if let Some(reg_code) = &settings.registration_code {
let email = settings.registration_email.as_deref().unwrap_or("");

Expand Down Expand Up @@ -146,7 +150,8 @@ mod test {
.body(
r#"{
"key": "",
"email": ""
"email": "",
"url": ""
}"#,
);
});
Expand All @@ -166,6 +171,7 @@ mod test {
id: Some("Tumbleweed".to_owned()),
registration_code: None,
registration_email: None,
registration_url: None,
addons: None,
};
// main assertion
Expand Down Expand Up @@ -215,6 +221,7 @@ mod test {
id: Some("Tumbleweed".to_owned()),
registration_code: None,
registration_email: None,
registration_url: None,
addons: None,
};

Expand Down
6 changes: 6 additions & 0 deletions rust/agama-lib/src/security.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod client;
pub mod http_client;
pub mod model;
pub mod proxies;
pub mod settings;
pub mod store;
69 changes: 69 additions & 0 deletions rust/agama-lib/src/security/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) [2025] SUSE LLC
//
// All Rights Reserved.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, contact SUSE LLC.
//
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use std::str::FromStr;

use zbus::Connection;

use crate::error::ServiceError;

use super::{
model::{SSLFingerprint, SSLFingerprintAlgorithm},
proxies::SecurityProxy,
};

/// D-Bus client for the security service
#[derive(Clone)]
pub struct SecurityClient<'a> {
security_proxy: SecurityProxy<'a>,
}

impl SecurityClient<'_> {
pub async fn new(connection: Connection) -> Result<Self, ServiceError> {
Ok(Self {
security_proxy: SecurityProxy::new(&connection).await?,
})
}

pub async fn get_ssl_fingerprints(&self) -> Result<Vec<SSLFingerprint>, ServiceError> {
let dbus_list = self.security_proxy.ssl_fingerprints().await?;
dbus_list
.into_iter()
.map(|(alg, value)| {
Ok(SSLFingerprint {
fingerprint: value,
algorithm: SSLFingerprintAlgorithm::from_str(&alg)?,
})
})
.collect()
}

pub async fn set_ssl_fingerprints(
&self,
list: &Vec<SSLFingerprint>,
) -> Result<(), ServiceError> {
let dbus_list: Vec<(&str, &str)> = list
.iter()
.map(|s| (s.algorithm.into(), s.fingerprint.as_str()))
.collect();
self.security_proxy.set_ssl_fingerprints(&dbus_list).await?;
Ok(())
}
}
46 changes: 46 additions & 0 deletions rust/agama-lib/src/security/http_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) [2025] SUSE LLC
//
// All Rights Reserved.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, contact SUSE LLC.
//
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use crate::{base_http_client::BaseHTTPClient, error::ServiceError};

use super::model::SSLFingerprint;

pub struct SecurityHTTPClient {
client: BaseHTTPClient,
}

impl SecurityHTTPClient {
pub fn new(base: BaseHTTPClient) -> Self {
Self { client: base }
}

pub async fn get_ssl_fingerprints(&self) -> Result<Vec<SSLFingerprint>, ServiceError> {
self.client.get("/security/ssl_fingerprints").await
}

pub async fn set_ssl_fingerprints(
&self,
fps: &Vec<SSLFingerprint>,
) -> Result<(), ServiceError> {
self.client
.put_void("/security/ssl_fingerprints", fps)
.await
}
}
Loading
Loading