Skip to main content

kmp_application/queries/
validate_scope.rs

1use std::collections::BTreeSet;
2
3use crate::queries::QueryApplicationService;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct ScopeValidation {
7    pub allowed: bool,
8    pub required_scopes: Vec<String>,
9    pub provided_scopes: Vec<String>,
10    pub missing_scopes: Vec<String>,
11    pub extra_scopes: Vec<String>,
12    pub reason: String,
13    pub diagnostics: Vec<String>,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ValidateScopeQuery {
18    pub required_scopes: Vec<String>,
19    pub provided_scopes: Vec<String>,
20}
21
22#[derive(Debug)]
23pub struct ValidateScopeUseCase;
24
25impl ValidateScopeUseCase {
26    pub fn execute(required_scopes: &[String], provided_scopes: &[String]) -> ScopeValidation {
27        let required = dedupe_scopes(required_scopes);
28        let provided = dedupe_scopes(provided_scopes);
29
30        let required_set: BTreeSet<_> = required.iter().cloned().collect();
31        let provided_set: BTreeSet<_> = provided.iter().cloned().collect();
32
33        let missing_scopes = required_set
34            .difference(&provided_set)
35            .cloned()
36            .collect::<Vec<_>>();
37        let extra_scopes = provided_set
38            .difference(&required_set)
39            .cloned()
40            .collect::<Vec<_>>();
41        let allowed = missing_scopes.is_empty() && extra_scopes.is_empty();
42        let reason = if allowed {
43            "scope validation passed".to_string()
44        } else {
45            "scope validation failed".to_string()
46        };
47
48        ScopeValidation {
49            allowed,
50            required_scopes: required,
51            provided_scopes: provided,
52            missing_scopes,
53            extra_scopes,
54            reason,
55            diagnostics: Vec::new(),
56        }
57    }
58}
59
60impl<G, D, S> QueryApplicationService<G, D, S> {
61    pub fn validate_scope(&self, query: ValidateScopeQuery) -> ScopeValidation {
62        ValidateScopeUseCase::execute(&query.required_scopes, &query.provided_scopes)
63    }
64}
65
66pub fn dedupe_scopes(scopes: &[String]) -> Vec<String> {
67    scopes
68        .iter()
69        .map(|scope| scope.trim())
70        .filter(|scope| !scope.is_empty())
71        .map(ToOwned::to_owned)
72        .collect::<BTreeSet<_>>()
73        .into_iter()
74        .collect()
75}