Skip to main content

brooks_lib/dcdn/processors/
cors.rs

1// brooks, Copyright 2026, Will Hawkins
2//
3// This file is part of brooks.
4
5// This file is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18//! The Mel Generic Metadata Processing Implementation
19
20use crate::{
21    cdni::cors::spec::TypedCrossoriginPolicy,
22    dcdn::{
23        imdi,
24        interpret::{
25            Error::{self, NotImplemented},
26            MetadataInformationResultElements, Stages,
27        },
28        processors::SimpleProcessorsInterpreterContext,
29    },
30    processing::interpret::{InterpretationResult, Interpreter},
31    verifying::cors::verify::CrossoriginPolicyVerificationKey,
32};
33
34impl
35    Interpreter<
36        SimpleProcessorsInterpreterContext<'_>,
37        (imdi::ResultValue, MetadataInformationResultElements),
38        Stages,
39        Error,
40    > for TypedCrossoriginPolicy<CrossoriginPolicyVerificationKey>
41{
42    fn interpret<'a>(
43        &self,
44        input: SimpleProcessorsInterpreterContext<'a>,
45    ) -> InterpretationResult<
46        SimpleProcessorsInterpreterContext<'a>,
47        (imdi::ResultValue, MetadataInformationResultElements),
48        Error,
49    > {
50        if let Some(preflight_only) = self.value.preflight_only
51            && preflight_only
52        {
53            return Err(Error::RuntimeError(
54                NotImplemented("Missing response when processing MI.CrossoriginPolicy".into())
55                    .into(),
56            )
57            .into());
58        }
59
60        let mut res = match input.res {
61            Some(res) => res,
62            None => {
63                return Err(Error::RuntimeError(
64                    Error::AssertionFailure(
65                        "Missing response when processing MI.CrossoriginPolicy".into(),
66                    )
67                    .into(),
68                )
69                .into());
70            }
71        };
72        let req = input.req;
73
74        let origin_value = match req
75            .headers()
76            .iter()
77            .find(|(name, _)| *name == http::header::ORIGIN.to_string())
78        {
79            Some(origin_value) => origin_value.1.clone(),
80            None => todo!(),
81        };
82
83        let origin_value = origin_value.to_str().map_err(|e| {
84            Error::InvalidInput(
85                format!("ORIGIN header value could not be converted to string: {e}").into(),
86            )
87        })?;
88
89        // See whether the ORIGIN value in the request matches any of the
90        // patterns.
91        let apply = self
92            .value
93            .allow_origin
94            .allow_origin
95            .iter()
96            .any(|v| v.aug.as_ref().unwrap().is_match(origin_value));
97
98        // If any of the patterns matched the ORIGIN value,
99        // set the ORIGIN header value in the response.
100        if apply {
101            if self.value.allow_origin.wildcard_return {
102                res.set_header_value(http::header::ACCESS_CONTROL_ALLOW_ORIGIN.as_str(), "*")
103                    .map_err(|e| Error::RuntimeError(e.into()))?;
104            } else {
105                res.set_header_value(
106                    http::header::ACCESS_CONTROL_ALLOW_ORIGIN.as_str(),
107                    origin_value,
108                )
109                .map_err(|e| Error::RuntimeError(e.into()))?;
110            }
111        }
112
113        let updated_context = SimpleProcessorsInterpreterContext {
114            scopes: input.scopes,
115            mode: input.mode,
116            runtime: input.runtime,
117            req,
118            res: Some(res),
119        };
120
121        Ok((
122            updated_context,
123            (None, MetadataInformationResultElements::default()),
124        ))
125    }
126
127    fn at(&self, stage: Stages) -> bool {
128        stage == Stages::Post
129    }
130}
131
132#[cfg(test)]
133mod test {}