itsi-server 0.1.11 → 0.1.12

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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +7 -0
  4. data/Cargo.lock +1536 -45
  5. data/README.md +4 -0
  6. data/_index.md +6 -0
  7. data/exe/itsi +33 -74
  8. data/ext/itsi_error/src/lib.rs +9 -0
  9. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  10. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  11. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  12. data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  13. data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
  14. data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
  15. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
  16. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
  17. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
  18. data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
  19. data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
  20. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
  21. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
  22. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
  23. data/ext/itsi_rb_helpers/Cargo.toml +1 -0
  24. data/ext/itsi_rb_helpers/src/heap_value.rs +18 -0
  25. data/ext/itsi_rb_helpers/src/lib.rs +34 -7
  26. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  27. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  28. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  29. data/ext/itsi_rb_helpers/target/debug/build/rb-sys-eb9ed4ff3a60f995/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  30. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
  31. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
  32. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
  33. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
  34. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
  35. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
  36. data/ext/itsi_server/Cargo.toml +69 -30
  37. data/ext/itsi_server/src/lib.rs +79 -147
  38. data/ext/itsi_server/src/{body_proxy → ruby_types/itsi_body_proxy}/big_bytes.rs +10 -5
  39. data/ext/itsi_server/src/{body_proxy/itsi_body_proxy.rs → ruby_types/itsi_body_proxy/mod.rs} +22 -3
  40. data/ext/itsi_server/src/ruby_types/itsi_grpc_request.rs +147 -0
  41. data/ext/itsi_server/src/ruby_types/itsi_grpc_response.rs +19 -0
  42. data/ext/itsi_server/src/ruby_types/itsi_grpc_stream/mod.rs +216 -0
  43. data/ext/itsi_server/src/{request/itsi_request.rs → ruby_types/itsi_http_request.rs} +101 -117
  44. data/ext/itsi_server/src/{response/itsi_response.rs → ruby_types/itsi_http_response.rs} +72 -41
  45. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
  46. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +355 -0
  47. data/ext/itsi_server/src/ruby_types/itsi_server.rs +82 -0
  48. data/ext/itsi_server/src/ruby_types/mod.rs +55 -0
  49. data/ext/itsi_server/src/server/bind.rs +13 -5
  50. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  51. data/ext/itsi_server/src/server/cache_store.rs +74 -0
  52. data/ext/itsi_server/src/server/itsi_service.rs +172 -0
  53. data/ext/itsi_server/src/server/lifecycle_event.rs +3 -0
  54. data/ext/itsi_server/src/server/listener.rs +102 -2
  55. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +153 -0
  56. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +47 -0
  57. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +58 -0
  58. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +82 -0
  59. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +321 -0
  60. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +139 -0
  61. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +300 -0
  62. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +287 -0
  63. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +48 -0
  64. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +127 -0
  65. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +191 -0
  66. data/ext/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +72 -0
  67. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +85 -0
  68. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +195 -0
  69. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  70. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +82 -0
  71. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +216 -0
  72. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +124 -0
  73. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
  74. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +43 -0
  75. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +34 -0
  76. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +93 -0
  77. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +162 -0
  78. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +158 -0
  79. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
  80. data/ext/itsi_server/src/server/middleware_stack/mod.rs +315 -0
  81. data/ext/itsi_server/src/server/mod.rs +8 -1
  82. data/ext/itsi_server/src/server/process_worker.rs +38 -12
  83. data/ext/itsi_server/src/server/rate_limiter.rs +565 -0
  84. data/ext/itsi_server/src/server/request_job.rs +11 -0
  85. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +119 -42
  86. data/ext/itsi_server/src/server/serve_strategy/mod.rs +9 -6
  87. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +256 -111
  88. data/ext/itsi_server/src/server/signal.rs +19 -0
  89. data/ext/itsi_server/src/server/static_file_server.rs +984 -0
  90. data/ext/itsi_server/src/server/thread_worker.rs +139 -94
  91. data/ext/itsi_server/src/server/types.rs +43 -0
  92. data/ext/itsi_tracing/Cargo.toml +1 -0
  93. data/ext/itsi_tracing/src/lib.rs +216 -45
  94. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
  95. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
  96. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
  97. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
  98. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
  99. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
  100. data/lib/itsi/{request.rb → http_request.rb} +29 -5
  101. data/lib/itsi/http_response.rb +39 -0
  102. data/lib/itsi/server/Itsi.rb +11 -19
  103. data/lib/itsi/server/config/dsl.rb +506 -0
  104. data/lib/itsi/server/config.rb +103 -8
  105. data/lib/itsi/server/default_app/default_app.rb +38 -0
  106. data/lib/itsi/server/grpc_interface.rb +213 -0
  107. data/lib/itsi/server/rack/handler/itsi.rb +8 -17
  108. data/lib/itsi/server/rack_interface.rb +23 -4
  109. data/lib/itsi/server/scheduler_interface.rb +1 -1
  110. data/lib/itsi/server/scheduler_mode.rb +4 -0
  111. data/lib/itsi/server/signal_trap.rb +7 -1
  112. data/lib/itsi/server/version.rb +1 -1
  113. data/lib/itsi/server.rb +74 -63
  114. data/lib/itsi/standard_headers.rb +86 -0
  115. metadata +84 -15
  116. data/ext/itsi_scheduler/extconf.rb +0 -6
  117. data/ext/itsi_server/src/body_proxy/mod.rs +0 -2
  118. data/ext/itsi_server/src/request/mod.rs +0 -1
  119. data/ext/itsi_server/src/response/mod.rs +0 -1
  120. data/ext/itsi_server/src/server/itsi_server.rs +0 -288
  121. data/lib/itsi/server/options_dsl.rb +0 -401
  122. data/lib/itsi/stream_io.rb +0 -38
  123. /data/lib/itsi/{index.html → server/default_app/index.html} +0 -0
