1pub struct DefaultPrivileges;
5impl redfish_core::privilege::OperationPrivilegeMapping for DefaultPrivileges {
6 type Get = redfish_core::privilege::Login;
7 type Head = redfish_core::privilege::Login;
8 type Post = redfish_core::privilege::ConfigureComponents;
9 type Put = redfish_core::privilege::ConfigureComponents;
10 type Patch = redfish_core::privilege::ConfigureComponents;
11 type Delete = redfish_core::privilege::ConfigureComponents;
12}
13
14pub struct EnvironmentMetricsPrivileges;
17impl redfish_core::privilege::OperationPrivilegeMapping for EnvironmentMetricsPrivileges {
18 type Get = redfish_core::privilege::Login;
19 type Head = redfish_core::privilege::Login;
20 type Post = redfish_core::privilege::ConfigureComponents;
21 type Put = redfish_core::privilege::ConfigureComponents;
22 type Patch = redfish_core::privilege::ConfigureComponents;
23 type Delete = redfish_core::privilege::ConfigureComponents;
24}
25
26pub struct StorageController<S, P>
31where
32 S: Clone,
33{
34 router: axum::routing::MethodRouter<S>,
35 privilege_marker: std::marker::PhantomData<fn() -> P>,
36 allowed_methods: Vec<axum::http::method::Method>,
37 environment_metrics: Option<axum::Router<S>>,
38 metrics: Option<axum::Router<S>>,
39 attach_namespaces: Option<axum::routing::MethodRouter<S>>,
40 detach_namespaces: Option<axum::routing::MethodRouter<S>>,
41 security_receive: Option<axum::routing::MethodRouter<S>>,
42 security_send: Option<axum::routing::MethodRouter<S>>,
43}
44
45impl<S> Default for StorageController<S, DefaultPrivileges>
46where
47 S: Clone,
48{
49 fn default() -> Self {
50 Self {
51 router: Default::default(),
52 privilege_marker: Default::default(),
53 allowed_methods: Vec::new(),
54 environment_metrics: Default::default(),
55 metrics: Default::default(),
56 attach_namespaces: Default::default(),
57 detach_namespaces: Default::default(),
58 security_receive: Default::default(),
59 security_send: Default::default(),
60 }
61 }
62}
63
64impl<S, P> StorageController<S, P>
65where
66 S: AsRef<dyn redfish_core::auth::AuthenticateRequest> + Clone + Send + Sync + 'static,
67 P: redfish_core::privilege::OperationPrivilegeMapping + 'static,
68 <P as redfish_core::privilege::OperationPrivilegeMapping>::Get: Send,
69 <P as redfish_core::privilege::OperationPrivilegeMapping>::Put: Send,
70 <P as redfish_core::privilege::OperationPrivilegeMapping>::Patch: Send,
71 <P as redfish_core::privilege::OperationPrivilegeMapping>::Post: Send,
72{
73 pub fn get<H, T>(mut self, handler: H) -> Self
74 where
75 H: axum::handler::Handler<T, S, axum::body::Body>,
76 T: 'static,
77 {
78 let operation = axum::routing::get(
79 |auth: redfish_core::extract::RedfishAuth<P::Get>,
80 axum::extract::State(state): axum::extract::State<S>,
81 mut request: axum::http::Request<axum::body::Body>| async {
82 request.extensions_mut().insert(auth.user);
83 handler.call(request, state).await
84 },
85 );
86 self.router = self.router.get(operation);
87 self.allowed_methods.push(axum::http::method::Method::GET);
88 self
89 }
90
91 pub fn put<H, T>(mut self, handler: H) -> Self
92 where
93 H: axum::handler::Handler<T, S, axum::body::Body>,
94 T: 'static,
95 {
96 let operation = axum::routing::put(
97 |auth: redfish_core::extract::RedfishAuth<P::Put>,
98 axum::extract::State(state): axum::extract::State<S>,
99 mut request: axum::http::Request<axum::body::Body>| async {
100 request.extensions_mut().insert(auth.user);
101 handler.call(request, state).await
102 },
103 );
104 self.router = self.router.put(operation);
105 self.allowed_methods.push(axum::http::method::Method::PUT);
106 self
107 }
108
109 pub fn patch<H, T>(mut self, handler: H) -> Self
110 where
111 H: axum::handler::Handler<T, S, axum::body::Body>,
112 T: 'static,
113 {
114 let operation = axum::routing::patch(
115 |auth: redfish_core::extract::RedfishAuth<P::Patch>,
116 axum::extract::State(state): axum::extract::State<S>,
117 mut request: axum::http::Request<axum::body::Body>| async {
118 request.extensions_mut().insert(auth.user);
119 handler.call(request, state).await
120 },
121 );
122 self.router = self.router.patch(operation);
123 self.allowed_methods.push(axum::http::method::Method::PATCH);
124 self
125 }
126
127 pub fn environment_metrics(mut self, environment_metrics: axum::Router<S>) -> Self {
129 self.environment_metrics = Some(environment_metrics);
130 self
131 }
132
133 pub fn metrics(mut self, metrics: axum::Router<S>) -> Self {
135 self.metrics = Some(metrics);
136 self
137 }
138
139 pub fn attach_namespaces<H, T>(mut self, handler: H) -> Self
141 where
142 H: axum::handler::Handler<T, S, axum::body::Body>,
143 T: 'static,
144 {
145 self.attach_namespaces = Some(axum::routing::post(
146 |auth: redfish_core::extract::RedfishAuth<P::Post>,
147 axum::extract::State(state): axum::extract::State<S>,
148 mut request: axum::http::Request<axum::body::Body>| async {
149 request.extensions_mut().insert(auth.user);
150 handler.call(request, state).await
151 },
152 ));
153 self
154 }
155
156 pub fn detach_namespaces<H, T>(mut self, handler: H) -> Self
158 where
159 H: axum::handler::Handler<T, S, axum::body::Body>,
160 T: 'static,
161 {
162 self.detach_namespaces = Some(axum::routing::post(
163 |auth: redfish_core::extract::RedfishAuth<P::Post>,
164 axum::extract::State(state): axum::extract::State<S>,
165 mut request: axum::http::Request<axum::body::Body>| async {
166 request.extensions_mut().insert(auth.user);
167 handler.call(request, state).await
168 },
169 ));
170 self
171 }
172
173 pub fn security_receive<H, T>(mut self, handler: H) -> Self
175 where
176 H: axum::handler::Handler<T, S, axum::body::Body>,
177 T: 'static,
178 {
179 self.security_receive = Some(axum::routing::post(
180 |auth: redfish_core::extract::RedfishAuth<P::Post>,
181 axum::extract::State(state): axum::extract::State<S>,
182 mut request: axum::http::Request<axum::body::Body>| async {
183 request.extensions_mut().insert(auth.user);
184 handler.call(request, state).await
185 },
186 ));
187 self
188 }
189
190 pub fn security_send<H, T>(mut self, handler: H) -> Self
192 where
193 H: axum::handler::Handler<T, S, axum::body::Body>,
194 T: 'static,
195 {
196 self.security_send = Some(axum::routing::post(
197 |auth: redfish_core::extract::RedfishAuth<P::Post>,
198 axum::extract::State(state): axum::extract::State<S>,
199 mut request: axum::http::Request<axum::body::Body>| async {
200 request.extensions_mut().insert(auth.user);
201 handler.call(request, state).await
202 },
203 ));
204 self
205 }
206
207 pub fn into_router(self) -> axum::Router<S> {
208 let Self {
209 router,
210 mut allowed_methods,
211 environment_metrics,
212 metrics,
213 attach_namespaces,
214 detach_namespaces,
215 security_receive,
216 security_send,
217 ..
218 } = self;
219 let result = axum::Router::default();
220 let result = match environment_metrics {
221 Some(router) => result.nest("/EnvironmentMetrics", router),
222 None => result,
223 };
224 let result = match metrics {
225 Some(router) => result.nest("/Metrics", router),
226 None => result,
227 };
228 let result = match attach_namespaces {
229 Some(router) => result.route("/Actions/StorageController.AttachNamespaces", router),
230 None => result,
231 };
232 let result = match detach_namespaces {
233 Some(router) => result.route("/Actions/StorageController.DetachNamespaces", router),
234 None => result,
235 };
236 let result = match security_receive {
237 Some(router) => result.route("/Actions/StorageController.SecurityReceive", router),
238 None => result,
239 };
240 let result = match security_send {
241 Some(router) => result.route("/Actions/StorageController.SecuritySend", router),
242 None => result,
243 };
244 allowed_methods.dedup();
245 let allow_header = allowed_methods
246 .into_iter()
247 .map(|method| method.to_string())
248 .reduce(|one, two| one + "," + &two)
249 .unwrap();
250 result.route(
251 "/",
252 router.fallback(|| async {
253 (
254 axum::http::StatusCode::METHOD_NOT_ALLOWED,
255 axum::Json(redfish_core::error::one_message(redfish_codegen::registries::base::v1_16_0::Base::OperationNotAllowed.into())),
256 )
257 })
258 .route_layer(axum::middleware::from_fn_with_state(
259 allow_header,
260 |axum::extract::State(allow_header): axum::extract::State<String>,
261 request: axum::http::Request<axum::body::Body>,
262 next: axum::middleware::Next<axum::body::Body>| async move {
263 let apply_allow = matches!(*request.method(), axum::http::Method::GET | axum::http::Method::HEAD);
264 let mut response = next.run(request).await;
265 if apply_allow && !response.headers().contains_key(axum::http::header::ALLOW) {
266 response.headers_mut().insert(
267 axum::http::header::ALLOW,
268 axum::http::HeaderValue::from_str(&allow_header).unwrap(),
269 );
270 }
271 response
272 },
273 )),
274 )
275 }
276}