Skip to content

Commit f99b6b6

Browse files
Support for {PUT, DELETE} /api/topic-permissions/{vhost}/{user}
1 parent 01798ca commit f99b6b6

File tree

6 files changed

+259
-12
lines changed

6 files changed

+259
-12
lines changed

src/api.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,10 @@ where
14041404

14051405
/// Lists all topic permissions of a user.
14061406
/// See [Topic Authorisation](https://www.rabbitmq.com/docs/access-control#topic-authorisation) to learn more.
1407-
pub async fn list_topic_permissions_of(&self, user: &str) -> Result<Vec<responses::TopicPermission>> {
1407+
pub async fn list_topic_permissions_of(
1408+
&self,
1409+
user: &str,
1410+
) -> Result<Vec<responses::TopicPermission>> {
14081411
let response = self
14091412
.http_get(path!("users", user, "topic-permissions"), None, None)
14101413
.await?;
@@ -1420,6 +1423,36 @@ where
14201423
Ok(response)
14211424
}
14221425

1426+
/// Sets [topic permissions](https://www.rabbitmq.com/docs/access-control#topic-authorisation) in a specific virtual host.
1427+
pub async fn declare_topic_permissions(
1428+
&self,
1429+
params: &requests::TopicPermissions<'_>,
1430+
) -> Result<()> {
1431+
self.put_api_request(
1432+
path!("topic-permissions", params.vhost, params.user),
1433+
params,
1434+
)
1435+
.await
1436+
}
1437+
1438+
/// Clears [topic permissions](https://www.rabbitmq.com/docs/access-control#topic-authorisation) for a user in a specific virtual host.
1439+
pub async fn clear_topic_permissions(
1440+
&self,
1441+
vhost: &str,
1442+
user: &str,
1443+
idempotently: bool,
1444+
) -> Result<()> {
1445+
let excludes = if idempotently {
1446+
Some(StatusCode::NOT_FOUND)
1447+
} else {
1448+
None
1449+
};
1450+
let _response = self
1451+
.http_delete(path!("topic-permissions", vhost, user), excludes, None)
1452+
.await?;
1453+
Ok(())
1454+
}
1455+
14231456
//
14241457
// Rebalancing
14251458
//

src/blocking_api.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,8 +1313,7 @@ where
13131313
/// Lists all topic permissions of a user.
13141314
/// See [Topic Authorisation](https://www.rabbitmq.com/docs/access-control#topic-authorisation) to learn more.
13151315
pub fn list_topic_permissions_of(&self, user: &str) -> Result<Vec<responses::TopicPermission>> {
1316-
let response = self
1317-
.http_get(path!("users", user, "topic-permissions"), None, None)?;
1316+
let response = self.http_get(path!("users", user, "topic-permissions"), None, None)?;
13181317
let response = response.json()?;
13191318
Ok(response)
13201319
}
@@ -1326,6 +1325,31 @@ where
13261325
Ok(response)
13271326
}
13281327

1328+
/// Sets [topic permissions](https://www.rabbitmq.com/docs/access-control#topic-authorisation) in a specific virtual host.
1329+
pub fn declare_topic_permissions(&self, params: &requests::TopicPermissions<'_>) -> Result<()> {
1330+
self.put_api_request(
1331+
path!("topic-permissions", params.vhost, params.user),
1332+
params,
1333+
)
1334+
}
1335+
1336+
/// Clears [topic permissions](https://www.rabbitmq.com/docs/access-control#topic-authorisation) for a user in a specific virtual host.
1337+
pub fn clear_topic_permissions(
1338+
&self,
1339+
vhost: &str,
1340+
user: &str,
1341+
idempotently: bool,
1342+
) -> Result<()> {
1343+
let excludes = if idempotently {
1344+
Some(StatusCode::NOT_FOUND)
1345+
} else {
1346+
None
1347+
};
1348+
let _response =
1349+
self.http_delete(path!("topic-permissions", vhost, user), excludes, None)?;
1350+
Ok(())
1351+
}
1352+
13291353
//
13301354
// Rebalancing
13311355
//

src/requests.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ impl From<PolDef> for PolicyDefinition {
568568
///
569569
/// # Pattern Examples
570570
/// * `"^amq\\."` - Matches resources starting with "amq."
571-
/// * `".*"` - Matches all resources
571+
/// * `".*"` - Matches all resources
572572
/// * `"orders\\.(urgent|normal)"` - Matches "orders.urgent" or "orders.normal"
573573
/// * `"temp_.*"` - Matches resources starting with "temp_"
574574
///
@@ -614,10 +614,25 @@ pub struct Permissions<'a> {
614614
pub configure: &'a str,
615615
/// Regex pattern for resources user can read from
616616
pub read: &'a str,
617-
/// Regex pattern for resources user can write to
617+
/// Regex pattern for resources user can write to
618618
pub write: &'a str,
619619
}
620620

