object-storage-proxy 0.4.1__tar.gz → 0.4.2__tar.gz

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 (98) hide show
  1. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/Cargo.lock +1 -1
  2. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/Cargo.toml +1 -1
  3. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/PKG-INFO +5 -3
  4. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/README.md +4 -2
  5. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/query.sql +2 -2
  6. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/credentials/signer.rs +3 -1
  7. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/lib.rs +88 -16
  8. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/parsers/cos_map.rs +17 -0
  9. object_storage_proxy-0.4.2/src/utils/functions.rs +27 -0
  10. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/utils/mod.rs +1 -0
  11. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/utils/validator.rs +61 -16
  12. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/test_server.py +40 -33
  13. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/.cargo/config.toml +0 -0
  14. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/.github/workflows/ci.yml +0 -0
  15. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/.gitignore +0 -0
  16. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/LICENSE +0 -0
  17. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/img/logo.svg +0 -0
  18. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/img/request_lifecycle.svg +0 -0
  19. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/img/request_stages.svg +0 -0
  20. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/docker/Dockerfile +0 -0
  21. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/docker-compose.yml +0 -0
  22. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/etc/catalog/hive.properties +0 -0
  23. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/etc/config.properties +0 -0
  24. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/etc/hadoop-conf/core-site.xml +0 -0
  25. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/etc/hadoop-conf/hive-site.xml +0 -0
  26. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/etc/jvm.config +0 -0
  27. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/hive-conf/hive-site.xml +0 -0
  28. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/worker/etc/catalog/hive.properties +0 -0
  29. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/worker/etc/config.properties +0 -0
  30. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/worker/etc/hadoop-conf/core-site.xml +0 -0
  31. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/worker/etc/hadoop-conf/hive-site.xml +0 -0
  32. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/worker/etc/jvm.config +0 -0
  33. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/worker/etc/log.properties +0 -0
  34. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/presto/worker/etc/node.properties +0 -0
  35. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/compose.yml +0 -0
  36. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/conf/hive-site.xml +0 -0
  37. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/catalog/hive.properties +0 -0
  38. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/catalog/tpcds.properties +0 -0
  39. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/catalog/tpch.properties +0 -0
  40. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/config.properties +0 -0
  41. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/hadoop-conf/core-site.xml +0 -0
  42. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/hadoop-conf/hive-site.xml +0 -0
  43. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/jvm.config +0 -0
  44. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/log.properties +0 -0
  45. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/coordinator/etc/node.properties +0 -0
  46. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/catalog/hive.properties +0 -0
  47. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/catalog/tpcds.properties +0 -0
  48. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/catalog/tpch.properties +0 -0
  49. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/config.properties +0 -0
  50. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/hadoop-conf/core-site.xml +0 -0
  51. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/hadoop-conf/hive-site.xml +0 -0
  52. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/jvm.config +0 -0
  53. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/log.properties +0 -0
  54. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/etc/node.properties +0 -0
  55. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/hadoop-conf/core-site.xml +0 -0
  56. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/hadoop-conf/hive-site.xml +0 -0
  57. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/lib/postgresql-42.7.4.jar +0 -0
  58. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/README.md +0 -0
  59. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/assets/bucket.png +0 -0
  60. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/assets/login.png +0 -0
  61. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/assets/metastore.png +0 -0
  62. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/assets/minio.png +0 -0
  63. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/assets/runtime.png +0 -0
  64. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/assets/storage.png +0 -0
  65. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/assets/tiny.png +0 -0
  66. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/conf/core-site.xml +0 -0
  67. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/conf/metastore-site.xml +0 -0
  68. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/docker-compose.yml +0 -0
  69. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/etc/catalog/minio.properties +0 -0
  70. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/etc/catalog/tpcds.properties +0 -0
  71. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/etc/catalog/tpch.properties +0 -0
  72. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/etc/config.properties +0 -0
  73. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/etc/jvm.config +0 -0
  74. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/etc/log.properties +0 -0
  75. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/trino-minio/etc/node.properties +0 -0
  76. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/catalog/hive.properties +0 -0
  77. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/catalog/tpcds.properties +0 -0
  78. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/catalog/tpch.properties +0 -0
  79. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/config.properties +0 -0
  80. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/hadoop-conf/core-site.xml +0 -0
  81. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/hadoop-conf/hive-site.xml +0 -0
  82. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/jvm.config +0 -0
  83. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/log.properties +0 -0
  84. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/integration/trino/worker/etc/node.properties +0 -0
  85. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/localhost.cnf +0 -0
  86. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/pyproject.toml +0 -0
  87. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/requirements.txt +0 -0
  88. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/credentials/hmac_keystore.rs +0 -0
  89. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/credentials/mod.rs +0 -0
  90. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/credentials/models.rs +0 -0
  91. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/credentials/secrets_proxy.rs +0 -0
  92. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/object_storage_proxy.pyi +0 -0
  93. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/parsers/credentials.rs +0 -0
  94. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/parsers/keystore.rs +0 -0
  95. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/parsers/mod.rs +0 -0
  96. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/src/parsers/path.rs +0 -0
  97. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/test_integration.sh +0 -0
  98. {object_storage_proxy-0.4.1 → object_storage_proxy-0.4.2}/uv.lock +0 -0
