1
use crate::core::{common::UnixUser, protocol::request_validation::GroupDenylist};
2
use nix::unistd::Group;
3
use sqlx::prelude::*;
4

            
5
/// This function retrieves the groups of a user, filtering out any groups
6
/// that are present in the provided denylist.
7
1
pub fn get_user_filtered_groups(user: &UnixUser, group_denylist: &GroupDenylist) -> Vec<String> {
8
1
    user.groups
9
1
        .iter()
10
1
        .cloned()
11
2
        .filter_map(|group_name| {
12
2
            match Group::from_name(&group_name) {
13
                Ok(Some(group)) => {
14
                    if group_denylist.contains(&group.gid.as_raw()) {
15
                        None
16
                    } else {
17
                        Some(group.name)
18
                    }
19
                }
20
                // NOTE: allow non-existing groups to pass through the filter
21
2
                _ => Some(group_name),
22
            }
23
2
        })
24
1
        .collect()
25
1
}
26

            
27
/// This function creates a regex that matches items (users, databases)
28
/// that belong to the user or any of the user's groups.
29
1
pub fn create_user_group_matching_regex(user: &UnixUser, group_denylist: &GroupDenylist) -> String {
30
1
    let filtered_groups = get_user_filtered_groups(user, group_denylist);
31
1
    if filtered_groups.is_empty() {
32
        format!("{}_.+", user.username)
33
    } else {
34
1
        format!("({}|{})_.+", user.username, filtered_groups.join("|"))
35
    }
36
1
}
37

            
38
/// Some mysql versions with some collations mark some columns as binary fields,
39
/// which in the current version of sqlx is not parsable as string.
40
/// See: <https://github.com/launchbadge/sqlx/issues/3387>
41
#[inline]
42
pub fn try_get_with_binary_fallback(
43
    row: &sqlx::mysql::MySqlRow,
44
    column: &str,
45
) -> Result<String, sqlx::Error> {
46
    row.try_get(column).or_else(|_| {
47
        row.try_get::<Vec<u8>, _>(column)
48
            .map(|v| String::from_utf8_lossy(&v).to_string())
49
    })
50
}
51

            
52
#[cfg(test)]
53
mod tests {
54
    use super::*;
55
    use regex::Regex;
56

            
57
    #[test]
58
1
    fn test_create_user_group_matching_regex() {
59
1
        let user = UnixUser {
60
1
            username: "user".to_owned(),
61
1
            groups: vec!["group1".to_owned(), "group2".to_owned()],
62
1
        };
63

            
64
1
        let regex = create_user_group_matching_regex(&user, &GroupDenylist::new());
65
1
        println!("Generated regex: {}", regex);
66
1
        let re = Regex::new(&regex).unwrap();
67

            
68
1
        assert!(re.is_match("user_something"));
69
1
        assert!(re.is_match("group1_something"));
70
1
        assert!(re.is_match("group2_something"));
71

            
72
1
        assert!(!re.is_match("other_something"));
73
1
        assert!(!re.is_match("user"));
74
1
        assert!(!re.is_match("usersomething"));
75
1
    }
76
}