acl: rustfmt module
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
parent
4f727a783e
commit
a7a5406c32
@ -1,8 +1,8 @@
|
|||||||
|
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::collections::{HashMap, BTreeMap, BTreeSet};
|
use std::path::{Path, PathBuf};
|
||||||
use std::path::{PathBuf, Path};
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, Error};
|
||||||
|
|
||||||
@ -11,11 +11,11 @@ use lazy_static::lazy_static;
|
|||||||
use ::serde::{Deserialize, Serialize};
|
use ::serde::{Deserialize, Serialize};
|
||||||
use serde::de::{value, IntoDeserializer};
|
use serde::de::{value, IntoDeserializer};
|
||||||
|
|
||||||
use proxmox::tools::{fs::replace_file, fs::CreateOptions};
|
|
||||||
use proxmox::constnamedbitmap;
|
|
||||||
use proxmox::api::{api, schema::*};
|
use proxmox::api::{api, schema::*};
|
||||||
|
use proxmox::constnamedbitmap;
|
||||||
|
use proxmox::tools::{fs::replace_file, fs::CreateOptions};
|
||||||
|
|
||||||
use crate::api2::types::{Authid,Userid};
|
use crate::api2::types::{Authid, Userid};
|
||||||
|
|
||||||
// define Privilege bitfield
|
// define Privilege bitfield
|
||||||
|
|
||||||
@ -66,7 +66,6 @@ constnamedbitmap! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Admin always has all privileges. It can do everything except a few actions
|
/// Admin always has all privileges. It can do everything except a few actions
|
||||||
/// which are limited to the 'root@pam` superuser
|
/// which are limited to the 'root@pam` superuser
|
||||||
pub const ROLE_ADMIN: u64 = std::u64::MAX;
|
pub const ROLE_ADMIN: u64 = std::u64::MAX;
|
||||||
@ -200,7 +199,9 @@ pub(crate) fn split_acl_path(path: &str) -> Vec<&str> {
|
|||||||
let mut components = vec![];
|
let mut components = vec![];
|
||||||
|
|
||||||
for name in items {
|
for name in items {
|
||||||
if name.is_empty() { continue; }
|
if name.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
components.push(name);
|
components.push(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,45 +212,70 @@ pub(crate) fn split_acl_path(path: &str) -> Vec<&str> {
|
|||||||
///
|
///
|
||||||
/// Currently this just checks for the number of components for various sub-trees.
|
/// Currently this just checks for the number of components for various sub-trees.
|
||||||
pub fn check_acl_path(path: &str) -> Result<(), Error> {
|
pub fn check_acl_path(path: &str) -> Result<(), Error> {
|
||||||
|
|
||||||
let components = split_acl_path(path);
|
let components = split_acl_path(path);
|
||||||
|
|
||||||
let components_len = components.len();
|
let components_len = components.len();
|
||||||
|
|
||||||
if components_len == 0 { return Ok(()); }
|
if components_len == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
match components[0] {
|
match components[0] {
|
||||||
"access" => {
|
"access" => {
|
||||||
if components_len == 1 { return Ok(()); }
|
if components_len == 1 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
match components[1] {
|
match components[1] {
|
||||||
"acl" | "users" => {
|
"acl" | "users" => {
|
||||||
if components_len == 2 { return Ok(()); }
|
if components_len == 2 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"datastore" => { // /datastore/{store}
|
"datastore" => {
|
||||||
if components_len <= 2 { return Ok(()); }
|
// /datastore/{store}
|
||||||
|
if components_len <= 2 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"remote" => { // /remote/{remote}/{store}
|
"remote" => {
|
||||||
if components_len <= 3 { return Ok(()); }
|
// /remote/{remote}/{store}
|
||||||
|
if components_len <= 3 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"system" => {
|
"system" => {
|
||||||
if components_len == 1 { return Ok(()); }
|
if components_len == 1 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
match components[1] {
|
match components[1] {
|
||||||
"disks" | "log" | "status" | "tasks" | "time" => {
|
"disks" | "log" | "status" | "tasks" | "time" => {
|
||||||
if components_len == 2 { return Ok(()); }
|
if components_len == 2 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"services" => { // /system/services/{service}
|
"services" => {
|
||||||
if components_len <= 3 { return Ok(()); }
|
// /system/services/{service}
|
||||||
|
if components_len <= 3 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"network" => {
|
"network" => {
|
||||||
if components_len == 2 { return Ok(()); }
|
if components_len == 2 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
match components[2] {
|
match components[2] {
|
||||||
"dns" => {
|
"dns" => {
|
||||||
if components_len == 3 { return Ok(()); }
|
if components_len == 3 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"interfaces" => { // /system/network/interfaces/{iface}
|
"interfaces" => {
|
||||||
if components_len <= 4 { return Ok(()); }
|
// /system/network/interfaces/{iface}
|
||||||
|
if components_len <= 4 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -284,7 +310,6 @@ pub struct AclTreeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AclTreeNode {
|
impl AclTreeNode {
|
||||||
|
|
||||||
/// Creates a new, empty AclTreeNode.
|
/// Creates a new, empty AclTreeNode.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -306,14 +331,13 @@ impl AclTreeNode {
|
|||||||
let user_roles = self.extract_user_roles(auth_id, leaf);
|
let user_roles = self.extract_user_roles(auth_id, leaf);
|
||||||
if !user_roles.is_empty() || auth_id.is_token() {
|
if !user_roles.is_empty() || auth_id.is_token() {
|
||||||
// user privs always override group privs
|
// user privs always override group privs
|
||||||
return user_roles
|
return user_roles;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.extract_group_roles(auth_id.user(), leaf)
|
self.extract_group_roles(auth_id.user(), leaf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_user_roles(&self, auth_id: &Authid, leaf: bool) -> HashMap<String, bool> {
|
fn extract_user_roles(&self, auth_id: &Authid, leaf: bool) -> HashMap<String, bool> {
|
||||||
|
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
let roles = match self.users.get(auth_id) {
|
let roles = match self.users.get(auth_id) {
|
||||||
@ -337,12 +361,13 @@ impl AclTreeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extract_group_roles(&self, _user: &Userid, leaf: bool) -> HashMap<String, bool> {
|
fn extract_group_roles(&self, _user: &Userid, leaf: bool) -> HashMap<String, bool> {
|
||||||
|
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for (_group, roles) in &self.groups {
|
for (_group, roles) in &self.groups {
|
||||||
let is_member = false; // fixme: check if user is member of the group
|
let is_member = false; // fixme: check if user is member of the group
|
||||||
if !is_member { continue; }
|
if !is_member {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (role, propagate) in roles {
|
for (role, propagate) in roles {
|
||||||
if *propagate || leaf {
|
if *propagate || leaf {
|
||||||
@ -400,7 +425,6 @@ impl AclTreeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AclTree {
|
impl AclTree {
|
||||||
|
|
||||||
/// Create a new, empty ACL tree with a single, empty root [node](AclTreeNode)
|
/// Create a new, empty ACL tree with a single, empty root [node](AclTreeNode)
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -428,7 +452,9 @@ impl AclTree {
|
|||||||
fn get_or_insert_node(&mut self, path: &[&str]) -> &mut AclTreeNode {
|
fn get_or_insert_node(&mut self, path: &[&str]) -> &mut AclTreeNode {
|
||||||
let mut node = &mut self.root;
|
let mut node = &mut self.root;
|
||||||
for comp in path {
|
for comp in path {
|
||||||
node = node.children.entry(String::from(*comp))
|
node = node
|
||||||
|
.children
|
||||||
|
.entry(String::from(*comp))
|
||||||
.or_insert_with(|| AclTreeNode::new());
|
.or_insert_with(|| AclTreeNode::new());
|
||||||
}
|
}
|
||||||
node
|
node
|
||||||
@ -480,26 +506,27 @@ impl AclTree {
|
|||||||
node.insert_user_role(auth_id.to_owned(), role.to_string(), propagate);
|
node.insert_user_role(auth_id.to_owned(), role.to_string(), propagate);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_node_config(
|
fn write_node_config(node: &AclTreeNode, path: &str, w: &mut dyn Write) -> Result<(), Error> {
|
||||||
node: &AclTreeNode,
|
|
||||||
path: &str,
|
|
||||||
w: &mut dyn Write,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
|
|
||||||
let mut role_ug_map0 = HashMap::new();
|
let mut role_ug_map0 = HashMap::new();
|
||||||
let mut role_ug_map1 = HashMap::new();
|
let mut role_ug_map1 = HashMap::new();
|
||||||
|
|
||||||
for (auth_id, roles) in &node.users {
|
for (auth_id, roles) in &node.users {
|
||||||
// no need to save, because root is always 'Administrator'
|
// no need to save, because root is always 'Administrator'
|
||||||
if !auth_id.is_token() && auth_id.user() == "root@pam" { continue; }
|
if !auth_id.is_token() && auth_id.user() == "root@pam" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (role, propagate) in roles {
|
for (role, propagate) in roles {
|
||||||
let role = role.as_str();
|
let role = role.as_str();
|
||||||
let auth_id = auth_id.to_string();
|
let auth_id = auth_id.to_string();
|
||||||
if *propagate {
|
if *propagate {
|
||||||
role_ug_map1.entry(role).or_insert_with(|| BTreeSet::new())
|
role_ug_map1
|
||||||
|
.entry(role)
|
||||||
|
.or_insert_with(|| BTreeSet::new())
|
||||||
.insert(auth_id);
|
.insert(auth_id);
|
||||||
} else {
|
} else {
|
||||||
role_ug_map0.entry(role).or_insert_with(|| BTreeSet::new())
|
role_ug_map0
|
||||||
|
.entry(role)
|
||||||
|
.or_insert_with(|| BTreeSet::new())
|
||||||
.insert(auth_id);
|
.insert(auth_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -509,10 +536,14 @@ impl AclTree {
|
|||||||
for (role, propagate) in roles {
|
for (role, propagate) in roles {
|
||||||
let group = format!("@{}", group);
|
let group = format!("@{}", group);
|
||||||
if *propagate {
|
if *propagate {
|
||||||
role_ug_map1.entry(role).or_insert_with(|| BTreeSet::new())
|
role_ug_map1
|
||||||
|
.entry(role)
|
||||||
|
.or_insert_with(|| BTreeSet::new())
|
||||||
.insert(group);
|
.insert(group);
|
||||||
} else {
|
} else {
|
||||||
role_ug_map0.entry(role).or_insert_with(|| BTreeSet::new())
|
role_ug_map0
|
||||||
|
.entry(role)
|
||||||
|
.or_insert_with(|| BTreeSet::new())
|
||||||
.insert(group);
|
.insert(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -524,11 +555,15 @@ impl AclTree {
|
|||||||
let mut result_map = BTreeMap::new();
|
let mut result_map = BTreeMap::new();
|
||||||
for (item, property_map) in item_property_map {
|
for (item, property_map) in item_property_map {
|
||||||
let item_list = property_map.iter().fold(String::new(), |mut acc, v| {
|
let item_list = property_map.iter().fold(String::new(), |mut acc, v| {
|
||||||
if !acc.is_empty() { acc.push(','); }
|
if !acc.is_empty() {
|
||||||
|
acc.push(',');
|
||||||
|
}
|
||||||
acc.push_str(v);
|
acc.push_str(v);
|
||||||
acc
|
acc
|
||||||
});
|
});
|
||||||
result_map.entry(item_list).or_insert_with(|| BTreeSet::new())
|
result_map
|
||||||
|
.entry(item_list)
|
||||||
|
.or_insert_with(|| BTreeSet::new())
|
||||||
.insert(item.to_string());
|
.insert(item.to_string());
|
||||||
}
|
}
|
||||||
result_map
|
result_map
|
||||||
@ -538,9 +573,13 @@ impl AclTree {
|
|||||||
let uglist_role_map1 = group_by_property_list(&role_ug_map1);
|
let uglist_role_map1 = group_by_property_list(&role_ug_map1);
|
||||||
|
|
||||||
fn role_list(roles: &BTreeSet<String>) -> String {
|
fn role_list(roles: &BTreeSet<String>) -> String {
|
||||||
if roles.contains(ROLE_NAME_NO_ACCESS) { return String::from(ROLE_NAME_NO_ACCESS); }
|
if roles.contains(ROLE_NAME_NO_ACCESS) {
|
||||||
|
return String::from(ROLE_NAME_NO_ACCESS);
|
||||||
|
}
|
||||||
roles.iter().fold(String::new(), |mut acc, v| {
|
roles.iter().fold(String::new(), |mut acc, v| {
|
||||||
if !acc.is_empty() { acc.push(','); }
|
if !acc.is_empty() {
|
||||||
|
acc.push(',');
|
||||||
|
}
|
||||||
acc.push_str(v);
|
acc.push_str(v);
|
||||||
acc
|
acc
|
||||||
})
|
})
|
||||||
@ -548,12 +587,24 @@ impl AclTree {
|
|||||||
|
|
||||||
for (uglist, roles) in &uglist_role_map0 {
|
for (uglist, roles) in &uglist_role_map0 {
|
||||||
let role_list = role_list(roles);
|
let role_list = role_list(roles);
|
||||||
writeln!(w, "acl:0:{}:{}:{}", if path.is_empty() { "/" } else { path }, uglist, role_list)?;
|
writeln!(
|
||||||
|
w,
|
||||||
|
"acl:0:{}:{}:{}",
|
||||||
|
if path.is_empty() { "/" } else { path },
|
||||||
|
uglist,
|
||||||
|
role_list
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uglist, roles) in &uglist_role_map1 {
|
for (uglist, roles) in &uglist_role_map1 {
|
||||||
let role_list = role_list(roles);
|
let role_list = role_list(roles);
|
||||||
writeln!(w, "acl:1:{}:{}:{}", if path.is_empty() { "/" } else { path }, uglist, role_list)?;
|
writeln!(
|
||||||
|
w,
|
||||||
|
"acl:1:{}:{}:{}",
|
||||||
|
if path.is_empty() { "/" } else { path },
|
||||||
|
uglist,
|
||||||
|
role_list
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, child) in node.children.iter() {
|
for (name, child) in node.children.iter() {
|
||||||
@ -569,7 +620,6 @@ impl AclTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_acl_line(&mut self, line: &str) -> Result<(), Error> {
|
fn parse_acl_line(&mut self, line: &str) -> Result<(), Error> {
|
||||||
|
|
||||||
let items: Vec<&str> = line.split(':').collect();
|
let items: Vec<&str> = line.split(':').collect();
|
||||||
|
|
||||||
if items.len() != 5 {
|
if items.len() != 5 {
|
||||||
@ -613,7 +663,7 @@ impl AclTree {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(filename: &Path) -> Result<(Self, [u8;32]), Error> {
|
fn load(filename: &Path) -> Result<(Self, [u8; 32]), Error> {
|
||||||
let mut tree = Self::new();
|
let mut tree = Self::new();
|
||||||
|
|
||||||
let raw = match std::fs::read_to_string(filename) {
|
let raw = match std::fs::read_to_string(filename) {
|
||||||
@ -631,10 +681,16 @@ impl AclTree {
|
|||||||
|
|
||||||
for (linenr, line) in raw.lines().enumerate() {
|
for (linenr, line) in raw.lines().enumerate() {
|
||||||
let line = line.trim();
|
let line = line.trim();
|
||||||
if line.is_empty() { continue; }
|
if line.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if let Err(err) = tree.parse_acl_line(line) {
|
if let Err(err) = tree.parse_acl_line(line) {
|
||||||
bail!("unable to parse acl config {:?}, line {} - {}",
|
bail!(
|
||||||
filename, linenr+1, err);
|
"unable to parse acl config {:?}, line {} - {}",
|
||||||
|
filename,
|
||||||
|
linenr + 1,
|
||||||
|
err
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -646,9 +702,15 @@ impl AclTree {
|
|||||||
let mut tree = Self::new();
|
let mut tree = Self::new();
|
||||||
for (linenr, line) in raw.lines().enumerate() {
|
for (linenr, line) in raw.lines().enumerate() {
|
||||||
let line = line.trim();
|
let line = line.trim();
|
||||||
if line.is_empty() { continue; }
|
if line.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if let Err(err) = tree.parse_acl_line(line) {
|
if let Err(err) = tree.parse_acl_line(line) {
|
||||||
bail!("unable to parse acl config data, line {} - {}", linenr+1, err);
|
bail!(
|
||||||
|
"unable to parse acl config data, line {} - {}",
|
||||||
|
linenr + 1,
|
||||||
|
err
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(tree)
|
Ok(tree)
|
||||||
@ -663,7 +725,6 @@ impl AclTree {
|
|||||||
/// -- user/token is more specific than group at each level
|
/// -- user/token is more specific than group at each level
|
||||||
/// -- roles lower in the tree are more specific than those higher up along the path
|
/// -- roles lower in the tree are more specific than those higher up along the path
|
||||||
pub fn roles(&self, auth_id: &Authid, path: &[&str]) -> HashMap<String, bool> {
|
pub fn roles(&self, auth_id: &Authid, path: &[&str]) -> HashMap<String, bool> {
|
||||||
|
|
||||||
let mut node = &self.root;
|
let mut node = &self.root;
|
||||||
let mut role_map = node.extract_roles(auth_id, path.is_empty());
|
let mut role_map = node.extract_roles(auth_id, path.is_empty());
|
||||||
|
|
||||||
@ -701,7 +762,6 @@ pub fn config() -> Result<(AclTree, [u8; 32]), Error> {
|
|||||||
/// Since the AclTree is used for every API request's permission check, this caching mechanism
|
/// Since the AclTree is used for every API request's permission check, this caching mechanism
|
||||||
/// allows to skip reading and parsing the file again if it is unchanged.
|
/// allows to skip reading and parsing the file again if it is unchanged.
|
||||||
pub fn cached_config() -> Result<Arc<AclTree>, Error> {
|
pub fn cached_config() -> Result<Arc<AclTree>, Error> {
|
||||||
|
|
||||||
struct ConfigCache {
|
struct ConfigCache {
|
||||||
data: Option<Arc<AclTree>>,
|
data: Option<Arc<AclTree>>,
|
||||||
last_mtime: i64,
|
last_mtime: i64,
|
||||||
@ -709,8 +769,11 @@ pub fn cached_config() -> Result<Arc<AclTree>, Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
|
static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(ConfigCache {
|
||||||
ConfigCache { data: None, last_mtime: 0, last_mtime_nsec: 0 });
|
data: None,
|
||||||
|
last_mtime: 0,
|
||||||
|
last_mtime_nsec: 0
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let stat = match nix::sys::stat::stat(ACL_CFG_FILENAME) {
|
let stat = match nix::sys::stat::stat(ACL_CFG_FILENAME) {
|
||||||
@ -719,11 +782,13 @@ pub fn cached_config() -> Result<Arc<AclTree>, Error> {
|
|||||||
Err(err) => bail!("unable to stat '{}' - {}", ACL_CFG_FILENAME, err),
|
Err(err) => bail!("unable to stat '{}' - {}", ACL_CFG_FILENAME, err),
|
||||||
};
|
};
|
||||||
|
|
||||||
{ // limit scope
|
{
|
||||||
|
// limit scope
|
||||||
let cache = CACHED_CONFIG.read().unwrap();
|
let cache = CACHED_CONFIG.read().unwrap();
|
||||||
if let Some(ref config) = cache.data {
|
if let Some(ref config) = cache.data {
|
||||||
if let Some(stat) = stat {
|
if let Some(stat) = stat {
|
||||||
if stat.st_mtime == cache.last_mtime && stat.st_mtime_nsec == cache.last_mtime_nsec {
|
if stat.st_mtime == cache.last_mtime && stat.st_mtime_nsec == cache.last_mtime_nsec
|
||||||
|
{
|
||||||
return Ok(config.clone());
|
return Ok(config.clone());
|
||||||
}
|
}
|
||||||
} else if cache.last_mtime == 0 && cache.last_mtime_nsec == 0 {
|
} else if cache.last_mtime == 0 && cache.last_mtime_nsec == 0 {
|
||||||
@ -768,30 +833,30 @@ pub fn save_config(acl: &AclTree) -> Result<(), Error> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use anyhow::{Error};
|
|
||||||
use super::AclTree;
|
use super::AclTree;
|
||||||
|
use anyhow::Error;
|
||||||
|
|
||||||
use crate::api2::types::Authid;
|
use crate::api2::types::Authid;
|
||||||
|
|
||||||
fn check_roles(
|
fn check_roles(tree: &AclTree, auth_id: &Authid, path: &str, expected_roles: &str) {
|
||||||
tree: &AclTree,
|
|
||||||
auth_id: &Authid,
|
|
||||||
path: &str,
|
|
||||||
expected_roles: &str,
|
|
||||||
) {
|
|
||||||
|
|
||||||
let path_vec = super::split_acl_path(path);
|
let path_vec = super::split_acl_path(path);
|
||||||
let mut roles = tree.roles(auth_id, &path_vec)
|
let mut roles = tree
|
||||||
.iter().map(|(v, _)| v.clone()).collect::<Vec<String>>();
|
.roles(auth_id, &path_vec)
|
||||||
|
.iter()
|
||||||
|
.map(|(v, _)| v.clone())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
roles.sort();
|
roles.sort();
|
||||||
let roles = roles.join(",");
|
let roles = roles.join(",");
|
||||||
|
|
||||||
assert_eq!(roles, expected_roles, "\nat check_roles for '{}' on '{}'", auth_id, path);
|
assert_eq!(
|
||||||
|
roles, expected_roles,
|
||||||
|
"\nat check_roles for '{}' on '{}'",
|
||||||
|
auth_id, path
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_acl_line_compression() {
|
fn test_acl_line_compression() {
|
||||||
|
|
||||||
let tree = AclTree::from_raw(
|
let tree = AclTree::from_raw(
|
||||||
"\
|
"\
|
||||||
acl:0:/store/store2:user1@pbs:Admin\n\
|
acl:0:/store/store2:user1@pbs:Admin\n\
|
||||||
@ -803,20 +868,25 @@ mod test {
|
|||||||
.expect("failed to parse acl tree");
|
.expect("failed to parse acl tree");
|
||||||
|
|
||||||
let mut raw: Vec<u8> = Vec::new();
|
let mut raw: Vec<u8> = Vec::new();
|
||||||
tree.write_config(&mut raw).expect("failed to write acl tree");
|
tree.write_config(&mut raw)
|
||||||
|
.expect("failed to write acl tree");
|
||||||
let raw = std::str::from_utf8(&raw).expect("acl tree is not valid utf8");
|
let raw = std::str::from_utf8(&raw).expect("acl tree is not valid utf8");
|
||||||
|
|
||||||
assert_eq!(raw, "acl:0:/store/store2:user1@pbs,user2@pbs:Admin,DatastoreBackup\n");
|
assert_eq!(
|
||||||
|
raw,
|
||||||
|
"acl:0:/store/store2:user1@pbs,user2@pbs:Admin,DatastoreBackup\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_roles_1() -> Result<(), Error> {
|
fn test_roles_1() -> Result<(), Error> {
|
||||||
|
let tree = AclTree::from_raw(
|
||||||
let tree = AclTree::from_raw(r###"
|
r###"
|
||||||
acl:1:/storage:user1@pbs:Admin
|
acl:1:/storage:user1@pbs:Admin
|
||||||
acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
||||||
acl:1:/storage/store2:user2@pbs:DatastoreBackup
|
acl:1:/storage/store2:user2@pbs:DatastoreBackup
|
||||||
"###)?;
|
"###,
|
||||||
|
)?;
|
||||||
let user1: Authid = "user1@pbs".parse()?;
|
let user1: Authid = "user1@pbs".parse()?;
|
||||||
check_roles(&tree, &user1, "/", "");
|
check_roles(&tree, &user1, "/", "");
|
||||||
check_roles(&tree, &user1, "/storage", "Admin");
|
check_roles(&tree, &user1, "/storage", "Admin");
|
||||||
@ -834,12 +904,13 @@ acl:1:/storage/store2:user2@pbs:DatastoreBackup
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_role_no_access() -> Result<(), Error> {
|
fn test_role_no_access() -> Result<(), Error> {
|
||||||
|
let tree = AclTree::from_raw(
|
||||||
let tree = AclTree::from_raw(r###"
|
r###"
|
||||||
acl:1:/:user1@pbs:Admin
|
acl:1:/:user1@pbs:Admin
|
||||||
acl:1:/storage:user1@pbs:NoAccess
|
acl:1:/storage:user1@pbs:NoAccess
|
||||||
acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
||||||
"###)?;
|
"###,
|
||||||
|
)?;
|
||||||
let user1: Authid = "user1@pbs".parse()?;
|
let user1: Authid = "user1@pbs".parse()?;
|
||||||
check_roles(&tree, &user1, "/", "Admin");
|
check_roles(&tree, &user1, "/", "Admin");
|
||||||
check_roles(&tree, &user1, "/storage", "NoAccess");
|
check_roles(&tree, &user1, "/storage", "NoAccess");
|
||||||
@ -847,11 +918,13 @@ acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
|||||||
check_roles(&tree, &user1, "/storage/store2", "NoAccess");
|
check_roles(&tree, &user1, "/storage/store2", "NoAccess");
|
||||||
check_roles(&tree, &user1, "/system", "Admin");
|
check_roles(&tree, &user1, "/system", "Admin");
|
||||||
|
|
||||||
let tree = AclTree::from_raw(r###"
|
let tree = AclTree::from_raw(
|
||||||
|
r###"
|
||||||
acl:1:/:user1@pbs:Admin
|
acl:1:/:user1@pbs:Admin
|
||||||
acl:0:/storage:user1@pbs:NoAccess
|
acl:0:/storage:user1@pbs:NoAccess
|
||||||
acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
||||||
"###)?;
|
"###,
|
||||||
|
)?;
|
||||||
check_roles(&tree, &user1, "/", "Admin");
|
check_roles(&tree, &user1, "/", "Admin");
|
||||||
check_roles(&tree, &user1, "/storage", "NoAccess");
|
check_roles(&tree, &user1, "/storage", "NoAccess");
|
||||||
check_roles(&tree, &user1, "/storage/store1", "DatastoreBackup");
|
check_roles(&tree, &user1, "/storage/store1", "DatastoreBackup");
|
||||||
@ -863,7 +936,6 @@ acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_role_add_delete() -> Result<(), Error> {
|
fn test_role_add_delete() -> Result<(), Error> {
|
||||||
|
|
||||||
let mut tree = AclTree::new();
|
let mut tree = AclTree::new();
|
||||||
|
|
||||||
let user1: Authid = "user1@pbs".parse()?;
|
let user1: Authid = "user1@pbs".parse()?;
|
||||||
@ -887,7 +959,6 @@ acl:1:/storage/store1:user1@pbs:DatastoreBackup
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_no_access_overwrite() -> Result<(), Error> {
|
fn test_no_access_overwrite() -> Result<(), Error> {
|
||||||
|
|
||||||
let mut tree = AclTree::new();
|
let mut tree = AclTree::new();
|
||||||
|
|
||||||
let user1: Authid = "user1@pbs".parse()?;
|
let user1: Authid = "user1@pbs".parse()?;
|
||||||
|
Loading…
Reference in New Issue
Block a user