Skip to content

Commit 5445852

Browse files
authored
Merge pull request #340 from umccr/feat/path-context
feat: path context
2 parents 5b7e28f + f6226cc commit 5445852

File tree

10 files changed

+184
-34
lines changed

10 files changed

+184
-34
lines changed

Cargo.lock

Lines changed: 5 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

htsget-config/README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,13 @@ The authorization server should respond with a rule set that htsget-rs can use t
430430

431431
The following additional options can be configured under the `auth` table to enable this:
432432

433-
| Option | Description | Type | Default |
434-
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------|----------|
435-
| `authorization_url` | The URL which will be called to authorize the user. A GET request will be issued to the url. Alternatively, this can be a file path to authorize users based on static config. | URL | Not set. |
436-
| `forward_headers` | For each header specified, forward any headers from the client to the authorization server. Headers are forwarded with the `Htsget-Context-` as a prefix. | Array of header names | Not set. |
437-
| `passthrough_auth` | Forward the authorization header to the authorization server directly without renaming it to a `Htsget-Context-` custom header. If this is true, then the `Authorization` header is required with the request. | Boolean | `false` |
433+
| Option | Description | Type | Default |
434+
|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------|----------|
435+
| `authorization_url` | The URL which will be called to authorize the user. A GET request will be issued to the url. Alternatively, this can be a file path to authorize users based on static config. | URL | Not set. |
436+
| `forward_headers` | For each header specified, forward any headers from the client to the authorization server. Headers are forwarded with the `Htsget-Context-` as a prefix. | Array of header names | Not set. |
437+
| `forward_endpoint_type` | Forwards the type of endpoint that the request used in a header called `Htsget-Context-Endpoint-Type`. The value of this header will either be `reads` or `variants`, depending on whether the user requested the reads or variants endpoint. | Boolean | `false` |
438+
| `forward_id` | Forwards the id of the request in a header called `Htsget-Context-Id`. The value of this header will be request path without the `/reads/` or `/variants/` component. For example, if a request path is `/reads/<id>`, this header will have the same value as `<id>`. | Boolean | `false` |
439+
| `passthrough_auth` | Forward the authorization header to the authorization server directly without renaming it to a `Htsget-Context-` custom header. If this is true, then the `Authorization` header is required with the request. | Boolean | `false` |
438440

