Integrity Verification & BTreeSet
This commit is contained in:
parent
ef9f0fd31c
commit
f4776e3c8b
36
Cargo.lock
generated
36
Cargo.lock
generated
@ -60,9 +60,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@ -75,33 +75,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.8"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
@ -267,9 +267,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-jwt-login"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "sparse+https://crates.esteil.dedyn.io/api/v1/crates/"
|
||||
checksum = "b45378650bae1dfcc3ed411ffc7b2bfe5977f6c3de5f140993b1ee2979cccc4c"
|
||||
checksum = "7866571ffba399da64ce58fab21c6eb0ad76a312ec7d546d5dfada2435ab26ea"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"chrono",
|
||||
@ -322,9 +322,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.7.3"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
|
||||
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
@ -489,9 +489,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
@ -1187,9 +1187,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.13"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8"
|
||||
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
|
@ -27,7 +27,7 @@ colored = "3.0.0"
|
||||
uuid = { version = "1.17.0", features = ["v4"] }
|
||||
blake3 = "1.1.18"
|
||||
minisign = "0.7.9"
|
||||
axum-jwt-login = { version = "0.1.1", registry = "kellnr" }
|
||||
axum-jwt-login = { version = "0.1.2", registry = "kellnr" }
|
||||
rust-argon2 = "2.1.0"
|
||||
rand = "0.9.1"
|
||||
ldap3 = "0.11.5"
|
||||
|
@ -17,13 +17,13 @@ pub struct IntegrityVerifier {
|
||||
}
|
||||
|
||||
impl IntegrityVerifier {
|
||||
pub fn new() -> Result<Self, ApiError> {
|
||||
Ok(Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mac: Hasher::new_derive_key(include_str!("./verification_secret")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign<T>(&self, data: &T) -> Result<Hash, ApiError>
|
||||
pub fn sign<T>(&self, data: &T) -> Hash
|
||||
where
|
||||
T: CustomHash,
|
||||
{
|
||||
@ -33,7 +33,7 @@ impl IntegrityVerifier {
|
||||
let signature = mac.finalize();
|
||||
mac.reset();
|
||||
|
||||
Ok(signature)
|
||||
signature
|
||||
}
|
||||
|
||||
pub fn verify<T>(&self, data: &T, signature: Hash) -> Result<(), ApiError>
|
||||
@ -73,14 +73,14 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
let verifier = IntegrityVerifier::new().unwrap();
|
||||
let verifier = IntegrityVerifier::new();
|
||||
|
||||
let mut item = TestData {
|
||||
one: 24,
|
||||
two: "MyTesting".to_string(),
|
||||
};
|
||||
|
||||
let signature = verifier.sign(&item).unwrap();
|
||||
let signature = verifier.sign(&item);
|
||||
assert!(verifier.verify(&item, signature).is_ok());
|
||||
|
||||
item.two = "Wrong".to_string();
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use axum_jwt_login::ApiKey as ApiKeyTrait;
|
||||
use blake3::{Hash, Hasher};
|
||||
@ -59,7 +59,7 @@ impl ApiKey {
|
||||
pub fn create(
|
||||
name: &str,
|
||||
requires_auth: bool,
|
||||
permissions: HashSet<Permission>,
|
||||
permissions: BTreeSet<Permission>,
|
||||
config: &Configuration,
|
||||
) -> Result<(String, Self), ApiError> {
|
||||
// create uuid
|
||||
@ -128,14 +128,14 @@ impl ApiKeyTrait for ApiKey {
|
||||
fn requires_user_auth(&self) -> bool {
|
||||
self.auth_required
|
||||
}
|
||||
fn get_permissions(&self) -> HashSet<Self::Permission> {
|
||||
fn get_permissions(&self) -> BTreeSet<Self::Permission> {
|
||||
self.permissions.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::collections::HashSet;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::config::Configuration;
|
||||
|
||||
@ -148,7 +148,7 @@ mod test {
|
||||
..Default::default()
|
||||
};
|
||||
let (key, api_key) =
|
||||
ApiKey::create("name", true, HashSet::new(), &config).expect("Error creating API Key");
|
||||
ApiKey::create("name", true, BTreeSet::new(), &config).expect("Error creating API Key");
|
||||
println!("{key}, {api_key:?}");
|
||||
assert_eq!(Ok(()), api_key.validate(&key));
|
||||
assert_ne!(Ok(()), api_key.validate("124"));
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
collections::{BTreeSet, HashMap, HashSet},
|
||||
fmt,
|
||||
};
|
||||
|
||||
@ -12,7 +12,10 @@ use ts_rs::TS;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::{
|
||||
api::routes::{departments::models::ShortDepartment, models::Status},
|
||||
api::{
|
||||
backend::integrity_verification::CustomHash,
|
||||
routes::{departments::models::ShortDepartment, models::Status},
|
||||
},
|
||||
errors::ApiError,
|
||||
};
|
||||
|
||||
@ -104,6 +107,17 @@ impl fmt::Debug for User {
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomHash for User {
|
||||
fn updater(&self, hasher: &mut blake3::Hasher) {
|
||||
hasher.update(self.user_id.as_bytes());
|
||||
hasher.update(self.name.as_bytes());
|
||||
hasher.update(self.surname.as_bytes());
|
||||
for permission in &self.permissions.0 {
|
||||
hasher.update(permission.to_string().as_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserPermissions for User {
|
||||
type Error = ApiError;
|
||||
type Permission = Permission;
|
||||
@ -113,10 +127,10 @@ impl UserPermissions for User {
|
||||
self.user_id.clone()
|
||||
}
|
||||
|
||||
fn get_user_permissions(&self) -> Result<HashSet<Self::Permission>, Self::Error> {
|
||||
fn get_user_permissions(&self) -> Result<BTreeSet<Self::Permission>, Self::Error> {
|
||||
Ok(self.permissions.0.iter().cloned().collect())
|
||||
}
|
||||
fn get_group_permissions(&self) -> Result<HashSet<Self::Permission>, Self::Error> {
|
||||
fn get_group_permissions(&self) -> Result<BTreeSet<Self::Permission>, Self::Error> {
|
||||
Ok(self.group_permissions.0.iter().cloned().collect())
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
use std::{collections::HashSet, convert::Infallible, fmt::Display, str::FromStr};
|
||||
use std::{collections::BTreeSet, convert::Infallible, fmt::Display, str::FromStr};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::EnumString;
|
||||
use ts_rs::TS;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, TS, ToSchema)]
|
||||
#[derive(
|
||||
Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord, TS, ToSchema,
|
||||
)]
|
||||
pub enum Permission {
|
||||
Read(PermissionDetail),
|
||||
Write(PermissionDetail),
|
||||
@ -52,6 +54,8 @@ impl Display for Permission {
|
||||
Deserialize,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
EnumString,
|
||||
strum::Display,
|
||||
@ -70,17 +74,17 @@ pub enum PermissionDetail {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS, ToSchema)]
|
||||
pub struct PermissionContainer(pub HashSet<Permission>);
|
||||
pub struct PermissionContainer(pub BTreeSet<Permission>);
|
||||
|
||||
impl From<Option<Vec<String>>> for PermissionContainer {
|
||||
fn from(value: Option<Vec<String>>) -> Self {
|
||||
let set = match value {
|
||||
Some(values) => HashSet::from_iter(
|
||||
Some(values) => BTreeSet::from_iter(
|
||||
values
|
||||
.iter()
|
||||
.map(|s| Permission::from_str(s).unwrap_or(Permission::None)),
|
||||
),
|
||||
None => HashSet::new(),
|
||||
None => BTreeSet::new(),
|
||||
};
|
||||
|
||||
Self(set)
|
||||
@ -89,9 +93,24 @@ impl From<Option<Vec<String>>> for PermissionContainer {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::str::FromStr;
|
||||
use std::{
|
||||
collections::{BTreeSet, HashSet},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use crate::api::routes::users::permissions::PermissionDetail;
|
||||
use chrono::Local;
|
||||
|
||||
use crate::api::{
|
||||
backend::integrity_verification::IntegrityVerifier,
|
||||
routes::{
|
||||
departments::models::ShortDepartment,
|
||||
models::Status,
|
||||
users::{
|
||||
models::{DepartmentContainer, GroupContainer, User},
|
||||
permissions::{PermissionContainer, PermissionDetail},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use super::Permission;
|
||||
|
||||
@ -103,4 +122,41 @@ mod test {
|
||||
let parsed = Permission::from_str("Write:Users").unwrap();
|
||||
assert_eq!(parsed, Permission::Write(PermissionDetail::Users));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_integrity_validation() {
|
||||
let mut user = User {
|
||||
user_id: "id".to_string(),
|
||||
active_directory_auth: false,
|
||||
password: "Some PW".to_string(),
|
||||
name: "Clark".to_string(),
|
||||
surname: "Kent".to_string(),
|
||||
email: "Superman@heros.com".to_string(),
|
||||
department: DepartmentContainer(Some(ShortDepartment {
|
||||
id: 1,
|
||||
name: "Some name".to_string(),
|
||||
description: None,
|
||||
})),
|
||||
groups: GroupContainer(HashSet::new()),
|
||||
group_permissions: PermissionContainer(BTreeSet::new()),
|
||||
permissions: PermissionContainer(BTreeSet::from_iter([Permission::Read(
|
||||
PermissionDetail::Departments,
|
||||
)])),
|
||||
status_flag: Status::Active,
|
||||
creation_date: Local::now().naive_local(),
|
||||
last_change: Local::now().naive_local(),
|
||||
};
|
||||
|
||||
let verifier = IntegrityVerifier::new();
|
||||
|
||||
let hash = verifier.sign(&user);
|
||||
|
||||
assert!(verifier.verify(&user, hash).is_ok());
|
||||
|
||||
user.permissions
|
||||
.0
|
||||
.insert(Permission::Write(PermissionDetail::Units));
|
||||
|
||||
assert!(verifier.verify(&user, hash).is_err());
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{collections::HashSet, path::PathBuf};
|
||||
use std::{collections::BTreeSet, path::PathBuf};
|
||||
|
||||
use clap::{command, Parser, Subcommand};
|
||||
use colored::Colorize;
|
||||
@ -134,7 +134,7 @@ impl Cli {
|
||||
} => {
|
||||
// create API Key
|
||||
let (key_secret, key) =
|
||||
ApiKey::create(name, *requires_auth, HashSet::new(), config)
|
||||
ApiKey::create(name, *requires_auth, BTreeSet::new(), config)
|
||||
.change_context(AppError)?;
|
||||
|
||||
// Add API Key to Database
|
||||
|
Loading…
Reference in New Issue
Block a user