Validate user config
This commit is contained in:
parent
31833a9f47
commit
0f5bfa3d5e
4 changed files with 122 additions and 6 deletions
7
backend/Cargo.lock
generated
7
backend/Cargo.lock
generated
|
|
@ -362,6 +362,12 @@ version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bimap"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
|
@ -2563,6 +2569,7 @@ dependencies = [
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"axum-jsonschema",
|
"axum-jsonschema",
|
||||||
"axum_typed_multipart",
|
"axum_typed_multipart",
|
||||||
|
"bimap",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"clap-verbosity-flag",
|
"clap-verbosity-flag",
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,17 @@ server:
|
||||||
response_timeout_seconds: 60
|
response_timeout_seconds: 60
|
||||||
|
|
||||||
users:
|
users:
|
||||||
user_tokens:
|
user_configs:
|
||||||
- name: admin
|
- name: admin
|
||||||
token: test-token-change-me
|
token: test-token-change-me
|
||||||
vault_access:
|
vault_access:
|
||||||
type: allow_access_to_all
|
type: allow_access_to_all
|
||||||
|
|
||||||
|
- name: other-admin
|
||||||
|
token: test-token-change-me2
|
||||||
|
vault_access:
|
||||||
|
type: allow_access_to_all
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
token: other-test-token
|
token: other-test-token
|
||||||
vault_access:
|
vault_access:
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ clap = { version = "4.5.38", features = ["derive"] }
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
clap-verbosity-flag = "3.0.3"
|
clap-verbosity-flag = "3.0.3"
|
||||||
|
bimap = "0.6.3"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,47 @@
|
||||||
|
use bimap::BiHashMap;
|
||||||
use rand::{Rng, distr::Alphanumeric, rng};
|
use rand::{Rng, distr::Alphanumeric, rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize, de::Error};
|
||||||
|
|
||||||
use crate::app_state::database::models::VaultId;
|
use crate::app_state::database::models::VaultId;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct UserConfig {
|
pub struct UserConfig {
|
||||||
#[serde(default = "default_users")]
|
#[serde(default = "default_users", deserialize_with = "validate_users")]
|
||||||
pub user_tokens: Vec<User>,
|
pub user_configs: Vec<User>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_users<'de, D>(deserializer: D) -> Result<Vec<User>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let users = Vec::<User>::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
let mut user_token_map = BiHashMap::new();
|
||||||
|
for user in &users {
|
||||||
|
if let Some(existing_name) = user_token_map.get_by_right(&user.token) {
|
||||||
|
return Err(D::Error::custom(format!(
|
||||||
|
"Duplicate user token found: '{}' for users '{}' and '{}'. User tokens must be \
|
||||||
|
unique.",
|
||||||
|
user.token, existing_name, user.name
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if user_token_map.contains_left(&user.name) {
|
||||||
|
return Err(D::Error::custom(format!(
|
||||||
|
"Duplicate user name found: '{}'. User names must be unique.",
|
||||||
|
user.name
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
user_token_map.insert(user.name.clone(), user.token.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(users)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserConfig {
|
impl UserConfig {
|
||||||
pub fn get_user(&self, token: &str) -> Option<&User> {
|
pub fn get_user(&self, token: &str) -> Option<&User> {
|
||||||
self.user_tokens.iter().find(|u| u.token == token)
|
self.user_configs.iter().find(|u| u.token == token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,7 +55,7 @@ pub struct User {
|
||||||
impl Default for UserConfig {
|
impl Default for UserConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
user_tokens: default_users(),
|
user_configs: default_users(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,3 +89,76 @@ pub fn get_random_token() -> String {
|
||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validate_users_unique_names_and_tokens() {
|
||||||
|
let config_json = json!({
|
||||||
|
"user_configs": [
|
||||||
|
{
|
||||||
|
"name": "alice",
|
||||||
|
"token": "token1",
|
||||||
|
"vault_access": { "type": "allow_access_to_all" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bob",
|
||||||
|
"token": "token2",
|
||||||
|
"vault_access": { "type": "allow_access_to_all" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
let config: Result<UserConfig, _> = serde_json::from_value(config_json);
|
||||||
|
assert!(config.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validate_users_duplicate_names() {
|
||||||
|
let config_json = json!({
|
||||||
|
"user_configs": [
|
||||||
|
{
|
||||||
|
"name": "alice",
|
||||||
|
"token": "token1",
|
||||||
|
"vault_access": { "type": "allow_access_to_all" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "alice",
|
||||||
|
"token": "token2",
|
||||||
|
"vault_access": { "type": "allow_access_to_all" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
let config: Result<UserConfig, _> = serde_json::from_value(config_json);
|
||||||
|
assert!(config.is_err());
|
||||||
|
let err = config.unwrap_err().to_string();
|
||||||
|
assert!(err.contains("Duplicate user name found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validate_users_duplicate_tokens() {
|
||||||
|
let config_json = json!({
|
||||||
|
"user_configs": [
|
||||||
|
{
|
||||||
|
"name": "alice",
|
||||||
|
"token": "token1",
|
||||||
|
"vault_access": { "type": "allow_access_to_all" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bob",
|
||||||
|
"token": "token1",
|
||||||
|
"vault_access": { "type": "allow_access_to_all" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
let config: Result<UserConfig, _> = serde_json::from_value(config_json);
|
||||||
|
assert!(config.is_err());
|
||||||
|
let err = config.unwrap_err().to_string();
|
||||||
|
assert!(err.contains("Duplicate user token found"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue