Skip to content
Open
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
2 changes: 1 addition & 1 deletion rust/agama-lib/share/storage.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@
},
"encryptionSwap": {
"description": "Swap encryptions.",
"enum": ["protected_swap", "secure_swap", "random_swap"]
"enum": ["protected_swap", "secure_swap", "random_swap", "protectedSwap", "secureSwap", "randomSwap"]
},
"encryptionPassword": {
"description": "Password to use when creating a new encryption device.",
Expand Down
7 changes: 7 additions & 0 deletions rust/agama-manager/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,10 @@ impl SolveStorageModel {
impl Message for SolveStorageModel {
type Reply = Option<Value>;
}

/// Gets the available encryption methods.
pub struct GetEncryptionMethods;

impl Message for GetEncryptionMethods {
type Reply = Vec<String>;
}
14 changes: 14 additions & 0 deletions rust/agama-manager/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,20 @@ impl MessageHandler<message::SolveStorageModel> for Service {
}
}

#[async_trait]
impl MessageHandler<message::GetEncryptionMethods> for Service {
/// Gets the available encryption methods.
async fn handle(
&mut self,
_message: message::GetEncryptionMethods,
) -> Result<Vec<String>, Error> {
Ok(self
.storage
.call(storage::message::GetEncryptionMethods)
.await?)
}
}

// FIXME: write a macro to forward a message.
#[async_trait]
impl MessageHandler<software::message::SetResolvables> for Service {
Expand Down
18 changes: 18 additions & 0 deletions rust/agama-server/src/server/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub fn server_with_state(state: ServerState) -> Result<Router, ServiceError> {
.route("/private/resolvables/:id", put(set_resolvables))
.route("/private/download_logs", get(download_logs))
.route("/private/password_check", post(check_password))
.route("/private/encryption_methods", get(get_encryption_methods))
.with_state(state))
}

Expand Down Expand Up @@ -586,3 +587,20 @@ async fn check_password(
.await?;
Ok(Json(result))
}

/// Returns the available encryption methods for the current system and product.
#[utoipa::path(
get,
path = "/private/encryption_methods",
context_path = "/api/v2",
responses(
(status = 200, description = "Available encryption methods", body = Vec<String>),
(status = 400, description = "Could not retrieve encryption methods")
)
)]
async fn get_encryption_methods(
State(state): State<ServerState>,
) -> Result<Json<Vec<String>>, Error> {
let methods = state.manager.call(message::GetEncryptionMethods).await?;
Ok(Json(methods))
}
6 changes: 6 additions & 0 deletions rust/agama-storage-client/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ impl Message for GetIssues {
type Reply = Vec<Issue>;
}

pub struct GetEncryptionMethods;

impl Message for GetEncryptionMethods {
type Reply = Vec<String>;
}

pub struct GetConfigFromModel {
pub model: serde_json::Value,
}
Expand Down
4 changes: 4 additions & 0 deletions rust/agama-storage-client/src/proxies/storage1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ pub trait Storage1 {
#[zbus(property)]
fn config_model(&self) -> zbus::Result<String>;

/// EncryptionMethods property
#[zbus(property)]
fn encryption_methods(&self) -> zbus::Result<String>;

/// Issues property
#[zbus(property)]
fn issues(&self) -> zbus::Result<String>;
Expand Down
11 changes: 11 additions & 0 deletions rust/agama-storage-client/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,17 @@ impl MessageHandler<message::GetIssues> for Service {
}
}

#[async_trait]
impl MessageHandler<message::GetEncryptionMethods> for Service {
async fn handle(
&mut self,
_message: message::GetEncryptionMethods,
) -> Result<Vec<String>, Error> {
let raw_json = self.storage_proxy.encryption_methods().await?;
Ok(try_from_string(&raw_json)?)
}
}

#[async_trait]
impl MessageHandler<message::GetConfigFromModel> for Service {
async fn handle(
Expand Down
9 changes: 9 additions & 0 deletions rust/agama-storage/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub trait StorageClient {
) -> Result<BoxFuture<Result<(), Error>>, Error>;
async fn solve_config_model(&self, model: Value) -> Result<Option<Value>, Error>;
async fn set_locale(&self, locale: String) -> Result<(), Error>;
async fn get_encryption_methods(&self) -> Result<Vec<String>, Error>;
}

/// D-Bus client for the storage service
Expand Down Expand Up @@ -164,4 +165,12 @@ impl StorageClient for Client {
.await?;
Ok(())
}

async fn get_encryption_methods(&self) -> Result<Vec<String>, Error> {
let value = self
.storage_client
.call(message::GetEncryptionMethods)
.await?;
Ok(value)
}
}
7 changes: 7 additions & 0 deletions rust/agama-storage/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,10 @@ impl SetLocale {
impl Message for SetLocale {
type Reply = ();
}

#[derive(Clone)]
pub struct GetEncryptionMethods;

impl Message for GetEncryptionMethods {
type Reply = Vec<String>;
}
10 changes: 10 additions & 0 deletions rust/agama-storage/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,13 @@ impl MessageHandler<message::SetLocale> for Service {
Ok(())
}
}

