| // 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 |
| } |
| } |