mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
server: Introduce a read-only user
This commit is contained in:
parent
1efab58d0c
commit
ff698df280
@ -438,7 +438,11 @@ where
|
|||||||
}
|
}
|
||||||
.into_inner();
|
.into_inner();
|
||||||
let user_id = ®istration_start_request.username;
|
let user_id = ®istration_start_request.username;
|
||||||
validation_result.can_access(user_id);
|
if !validation_result.can_write(user_id) {
|
||||||
|
return ApiResult::Right(
|
||||||
|
HttpResponse::Unauthorized().body("Not authorized to change the user's password"),
|
||||||
|
);
|
||||||
|
}
|
||||||
data.backend_handler
|
data.backend_handler
|
||||||
.registration_start(registration_start_request)
|
.registration_start(registration_start_request)
|
||||||
.await
|
.await
|
||||||
@ -519,9 +523,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub enum Permission {
|
||||||
|
Admin,
|
||||||
|
Readonly,
|
||||||
|
Regular,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ValidationResults {
|
pub struct ValidationResults {
|
||||||
pub user: String,
|
pub user: String,
|
||||||
pub is_admin: bool,
|
pub permission: Permission,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidationResults {
|
impl ValidationResults {
|
||||||
@ -529,12 +540,30 @@ impl ValidationResults {
|
|||||||
pub fn admin() -> Self {
|
pub fn admin() -> Self {
|
||||||
Self {
|
Self {
|
||||||
user: "admin".to_string(),
|
user: "admin".to_string(),
|
||||||
is_admin: true,
|
permission: Permission::Admin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_access(&self, user: &str) -> bool {
|
#[must_use]
|
||||||
self.is_admin || self.user == user
|
pub fn is_admin(&self) -> bool {
|
||||||
|
self.permission == Permission::Admin
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_admin_or_readonly(&self) -> bool {
|
||||||
|
self.permission == Permission::Admin || self.permission == Permission::Readonly
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn can_read(&self, user: &str) -> bool {
|
||||||
|
self.permission == Permission::Admin
|
||||||
|
|| self.permission == Permission::Readonly
|
||||||
|
|| self.user == user
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn can_write(&self, user: &str) -> bool {
|
||||||
|
self.permission == Permission::Admin || self.user == user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,10 +590,16 @@ pub(crate) fn check_if_token_is_valid<Backend>(
|
|||||||
if state.jwt_blacklist.read().unwrap().contains(&jwt_hash) {
|
if state.jwt_blacklist.read().unwrap().contains(&jwt_hash) {
|
||||||
return Err(ErrorUnauthorized("JWT was logged out"));
|
return Err(ErrorUnauthorized("JWT was logged out"));
|
||||||
}
|
}
|
||||||
let is_admin = token.claims().groups.contains("lldap_admin");
|
let is_in_group = |name| token.claims().groups.contains(name);
|
||||||
Ok(ValidationResults {
|
Ok(ValidationResults {
|
||||||
user: token.claims().user.clone(),
|
user: token.claims().user.clone(),
|
||||||
is_admin,
|
permission: if is_in_group("lldap_admin") {
|
||||||
|
Permission::Admin
|
||||||
|
} else if is_in_group("lldap_readonly") {
|
||||||
|
Permission::Readonly
|
||||||
|
} else {
|
||||||
|
Permission::Regular
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
|
|||||||
context: &Context<Handler>,
|
context: &Context<Handler>,
|
||||||
user: CreateUserInput,
|
user: CreateUserInput,
|
||||||
) -> FieldResult<super::query::User<Handler>> {
|
) -> FieldResult<super::query::User<Handler>> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin() {
|
||||||
return Err("Unauthorized user creation".into());
|
return Err("Unauthorized user creation".into());
|
||||||
}
|
}
|
||||||
let user_id = UserId::new(&user.id);
|
let user_id = UserId::new(&user.id);
|
||||||
@ -88,7 +88,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
|
|||||||
context: &Context<Handler>,
|
context: &Context<Handler>,
|
||||||
name: String,
|
name: String,
|
||||||
) -> FieldResult<super::query::Group<Handler>> {
|
) -> FieldResult<super::query::Group<Handler>> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin() {
|
||||||
return Err("Unauthorized group creation".into());
|
return Err("Unauthorized group creation".into());
|
||||||
}
|
}
|
||||||
let group_id = context.handler.create_group(&name).await?;
|
let group_id = context.handler.create_group(&name).await?;
|
||||||
@ -103,7 +103,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
|
|||||||
context: &Context<Handler>,
|
context: &Context<Handler>,
|
||||||
user: UpdateUserInput,
|
user: UpdateUserInput,
|
||||||
) -> FieldResult<Success> {
|
) -> FieldResult<Success> {
|
||||||
if !context.validation_result.can_access(&user.id) {
|
if !context.validation_result.can_write(&user.id) {
|
||||||
return Err("Unauthorized user update".into());
|
return Err("Unauthorized user update".into());
|
||||||
}
|
}
|
||||||
context
|
context
|
||||||
@ -123,7 +123,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
|
|||||||
context: &Context<Handler>,
|
context: &Context<Handler>,
|
||||||
group: UpdateGroupInput,
|
group: UpdateGroupInput,
|
||||||
) -> FieldResult<Success> {
|
) -> FieldResult<Success> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin() {
|
||||||
return Err("Unauthorized group update".into());
|
return Err("Unauthorized group update".into());
|
||||||
}
|
}
|
||||||
if group.id == 1 {
|
if group.id == 1 {
|
||||||
@ -144,7 +144,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
|
|||||||
user_id: String,
|
user_id: String,
|
||||||
group_id: i32,
|
group_id: i32,
|
||||||
) -> FieldResult<Success> {
|
) -> FieldResult<Success> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin() {
|
||||||
return Err("Unauthorized group membership modification".into());
|
return Err("Unauthorized group membership modification".into());
|
||||||
}
|
}
|
||||||
context
|
context
|
||||||
@ -159,7 +159,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
|
|||||||
user_id: String,
|
user_id: String,
|
||||||
group_id: i32,
|
group_id: i32,
|
||||||
) -> FieldResult<Success> {
|
) -> FieldResult<Success> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin() {
|
||||||
return Err("Unauthorized group membership modification".into());
|
return Err("Unauthorized group membership modification".into());
|
||||||
}
|
}
|
||||||
if context.validation_result.user == user_id && group_id == 1 {
|
if context.validation_result.user == user_id && group_id == 1 {
|
||||||
@ -173,7 +173,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_user(context: &Context<Handler>, user_id: String) -> FieldResult<Success> {
|
async fn delete_user(context: &Context<Handler>, user_id: String) -> FieldResult<Success> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin() {
|
||||||
return Err("Unauthorized user deletion".into());
|
return Err("Unauthorized user deletion".into());
|
||||||
}
|
}
|
||||||
if context.validation_result.user == user_id {
|
if context.validation_result.user == user_id {
|
||||||
@ -184,7 +184,7 @@ impl<Handler: BackendHandler + Sync> Mutation<Handler> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_group(context: &Context<Handler>, group_id: i32) -> FieldResult<Success> {
|
async fn delete_group(context: &Context<Handler>, group_id: i32) -> FieldResult<Success> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin() {
|
||||||
return Err("Unauthorized group deletion".into());
|
return Err("Unauthorized group deletion".into());
|
||||||
}
|
}
|
||||||
if group_id == 1 {
|
if group_id == 1 {
|
||||||
|
@ -107,7 +107,7 @@ impl<Handler: BackendHandler + Sync> Query<Handler> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn user(context: &Context<Handler>, user_id: String) -> FieldResult<User<Handler>> {
|
pub async fn user(context: &Context<Handler>, user_id: String) -> FieldResult<User<Handler>> {
|
||||||
if !context.validation_result.can_access(&user_id) {
|
if !context.validation_result.can_read(&user_id) {
|
||||||
return Err("Unauthorized access to user data".into());
|
return Err("Unauthorized access to user data".into());
|
||||||
}
|
}
|
||||||
Ok(context
|
Ok(context
|
||||||
@ -121,7 +121,7 @@ impl<Handler: BackendHandler + Sync> Query<Handler> {
|
|||||||
context: &Context<Handler>,
|
context: &Context<Handler>,
|
||||||
#[graphql(name = "where")] filters: Option<RequestFilter>,
|
#[graphql(name = "where")] filters: Option<RequestFilter>,
|
||||||
) -> FieldResult<Vec<User<Handler>>> {
|
) -> FieldResult<Vec<User<Handler>>> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin_or_readonly() {
|
||||||
return Err("Unauthorized access to user list".into());
|
return Err("Unauthorized access to user list".into());
|
||||||
}
|
}
|
||||||
Ok(context
|
Ok(context
|
||||||
@ -132,7 +132,7 @@ impl<Handler: BackendHandler + Sync> Query<Handler> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn groups(context: &Context<Handler>) -> FieldResult<Vec<Group<Handler>>> {
|
async fn groups(context: &Context<Handler>) -> FieldResult<Vec<Group<Handler>>> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin_or_readonly() {
|
||||||
return Err("Unauthorized access to group list".into());
|
return Err("Unauthorized access to group list".into());
|
||||||
}
|
}
|
||||||
Ok(context
|
Ok(context
|
||||||
@ -143,7 +143,7 @@ impl<Handler: BackendHandler + Sync> Query<Handler> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn group(context: &Context<Handler>, group_id: i32) -> FieldResult<Group<Handler>> {
|
async fn group(context: &Context<Handler>, group_id: i32) -> FieldResult<Group<Handler>> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin_or_readonly() {
|
||||||
return Err("Unauthorized access to group data".into());
|
return Err("Unauthorized access to group data".into());
|
||||||
}
|
}
|
||||||
Ok(context
|
Ok(context
|
||||||
@ -234,7 +234,7 @@ impl<Handler: BackendHandler + Sync> Group<Handler> {
|
|||||||
}
|
}
|
||||||
/// The groups to which this user belongs.
|
/// The groups to which this user belongs.
|
||||||
async fn users(&self, context: &Context<Handler>) -> FieldResult<Vec<User<Handler>>> {
|
async fn users(&self, context: &Context<Handler>) -> FieldResult<Vec<User<Handler>>> {
|
||||||
if !context.validation_result.is_admin {
|
if !context.validation_result.is_admin_or_readonly() {
|
||||||
return Err("Unauthorized access to group data".into());
|
return Err("Unauthorized access to group data".into());
|
||||||
}
|
}
|
||||||
Ok(context
|
Ok(context
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
use crate::domain::{
|
use crate::{
|
||||||
|
domain::{
|
||||||
handler::{
|
handler::{
|
||||||
BackendHandler, BindRequest, Group, GroupRequestFilter, LoginHandler, User, UserId,
|
BackendHandler, BindRequest, Group, GroupRequestFilter, LoginHandler, User, UserId,
|
||||||
UserRequestFilter,
|
UserRequestFilter,
|
||||||
},
|
},
|
||||||
opaque_handler::OpaqueHandler,
|
opaque_handler::OpaqueHandler,
|
||||||
|
},
|
||||||
|
infra::auth_service::Permission,
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@ -378,12 +381,6 @@ fn root_dse_response(base_dn: &str) -> LdapOp {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
||||||
enum Permission {
|
|
||||||
Admin,
|
|
||||||
Regular,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LdapHandler<Backend: BackendHandler + LoginHandler + OpaqueHandler> {
|
pub struct LdapHandler<Backend: BackendHandler + LoginHandler + OpaqueHandler> {
|
||||||
user_info: Option<(UserId, Permission)>,
|
user_info: Option<(UserId, Permission)>,
|
||||||
backend_handler: Backend,
|
backend_handler: Backend,
|
||||||
@ -436,16 +433,19 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let is_admin = self
|
let user_groups = self.backend_handler.get_user_groups(&user_id).await;
|
||||||
.backend_handler
|
let is_in_group = |name| {
|
||||||
.get_user_groups(&user_id)
|
user_groups
|
||||||
.await
|
.as_ref()
|
||||||
.map(|groups| groups.iter().any(|g| g.1 == "lldap_admin"))
|
.map(|groups| groups.iter().any(|g| g.1 == name))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false)
|
||||||
|
};
|
||||||
self.user_info = Some((
|
self.user_info = Some((
|
||||||
user_id,
|
user_id,
|
||||||
if is_admin {
|
if is_in_group("lldap_admin") {
|
||||||
Permission::Admin
|
Permission::Admin
|
||||||
|
} else if is_in_group("lldap_readonly") {
|
||||||
|
Permission::Readonly
|
||||||
} else {
|
} else {
|
||||||
Permission::Regular
|
Permission::Regular
|
||||||
},
|
},
|
||||||
@ -497,10 +497,10 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
|||||||
match get_user_id_from_distinguished_name(user, &self.base_dn, &self.base_dn_str) {
|
match get_user_id_from_distinguished_name(user, &self.base_dn, &self.base_dn_str) {
|
||||||
Ok(uid) => {
|
Ok(uid) => {
|
||||||
if *permission != Permission::Admin && user_id != &uid {
|
if *permission != Permission::Admin && user_id != &uid {
|
||||||
return vec![make_search_error(
|
return vec![make_extended_response(
|
||||||
LdapResultCode::InsufficentAccessRights,
|
LdapResultCode::InsufficentAccessRights,
|
||||||
format!(
|
format!(
|
||||||
r#"User {} cannot modify the password of user {}"#,
|
r#"User `{}` cannot modify the password of user `{}`"#,
|
||||||
&user_id, &uid
|
&user_id, &uid
|
||||||
),
|
),
|
||||||
)];
|
)];
|
||||||
@ -542,7 +542,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
|||||||
|
|
||||||
pub async fn do_search(&mut self, request: &LdapSearchRequest) -> Vec<LdapOp> {
|
pub async fn do_search(&mut self, request: &LdapSearchRequest) -> Vec<LdapOp> {
|
||||||
let user_filter = match &self.user_info {
|
let user_filter = match &self.user_info {
|
||||||
Some((_, Permission::Admin)) => None,
|
Some((_, Permission::Admin)) | Some((_, Permission::Readonly)) => None,
|
||||||
Some((user_id, Permission::Regular)) => Some(user_id),
|
Some((user_id, Permission::Regular)) => Some(user_id),
|
||||||
None => {
|
None => {
|
||||||
return vec![make_search_error(
|
return vec![make_search_error(
|
||||||
@ -959,8 +959,9 @@ mod tests {
|
|||||||
make_search_request::<S>("ou=people,Dc=example,dc=com", filter, attrs)
|
make_search_request::<S>("ou=people,Dc=example,dc=com", filter, attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_bound_handler(
|
async fn setup_bound_handler_with_group(
|
||||||
mut mock: MockTestBackendHandler,
|
mut mock: MockTestBackendHandler,
|
||||||
|
group: &str,
|
||||||
) -> LdapHandler<MockTestBackendHandler> {
|
) -> LdapHandler<MockTestBackendHandler> {
|
||||||
mock.expect_bind()
|
mock.expect_bind()
|
||||||
.with(eq(BindRequest {
|
.with(eq(BindRequest {
|
||||||
@ -968,11 +969,12 @@ mod tests {
|
|||||||
password: "pass".to_string(),
|
password: "pass".to_string(),
|
||||||
}))
|
}))
|
||||||
.return_once(|_| Ok(()));
|
.return_once(|_| Ok(()));
|
||||||
|
let group = group.to_string();
|
||||||
mock.expect_get_user_groups()
|
mock.expect_get_user_groups()
|
||||||
.with(eq(UserId::new("test")))
|
.with(eq(UserId::new("test")))
|
||||||
.return_once(|_| {
|
.return_once(|_| {
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
set.insert(GroupIdAndName(GroupId(42), "lldap_admin".to_string()));
|
set.insert(GroupIdAndName(GroupId(42), group));
|
||||||
Ok(set)
|
Ok(set)
|
||||||
});
|
});
|
||||||
let mut ldap_handler =
|
let mut ldap_handler =
|
||||||
@ -988,6 +990,18 @@ mod tests {
|
|||||||
ldap_handler
|
ldap_handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn setup_bound_readonly_handler(
|
||||||
|
mock: MockTestBackendHandler,
|
||||||
|
) -> LdapHandler<MockTestBackendHandler> {
|
||||||
|
setup_bound_handler_with_group(mock, "lldap_readonly").await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_bound_admin_handler(
|
||||||
|
mock: MockTestBackendHandler,
|
||||||
|
) -> LdapHandler<MockTestBackendHandler> {
|
||||||
|
setup_bound_handler_with_group(mock, "lldap_admin").await
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_bind() {
|
async fn test_bind() {
|
||||||
let mut mock = MockTestBackendHandler::new();
|
let mut mock = MockTestBackendHandler::new();
|
||||||
@ -1053,15 +1067,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_search_non_admin_user() {
|
async fn test_search_regular_user() {
|
||||||
let mut mock = MockTestBackendHandler::new();
|
let mut mock = MockTestBackendHandler::new();
|
||||||
mock.expect_bind()
|
|
||||||
.with(eq(crate::domain::handler::BindRequest {
|
|
||||||
name: UserId::new("test"),
|
|
||||||
password: "pass".to_string(),
|
|
||||||
}))
|
|
||||||
.times(1)
|
|
||||||
.return_once(|_| Ok(()));
|
|
||||||
mock.expect_list_users()
|
mock.expect_list_users()
|
||||||
.with(eq(Some(UserRequestFilter::And(vec![
|
.with(eq(Some(UserRequestFilter::And(vec![
|
||||||
UserRequestFilter::And(vec![]),
|
UserRequestFilter::And(vec![]),
|
||||||
@ -1074,20 +1081,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}])
|
}])
|
||||||
});
|
});
|
||||||
mock.expect_get_user_groups()
|
let mut ldap_handler = setup_bound_handler_with_group(mock, "regular").await;
|
||||||
.with(eq(UserId::new("test")))
|
|
||||||
.return_once(|_| Ok(HashSet::new()));
|
|
||||||
let mut ldap_handler =
|
|
||||||
LdapHandler::new(mock, "dc=example,dc=com".to_string(), vec![], vec![]);
|
|
||||||
|
|
||||||
let request = LdapBindRequest {
|
|
||||||
dn: "uid=test,ou=people,dc=example,dc=com".to_string(),
|
|
||||||
cred: LdapBindCred::Simple("pass".to_string()),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
ldap_handler.do_bind(&request).await.0,
|
|
||||||
LdapResultCode::Success
|
|
||||||
);
|
|
||||||
|
|
||||||
let request =
|
let request =
|
||||||
make_user_search_request::<String>(LdapFilter::And(vec![]), vec!["1.1".to_string()]);
|
make_user_search_request::<String>(LdapFilter::And(vec![]), vec!["1.1".to_string()]);
|
||||||
@ -1103,6 +1097,23 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_search_readonly_user() {
|
||||||
|
let mut mock = MockTestBackendHandler::new();
|
||||||
|
mock.expect_list_users()
|
||||||
|
.with(eq(Some(UserRequestFilter::And(vec![]))))
|
||||||
|
.times(1)
|
||||||
|
.return_once(|_| Ok(vec![]));
|
||||||
|
let mut ldap_handler = setup_bound_readonly_handler(mock).await;
|
||||||
|
|
||||||
|
let request =
|
||||||
|
make_user_search_request::<String>(LdapFilter::And(vec![]), vec!["1.1".to_string()]);
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.do_search(&request).await,
|
||||||
|
vec![make_search_success()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_bind_invalid_dn() {
|
async fn test_bind_invalid_dn() {
|
||||||
let mock = MockTestBackendHandler::new();
|
let mock = MockTestBackendHandler::new();
|
||||||
@ -1208,7 +1219,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_user_search_request(
|
let request = make_user_search_request(
|
||||||
LdapFilter::And(vec![]),
|
LdapFilter::And(vec![]),
|
||||||
vec![
|
vec![
|
||||||
@ -1334,7 +1345,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_search_request(
|
let request = make_search_request(
|
||||||
"ou=groups,dc=example,dc=cOm",
|
"ou=groups,dc=example,dc=cOm",
|
||||||
LdapFilter::And(vec![]),
|
LdapFilter::And(vec![]),
|
||||||
@ -1417,7 +1428,7 @@ mod tests {
|
|||||||
users: vec![],
|
users: vec![],
|
||||||
}])
|
}])
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_search_request(
|
let request = make_search_request(
|
||||||
"ou=groups,dc=example,dc=com",
|
"ou=groups,dc=example,dc=com",
|
||||||
LdapFilter::And(vec![
|
LdapFilter::And(vec![
|
||||||
@ -1466,7 +1477,7 @@ mod tests {
|
|||||||
users: vec![],
|
users: vec![],
|
||||||
}])
|
}])
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_search_request(
|
let request = make_search_request(
|
||||||
"ou=groups,dc=example,dc=com",
|
"ou=groups,dc=example,dc=com",
|
||||||
LdapFilter::Or(vec![LdapFilter::Not(Box::new(LdapFilter::Equality(
|
LdapFilter::Or(vec![LdapFilter::Not(Box::new(LdapFilter::Equality(
|
||||||
@ -1505,7 +1516,7 @@ mod tests {
|
|||||||
"Error getting groups".to_string(),
|
"Error getting groups".to_string(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_search_request(
|
let request = make_search_request(
|
||||||
"ou=groups,dc=example,dc=com",
|
"ou=groups,dc=example,dc=com",
|
||||||
LdapFilter::Or(vec![LdapFilter::Not(Box::new(LdapFilter::Equality(
|
LdapFilter::Or(vec![LdapFilter::Not(Box::new(LdapFilter::Equality(
|
||||||
@ -1525,7 +1536,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_search_groups_filter_error() {
|
async fn test_search_groups_filter_error() {
|
||||||
let mut ldap_handler = setup_bound_handler(MockTestBackendHandler::new()).await;
|
let mut ldap_handler = setup_bound_admin_handler(MockTestBackendHandler::new()).await;
|
||||||
let request = make_search_request(
|
let request = make_search_request(
|
||||||
"ou=groups,dc=example,dc=com",
|
"ou=groups,dc=example,dc=com",
|
||||||
LdapFilter::And(vec![LdapFilter::Substring(
|
LdapFilter::And(vec![LdapFilter::Substring(
|
||||||
@ -1561,7 +1572,7 @@ mod tests {
|
|||||||
]))))
|
]))))
|
||||||
.times(1)
|
.times(1)
|
||||||
.return_once(|_| Ok(vec![]));
|
.return_once(|_| Ok(vec![]));
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_user_search_request(
|
let request = make_user_search_request(
|
||||||
LdapFilter::And(vec![LdapFilter::Or(vec![
|
LdapFilter::And(vec![LdapFilter::Or(vec![
|
||||||
LdapFilter::Not(Box::new(LdapFilter::Equality(
|
LdapFilter::Not(Box::new(LdapFilter::Equality(
|
||||||
@ -1590,7 +1601,7 @@ mod tests {
|
|||||||
.with(eq(Some(UserRequestFilter::MemberOf("group_1".to_string()))))
|
.with(eq(Some(UserRequestFilter::MemberOf("group_1".to_string()))))
|
||||||
.times(1)
|
.times(1)
|
||||||
.return_once(|_| Ok(vec![]));
|
.return_once(|_| Ok(vec![]));
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_user_search_request(
|
let request = make_user_search_request(
|
||||||
LdapFilter::Equality(
|
LdapFilter::Equality(
|
||||||
"memberOf".to_string(),
|
"memberOf".to_string(),
|
||||||
@ -1645,7 +1656,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}])
|
}])
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_user_search_request(
|
let request = make_user_search_request(
|
||||||
LdapFilter::And(vec![LdapFilter::Or(vec![LdapFilter::Not(Box::new(
|
LdapFilter::And(vec![LdapFilter::Or(vec![LdapFilter::Not(Box::new(
|
||||||
LdapFilter::Equality("givenname".to_string(), "bob".to_string()),
|
LdapFilter::Equality("givenname".to_string(), "bob".to_string()),
|
||||||
@ -1695,7 +1706,7 @@ mod tests {
|
|||||||
users: vec![UserId::new("bob"), UserId::new("john")],
|
users: vec![UserId::new("bob"), UserId::new("john")],
|
||||||
}])
|
}])
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = make_search_request(
|
let request = make_search_request(
|
||||||
"dc=example,dc=com",
|
"dc=example,dc=com",
|
||||||
LdapFilter::And(vec![]),
|
LdapFilter::And(vec![]),
|
||||||
@ -1771,7 +1782,7 @@ mod tests {
|
|||||||
users: vec![UserId::new("bob"), UserId::new("john")],
|
users: vec![UserId::new("bob"), UserId::new("john")],
|
||||||
}])
|
}])
|
||||||
});
|
});
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
|
|
||||||
// Test simple wildcard
|
// Test simple wildcard
|
||||||
let request =
|
let request =
|
||||||
@ -1895,7 +1906,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_search_wrong_base() {
|
async fn test_search_wrong_base() {
|
||||||
let mut ldap_handler = setup_bound_handler(MockTestBackendHandler::new()).await;
|
let mut ldap_handler = setup_bound_admin_handler(MockTestBackendHandler::new()).await;
|
||||||
let request = make_search_request(
|
let request = make_search_request(
|
||||||
"ou=users,dc=example,dc=com",
|
"ou=users,dc=example,dc=com",
|
||||||
LdapFilter::And(vec![]),
|
LdapFilter::And(vec![]),
|
||||||
@ -1909,7 +1920,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_search_unsupported_filters() {
|
async fn test_search_unsupported_filters() {
|
||||||
let mut ldap_handler = setup_bound_handler(MockTestBackendHandler::new()).await;
|
let mut ldap_handler = setup_bound_admin_handler(MockTestBackendHandler::new()).await;
|
||||||
let request = make_user_search_request(
|
let request = make_user_search_request(
|
||||||
LdapFilter::Substring(
|
LdapFilter::Substring(
|
||||||
"uid".to_string(),
|
"uid".to_string(),
|
||||||
@ -1952,7 +1963,7 @@ mod tests {
|
|||||||
mock.expect_registration_finish()
|
mock.expect_registration_finish()
|
||||||
.times(1)
|
.times(1)
|
||||||
.return_once(|_| Ok(()));
|
.return_once(|_| Ok(()));
|
||||||
let mut ldap_handler = setup_bound_handler(mock).await;
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
let request = LdapOp::ExtendedRequest(
|
let request = LdapOp::ExtendedRequest(
|
||||||
LdapPasswordModifyRequest {
|
LdapPasswordModifyRequest {
|
||||||
user_identity: Some("uid=bob,ou=people,dc=example,dc=com".to_string()),
|
user_identity: Some("uid=bob,ou=people,dc=example,dc=com".to_string()),
|
||||||
@ -1972,7 +1983,7 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_password_change_errors() {
|
async fn test_password_change_errors() {
|
||||||
let mut ldap_handler = setup_bound_handler(MockTestBackendHandler::new()).await;
|
let mut ldap_handler = setup_bound_admin_handler(MockTestBackendHandler::new()).await;
|
||||||
let request = LdapOp::ExtendedRequest(
|
let request = LdapOp::ExtendedRequest(
|
||||||
LdapPasswordModifyRequest {
|
LdapPasswordModifyRequest {
|
||||||
user_identity: None,
|
user_identity: None,
|
||||||
@ -2016,9 +2027,29 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_password_change_unauthorized() {
|
||||||
|
let mut ldap_handler = setup_bound_readonly_handler(MockTestBackendHandler::new()).await;
|
||||||
|
let request = LdapOp::ExtendedRequest(
|
||||||
|
LdapPasswordModifyRequest {
|
||||||
|
user_identity: Some("uid=bob,ou=people,dc=example,dc=com".to_string()),
|
||||||
|
old_password: Some("pass".to_string()),
|
||||||
|
new_password: Some("password".to_string()),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.handle_ldap_message(request).await,
|
||||||
|
Some(vec![make_extended_response(
|
||||||
|
LdapResultCode::InsufficentAccessRights,
|
||||||
|
"User `test` cannot modify the password of user `bob`".to_string(),
|
||||||
|
)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_search_root_dse() {
|
async fn test_search_root_dse() {
|
||||||
let mut ldap_handler = setup_bound_handler(MockTestBackendHandler::new()).await;
|
let mut ldap_handler = setup_bound_admin_handler(MockTestBackendHandler::new()).await;
|
||||||
let request = LdapSearchRequest {
|
let request = LdapSearchRequest {
|
||||||
base: "".to_string(),
|
base: "".to_string(),
|
||||||
scope: LdapSearchScope::Base,
|
scope: LdapSearchScope::Base,
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
domain::{
|
domain::{
|
||||||
handler::{BackendHandler, CreateUserRequest},
|
handler::{BackendHandler, CreateUserRequest, GroupRequestFilter},
|
||||||
sql_backend_handler::SqlBackendHandler,
|
sql_backend_handler::SqlBackendHandler,
|
||||||
sql_opaque_handler::register_password,
|
sql_opaque_handler::register_password,
|
||||||
sql_tables::PoolOptions,
|
sql_tables::PoolOptions,
|
||||||
@ -62,6 +62,19 @@ async fn run_server(config: Configuration) -> Result<()> {
|
|||||||
.map_err(|e| anyhow!("Error setting up admin login/account: {:#}", e))
|
.map_err(|e| anyhow!("Error setting up admin login/account: {:#}", e))
|
||||||
.context("while creating the admin user")?;
|
.context("while creating the admin user")?;
|
||||||
}
|
}
|
||||||
|
if backend_handler
|
||||||
|
.list_groups(Some(GroupRequestFilter::DisplayName(
|
||||||
|
"lldap_readonly".to_string(),
|
||||||
|
)))
|
||||||
|
.await?
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
|
warn!("Could not find readonly group, trying to create it");
|
||||||
|
backend_handler
|
||||||
|
.create_group("lldap_readonly")
|
||||||
|
.await
|
||||||
|
.context("while creating readonly group")?;
|
||||||
|
}
|
||||||
let server_builder = infra::ldap_server::build_ldap_server(
|
let server_builder = infra::ldap_server::build_ldap_server(
|
||||||
&config,
|
&config,
|
||||||
backend_handler.clone(),
|
backend_handler.clone(),
|
||||||
|
Loading…
Reference in New Issue
Block a user