Skip to main content

swf_core/validation/
authentication.rs

1use super::enum_validators::{
2    validate_oauth2_client_auth_method, validate_oauth2_grant_type,
3    validate_oauth2_request_encoding,
4};
5use super::one_of_validators::validate_auth_policy_one_of;
6use super::{ValidationResult, ValidationRule};
7use crate::models::authentication::{
8    AuthenticationPolicyDefinition, BasicAuthenticationSchemeDefinition,
9    BearerAuthenticationSchemeDefinition, DigestAuthenticationSchemeDefinition,
10    OAuth2AuthenticationClientDefinition, OAuth2AuthenticationEndpointsDefinition,
11    OAuth2AuthenticationRequestDefinition, OAuth2AuthenticationSchemeDefinition,
12    OpenIDConnectSchemeDefinition,
13};
14
15/// Validates a credential-based auth scheme (Basic or Digest) for mutual exclusivity.
16/// Must have either `use` (secret reference) OR username/password, not both.
17fn validate_credentials_auth(
18    scheme_name: &str,
19    use_: &Option<String>,
20    username: &Option<String>,
21    password: &Option<String>,
22    prefix: &str,
23    result: &mut ValidationResult,
24) {
25    let has_use = use_.as_ref().is_some_and(|s| !s.is_empty());
26    let has_credentials = username.as_ref().is_some_and(|s| !s.is_empty())
27        || password.as_ref().is_some_and(|s| !s.is_empty());
28    if has_use && has_credentials {
29        result.add_error(
30            &format!("{}.{}", prefix, scheme_name),
31            ValidationRule::MutualExclusion,
32            &format!(
33                "{} auth: 'use' and username/password are mutually exclusive",
34                scheme_name
35            ),
36        );
37    }
38}
39
40/// Validates a Basic authentication scheme for mutual exclusivity
41pub fn validate_basic_auth(
42    basic: &BasicAuthenticationSchemeDefinition,
43    prefix: &str,
44    result: &mut ValidationResult,
45) {
46    validate_credentials_auth(
47        "basic",
48        &basic.use_,
49        &basic.username,
50        &basic.password,
51        prefix,
52        result,
53    );
54}
55
56/// Validates a Bearer authentication scheme for mutual exclusivity
57/// Bearer auth must have either `use` (secret reference) OR token, not both
58pub fn validate_bearer_auth(
59    bearer: &BearerAuthenticationSchemeDefinition,
60    prefix: &str,
61    result: &mut ValidationResult,
62) {
63    let has_use = bearer.use_.as_ref().is_some_and(|s| !s.is_empty());
64    let has_token = bearer.token.as_ref().is_some_and(|s| !s.is_empty());
65    if has_use && has_token {
66        result.add_error(
67            &format!("{}.bearer", prefix),
68            ValidationRule::MutualExclusion,
69            "bearer auth: 'use' and token are mutually exclusive",
70        );
71    }
72}
73
74/// Validates a Digest authentication scheme for mutual exclusivity
75pub fn validate_digest_auth(
76    digest: &DigestAuthenticationSchemeDefinition,
77    prefix: &str,
78    result: &mut ValidationResult,
79) {
80    validate_credentials_auth(
81        "digest",
82        &digest.use_,
83        &digest.username,
84        &digest.password,
85        prefix,
86        result,
87    );
88}
89
90/// Shared validation for OAuth2-like authentication schemes (OAuth2 and OIDC)
91#[allow(clippy::too_many_arguments)]
92fn validate_oauth2_like_auth(
93    scheme_name: &str,
94    use_: &Option<String>,
95    authority: &Option<String>,
96    grant: &Option<String>,
97    client: &Option<OAuth2AuthenticationClientDefinition>,
98    endpoints: &Option<OAuth2AuthenticationEndpointsDefinition>,
99    scopes: &Option<Vec<String>>,
100    audiences: &Option<Vec<String>>,
101    issuers: &Option<Vec<String>>,
102    request: &Option<OAuth2AuthenticationRequestDefinition>,
103    prefix: &str,
104    result: &mut ValidationResult,
105) {
106    let has_use = use_.as_ref().is_some_and(|s| !s.is_empty());
107    let has_properties = authority.as_ref().is_some_and(|s| !s.is_empty())
108        || grant.as_ref().is_some_and(|s| !s.is_empty())
109        || client.is_some()
110        || endpoints.is_some()
111        || scopes.is_some()
112        || audiences.is_some()
113        || issuers.is_some();
114
115    if has_use && has_properties {
116        result.add_error(
117            &format!("{}.{}", prefix, scheme_name),
118            ValidationRule::MutualExclusion,
119            &format!(
120                "{} auth: 'use' and inline properties are mutually exclusive",
121                scheme_name
122            ),
123        );
124    }
125    if !has_use && !has_properties {
126        result.add_error(
127            &format!("{}.{}", prefix, scheme_name),
128            ValidationRule::Required,
129            &format!(
130                "{} auth: either 'use' or inline properties must be set",
131                scheme_name
132            ),
133        );
134    }
135    if let Some(ref grant) = grant {
136        validate_oauth2_grant_type(grant, &format!("{}.{}", prefix, scheme_name), result);
137    }
138    if let Some(ref client) = client {
139        if let Some(ref auth_method) = client.authentication {
140            validate_oauth2_client_auth_method(
141                auth_method,
142                &format!("{}.{}", prefix, scheme_name),
143                result,
144            );
145        }
146    }
147    if let Some(ref request) = request {
148        validate_oauth2_request_encoding(
149            &request.encoding,
150            &format!("{}.{}", prefix, scheme_name),
151            result,
152        );
153    }
154}
155
156/// Validates an OAuth2 authentication scheme for mutual exclusivity
157pub fn validate_oauth2_auth(
158    oauth2: &OAuth2AuthenticationSchemeDefinition,
159    prefix: &str,
160    result: &mut ValidationResult,
161) {
162    validate_oauth2_like_auth(
163        "oauth2",
164        &oauth2.use_,
165        &oauth2.authority,
166        &oauth2.grant,
167        &oauth2.client,
168        &oauth2.endpoints,
169        &oauth2.scopes,
170        &oauth2.audiences,
171        &oauth2.issuers,
172        &oauth2.request,
173        prefix,
174        result,
175    );
176}
177
178/// Validates an OIDC authentication scheme for mutual exclusivity
179pub fn validate_oidc_auth(
180    oidc: &OpenIDConnectSchemeDefinition,
181    prefix: &str,
182    result: &mut ValidationResult,
183) {
184    validate_oauth2_like_auth(
185        "oidc",
186        &oidc.use_,
187        &oidc.authority,
188        &oidc.grant,
189        &oidc.client,
190        &None, // OIDC doesn't have endpoints
191        &oidc.scopes,
192        &oidc.audiences,
193        &oidc.issuers,
194        &oidc.request,
195        prefix,
196        result,
197    );
198}
199
200/// Validates an authentication policy definition
201pub fn validate_auth_policy(
202    policy: &AuthenticationPolicyDefinition,
203    prefix: &str,
204    result: &mut ValidationResult,
205) {
206    // Enforce oneOf constraint: only one authentication scheme should be set
207    validate_auth_policy_one_of(policy, prefix, result);
208
209    if let Some(ref basic) = policy.basic {
210        validate_basic_auth(basic, prefix, result);
211    }
212    if let Some(ref bearer) = policy.bearer {
213        validate_bearer_auth(bearer, prefix, result);
214    }
215    if let Some(ref digest) = policy.digest {
216        validate_digest_auth(digest, prefix, result);
217    }
218    if let Some(ref oauth2) = policy.oauth2 {
219        validate_oauth2_auth(oauth2, prefix, result);
220    }
221    if let Some(ref oidc) = policy.oidc {
222        validate_oidc_auth(oidc, prefix, result);
223    }
224}