server: statically enforce access control
This commit is contained in:
committed by
nitnelave
parent
322bf26db5
commit
c9997d4c17
@@ -122,13 +122,17 @@ pub struct UpdateGroupRequest {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait LoginHandler: Clone + Send {
|
||||
pub trait LoginHandler: Send + Sync {
|
||||
async fn bind(&self, request: BindRequest) -> Result<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait GroupBackendHandler {
|
||||
pub trait GroupListerBackendHandler {
|
||||
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait GroupBackendHandler {
|
||||
async fn get_group_details(&self, group_id: GroupId) -> Result<GroupDetails>;
|
||||
async fn update_group(&self, request: UpdateGroupRequest) -> Result<()>;
|
||||
async fn create_group(&self, group_name: &str) -> Result<GroupId>;
|
||||
@@ -136,12 +140,16 @@ pub trait GroupBackendHandler {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserBackendHandler {
|
||||
pub trait UserListerBackendHandler {
|
||||
async fn list_users(
|
||||
&self,
|
||||
filters: Option<UserRequestFilter>,
|
||||
get_groups: bool,
|
||||
) -> Result<Vec<UserAndGroups>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserBackendHandler {
|
||||
async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
|
||||
async fn create_user(&self, request: CreateUserRequest) -> Result<()>;
|
||||
async fn update_user(&self, request: UpdateUserRequest) -> Result<()>;
|
||||
@@ -152,7 +160,15 @@ pub trait UserBackendHandler {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait BackendHandler: Clone + Send + GroupBackendHandler + UserBackendHandler {}
|
||||
pub trait BackendHandler:
|
||||
Send
|
||||
+ Sync
|
||||
+ GroupBackendHandler
|
||||
+ UserBackendHandler
|
||||
+ UserListerBackendHandler
|
||||
+ GroupListerBackendHandler
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mockall::mock! {
|
||||
@@ -161,16 +177,22 @@ mockall::mock! {
|
||||
fn clone(&self) -> Self;
|
||||
}
|
||||
#[async_trait]
|
||||
impl GroupBackendHandler for TestBackendHandler {
|
||||
impl GroupListerBackendHandler for TestBackendHandler {
|
||||
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;
|
||||
}
|
||||
#[async_trait]
|
||||
impl GroupBackendHandler for TestBackendHandler {
|
||||
async fn get_group_details(&self, group_id: GroupId) -> Result<GroupDetails>;
|
||||
async fn update_group(&self, request: UpdateGroupRequest) -> Result<()>;
|
||||
async fn create_group(&self, group_name: &str) -> Result<GroupId>;
|
||||
async fn delete_group(&self, group_id: GroupId) -> Result<()>;
|
||||
}
|
||||
#[async_trait]
|
||||
impl UserBackendHandler for TestBackendHandler {
|
||||
impl UserListerBackendHandler for TestBackendHandler {
|
||||
async fn list_users(&self, filters: Option<UserRequestFilter>, get_groups: bool) -> Result<Vec<UserAndGroups>>;
|
||||
}
|
||||
#[async_trait]
|
||||
impl UserBackendHandler for TestBackendHandler {
|
||||
async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
|
||||
async fn create_user(&self, request: CreateUserRequest) -> Result<()>;
|
||||
async fn update_user(&self, request: UpdateUserRequest) -> Result<()>;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use ldap3_proto::{
|
||||
proto::LdapOp, LdapFilter, LdapPartialAttribute, LdapResultCode, LdapSearchResultEntry,
|
||||
};
|
||||
use tracing::{debug, info, instrument, warn};
|
||||
use tracing::{debug, instrument, warn};
|
||||
|
||||
use crate::domain::{
|
||||
handler::{BackendHandler, GroupRequestFilter},
|
||||
handler::{GroupListerBackendHandler, GroupRequestFilter},
|
||||
ldap::error::LdapError,
|
||||
types::{Group, GroupColumn, UserId, Uuid},
|
||||
};
|
||||
@@ -21,7 +21,7 @@ pub fn get_group_attribute(
|
||||
group: &Group,
|
||||
base_dn_str: &str,
|
||||
attribute: &str,
|
||||
user_filter: &Option<&UserId>,
|
||||
user_filter: &Option<UserId>,
|
||||
ignored_group_attributes: &[String],
|
||||
) -> Option<Vec<Vec<u8>>> {
|
||||
let attribute = attribute.to_ascii_lowercase();
|
||||
@@ -34,7 +34,7 @@ pub fn get_group_attribute(
|
||||
"member" | "uniquemember" => group
|
||||
.users
|
||||
.iter()
|
||||
.filter(|u| user_filter.map(|f| *u == f).unwrap_or(true))
|
||||
.filter(|u| user_filter.as_ref().map(|f| *u == f).unwrap_or(true))
|
||||
.map(|u| format!("uid={},ou=people,{}", u, base_dn_str).into_bytes())
|
||||
.collect(),
|
||||
"1.1" => return None,
|
||||
@@ -81,7 +81,7 @@ fn make_ldap_search_group_result_entry(
|
||||
group: Group,
|
||||
base_dn_str: &str,
|
||||
attributes: &[String],
|
||||
user_filter: &Option<&UserId>,
|
||||
user_filter: &Option<UserId>,
|
||||
ignored_group_attributes: &[String],
|
||||
) -> LdapSearchResultEntry {
|
||||
let expanded_attributes = expand_group_attribute_wildcards(attributes);
|
||||
@@ -201,25 +201,17 @@ fn convert_group_filter(
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
pub async fn get_groups_list<Backend: BackendHandler>(
|
||||
pub async fn get_groups_list<Backend: GroupListerBackendHandler>(
|
||||
ldap_info: &LdapInfo,
|
||||
ldap_filter: &LdapFilter,
|
||||
base: &str,
|
||||
user_filter: &Option<&UserId>,
|
||||
backend: &mut Backend,
|
||||
backend: &Backend,
|
||||
) -> LdapResult<Vec<Group>> {
|
||||
debug!(?ldap_filter);
|
||||
let filter = convert_group_filter(ldap_info, ldap_filter)?;
|
||||
let parsed_filters = match user_filter {
|
||||
None => filter,
|
||||
Some(u) => {
|
||||
info!("Unprivileged search, limiting results");
|
||||
GroupRequestFilter::And(vec![filter, GroupRequestFilter::Member((*u).clone())])
|
||||
}
|
||||
};
|
||||
debug!(?parsed_filters);
|
||||
let filters = convert_group_filter(ldap_info, ldap_filter)?;
|
||||
debug!(?filters);
|
||||
backend
|
||||
.list_groups(Some(parsed_filters))
|
||||
.list_groups(Some(filters))
|
||||
.await
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::Other,
|
||||
@@ -231,7 +223,7 @@ pub fn convert_groups_to_ldap_op<'a>(
|
||||
groups: Vec<Group>,
|
||||
attributes: &'a [String],
|
||||
ldap_info: &'a LdapInfo,
|
||||
user_filter: &'a Option<&'a UserId>,
|
||||
user_filter: &'a Option<UserId>,
|
||||
) -> impl Iterator<Item = LdapOp> + 'a {
|
||||
groups.into_iter().map(move |g| {
|
||||
LdapOp::SearchResultEntry(make_ldap_search_group_result_entry(
|
||||
|
||||
@@ -2,10 +2,10 @@ use chrono::TimeZone;
|
||||
use ldap3_proto::{
|
||||
proto::LdapOp, LdapFilter, LdapPartialAttribute, LdapResultCode, LdapSearchResultEntry,
|
||||
};
|
||||
use tracing::{debug, info, instrument, warn};
|
||||
use tracing::{debug, instrument, warn};
|
||||
|
||||
use crate::domain::{
|
||||
handler::{BackendHandler, UserRequestFilter},
|
||||
handler::{UserListerBackendHandler, UserRequestFilter},
|
||||
ldap::{
|
||||
error::LdapError,
|
||||
utils::{expand_attribute_wildcards, get_user_id_from_distinguished_name},
|
||||
@@ -217,26 +217,18 @@ fn expand_user_attribute_wildcards(attributes: &[String]) -> Vec<&str> {
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
pub async fn get_user_list<Backend: BackendHandler>(
|
||||
pub async fn get_user_list<Backend: UserListerBackendHandler>(
|
||||
ldap_info: &LdapInfo,
|
||||
ldap_filter: &LdapFilter,
|
||||
request_groups: bool,
|
||||
base: &str,
|
||||
user_filter: &Option<&UserId>,
|
||||
backend: &mut Backend,
|
||||
backend: &Backend,
|
||||
) -> LdapResult<Vec<UserAndGroups>> {
|
||||
debug!(?ldap_filter);
|
||||
let filters = convert_user_filter(ldap_info, ldap_filter)?;
|
||||
let parsed_filters = match user_filter {
|
||||
None => filters,
|
||||
Some(u) => {
|
||||
info!("Unprivileged search, limiting results");
|
||||
UserRequestFilter::And(vec![filters, UserRequestFilter::UserId((*u).clone())])
|
||||
}
|
||||
};
|
||||
debug!(?parsed_filters);
|
||||
debug!(?filters);
|
||||
backend
|
||||
.list_users(Some(parsed_filters), request_groups)
|
||||
.list_users(Some(filters), request_groups)
|
||||
.await
|
||||
.map_err(|e| LdapError {
|
||||
code: LdapResultCode::Other,
|
||||
|
||||
@@ -4,7 +4,7 @@ use async_trait::async_trait;
|
||||
pub use lldap_auth::{login, registration};
|
||||
|
||||
#[async_trait]
|
||||
pub trait OpaqueHandler: Clone + Send {
|
||||
pub trait OpaqueHandler: Send + Sync {
|
||||
async fn login_start(
|
||||
&self,
|
||||
request: login::ClientLoginStartRequest,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{handler::BackendHandler, sql_tables::DbConnection};
|
||||
use crate::domain::{handler::BackendHandler, sql_tables::DbConnection};
|
||||
use crate::infra::configuration::Configuration;
|
||||
use async_trait::async_trait;
|
||||
|
||||
@@ -23,7 +23,8 @@ pub mod tests {
|
||||
use crate::{
|
||||
domain::{
|
||||
handler::{
|
||||
CreateUserRequest, GroupBackendHandler, UserBackendHandler, UserRequestFilter,
|
||||
CreateUserRequest, GroupBackendHandler, UserBackendHandler,
|
||||
UserListerBackendHandler, UserRequestFilter,
|
||||
},
|
||||
sql_tables::init_table,
|
||||
types::{GroupId, UserId},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::domain::{
|
||||
error::{DomainError, Result},
|
||||
handler::{GroupBackendHandler, GroupRequestFilter, UpdateGroupRequest},
|
||||
handler::{
|
||||
GroupBackendHandler, GroupListerBackendHandler, GroupRequestFilter, UpdateGroupRequest,
|
||||
},
|
||||
model::{self, GroupColumn, MembershipColumn},
|
||||
sql_backend_handler::SqlBackendHandler,
|
||||
types::{Group, GroupDetails, GroupId, Uuid},
|
||||
@@ -57,7 +59,7 @@ fn get_group_filter_expr(filter: GroupRequestFilter) -> Cond {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl GroupBackendHandler for SqlBackendHandler {
|
||||
impl GroupListerBackendHandler for SqlBackendHandler {
|
||||
#[instrument(skip_all, level = "debug", ret, err)]
|
||||
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>> {
|
||||
debug!(?filters);
|
||||
@@ -94,7 +96,10 @@ impl GroupBackendHandler for SqlBackendHandler {
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl GroupBackendHandler for SqlBackendHandler {
|
||||
#[instrument(skip_all, level = "debug", ret, err)]
|
||||
async fn get_group_details(&self, group_id: GroupId) -> Result<GroupDetails> {
|
||||
debug!(?group_id);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use super::{
|
||||
use crate::domain::{
|
||||
error::{DomainError, Result},
|
||||
handler::{CreateUserRequest, UpdateUserRequest, UserBackendHandler, UserRequestFilter},
|
||||
handler::{
|
||||
CreateUserRequest, UpdateUserRequest, UserBackendHandler, UserListerBackendHandler,
|
||||
UserRequestFilter,
|
||||
},
|
||||
model::{self, GroupColumn, UserColumn},
|
||||
sql_backend_handler::SqlBackendHandler,
|
||||
types::{GroupDetails, GroupId, User, UserAndGroups, UserId, Uuid},
|
||||
@@ -70,7 +73,7 @@ fn to_value(opt_name: &Option<String>) -> ActiveValue<Option<String>> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UserBackendHandler for SqlBackendHandler {
|
||||
impl UserListerBackendHandler for SqlBackendHandler {
|
||||
#[instrument(skip_all, level = "debug", ret, err)]
|
||||
async fn list_users(
|
||||
&self,
|
||||
@@ -135,7 +138,10 @@ impl UserBackendHandler for SqlBackendHandler {
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UserBackendHandler for SqlBackendHandler {
|
||||
#[instrument(skip_all, level = "debug", ret)]
|
||||
async fn get_user_details(&self, user_id: &UserId) -> Result<User> {
|
||||
debug!(?user_id);
|
||||
|
||||
Reference in New Issue
Block a user