Fixed query parameters and dynamic Client ID
This commit is contained in:
parent
d6d73d74fe
commit
bf92aea1bb
1 changed files with 12 additions and 23 deletions
35
src/main.rs
35
src/main.rs
|
@ -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)) });
|
||||||
|
|
Loading…
Reference in a new issue