itsi-scheduler 0.2.22-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +8 -0
  3. data/Cargo.lock +997 -0
  4. data/Cargo.toml +7 -0
  5. data/Rakefile +39 -0
  6. data/ext/itsi_acme/Cargo.toml +86 -0
  7. data/ext/itsi_acme/examples/high_level.rs +63 -0
  8. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  9. data/ext/itsi_acme/examples/low_level.rs +87 -0
  10. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  11. data/ext/itsi_acme/src/acceptor.rs +81 -0
  12. data/ext/itsi_acme/src/acme.rs +354 -0
  13. data/ext/itsi_acme/src/axum.rs +86 -0
  14. data/ext/itsi_acme/src/cache.rs +39 -0
  15. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  16. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  17. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  18. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  19. data/ext/itsi_acme/src/caches/no.rs +78 -0
  20. data/ext/itsi_acme/src/caches/test.rs +136 -0
  21. data/ext/itsi_acme/src/config.rs +172 -0
  22. data/ext/itsi_acme/src/https_helper.rs +69 -0
  23. data/ext/itsi_acme/src/incoming.rs +142 -0
  24. data/ext/itsi_acme/src/jose.rs +161 -0
  25. data/ext/itsi_acme/src/lib.rs +142 -0
  26. data/ext/itsi_acme/src/resolver.rs +59 -0
  27. data/ext/itsi_acme/src/state.rs +424 -0
  28. data/ext/itsi_error/Cargo.lock +368 -0
  29. data/ext/itsi_error/Cargo.toml +12 -0
  30. data/ext/itsi_error/src/lib.rs +140 -0
  31. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  32. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  33. data/ext/itsi_rb_helpers/Cargo.lock +355 -0
  34. data/ext/itsi_rb_helpers/Cargo.toml +11 -0
  35. data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
  36. data/ext/itsi_rb_helpers/src/lib.rs +232 -0
  37. data/ext/itsi_scheduler/Cargo.toml +24 -0
  38. data/ext/itsi_scheduler/extconf.rb +11 -0
  39. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  40. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  41. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  42. data/ext/itsi_scheduler/src/itsi_scheduler.rs +320 -0
  43. data/ext/itsi_scheduler/src/lib.rs +39 -0
  44. data/ext/itsi_server/Cargo.lock +2956 -0
  45. data/ext/itsi_server/Cargo.toml +94 -0
  46. data/ext/itsi_server/src/default_responses/mod.rs +14 -0
  47. data/ext/itsi_server/src/env.rs +43 -0
  48. data/ext/itsi_server/src/lib.rs +154 -0
  49. data/ext/itsi_server/src/prelude.rs +2 -0
  50. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +116 -0
  51. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +149 -0
  52. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +346 -0
  53. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +265 -0
  54. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +399 -0
  55. data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +447 -0
  56. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +545 -0
  57. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +650 -0
  58. data/ext/itsi_server/src/ruby_types/itsi_server.rs +102 -0
  59. data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
  60. data/ext/itsi_server/src/server/binds/bind.rs +204 -0
  61. data/ext/itsi_server/src/server/binds/bind_protocol.rs +37 -0
  62. data/ext/itsi_server/src/server/binds/listener.rs +485 -0
  63. data/ext/itsi_server/src/server/binds/mod.rs +4 -0
  64. data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +132 -0
  65. data/ext/itsi_server/src/server/binds/tls.rs +278 -0
  66. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  67. data/ext/itsi_server/src/server/frame_stream.rs +143 -0
  68. data/ext/itsi_server/src/server/http_message_types.rs +230 -0
  69. data/ext/itsi_server/src/server/io_stream.rs +128 -0
  70. data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
  71. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +170 -0
  72. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +63 -0
  73. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +94 -0
  74. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +93 -0
  75. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +343 -0
  76. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +151 -0
  77. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +329 -0
  78. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +300 -0
  79. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +193 -0
  80. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +64 -0
  81. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +188 -0
  82. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +168 -0
  83. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +183 -0
  84. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
  85. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +209 -0
  86. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +133 -0
  87. data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  88. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +122 -0
  89. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +407 -0
  90. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +155 -0
  91. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +54 -0
  92. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +54 -0
  93. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +51 -0
  94. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +138 -0
  95. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +269 -0
  96. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +62 -0
  97. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +218 -0
  98. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +31 -0
  99. data/ext/itsi_server/src/server/middleware_stack/mod.rs +381 -0
  100. data/ext/itsi_server/src/server/mod.rs +14 -0
  101. data/ext/itsi_server/src/server/process_worker.rs +247 -0
  102. data/ext/itsi_server/src/server/redirect_type.rs +26 -0
  103. data/ext/itsi_server/src/server/request_job.rs +11 -0
  104. data/ext/itsi_server/src/server/serve_strategy/acceptor.rs +100 -0
  105. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +411 -0
  106. data/ext/itsi_server/src/server/serve_strategy/mod.rs +31 -0
  107. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +449 -0
  108. data/ext/itsi_server/src/server/signal.rs +129 -0
  109. data/ext/itsi_server/src/server/size_limited_incoming.rs +107 -0
  110. data/ext/itsi_server/src/server/thread_worker.rs +504 -0
  111. data/ext/itsi_server/src/services/cache_store.rs +74 -0
  112. data/ext/itsi_server/src/services/itsi_http_service.rs +270 -0
  113. data/ext/itsi_server/src/services/mime_types.rs +2896 -0
  114. data/ext/itsi_server/src/services/mod.rs +6 -0
  115. data/ext/itsi_server/src/services/password_hasher.rs +89 -0
  116. data/ext/itsi_server/src/services/rate_limiter.rs +609 -0
  117. data/ext/itsi_server/src/services/static_file_server.rs +1400 -0
  118. data/ext/itsi_tracing/Cargo.lock +274 -0
  119. data/ext/itsi_tracing/Cargo.toml +17 -0
  120. data/ext/itsi_tracing/src/lib.rs +370 -0
  121. data/itsi-scheduler-100.png +0 -0
  122. data/lib/itsi/schedule_refinement.rb +96 -0
  123. data/lib/itsi/scheduler/3.1/itsi_scheduler.so +0 -0
  124. data/lib/itsi/scheduler/3.2/itsi_scheduler.so +0 -0
  125. data/lib/itsi/scheduler/3.3/itsi_scheduler.so +0 -0
  126. data/lib/itsi/scheduler/3.4/itsi_scheduler.so +0 -0
  127. data/lib/itsi/scheduler/4.0/itsi_scheduler.so +0 -0
  128. data/lib/itsi/scheduler/native_extension.rb +34 -0
  129. data/lib/itsi/scheduler/version.rb +7 -0
  130. data/lib/itsi/scheduler.rb +153 -0
  131. data/vendor/rb-sys-build/.cargo-ok +1 -0
  132. data/vendor/rb-sys-build/.cargo_vcs_info.json +6 -0
  133. data/vendor/rb-sys-build/Cargo.lock +294 -0
  134. data/vendor/rb-sys-build/Cargo.toml +71 -0
  135. data/vendor/rb-sys-build/Cargo.toml.orig +32 -0
  136. data/vendor/rb-sys-build/LICENSE-APACHE +190 -0
  137. data/vendor/rb-sys-build/LICENSE-MIT +21 -0
  138. data/vendor/rb-sys-build/src/bindings/sanitizer.rs +185 -0
  139. data/vendor/rb-sys-build/src/bindings/stable_api.rs +247 -0
  140. data/vendor/rb-sys-build/src/bindings/wrapper.h +71 -0
  141. data/vendor/rb-sys-build/src/bindings.rs +280 -0
  142. data/vendor/rb-sys-build/src/cc.rs +421 -0
  143. data/vendor/rb-sys-build/src/lib.rs +12 -0
  144. data/vendor/rb-sys-build/src/rb_config/flags.rs +101 -0
  145. data/vendor/rb-sys-build/src/rb_config/library.rs +132 -0
  146. data/vendor/rb-sys-build/src/rb_config/search_path.rs +57 -0
  147. data/vendor/rb-sys-build/src/rb_config.rs +906 -0
  148. data/vendor/rb-sys-build/src/utils.rs +53 -0
  149. metadata +210 -0
