Fixed query parameters and dynamic Client ID

This commit is contained in:
Evann Regnault 2024-07-16 19:20:30 +02:00
parent d6d73d74fe
commit bf92aea1bb

View file

@ -1,4 +1,4 @@
use std::{convert::Infallible, env, error::Error, net::SocketAddr, str::FromStr}; use std::{collections::HashMap, convert::Infallible, env, error::Error, net::SocketAddr, str::FromStr};
use redis::Commands; use redis::Commands;
use base64::prelude::*; use base64::prelude::*;
use hmac::{digest::generic_array::functional::FunctionalSequence, Hmac, Mac}; use hmac::{digest::generic_array::functional::FunctionalSequence, Hmac, Mac};
@ -31,18 +31,12 @@ struct KeycloakToken {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct DecodedJWT { pub struct DecodedJWT {
#[serde(rename = "resource_access")] #[serde(rename = "resource_access")]
pub resource_access: Option<ResourceAccess>, pub resource_access: HashMap<String, Roles>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ResourceAccess { pub struct Roles {
pub couchdb: Option<Couchdb>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Couchdb {
pub roles: Vec<String>, pub roles: Vec<String>,
} }
@ -80,7 +74,7 @@ impl KeycloakToken {
} }
fn decode_token(&self) -> Result<DecodedJWT, Box<dyn Error>> { fn decode_token(&self) -> Result<DecodedJWT, Box<dyn Error>> {
let data = self.access_token.split(".").skip(1).next(); let data = self.access_token.split('.').nth(1);
if data.is_none() { if data.is_none() {
return Err("Invalid JWT".into()); return Err("Invalid JWT".into());
} }
@ -92,13 +86,10 @@ impl KeycloakToken {
impl DecodedJWT { impl DecodedJWT {
/// Creates the user struct from the JWT Token /// Creates the user struct from the JWT Token
fn get_user(self, name: String) -> User { fn get_user(self, name: String) -> User {
let roles = match self.resource_access { let roles = match self.resource_access.get(&env::var("KEYCLOAK_CLIENT_ID").unwrap()) {
None => vec![], None => vec![],
Some(ra) => { Some(roles) => {
match ra.couchdb { roles.roles.clone()
None => vec![],
Some(cdb) => cdb.roles
}
} }
}; };
User { User {
@ -191,9 +182,9 @@ async fn authenticate_keycloak(username: String, password: String) -> Result<Use
/// Retrieves the username and password from a Basic Auth /// Retrieves the username and password from a Basic Auth
fn extract_creds_from_request(req: &Request<Body>) -> Option<(String, String)> { fn extract_creds_from_request(req: &Request<Body>) -> Option<(String, String)> {
let auth_value = req.headers().get("authorization")?; let auth_value = req.headers().get("authorization")?;
let b64_auth = auth_value.to_str().unwrap_or("").split(" ").skip(1).next()?; let b64_auth = auth_value.to_str().unwrap_or("").split(' ').nth(1)?;
let decoded_auth = String::from_utf8(BASE64_STANDARD.decode(b64_auth.as_bytes()).unwrap_or(vec![])).unwrap(); let decoded_auth = String::from_utf8(BASE64_STANDARD.decode(b64_auth.as_bytes()).unwrap_or(vec![])).unwrap();
if let [username, password] = &decoded_auth.split(":").map(String::from).collect::<Vec<String>>()[..] { if let [username, password] = &decoded_auth.split(':').map(String::from).collect::<Vec<String>>()[..] {
Some((username.to_string(), password.to_string())) Some((username.to_string(), password.to_string()))
} else { } else {
None None
@ -215,14 +206,13 @@ fn error_response((status_code, msg) : (u16, String)) -> Response<Body> {
.unwrap() .unwrap()
} }
async fn handle(old_req: Request<Body>) -> Result<Response<Body>, hyper::Error> { async fn handle(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let client = Client::new(); let client = Client::new();
// Build proxied Request // Build proxied Request
let mut req = Request::from(old_req); let path = req.uri().path_and_query();
let path = req.uri().path();
*req.uri_mut() = *req.uri_mut() =
Uri::from_str(format!("http://{}:{}{}", env::var("COUCHDB_HOST").unwrap(), env::var("COUCHDB_PORT").unwrap(), path.to_string()).as_str()).unwrap(); Uri::from_str(format!("http://{}:{}{}", env::var("COUCHDB_HOST").unwrap(), env::var("COUCHDB_PORT").unwrap(), path.unwrap()).as_str()).unwrap();
if req.method() == Method::OPTIONS { if req.method() == Method::OPTIONS {
return client.request(req).await return client.request(req).await
@ -257,7 +247,6 @@ async fn handle(old_req: Request<Body>) -> Result<Response<Body>, hyper::Error>
Err(x) => Err(x), Err(x) => Err(x),
} }
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let make_service = make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(handle)) }); let make_service = make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(handle)) });