#[async_trait]
impl MessageHandler<message::GetEncryptionMethods> for Service {
async fn handle(
&mut self,
_message: message::GetEncryptionMethods,
) -> Result<Vec<String>, Error> {
Ok(self.client.get_encryption_methods().await?)
}
}
4 changes: 4 additions & 0 deletions rust/agama-storage/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ impl StorageClient for TestClient {
async fn set_locale(&self, _locale: String) -> Result<(), Error> {
Ok(())
}

async fn get_encryption_methods(&self) -> Result<Vec<String>, Error> {
Ok(vec!["luks2".to_string()])
}
}

/// Starts a testing storage service.
Expand Down
9 changes: 9 additions & 0 deletions rust/package/agama.changes
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
-------------------------------------------------------------------
Thu Apr 9 14:26:29 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- Support camelCase encryption method names (randomSwap,
protectedSwap, secureSwap) in addition to snake_case for
backward compatibility (gh#agama-project/agama#3369).
- Move encryption methods from the /system endpoint to
/private/encryption_methods (gh#agama-project/agama#3369).

-------------------------------------------------------------------
Thu Apr 9 06:53:34 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- Fix OpenAPI specification for PATCH /config (gh#agama-project/agama#3368).
Expand Down
5 changes: 0 additions & 5 deletions rust/share/system.storage.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@
"type": "array",
"items": { "type": "string" }
},
"encryptionMethods": {
"description": "Possible encryption methods for the current system and product",
"type": "array",
"items": { "$ref": "#/$defs/encryptionMethod" }
},
"volumeTemplates": {
"description": "Volumes defined by the product as templates",
"type": "array",
Expand Down
23 changes: 13 additions & 10 deletions service/lib/agama/dbus/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def initialize(manager, task_runner, logger: nil)
@serialized_proposal = serialize_proposal
@serialized_issues = serialize_issues
@serialized_bootloader_config = serialize_bootloader_config
@serialized_encryption_methods = serialize_encryption_methods
register_progress_callbacks
end

Expand All @@ -68,6 +69,8 @@ def initialize(manager, task_runner, logger: nil)
dbus_reader_attr_accessor :serialized_config_model, "s", dbus_name: "ConfigModel"
dbus_reader_attr_accessor :serialized_proposal, "s", dbus_name: "Proposal"
dbus_reader_attr_accessor :serialized_issues, "s", dbus_name: "Issues"
dbus_reader_attr_accessor :serialized_encryption_methods, "s",
dbus_name: "EncryptionMethods"
dbus_method(:Activate) { activate }
dbus_method(:Probe) { probe }
dbus_method(:Install) { install }
Expand Down Expand Up @@ -182,6 +185,16 @@ def solve_config_model(serialized_model)
JSON.pretty_generate(solved_model_json)
end

# Generates the serialized JSON of the available encryption methods.
#
# @return [String] Serialized list of encryption method IDs.
def serialize_encryption_methods
methods = Agama::Storage::EncryptionSettings
.available_methods
.map { |m| Agama::Storage::EncryptionSettings.method_id(m) }
JSON.pretty_generate(methods)
end

# Implementation for the API method #Install.
def install
start_progress(3, _("Preparing bootloader proposal"))
Expand Down Expand Up @@ -406,7 +419,6 @@ def serialize_system
candidateMdRaids: candidate_md_raids,
issues: system_issues_json,
productMountPoints: product_mount_points,
encryptionMethods: encryption_methods,
volumeTemplates: volume_templates
}
JSON.pretty_generate(json)
Expand Down Expand Up @@ -535,15 +547,6 @@ def product_mount_points
.reject(&:empty?)
end

# Reads the list of possible encryption methods for the current system and product.
#
# @return [Array<String>]
def encryption_methods
Agama::Storage::EncryptionSettings
.available_methods
.map { |m| Agama::Storage::EncryptionSettings.method_id(m) }
end

# Default volumes to be used as templates
#
# @return [Array<Hash>]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ def swap_encryption_conversions
return {} unless encryption_json.is_a?(String)

# TODO: Report issue if the schema admits an unknown method.
method = Y2Storage::EncryptionMethod.find(encryption_json.to_sym)
# Normalize camelCase to snake_case (y2storage uses "random_swap", "tmp_fde", etc.).
normalized = encryption_json.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
method = Y2Storage::EncryptionMethod.find(normalized.to_sym)
return {} unless method

{
Expand Down
8 changes: 8 additions & 0 deletions service/package/rubygem-agama-yast.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
-------------------------------------------------------------------
Thu Apr 9 14:26:29 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- Expose encryption methods as a D-Bus property (gh#agama-project/agama#3369).
- Support camelCase encryption method names (randomSwap,
protectedSwap, secureSwap) in addition to snake_case for
backward compatibility.

-------------------------------------------------------------------
Wed Apr 8 09:28:31 UTC 2026 - Knut Anderssen <kanderssen@suse.com>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,72 @@
expect(encryption.label).to be_nil
end
end

context "if 'encryption' is 'random_swap' (snake_case)" do
let(:encryption) { "random_swap" }

it "sets #encryption to the expected value" do
config = subject.convert
encryption = config.encryption
expect(encryption).to be_a(Agama::Storage::Configs::Encryption)
expect(encryption.method).to eq(Y2Storage::EncryptionMethod::RANDOM_SWAP)
end
end

context "if 'encryption' is 'randomSwap' (camelCase)" do
let(:encryption) { "randomSwap" }

it "sets #encryption to the expected value" do
config = subject.convert
encryption = config.encryption
expect(encryption).to be_a(Agama::Storage::Configs::Encryption)
expect(encryption.method).to eq(Y2Storage::EncryptionMethod::RANDOM_SWAP)
end
end

context "if 'encryption' is 'protected_swap' (snake_case)" do
let(:encryption) { "protected_swap" }

it "sets #encryption to the expected value" do
config = subject.convert
encryption = config.encryption
expect(encryption).to be_a(Agama::Storage::Configs::Encryption)
expect(encryption.method).to eq(Y2Storage::EncryptionMethod::PROTECTED_SWAP)
end
end

context "if 'encryption' is 'protectedSwap' (camelCase)" do
let(:encryption) { "protectedSwap" }

it "sets #encryption to the expected value" do
config = subject.convert
encryption = config.encryption
expect(encryption).to be_a(Agama::Storage::Configs::Encryption)
expect(encryption.method).to eq(Y2Storage::EncryptionMethod::PROTECTED_SWAP)
end
end

context "if 'encryption' is 'secure_swap' (snake_case)" do
let(:encryption) { "secure_swap" }

it "sets #encryption to the expected value" do
config = subject.convert
encryption = config.encryption
expect(encryption).to be_a(Agama::Storage::Configs::Encryption)
expect(encryption.method).to eq(Y2Storage::EncryptionMethod::SECURE_SWAP)
end
end

context "if 'encryption' is 'secureSwap' (camelCase)" do
let(:encryption) { "secureSwap" }

it "sets #encryption to the expected value" do
config = subject.convert
encryption = config.encryption
expect(encryption).to be_a(Agama::Storage::Configs::Encryption)
expect(encryption.method).to eq(Y2Storage::EncryptionMethod::SECURE_SWAP)
end
end
end
end

Expand Down
7 changes: 7 additions & 0 deletions web/package/agama-web-ui.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Thu Apr 9 14:26:29 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- Update TypeScript types to support camelCase encryption
method names (randomSwap, protectedSwap, secureSwap) in addition
to snake_case (gh#agama-project/agama#3369).

-------------------------------------------------------------------
Tue Apr 7 14:19:47 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

Expand Down
3 changes: 3 additions & 0 deletions web/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ const solveStorageModel = (model: ConfigModel.Config): Promise<ConfigModel.Confi
return get(`/api/v2/private/solve_storage_model?model=${json}`);
};

const getEncryptionMethods = (): Promise<string[]> => get("/api/v2/private/encryption_methods");

const putConfig = (config: Config): Response => put("/api/v2/config", config);

const putStorageModel = (model: ConfigModel.Config) => put("/api/v2/private/storage_model", model);
Expand Down Expand Up @@ -114,6 +116,7 @@ export {
getQuestions,
getStorageModel,
solveStorageModel,
getEncryptionMethods,
putConfig,
putStorageModel,
patchConfig,
Expand Down
Loading
Loading