@@ -0,0 +1,343 @@
1
+ use super::{error_response::ErrorResponse, token_source::TokenSource, FromValue, MiddlewareLayer};
2
+ use crate::{
3
+ server::http_message_types::{HttpRequest, HttpResponse, RequestExt},
4
+ services::itsi_http_service::HttpRequestContext,
5
+ };
6
+
7
+ use async_trait::async_trait;
8
+ use base64::{engine::general_purpose, Engine};
9
+ use derive_more::Debug;
10
+ use either::Either;
11
+ use itsi_error::ItsiError;
12
+ use jsonwebtoken::{
13
+ decode, decode_header, Algorithm as JwtAlg, DecodingKey, TokenData, Validation,
14
+ };
15
+ use magnus::error::Result;
16
+ use serde::Deserialize;
17
+ use std::{
18
+ collections::{HashMap, HashSet},
19
+ sync::OnceLock,
20
+ };
21
+ use tracing::debug;
22
+
23
+ #[derive(Debug, Clone, Deserialize)]
24
+ pub struct AuthJwt {
25
+ pub token_source: TokenSource,
26
+ // The verifiers map still holds base64-encoded key strings keyed by algorithm.
27
+ pub verifiers: HashMap<JwtAlgorithm, Vec<String>>,
28
+ // We now store jsonwebtoken’s DecodingKey in our OnceLock.
29
+ #[serde(skip_deserializing)]
30
+ #[debug(skip)]
31
+ pub keys: OnceLock<HashMap<JwtAlgorithm, Vec<DecodingKey>>>,
32
+ pub audiences: Option<HashSet<String>>,
33
+ pub subjects: Option<HashSet<String>>,
34
+ pub issuers: Option<HashSet<String>>,
35
+ #[serde(skip_deserializing)]
36
+ pub audience_vec: OnceLock<Option<Vec<String>>>,
37
+ #[serde(skip_deserializing)]
38
+ pub issuer_vec: OnceLock<Option<Vec<String>>>,
39
+ pub leeway: Option<u64>,
40
+ #[serde(default = "unauthorized_error_response")]
41
+ pub error_response: ErrorResponse,
42
+ }
43
+
44
+ fn unauthorized_error_response() -> ErrorResponse {
45
+ ErrorResponse::unauthorized()
46
+ }
47
+
48
+ #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Hash)]
49
+ pub enum JwtAlgorithm {
50
+ #[serde(rename(deserialize = "hs256"))]
51
+ Hs256,
52
+ #[serde(rename(deserialize = "hs384"))]
53
+ Hs384,
54
+ #[serde(rename(deserialize = "hs512"))]
55
+ Hs512,
56
+ #[serde(rename(deserialize = "rs256"))]
57
+ Rs256,
58
+ #[serde(rename(deserialize = "rs384"))]
59
+ Rs384,
60
+ #[serde(rename(deserialize = "rs512"))]
61
+ Rs512,
62
+ #[serde(rename(deserialize = "es256"))]
63
+ Es256,
64
+ #[serde(rename(deserialize = "es384"))]
65
+ Es384,
66
+ #[serde(rename(deserialize = "ps256"))]
67
+ Ps256,
68
+ #[serde(rename(deserialize = "ps384"))]
69
+ Ps384,
70
+ #[serde(rename(deserialize = "ps512"))]
71
+ Ps512,
72
+ }
73
+
74
+ // Allow conversion from jsonwebtoken’s Algorithm to our JwtAlgorithm.
75
+ impl From<JwtAlg> for JwtAlgorithm {
76
+ fn from(alg: JwtAlg) -> Self {
77
+ match alg {
78
+ JwtAlg::HS256 => JwtAlgorithm::Hs256,
79
+ JwtAlg::HS384 => JwtAlgorithm::Hs384,
80
+ JwtAlg::HS512 => JwtAlgorithm::Hs512,
81
+ JwtAlg::RS256 => JwtAlgorithm::Rs256,
82
+ JwtAlg::RS384 => JwtAlgorithm::Rs384,
83
+ JwtAlg::RS512 => JwtAlgorithm::Rs512,
84
+ JwtAlg::ES256 => JwtAlgorithm::Es256,
85
+ JwtAlg::ES384 => JwtAlgorithm::Es384,
86
+ JwtAlg::PS256 => JwtAlgorithm::Ps256,
87
+ JwtAlg::PS384 => JwtAlgorithm::Ps384,
88
+ JwtAlg::PS512 => JwtAlgorithm::Ps512,
89
+ _ => panic!("Unsupported algorithm"),
90
+ }
91
+ }
92
+ }
93
+
94
+ impl JwtAlgorithm {
95
+ /// Given a base64-encoded key string, decode and construct a jsonwebtoken::DecodingKey.
96
+ pub fn key_from(&self, base64: &str) -> itsi_error::Result<DecodingKey> {
97
+ match self {
98
+ // For HMAC algorithms, expect a base64 encoded secret.
99
+ JwtAlgorithm::Hs256 | JwtAlgorithm::Hs384 | JwtAlgorithm::Hs512 => {
100
+ Ok(DecodingKey::from_secret(
101
+ &general_purpose::STANDARD
102
+ .decode(base64)
103
+ .map_err(ItsiError::new)?,
104
+ ))
105
+ }
106
+ // For RSA (and PS) algorithms, expect a PEM-formatted key.
107
+ JwtAlgorithm::Rs256
108
+ | JwtAlgorithm::Rs384
109
+ | JwtAlgorithm::Rs512
110
+ | JwtAlgorithm::Ps256
111
+ | JwtAlgorithm::Ps384
112
+ | JwtAlgorithm::Ps512 => DecodingKey::from_rsa_pem(base64.trim_ascii().as_bytes())
113
+ .map_err(|e| ItsiError::new(e.to_string())),
114
+ // For ECDSA algorithms, expect a PEM-formatted key.
115
+ JwtAlgorithm::Es256 | JwtAlgorithm::Es384 => {
116
+ DecodingKey::from_ec_pem(base64.trim_ascii().as_bytes())
117
+ .map_err(|e| ItsiError::new(e.to_string()))
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ #[derive(Debug, Deserialize)]
124
+ #[serde(untagged)]
125
+ #[allow(dead_code)]
126
+ enum Audience {
127
+ Single(String),
128
+ Multiple(Vec<String>),
129
+ }
130
+
131
+ #[derive(Debug, Deserialize)]
132
+ #[allow(dead_code)]
133
+ struct Claims {
134
+ // Here we assume the token includes an expiration.
135
+ exp: usize,
136
+ // The audience claim may be a single string or an array.
137
+ aud: Option<Audience>,
138
+ sub: Option<String>,
139
+ iss: Option<String>,
140
+ }
141
+
142
+ #[async_trait]
143
+ impl MiddlewareLayer for AuthJwt {
144
+ async fn initialize(&self) -> Result<()> {
145
+ debug!(
146
+ target: "middleware::auth_jwt",
147
+ "Instantiating auth_jwt with {} verifiers", self.verifiers.len()
148
+ );
149
+
150
+ let keys: HashMap<JwtAlgorithm, Vec<DecodingKey>> = self
151
+ .verifiers
152
+ .iter()
153
+ .map(|(algorithm, key_strings)| {
154
+ let algo = algorithm.clone();
155
+ let keys: itsi_error::Result<Vec<DecodingKey>> = key_strings
156
+ .iter()
157
+ .map(|key_string| algorithm.key_from(key_string))
158
+ .inspect(|key_result| {
159
+ if key_result.is_err() {
160
+ debug!(
161
+ target: "middleware::auth_jwt",
162
+ "Failed to load key for algorithm {:?}", algorithm
163
+ )
164
+ } else {
165
+ debug!(
166
+ target: "middleware::auth_jwt",
167
+ "Loaded key for algorithm {:?}", algorithm
168
+ )
169
+ }
170
+ })
171
+ .collect();
172
+ keys.map(|keys| (algo, keys))
173
+ })
174
+ .collect::<itsi_error::Result<HashMap<JwtAlgorithm, Vec<DecodingKey>>>>()?;
175
+
176
+ self.keys
177
+ .set(keys)
178
+ .map_err(|_| ItsiError::new("Failed to set keys"))?;
179
+
180
+ if let Some(audiences) = self.audiences.as_ref() {
181
+ self.audience_vec
182
+ .set(Some(audiences.iter().cloned().collect::<Vec<_>>()))
183
+ .ok();
184
+ }
185
+ if let Some(issuers) = self.issuers.as_ref() {
186
+ self.issuer_vec
187
+ .set(Some(issuers.iter().cloned().collect::<Vec<_>>()))
188
+ .ok();
189
+ }
190
+ Ok(())
191
+ }
192
+
193
+ async fn before(
194
+ &self,
195
+ req: HttpRequest,
196
+ _: &mut HttpRequestContext,
197
+ ) -> Result<Either<HttpRequest, HttpResponse>> {
198
+ // Retrieve the JWT token from either a header or a query parameter.
199
+ let token_str = match &self.token_source {
200
+ TokenSource::Header { name, prefix } => {
201
+ debug!(
202
+ target: "middleware::auth_jwt",
203
+ "Extracting JWT from header: {}, prefix: {:?}",
204
+ name, prefix
205
+ );
206
+ if let Some(header) = req.header(name) {
207
+ if let Some(prefix) = prefix {
208
+ Some(header.strip_prefix(prefix).unwrap_or("").trim_ascii())
209
+ } else {
210
+ Some(header.trim_ascii())
211
+ }
212
+ } else {
213
+ None
214
+ }
215
+ }
216
+ TokenSource::Query(query_name) => {
217
+ debug!(
218
+ target: "middleware::auth_jwt",
219
+ "Extracting JWT from query parameter: {}",
220
+ query_name
221
+ );
222
+ req.query_param(query_name)
223
+ }
224
+ };
225
+
226
+ if token_str.is_none() {
227
+ debug!(
228
+ target: "middleware::auth_jwt",
229
+ "No JWT found in headers or query parameters"
230
+ );
231
+ return Ok(Either::Right(
232
+ self.error_response
233
+ .to_http_response(req.accept().into())
234
+ .await,
235
+ ));
236
+ }
237
+ let token_str = token_str.unwrap();
238
+ let header = match decode_header(token_str) {
239
+ Ok(header) => header,
240
+ Err(_) => {
241
+ debug!(target: "middleware::auth_jwt", "JWT decoding failed");
242
+ return Ok(Either::Right(
243
+ self.error_response
244
+ .to_http_response(req.accept().into())
245
+ .await,
246
+ ));
247
+ }
248
+ };
249
+
250
+ let alg: JwtAlgorithm = header.alg.into();
251
+
252
+ debug!(
253
+ target: "middleware::auth_jwt",
254
+ "Matched algorithm {:?}", alg
255
+ );
256
+ if !self.verifiers.contains_key(&alg) {
257
+ return Ok(Either::Right(
258
+ self.error_response
259
+ .to_http_response(req.accept().into())
260
+ .await,
261
+ ));
262
+ }
263
+ let keys = self.keys.get().unwrap().get(&alg).unwrap();
264
+
265
+ // Build validation based on the algorithm and optional leeway.
266
+ let mut validation = Validation::new(match alg {
267
+ JwtAlgorithm::Hs256 => JwtAlg::HS256,
268
+ JwtAlgorithm::Hs384 => JwtAlg::HS384,
269
+ JwtAlgorithm::Hs512 => JwtAlg::HS512,
270
+ JwtAlgorithm::Rs256 => JwtAlg::RS256,
271
+ JwtAlgorithm::Rs384 => JwtAlg::RS384,
272
+ JwtAlgorithm::Rs512 => JwtAlg::RS512,
273
+ JwtAlgorithm::Es256 => JwtAlg::ES256,
274
+ JwtAlgorithm::Es384 => JwtAlg::ES384,
275
+ JwtAlgorithm::Ps256 => JwtAlg::PS256,
276
+ JwtAlgorithm::Ps384 => JwtAlg::PS384,
277
+ JwtAlgorithm::Ps512 => JwtAlg::PS512,
278
+ });
279
+
280
+ if let Some(leeway) = self.leeway {
281
+ validation.leeway = leeway;
282
+ }
283
+
284
+ if let Some(Some(auds)) = &self.audience_vec.get() {
285
+ validation.set_audience(auds);
286
+ validation.required_spec_claims.insert("aud".to_owned());
287
+ } else {
288
+ validation.validate_aud = false;
289
+ }
290
+
291
+ if let Some(Some(issuers)) = &self.issuer_vec.get() {
292
+ validation.set_issuer(issuers);
293
+ validation.required_spec_claims.insert("iss".to_owned());
294
+ }
295
+
296
+ if self.subjects.is_some() {
297
+ validation.required_spec_claims.insert("sub".to_owned());
298
+ }
299
+
300
+ let token_data: Option<TokenData<Claims>> =
301
+ keys.iter()
302
+ .find_map(|key| match decode::<Claims>(token_str, key, &validation) {
303
+ Ok(data) => Some(data),
304
+ Err(e) => {
305
+ debug!("Token validation failed: {:?}", e);
306
+ None
307
+ }
308
+ });
309
+
310
+ let token_data = if let Some(data) = token_data {
311
+ data
312
+ } else {
313
+ return Ok(Either::Right(
314
+ self.error_response
315
+ .to_http_response(req.accept().into())
316
+ .await,
317
+ ));
318
+ };
319
+
320
+ let claims = token_data.claims;
321
+
322
+ if let Some(expected_subjects) = &self.subjects {
323
+ if let Some(sub) = &claims.sub {
324
+ if !expected_subjects.contains(sub) {
325
+ debug!(
326
+ target: "middleware::auth_jwt",
327
+ "SUB check failed, token_sub: {:?}, expected_subjects: {:?}",
328
+ sub, expected_subjects
329
+ );
330
+ return Ok(Either::Right(
331
+ self.error_response
332
+ .to_http_response(req.accept().into())
333
+ .await,
334
+ ));
335
+ }
336
+ }
337
+ }
338
+
339
+ Ok(Either::Left(req))
340
+ }
341
+ }
342
+
343
+ impl FromValue for AuthJwt {}
@@ -0,0 +1,151 @@
1
+ use crate::{
2
+ server::http_message_types::HttpResponse, services::itsi_http_service::HttpRequestContext,
3
+ };
4
+
5
+ use super::{FromValue, MiddlewareLayer};
6
+ use async_trait::async_trait;
7
+ use http::{HeaderName, HeaderValue};
8
+ use magnus::error::Result;
9
+ use serde::Deserialize;
10
+ use std::{collections::HashMap, sync::OnceLock};
11
+ use tracing::debug;
12
+
13
+ #[derive(Debug, Deserialize)]
14
+ pub struct CacheControl {
15
+ #[serde(default)]
16
+ pub max_age: Option<u64>,
17
+ #[serde(default)]
18
+ pub s_max_age: Option<u64>,
19
+ #[serde(default)]
20
+ pub stale_while_revalidate: Option<u64>,
21
+ #[serde(default)]
22
+ pub stale_if_error: Option<u64>,
23
+ #[serde(default)]
24
+ pub public: bool,
25
+ #[serde(default)]
26
+ pub private: bool,
27
+ #[serde(default)]
28
+ pub no_cache: bool,
29
+ #[serde(default)]
30
+ pub no_store: bool,
31
+ #[serde(default)]
32
+ pub must_revalidate: bool,
33
+ #[serde(default)]
34
+ pub proxy_revalidate: bool,
35
+ #[serde(default)]
36
+ pub immutable: bool,
37
+ #[serde(default)]
38
+ pub vary: Vec<String>,
39
+ #[serde(default)]
40
+ pub additional_headers: HashMap<String, String>,
41
+ #[serde(skip_deserializing)]
42
+ pub cache_control_str: OnceLock<String>,
43
+ }
44
+
45
+ #[async_trait]
46
+ impl MiddlewareLayer for CacheControl {
47
+ async fn initialize(&self) -> Result<()> {
48
+ let mut directives = Vec::new();
49
+
50
+ if self.public && !self.private {
51
+ directives.push("public".to_owned());
52
+ } else if self.private && !self.public {
53
+ directives.push("private".to_owned());
54
+ }
55
+ if self.no_cache {
56
+ directives.push("no-cache".to_owned());
57
+ }
58
+ if self.no_store {
59
+ directives.push("no-store".to_owned());
60
+ }
61
+ if self.must_revalidate {
62
+ directives.push("must-revalidate".to_owned());
63
+ }
64
+ if self.proxy_revalidate {
65
+ directives.push("proxy-revalidate".to_owned());
66
+ }
67
+ if self.immutable {
68
+ directives.push("immutable".to_owned());
69
+ }
70
+
71
+ // Add age parameters
72
+ if let Some(max_age) = self.max_age {
73
+ directives.push(format!("max-age={}", max_age));
74
+ }
75
+
76
+ if let Some(s_max_age) = self.s_max_age {
77
+ directives.push(format!("s-maxage={}", s_max_age));
78
+ }
79
+
80
+ if let Some(stale_while_revalidate) = self.stale_while_revalidate {
81
+ directives.push(format!("stale-while-revalidate={}", stale_while_revalidate));
82
+ }
83
+
84
+ if let Some(stale_if_error) = self.stale_if_error {
85
+ directives.push(format!("stale-if-error={}", stale_if_error));
86
+ }
87
+
88
+ // Set the Cache-Control header if we have directives
89
+ if !directives.is_empty() {
90
+ let cache_control_value = directives.join(", ");
91
+ debug!(target: "middleware::cache_control", "Built cache-control directive {}", cache_control_value);
92
+ self.cache_control_str.set(cache_control_value).unwrap();
93
+ }
94
+
95
+ Ok(())
96
+ }
97
+
98
+ async fn after(&self, mut resp: HttpResponse, _: &mut HttpRequestContext) -> HttpResponse {
99
+ // Skip for statuses where caching doesn't make sense
100
+ let status = resp.status().as_u16();
101
+ if matches!(status, 401 | 403 | 500..=599) {
102
+ debug!(target: "middleware::cache_control", "Skipping cache-control for status {}", status);
103
+ return resp;
104
+ }
105
+
106
+ // Set the Cache-Control header if we have directives
107
+ if let Some(cache_control_value) = self.cache_control_str.get() {
108
+ if let Ok(value) = HeaderValue::from_str(cache_control_value) {
109
+ debug!(target: "middleware::cache_control", "Setting cache-control header to {}", cache_control_value);
110
+ resp.headers_mut().insert("Cache-Control", value);
111
+ } else {
112
+ debug!(target: "middleware::cache_control", "Failed to parse cache-control value {}", cache_control_value);
113
+ }
114
+ } else {
115
+ debug!(target: "middleware::cache_control", "No cache-control value provided");
116
+ }
117
+
118
+ // Set Expires header based on max-age if present
119
+ if let Some(max_age) = self.max_age {
120
+ // Set the Expires header based on max-age
121
+ // Use a helper to format the HTTP date correctly
122
+ debug!(target: "middleware::cache_control", "Setting expires header to {}", max_age);
123
+ let expires = chrono::Utc::now() + chrono::Duration::seconds(max_age as i64);
124
+ let expires_str = expires.format("%a, %d %b %Y %H:%M:%S GMT").to_string();
125
+ if let Ok(value) = HeaderValue::from_str(&expires_str) {
126
+ resp.headers_mut().insert("Expires", value);
127
+ }
128
+ }
129
+
130
+ if !self.vary.is_empty() {
131
+ let vary_value = self.vary.join(", ");
132
+ if let Ok(value) = HeaderValue::from_str(&vary_value) {
133
+ resp.headers_mut().insert("Vary", value);
134
+ }
135
+ }
136
+
137
+ // Set additional custom headers
138
+ for (name, value) in &self.additional_headers {
139
+ if let Ok(header_value) = HeaderValue::from_str(value) {
140
+ if let Ok(header_name) = name.parse::<HeaderName>() {
141
+ debug!(target: "middleware::cache_control", "Setting custom header {} to {:?}", header_name, header_value);
142
+ resp.headers_mut().insert(header_name, header_value);
143
+ }
144
+ }
145
+ }
146
+
147
+ resp
148
+ }
149
+ }
150
+
151
+ impl FromValue for CacheControl {}