439441
When using the `authorization_url`, the [authentication](#jwt-authentication) config must also be set as htsget-rs will
440442
forward the JWT token to the authorization server so that it can make decisions about the user's authorization. If the

htsget-config/docs/examples/auth.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ authorization_url = "https://www.example.com/authorize"
2222
passthrough_auth = true
2323
## Any headers to forward
2424
#forward_headers = ["Content-Type"]
25+
## Forward the endpoint type to the auth service.
26+
#forward_endpoint_type = true
27+
## Forward the request id.
28+
#forward_id = true
2529

2630
## Set client authentication
2731
#http.key = "key.pem"

htsget-config/src/config/advanced/auth/authorization.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<'de> Deserialize<'de> for UrlOrStatic {
3838
}
3939

4040
/// The extensions to pass through to the authorization server from http request extensions.
41-
#[derive(Serialize, Deserialize, Debug, Clone)]
41+
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
4242
#[serde(deny_unknown_fields)]
4343
pub struct ForwardExtensions {
4444
json_path: String,

htsget-config/src/config/advanced/auth/mod.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub struct AuthConfig {
2828
validate_subject: Option<String>,
2929
authorization_url: Option<UrlOrStatic>,
3030
forward_headers: Vec<String>,
31+
forward_endpoint_type: bool,
32+
forward_id: bool,
3133
passthrough_auth: bool,
3234
forward_extensions: Vec<ForwardExtensions>,
3335
http_client: HttpClient,
@@ -95,6 +97,16 @@ impl AuthConfig {
9597
self.forward_headers.as_slice()
9698
}
9799

100+
/// Get whether to forward the endpoint type of the request.
101+
pub fn forward_endpoint_type(&self) -> bool {
102+
self.forward_endpoint_type
103+
}
104+
105+
/// Get whether to forward the id of the request.
106+
pub fn forward_id(&self) -> bool {
107+
self.forward_id
108+
}
109+
98110
/// Get whether to pass through the auth header.
99111
pub fn passthrough_auth(&self) -> bool {
100112
self.passthrough_auth
@@ -126,6 +138,8 @@ pub struct AuthConfigBuilder {
126138
validate_subject: Option<String>,
127139
authorization_url: Option<UrlOrStatic>,
128140
forward_headers: Vec<String>,
141+
forward_endpoint_type: bool,
142+
forward_id: bool,
129143
passthrough_auth: bool,
130144
forward_extensions: Vec<ForwardExtensions>,
131145
#[serde(rename = "http", alias = "tls", skip_serializing)]
@@ -193,6 +207,18 @@ impl AuthConfigBuilder {
193207
self
194208
}
195209

210+
/// Set whether to forward the endpoint type.
211+
pub fn forward_endpoint_type(mut self, forward_endpoint_type: bool) -> Self {
212+
self.forward_endpoint_type = forward_endpoint_type;
213+
self
214+
}
215+
216+
/// Set whether to forward the id.
217+
pub fn forward_id(mut self, forward_id: bool) -> Self {
218+
self.forward_id = forward_id;
219+
self
220+
}
221+
196222
/// Set whether to pass through auth.
197223
pub fn passthrough_auth(mut self, passthrough_auth: bool) -> Self {
198224
self.passthrough_auth = passthrough_auth;
@@ -214,6 +240,8 @@ impl AuthConfigBuilder {
214240
validate_subject: self.validate_subject,
215241
authorization_url: self.authorization_url,
216242
forward_headers: self.forward_headers,
243+
forward_endpoint_type: self.forward_endpoint_type,
244+
forward_id: self.forward_id,
217245
passthrough_auth: self.passthrough_auth,
218246
forward_extensions: self.forward_extensions,
219247
http_client: self
@@ -239,6 +267,8 @@ impl Default for AuthConfigBuilder {
239267
validate_subject: None,
240268
authorization_url,
241269
forward_headers: vec![],
270+
forward_endpoint_type: false,
271+
forward_id: false,
242272
passthrough_auth: false,
243273
forward_extensions: vec![],
244274
http_client: None,
@@ -376,6 +406,11 @@ mod tests {
376406
validate_issuer = ["iss1"]
377407
validate_subject = "sub"
378408
authorization_url = "https://www.example.com"
409+
passthrough_auth = true
410+
forward_headers = ["header"]
411+
forward_endpoint_type = true
412+
forward_id = true
413+
forward_extensions = [ { json_path = '$.extension', name = 'Extension'} ]
379414
"#,
380415
)
381416
.unwrap();
@@ -396,6 +431,17 @@ mod tests {
396431
config.authorization_url().unwrap(),
397432
&UrlOrStatic::Url("https://www.example.com".parse::<Uri>().unwrap())
398433
);
434+
assert!(config.passthrough_auth());
435+
assert_eq!(config.forward_headers(), ["header".to_string()]);
436+
assert!(config.forward_endpoint_type());
437+
assert!(config.forward_id());
438+
assert_eq!(
439+
config.forward_extensions(),
440+
[ForwardExtensions::new(
441+
"$.extension".to_string(),
442+
"Extension".to_string()
443+
)]
444+
);
399445
}
400446

401447
#[cfg(feature = "experimental")]

htsget-http/src/error.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use http::StatusCode;
2+
use http::header::{InvalidHeaderName, InvalidHeaderValue};
23
use serde::Serialize;
34
use thiserror::Error;
45

@@ -84,3 +85,15 @@ impl From<HtsGetSearchError> for HtsGetError {
8485
}
8586
}
8687
}
88+
89+
impl From<InvalidHeaderName> for HtsGetError {
90+
fn from(err: InvalidHeaderName) -> Self {
91+
Self::InternalError(err.to_string())
92+
}
93+
}
94+
95+
impl From<InvalidHeaderValue> for HtsGetError {
96+
fn from(err: InvalidHeaderValue) -> Self {
97+
Self::InternalError(err.to_string())
98+
}
99+
}

htsget-http/src/http_core.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ async fn authorize(
3737
queries: &mut [Query],
3838
auth: Option<(TokenData<Value>, Auth)>,
3939
extensions: Option<Value>,
40+
endpoint: &Endpoint,
4041
) -> Result<Option<AuthorizationRestrictions>> {
4142
if let Some((_, mut auth)) = auth {
4243
let _rules = auth
43-
.validate_authorization(headers, path, queries, extensions)
44+
.validate_authorization(headers, path, queries, extensions, endpoint)
4445
.await?;
4546
cfg_if! {
4647
if #[cfg(feature = "experimental")] {
@@ -77,7 +78,15 @@ pub async fn get(
7778

7879
let format = match_format_from_query(&endpoint, request.query())?;
7980
let mut query = vec![convert_to_query(request, format)?];
80-
let rules = authorize(&headers, &path, query.as_mut_slice(), auth, extensions).await?;
81+
let rules = authorize(
82+
&headers,
83+
&path,
84+
query.as_mut_slice(),
85+
auth,
86+
extensions,
87+
&endpoint,
88+
)
89+
.await?;
8190

8291
debug!(endpoint = ?endpoint, query = ?query, "getting GET response");
8392

@@ -137,7 +146,15 @@ pub async fn post(
137146
}
138147

139148
let mut queries = body.get_queries(request, &endpoint)?;
140-
let rules = authorize(&headers, &path, queries.as_mut_slice(), auth, extensions).await?;
149+
let rules = authorize(
150+
&headers,
151+
&path,
152+
queries.as_mut_slice(),
153+
auth,
154+
extensions,
155+
&endpoint,
156+
)
157+
.await?;
141158

142159
debug!(endpoint = ?endpoint, queries = ?queries, "getting POST response");
143160

htsget-http/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ use query_builder::QueryBuilder;
99
pub use service_info::get_service_info_json;
1010
pub use service_info::{Htsget, ServiceInfo, Type};
1111
use std::collections::HashMap;
12-
use std::result;
12+
use std::fmt::{Display, Formatter};
1313
use std::str::FromStr;
14+
use std::{fmt, result};
1415

1516
pub mod error;
1617
pub mod http_core;
@@ -39,6 +40,15 @@ impl FromStr for Endpoint {
3940
}
4041
}
4142

43+
impl Display for Endpoint {
44+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
45+
match self {
46+
Self::Reads => write!(f, "reads"),
47+
Self::Variants => write!(f, "variants"),
48+
}
49+
}
50+
}
51+
4252
/// Match the format from a query parameter.
4353
pub fn match_format_from_query(
4454
endpoint: &Endpoint,

0 commit comments

Comments
 (0)