1
use crate::core::{
2
    common::UnixUser,
3
    protocol::request_validation::{NameValidationError, OwnerValidationError},
4
};
5

            
6
const MAX_NAME_LENGTH: usize = 64;
7

            
8
35
pub fn validate_name(name: &str) -> Result<(), NameValidationError> {
9
35
    if name.is_empty() {
10
1
        Err(NameValidationError::EmptyString)
11
34
    } else if name.len() > MAX_NAME_LENGTH {
12
1
        Err(NameValidationError::TooLong)
13
33
    } else if !name
14
33
        .chars()
15
157
        .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
16
    {
17
29
        Err(NameValidationError::InvalidCharacters)
18
    } else {
19
4
        Ok(())
20
    }
21
35
}
22

            
23
pub fn validate_ownership_by_unix_user(
24
    name: &str,
25
    user: &UnixUser,
26
) -> Result<(), OwnerValidationError> {
27
    let prefixes = std::iter::once(user.username.to_owned())
28
        .chain(user.groups.iter().cloned())
29
        .collect::<Vec<String>>();
30

            
31
    validate_ownership_by_prefixes(name, &prefixes)
32
}
33

            
34
/// Core logic for validating the ownership of a database name.
35
/// This function checks if the given name matches any of the given prefixes.
36
/// These prefixes will in most cases be the user's unix username and any
37
/// unix groups the user is a member of.
38
6
pub fn validate_ownership_by_prefixes(
39
6
    name: &str,
40
6
    prefixes: &[String],
41
6
) -> Result<(), OwnerValidationError> {
42
6
    if name.is_empty() {
43
1
        return Err(OwnerValidationError::StringEmpty);
44
5
    }
45

            
46
5
    if prefixes
47
5
        .iter()
48
10
        .filter(|p| name.starts_with(&(p.to_string() + "_")))
49
5
        .collect::<Vec<_>>()
50
5
        .is_empty()
51
    {
52
1
        return Err(OwnerValidationError::NoMatch);
53
4
    };
54

            
55
4
    Ok(())
56
6
}
57

            
58
#[inline]
59
1
pub fn quote_literal(s: &str) -> String {
60
1
    format!("'{}'", s.replace('\'', r"\'"))
61
1
}
62

            
63
#[inline]
64
1
pub fn quote_identifier(s: &str) -> String {
65
1
    format!("`{}`", s.replace('`', r"\`"))
66
1
}
67

            
68
#[cfg(test)]
69
mod tests {
70
    use super::*;
71
    #[test]
72
1
    fn test_quote_literal() {
73
1
        let payload = "' OR 1=1 --";
74
1
        assert_eq!(quote_literal(payload), r#"'\' OR 1=1 --'"#);
75
1
    }
76

            
77
    #[test]
78
1
    fn test_quote_identifier() {
79
1
        let payload = "` OR 1=1 --";
80
1
        assert_eq!(quote_identifier(payload), r#"`\` OR 1=1 --`"#);
81
1
    }
82

            
83
    #[test]
84
1
    fn test_validate_name() {
85
1
        assert_eq!(validate_name(""), Err(NameValidationError::EmptyString));
86
1
        assert_eq!(validate_name("abcdefghijklmnopqrstuvwxyz"), Ok(()));
87
1
        assert_eq!(validate_name("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), Ok(()));
88
1
        assert_eq!(validate_name("0123456789_-"), Ok(()));
89

            
90
29
        for c in "\n\t\r !@#$%^&*()+=[]{}|;:,.<>?/".chars() {
91
29
            assert_eq!(
92
29
                validate_name(&c.to_string()),
93
                Err(NameValidationError::InvalidCharacters)
94
            );
95
        }
96

            
97
1
        assert_eq!(validate_name(&"a".repeat(MAX_NAME_LENGTH)), Ok(()));
98

            
99
1
        assert_eq!(
100
1
            validate_name(&"a".repeat(MAX_NAME_LENGTH + 1)),
101
            Err(NameValidationError::TooLong)
102
        );
103
1
    }
104

            
105
    #[test]
106
1
    fn test_validate_owner_by_prefixes() {
107
1
        let prefixes = vec!["user".to_string(), "group".to_string()];
108

            
109
1
        assert_eq!(
110
1
            validate_ownership_by_prefixes("", &prefixes),
111
            Err(OwnerValidationError::StringEmpty)
112
        );
113

            
114
1
        assert_eq!(
115
1
            validate_ownership_by_prefixes("user_testdb", &prefixes),
116
            Ok(())
117
        );
118
1
        assert_eq!(
119
1
            validate_ownership_by_prefixes("group_testdb", &prefixes),
120
            Ok(())
121
        );
122
1
        assert_eq!(
123
1
            validate_ownership_by_prefixes("group_test_db", &prefixes),
124
            Ok(())
125
        );
126
1
        assert_eq!(
127
1
            validate_ownership_by_prefixes("group_test-db", &prefixes),
128
            Ok(())
129
        );
130

            
131
1
        assert_eq!(
132
1
            validate_ownership_by_prefixes("nonexistent_testdb", &prefixes),
133
            Err(OwnerValidationError::NoMatch)
134
        );
135
1
    }
136
}