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