use std::collections::BTreeMap;
use std::fmt;
use std::mem::size_of;
use std::ops::BitOrAssign;
use std::str::FromStr;
use crate::adt::system::Oid;
use anyhow::{anyhow, Error};
use bitflags::bitflags;
use columnation::{Columnation, CopyRegion};
use mz_ore::str::StrExt;
use mz_proto::{RustType, TryFromProtoError};
use proptest::arbitrary::Arbitrary;
use proptest::proptest;
use proptest::strategy::{BoxedStrategy, Strategy};
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use crate::role_id::RoleId;
include!(concat!(env!("OUT_DIR"), "/mz_repr.adt.mz_acl_item.rs"));
const INSERT_CHAR: char = 'a';
const SELECT_CHAR: char = 'r';
const UPDATE_CHAR: char = 'w';
const DELETE_CHAR: char = 'd';
const USAGE_CHAR: char = 'U';
const CREATE_CHAR: char = 'C';
const CREATE_ROLE_CHAR: char = 'R';
const CREATE_DB_CHAR: char = 'B';
const CREATE_CLUSTER_CHAR: char = 'N';
const INSERT_STR: &str = "INSERT";
const SELECT_STR: &str = "SELECT";
const UPDATE_STR: &str = "UPDATE";
const DELETE_STR: &str = "DELETE";
const USAGE_STR: &str = "USAGE";
const CREATE_STR: &str = "CREATE";
const CREATE_ROLE_STR: &str = "CREATEROLE";
const CREATE_DB_STR: &str = "CREATEDB";
const CREATE_CLUSTER_STR: &str = "CREATECLUSTER";
pub const PUBLIC_ROLE_OID: Oid = Oid(0);
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct AclMode: u64 {
const INSERT = 1 << 0;
const SELECT = 1 << 1;
const UPDATE = 1 << 2;
const DELETE = 1 << 3;
const USAGE = 1 << 8;
const CREATE = 1 << 9;
const CREATE_CLUSTER = 1 << 29;
const CREATE_DB = 1 << 30;
const CREATE_ROLE = 1 << 31;
}
}
impl AclMode {
pub fn parse_single_privilege(s: &str) -> Result<Self, Error> {
match s.trim().to_uppercase().as_str() {
INSERT_STR => Ok(AclMode::INSERT),
SELECT_STR => Ok(AclMode::SELECT),
UPDATE_STR => Ok(AclMode::UPDATE),
DELETE_STR => Ok(AclMode::DELETE),
USAGE_STR => Ok(AclMode::USAGE),
CREATE_STR => Ok(AclMode::CREATE),
CREATE_ROLE_STR => Ok(AclMode::CREATE_ROLE),
CREATE_DB_STR => Ok(AclMode::CREATE_DB),
CREATE_CLUSTER_STR => Ok(AclMode::CREATE_CLUSTER),
_ => Err(anyhow!("{}", s.quoted())),
}
}
pub fn parse_multiple_privileges(s: &str) -> Result<Self, Error> {
let mut acl_mode = AclMode::empty();
for privilege in s.split(',') {
let privilege = AclMode::parse_single_privilege(privilege)?;
acl_mode.bitor_assign(privilege);
}
Ok(acl_mode)
}
pub fn to_error_string(&self) -> String {
self.explode().join(", ")
}
pub fn explode(&self) -> Vec<&'static str> {
let mut privileges = Vec::new();
if self.contains(AclMode::SELECT) {
privileges.push(SELECT_STR);
}
if self.contains(AclMode::INSERT) {
privileges.push(INSERT_STR);
}
if self.contains(AclMode::UPDATE) {
privileges.push(UPDATE_STR);
}
if self.contains(AclMode::DELETE) {
privileges.push(DELETE_STR);
}
if self.contains(AclMode::USAGE) {
privileges.push(USAGE_STR);
}
if self.contains(AclMode::CREATE) {
privileges.push(CREATE_STR);
}
if self.contains(AclMode::CREATE_ROLE) {
privileges.push(CREATE_ROLE_STR);
}
if self.contains(AclMode::CREATE_DB) {
privileges.push(CREATE_DB_STR);
}
if self.contains(AclMode::CREATE_CLUSTER) {
privileges.push(CREATE_CLUSTER_STR);
}
privileges
}
}
impl FromStr for AclMode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut acl_mode = AclMode::empty();
for c in s.chars() {
match c {
INSERT_CHAR => acl_mode.bitor_assign(AclMode::INSERT),
SELECT_CHAR => acl_mode.bitor_assign(AclMode::SELECT),
UPDATE_CHAR => acl_mode.bitor_assign(AclMode::UPDATE),
DELETE_CHAR => acl_mode.bitor_assign(AclMode::DELETE),
USAGE_CHAR => acl_mode.bitor_assign(AclMode::USAGE),
CREATE_CHAR => acl_mode.bitor_assign(AclMode::CREATE),
CREATE_ROLE_CHAR => acl_mode.bitor_assign(AclMode::CREATE_ROLE),
CREATE_DB_CHAR => acl_mode.bitor_assign(AclMode::CREATE_DB),
CREATE_CLUSTER_CHAR => acl_mode.bitor_assign(AclMode::CREATE_CLUSTER),
_ => return Err(anyhow!("invalid privilege '{c}' in acl mode '{s}'")),
}
}
Ok(acl_mode)
}
}
impl fmt::Display for AclMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.contains(AclMode::INSERT) {
write!(f, "{INSERT_CHAR}")?;
}
if self.contains(AclMode::SELECT) {
write!(f, "{SELECT_CHAR}")?;
}
if self.contains(AclMode::UPDATE) {
write!(f, "{UPDATE_CHAR}")?;
}
if self.contains(AclMode::DELETE) {
write!(f, "{DELETE_CHAR}")?;
}
if self.contains(AclMode::USAGE) {
write!(f, "{USAGE_CHAR}")?;
}
if self.contains(AclMode::CREATE) {
write!(f, "{CREATE_CHAR}")?;
}
if self.contains(AclMode::CREATE_ROLE) {
write!(f, "{CREATE_ROLE_CHAR}")?;
}
if self.contains(AclMode::CREATE_DB) {
write!(f, "{CREATE_DB_CHAR}")?;
}
if self.contains(AclMode::CREATE_CLUSTER) {
write!(f, "{CREATE_CLUSTER_CHAR}")?;
}
Ok(())
}
}
impl RustType<ProtoAclMode> for AclMode {
fn into_proto(&self) -> ProtoAclMode {
ProtoAclMode {
acl_mode: self.bits,
}
}
fn from_proto(proto: ProtoAclMode) -> Result<Self, TryFromProtoError> {
Ok(AclMode {
bits: proto.acl_mode,
})
}
}
impl Columnation for AclMode {
type InnerRegion = CopyRegion<AclMode>;
}
impl Arbitrary for AclMode {
type Parameters = ();
type Strategy = BoxedStrategy<AclMode>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
proptest::bits::BitSetStrategy::masked(AclMode::all().bits)
.prop_map(|bits| AclMode::from_bits(bits).expect("invalid proptest implementation"))
.boxed()
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Hash, Deserialize, Arbitrary,
)]
pub struct MzAclItem {
pub grantee: RoleId,
pub grantor: RoleId,
pub acl_mode: AclMode,
}
impl MzAclItem {
pub fn empty(grantee: RoleId, grantor: RoleId) -> MzAclItem {
MzAclItem {
grantee,
grantor,
acl_mode: AclMode::empty(),
}
}
pub fn encode_binary(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(Self::binary_size());
res.extend_from_slice(&self.grantee.encode_binary());
res.extend_from_slice(&self.grantor.encode_binary());
res.extend_from_slice(&self.acl_mode.bits().to_le_bytes());
res
}
pub fn decode_binary(raw: &[u8]) -> Result<MzAclItem, Error> {
if raw.len() != MzAclItem::binary_size() {
return Err(anyhow!(
"invalid binary size, expecting {}, found {}",
MzAclItem::binary_size(),
raw.len()
));
}
let role_id_size = RoleId::binary_size();
let grantee = RoleId::decode_binary(&raw[0..role_id_size])?;
let raw = &raw[role_id_size..];
let grantor = RoleId::decode_binary(&raw[0..role_id_size])?;
let raw = &raw[role_id_size..];
let acl_mode = u64::from_le_bytes(raw.try_into()?);
Ok(MzAclItem {
grantee,
grantor,
acl_mode: AclMode { bits: acl_mode },
})
}
pub const fn binary_size() -> usize {
RoleId::binary_size() + RoleId::binary_size() + size_of::<u64>()
}
}
impl FromStr for MzAclItem {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.split('=').collect();
let &[grantee, rest] = parts.as_slice() else {
return Err(anyhow!("invalid mz_aclitem '{s}'"));
};
let parts: Vec<_> = rest.split('/').collect();
let &[acl_mode, grantor] = parts.as_slice() else {
return Err(anyhow!("invalid mz_aclitem '{s}'"));
};
let grantee: RoleId = if grantee.is_empty() {
RoleId::Public
} else {
grantee.parse()?
};
let acl_mode: AclMode = acl_mode.parse()?;
let grantor: RoleId = grantor.parse()?;
Ok(MzAclItem {
grantee,
grantor,
acl_mode,
})
}
}
impl fmt::Display for MzAclItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.grantee.is_public() {
write!(f, "{}", self.grantee)?;
}
write!(f, "={}/{}", self.acl_mode, self.grantor)
}
}
impl RustType<ProtoMzAclItem> for MzAclItem {
fn into_proto(&self) -> ProtoMzAclItem {
ProtoMzAclItem {
grantee: Some(self.grantee.into_proto()),
grantor: Some(self.grantor.into_proto()),
acl_mode: Some(self.acl_mode.into_proto()),
}
}
fn from_proto(proto: ProtoMzAclItem) -> Result<Self, TryFromProtoError> {
match (proto.grantee, proto.grantor, proto.acl_mode) {
(Some(grantee), Some(grantor), Some(acl_mode)) => Ok(MzAclItem {
grantee: RoleId::from_proto(grantee)?,
grantor: RoleId::from_proto(grantor)?,
acl_mode: AclMode::from_proto(acl_mode)?,
}),
(None, _, _) => Err(TryFromProtoError::missing_field("ProtoMzAclItem::grantee")),
(_, None, _) => Err(TryFromProtoError::missing_field("ProtoMzAclItem::grantor")),
(_, _, None) => Err(TryFromProtoError::missing_field("ProtoMzAclItem::acl_mode")),
}
}
}
impl Columnation for MzAclItem {
type InnerRegion = CopyRegion<MzAclItem>;
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Hash, Deserialize, Arbitrary,
)]
pub struct AclItem {
pub grantee: Oid,
pub grantor: Oid,
pub acl_mode: AclMode,
}
impl AclItem {
pub fn empty(grantee: Oid, grantor: Oid) -> AclItem {
AclItem {
grantee,
grantor,
acl_mode: AclMode::empty(),
}
}
}
impl AclItem {
pub fn encode_binary(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(Self::binary_size());
res.extend_from_slice(&self.grantee.0.to_le_bytes());
res.extend_from_slice(&self.grantor.0.to_le_bytes());
res.extend_from_slice(&self.acl_mode.bits().to_le_bytes());
res
}
pub fn decode_binary(raw: &[u8]) -> Result<AclItem, Error> {
if raw.len() != AclItem::binary_size() {
return Err(anyhow!(
"invalid binary size, expecting {}, found {}",
AclItem::binary_size(),
raw.len()
));
}
let oid_size = size_of::<u32>();
let grantee = Oid(u32::from_le_bytes(raw[0..oid_size].try_into()?));
let raw = &raw[oid_size..];
let grantor = Oid(u32::from_le_bytes(raw[0..oid_size].try_into()?));
let raw = &raw[oid_size..];
let acl_mode = u64::from_le_bytes(raw.try_into()?);
Ok(AclItem {
grantee,
grantor,
acl_mode: AclMode { bits: acl_mode },
})
}
pub const fn binary_size() -> usize {
size_of::<u32>() + size_of::<u32>() + size_of::<u64>()
}
}
impl FromStr for AclItem {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.split('=').collect();
let &[grantee, rest] = parts.as_slice() else {
return Err(anyhow!("invalid aclitem '{s}'"));
};
let parts: Vec<_> = rest.split('/').collect();
let &[acl_mode, grantor] = parts.as_slice() else {
return Err(anyhow!("invalid mz_aclitem '{s}'"));
};
let grantee: Oid = if grantee.is_empty() {
PUBLIC_ROLE_OID
} else {
Oid(grantee.parse()?)
};
let acl_mode: AclMode = acl_mode.parse()?;
let grantor: Oid = Oid(grantor.parse()?);
Ok(AclItem {
grantee,
grantor,
acl_mode,
})
}
}
impl fmt::Display for AclItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.grantee != PUBLIC_ROLE_OID {
write!(f, "{}", self.grantee.0)?;
}
write!(f, "={}/{}", self.acl_mode, self.grantor.0)
}
}
impl RustType<ProtoAclItem> for AclItem {
fn into_proto(&self) -> ProtoAclItem {
ProtoAclItem {
grantee: self.grantee.0,
grantor: self.grantor.0,
acl_mode: Some(self.acl_mode.into_proto()),
}
}
fn from_proto(proto: ProtoAclItem) -> Result<Self, TryFromProtoError> {
match proto.acl_mode {
Some(acl_mode) => Ok(AclItem {
grantee: Oid(proto.grantee),
grantor: Oid(proto.grantor),
acl_mode: AclMode::from_proto(acl_mode)?,
}),
None => Err(TryFromProtoError::missing_field("ProtoMzAclItem::acl_mode")),
}
}
}
impl Columnation for AclItem {
type InnerRegion = CopyRegion<AclItem>;
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct PrivilegeMap(
#[serde(serialize_with = "mz_ore::serde::map_key_to_string")] BTreeMap<RoleId, Vec<MzAclItem>>,
);
impl PrivilegeMap {
pub fn new() -> PrivilegeMap {
PrivilegeMap(BTreeMap::new())
}
pub fn from_mz_acl_items(items: impl IntoIterator<Item = MzAclItem>) -> PrivilegeMap {
let mut map = PrivilegeMap::new();
map.grant_all(items);
map
}
pub fn get_acl_item(&self, grantee: &RoleId, grantor: &RoleId) -> Option<&MzAclItem> {
self.0.get(grantee).and_then(|privileges| {
privileges
.into_iter()
.find(|mz_acl_item| &mz_acl_item.grantor == grantor)
})
}
pub fn get_acl_items_for_grantee(&self, grantee: &RoleId) -> impl Iterator<Item = &MzAclItem> {
self.0
.get(grantee)
.into_iter()
.flat_map(|privileges| privileges.into_iter())
}
pub fn all_values(&self) -> impl Iterator<Item = &MzAclItem> {
self.0
.values()
.flat_map(|privileges| privileges.into_iter())
}
pub fn all_values_owned(&self) -> impl Iterator<Item = MzAclItem> + '_ {
self.all_values().cloned()
}
pub fn into_all_values(self) -> impl Iterator<Item = MzAclItem> {
self.0
.into_values()
.flat_map(|privileges| privileges.into_iter())
}
pub fn grant(&mut self, privilege: MzAclItem) {
let grantee_privileges = self.0.entry(privilege.grantee).or_default();
if let Some(existing_privilege) = grantee_privileges
.iter_mut()
.find(|cur_privilege| cur_privilege.grantor == privilege.grantor)
{
assert_eq!(
privilege.grantee, existing_privilege.grantee,
"PrivilegeMap out of sync"
);
existing_privilege.acl_mode = existing_privilege.acl_mode.union(privilege.acl_mode);
} else {
grantee_privileges.push(privilege);
}
}
pub fn grant_all(&mut self, mz_acl_items: impl IntoIterator<Item = MzAclItem>) {
for mz_acl_item in mz_acl_items {
self.grant(mz_acl_item);
}
}
pub fn revoke(&mut self, privilege: &MzAclItem) {
let grantee_privileges = self.0.entry(privilege.grantee).or_default();
if let Some(existing_privilege) = grantee_privileges
.iter_mut()
.find(|cur_privilege| cur_privilege.grantor == privilege.grantor)
{
assert_eq!(
privilege.grantee, existing_privilege.grantee,
"PrivilegeMap out of sync"
);
existing_privilege.acl_mode =
existing_privilege.acl_mode.difference(privilege.acl_mode);
}
grantee_privileges.retain(|privilege| !privilege.acl_mode.is_empty());
if grantee_privileges.is_empty() {
self.0.remove(&privilege.grantee);
}
}
pub fn debug_json(&self) -> serde_json::Value {
let privileges_by_str: BTreeMap<String, _> = self
.0
.iter()
.map(|(key, value)| (key.to_string(), value))
.collect();
serde_json::json!(privileges_by_str)
}
}
impl Default for PrivilegeMap {
fn default() -> PrivilegeMap {
PrivilegeMap::new()
}
}
pub fn merge_mz_acl_items(
mz_acl_items: impl Iterator<Item = MzAclItem>,
) -> impl Iterator<Item = MzAclItem> {
mz_acl_items
.fold(BTreeMap::new(), |mut accum, mz_acl_item| {
let item = accum
.entry((mz_acl_item.grantee, mz_acl_item.grantor))
.or_insert(MzAclItem::empty(mz_acl_item.grantee, mz_acl_item.grantor));
item.acl_mode |= mz_acl_item.acl_mode;
accum
})
.into_values()
}
#[mz_ore::test]
fn test_mz_acl_parsing() {
let s = "u42=rw/s666";
let mz_acl: MzAclItem = s.parse().unwrap();
assert_eq!(RoleId::User(42), mz_acl.grantee);
assert_eq!(RoleId::System(666), mz_acl.grantor);
assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
assert!(mz_acl.acl_mode.contains(AclMode::SELECT));
assert!(mz_acl.acl_mode.contains(AclMode::UPDATE));
assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
assert!(!mz_acl.acl_mode.contains(AclMode::USAGE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, mz_acl.to_string());
let s = "=UC/u4";
let mz_acl: MzAclItem = s.parse().unwrap();
assert_eq!(RoleId::Public, mz_acl.grantee);
assert_eq!(RoleId::User(4), mz_acl.grantor);
assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
assert!(!mz_acl.acl_mode.contains(AclMode::SELECT));
assert!(!mz_acl.acl_mode.contains(AclMode::UPDATE));
assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
assert!(mz_acl.acl_mode.contains(AclMode::USAGE));
assert!(mz_acl.acl_mode.contains(AclMode::CREATE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, mz_acl.to_string());
let s = "s7=/s12";
let mz_acl: MzAclItem = s.parse().unwrap();
assert_eq!(RoleId::System(7), mz_acl.grantee);
assert_eq!(RoleId::System(12), mz_acl.grantor);
assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
assert!(!mz_acl.acl_mode.contains(AclMode::SELECT));
assert!(!mz_acl.acl_mode.contains(AclMode::UPDATE));
assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
assert!(!mz_acl.acl_mode.contains(AclMode::USAGE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, mz_acl.to_string());
let s = "=/u100";
let mz_acl: MzAclItem = s.parse().unwrap();
assert_eq!(RoleId::Public, mz_acl.grantee);
assert_eq!(RoleId::User(100), mz_acl.grantor);
assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
assert!(!mz_acl.acl_mode.contains(AclMode::SELECT));
assert!(!mz_acl.acl_mode.contains(AclMode::UPDATE));
assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
assert!(!mz_acl.acl_mode.contains(AclMode::USAGE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, mz_acl.to_string());
let s = "u1=RBN/u2";
let mz_acl: MzAclItem = s.parse().unwrap();
assert_eq!(RoleId::User(1), mz_acl.grantee);
assert_eq!(RoleId::User(2), mz_acl.grantor);
assert!(!mz_acl.acl_mode.contains(AclMode::INSERT));
assert!(!mz_acl.acl_mode.contains(AclMode::SELECT));
assert!(!mz_acl.acl_mode.contains(AclMode::UPDATE));
assert!(!mz_acl.acl_mode.contains(AclMode::DELETE));
assert!(!mz_acl.acl_mode.contains(AclMode::USAGE));
assert!(!mz_acl.acl_mode.contains(AclMode::CREATE));
assert!(mz_acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(mz_acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(mz_acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, mz_acl.to_string());
assert!("u42/rw=u666".parse::<MzAclItem>().is_err());
assert!("u32=C/".parse::<MzAclItem>().is_err());
assert!("=/".parse::<MzAclItem>().is_err());
assert!("f62hfiuew827fhh".parse::<MzAclItem>().is_err());
assert!("u2=rw/s66=CU/u33".parse::<MzAclItem>().is_err());
}
#[mz_ore::test]
fn test_mz_acl_item_binary() {
use std::ops::BitAnd;
let mz_acl_item = MzAclItem {
grantee: RoleId::User(42),
grantor: RoleId::System(666),
acl_mode: AclMode::empty()
.bitand(AclMode::SELECT)
.bitand(AclMode::UPDATE),
};
assert_eq!(
mz_acl_item,
MzAclItem::decode_binary(&mz_acl_item.encode_binary()).unwrap()
);
let mz_acl_item = MzAclItem {
grantee: RoleId::Public,
grantor: RoleId::User(4),
acl_mode: AclMode::empty()
.bitand(AclMode::USAGE)
.bitand(AclMode::CREATE),
};
assert_eq!(
mz_acl_item,
MzAclItem::decode_binary(&mz_acl_item.encode_binary()).unwrap()
);
let mz_acl_item = MzAclItem {
grantee: RoleId::System(7),
grantor: RoleId::System(12),
acl_mode: AclMode::empty(),
};
assert_eq!(
mz_acl_item,
MzAclItem::decode_binary(&mz_acl_item.encode_binary()).unwrap()
);
let mz_acl_item = MzAclItem {
grantee: RoleId::Public,
grantor: RoleId::User(100),
acl_mode: AclMode::empty(),
};
assert_eq!(
mz_acl_item,
MzAclItem::decode_binary(&mz_acl_item.encode_binary()).unwrap()
);
assert!(MzAclItem::decode_binary(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]).is_err())
}
#[mz_ore::test]
fn test_mz_acl_item_binary_size() {
assert_eq!(26, MzAclItem::binary_size());
}
#[mz_ore::test]
fn test_acl_parsing() {
let s = "42=rw/666";
let acl: AclItem = s.parse().unwrap();
assert_eq!(42, acl.grantee.0);
assert_eq!(666, acl.grantor.0);
assert!(!acl.acl_mode.contains(AclMode::INSERT));
assert!(acl.acl_mode.contains(AclMode::SELECT));
assert!(acl.acl_mode.contains(AclMode::UPDATE));
assert!(!acl.acl_mode.contains(AclMode::DELETE));
assert!(!acl.acl_mode.contains(AclMode::USAGE));
assert!(!acl.acl_mode.contains(AclMode::CREATE));
assert!(!acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(!acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(!acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, acl.to_string());
let s = "=UC/4";
let acl: AclItem = s.parse().unwrap();
assert_eq!(PUBLIC_ROLE_OID, acl.grantee);
assert_eq!(4, acl.grantor.0);
assert!(!acl.acl_mode.contains(AclMode::INSERT));
assert!(!acl.acl_mode.contains(AclMode::SELECT));
assert!(!acl.acl_mode.contains(AclMode::UPDATE));
assert!(!acl.acl_mode.contains(AclMode::DELETE));
assert!(acl.acl_mode.contains(AclMode::USAGE));
assert!(acl.acl_mode.contains(AclMode::CREATE));
assert!(!acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(!acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(!acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, acl.to_string());
let s = "7=/12";
let acl: AclItem = s.parse().unwrap();
assert_eq!(7, acl.grantee.0);
assert_eq!(12, acl.grantor.0);
assert!(!acl.acl_mode.contains(AclMode::INSERT));
assert!(!acl.acl_mode.contains(AclMode::SELECT));
assert!(!acl.acl_mode.contains(AclMode::UPDATE));
assert!(!acl.acl_mode.contains(AclMode::DELETE));
assert!(!acl.acl_mode.contains(AclMode::USAGE));
assert!(!acl.acl_mode.contains(AclMode::CREATE));
assert!(!acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(!acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(!acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, acl.to_string());
let s = "=/100";
let acl: AclItem = s.parse().unwrap();
assert_eq!(PUBLIC_ROLE_OID, acl.grantee);
assert_eq!(100, acl.grantor.0);
assert!(!acl.acl_mode.contains(AclMode::INSERT));
assert!(!acl.acl_mode.contains(AclMode::SELECT));
assert!(!acl.acl_mode.contains(AclMode::UPDATE));
assert!(!acl.acl_mode.contains(AclMode::DELETE));
assert!(!acl.acl_mode.contains(AclMode::USAGE));
assert!(!acl.acl_mode.contains(AclMode::CREATE));
assert!(!acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(!acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(!acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, acl.to_string());
let s = "1=RBN/2";
let acl: AclItem = s.parse().unwrap();
assert_eq!(1, acl.grantee.0);
assert_eq!(2, acl.grantor.0);
assert!(!acl.acl_mode.contains(AclMode::INSERT));
assert!(!acl.acl_mode.contains(AclMode::SELECT));
assert!(!acl.acl_mode.contains(AclMode::UPDATE));
assert!(!acl.acl_mode.contains(AclMode::DELETE));
assert!(!acl.acl_mode.contains(AclMode::USAGE));
assert!(!acl.acl_mode.contains(AclMode::CREATE));
assert!(acl.acl_mode.contains(AclMode::CREATE_ROLE));
assert!(acl.acl_mode.contains(AclMode::CREATE_DB));
assert!(acl.acl_mode.contains(AclMode::CREATE_CLUSTER));
assert_eq!(s, acl.to_string());
assert!("42/rw=666".parse::<AclItem>().is_err());
assert!("u42=rw/u666".parse::<AclItem>().is_err());
assert!("s42=rw/s666".parse::<AclItem>().is_err());
assert!("u32=C/".parse::<AclItem>().is_err());
assert!("=/".parse::<AclItem>().is_err());
assert!("f62hfiuew827fhh".parse::<AclItem>().is_err());
assert!("u2=rw/s66=CU/u33".parse::<AclItem>().is_err());
}
#[mz_ore::test]
fn test_acl_item_binary() {
use std::ops::BitAnd;
let acl_item = AclItem {
grantee: Oid(42),
grantor: Oid(666),
acl_mode: AclMode::empty()
.bitand(AclMode::SELECT)
.bitand(AclMode::UPDATE),
};
assert_eq!(
acl_item,
AclItem::decode_binary(&acl_item.encode_binary()).unwrap()
);
let acl_item = AclItem {
grantee: PUBLIC_ROLE_OID,
grantor: Oid(4),
acl_mode: AclMode::empty()
.bitand(AclMode::USAGE)
.bitand(AclMode::CREATE),
};
assert_eq!(
acl_item,
AclItem::decode_binary(&acl_item.encode_binary()).unwrap()
);
let acl_item = AclItem {
grantee: Oid(7),
grantor: Oid(12),
acl_mode: AclMode::empty(),
};
assert_eq!(
acl_item,
AclItem::decode_binary(&acl_item.encode_binary()).unwrap()
);
let acl_item = AclItem {
grantee: PUBLIC_ROLE_OID,
grantor: Oid(100),
acl_mode: AclMode::empty(),
};
assert_eq!(
acl_item,
AclItem::decode_binary(&acl_item.encode_binary()).unwrap()
);
assert!(AclItem::decode_binary(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]).is_err())
}
#[mz_ore::test]
fn test_acl_item_binary_size() {
assert_eq!(16, AclItem::binary_size());
}
proptest! {
#[mz_ore::test]
#[cfg_attr(miri, ignore)] fn proptest_acl_item_binary_encoding_roundtrip(acl_item: AclItem) {
let encoded = acl_item.encode_binary();
let decoded = AclItem::decode_binary(&encoded).unwrap();
assert_eq!(acl_item, decoded);
}
#[mz_ore::test]
#[cfg_attr(miri, ignore)] fn proptest_valid_acl_item_str(acl_item: AclItem) {
let encoded = acl_item.to_string();
let decoded = AclItem::from_str(&encoded).unwrap();
assert_eq!(acl_item, decoded);
}
}