621+
/// Represents a user's [topic permission in a particular virtual host](https://www.rabbitmq.com/docs/access-control#topic-authorisation).
622+
///
623+
/// Topic permissions are defined using regular expression patterns that match exchange names.
624+
#[derive(Serialize, Debug)]
625+
pub struct TopicPermissions<'a> {
626+
pub user: &'a str,
627+
pub vhost: &'a str,
628+
/// Regex pattern for the topics the user can publish to
629+
pub write: &'a str,
630+
/// Regex pattern for the topics the user can consume from (subscribe to)
631+
pub read: &'a str,
632+
/// The topic exchange these permissions apply to
633+
pub exchange: &'a str,
634+
}
635+
621636
/// Controls when federation resources (temporary queues/exchanges) are cleaned up.
622637
///
623638
/// Federation creates temporary resources on the downstream cluster. This enum controls

src/responses.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1590,7 +1590,6 @@ pub struct TopicPermission {
15901590
pub write: String,
15911591
}
15921592

1593-
15941593
impl Permissions {
15951594
pub fn with_username(&self, username: &str) -> Self {
15961595
Permissions {

tests/async_permission_tests.rs

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14-
use rabbitmq_http_client::requests::VirtualHostParams;
15-
use rabbitmq_http_client::responses;
1614
use rabbitmq_http_client::api::Client;
15+
use rabbitmq_http_client::requests;
1716
use rabbitmq_http_client::requests::Permissions;
17+
use rabbitmq_http_client::requests::VirtualHostParams;
18+
use rabbitmq_http_client::responses;
1819

1920
mod test_helpers;
2021
use crate::test_helpers::{PASSWORD, USERNAME, endpoint};
@@ -180,7 +181,95 @@ async fn test_async_list_topic_permissions_of() {
180181
assert!(result1.is_ok());
181182

182183
let result = rc.list_topic_permissions_of("guest").await;
183-
assert!(result.is_ok(), "list_topic_permissions_of returned {result:?}");
184+
assert!(
185+
result.is_ok(),
186+
"list_topic_permissions_of returned {result:?}"
187+
);
188+
189+
rc.delete_vhost(vh_params.name, false).await.unwrap();
190+
}
191+
192+
#[tokio::test]
193+
async fn test_async_declare_topic_permissions() {
194+
let endpoint = endpoint();
195+
let rc = Client::new(&endpoint, USERNAME, PASSWORD);
196+
197+
let vh_params = VirtualHostParams::named("test_declare_topic_permissions");
198+
let _ = rc.delete_vhost(vh_params.name, false).await;
199+
let result1 = rc.create_vhost(&vh_params).await;
200+
assert!(result1.is_ok());
201+
202+
let params = requests::TopicPermissions {
203+
user: "guest",
204+
vhost: vh_params.name,
205+
exchange: "amq.topic",
206+
read: ".*",
207+
write: ".*",
208+
};
209+
let result = rc.declare_topic_permissions(&params).await;
210+
assert!(
211+
result.is_ok(),
212+
"declare_topic_permissions returned {result:?}"
213+
);
214+
215+
// Verify that the topic permissions are set
216+
let topic_permissions = rc.list_topic_permissions_of("guest").await.unwrap();
217+
assert!(topic_permissions.iter().any(|p| p.vhost == vh_params.name
218+
&& p.exchange == "amq.topic"
219+
&& p.read == ".*"
220+
&& p.write == ".*"));
221+
222+
rc.delete_vhost(vh_params.name, false).await.unwrap();
223+
}
224+
225+
#[tokio::test]
226+
async fn test_async_clear_topic_permissions() {
227+
let endpoint = endpoint();
228+
let rc = Client::new(&endpoint, USERNAME, PASSWORD);
229+
230+
let vh_params = VirtualHostParams::named("test_clear_topic_permissions");
231+
let _ = rc.delete_vhost(vh_params.name, false).await;
232+
let result1 = rc.create_vhost(&vh_params).await;
233+
assert!(result1.is_ok());
234+
235+
let params = requests::TopicPermissions {
236+
user: "guest",
237+
vhost: vh_params.name,
238+
exchange: "amq.topic",
239+
read: ".*",
240+
write: ".*",
241+
};
242+
let result = rc.declare_topic_permissions(&params).await;
243+
assert!(
244+
result.is_ok(),
245+
"declare_topic_permissions returned {result:?}"
246+
);
247+
248+
// Verify that the topic permissions are set
249+
let topic_permissions = rc.list_topic_permissions_of("guest").await.unwrap();
250+
assert!(topic_permissions.iter().any(|p| p.vhost == vh_params.name
251+
&& p.exchange == "amq.topic"
252+
&& p.read == ".*"
253+
&& p.write == ".*"));
254+
255+
let result2 = rc
256+
.clear_topic_permissions(vh_params.name, "guest", false)
257+
.await;
258+
assert!(
259+
result2.is_ok(),
260+
"clear_topic_permissions returned {result2:?}"
261+
);
262+
263+
// Verify that the topic permissions are cleared
264+
let topic_permissions_after_clear = rc.list_topic_permissions_of("guest").await.unwrap();
265+
assert!(
266+
!topic_permissions_after_clear
267+
.iter()
268+
.any(|p| p.vhost == vh_params.name
269+
&& p.exchange == "amq.topic"
270+
&& p.read == ".*"
271+
&& p.write == ".*")
272+
);
184273

185274
rc.delete_vhost(vh_params.name, false).await.unwrap();
186275
}

tests/blocking_permission_tests.rs

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14-
use rabbitmq_http_client::requests::VirtualHostParams;
15-
use rabbitmq_http_client::responses;
1614
use rabbitmq_http_client::blocking_api::Client;
15+
use rabbitmq_http_client::requests;
1716
use rabbitmq_http_client::requests::Permissions;
17+
use rabbitmq_http_client::requests::VirtualHostParams;
18+
use rabbitmq_http_client::responses;
1819

1920
mod test_helpers;
2021
use crate::test_helpers::{PASSWORD, USERNAME, endpoint};
@@ -136,7 +137,93 @@ fn test_blocking_list_topic_permissions_of() {
136137
assert!(result1.is_ok());
137138

138139
let result = rc.list_topic_permissions_of("guest");
139-
assert!(result.is_ok(), "list_topic_permissions_of returned {result:?}");
140+
assert!(
141+
result.is_ok(),
142+
"list_topic_permissions_of returned {result:?}"
143+
);
144+
145+
rc.delete_vhost(vh_params.name, false).unwrap();
146+
}
147+
148+
#[test]
149+
fn test_blocking_declare_topic_permissions() {
150+
let endpoint = endpoint();
151+
let rc = Client::new(&endpoint, USERNAME, PASSWORD);
152+
153+
let vh_params = VirtualHostParams::named("test_blocking_declare_topic_permissions");
154+
let _ = rc.delete_vhost(vh_params.name, false);
155+
let result1 = rc.create_vhost(&vh_params);
156+
assert!(result1.is_ok());
157+
158+
let params = requests::TopicPermissions {
159+
user: "guest",
160+
vhost: vh_params.name,
161+
exchange: "amq.topic",
162+
read: ".*",
163+
write: ".*",
164+
};
165+
let result = rc.declare_topic_permissions(&params);
166+
assert!(
167+
result.is_ok(),
168+
"declare_topic_permissions returned {result:?}"
169+
);
170+
171+
// Verify that the topic permissions are set
172+
let topic_permissions = rc.list_topic_permissions_of("guest").unwrap();
173+
assert!(topic_permissions.iter().any(|p| p.vhost == vh_params.name
174+
&& p.exchange == "amq.topic"
175+
&& p.read == ".*"
176+
&& p.write == ".*"));
177+
178+
rc.delete_vhost(vh_params.name, false).unwrap();
179+
}
180+
181+
#[test]
182+
fn test_blocking_clear_topic_permissions() {
183+
let endpoint = endpoint();
184+
let rc = Client::new(&endpoint, USERNAME, PASSWORD);
185+
186+
let vh_params = VirtualHostParams::named("test_blocking_clear_topic_permissions");
187+
let _ = rc.delete_vhost(vh_params.name, false);
188+
let result1 = rc.create_vhost(&vh_params);
189+
assert!(result1.is_ok());
190+
191+
let params = requests::TopicPermissions {
192+
user: "guest",
193+
vhost: vh_params.name,
194+
exchange: "amq.topic",
195+
read: ".*",
196+
write: ".*",
197+
};
198+
let result = rc.declare_topic_permissions(&params);
199+
assert!(
200+
result.is_ok(),
201+
"declare_topic_permissions returned {result:?}"
202+
);
203+
204+
// Verify that the topic permissions are set
205+
let topic_permissions = rc.list_topic_permissions_of("guest").unwrap();
206+
assert!(topic_permissions.iter().any(|p| p.vhost == vh_params.name
207+
&& p.exchange == "amq.topic"
208+
&& p.read == ".*"
209+
&& p.write == ".*"));
210+
211+
let result2 = rc.clear_topic_permissions(vh_params.name, "guest", false);
212+
assert!(
213+
result2.is_ok(),
214+
"clear_topic_permissions returned {result2:?}"
215+
);
216+
217+
// Verify that the topic permissions are cleared
218+
let topic_permissions_after_clear = rc.list_topic_permissions_of("guest").unwrap();
219+
assert!(
220+
!topic_permissions_after_clear
221+
.iter()
222+
.any(|p| p.vhost == vh_params.name
223+
&& p.exchange == "amq.topic"
224+
&& p.read == ".*"
225+
&& p.write == ".*")
226+
);
140227

141228
rc.delete_vhost(vh_params.name, false).unwrap();
142229
}

0 commit comments

Comments
 (0)