@@ -0,0 +1,321 @@
1
+ use super::{error_response::ErrorResponse, token_source::TokenSource, FromValue, MiddlewareLayer};
2
+ use crate::server::{
3
+ itsi_service::RequestContext,
4
+ types::{HttpRequest, HttpResponse, RequestExt},
5
+ };
6
+ use async_trait::async_trait;
7
+ use base64::{engine::general_purpose, Engine};
8
+ use either::Either;
9
+ use itsi_error::ItsiError;
10
+ use jwt_simple::{
11
+ claims::{self, JWTClaims, NoCustomClaims},
12
+ prelude::{
13
+ ECDSAP256PublicKeyLike, ECDSAP384PublicKeyLike, ES256PublicKey, ES384PublicKey, HS256Key,
14
+ HS384Key, HS512Key, MACLike, PS256PublicKey, PS384PublicKey, PS512PublicKey,
15
+ RS256PublicKey, RS384PublicKey, RS512PublicKey, RSAPublicKeyLike,
16
+ },
17
+ token::Token,
18
+ };
19
+ use magnus::error::Result;
20
+ use serde::Deserialize;
21
+ use std::str;
22
+ use std::{
23
+ collections::{HashMap, HashSet},
24
+ sync::OnceLock,
25
+ };
26
+
27
+ #[derive(Debug, Clone, Deserialize)]
28
+ pub struct AuthJwt {
29
+ pub token_source: TokenSource,
30
+ pub verifiers: HashMap<JwtAlgorithm, Vec<String>>,
31
+ #[serde(skip_deserializing)]
32
+ pub keys: OnceLock<HashMap<JwtAlgorithm, Vec<JwtKey>>>,
33
+ pub audiences: Option<HashSet<String>>,
34
+ pub subjects: Option<HashSet<String>>,
35
+ pub issuers: Option<HashSet<String>>,
36
+ pub leeway: Option<u64>,
37
+ pub error_response: ErrorResponse,
38
+ }
39
+
40
+ #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Hash)]
41
+ pub enum JwtAlgorithm {
42
+ #[serde(rename(deserialize = "hs256"))]
43
+ Hs256,
44
+ #[serde(rename(deserialize = "hs384"))]
45
+ Hs384,
46
+ #[serde(rename(deserialize = "hs512"))]
47
+ Hs512,
48
+ #[serde(rename(deserialize = "rs256"))]
49
+ Rs256,
50
+ #[serde(rename(deserialize = "rs384"))]
51
+ Rs384,
52
+ #[serde(rename(deserialize = "rs512"))]
53
+ Rs512,
54
+ #[serde(rename(deserialize = "es256"))]
55
+ Es256,
56
+ #[serde(rename(deserialize = "es384"))]
57
+ Es384,
58
+ #[serde(rename(deserialize = "ps256"))]
59
+ Ps256,
60
+ #[serde(rename(deserialize = "ps384"))]
61
+ Ps384,
62
+ #[serde(rename(deserialize = "ps512"))]
63
+ Ps512,
64
+ }
65
+
66
+ impl JwtAlgorithm {
67
+ pub fn key_from(&self, base64: &str) -> Result<JwtKey> {
68
+ let bytes = general_purpose::STANDARD
69
+ .decode(base64)
70
+ .map_err(ItsiError::default)?;
71
+
72
+ match self {
73
+ JwtAlgorithm::Hs256 => Ok(JwtKey::Hs256(HS256Key::from_bytes(&bytes))),
74
+ JwtAlgorithm::Hs384 => Ok(JwtKey::Hs384(HS384Key::from_bytes(&bytes))),
75
+ JwtAlgorithm::Hs512 => Ok(JwtKey::Hs512(HS512Key::from_bytes(&bytes))),
76
+ JwtAlgorithm::Rs256 => Ok(RS256PublicKey::from_der(&bytes)
77
+ .or_else(|_| {
78
+ RS256PublicKey::from_pem(
79
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
80
+ )
81
+ })
82
+ .map(JwtKey::Rs256)
83
+ .map_err(ItsiError::default)?),
84
+ JwtAlgorithm::Rs384 => Ok(RS384PublicKey::from_der(&bytes)
85
+ .or_else(|_| {
86
+ RS384PublicKey::from_pem(
87
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
88
+ )
89
+ })
90
+ .map(JwtKey::Rs384)
91
+ .map_err(ItsiError::default)?),
92
+ JwtAlgorithm::Rs512 => Ok(RS512PublicKey::from_der(&bytes)
93
+ .or_else(|_| {
94
+ RS512PublicKey::from_pem(
95
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
96
+ )
97
+ })
98
+ .map(JwtKey::Rs512)
99
+ .map_err(ItsiError::default)?),
100
+ JwtAlgorithm::Es256 => Ok(ES256PublicKey::from_der(&bytes)
101
+ .or_else(|_| {
102
+ ES256PublicKey::from_pem(
103
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
104
+ )
105
+ })
106
+ .map(JwtKey::Es256)
107
+ .map_err(ItsiError::default)?),
108
+ JwtAlgorithm::Es384 => Ok(ES384PublicKey::from_der(&bytes)
109
+ .or_else(|_| {
110
+ ES384PublicKey::from_pem(
111
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
112
+ )
113
+ })
114
+ .map(JwtKey::Es384)
115
+ .map_err(ItsiError::default)?),
116
+ JwtAlgorithm::Ps256 => Ok(PS256PublicKey::from_der(&bytes)
117
+ .or_else(|_| {
118
+ PS256PublicKey::from_pem(
119
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
120
+ )
121
+ })
122
+ .map(JwtKey::Ps256)
123
+ .map_err(ItsiError::default)?),
124
+ JwtAlgorithm::Ps384 => Ok(PS384PublicKey::from_der(&bytes)
125
+ .or_else(|_| {
126
+ PS384PublicKey::from_pem(
127
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
128
+ )
129
+ })
130
+ .map(JwtKey::Ps384)
131
+ .map_err(ItsiError::default)?),
132
+ JwtAlgorithm::Ps512 => Ok(PS512PublicKey::from_der(&bytes)
133
+ .or_else(|_| {
134
+ PS512PublicKey::from_pem(
135
+ &String::from_utf8(bytes.clone()).map_err(ItsiError::default)?,
136
+ )
137
+ })
138
+ .map(JwtKey::Ps512)
139
+ .map_err(ItsiError::default)?),
140
+ }
141
+ }
142
+ }
143
+
144
+ #[derive(Debug, Clone)]
145
+ pub enum JwtKey {
146
+ Hs256(HS256Key),
147
+ Hs384(HS384Key),
148
+ Hs512(HS512Key),
149
+ Rs256(RS256PublicKey),
150
+ Rs384(RS384PublicKey),
151
+ Rs512(RS512PublicKey),
152
+ Es256(ES256PublicKey),
153
+ Es384(ES384PublicKey),
154
+ Ps256(PS256PublicKey),
155
+ Ps384(PS384PublicKey),
156
+ Ps512(PS512PublicKey),
157
+ }
158
+
159
+ impl TryFrom<&str> for JwtAlgorithm {
160
+ type Error = itsi_error::ItsiError;
161
+
162
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
163
+ match value.to_ascii_lowercase().as_str() {
164
+ "hs256" => Ok(JwtAlgorithm::Hs256),
165
+ "hs384" => Ok(JwtAlgorithm::Hs384),
166
+ "hs512" => Ok(JwtAlgorithm::Hs512),
167
+ "rs256" => Ok(JwtAlgorithm::Rs256),
168
+ "rs384" => Ok(JwtAlgorithm::Rs384),
169
+ "rs512" => Ok(JwtAlgorithm::Rs512),
170
+ "es256" => Ok(JwtAlgorithm::Es256),
171
+ "es384" => Ok(JwtAlgorithm::Es384),
172
+ "ps256" => Ok(JwtAlgorithm::Ps256),
173
+ "ps384" => Ok(JwtAlgorithm::Ps384),
174
+ "ps512" => Ok(JwtAlgorithm::Ps512),
175
+ _ => Err(itsi_error::ItsiError::UnsupportedProtocol(
176
+ "Unsupported JWT Algorithm".to_string(),
177
+ )),
178
+ }
179
+ }
180
+ }
181
+
182
+ impl JwtKey {
183
+ pub fn verify(
184
+ &self,
185
+ token: &str,
186
+ ) -> std::result::Result<JWTClaims<claims::NoCustomClaims>, jwt_simple::Error> {
187
+ match self {
188
+ JwtKey::Hs256(key) => key.verify_token::<NoCustomClaims>(token, None),
189
+ JwtKey::Hs384(key) => key.verify_token::<NoCustomClaims>(token, None),
190
+ JwtKey::Hs512(key) => key.verify_token::<NoCustomClaims>(token, None),
191
+ JwtKey::Rs256(key) => key.verify_token::<NoCustomClaims>(token, None),
192
+ JwtKey::Rs384(key) => key.verify_token::<NoCustomClaims>(token, None),
193
+ JwtKey::Rs512(key) => key.verify_token::<NoCustomClaims>(token, None),
194
+ JwtKey::Es256(key) => key.verify_token::<NoCustomClaims>(token, None),
195
+ JwtKey::Es384(key) => key.verify_token::<NoCustomClaims>(token, None),
196
+ JwtKey::Ps256(key) => key.verify_token::<NoCustomClaims>(token, None),
197
+ JwtKey::Ps384(key) => key.verify_token::<NoCustomClaims>(token, None),
198
+ JwtKey::Ps512(key) => key.verify_token::<NoCustomClaims>(token, None),
199
+ }
200
+ }
201
+ }
202
+
203
+ #[async_trait]
204
+ impl MiddlewareLayer for AuthJwt {
205
+ async fn initialize(&self) -> Result<()> {
206
+ let keys: HashMap<JwtAlgorithm, Vec<JwtKey>> = self
207
+ .verifiers
208
+ .iter()
209
+ .map(|(algorithm, key_strings)| {
210
+ let algo = algorithm.clone();
211
+ let keys: Result<Vec<JwtKey>> = key_strings
212
+ .iter()
213
+ .map(|key_string| algorithm.key_from(key_string))
214
+ .collect();
215
+ keys.map(|keys| (algo, keys))
216
+ })
217
+ .collect::<Result<HashMap<JwtAlgorithm, Vec<JwtKey>>>>()?;
218
+ self.keys
219
+ .set(keys)
220
+ .map_err(|e| ItsiError::default(format!("Failed to set keys: {:?}", e)))?;
221
+ Ok(())
222
+ }
223
+
224
+ async fn before(
225
+ &self,
226
+ req: HttpRequest,
227
+ _context: &mut RequestContext,
228
+ ) -> Result<Either<HttpRequest, HttpResponse>> {
229
+ let token_str = match &self.token_source {
230
+ TokenSource::Header { name, prefix } => {
231
+ if let Some(header) = req.header(name) {
232
+ if let Some(prefix) = prefix {
233
+ Some(header.strip_prefix(prefix).unwrap_or("").trim_ascii())
234
+ } else {
235
+ Some(header.trim_ascii())
236
+ }
237
+ } else {
238
+ None
239
+ }
240
+ }
241
+ TokenSource::Query(query_name) => req.query_param(query_name),
242
+ };
243
+
244
+ if token_str.is_none() {
245
+ return Ok(Either::Right(
246
+ self.error_response.to_http_response(&req).await,
247
+ ));
248
+ }
249
+
250
+ let token_str = token_str.unwrap();
251
+ let token_meta = Token::decode_metadata(token_str);
252
+
253
+ if token_meta.is_err() {
254
+ return Ok(Either::Right(
255
+ self.error_response.to_http_response(&req).await,
256
+ ));
257
+ }
258
+ let token_meta: std::result::Result<JwtAlgorithm, ItsiError> =
259
+ token_meta.unwrap().algorithm().try_into();
260
+ if token_meta.is_err() {
261
+ return Ok(Either::Right(
262
+ self.error_response.to_http_response(&req).await,
263
+ ));
264
+ }
265
+ let algorithm = token_meta.unwrap();
266
+
267
+ if !self.verifiers.contains_key(&algorithm) {
268
+ return Ok(Either::Right(
269
+ self.error_response.to_http_response(&req).await,
270
+ ));
271
+ }
272
+
273
+ let keys = self.keys.get().unwrap().get(&algorithm).unwrap();
274
+
275
+ let verified_claims = keys.iter().find_map(|key| key.verify(token_str).ok());
276
+ if verified_claims.is_none() {
277
+ return Ok(Either::Right(
278
+ self.error_response.to_http_response(&req).await,
279
+ ));
280
+ }
281
+
282
+ let claims = verified_claims.unwrap();
283
+
284
+ if let Some(expected_audiences) = &self.audiences {
285
+ // The aud claim may be a string or an array.
286
+ if let Some(audiences) = &claims.audiences {
287
+ if !audiences.contains(expected_audiences) {
288
+ return Ok(Either::Right(
289
+ self.error_response.to_http_response(&req).await,
290
+ ));
291
+ }
292
+ }
293
+ }
294
+
295
+ if let Some(expected_subjects) = &self.subjects {
296
+ // The aud claim may be a string or an array.
297
+ if let Some(subject) = &claims.subject {
298
+ if !expected_subjects.contains(subject) {
299
+ return Ok(Either::Right(
300
+ self.error_response.to_http_response(&req).await,
301
+ ));
302
+ }
303
+ }
304
+ }
305
+
306
+ if let Some(expected_issuers) = &self.issuers {
307
+ // The aud claim may be a string or an array.
308
+ if let Some(issuer) = &claims.issuer {
309
+ if !expected_issuers.contains(issuer) {
310
+ return Ok(Either::Right(
311
+ self.error_response.to_http_response(&req).await,
312
+ ));
313
+ }
314
+ }
315
+ }
316
+
317
+ Ok(Either::Left(req))
318
+ }
319
+ }
320
+
321
+ impl FromValue for AuthJwt {}
@@ -0,0 +1,139 @@
1
+ use super::{FromValue, MiddlewareLayer};
2
+ use crate::server::{itsi_service::RequestContext, types::HttpResponse};
3
+ use async_trait::async_trait;
4
+ use http::{HeaderName, HeaderValue};
5
+ use magnus::error::Result;
6
+ use serde::Deserialize;
7
+ use std::{collections::HashMap, sync::OnceLock};
8
+
9
+ #[derive(Debug, Clone, Deserialize)]
10
+ pub struct CacheControl {
11
+ #[serde(default)]
12
+ pub max_age: Option<u64>,
13
+ #[serde(default)]
14
+ pub s_max_age: Option<u64>,
15
+ #[serde(default)]
16
+ pub stale_while_revalidate: Option<u64>,
17
+ #[serde(default)]
18
+ pub stale_if_error: Option<u64>,
19
+ #[serde(default)]
20
+ pub public: bool,
21
+ #[serde(default)]
22
+ pub private: bool,
23
+ #[serde(default)]
24
+ pub no_cache: bool,
25
+ #[serde(default)]
26
+ pub no_store: bool,
27
+ #[serde(default)]
28
+ pub must_revalidate: bool,
29
+ #[serde(default)]
30
+ pub proxy_revalidate: bool,
31
+ #[serde(default)]
32
+ pub immutable: bool,
33
+ #[serde(default)]
34
+ pub vary: Vec<String>,
35
+ #[serde(default)]
36
+ pub additional_headers: HashMap<String, String>,
37
+ #[serde(skip_deserializing)]
38
+ pub cache_control_str: OnceLock<String>,
39
+ }
40
+
41
+ #[async_trait]
42
+ impl MiddlewareLayer for CacheControl {
43
+ async fn initialize(&self) -> Result<()> {
44
+ let mut directives = Vec::new();
45
+
46
+ if self.public && !self.private {
47
+ directives.push("public".to_owned());
48
+ } else if self.private && !self.public {
49
+ directives.push("private".to_owned());
50
+ }
51
+ if self.no_cache {
52
+ directives.push("no-cache".to_owned());
53
+ }
54
+ if self.no_store {
55
+ directives.push("no-store".to_owned());
56
+ }
57
+ if self.must_revalidate {
58
+ directives.push("must-revalidate".to_owned());
59
+ }
60
+ if self.proxy_revalidate {
61
+ directives.push("proxy-revalidate".to_owned());
62
+ }
63
+ if self.immutable {
64
+ directives.push("immutable".to_owned());
65
+ }
66
+
67
+ // Add age parameters
68
+ if let Some(max_age) = self.max_age {
69
+ directives.push(format!("max-age={}", max_age));
70
+ }
71
+
72
+ if let Some(s_max_age) = self.s_max_age {
73
+ directives.push(format!("s-maxage={}", s_max_age));
74
+ }
75
+
76
+ if let Some(stale_while_revalidate) = self.stale_while_revalidate {
77
+ directives.push(format!("stale-while-revalidate={}", stale_while_revalidate));
78
+ }
79
+
80
+ if let Some(stale_if_error) = self.stale_if_error {
81
+ directives.push(format!("stale-if-error={}", stale_if_error));
82
+ }
83
+
84
+ // Set the Cache-Control header if we have directives
85
+ if !directives.is_empty() {
86
+ let cache_control_value = directives.join(", ");
87
+ self.cache_control_str.set(cache_control_value).unwrap();
88
+ }
89
+
90
+ Ok(())
91
+ }
92
+
93
+ async fn after(&self, mut resp: HttpResponse, _: &mut RequestContext) -> HttpResponse {
94
+ // Skip for statuses where caching doesn't make sense
95
+ let status = resp.status().as_u16();
96
+ if matches!(status, 401 | 403 | 500..=599) {
97
+ return resp;
98
+ }
99
+
100
+ // Set the Cache-Control header if we have directives
101
+ if let Some(cache_control_value) = self.cache_control_str.get() {
102
+ if let Ok(value) = HeaderValue::from_str(cache_control_value) {
103
+ resp.headers_mut().insert("Cache-Control", value);
104
+ }
105
+ }
106
+
107
+ // Set Expires header based on max-age if present
108
+ if let Some(max_age) = self.max_age {
109
+ // Set the Expires header based on max-age
110
+ // Use a helper to format the HTTP date correctly
111
+ let expires = chrono::Utc::now() + chrono::Duration::seconds(max_age as i64);
112
+ let expires_str = expires.format("%a, %d %b %Y %H:%M:%S GMT").to_string();
113
+ if let Ok(value) = HeaderValue::from_str(&expires_str) {
114
+ resp.headers_mut().insert("Expires", value);
115
+ }
116
+ }
117
+
118
+ // Set Vary header
119
+ if !self.vary.is_empty() {
120
+ let vary_value = self.vary.join(", ");
121
+ if let Ok(value) = HeaderValue::from_str(&vary_value) {
122
+ resp.headers_mut().insert("Vary", value);
123
+ }
124
+ }
125
+
126
+ // Set additional custom headers
127
+ for (name, value) in &self.additional_headers {
128
+ if let Ok(header_value) = HeaderValue::from_str(value) {
129
+ if let Ok(header_name) = name.parse::<HeaderName>() {
130
+ resp.headers_mut().insert(header_name, header_value);
131
+ }
132
+ }
133
+ }
134
+
135
+ resp
136
+ }
137
+ }
138
+
139
+ impl FromValue for CacheControl {}