spikard 0.3.6 → 0.5.0
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.
- checksums.yaml +4 -4
- data/README.md +21 -6
- data/ext/spikard_rb/Cargo.toml +2 -2
- data/lib/spikard/app.rb +33 -14
- data/lib/spikard/testing.rb +47 -12
- data/lib/spikard/version.rb +1 -1
- data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -0
- data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +132 -0
- data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +752 -0
- data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -0
- data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -0
- data/vendor/crates/spikard-bindings-shared/src/error_response.rs +401 -0
- data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +238 -0
- data/vendor/crates/spikard-bindings-shared/src/lib.rs +24 -0
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +292 -0
- data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +616 -0
- data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +305 -0
- data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -0
- data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +351 -0
- data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +454 -0
- data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +383 -0
- data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +280 -0
- data/vendor/crates/spikard-core/Cargo.toml +4 -4
- data/vendor/crates/spikard-core/src/debug.rs +64 -0
- data/vendor/crates/spikard-core/src/di/container.rs +3 -27
- data/vendor/crates/spikard-core/src/di/factory.rs +1 -5
- data/vendor/crates/spikard-core/src/di/graph.rs +8 -47
- data/vendor/crates/spikard-core/src/di/mod.rs +1 -1
- data/vendor/crates/spikard-core/src/di/resolved.rs +1 -7
- data/vendor/crates/spikard-core/src/di/value.rs +2 -4
- data/vendor/crates/spikard-core/src/errors.rs +30 -0
- data/vendor/crates/spikard-core/src/http.rs +262 -0
- data/vendor/crates/spikard-core/src/lib.rs +1 -1
- data/vendor/crates/spikard-core/src/lifecycle.rs +764 -0
- data/vendor/crates/spikard-core/src/metadata.rs +389 -0
- data/vendor/crates/spikard-core/src/parameters.rs +1962 -159
- data/vendor/crates/spikard-core/src/problem.rs +34 -0
- data/vendor/crates/spikard-core/src/request_data.rs +966 -1
- data/vendor/crates/spikard-core/src/router.rs +263 -2
- data/vendor/crates/spikard-core/src/validation/error_mapper.rs +688 -0
- data/vendor/crates/spikard-core/src/{validation.rs → validation/mod.rs} +26 -268
- data/vendor/crates/spikard-http/Cargo.toml +12 -16
- data/vendor/crates/spikard-http/examples/sse-notifications.rs +148 -0
- data/vendor/crates/spikard-http/examples/websocket-chat.rs +92 -0
- data/vendor/crates/spikard-http/src/auth.rs +65 -16
- data/vendor/crates/spikard-http/src/background.rs +1614 -3
- data/vendor/crates/spikard-http/src/cors.rs +515 -0
- data/vendor/crates/spikard-http/src/debug.rs +65 -0
- data/vendor/crates/spikard-http/src/di_handler.rs +1322 -77
- data/vendor/crates/spikard-http/src/handler_response.rs +711 -0
- data/vendor/crates/spikard-http/src/handler_trait.rs +607 -5
- data/vendor/crates/spikard-http/src/handler_trait_tests.rs +6 -0
- data/vendor/crates/spikard-http/src/lib.rs +33 -28
- data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +81 -0
- data/vendor/crates/spikard-http/src/lifecycle.rs +765 -0
- data/vendor/crates/spikard-http/src/middleware/mod.rs +372 -117
- data/vendor/crates/spikard-http/src/middleware/multipart.rs +836 -10
- data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +409 -43
- data/vendor/crates/spikard-http/src/middleware/validation.rs +513 -65
- data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +345 -0
- data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +1055 -0
- data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +473 -3
- data/vendor/crates/spikard-http/src/query_parser.rs +455 -31
- data/vendor/crates/spikard-http/src/response.rs +321 -0
- data/vendor/crates/spikard-http/src/server/handler.rs +1572 -9
- data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +136 -0
- data/vendor/crates/spikard-http/src/server/mod.rs +875 -178
- data/vendor/crates/spikard-http/src/server/request_extraction.rs +674 -23
- data/vendor/crates/spikard-http/src/server/routing_factory.rs +599 -0
- data/vendor/crates/spikard-http/src/sse.rs +983 -21
- data/vendor/crates/spikard-http/src/testing/form.rs +38 -0
- data/vendor/crates/spikard-http/src/testing/test_client.rs +0 -2
- data/vendor/crates/spikard-http/src/testing.rs +7 -7
- data/vendor/crates/spikard-http/src/websocket.rs +1055 -4
- data/vendor/crates/spikard-http/tests/background_behavior.rs +832 -0
- data/vendor/crates/spikard-http/tests/common/handlers.rs +309 -0
- data/vendor/crates/spikard-http/tests/common/mod.rs +26 -0
- data/vendor/crates/spikard-http/tests/di_integration.rs +192 -0
- data/vendor/crates/spikard-http/tests/doc_snippets.rs +5 -0
- data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1093 -0
- data/vendor/crates/spikard-http/tests/multipart_behavior.rs +656 -0
- data/vendor/crates/spikard-http/tests/server_config_builder.rs +314 -0
- data/vendor/crates/spikard-http/tests/sse_behavior.rs +620 -0
- data/vendor/crates/spikard-http/tests/websocket_behavior.rs +663 -0
- data/vendor/crates/spikard-rb/Cargo.toml +10 -4
- data/vendor/crates/spikard-rb/build.rs +196 -5
- data/vendor/crates/spikard-rb/src/config/mod.rs +5 -0
- data/vendor/crates/spikard-rb/src/{config.rs → config/server_config.rs} +100 -109
- data/vendor/crates/spikard-rb/src/conversion.rs +121 -20
- data/vendor/crates/spikard-rb/src/di/builder.rs +100 -0
- data/vendor/crates/spikard-rb/src/{di.rs → di/mod.rs} +12 -46
- data/vendor/crates/spikard-rb/src/handler.rs +100 -107
- data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -0
- data/vendor/crates/spikard-rb/src/lib.rs +467 -1428
- data/vendor/crates/spikard-rb/src/lifecycle.rs +1 -0
- data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -0
- data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +447 -0
- data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -0
- data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +324 -0
- data/vendor/crates/spikard-rb/src/server.rs +47 -22
- data/vendor/crates/spikard-rb/src/{test_client.rs → testing/client.rs} +187 -40
- data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -0
- data/vendor/crates/spikard-rb/src/testing/websocket.rs +635 -0
- data/vendor/crates/spikard-rb/src/websocket.rs +178 -37
- metadata +46 -13
- data/vendor/crates/spikard-http/src/parameters.rs +0 -1
- data/vendor/crates/spikard-http/src/problem.rs +0 -1
- data/vendor/crates/spikard-http/src/router.rs +0 -1
- data/vendor/crates/spikard-http/src/schema_registry.rs +0 -1
- data/vendor/crates/spikard-http/src/type_hints.rs +0 -1
- data/vendor/crates/spikard-http/src/validation.rs +0 -1
- data/vendor/crates/spikard-rb/src/test_websocket.rs +0 -221
- /data/vendor/crates/spikard-rb/src/{test_sse.rs → testing/sse.rs} +0 -0
|
@@ -43,6 +43,13 @@ pub struct Claims {
|
|
|
43
43
|
/// Validates JWT tokens from the Authorization header (Bearer scheme).
|
|
44
44
|
/// On success, the validated claims are available to downstream handlers.
|
|
45
45
|
/// On failure, returns 401 Unauthorized with RFC 9457 Problem Details.
|
|
46
|
+
///
|
|
47
|
+
/// Coverage: Tested via integration tests (`auth_integration.rs`)
|
|
48
|
+
///
|
|
49
|
+
/// # Errors
|
|
50
|
+
/// Returns an error response when the Authorization header is missing, malformed,
|
|
51
|
+
/// the token is invalid, or configuration is incorrect.
|
|
52
|
+
#[cfg(not(tarpaulin_include))]
|
|
46
53
|
pub async fn jwt_auth_middleware(
|
|
47
54
|
config: JwtConfig,
|
|
48
55
|
headers: HeaderMap,
|
|
@@ -107,20 +114,18 @@ pub async fn jwt_auth_middleware(
|
|
|
107
114
|
let detail = match e.kind() {
|
|
108
115
|
jsonwebtoken::errors::ErrorKind::ExpiredSignature => "Token has expired".to_string(),
|
|
109
116
|
jsonwebtoken::errors::ErrorKind::InvalidToken => "Token is invalid".to_string(),
|
|
110
|
-
jsonwebtoken::errors::ErrorKind::InvalidSignature
|
|
111
|
-
|
|
112
|
-
jsonwebtoken::errors::ErrorKind::InvalidAudience => "Token audience is invalid".to_string(),
|
|
113
|
-
jsonwebtoken::errors::ErrorKind::InvalidIssuer => {
|
|
114
|
-
if let Some(ref expected_iss) = config.issuer {
|
|
115
|
-
format!("Token issuer is invalid, expected '{}'", expected_iss)
|
|
116
|
-
} else {
|
|
117
|
-
"Token issuer is invalid".to_string()
|
|
118
|
-
}
|
|
117
|
+
jsonwebtoken::errors::ErrorKind::InvalidSignature | jsonwebtoken::errors::ErrorKind::Base64(_) => {
|
|
118
|
+
"Token signature is invalid".to_string()
|
|
119
119
|
}
|
|
120
|
+
jsonwebtoken::errors::ErrorKind::InvalidAudience => "Token audience is invalid".to_string(),
|
|
121
|
+
jsonwebtoken::errors::ErrorKind::InvalidIssuer => config.issuer.as_ref().map_or_else(
|
|
122
|
+
|| "Token issuer is invalid".to_string(),
|
|
123
|
+
|expected_iss| format!("Token issuer is invalid, expected '{expected_iss}'"),
|
|
124
|
+
),
|
|
120
125
|
jsonwebtoken::errors::ErrorKind::ImmatureSignature => {
|
|
121
126
|
"JWT not valid yet, not before claim is in the future".to_string()
|
|
122
127
|
}
|
|
123
|
-
_ => format!("Token validation failed: {}"
|
|
128
|
+
_ => format!("Token validation failed: {e}"),
|
|
124
129
|
};
|
|
125
130
|
|
|
126
131
|
let problem =
|
|
@@ -146,7 +151,7 @@ fn parse_algorithm(alg: &str) -> Result<Algorithm, String> {
|
|
|
146
151
|
"PS256" => Ok(Algorithm::PS256),
|
|
147
152
|
"PS384" => Ok(Algorithm::PS384),
|
|
148
153
|
"PS512" => Ok(Algorithm::PS512),
|
|
149
|
-
_ => Err(format!("Unsupported algorithm: {}"
|
|
154
|
+
_ => Err(format!("Unsupported algorithm: {alg}")),
|
|
150
155
|
}
|
|
151
156
|
}
|
|
152
157
|
|
|
@@ -156,6 +161,12 @@ fn parse_algorithm(alg: &str) -> Result<Algorithm, String> {
|
|
|
156
161
|
/// Checks header first, then query parameter as fallback.
|
|
157
162
|
/// On success, the request proceeds to the next handler.
|
|
158
163
|
/// On failure, returns 401 Unauthorized with RFC 9457 Problem Details.
|
|
164
|
+
///
|
|
165
|
+
/// Coverage: Tested via integration tests (`auth_integration.rs`)
|
|
166
|
+
///
|
|
167
|
+
/// # Errors
|
|
168
|
+
/// Returns an error response when the API key is missing or invalid.
|
|
169
|
+
#[cfg(not(tarpaulin_include))]
|
|
159
170
|
pub async fn api_key_auth_middleware(
|
|
160
171
|
config: ApiKeyConfig,
|
|
161
172
|
headers: HeaderMap,
|
|
@@ -168,11 +179,7 @@ pub async fn api_key_auth_middleware(
|
|
|
168
179
|
|
|
169
180
|
let api_key_from_header = headers.get(&config.header_name).and_then(|v| v.to_str().ok());
|
|
170
181
|
|
|
171
|
-
let api_key =
|
|
172
|
-
Some(key)
|
|
173
|
-
} else {
|
|
174
|
-
extract_api_key_from_query(&uri)
|
|
175
|
-
};
|
|
182
|
+
let api_key = api_key_from_header.map_or_else(|| extract_api_key_from_query(&uri), Some);
|
|
176
183
|
|
|
177
184
|
let api_key = api_key.ok_or_else(|| {
|
|
178
185
|
let problem =
|
|
@@ -244,4 +251,46 @@ mod tests {
|
|
|
244
251
|
assert!(json.contains("user123"));
|
|
245
252
|
assert!(json.contains("1234567890"));
|
|
246
253
|
}
|
|
254
|
+
|
|
255
|
+
#[test]
|
|
256
|
+
fn test_extract_api_key_from_query_api_key() {
|
|
257
|
+
let uri: axum::http::Uri = "/api/endpoint?api_key=secret123".parse().unwrap();
|
|
258
|
+
let result = extract_api_key_from_query(&uri);
|
|
259
|
+
assert_eq!(result, Some("secret123"));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#[test]
|
|
263
|
+
fn test_extract_api_key_from_query_api_key_camel_case() {
|
|
264
|
+
let uri: axum::http::Uri = "/api/endpoint?apiKey=mykey456".parse().unwrap();
|
|
265
|
+
let result = extract_api_key_from_query(&uri);
|
|
266
|
+
assert_eq!(result, Some("mykey456"));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
#[test]
|
|
270
|
+
fn test_extract_api_key_from_query_key() {
|
|
271
|
+
let uri: axum::http::Uri = "/api/endpoint?key=testkey789".parse().unwrap();
|
|
272
|
+
let result = extract_api_key_from_query(&uri);
|
|
273
|
+
assert_eq!(result, Some("testkey789"));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#[test]
|
|
277
|
+
fn test_extract_api_key_from_query_no_key() {
|
|
278
|
+
let uri: axum::http::Uri = "/api/endpoint?foo=bar&baz=qux".parse().unwrap();
|
|
279
|
+
let result = extract_api_key_from_query(&uri);
|
|
280
|
+
assert_eq!(result, None);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
#[test]
|
|
284
|
+
fn test_extract_api_key_from_query_empty_string() {
|
|
285
|
+
let uri: axum::http::Uri = "/api/endpoint".parse().unwrap();
|
|
286
|
+
let result = extract_api_key_from_query(&uri);
|
|
287
|
+
assert_eq!(result, None);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
#[test]
|
|
291
|
+
fn test_extract_api_key_from_query_multiple_params() {
|
|
292
|
+
let uri: axum::http::Uri = "/api/endpoint?foo=bar&api_key=found&baz=qux".parse().unwrap();
|
|
293
|
+
let result = extract_api_key_from_query(&uri);
|
|
294
|
+
assert_eq!(result, Some("found"));
|
|
295
|
+
}
|
|
247
296
|
}
|