diff --git a/src/handlers.rs b/src/handlers.rs
index 1319bad..5b361e6 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -6,4 +6,5 @@
pub mod jwt;
pub mod sessions;
pub mod router;
-pub mod kratos;
\ No newline at end of file
+pub mod kratos;
+pub mod extauthz;
\ No newline at end of file
diff --git a/src/handlers/extauthz.rs b/src/handlers/extauthz.rs
new file mode 100644
index 0000000..8b1ab51
--- /dev/null
+++ b/src/handlers/extauthz.rs
@@ -0,0 +1,132 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+use axum::{
+// response::Html,
+// routing::get,
+// routing::post,
+// Router,
+ // extract::{self,Extension,Path,Query},
+// extract::{Extension,Path},
+ http::{
+ header::{HeaderMap,HeaderName,HeaderValue},
+ // Request, StatusCode,
+ },
+ // body::{Bytes, Body},
+ response::{IntoResponse, Headers},
+};
+
+// use crate::defs::{DataDBs};
+// use crate::utils::reqenv::ReqEnv;
+
+const CHECK_HEADER: &'static str = "x-ext-authz";
+const ALLOWED_VALUE: &'static str = "allow";
+const RESULT_HEADER: &'static str = "x-ext-authz-check-result";
+const RECEIVED_HEADER: &'static str = "x-ext-authz-check-received";
+const OVERRIDE_HEADER: &'static str = "x-ext-authz-additional-header-override";
+// const OVERRIDE_GRPC_VALUE: &'static str = "grpc-additional-header-override-value";
+const RESULT_ALLOWED: &'static str = "allowed";
+const RESULT_DENIED: &'static str = "denied";
+
+// pub fn create_headers(is_allowed: bool, str_head: &str, id: &str,token: &str,csrf_token: &str, cookie: &str) -> HeaderMap {
+// let mut headers = HeaderMap::new();
+// if is_allowed {
+// headers.insert(RESULT_HEADER,
+// HeaderValue::from_str(RESULT_ALLOWED).unwrap_or(HeaderValue::from_static(""))
+// );
+// } else {
+// headers.insert(RESULT_HEADER,
+// HeaderValue::from_str(RESULT_DENIED).unwrap_or(HeaderValue::from_static(""))
+// );
+// }
+// headers.insert(OVERRIDE_HEADER,
+// HeaderValue::from_str(OVERRIDE_HEADER).unwrap_or(HeaderValue::from_static(""))
+// );
+// headers.insert(RECEIVED_HEADER,
+// HeaderValue::from_str(str_head).unwrap_or(HeaderValue::from_static(""))
+// );
+ // headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
+ // headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
+ // if cookie != "" {
+ // headers.insert(COOKIE, HeaderValue::from_str(cookie)
+ // .unwrap_or(HeaderValue::from_static(""))
+ // );
+ // // headers.insert(SET_COOKIE, HeaderValue::from_str(cookie)
+ // // .unwrap_or(HeaderValue::from_static(""))
+ // // );
+ // }
+ // if token != "" {
+ // headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}",token))
+ // .unwrap_or(HeaderValue::from_static(""))
+ // );
+ // }
+ // if csrf_token != "" {
+ // headers.insert("X-CSRF-Token",
+ // HeaderValue::from_str(csrf_token).unwrap_or(HeaderValue::from_static(""))
+ // );
+ // }
+ // if id != "" {
+ // headers.insert("x-kratos-authenticated-identity-id",
+ // HeaderValue::from_str(id).unwrap_or(HeaderValue::from_static(""))
+ // );
+ // }
+ // dbg!("{:#?}",&headers);
+ // dbg!("{:#?}",&headers);
+// headers
+// }
+// pub async fn authz_handler(header: HeaderMap, req: Request<()> ) -> impl IntoResponse {
+pub async fn authz_handler(header: HeaderMap) -> impl IntoResponse {
+ dbg!("{:#?}",&header);
+ let result: &str;
+ if let Some(check) = &header.get(CHECK_HEADER) {
+ match check.to_str() {
+ Ok(check_val) =>
+ if ALLOWED_VALUE == check_val {
+ result = RESULT_ALLOWED;
+ } else {
+ result = RESULT_DENIED;
+ },
+ Err(_) => result = RESULT_DENIED,
+ }
+ } else {
+ result = RESULT_DENIED;
+ }
+ //let headers = create_headers("",token,csrf_token,cookie);
+ // body, err := io.ReadAll(request.Body)
+ // if err != nil {
+ // log.Printf("[HTTP] read body failed: %v", err)
+ // }
+ // format!("%s %s%s, headers: %v, body: [%s]\n", request.Method, request.Host, request.URL, request.Header, body);
+ let str_head = format!("head");
+ // let headers = create_headers(is_allowed,&str_head,"","","","");
+ let res_headers: Vec<(HeaderName, HeaderValue)> = vec![
+ (HeaderName::from_static(RESULT_HEADER), HeaderValue::from_static(result)),
+ (HeaderName::from_static(OVERRIDE_HEADER), HeaderValue::from_static(OVERRIDE_HEADER)),
+ (HeaderName::from_static(RECEIVED_HEADER), HeaderValue::from_str(&str_head).unwrap_or(HeaderValue::from_static(""))),
+ ];
+ // if is_allowed {
+ // response.WriteHeader(StatusCode::OK)
+ // } else {
+ // response.WriteHeader(http.StatusForbidden)
+ // response.Write([]byte(denyBody))
+ // }
+ dbg!("{:#?}",&res_headers);
+ Headers(res_headers)
+ // let html = format!(
+ // r#"
+ //
Done: {}
+ // "#,RESULT_ALLOWED);
+ // Html(html)
+}
+// pub async fn alive_handler() -> Html<&'static str> {
+// Html("ok")
+// }
+// pub async fn ready_handler() -> Html<&'static str> {
+// Html("ok")
+// }
+// pub fn router_handlers(web_router: Router) -> Router {
+// web_router
+// .route("/authz", get(authz_handler))
+// }
diff --git a/src/handlers/kratos.rs b/src/handlers/kratos.rs
index 2e02277..1616af7 100644
--- a/src/handlers/kratos.rs
+++ b/src/handlers/kratos.rs
@@ -17,7 +17,10 @@ use axum::{
response::IntoResponse,
extract::{Extension,Path,Query},
//http::{Request, header::HeaderMap, Method,StatusCode},
- http::{header::HeaderMap,StatusCode},
+ http::{
+ header::{HeaderMap,HeaderName,HeaderValue},
+ StatusCode,
+ },
// body::{Bytes, Body},
};
// use serde::{Deserialize, Serialize};
@@ -115,7 +118,7 @@ pub async fn registration_handler(header: HeaderMap, Extension(dbs): ExtensionRegister
login
Status: {}
- "#,&reqenv.websrvr_url(),res.status);
+ "#,&reqenv.public_url(),res.status);
},
Err(e) => {
eprintln!("{}",e);
@@ -162,6 +165,8 @@ pub async fn login_handler(header: HeaderMap, Extension(dbs): Extension
let flow = String::from("self-service/login");
let query = String::from("/api?refresh=false&aal=&return_to=");
let root_url = get_root_url(reqenv.websrvr().signin.protocol,reqenv.websrvr().signin.root,reqenv.websrvr().signin.port);
+ let mut headers = HeaderMap::new();
+ let ret_status: StatusCode;
let mut user_data = HashMap::new();
user_data.insert("password".to_string(),"19Ting22".to_string());
user_data.insert("password_identifier".to_string(),"jesuspl".to_string());
@@ -183,6 +188,11 @@ pub async fn login_handler(header: HeaderMap, Extension(dbs): Extension
// res.session_resp.session_token);
// return html.to_owned();
if res.status == StatusCode::OK {
+ headers.insert(
+ HeaderName::from_static("x-authz"),
+ HeaderValue::from_str(&format!("{}:tk:{}",res.session_resp.session.identity.id, res.session_resp.session_token)).unwrap_or(HeaderValue::from_static("")),
+ );
+ ret_status = StatusCode::OK;
html = format!(
r#"
Login
@@ -190,39 +200,46 @@ pub async fn login_handler(header: HeaderMap, Extension(dbs): Extension
Status: {}
"#,
- &reqenv.websrvr_url(),res.session_resp.session.identity.id, res.session_resp.session_token,
- &reqenv.websrvr_url(),res.session_resp.session.identity.id, res.session_resp.session_token,
+ &reqenv.public_url(),res.session_resp.session.identity.id, res.session_resp.session_token,
+ &reqenv.public_url(),res.session_resp.session.identity.id, res.session_resp.session_token,
res.status);
} else {
+ ret_status = StatusCode::OK;
html = format!(
r#"
Login
Status: {}
- "#,res.status,&reqenv.websrvr_url());
+ "#,res.status,&reqenv.public_url());
}
},
Err(e) => {
eprintln!("{}",e);
+ // ret_status = StatusCode::FORBIDDEN;
+ ret_status = StatusCode::NOT_FOUND;
html = format!(
r#"
Login
Error: {}
- "#,&reqenv.websrvr_url(),e);
+ "#,&reqenv.public_url(),e);
}
}
},
Err(e) => {
eprintln!("{}",e);
+ ret_status = StatusCode::BAD_REQUEST;
+ // ret_status = StatusCode::UNAUTHORIZED;
html = format!(
r#"
Login
- Error: {}
- "#,e);
+ Error: No Login Found
+ "#);
},
};
- Html(html)
+ (ret_status, headers, Html(html))
+ // (StatusCode, HeaderMap, &'static str)
+ //Html(html)
}
pub async fn recovery_handler() -> Html<&'static str> {
Html("Hello, World!
")
@@ -255,14 +272,14 @@ pub async fn whoami(id: String, token: String,
Status: {}
"#,res.status,
- &reqenv.websrvr_url(),id,token)
+ &reqenv.public_url(),id,token)
} else {
html = format!(
r#"
whoami
Status: {}
- "#,res.status, &reqenv.websrvr_url())
+ "#,res.status, &reqenv.public_url())
}
},
Err(e) => {
@@ -272,7 +289,7 @@ pub async fn whoami(id: String, token: String,
whoami
Error: {}
- "#,&reqenv.websrvr_url(),e);
+ "#,&reqenv.public_url(),e);
}
}
},
@@ -308,8 +325,9 @@ pub async fn whoami_handler(Path(id): Path, query: Query,
println!("flow: {}",&reqid.flow);
println!("csrf_token: {}",&reqid.csrf);
println!("cookie: {}",&reqid.cookie);
+ let flow_logout = String::from("self-service/logout");
let query_id = format!("/api?{}&flow={}",&urlquery.token,&reqid.flow);
- let reg_url=format!("{}/{}{}",&root_url,flow,query_id);
+ let reg_url=format!("{}/{}{}",&root_url,flow_logout,query_id);
// match on_kratos_user(reqenv.websrvr().signin,&reqid.csrf,&id,®_url,user).await {
match logout_kratos_user(&id,&urlquery.token.to_string(),reqenv.websrvr().signin,&req_cli,&reqid.csrf,&reqid.cookie,®_url).await {
Ok(res) => {
@@ -319,7 +337,7 @@ pub async fn whoami_handler(Path(id): Path, query: Query,
Logout
login
Status: {}
- "#,reqenv.websrvr_url(),res.status);
+ "#,reqenv.public_url(),res.status);
},
Err(e) => {
eprintln!("{}",e);
diff --git a/src/handlers/router.rs b/src/handlers/router.rs
index 30b8fc8..ea51a92 100644
--- a/src/handlers/router.rs
+++ b/src/handlers/router.rs
@@ -21,6 +21,15 @@ use std::{
use crate::defs::{DataDBs};
use crate::utils::reqenv::ReqEnv;
+use app_env::{
+ // AppStore,
+ // appenv::AppEnv,
+ // appinfo::AppInfo,
+ // appdata::AppData,
+ //config::{WebServer,SigninServer,SigninAuthzRoutes,AuthzMode},
+ config::{SigninAuthzRoute,AuthzMode},
+};
+
// pub async fn html_handler(Path(path): Path,query: Option>, Extension(dbs): Extension) -> Html<&'static str> {
// pub async fn html_handler(Path(path): Path,Extension(dbs): Extension) -> Html<&'static str> {
pub async fn html_handler(header: HeaderMap, Path(name): Path,Extension(dbs): Extension) -> Html<&'static str> {
@@ -66,3 +75,32 @@ pub fn router_handlers(web_router: Router) -> Router {
.route("/health/alive", get(alive_handler))
.route("/health/ready", get(ready_handler))
}
+// These are to implement configuration defined signin authz routes
+// using authz-route "path" ,"mode"(AuthMode) and "handler"
+//
+fn add_kratos_handler(web_router: Router,route: SigninAuthzRoute) -> Router {
+ let mut webrouter = web_router.to_owned();
+ if route.path.is_empty() {
+ return webrouter;
+ }
+ match route.handler.as_str() {
+ // TODO Add handlers for Kratos. This should be moved from String to Enum type in SinginAuthRoute
+ "headers" => webrouter = webrouter.route(route.path.as_str(), get(crate::handlers::extauthz::authz_handler)),
+ _ => {
+ println!("Kratos signin authz route ({}) path ({}) with handler ({}) use authz_handler",route.id,route.path,route.handler);
+ webrouter = webrouter.route(route.path.as_str(), get(crate::handlers::extauthz::authz_handler));
+ },
+ };
+ webrouter
+}
+pub fn router_authz_handlers(web_router: Router,signin_auth_routes: Vec) -> Router {
+ let mut webrouter = web_router.to_owned();
+ for route in signin_auth_routes {
+ match route.mode {
+ AuthzMode::Kratos => webrouter = add_kratos_handler(webrouter,route),
+ // TODO Add new AuthMode
+ _ => eprintln!("Signin authz route {} undefined mode {}",route.id,route.mode),
+ }
+ }
+ webrouter
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index b0feed9..f35a390 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -241,6 +241,7 @@ async fn up_web_server(webpos: usize) -> Result<()> { // WebSettings> {
println!("Web services: done {} __________ ",chrono::Utc::now().timestamp());
}
let mut web_router = Router::new();
+ web_router = crate::handlers::router::router_authz_handlers(web_router,config.signin.authz_routes);
web_router = crate::handlers::router::router_handlers(web_router);
if !config.html_path.is_empty() {
web_router = web_router.nest(
diff --git a/src/utils/reqenv.rs b/src/utils/reqenv.rs
index d50c6d6..c05ca0f 100644
--- a/src/utils/reqenv.rs
+++ b/src/utils/reqenv.rs
@@ -114,6 +114,15 @@ impl ReqEnv {
let config=self.req.config().websrvrs[self.req.env().curr_web].to_owned();
format!("{}://{}:{}", config.srv_protocol, config.srv_host, config.srv_port)
}
+ #[must_use]
+ pub fn public_url(&self) -> String {
+ let config=self.req.config().websrvrs[self.req.env().curr_web].to_owned();
+ if config.public_url.is_empty() {
+ format!("{}://{}:{}", config.srv_protocol, config.srv_host, config.srv_port)
+ } else {
+ config.public_url
+ }
+ }
#[must_use]
pub fn module(&self) -> Module {
self.req.module()