This is an automated email from the ASF dual-hosted git repository.
hubcio pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/master by this push:
new 3b9ba2f93 fix(security): require read_servers permission for system
snapshot (#3579)
3b9ba2f93 is described below
commit 3b9ba2f9340cffc077c9ea39d01d7e8c28be05d0
Author: Hubert Gruszecki <[email protected]>
AuthorDate: Mon Jun 29 12:00:22 2026 +0200
fix(security): require read_servers permission for system snapshot (#3579)
---
.../tests/server/scenarios/permissions_scenario.rs | 26 ++++++++++++++++++++++
.../binary/handlers/system/get_snapshot_handler.rs | 1 +
core/server/src/http/system.rs | 7 +++++-
core/server/src/metadata/reader.rs | 4 ++++
4 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/core/integration/tests/server/scenarios/permissions_scenario.rs
b/core/integration/tests/server/scenarios/permissions_scenario.rs
index b9eac9f23..f16c168f3 100644
--- a/core/integration/tests/server/scenarios/permissions_scenario.rs
+++ b/core/integration/tests/server/scenarios/permissions_scenario.rs
@@ -213,6 +213,18 @@ async fn test_no_permissions(harness: &TestHarness,
root_client: &IggyClient) {
.await,
"poll_messages",
);
+ // Snapshot returns a real archive, never Ok(None), so a denied caller must
+ // get an explicit Unauthorized rather than the enumeration-safe Ok of
reads.
+ let snapshot_result = client
+ .snapshot(
+ SnapshotCompression::Deflated,
+ vec![SystemSnapshotType::Test],
+ )
+ .await;
+ assert!(
+ matches!(&snapshot_result, Err(e) if e.as_code() ==
IggyError::Unauthorized.as_code()),
+ "snapshot must be rejected as unauthorized without read_servers, got
{snapshot_result:?}"
+ );
delete_test_user(root_client, USER).await;
}
@@ -246,6 +258,13 @@ async fn test_system_permissions(harness: &TestHarness,
root_client: &IggyClient
.get_clients()
.await
.expect("read_servers: get_clients should work");
+ client
+ .snapshot(
+ SnapshotCompression::Deflated,
+ vec![SystemSnapshotType::Test],
+ )
+ .await
+ .expect("read_servers: snapshot should work");
// But cannot read users or streams
assert_unauthorized(client.get_users().await, "read_servers: get_users");
@@ -277,6 +296,13 @@ async fn test_system_permissions(harness: &TestHarness,
root_client: &IggyClient
.get_clients()
.await
.expect("manage_servers: get_clients should work");
+ client
+ .snapshot(
+ SnapshotCompression::Deflated,
+ vec![SystemSnapshotType::Test],
+ )
+ .await
+ .expect("manage_servers: snapshot should work");
delete_test_user(root_client, MANAGE_USER).await;
}
diff --git a/core/server/src/binary/handlers/system/get_snapshot_handler.rs
b/core/server/src/binary/handlers/system/get_snapshot_handler.rs
index 73cdda2d9..132613f6a 100644
--- a/core/server/src/binary/handlers/system/get_snapshot_handler.rs
+++ b/core/server/src/binary/handlers/system/get_snapshot_handler.rs
@@ -33,6 +33,7 @@ pub async fn handle_get_snapshot(
) -> Result<HandlerResult, IggyError> {
debug!("session: {session}, command: get_snapshot");
shard.ensure_authenticated(session)?;
+ shard.metadata.perm_get_snapshot(session.get_user_id())?;
let compression = SnapshotCompression::from_code(req.compression)?;
let snapshot_types: Vec<SystemSnapshotType> = req
diff --git a/core/server/src/http/system.rs b/core/server/src/http/system.rs
index 507613db8..7d8e26b94 100644
--- a/core/server/src/http/system.rs
+++ b/core/server/src/http/system.rs
@@ -127,9 +127,14 @@ async fn get_clients(
#[debug_handler]
async fn get_snapshot(
State(state): State<Arc<AppState>>,
- Extension(_identity): Extension<Identity>,
+ Extension(identity): Extension<Identity>,
Json(command): Json<GetSnapshot>,
) -> Result<impl IntoResponse, CustomError> {
+ state
+ .shard
+ .shard()
+ .metadata
+ .perm_get_snapshot(identity.user_id)?;
if command.snapshot_types.contains(&SystemSnapshotType::All) &&
command.snapshot_types.len() > 1
{
error!("When using 'All' snapshot type, no other types can be
specified");
diff --git a/core/server/src/metadata/reader.rs
b/core/server/src/metadata/reader.rs
index 59d6fcd0b..a5a5ed29a 100644
--- a/core/server/src/metadata/reader.rs
+++ b/core/server/src/metadata/reader.rs
@@ -1290,6 +1290,10 @@ impl Metadata {
self.perm_get_server_info(user_id)
}
+ pub fn perm_get_snapshot(&self, user_id: u32) -> Result<(), IggyError> {
+ self.perm_get_server_info(user_id)
+ }
+
fn perm_get_server_info(&self, user_id: u32) -> Result<(), IggyError> {
let metadata = self.load();