@@ -1757,7 +1757,7 @@ dependencies = [
1757
1757
 
1758
1758
  [[package]]
1759
1759
  name = "object-storage-proxy"
1760
- version = "0.4.1"
1760
+ version = "0.4.2"
1761
1761
  dependencies = [
1762
1762
  "async-stream",
1763
1763
  "async-trait",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "object-storage-proxy"
3
- version = "0.4.1"
3
+ version = "0.4.2"
4
4
  edition = "2024"
5
5
 
6
6
  [dependencies]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: object-storage-proxy
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Classifier: License :: Other/Proprietary License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: Implementation :: CPython
@@ -104,12 +104,14 @@ cos_map = {
104
104
  }
105
105
  ```
106
106
 
107
- The Python callables take two arguments:
108
- TODO: add prefix for fine-grained validation
107
+ The Python callables take two or three arguments:
109
108
 
110
109
  - token: parsed from the original aws request's authorization header
111
110
  - bucket: parsed from the uri path
112
111
 
112
+ the validator may also take a third optional argument
113
+ - request: dict of the original request (method, path, query, ...)
114
+
113
115
  ```python
114
116
  def your_credentials_fetcher(token: str, bucket: str) -> str
115
117
  def your_request_authorizer(token: str, bucket: str) -> bool
@@ -85,12 +85,14 @@ cos_map = {
85
85
  }
86
86
  ```
87
87
 
88
- The Python callables take two arguments:
89
- TODO: add prefix for fine-grained validation
88
+ The Python callables take two or three arguments:
90
89
 
91
90
  - token: parsed from the original aws request's authorization header
92
91
  - bucket: parsed from the uri path
93
92
 
93
+ the validator may also take a third optional argument
94
+ - request: dict of the original request (method, path, query, ...)
95
+
94
96
  ```python
95
97
  def your_credentials_fetcher(token: str, bucket: str) -> str
96
98
  def your_request_authorizer(token: str, bucket: str) -> bool
@@ -54,7 +54,7 @@ WITH order_stats AS (
54
54
  o.o_custkey AS custkey,
55
55
  COUNT(*) AS order_count,
56
56
  MAX(o.o_orderdate) AS last_order_date
57
- FROM hive.default.orders AS o
57
+ FROM hive.default.orders_minio AS o
58
58
  GROUP BY
59
59
  o.o_custkey
60
60
  ORDER BY
@@ -67,7 +67,7 @@ SELECT
67
67
  os.order_count,
68
68
  os.last_order_date
69
69
  FROM order_stats AS os
70
- JOIN hive.default.customer AS c
70
+ JOIN hive.default.customer_minio AS c
71
71
  ON os.custkey = c.c_custkey
72
72
  ORDER BY
73
73
  os.order_count DESC
@@ -160,7 +160,7 @@ impl<'a> AwsSign<'a, HashMap<String, String>> {
160
160
  // })
161
161
  // .collect();
162
162
 
163
- debug!("{:#?}", &url);
163
+ dbg!("{:#?}", &url);
164
164
  let url: Url = url.parse().unwrap();
165
165
  // let headers: HashMap<String, String> = headers
166
166
  // .iter()
@@ -1263,6 +1263,8 @@ mod tests {
1263
1263
  port: 443,
1264
1264
  api_key: None,
1265
1265
  ttl: None,
1266
+ tls: Some(true),
1267
+ addressing_style: Some("path".to_string()),
1266
1268
  }
1267
1269
  }
1268
1270
 
@@ -20,6 +20,8 @@ use std::sync::{
20
20
  atomic::{AtomicBool, AtomicUsize, Ordering},
21
21
  };
22
22
 
23
+ // use utils::functions::inspect_callable_signature;
24
+
23
25
 
24
26
  use std::collections::HashMap;
25
27
  use std::fmt::Debug;
@@ -252,13 +254,14 @@ impl ProxyHttp for MyProxy {
252
254
 
253
255
  let path = session.req_header().uri.path();
254
256
 
257
+
255
258
  let parse_path_result = parse_path(path);
256
259
  if parse_path_result.is_err() {
257
260
  error!("Failed to parse path: {:?}", parse_path_result);
258
261
  return Err(pingora::Error::new_str("Failed to parse path"));
259
262
  }
260
263
 
261
- let (_, (bucket, _)) = parse_path(path).unwrap();
264
+ let (_, (bucket, _)) = parse_path_result.unwrap();
262
265
 
263
266
  let hdr_bucket = bucket.to_owned();
264
267
 
@@ -266,20 +269,39 @@ impl ProxyHttp for MyProxy {
266
269
  let map = ctx.cos_mapping.read().await;
267
270
  map.get(&hdr_bucket).cloned()
268
271
  };
272
+
273
+ let addressing_style = bucket_config.clone()
274
+ .and_then(|config| config.addressing_style)
275
+ .unwrap_or("virtual".to_string());
276
+
269
277
  let endpoint = match bucket_config.clone() {
270
- Some(config) => format!("{}.{}", bucket, config.host.to_owned()),
278
+ Some(config) => {
279
+ if addressing_style == "path" {
280
+ config.host.to_owned()
281
+ } else {
282
+ format!("{}.{}", bucket, config.host)
283
+ }
284
+ },
271
285
  None => {
272
286
  format!("{}.{}", bucket, self.cos_endpoint)
273
287
  }
274
288
  };
275
289
 
276
- let port = bucket_config
290
+ let port = bucket_config.clone()
277
291
  .and_then(|config| Some(config.port))
278
292
  .unwrap_or(443);
279
293
 
280
294
  let addr = (endpoint.clone(), port);
281
295
 
282
- let mut peer = Box::new(HttpPeer::new(addr, true, endpoint.clone()));
296
+ let endpoint_is_tls = bucket_config
297
+ .and_then(|config| config.tls)
298
+ .unwrap_or(true);
299
+
300
+ dbg!("is_tls: {}", endpoint_is_tls);
301
+ dbg!("endpoint: {}", &endpoint);
302
+
303
+ let mut peer = Box::new(HttpPeer::new(addr, endpoint_is_tls, endpoint.clone()));
304
+ dbg!("peer: {:#?}", &peer);
283
305
 
284
306
  // todo: make ths configurable
285
307
 
@@ -322,21 +344,36 @@ impl ProxyHttp for MyProxy {
322
344
  info!("request method : {}", session.req_header().method);
323
345
 
324
346
  let parsed_query_result = parse_query(request_query);
347
+
325
348
  if parsed_query_result.is_err() {
326
349
  error!("Failed to parse query: {:?}", parsed_query_result);
327
350
  return Err(pingora::Error::new_str("Failed to parse query"));
328
351
  }
329
- let (rest, query_dict) = parsed_query_result.unwrap();
352
+ let (rest, mut query_dict) = parsed_query_result.unwrap();
330
353
  if rest.is_empty() {
331
354
  info!("Parsed query: {:#?}", query_dict);
332
355
  } else {
333
356
  error!("Failed to parse query: {}", rest);
334
357
  }
335
358
 
336
- info!("Parsed query: {:#?}", query_dict);
359
+ query_dict.insert("method".to_string(), session.req_header().method.to_string());
360
+ query_dict.insert("path".to_string(), session.req_header().uri.path().to_string());
361
+ // insert source
362
+ query_dict.insert(
363
+ "source".to_string(),
364
+ session
365
+ .req_header()
366
+ .headers
367
+ .get("x-forwarded-for")
368
+ .and_then(|h| h.to_str().ok())
369
+ .unwrap_or_default()
370
+ .to_string(),
371
+ );
372
+
337
373
 
338
374
 
339
375
 
376
+ info!("---> Parsed query: {:#?}", query_dict);
340
377
 
341
378
  if session
342
379
  .req_header()
@@ -357,7 +394,7 @@ impl ProxyHttp for MyProxy {
357
394
  return Err(pingora::Error::new_str("Failed to parse path"));
358
395
  }
359
396
 
360
- let (_, (bucket, _uri_path)) = parse_path(path).unwrap();
397
+ let (_, (bucket, _uri_path)) = parse_path_result.unwrap();
361
398
 
362
399
  let hdr_bucket = bucket.to_owned();
363
400
 
@@ -522,21 +559,27 @@ impl ProxyHttp for MyProxy {
522
559
  }
523
560
  }
524
561
  info!("Signature check passed, continuing now onto the bespoke validation");
525
- let cache_key = format!("{}:{}", &access_key, bucket);
562
+ let cache_key = format!("{}:{}:{:?}", &access_key, bucket, &query_dict);
526
563
  debug!("Cache key: {}", cache_key);
527
564
 
528
565
  let bucket_clone = bucket.to_string();
529
566
  let callback_clone: PyObject = Python::with_gil(|py| py_cb.clone_ref(py));
567
+
530
568
  let move_access_key = access_key.clone();
569
+ let req = query_dict.clone();
570
+
531
571
  ctx.auth_cache
532
572
  .get_or_validate(&cache_key, Duration::from_secs(ttl), move || {
533
573
  let tk = move_access_key.clone();
534
574
  let bu = bucket_clone.clone();
535
575
  let cb = Python::with_gil(|py| callback_clone.clone_ref(py));
536
- async move {
537
- validate_request(&tk, &bu, cb)
538
- .await
539
- .map_err(|_| pingora::Error::new_str("Validator error"))
576
+ {
577
+ let req_value = req.clone();
578
+ async move {
579
+ validate_request(&tk, &bu, &req_value, cb)
580
+ .await
581
+ .map_err(|_| pingora::Error::new_str("Validator error"))
582
+ }
540
583
  }
541
584
  })
542
585
  .await?
@@ -630,8 +673,14 @@ impl ProxyHttp for MyProxy {
630
673
  let _ = upstream_request.remove_header("accept-encoding");
631
674
 
632
675
  debug!("upstream_request_filter::start");
676
+
677
+
633
678
  let (_, (bucket, my_updated_url)) = parse_path(upstream_request.uri.path()).unwrap();
634
679
 
680
+ dbg!(&my_updated_url);
681
+
682
+
683
+
635
684
  let hdr_bucket = bucket.to_string();
636
685
 
637
686
  let my_query = match upstream_request.uri.query() {
@@ -644,12 +693,33 @@ impl ProxyHttp for MyProxy {
644
693
  map.get(&hdr_bucket).cloned()
645
694
  };
646
695
 
696
+ let addressing_style = bucket_config
697
+ .clone()
698
+ .and_then(|config| config.addressing_style)
699
+ .unwrap_or("virtual".to_string());
700
+
701
+
702
+ let this_url = match addressing_style.as_str() {
703
+ "virtual" => my_updated_url,
704
+ _ => {
705
+
706
+ let u_url = format!("/{}{}", bucket, my_updated_url);
707
+ dbg!("u_url: {}", &u_url);
708
+ &u_url.clone()
709
+ },
710
+ };
711
+
712
+
647
713
  let endpoint = match bucket_config.clone() {
648
714
  Some(cfg) => {
715
+ let this_host = match addressing_style.as_str() {
716
+ "path" => cfg.host.to_owned(),
717
+ _ => format!("{}.{}", bucket, cfg.host),
718
+ };
649
719
  if cfg.port == 443 {
650
- format!("{}.{}", bucket, cfg.host)
720
+ this_host
651
721
  } else {
652
- format!("{}.{}:{}", bucket, cfg.host, cfg.port)
722
+ format!("{}:{}", this_host, cfg.port)
653
723
  }
654
724
  }
655
725
  None => format!("{}.{}", bucket, self.cos_endpoint),
@@ -659,15 +729,17 @@ impl ProxyHttp for MyProxy {
659
729
 
660
730
  // Box:leak the temporary string to get a static reference which will outlive the function
661
731
  let authority = Authority::from_static(Box::leak(endpoint.clone().into_boxed_str()));
732
+ // if addressing_style == "virtual" {
662
733
 
663
734
  let new_uri = Uri::builder()
664
735
  .scheme("https")
665
736
  .authority(authority.clone())
666
- .path_and_query(my_updated_url.to_owned() + &my_query)
737
+ .path_and_query(this_url.to_owned() + &my_query)
667
738
  .build()
668
739
  .expect("should build a valid URI");
669
740
 
670
741
  upstream_request.set_uri(new_uri.clone());
742
+ // }
671
743
  upstream_request.insert_header("host", authority.as_str())?;
672
744
 
673
745
  let (maybe_hmac, maybe_api_key) = match &bucket_config {
@@ -837,7 +909,7 @@ impl ProxyHttp for MyProxy {
837
909
  upstream_request.insert_header("Authorization", format!("Bearer {bearer_token}"))?;
838
910
  }
839
911
 
840
- debug!("Sending request to upstream: {}", &new_uri);
912
+ // debug!("Sending request to upstream: {}", &new_uri);
841
913
 
842
914
  debug!("Request sent to upstream.");
843
915
  debug!("upstream_request_filter::end");
@@ -28,6 +28,8 @@ pub struct CosMapItem {
28
28
  pub access_key: Option<String>,
29
29
  pub secret_key: Option<String>,
30
30
  pub ttl: Option<u64>,
31
+ pub tls: Option<bool>,
32
+ pub addressing_style: Option<String>,
31
33
  }
32
34
 
33
35
  impl CosMapItem {
@@ -119,6 +121,13 @@ pub(crate) fn parse_cos_map(
119
121
  None
120
122
  };
121
123
 
124
+ let tls = inner_map
125
+ .get("tls")
126
+ .or_else(|| inner_map.get("is_tls_enabled"))
127
+ .map(|v| v.extract(py))
128
+ .transpose()?;
129
+
130
+
122
131
  let access_key = inner_map
123
132
  .get("access_key")
124
133
  .or_else(|| inner_map.get("accessKey"))
@@ -131,6 +140,12 @@ pub(crate) fn parse_cos_map(
131
140
  .map(|v| v.extract(py))
132
141
  .transpose()?;
133
142
 
143
+ let addressing_style = inner_map
144
+ .get("addressing_style")
145
+ .or_else(|| inner_map.get("addressingStyle"))
146
+ .map(|v| v.extract(py))
147
+ .transpose()?;
148
+
134
149
  map.insert(
135
150
  bucket.clone(),
136
151
  CosMapItem {
@@ -141,6 +156,8 @@ pub(crate) fn parse_cos_map(
141
156
  access_key,
142
157
  secret_key,
143
158
  ttl,
159
+ tls,
160
+ addressing_style,
144
161
  },
145
162
  );
146
163
  }
@@ -0,0 +1,27 @@
1
+ use pyo3::prelude::*;
2
+ use pyo3::types::{IntoPyDict, PyAny, PyFunction};
3
+ use tracing::debug;
4
+
5
+ pub(crate) fn callable_accepts_request(py: Python<'_>, callable: &PyObject) -> PyResult<bool> {
6
+
7
+ let inspect = py.import("inspect")?;
8
+ let signature = inspect.call_method1("signature", (callable.to_owned(),))?;
9
+ let parameters = signature.getattr("parameters")?;
10
+ dbg!(&parameters);
11
+ let parameters = parameters.call_method0("items")?;
12
+
13
+
14
+ for p in parameters.try_iter()? {
15
+ let (name, param) = p?.extract::<(String, PyObject)>()?;
16
+ let annotation = param.getattr(py, "annotation")?;
17
+ debug!("Param: {}", name);
18
+ let arg_type = annotation.to_string();
19
+ debug!("Annotation: {}", &arg_type);
20
+ if name == "request" && arg_type.contains("dict"){
21
+ return Ok(true)
22
+ }
23
+
24
+ }
25
+
26
+ Ok(false)
27
+ }
@@ -1 +1,2 @@
1
1
  pub mod validator;
2
+ pub mod functions;
@@ -1,6 +1,8 @@
1
- use pyo3::{PyObject, Python};
1
+ use pyo3::{types::IntoPyDict, PyObject, Python};
2
+ use rustls::crypto::hash::Hash;
2
3
  use tokio::{sync::Mutex, task};
3
4
  use tracing::{debug, error};
5
+ use tracing_subscriber::field::debug;
4
6
 
5
7
  use std::{
6
8
  collections::HashMap,
@@ -8,6 +10,8 @@ use std::{
8
10
  time::{Duration, Instant},
9
11
  };
10
12
 
13
+ use crate::utils::functions::callable_accepts_request;
14
+
11
15
  #[derive(Clone, Debug)]
12
16
  struct AuthEntry {
13
17
  authorized: bool,
@@ -107,26 +111,67 @@ impl AuthCache {
107
111
  pub async fn validate_request(
108
112
  token: &str,
109
113
  bucket: &str,
114
+ request: &HashMap<String, String>,
110
115
  callback: PyObject,
111
116
  ) -> Result<bool, String> {
112
117
  let token = token.to_string();
113
118
  let bucket = bucket.to_string();
114
119
 
115
- let authorized = task::spawn_blocking(move || {
116
- Python::with_gil(
117
- |py| match callback.call1(py, (token.as_str(), bucket.as_str())) {
118
- Ok(result_obj) => result_obj
119
- .extract::<bool>(py)
120
- .map_err(|_| "Failed to extract boolean".to_string()),
121
- Err(e) => {
122
- error!("Python callback error: {:?}", e);
123
- Err("Inner Python exception".to_string())
124
- }
125
- },
126
- )
127
- })
128
- .await
129
- .map_err(|e| format!("Join error: {:?}", e))??;
120
+ let req = request
121
+ .into_iter()
122
+ .map(|(k, v)| (k.clone(), v.clone()))
123
+ .collect::<HashMap<String, String>>();
124
+
125
+ debug!("request details sent to Python callable: {:?}", &req);
126
+
127
+ let takes_request = Python::with_gil(|py| {
128
+ let sig = callable_accepts_request(py, &callback);
129
+ if sig.is_err() {
130
+ return Err(format!("Invalid callable signature: {:?}", sig));
131
+ }
132
+ Ok(sig.unwrap())
133
+ });
134
+
135
+ if takes_request.is_err() {
136
+ return Err(format!("Invalid callable signature: {:?}", takes_request));
137
+ }
138
+ let takes_request = takes_request.unwrap();
139
+
140
+ debug!("Python callable can take request: {:?}", &takes_request);
141
+
142
+ let authorized = if takes_request {
143
+ task::spawn_blocking(move || {
144
+ Python::with_gil(
145
+ |py| match callback.call1(py, (token.as_str(), bucket.as_str(), &req)) {
146
+ Ok(result_obj) => result_obj
147
+ .extract::<bool>(py)
148
+ .map_err(|_| "Failed to extract boolean".to_string()),
149
+ Err(e) => {
150
+ error!("Python callback error: {:?}", e);
151
+ Err("Inner Python exception".to_string())
152
+ }
153
+ },
154
+ )
155
+ })
156
+ .await
157
+ .map_err(|e| format!("Join error: {:?}", e))??
158
+ } else {
159
+ task::spawn_blocking(move || {
160
+ Python::with_gil(
161
+ |py| match callback.call1(py, (token.as_str(), bucket.as_str())) {
162
+ Ok(result_obj) => result_obj
163
+ .extract::<bool>(py)
164
+ .map_err(|_| "Failed to extract boolean".to_string()),
165
+ Err(e) => {
166
+ error!("Python callback error: {:?}", e);
167
+ Err("Inner Python exception".to_string())
168
+ }
169
+ },
170
+ )
171
+ })
172
+ .await
173
+ .map_err(|e| format!("Join error: {:?}", e))??
174
+ };
130
175
 
131
176
  Ok(authorized)
132
177
  }
@@ -8,7 +8,7 @@ from dotenv import load_dotenv
8
8
  from object_storage_proxy import start_server, ProxyServerConfig
9
9
 
10
10
 
11
- _TRUES = {"y", "yes", "t", "true", "on", "1"}
11
+ _TRUES = {"y", "yes", "t", "true", "on", "1"}
12
12
  _FALSES = {"n", "no", "f", "false", "off", "0"}
13
13
 
14
14
 
@@ -23,35 +23,39 @@ def strtobool(val: str) -> bool:
23
23
 
24
24
 
25
25
  def do_api_creds(token: str, bucket: str) -> str:
26
- """Fetch credentials (ro, rw, access_denied) for the given bucket, depending on the token. """
26
+ """Fetch credentials (ro, rw, access_denied) for the given bucket, depending on the token."""
27
27
  apikey = os.getenv("COS_API_KEY")
28
28
  if not apikey:
29
29
  raise ValueError("COS_API_KEY environment variable not set")
30
-
30
+
31
31
  print(f"Fetching credentials for {bucket}...")
32
32
  return apikey
33
33
 
34
34
 
35
35
  def do_hmac_creds(token: str, bucket: str) -> str:
36
- """ Fetch HMAC credentials (ro, rw, access_denied) for the given bucket, depending on the token """
36
+ """Fetch HMAC credentials (ro, rw, access_denied) for the given bucket, depending on the token"""
37
37
  access_key = os.getenv("ACCESS_KEY")
38
38
  secret_key = os.getenv("SECRET_KEY")
39
39
  if not access_key or not secret_key:
40
40
  raise ValueError("ACCESS_KEY or SECRET_KEY environment variable not set")
41
-
41
+
42
42
  print(f"Fetching HMAC credentials for {bucket}...")
43
43
 
44
- return json.dumps({
45
- "access_key": access_key,
46
- "secret_key": secret_key
47
- })
44
+ return json.dumps({"access_key": access_key, "secret_key": secret_key})
45
+
48
46
 
49
47
  def lookup_secret_key(access_key: str) -> str | None:
50
48
  # get all environment variables ending in ACCESS_KEY
51
- access_keys = [{key:value} for key, value in os.environ.items() if key.endswith("ACCESS_KEY") and value==access_key ]
49
+ access_keys = [
50
+ {key: value}
51
+ for key, value in os.environ.items()
52
+ if key.endswith("ACCESS_KEY") and value == access_key
53
+ ]
52
54
 
53
55
  if len(access_keys) > 0:
54
- access_key_var = next((k for k, v in access_keys[0].items() if v == access_key), None)
56
+ access_key_var = next(
57
+ (k for k, v in access_keys[0].items() if v == access_key), None
58
+ )
55
59
 
56
60
  secret_key_var = access_key_var.replace("ACCESS_KEY", "SECRET_KEY")
57
61
  return os.getenv(secret_key_var, None)
@@ -59,16 +63,14 @@ def lookup_secret_key(access_key: str) -> str | None:
59
63
  print(f"no access keys found for : {access_key}")
60
64
 
61
65
 
62
- def do_validation(token: str, bucket: str) -> bool:
63
- """ Authorize the request based on token for the given bucket.
64
- You can plug in your own authorization service here.
65
- The token is the authorization token passed in the request.
66
- The bucket is the bucket name.
67
- The function should return True if the request is authorized, False otherwise.
66
+ def do_validation(token: str, bucket: str, request: dict) -> bool:
67
+ """Authorize the request based on token for the given bucket.
68
+ You can plug in your own authorization service here.
69
+ The token is the authorization token passed in the request.
70
+ The bucket is the bucket name.
71
+ The function should return True if the request is authorized, False otherwise.
68
72
  """
69
-
70
- print(f"PYTHON: Validating headers: {token} for {bucket}...")
71
- # return random.choice([True, False])
73
+ print(f"------> From Python: Validating headers: {token=}, {bucket=}, {request=}")
72
74
  return True
73
75
 
74
76
 
@@ -86,18 +88,27 @@ def main() -> None:
86
88
  raise ValueError("COS_API_KEY environment variable not set")
87
89
 
88
90
  cos_map = {
91
+ "tpch": {
92
+ "host": "biggie",
93
+ "region": "eu-de",
94
+ "port": 9000,
95
+ "access_key": "localkey",
96
+ "secret_key": "localpass",
97
+ "addressing_style": "path",
98
+ "is_tls_enabled": False,
99
+ },
89
100
  "bucket1": {
90
101
  "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
91
102
  "region": "eu-de",
92
103
  "port": 443,
93
104
  "apikey": apikey,
94
- "ttl": 0
105
+ "ttl": 0,
95
106
  },
96
107
  "bucket2": {
97
108
  "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
98
109
  "region": "eu-de",
99
110
  "port": 443,
100
- "apikey": apikey
111
+ "apikey": apikey,
101
112
  },
102
113
  "proxy-bucket01": {
103
114
  "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
@@ -105,7 +116,7 @@ def main() -> None:
105
116
  # "access_key": os.getenv("ACCESS_KEY"),
106
117
  # "secret_key": os.getenv("SECRET_KEY"),
107
118
  "port": 443,
108
- "ttl": 300
119
+ "ttl": 300,
109
120
  },
110
121
  "proxy-bucket05": {
111
122
  "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
@@ -113,7 +124,7 @@ def main() -> None:
113
124
  "access_key": os.getenv("PROXY_BUCKET05_ACCESS_KEY"),
114
125
  "secret_key": os.getenv("PROXY_BUCKET05_SECRET_KEY"),
115
126
  "port": 443,
116
- "ttl": 300
127
+ "ttl": 300,
117
128
  },
118
129
  "proxy-aws-bucket01": {
119
130
  "host": "s3.eu-west-3.amazonaws.com",
@@ -121,20 +132,19 @@ def main() -> None:
121
132
  "access_key": os.getenv("AWS_ACCESS_KEY"),
122
133
  "secret_key": os.getenv("AWS_SECRET_KEY"),
123
134
  "port": 443,
124
- "ttl": 300
125
- }
135
+ "ttl": 300,
136
+ },
126
137
  }
127
138
 
128
- hmac_keys= [
139
+ hmac_keys = [
129
140
  # {
130
141
  # "access_key": os.getenv("LOCAL_ACCESS_KEY"),
131
142
  # "secret_key": os.getenv("LOCAL_SECRET_KEY")
132
143
  # },
133
144
  {
134
145
  "access_key": os.getenv("LOCAL2_ACCESS_KEY"),
135
- "secret_key": os.getenv("LOCAL2_SECRET_KEY")
146
+ "secret_key": os.getenv("LOCAL2_SECRET_KEY"),
136
147
  },
137
-
138
148
  ]
139
149
 
140
150
  ra = ProxyServerConfig(
@@ -147,7 +157,7 @@ def main() -> None:
147
157
  # verify=False,
148
158
  hmac_keystore=hmac_keys,
149
159
  skip_signature_validation=False,
150
- hmac_fetcher=lookup_secret_key
160
+ hmac_fetcher=lookup_secret_key,
151
161
  )
152
162
 
153
163
  start_server(ra)
@@ -155,6 +165,3 @@ def main() -> None:
155
165
 
156
166
  if __name__ == "__main__":
157
167
  main()
158
-
159
-
160
-