Start authorization route
This commit is contained in:
parent
1647ca3838
commit
bd4144a5ba
|
@ -25,4 +25,5 @@ axum_typed_multipart = "0.8"
|
|||
async-trait = "0.1"
|
||||
serde = { version = "1", features = [ "derive" ] }
|
||||
file-owner = { version = "0.1" }
|
||||
tower-cookies = "0.9"
|
||||
tower-cookies = "0.9"
|
||||
url = "2.4"
|
||||
|
|
63
src/routes/authorize.rs
Normal file
63
src/routes/authorize.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use axum::{
|
||||
RequestPartsExt,
|
||||
extract::{FromRequestParts, State},
|
||||
http::{StatusCode, header::HeaderMap, request::Parts},
|
||||
};
|
||||
use snafu::prelude::*;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
error::*,
|
||||
state::{RoutableAppState, LoggedInUser},
|
||||
state::sessions::*,
|
||||
};
|
||||
|
||||
// TODO: Implement as a typed header
|
||||
pub struct OriginalURI(Url);
|
||||
|
||||
#[async_trait]
|
||||
impl FromRequestParts<RoutableAppState> for OriginalURI {
|
||||
type Rejection = Error;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, state: &RoutableAppState) -> Result<Self, Self::Rejection> {
|
||||
let headers: HeaderMap = parts.extract().await.unwrap();
|
||||
if let Some(uri) = headers.get("X-Original-URI") {
|
||||
// TODO: error
|
||||
Ok(
|
||||
OriginalURI(Url::parse(uri.to_str().unwrap()).unwrap())
|
||||
)
|
||||
} else {
|
||||
// TODO: error
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Url> for OriginalURI {
|
||||
fn as_ref(&self) -> &Url {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[debug_handler]
|
||||
pub async fn route(user: Option<LoggedInUser>, uri: Option<OriginalURI>, state: State<RoutableAppState>) -> StatusCode {
|
||||
if uri.is_none() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
let uri = uri.unwrap();
|
||||
let username = user.map(|u| u.username().clone());
|
||||
|
||||
match state.permissions.ssowat_config() {
|
||||
Ok(conf) => {
|
||||
if conf.user_has_permission_for_uri(username.as_ref(), uri.as_ref()) {
|
||||
StatusCode::OK
|
||||
} else {
|
||||
StatusCode::FORBIDDEN
|
||||
}
|
||||
}, Err(e) => {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use tower_cookies::CookieManagerLayer;
|
|||
|
||||
use crate::state::RoutableAppState;
|
||||
|
||||
mod authorize;
|
||||
mod index;
|
||||
mod login;
|
||||
mod logout;
|
||||
|
@ -16,6 +17,7 @@ pub fn router(subpath: Option<String>, state: RoutableAppState) -> Router {
|
|||
.route("/", get(index::route))
|
||||
.route("/login/", post(login::route))
|
||||
.route("/logout/", get(logout::route))
|
||||
.route("/authorize/", get(authorize::route))
|
||||
.layer(CookieManagerLayer::new())
|
||||
.with_state(state);
|
||||
if let Some(p) = subpath {
|
||||
|
|
|
@ -49,6 +49,12 @@ impl std::fmt::Display for LoggedInUser {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<Username> for LoggedInUser {
|
||||
fn as_ref(&self) -> &Username {
|
||||
&self.username()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromRequestParts<RoutableAppState> for LoggedInUser {
|
||||
type Rejection = Error;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use snafu::prelude::*;
|
||||
use yunohost_api::{YunohostUsers, Username, Password};
|
||||
use yunohost_api::{YunohostUsers, Username, Password, YunohostPermissions};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -19,6 +19,7 @@ pub const COOKIE_NAME: &'static str = "yunohost.ssowat";
|
|||
pub struct AppState {
|
||||
pub sessions: SessionManager,
|
||||
pub users: YunohostUsers,
|
||||
pub permissions: YunohostPermissions,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
|
@ -27,6 +28,8 @@ impl AppState {
|
|||
sessions: SessionManager::new().context(SessionSnafu)?,
|
||||
// Timeout in ms
|
||||
users: YunohostUsers::new(500).await.context(YunohostSnafu)?,
|
||||
// TODO: make async
|
||||
permissions: YunohostPermissions::new().context(YunohostSnafu)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
3
ssowat.toml
Normal file
3
ssowat.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
socket = /tmp/ssowat.socket
|
||||
socket_group = www-data
|
||||
socket_mode = "0o770"
|
|
@ -1,3 +1,4 @@
|
|||
use regex::Regex;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -23,12 +24,58 @@ impl SSOWatConfig {
|
|||
// Domain not managed
|
||||
return None;
|
||||
}
|
||||
|
||||
// Strip protocol but keep full URL
|
||||
let stripped_uri = AsRef::<str>::as_ref(uri)
|
||||
.trim_start_matches("http")
|
||||
.trim_start_matches("s")
|
||||
.trim_start_matches("://");
|
||||
|
||||
// Check which app matches this URI, to find corresponding permission
|
||||
for (key, val) in &self.permissions {
|
||||
for uri_format in &val.uris {
|
||||
if uri_format.starts_with("re:") {
|
||||
let uri_format = uri_format.trim_start_matches("re:");
|
||||
// TODO: generate regex in advance
|
||||
// TODO: error
|
||||
let re = Regex::new(uri_format).unwrap();
|
||||
if re.is_match(stripped_uri) {
|
||||
return Some(key.clone());
|
||||
}
|
||||
} else {
|
||||
if stripped_uri.starts_with(uri_format) {
|
||||
return Some(key.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No app URI matched
|
||||
return None;
|
||||
} else {
|
||||
// No domain (eg. http://8.8.8.8/)
|
||||
return None;
|
||||
}
|
||||
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn user_has_permission_for_uri(&self, username: Option<&Username>, uri: &Url) -> bool {
|
||||
if let Some(permission_name) = self.permission_for_uri(uri) {
|
||||
let permission = self.permissions.get(&permission_name).unwrap();
|
||||
if permission.public {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(username) = username {
|
||||
permission.users.contains(username)
|
||||
} else {
|
||||
// User is not logged-in. Non-public URIs are not authorized
|
||||
false
|
||||
}
|
||||
} else {
|
||||
// No permission matching this URI
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user