1use std::collections::BTreeMap;
13use std::fmt;
14use std::mem::size_of;
15use std::ops::BitOrAssign;
16use std::str::FromStr;
17
18use crate::adt::system::Oid;
19use anyhow::{Error, anyhow};
20use bitflags::bitflags;
21use columnation::{Columnation, CopyRegion};
22use mz_ore::soft_assert_no_log;
23use mz_ore::str::StrExt;
24use mz_persist_types::columnar::FixedSizeCodec;
25use mz_proto::{RustType, TryFromProtoError};
26use proptest::arbitrary::Arbitrary;
27use proptest::prelude::*;
28use proptest::strategy::{BoxedStrategy, Strategy};
29use proptest_derive::Arbitrary;
30use serde::{Deserialize, Serialize};
31
32use crate::role_id::RoleId;
33
34include!(concat!(env!("OUT_DIR"), "/mz_repr.adt.mz_acl_item.rs"));
35
36const INSERT_CHAR: char = 'a';
38const SELECT_CHAR: char = 'r';
40const UPDATE_CHAR: char = 'w';
42const DELETE_CHAR: char = 'd';
44const USAGE_CHAR: char = 'U';
46const CREATE_CHAR: char = 'C';
48const CREATE_ROLE_CHAR: char = 'R';
50const CREATE_DB_CHAR: char = 'B';
52const CREATE_CLUSTER_CHAR: char = 'N';
54const CREATE_NETWORK_POLICY_CHAR: char = 'P';
56
57const INSERT_STR: &str = "INSERT";
58const SELECT_STR: &str = "SELECT";
59const UPDATE_STR: &str = "UPDATE";
60const DELETE_STR: &str = "DELETE";
61const USAGE_STR: &str = "USAGE";
62const CREATE_STR: &str = "CREATE";
63const CREATE_ROLE_STR: &str = "CREATEROLE";
64const CREATE_DB_STR: &str = "CREATEDB";
65const CREATE_CLUSTER_STR: &str = "CREATECLUSTER";
66const CREATE_NETWORK_POLICY_STR: &str = "CREATENETWORKPOLICY";
67
68pub const PUBLIC_ROLE_OID: Oid = Oid(0);
71
72bitflags! {
73 #[derive(Serialize, Deserialize)]
87 pub struct AclMode: u64 {
88 const INSERT = 1 << 0;
90 const SELECT = 1 << 1;
91 const UPDATE = 1 << 2;
92 const DELETE = 1 << 3;
93 const USAGE = 1 << 8;
94 const CREATE = 1 << 9;
95
96 const CREATE_CLUSTER = 1 << 29;
98 const CREATE_DB = 1 << 30;
99 const CREATE_ROLE = 1 << 31;
100 const CREATE_NETWORK_POLICY = 1 << 32;
101
102 }
105}
106
107impl AclMode {
108 pub fn parse_single_privilege(s: &str) -> Result<Self, Error> {
109 match s.trim().to_uppercase().as_str() {
110 INSERT_STR => Ok(AclMode::INSERT),
111 SELECT_STR => Ok(AclMode::SELECT),
112 UPDATE_STR => Ok(AclMode::UPDATE),
113 DELETE_STR => Ok(AclMode::DELETE),
114 USAGE_STR => Ok(AclMode::USAGE),
115 CREATE_STR => Ok(AclMode::CREATE),
116 CREATE_ROLE_STR => Ok(AclMode::CREATE_ROLE),
117 CREATE_DB_STR => Ok(AclMode::CREATE_DB),
118 CREATE_CLUSTER_STR => Ok(AclMode::CREATE_CLUSTER),
119 CREATE_NETWORK_POLICY_STR => Ok(AclMode::CREATE_NETWORK_POLICY),
120 _ => Err(anyhow!("{}", s.quoted())),
121 }
122 }
123
124 pub fn parse_multiple_privileges(s: &str) -> Result<Self, Error> {
125 let mut acl_mode = AclMode::empty();
126 for privilege in s.split(',') {
127 let privilege = AclMode::parse_single_privilege(privilege)?;
128 acl_mode.bitor_assign(privilege);
129 }
130 Ok(acl_mode)
131 }
132
133 pub fn to_error_string(&self) -> String {
134 self.explode().join(", ")
135 }
136
137 pub fn explode(&self) -> Vec<&'static str> {
138 let mut privileges = Vec::new();
139 if self.contains(AclMode::SELECT) {
140 privileges.push(SELECT_STR);
141 }
142 if self.contains(AclMode::INSERT) {
143 privileges.push(INSERT_STR);
144 }
145 if self.contains(AclMode::UPDATE) {
146 privileges.push(UPDATE_STR);
147 }
148 if self.contains(AclMode::DELETE) {
149 privileges.push(DELETE_STR);
150 }
151 if self.contains(AclMode::USAGE) {
152 privileges.push(USAGE_STR);
153 }
154 if self.contains(AclMode::CREATE) {
155 privileges.push(CREATE_STR);
156 }
157 if self.contains(AclMode::CREATE_ROLE) {
158 privileges.push(CREATE_ROLE_STR);
159 }
160 if self.contains(AclMode::CREATE_DB) {
161 privileges.push(CREATE_DB_STR);
162 }
163 if self.contains(AclMode::CREATE_CLUSTER) {
164 privileges.push(CREATE_CLUSTER_STR);
165 }
166 if self.contains(AclMode::CREATE_NETWORK_POLICY) {
167 privileges.push(CREATE_NETWORK_POLICY_STR);
168 }
169 privileges
170 }
171}
172
173impl FromStr for AclMode {
174 type Err = Error;
175
176 fn from_str(s: &str) -> Result<Self, Self::Err> {
177 let mut acl_mode = AclMode::empty();
178 for c in s.chars() {
179 match c {
180 INSERT_CHAR => acl_mode.bitor_assign(AclMode::INSERT),
181 SELECT_CHAR => acl_mode.bitor_assign(AclMode::SELECT),
182 UPDATE_CHAR => acl_mode.bitor_assign(AclMode::UPDATE),
183 DELETE_CHAR => acl_mode.bitor_assign(AclMode::DELETE),
184 USAGE_CHAR => acl_mode.bitor_assign(AclMode::USAGE),
185 CREATE_CHAR => acl_mode.bitor_assign(AclMode::CREATE),
186 CREATE_ROLE_CHAR => acl_mode.bitor_assign(AclMode::CREATE_ROLE),
187 CREATE_DB_CHAR => acl_mode.bitor_assign(AclMode::CREATE_DB),
188 CREATE_CLUSTER_CHAR => acl_mode.bitor_assign(AclMode::CREATE_CLUSTER),
189 CREATE_NETWORK_POLICY_CHAR => acl_mode.bitor_assign(AclMode::CREATE_NETWORK_POLICY),
190 _ => return Err(anyhow!("invalid privilege '{c}' in acl mode '{s}'")),
191 }
192 }
193 Ok(acl_mode)
194 }
195}
196
197impl fmt::Display for AclMode {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 if self.contains(AclMode::INSERT) {
202 write!(f, "{INSERT_CHAR}")?;
203 }
204 if self.contains(AclMode::SELECT) {
205 write!(f, "{SELECT_CHAR}")?;
206 }
207 if self.contains(AclMode::UPDATE) {
208 write!(f, "{UPDATE_CHAR}")?;
209 }
210 if self.contains(AclMode::DELETE) {
211 write!(f, "{DELETE_CHAR}")?;
212 }
213 if self.contains(AclMode::USAGE) {
214 write!(f, "{USAGE_CHAR}")?;
215 }
216 if self.contains(AclMode::CREATE) {
217 write!(f, "{CREATE_CHAR}")?;
218 }
219 if self.contains(AclMode::CREATE_ROLE) {
220 write!(f, "{CREATE_ROLE_CHAR}")?;
221 }
222 if self.contains(AclMode::CREATE_DB) {
223 write!(f, "{CREATE_DB_CHAR}")?;
224 }
225 if self.contains(AclMode::CREATE_CLUSTER) {
226 write!(f, "{CREATE_CLUSTER_CHAR}")?;
227 }
228 if self.contains(AclMode::CREATE_NETWORK_POLICY) {
229 write!(f, "{CREATE_NETWORK_POLICY_CHAR}")?;
230 }
231 Ok(())
232 }
233}
234
235impl RustType<ProtoAclMode> for AclMode {
236 fn into_proto(&self) -> ProtoAclMode {
237 ProtoAclMode {
238 acl_mode: self.bits,
239 }
240 }
241
242 fn from_proto(proto: ProtoAclMode) -> Result<Self, TryFromProtoError> {
243 Ok(AclMode {
244 bits: proto.acl_mode,
245 })
246 }
247}
248
249impl Columnation for AclMode {
250 type InnerRegion = CopyRegion<AclMode>;
251}
252
253impl Arbitrary for AclMode {
254 type Parameters = ();
255 type Strategy = BoxedStrategy<AclMode>;
256
257 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
258 proptest::bits::BitSetStrategy::masked(AclMode::all().bits)
259 .prop_map(|bits| AclMode::from_bits(bits).expect("invalid proptest implementation"))
260 .boxed()
261 }
262}
263
264#[derive(
271 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Hash, Deserialize, Arbitrary,
272)]
273pub struct MzAclItem {
274 pub grantee: RoleId,
276 pub grantor: RoleId,
278 pub acl_mode: AclMode,
280}
281
282impl MzAclItem {
283 pub fn empty(grantee: RoleId, grantor: RoleId) -> MzAclItem {
284 MzAclItem {
285 grantee,
286 grantor,
287 acl_mode: AclMode::empty(),
288 }
289 }
290
291 pub fn encode_binary(&self) -> Vec<u8> {
292 let mut res = Vec::with_capacity(Self::binary_size());
293 res.extend_from_slice(&self.grantee.encode_binary());
294 res.extend_from_slice(&self.grantor.encode_binary());
295 res.extend_from_slice(&self.acl_mode.bits().to_le_bytes());
296 res
297 }
298
299 pub fn decode_binary(raw: &[u8]) -> Result<MzAclItem, Error> {
300 if raw.len() != MzAclItem::binary_size() {
301 return Err(anyhow!(
302 "invalid binary size, expecting {}, found {}",
303 MzAclItem::binary_size(),
304 raw.len()
305 ));
306 }
307
308 let role_id_size = RoleId::binary_size();
309
310 let grantee = RoleId::decode_binary(&raw[0..role_id_size])?;
311 let raw = &raw[role_id_size..];
312 let grantor = RoleId::decode_binary(&raw[0..role_id_size])?;
313 let raw = &raw[role_id_size..];
314 let acl_mode = u64::from_le_bytes(raw.try_into()?);
315
316 Ok(MzAclItem {
317 grantee,
318 grantor,
319 acl_mode: AclMode { bits: acl_mode },
320 })
321 }
322
323 pub const fn binary_size() -> usize {
324 RoleId::binary_size() + RoleId::binary_size() + size_of::<u64>()
325 }
326}
327
328impl FromStr for MzAclItem {
329 type Err = Error;
330
331 fn from_str(s: &str) -> Result<Self, Self::Err> {
332 let parts: Vec<_> = s.split('=').collect();
333 let &[grantee, rest] = parts.as_slice() else {
334 return Err(anyhow!("invalid mz_aclitem '{s}'"));
335 };
336
337 let parts: Vec<_> = rest.split('/').collect();
338 let &[acl_mode, grantor] = parts.as_slice() else {
339 return Err(anyhow!("invalid mz_aclitem '{s}'"));
340 };
341
342 let grantee: RoleId = if grantee.is_empty() {
343 RoleId::Public
344 } else {
345 grantee.parse()?
346 };
347 let acl_mode: AclMode = acl_mode.parse()?;
348 let grantor: RoleId = grantor.parse()?;
349
350 Ok(MzAclItem {
351 grantee,
352 grantor,
353 acl_mode,
354 })
355 }
356}
357
358impl fmt::Display for MzAclItem {
359 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
360 if !self.grantee.is_public() {
361 write!(f, "{}", self.grantee)?;
362 }
363 write!(f, "={}/{}", self.acl_mode, self.grantor)
364 }
365}
366
367impl RustType<ProtoMzAclItem> for MzAclItem {
368 fn into_proto(&self) -> ProtoMzAclItem {
369 ProtoMzAclItem {
370 grantee: Some(self.grantee.into_proto()),
371 grantor: Some(self.grantor.into_proto()),
372 acl_mode: Some(self.acl_mode.into_proto()),
373 }
374 }
375
376 fn from_proto(proto: ProtoMzAclItem) -> Result<Self, TryFromProtoError> {
377 match (proto.grantee, proto.grantor, proto.acl_mode) {
378 (Some(grantee), Some(grantor), Some(acl_mode)) => Ok(MzAclItem {
379 grantee: RoleId::from_proto(grantee)?,
380 grantor: RoleId::from_proto(grantor)?,
381 acl_mode: AclMode::from_proto(acl_mode)?,
382 }),
383 (None, _, _) => Err(TryFromProtoError::missing_field("ProtoMzAclItem::grantee")),
384 (_, None, _) => Err(TryFromProtoError::missing_field("ProtoMzAclItem::grantor")),
385 (_, _, None) => Err(TryFromProtoError::missing_field("ProtoMzAclItem::acl_mode")),
386 }
387 }
388}
389
390impl Columnation for MzAclItem {
391 type InnerRegion = CopyRegion<MzAclItem>;
392}
393
394#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
398pub struct PackedMzAclItem([u8; Self::SIZE]);
399
400impl PackedMzAclItem {
401 pub const SYSTEM_TAG: u32 = 100;
410 pub const PREDEFINED_TAG: u32 = 200;
411 pub const USER_TAG: u32 = 300;
412 pub const PUBLIC_TAG: u32 = 400;
413
414 #[inline]
415 fn encode_role(buf: &mut [u8], role: RoleId) {
416 soft_assert_no_log!(buf.len() == 12);
417
418 match role {
419 RoleId::System(val) => {
420 buf[..4].copy_from_slice(&Self::SYSTEM_TAG.to_be_bytes());
421 buf[4..].copy_from_slice(&val.to_be_bytes());
422 }
423 RoleId::Predefined(val) => {
424 buf[..4].copy_from_slice(&Self::PREDEFINED_TAG.to_be_bytes());
425 buf[4..].copy_from_slice(&val.to_be_bytes());
426 }
427 RoleId::User(val) => {
428 buf[..4].copy_from_slice(&Self::USER_TAG.to_be_bytes());
429 buf[4..].copy_from_slice(&val.to_be_bytes());
430 }
431 RoleId::Public => {
432 buf[..4].copy_from_slice(&Self::PUBLIC_TAG.to_be_bytes());
433 }
434 }
435 }
436
437 #[inline]
438 fn decode_role(buf: &[u8]) -> RoleId {
439 soft_assert_no_log!(buf.len() == 12);
440
441 let tag: [u8; 4] = buf[..4]
442 .try_into()
443 .expect("PackedMzAclItem should roundtrip");
444 let tag = u32::from_be_bytes(tag);
445
446 let val: [u8; 8] = buf[4..]
447 .try_into()
448 .expect("PackedMzAclItem should roundtrip");
449 let val = u64::from_be_bytes(val);
450
451 match tag {
452 Self::SYSTEM_TAG => RoleId::System(val),
453 Self::PREDEFINED_TAG => RoleId::Predefined(val),
454 Self::USER_TAG => RoleId::User(val),
455 Self::PUBLIC_TAG => RoleId::Public,
456 x => panic!("unrecognized tag {x}"),
457 }
458 }
459}
460
461impl FixedSizeCodec<MzAclItem> for PackedMzAclItem {
462 const SIZE: usize = 32;
463
464 fn as_bytes(&self) -> &[u8] {
465 &self.0
466 }
467
468 fn from_bytes(val: &[u8]) -> Result<Self, String>
469 where
470 Self: Sized,
471 {
472 let buf: [u8; Self::SIZE] = val.try_into().map_err(|_| {
473 format!(
474 "size for PackedMzAclItem is {} bytes, got {}",
475 Self::SIZE,
476 val.len()
477 )
478 })?;
479
480 Ok(PackedMzAclItem(buf))
481 }
482
483 #[inline]
484 fn from_value(value: MzAclItem) -> Self {
485 let mut buf = [0u8; 32];
486
487 Self::encode_role(&mut buf[..12], value.grantee);
488 Self::encode_role(&mut buf[12..24], value.grantor);
489 buf[24..].copy_from_slice(&value.acl_mode.bits().to_be_bytes());
490
491 PackedMzAclItem(buf)
492 }
493
494 #[inline]
495 fn into_value(self) -> MzAclItem {
496 let grantee = PackedMzAclItem::decode_role(&self.0[..12]);
497 let grantor = PackedMzAclItem::decode_role(&self.0[12..24]);
498
499 let acl_mode: [u8; 8] = self.0[24..]
500 .try_into()
501 .expect("PackedMzAclItem should roundtrip");
502 let acl_mode = AclMode::from_bits(u64::from_be_bytes(acl_mode))
503 .expect("PackedMzAclItem should roundtrip");
504
505 MzAclItem {
506 grantee,
507 grantor,
508 acl_mode,
509 }
510 }
511}
512
513#[derive(
519 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Hash, Deserialize, Arbitrary,
520)]
521pub struct AclItem {
522 pub grantee: Oid,
524 pub grantor: Oid,
526 pub acl_mode: AclMode,
528}
529
530impl AclItem {
531 pub fn empty(grantee: Oid, grantor: Oid) -> AclItem {
532 AclItem {
533 grantee,
534 grantor,
535 acl_mode: AclMode::empty(),
536 }
537 }
538}
539
540impl AclItem {
544 pub fn encode_binary(&self) -> Vec<u8> {
545 let mut res = Vec::with_capacity(Self::binary_size());
546 res.extend_from_slice(&self.grantee.0.to_le_bytes());
547 res.extend_from_slice(&self.grantor.0.to_le_bytes());
548 res.extend_from_slice(&self.acl_mode.bits().to_le_bytes());
549 res
550 }
551
552 pub fn decode_binary(raw: &[u8]) -> Result<AclItem, Error> {
553 if raw.len() != AclItem::binary_size() {
554 return Err(anyhow!(
555 "invalid binary size, expecting {}, found {}",
556 AclItem::binary_size(),
557 raw.len()
558 ));
559 }
560
561 let oid_size = size_of::<u32>();
562
563 let grantee = Oid(u32::from_le_bytes(raw[0..oid_size].try_into()?));
564 let raw = &raw[oid_size..];
565 let grantor = Oid(u32::from_le_bytes(raw[0..oid_size].try_into()?));
566 let raw = &raw[oid_size..];
567 let acl_mode = u64::from_le_bytes(raw.try_into()?);
568
569 Ok(AclItem {
570 grantee,
571 grantor,
572 acl_mode: AclMode { bits: acl_mode },
573 })
574 }
575
576 pub const fn binary_size() -> usize {
577 size_of::<u32>() + size_of::<u32>() + size_of::<u64>()
578 }
579}
580
581impl FromStr for AclItem {
582 type Err = Error;
583
584 fn from_str(s: &str) -> Result<Self, Self::Err> {
593 let parts: Vec<_> = s.split('=').collect();
594 let &[grantee, rest] = parts.as_slice() else {
595 return Err(anyhow!("invalid aclitem '{s}'"));
596 };
597
598 let parts: Vec<_> = rest.split('/').collect();
599 let &[acl_mode, grantor] = parts.as_slice() else {
600 return Err(anyhow!("invalid mz_aclitem '{s}'"));
601 };
602
603 let grantee: Oid = if grantee.is_empty() {
604 PUBLIC_ROLE_OID
605 } else {
606 Oid(grantee.parse()?)
607 };
608 let acl_mode: AclMode = acl_mode.parse()?;
609 let grantor: Oid = Oid(grantor.parse()?);
610
611 Ok(AclItem {
612 grantee,
613 grantor,
614 acl_mode,
615 })
616 }
617}
618
619impl fmt::Display for AclItem {
620 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
621 if self.grantee != PUBLIC_ROLE_OID {
622 write!(f, "{}", self.grantee.0)?;
623 }
624 write!(f, "={}/{}", self.acl_mode, self.grantor.0)
625 }
626}
627
628impl RustType<ProtoAclItem> for AclItem {
629 fn into_proto(&self) -> ProtoAclItem {
630 ProtoAclItem {
631 grantee: self.grantee.0,
632 grantor: self.grantor.0,
633 acl_mode: Some(self.acl_mode.into_proto()),
634 }
635 }
636
637 fn from_proto(proto: ProtoAclItem) -> Result<Self, TryFromProtoError> {
638 match proto.acl_mode {
639 Some(acl_mode) => Ok(AclItem {
640 grantee: Oid(proto.grantee),
641 grantor: Oid(proto.grantor),
642 acl_mode: AclMode::from_proto(acl_mode)?,
643 }),
644 None => Err(TryFromProtoError::missing_field("ProtoMzAclItem::acl_mode")),
645 }
646 }
647}
648
649impl Columnation for AclItem {
650 type InnerRegion = CopyRegion<AclItem>;
651}
652
653#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
657pub struct PackedAclItem([u8; Self::SIZE]);
658
659impl FixedSizeCodec<AclItem> for PackedAclItem {
660 const SIZE: usize = 16;
661
662 fn as_bytes(&self) -> &[u8] {
663 &self.0
664 }
665
666 fn from_bytes(slice: &[u8]) -> Result<Self, String> {
667 let buf: [u8; Self::SIZE] = slice.try_into().map_err(|_| {
668 format!(
669 "size for PackedAclItem is {} bytes, got {}",
670 Self::SIZE,
671 slice.len()
672 )
673 })?;
674 Ok(PackedAclItem(buf))
675 }
676
677 #[inline]
678 fn from_value(value: AclItem) -> Self {
679 let mut buf = [0u8; 16];
680
681 buf[..4].copy_from_slice(&value.grantee.0.to_be_bytes());
682 buf[4..8].copy_from_slice(&value.grantor.0.to_be_bytes());
683 buf[8..].copy_from_slice(&value.acl_mode.bits().to_be_bytes());
684
685 PackedAclItem(buf)
686 }
687
688 #[inline]
689 fn into_value(self) -> AclItem {
690 let mut grantee = [0; 4];
691 grantee.copy_from_slice(&self.0[..4]);
692
693 let mut grantor = [0; 4];
694 grantor.copy_from_slice(&self.0[4..8]);
695
696 let mut acl_mode = [0; 8];
697 acl_mode.copy_from_slice(&self.0[8..]);
698 let acl_mode = AclMode::from_bits(u64::from_be_bytes(acl_mode))
699 .expect("PackedAclItem should roundtrip");
700
701 AclItem {
702 grantee: Oid(u32::from_be_bytes(grantee)),
703 grantor: Oid(u32::from_be_bytes(grantor)),
704 acl_mode,
705 }
706 }
707}
708
709#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
711pub struct PrivilegeMap(
712 #[serde(serialize_with = "mz_ore::serde::map_key_to_string")] BTreeMap<RoleId, Vec<MzAclItem>>,
713);
714
715impl PrivilegeMap {
716 pub fn new() -> PrivilegeMap {
718 PrivilegeMap(BTreeMap::new())
719 }
720
721 pub fn from_mz_acl_items(items: impl IntoIterator<Item = MzAclItem>) -> PrivilegeMap {
723 let mut map = PrivilegeMap::new();
724 map.grant_all(items);
725 map
726 }
727
728 pub fn get_acl_item(&self, grantee: &RoleId, grantor: &RoleId) -> Option<&MzAclItem> {
730 self.0.get(grantee).and_then(|privileges| {
731 privileges
732 .into_iter()
733 .find(|mz_acl_item| &mz_acl_item.grantor == grantor)
734 })
735 }
736
737 pub fn get_acl_items_for_grantee(&self, grantee: &RoleId) -> impl Iterator<Item = &MzAclItem> {
739 self.0
740 .get(grantee)
741 .into_iter()
742 .flat_map(|privileges| privileges.into_iter())
743 }
744
745 pub fn all_values(&self) -> impl Iterator<Item = &MzAclItem> {
747 self.0
748 .values()
749 .flat_map(|privileges| privileges.into_iter())
750 }
751
752 pub fn all_values_owned(&self) -> impl Iterator<Item = MzAclItem> + '_ {
754 self.all_values().cloned()
755 }
756
757 pub fn into_all_values(self) -> impl Iterator<Item = MzAclItem> {
759 self.0
760 .into_values()
761 .flat_map(|privileges| privileges.into_iter())
762 }
763
764 pub fn grant(&mut self, privilege: MzAclItem) {
766 let grantee_privileges = self.0.entry(privilege.grantee).or_default();
767 if let Some(existing_privilege) = grantee_privileges
768 .iter_mut()
769 .find(|cur_privilege| cur_privilege.grantor == privilege.grantor)
770 {
771 assert_eq!(
773 privilege.grantee, existing_privilege.grantee,
774 "PrivilegeMap out of sync"
775 );
776 existing_privilege.acl_mode = existing_privilege.acl_mode.union(privilege.acl_mode);
777 } else {
778 grantee_privileges.push(privilege);
779 }
780 }
781
782 pub fn grant_all(&mut self, mz_acl_items: impl IntoIterator<Item = MzAclItem>) {
784 for mz_acl_item in mz_acl_items {
785 self.grant(mz_acl_item);
786 }
787 }
788
789 pub fn revoke(&mut self, privilege: &MzAclItem) {
791 let grantee_privileges = self.0.entry(privilege.grantee).or_default();
792 if let Some(existing_privilege) = grantee_privileges
793 .iter_mut()
794 .find(|cur_privilege| cur_privilege.grantor == privilege.grantor)
795 {
796 assert_eq!(
798 privilege.grantee, existing_privilege.grantee,
799 "PrivilegeMap out of sync"
800 );
801 existing_privilege.acl_mode =
802 existing_privilege.acl_mode.difference(privilege.acl_mode);
803 }
804
805 grantee_privileges.retain(|privilege| !privilege.acl_mode.is_empty());
807 if grantee_privileges.is_empty() {
808 self.0.remove(&privilege.grantee);
809 }
810 }
811
812 pub fn debug_json(&self) -> serde_json::Value {
815 let privileges_by_str: BTreeMap<String, _> = self
816 .0
817 .iter()
818 .map(|(key, value)| (key.to_string(), value))
819 .collect();
820 serde_json::json!(privileges_by_str)
821 }
822}
823
824impl Default for PrivilegeMap {
825 fn default() -> PrivilegeMap {
826 PrivilegeMap::new()
827 }
828}
829
830pub fn merge_mz_acl_items(
832 mz_acl_items: impl Iterator<Item = MzAclItem>,
833) -> impl Iterator<Item = MzAclItem> {
834 mz_acl_items
835 .fold(BTreeMap::new(), |mut accum, mz_acl_item| {
836 let item = accum
837 .entry((mz_acl_item.grantee, mz_acl_item.grantor))
838 .or_insert(MzAclItem::empty(mz_acl_item.grantee, mz_acl_item.grantor));
839 item.acl_mode |= mz_acl_item.acl_mode;
840 accum
841 })
842 .into_values()
843}
844
845#[mz_ore::test]
846fn test_mz_acl_parsing() {
847 let s = "u42=rw/s666";
848 let mz_acl: MzAclItem = s.parse().unwrap();
849 assert_eq!(RoleId::User(42), mz_acl.grantee);
850 assert_eq!(RoleId::System(666), mz_acl.grantor);
851 assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
852 assert!(mz_acl.acl_mode.contains(AclMode::SELECT));
853 assert!(mz_acl.acl_mode.contains(AclMode::UPDATE));
854 assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
855 assert!(!mz_acl.acl_mode.contains(AclMode::USAGE));
856 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE));
857 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
858 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_DB));
859 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
860 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
861 assert_eq!(s, mz_acl.to_string());
862
863 let s = "=UC/u4";
864 let mz_acl: MzAclItem = s.parse().unwrap();
865 assert_eq!(RoleId::Public, mz_acl.grantee);
866 assert_eq!(RoleId::User(4), mz_acl.grantor);
867 assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
868 assert!(!mz_acl.acl_mode.contains(AclMode::SELECT));
869 assert!(!mz_acl.acl_mode.contains(AclMode::UPDATE));
870 assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
871 assert!(mz_acl.acl_mode.contains(AclMode::USAGE));
872 assert!(mz_acl.acl_mode.contains(AclMode::CREATE));
873 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
874 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_DB));
875 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
876 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
877 assert_eq!(s, mz_acl.to_string());
878
879 let s = "s7=/s12";
880 let mz_acl: MzAclItem = s.parse().unwrap();
881 assert_eq!(RoleId::System(7), mz_acl.grantee);
882 assert_eq!(RoleId::System(12), mz_acl.grantor);
883 assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
884 assert!(!mz_acl.acl_mode.contains(AclMode::SELECT));
885 assert!(!mz_acl.acl_mode.contains(AclMode::UPDATE));
886 assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
887 assert!(!mz_acl.acl_mode.contains(AclMode::USAGE));
888 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE));
889 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
890 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_DB));
891 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
892 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
893 assert_eq!(s, mz_acl.to_string());
894
895 let s = "=/u100";
896 let mz_acl: MzAclItem = s.parse().unwrap();
897 assert_eq!(RoleId::Public, mz_acl.grantee);
898 assert_eq!(RoleId::User(100), mz_acl.grantor);
899 assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
900 assert!(!mz_acl.acl_mode.contains(AclMode::SELECT));
901 assert!(!mz_acl.acl_mode.contains(AclMode::UPDATE));
902 assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
903 assert!(!mz_acl.acl_mode.contains(AclMode::USAGE));
904 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE));
905 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
906 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_DB));
907 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
908 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
909 assert_eq!(s, mz_acl.to_string());
910
911 let s = "u1=RBNP/u2";
912 let mz_acl: MzAclItem = s.parse().unwrap();
913 assert_eq!(RoleId::User(1), mz_acl.grantee);
914 assert_eq!(RoleId::User(2), mz_acl.grantor);
915 assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
916 assert!(!mz_acl.acl_mode.contains(AclMode::SELECT));
917 assert!(!mz_acl.acl_mode.contains(AclMode::UPDATE));
918 assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
919 assert!(!mz_acl.acl_mode.contains(AclMode::USAGE));
920 assert!(!mz_acl.acl_mode.contains(AclMode::CREATE));
921 assert!(mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
922 assert!(mz_acl.acl_mode.contains(AclMode::CREATE_DB));
923 assert!(mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
924 assert!(mz_acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
925 assert_eq!(s, mz_acl.to_string());
926
927 mz_ore::assert_err!("u42/rw=u666".parse::<MzAclItem>());
928 mz_ore::assert_err!("u32=C/".parse::<MzAclItem>());
929 mz_ore::assert_err!("=/".parse::<MzAclItem>());
930 mz_ore::assert_err!("f62hfiuew827fhh".parse::<MzAclItem>());
931 mz_ore::assert_err!("u2=rw/s66=CU/u33".parse::<MzAclItem>());
932}
933
934#[mz_ore::test]
935fn test_mz_acl_item_binary() {
936 use std::ops::BitAnd;
937
938 let mz_acl_item = MzAclItem {
939 grantee: RoleId::User(42),
940 grantor: RoleId::System(666),
941 acl_mode: AclMode::empty()
942 .bitand(AclMode::SELECT)
943 .bitand(AclMode::UPDATE),
944 };
945 assert_eq!(
946 mz_acl_item,
947 MzAclItem::decode_binary(&mz_acl_item.encode_binary()).unwrap()
948 );
949
950 let mz_acl_item = MzAclItem {
951 grantee: RoleId::Public,
952 grantor: RoleId::User(4),
953 acl_mode: AclMode::empty()
954 .bitand(AclMode::USAGE)
955 .bitand(AclMode::CREATE),
956 };
957 assert_eq!(
958 mz_acl_item,
959 MzAclItem::decode_binary(&mz_acl_item.encode_binary()).unwrap()
960 );
961
962 let mz_acl_item = MzAclItem {
963 grantee: RoleId::System(7),
964 grantor: RoleId::System(12),
965 acl_mode: AclMode::empty(),
966 };
967 assert_eq!(
968 mz_acl_item,
969 MzAclItem::decode_binary(&mz_acl_item.encode_binary()).unwrap()
970 );
971
972 let mz_acl_item = MzAclItem {
973 grantee: RoleId::Public,
974 grantor: RoleId::User(100),
975 acl_mode: AclMode::empty(),
976 };
977 assert_eq!(
978 mz_acl_item,
979 MzAclItem::decode_binary(&mz_acl_item.encode_binary()).unwrap()
980 );
981
982 mz_ore::assert_err!(MzAclItem::decode_binary(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]))
983}
984
985#[mz_ore::test]
986fn test_mz_acl_item_binary_size() {
987 assert_eq!(26, MzAclItem::binary_size());
988}
989
990#[mz_ore::test]
991fn test_acl_parsing() {
992 let s = "42=rw/666";
993 let acl: AclItem = s.parse().unwrap();
994 assert_eq!(42, acl.grantee.0);
995 assert_eq!(666, acl.grantor.0);
996 assert!(!acl.acl_mode.contains(AclMode::INSERT));
997 assert!(acl.acl_mode.contains(AclMode::SELECT));
998 assert!(acl.acl_mode.contains(AclMode::UPDATE));
999 assert!(!acl.acl_mode.contains(AclMode::DELETE));
1000 assert!(!acl.acl_mode.contains(AclMode::USAGE));
1001 assert!(!acl.acl_mode.contains(AclMode::CREATE));
1002 assert!(!acl.acl_mode.contains(AclMode::CREATE_ROLE));
1003 assert!(!acl.acl_mode.contains(AclMode::CREATE_DB));
1004 assert!(!acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
1005 assert!(!acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
1006 assert_eq!(s, acl.to_string());
1007
1008 let s = "=UC/4";
1009 let acl: AclItem = s.parse().unwrap();
1010 assert_eq!(PUBLIC_ROLE_OID, acl.grantee);
1011 assert_eq!(4, acl.grantor.0);
1012 assert!(!acl.acl_mode.contains(AclMode::INSERT));
1013 assert!(!acl.acl_mode.contains(AclMode::SELECT));
1014 assert!(!acl.acl_mode.contains(AclMode::UPDATE));
1015 assert!(!acl.acl_mode.contains(AclMode::DELETE));
1016 assert!(acl.acl_mode.contains(AclMode::USAGE));
1017 assert!(acl.acl_mode.contains(AclMode::CREATE));
1018 assert!(!acl.acl_mode.contains(AclMode::CREATE_ROLE));
1019 assert!(!acl.acl_mode.contains(AclMode::CREATE_DB));
1020 assert!(!acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
1021 assert!(!acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
1022 assert_eq!(s, acl.to_string());
1023
1024 let s = "7=/12";
1025 let acl: AclItem = s.parse().unwrap();
1026 assert_eq!(7, acl.grantee.0);
1027 assert_eq!(12, acl.grantor.0);
1028 assert!(!acl.acl_mode.contains(AclMode::INSERT));
1029 assert!(!acl.acl_mode.contains(AclMode::SELECT));
1030 assert!(!acl.acl_mode.contains(AclMode::UPDATE));
1031 assert!(!acl.acl_mode.contains(AclMode::DELETE));
1032 assert!(!acl.acl_mode.contains(AclMode::USAGE));
1033 assert!(!acl.acl_mode.contains(AclMode::CREATE));
1034 assert!(!acl.acl_mode.contains(AclMode::CREATE_ROLE));
1035 assert!(!acl.acl_mode.contains(AclMode::CREATE_DB));
1036 assert!(!acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
1037 assert!(!acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
1038 assert_eq!(s, acl.to_string());
1039
1040 let s = "=/100";
1041 let acl: AclItem = s.parse().unwrap();
1042 assert_eq!(PUBLIC_ROLE_OID, acl.grantee);
1043 assert_eq!(100, acl.grantor.0);
1044 assert!(!acl.acl_mode.contains(AclMode::INSERT));
1045 assert!(!acl.acl_mode.contains(AclMode::SELECT));
1046 assert!(!acl.acl_mode.contains(AclMode::UPDATE));
1047 assert!(!acl.acl_mode.contains(AclMode::DELETE));
1048 assert!(!acl.acl_mode.contains(AclMode::USAGE));
1049 assert!(!acl.acl_mode.contains(AclMode::CREATE));
1050 assert!(!acl.acl_mode.contains(AclMode::CREATE_ROLE));
1051 assert!(!acl.acl_mode.contains(AclMode::CREATE_DB));
1052 assert!(!acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
1053 assert!(!acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
1054 assert_eq!(s, acl.to_string());
1055
1056 let s = "1=RBNP/2";
1057 let acl: AclItem = s.parse().unwrap();
1058 assert_eq!(1, acl.grantee.0);
1059 assert_eq!(2, acl.grantor.0);
1060 assert!(!acl.acl_mode.contains(AclMode::INSERT));
1061 assert!(!acl.acl_mode.contains(AclMode::SELECT));
1062 assert!(!acl.acl_mode.contains(AclMode::UPDATE));
1063 assert!(!acl.acl_mode.contains(AclMode::DELETE));
1064 assert!(!acl.acl_mode.contains(AclMode::USAGE));
1065 assert!(!acl.acl_mode.contains(AclMode::CREATE));
1066 assert!(acl.acl_mode.contains(AclMode::CREATE_ROLE));
1067 assert!(acl.acl_mode.contains(AclMode::CREATE_DB));
1068 assert!(acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
1069 assert!(acl.acl_mode.contains(AclMode::CREATE_NETWORK_POLICY));
1070 assert_eq!(s, acl.to_string());
1071
1072 mz_ore::assert_err!("42/rw=666".parse::<AclItem>());
1073 mz_ore::assert_err!("u42=rw/u666".parse::<AclItem>());
1074 mz_ore::assert_err!("s42=rw/s666".parse::<AclItem>());
1075 mz_ore::assert_err!("u32=C/".parse::<AclItem>());
1076 mz_ore::assert_err!("=/".parse::<AclItem>());
1077 mz_ore::assert_err!("f62hfiuew827fhh".parse::<AclItem>());
1078 mz_ore::assert_err!("u2=rw/s66=CU/u33".parse::<AclItem>());
1079}
1080
1081#[mz_ore::test]
1082fn test_acl_item_binary() {
1083 use std::ops::BitAnd;
1084
1085 let acl_item = AclItem {
1086 grantee: Oid(42),
1087 grantor: Oid(666),
1088 acl_mode: AclMode::empty()
1089 .bitand(AclMode::SELECT)
1090 .bitand(AclMode::UPDATE),
1091 };
1092 assert_eq!(
1093 acl_item,
1094 AclItem::decode_binary(&acl_item.encode_binary()).unwrap()
1095 );
1096
1097 let acl_item = AclItem {
1098 grantee: PUBLIC_ROLE_OID,
1099 grantor: Oid(4),
1100 acl_mode: AclMode::empty()
1101 .bitand(AclMode::USAGE)
1102 .bitand(AclMode::CREATE),
1103 };
1104 assert_eq!(
1105 acl_item,
1106 AclItem::decode_binary(&acl_item.encode_binary()).unwrap()
1107 );
1108
1109 let acl_item = AclItem {
1110 grantee: Oid(7),
1111 grantor: Oid(12),
1112 acl_mode: AclMode::empty(),
1113 };
1114 assert_eq!(
1115 acl_item,
1116 AclItem::decode_binary(&acl_item.encode_binary()).unwrap()
1117 );
1118
1119 let acl_item = AclItem {
1120 grantee: PUBLIC_ROLE_OID,
1121 grantor: Oid(100),
1122 acl_mode: AclMode::empty(),
1123 };
1124 assert_eq!(
1125 acl_item,
1126 AclItem::decode_binary(&acl_item.encode_binary()).unwrap()
1127 );
1128
1129 mz_ore::assert_err!(AclItem::decode_binary(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]))
1130}
1131
1132#[mz_ore::test]
1133fn test_acl_item_binary_size() {
1134 assert_eq!(16, AclItem::binary_size());
1135}
1136
1137proptest! {
1138 #[mz_ore::test]
1139 #[cfg_attr(miri, ignore)] fn proptest_acl_item_binary_encoding_roundtrip(acl_item: AclItem) {
1141 let encoded = acl_item.encode_binary();
1142 let decoded = AclItem::decode_binary(&encoded).unwrap();
1143 assert_eq!(acl_item, decoded);
1144 }
1145
1146 #[mz_ore::test]
1147 #[cfg_attr(miri, ignore)] fn proptest_valid_acl_item_str(acl_item: AclItem) {
1149 let encoded = acl_item.to_string();
1150 let decoded = AclItem::from_str(&encoded).unwrap();
1151 assert_eq!(acl_item, decoded);
1152 }
1153}
1154
1155#[mz_ore::test]
1156fn proptest_packed_acl_item_roundtrips() {
1157 fn roundtrip_acl_item(og: AclItem) {
1158 let packed = PackedAclItem::from_value(og);
1159 let rnd = packed.into_value();
1160 assert_eq!(og, rnd);
1161 }
1162
1163 proptest!(|(acl_item in proptest::arbitrary::any::<AclItem>())| {
1164 roundtrip_acl_item(acl_item);
1165 })
1166}
1167
1168#[mz_ore::test]
1169#[cfg_attr(miri, ignore)] fn proptest_packed_acl_item_sorts() {
1171 fn sort_acl_items(mut og: Vec<AclItem>) {
1172 let mut packed: Vec<_> = og.iter().copied().map(PackedAclItem::from_value).collect();
1173
1174 og.sort();
1175 packed.sort();
1176
1177 let rnd: Vec<_> = packed.into_iter().map(PackedAclItem::into_value).collect();
1178 assert_eq!(og, rnd);
1179 }
1180
1181 proptest!(|(acl_items in proptest::collection::vec(any::<AclItem>(), 0..64))| {
1182 sort_acl_items(acl_items);
1183 });
1184}
1185
1186#[mz_ore::test]
1187fn proptest_packed_mz_acl_item_roundtrips() {
1188 fn roundtrip_mz_acl_item(og: MzAclItem) {
1189 let packed = PackedMzAclItem::from_value(og);
1190 let rnd = packed.into_value();
1191 assert_eq!(og, rnd);
1192 }
1193
1194 proptest!(|(acl_item in proptest::arbitrary::any::<MzAclItem>())| {
1195 roundtrip_mz_acl_item(acl_item);
1196 })
1197}
1198
1199#[mz_ore::test]
1200#[cfg_attr(miri, ignore)] fn proptest_packed_mz_acl_item_sorts() {
1202 fn sort_mz_acl_items(mut og: Vec<MzAclItem>) {
1203 let mut packed: Vec<_> = og
1204 .iter()
1205 .copied()
1206 .map(PackedMzAclItem::from_value)
1207 .collect();
1208
1209 og.sort();
1210 packed.sort();
1211
1212 let rnd: Vec<_> = packed
1213 .into_iter()
1214 .map(PackedMzAclItem::into_value)
1215 .collect();
1216 assert_eq!(og, rnd);
1217 }
1218
1219 proptest!(|(acl_items in proptest::collection::vec(any::<MzAclItem>(), 0..64))| {
1220 sort_mz_acl_items(acl_items);
1221 });
1222}