blob: ca1a5d22d25898bb97ce4684e1136c725fa9dd4a [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use iggy::prelude::{
GlobalPermissions as RustGlobalPermissions, Permissions as RustPermissions,
StreamPermissions as RustStreamPermissions, TopicPermissions as RustTopicPermissions,
};
use pyo3::prelude::*;
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};
use std::collections::BTreeMap;
/// The permissions of a user: global permissions applied to all streams,
/// optionally extended by per-stream permissions.
#[derive(Debug, Clone, PartialEq)]
#[gen_stub_pyclass]
#[pyclass(eq, from_py_object)]
pub struct Permissions {
pub(crate) inner: RustPermissions,
}
impl From<RustPermissions> for Permissions {
fn from(permissions: RustPermissions) -> Self {
Self { inner: permissions }
}
}
#[gen_stub_pymethods]
#[pymethods]
impl Permissions {
/// Create permissions from global permissions and optional per-stream permissions.
///
/// Args:
/// global_permissions: Global permissions as `GlobalPermissions | None`;
/// defaults to all denied.
/// streams: Per-stream permissions keyed by stream ID as
/// `dict[int, StreamPermissions] | None`; an empty dict is
/// treated as `None`.
#[new]
#[pyo3(signature = (global_permissions=None, streams=None))]
fn new(
#[gen_stub(override_type(type_repr = "GlobalPermissions | None"))]
global_permissions: Option<GlobalPermissions>,
#[gen_stub(override_type(type_repr = "dict[int, StreamPermissions] | None"))]
streams: Option<BTreeMap<u32, StreamPermissions>>,
) -> Self {
Self {
inner: RustPermissions {
global: global_permissions
.map(|global| global.inner)
.unwrap_or_default(),
// The wire format encodes an empty map the same as no map, so
// normalize here to keep round-trip equality.
streams: streams
.filter(|streams| !streams.is_empty())
.map(|streams| {
streams
.into_iter()
.map(|(stream_id, stream)| (stream_id as usize, stream.inner))
.collect()
}),
},
}
}
/// The global permissions, applied to all streams.
#[getter]
fn global_permissions(&self) -> GlobalPermissions {
GlobalPermissions {
inner: self.inner.global.clone(),
}
}
/// The per-stream permissions keyed by stream ID, or `None` when not set.
#[getter]
#[gen_stub(override_return_type(type_repr = "dict[int, StreamPermissions] | None"))]
fn streams(&self) -> Option<BTreeMap<u32, StreamPermissions>> {
self.inner.streams.as_ref().map(|streams| {
streams
.iter()
.map(|(stream_id, stream)| {
// IDs are u32 on the wire, so the cast cannot truncate.
(
*stream_id as u32,
StreamPermissions {
inner: stream.clone(),
},
)
})
.collect()
})
}
}
/// Global permissions, applied to all streams without specifying them one by one.
#[derive(Debug, Clone, PartialEq)]
#[gen_stub_pyclass]
#[pyclass(eq, from_py_object)]
pub struct GlobalPermissions {
pub(crate) inner: RustGlobalPermissions,
}
#[gen_stub_pymethods]
#[pymethods]
impl GlobalPermissions {
/// Create global permissions. Every flag defaults to `False`.
///
/// The `includes` notes below are transitive: a flag also grants everything
/// its included flags grant. For example `manage_streams` includes
/// `manage_topics`, and through it `read_topics`, `poll_messages`, and
/// `send_messages`.
///
/// Args:
/// manage_servers: Allow managing servers; includes `read_servers`.
/// read_servers: Allow reading server info (stats, clients).
/// manage_users: Allow managing users; includes `read_users`.
/// read_users: Allow reading user info.
/// manage_streams: Allow managing all streams; includes `read_streams`
/// and `manage_topics`.
/// read_streams: Allow reading all streams; includes `read_topics`.
/// manage_topics: Allow managing all topics; includes `read_topics`
/// and `send_messages`.
/// read_topics: Allow reading all topics and managing consumer groups
/// (including create and delete); includes `poll_messages`.
/// poll_messages: Allow polling messages from all streams and managing
/// consumer offsets.
/// send_messages: Allow sending messages to all streams.
#[new]
#[pyo3(signature = (
*,
manage_servers=false,
read_servers=false,
manage_users=false,
read_users=false,
manage_streams=false,
read_streams=false,
manage_topics=false,
read_topics=false,
poll_messages=false,
send_messages=false,
))]
#[allow(clippy::too_many_arguments)]
fn new(
manage_servers: bool,
read_servers: bool,
manage_users: bool,
read_users: bool,
manage_streams: bool,
read_streams: bool,
manage_topics: bool,
read_topics: bool,
poll_messages: bool,
send_messages: bool,
) -> Self {
Self {
inner: RustGlobalPermissions {
manage_servers,
read_servers,
manage_users,
read_users,
manage_streams,
read_streams,
manage_topics,
read_topics,
poll_messages,
send_messages,
},
}
}
/// Whether managing servers is allowed; includes `read_servers`.
#[getter]
fn manage_servers(&self) -> bool {
self.inner.manage_servers
}
/// Whether reading server info (stats, clients) is allowed.
#[getter]
fn read_servers(&self) -> bool {
self.inner.read_servers
}
/// Whether managing users is allowed; includes `read_users`.
#[getter]
fn manage_users(&self) -> bool {
self.inner.manage_users
}
/// Whether reading user info is allowed.
#[getter]
fn read_users(&self) -> bool {
self.inner.read_users
}
/// Whether managing all streams is allowed; includes `read_streams` and
/// `manage_topics`.
#[getter]
fn manage_streams(&self) -> bool {
self.inner.manage_streams
}
/// Whether reading all streams is allowed; includes `read_topics`.
#[getter]
fn read_streams(&self) -> bool {
self.inner.read_streams
}
/// Whether managing all topics is allowed; includes `read_topics` and
/// `send_messages`.
#[getter]
fn manage_topics(&self) -> bool {
self.inner.manage_topics
}
/// Whether reading all topics and managing consumer groups is allowed;
/// includes `poll_messages`.
#[getter]
fn read_topics(&self) -> bool {
self.inner.read_topics
}
/// Whether polling messages from all streams and managing consumer
/// offsets is allowed.
#[getter]
fn poll_messages(&self) -> bool {
self.inner.poll_messages
}
/// Whether sending messages to all streams is allowed.
#[getter]
fn send_messages(&self) -> bool {
self.inner.send_messages
}
}
/// Permissions for a specific stream and all its topics, optionally refined per topic.
/// They extend the global permissions, they do not override them.
#[derive(Debug, Clone, PartialEq)]
#[gen_stub_pyclass]
#[pyclass(eq, from_py_object)]
pub struct StreamPermissions {
pub(crate) inner: RustStreamPermissions,
}
#[gen_stub_pymethods]
#[pymethods]
impl StreamPermissions {
/// Create stream permissions. Every flag defaults to `False`.
///
/// The `includes` notes below are transitive: a flag also grants everything
/// its included flags grant. For example `manage_stream` includes
/// `manage_topics`, and through it `read_topics`, `poll_messages`, and
/// `send_messages`.
///
/// Args:
/// manage_stream: Allow managing the stream; includes `read_stream`
/// and `manage_topics`.
/// read_stream: Allow reading the stream; includes `read_topics`.
/// manage_topics: Allow managing the stream topics; includes
/// `read_topics` and `send_messages`.
/// read_topics: Allow reading the stream topics and managing their
/// consumer groups (including create and delete); includes
/// `poll_messages`.
/// poll_messages: Allow polling messages from the stream and managing
/// its consumer offsets.
/// send_messages: Allow sending messages to the stream.
/// topics: Per-topic permissions keyed by topic ID as
/// `dict[int, TopicPermissions] | None`; an empty dict is
/// treated as `None`.
#[new]
#[pyo3(signature = (
*,
manage_stream=false,
read_stream=false,
manage_topics=false,
read_topics=false,
poll_messages=false,
send_messages=false,
topics=None,
))]
fn new(
manage_stream: bool,
read_stream: bool,
manage_topics: bool,
read_topics: bool,
poll_messages: bool,
send_messages: bool,
#[gen_stub(override_type(type_repr = "dict[int, TopicPermissions] | None"))] topics: Option<
BTreeMap<u32, TopicPermissions>,
>,
) -> Self {
Self {
inner: RustStreamPermissions {
manage_stream,
read_stream,
manage_topics,
read_topics,
poll_messages,
send_messages,
// The wire format encodes an empty map the same as no map, so
// normalize here to keep round-trip equality.
topics: topics.filter(|topics| !topics.is_empty()).map(|topics| {
topics
.into_iter()
.map(|(topic_id, topic)| (topic_id as usize, topic.inner))
.collect()
}),
},
}
}
/// Whether managing the stream is allowed; includes `read_stream` and
/// `manage_topics`.
#[getter]
fn manage_stream(&self) -> bool {
self.inner.manage_stream
}
/// Whether reading the stream is allowed; includes `read_topics`.
#[getter]
fn read_stream(&self) -> bool {
self.inner.read_stream
}
/// Whether managing the stream topics is allowed; includes `read_topics`
/// and `send_messages`.
#[getter]
fn manage_topics(&self) -> bool {
self.inner.manage_topics
}
/// Whether reading the stream topics and managing their consumer groups
/// is allowed; includes `poll_messages`.
#[getter]
fn read_topics(&self) -> bool {
self.inner.read_topics
}
/// Whether polling messages from the stream and managing its consumer
/// offsets is allowed.
#[getter]
fn poll_messages(&self) -> bool {
self.inner.poll_messages
}
/// Whether sending messages to the stream is allowed.
#[getter]
fn send_messages(&self) -> bool {
self.inner.send_messages
}
/// The per-topic permissions keyed by topic ID, or `None` when not set.
#[getter]
#[gen_stub(override_return_type(type_repr = "dict[int, TopicPermissions] | None"))]
fn topics(&self) -> Option<BTreeMap<u32, TopicPermissions>> {
self.inner.topics.as_ref().map(|topics| {
topics
.iter()
.map(|(topic_id, topic)| {
// IDs are u32 on the wire, so the cast cannot truncate.
(
*topic_id as u32,
TopicPermissions {
inner: topic.clone(),
},
)
})
.collect()
})
}
}
/// Permissions for a specific topic of a stream. The lowest level of permissions.
/// They extend the stream and global permissions, they do not override them.
#[derive(Debug, Clone, PartialEq)]
#[gen_stub_pyclass]
#[pyclass(eq, from_py_object)]
pub struct TopicPermissions {
pub(crate) inner: RustTopicPermissions,
}
#[gen_stub_pymethods]
#[pymethods]
impl TopicPermissions {
/// Create topic permissions. Every flag defaults to `False`.
///
/// The `includes` notes below are transitive: a flag also grants everything
/// its included flags grant. For example `manage_topic` includes
/// `read_topic` and `send_messages`, and through `read_topic` also
/// `poll_messages`.
///
/// Args:
/// manage_topic: Allow managing the topic; includes `read_topic` and
/// `send_messages`.
/// read_topic: Allow reading the topic and managing its consumer
/// groups (including create and delete); includes `poll_messages`.
/// poll_messages: Allow polling messages from the topic and managing
/// its consumer offsets.
/// send_messages: Allow sending messages to the topic.
#[new]
#[pyo3(signature = (
*,
manage_topic=false,
read_topic=false,
poll_messages=false,
send_messages=false,
))]
fn new(manage_topic: bool, read_topic: bool, poll_messages: bool, send_messages: bool) -> Self {
Self {
inner: RustTopicPermissions {
manage_topic,
read_topic,
poll_messages,
send_messages,
},
}
}
/// Whether managing the topic is allowed; includes `read_topic` and
/// `send_messages`.
#[getter]
fn manage_topic(&self) -> bool {
self.inner.manage_topic
}
/// Whether reading the topic and managing its consumer groups is allowed;
/// includes `poll_messages`.
#[getter]
fn read_topic(&self) -> bool {
self.inner.read_topic
}
/// Whether polling messages from the topic and managing its consumer
/// offsets is allowed.
#[getter]
fn poll_messages(&self) -> bool {
self.inner.poll_messages
}
/// Whether sending messages to the topic is allowed.
#[getter]
fn send_messages(&self) -> bool {
self.inner.send_messages
}
}