Lines
53.85 %
Functions
33.33 %
use std::{collections::HashSet, path::Path, str::Lines};
use anyhow::Context;
use nix::unistd::Group;
use crate::core::{
common::UnixUser,
protocol::{
CheckAuthorizationError,
request_validation::{GroupDenylist, validate_db_or_user_request},
},
types::DbOrUser,
};
pub async fn check_authorization(
dbs_or_users: &[DbOrUser],
unix_user: &UnixUser,
group_denylist: &GroupDenylist,
) -> std::collections::BTreeMap<DbOrUser, Result<(), CheckAuthorizationError>> {
dbs_or_users
.iter()
.cloned()
.map(|db_or_user| {
let result = validate_db_or_user_request(&db_or_user, unix_user, group_denylist)
.map_err(CheckAuthorizationError);
(db_or_user, result)
})
.collect()
}
/// Reads and parses a group denylist file, returning a set of GUIDs
///
/// The format of the denylist file is expected to be one group name or GID per line.
/// Lines starting with '#' are treated as comments and ignored.
/// Empty lines are also ignored.
/// Each line looks like one of the following:
/// - `gid:1001`
/// - `group:admins`
pub fn read_and_parse_group_denylist(denylist_path: &Path) -> anyhow::Result<GroupDenylist> {
let content = std::fs::read_to_string(denylist_path)
.context(format!("Failed to read denylist file at {denylist_path:?}"))?;
let lines = content.lines();
let groups = parse_group_denylist(denylist_path, lines);
Ok(groups)
fn parse_group_denylist(denylist_path: &Path, lines: Lines) -> GroupDenylist {
let mut groups = HashSet::<u32>::new();
for (line_number, line) in lines.enumerate() {
let trimmed_line = if let Some(comment_start) = line.find('#') {
&line[..comment_start]
} else {
line
.trim();
if trimmed_line.is_empty() {
continue;
let parts: Vec<&str> = trimmed_line.splitn(2, ':').collect();
if parts.len() != 2 {
tracing::warn!(
"Invalid format in denylist file at {:?} on line {}: {}",
denylist_path,
line_number + 1,
);
match parts[0] {
"gid" => {
let gid: u32 = match parts[1].parse() {
Ok(gid) => gid,
Err(err) => {
"Invalid GID '{}' in denylist file at {:?} on line {}: {}",
parts[1],
err
let group = match Group::from_gid(nix::unistd::Gid::from_raw(gid)) {
Ok(Some(g)) => g,
Ok(None) => {
"No group found for GID {} in denylist file at {:?} on line {}",
gid,
line_number + 1
"Failed to get group for GID {} in denylist file at {:?} on line {}: {}",
groups.insert(group.gid.as_raw());
"group" => match Group::from_name(parts[1]) {
Ok(Some(group)) => {
"No group found for name '{}' in denylist file at {:?} on line {}",
"Failed to get group for name '{}' in denylist file at {:?} on line {}: {}",
_ => {
"Invalid prefix '{}' in denylist file at {:?} on line {}: {}",
parts[0],
groups
#[cfg(test)]
mod tests {
use indoc::indoc;
use super::*;
#[test]
fn test_parse_group_denylist() {
let denylist_content = indoc! {"
# Valid entries
gid:0 # This is usually the 'root' group
group:root # This is also the 'root' group, should deduplicate
# Invalid entries
invalid_line
gid:not_a_number
group:nonexistent_group
"};
let lines = denylist_content.lines();
let group_denylist = parse_group_denylist(Path::new("test_denylist"), lines);
assert_eq!(group_denylist.len(), 1);
assert!(group_denylist.contains(&0));