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