mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
Translate the LDAP DN into user IDs
This commit is contained in:
parent
7e76d3aae2
commit
b49a03fd87
@ -160,6 +160,11 @@ impl BackendHandler for SqlBackendHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user_groups(&self, user: String) -> Result<HashSet<String>> {
|
async fn get_user_groups(&self, user: String) -> Result<HashSet<String>> {
|
||||||
|
if user == self.config.ldap_user_dn {
|
||||||
|
let mut groups = HashSet::new();
|
||||||
|
groups.insert("lldap_admin".to_string());
|
||||||
|
return Ok(groups);
|
||||||
|
}
|
||||||
let query: String = Query::select()
|
let query: String = Query::select()
|
||||||
.column(Groups::DisplayName)
|
.column(Groups::DisplayName)
|
||||||
.from(Groups::Table)
|
.from(Groups::Table)
|
||||||
|
@ -30,7 +30,8 @@ impl Default for Configuration {
|
|||||||
secret_pepper: String::from("secretsecretpepper"),
|
secret_pepper: String::from("secretsecretpepper"),
|
||||||
jwt_secret: String::from("secretjwtsecret"),
|
jwt_secret: String::from("secretjwtsecret"),
|
||||||
ldap_base_dn: String::from("dc=example,dc=com"),
|
ldap_base_dn: String::from("dc=example,dc=com"),
|
||||||
ldap_user_dn: String::from("cn=admin,dc=example,dc=com"),
|
// cn=admin,dc=example,dc=com
|
||||||
|
ldap_user_dn: String::from("admin"),
|
||||||
ldap_user_pass: String::from("password"),
|
ldap_user_pass: String::from("password"),
|
||||||
database_url: String::from("sqlite://users.db?mode=rwc"),
|
database_url: String::from("sqlite://users.db?mode=rwc"),
|
||||||
verbose: false,
|
verbose: false,
|
||||||
|
@ -29,6 +29,37 @@ fn parse_distinguished_name(dn: &str) -> Result<Vec<(String, String)>> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_user_id_from_distinguished_name(
|
||||||
|
dn: &str,
|
||||||
|
base_tree: &[(String, String)],
|
||||||
|
base_dn_str: &str,
|
||||||
|
ldap_user_dn: &str,
|
||||||
|
) -> Result<String> {
|
||||||
|
let parts = parse_distinguished_name(dn)?;
|
||||||
|
if !is_subtree(&parts, base_tree) {
|
||||||
|
bail!("Not a subtree of the base tree");
|
||||||
|
}
|
||||||
|
if parts.len() == base_tree.len() + 1 {
|
||||||
|
if dn != ldap_user_dn {
|
||||||
|
bail!(r#"Wrong admin DN. Expected: "{}""#, ldap_user_dn);
|
||||||
|
}
|
||||||
|
Ok(parts[0].1.to_string())
|
||||||
|
} else if parts.len() == base_tree.len() + 2 {
|
||||||
|
if parts[1].0 != "ou" || parts[1].1 != "people" || parts[0].0 != "cn" {
|
||||||
|
bail!(
|
||||||
|
r#"Unexpected user DN format. Expected: "cn=username,ou=people,{}""#,
|
||||||
|
base_dn_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(parts[0].1.to_string())
|
||||||
|
} else {
|
||||||
|
bail!(
|
||||||
|
r#"Unexpected user DN format. Expected: "cn=username,ou=people,{}""#,
|
||||||
|
base_dn_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_attribute(user: &User, attribute: &str) -> Result<Vec<String>> {
|
fn get_attribute(user: &User, attribute: &str) -> Result<Vec<String>> {
|
||||||
match attribute {
|
match attribute {
|
||||||
"objectClass" => Ok(vec![
|
"objectClass" => Ok(vec![
|
||||||
@ -132,16 +163,25 @@ impl<Backend: BackendHandler> LdapHandler<Backend> {
|
|||||||
ldap_base_dn
|
ldap_base_dn
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
ldap_user_dn: format!("cn={},{}", ldap_user_dn, &ldap_base_dn),
|
||||||
base_dn_str: ldap_base_dn,
|
base_dn_str: ldap_base_dn,
|
||||||
ldap_user_dn,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn do_bind(&mut self, sbr: &SimpleBindRequest) -> LdapMsg {
|
pub async fn do_bind(&mut self, sbr: &SimpleBindRequest) -> LdapMsg {
|
||||||
|
let user_id = match get_user_id_from_distinguished_name(
|
||||||
|
&sbr.dn,
|
||||||
|
&self.base_dn,
|
||||||
|
&self.base_dn_str,
|
||||||
|
&self.ldap_user_dn,
|
||||||
|
) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return sbr.gen_error(LdapResultCode::NamingViolation, e.to_string()),
|
||||||
|
};
|
||||||
match self
|
match self
|
||||||
.backend_handler
|
.backend_handler
|
||||||
.bind(crate::domain::handler::BindRequest {
|
.bind(crate::domain::handler::BindRequest {
|
||||||
name: sbr.dn.clone(),
|
name: user_id,
|
||||||
password: sbr.pw.clone(),
|
password: sbr.pw.clone(),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -232,6 +272,7 @@ impl<Backend: BackendHandler> LdapHandler<Backend> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::domain::handler::BindRequest;
|
||||||
use crate::domain::handler::MockTestBackendHandler;
|
use crate::domain::handler::MockTestBackendHandler;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use mockall::predicate::eq;
|
use mockall::predicate::eq;
|
||||||
@ -240,12 +281,17 @@ mod tests {
|
|||||||
async fn setup_bound_handler(
|
async fn setup_bound_handler(
|
||||||
mut mock: MockTestBackendHandler,
|
mut mock: MockTestBackendHandler,
|
||||||
) -> LdapHandler<MockTestBackendHandler> {
|
) -> LdapHandler<MockTestBackendHandler> {
|
||||||
mock.expect_bind().return_once(|_| Ok(()));
|
mock.expect_bind()
|
||||||
|
.with(eq(BindRequest {
|
||||||
|
name: "test".to_string(),
|
||||||
|
password: "pass".to_string(),
|
||||||
|
}))
|
||||||
|
.return_once(|_| Ok(()));
|
||||||
let mut ldap_handler =
|
let mut ldap_handler =
|
||||||
LdapHandler::new(mock, "dc=example,dc=com".to_string(), "test".to_string());
|
LdapHandler::new(mock, "dc=example,dc=com".to_string(), "test".to_string());
|
||||||
let request = SimpleBindRequest {
|
let request = SimpleBindRequest {
|
||||||
msgid: 1,
|
msgid: 1,
|
||||||
dn: "test".to_string(),
|
dn: "cn=test,dc=example,dc=com".to_string(),
|
||||||
pw: "pass".to_string(),
|
pw: "pass".to_string(),
|
||||||
};
|
};
|
||||||
ldap_handler.do_bind(&request).await;
|
ldap_handler.do_bind(&request).await;
|
||||||
@ -254,6 +300,39 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_bind() {
|
async fn test_bind() {
|
||||||
|
let mut mock = MockTestBackendHandler::new();
|
||||||
|
mock.expect_bind()
|
||||||
|
.with(eq(crate::domain::handler::BindRequest {
|
||||||
|
name: "bob".to_string(),
|
||||||
|
password: "pass".to_string(),
|
||||||
|
}))
|
||||||
|
.times(1)
|
||||||
|
.return_once(|_| Ok(()));
|
||||||
|
let mut ldap_handler =
|
||||||
|
LdapHandler::new(mock, "dc=example,dc=com".to_string(), "test".to_string());
|
||||||
|
|
||||||
|
let request = WhoamiRequest { msgid: 1 };
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.do_whoami(&request),
|
||||||
|
request.gen_operror("Unauthenticated")
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = SimpleBindRequest {
|
||||||
|
msgid: 2,
|
||||||
|
dn: "cn=bob,ou=people,dc=example,dc=com".to_string(),
|
||||||
|
pw: "pass".to_string(),
|
||||||
|
};
|
||||||
|
assert_eq!(ldap_handler.do_bind(&request).await, request.gen_success());
|
||||||
|
|
||||||
|
let request = WhoamiRequest { msgid: 3 };
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.do_whoami(&request),
|
||||||
|
request.gen_success("dn: cn=bob,ou=people,dc=example,dc=com")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_admin_bind() {
|
||||||
let mut mock = MockTestBackendHandler::new();
|
let mut mock = MockTestBackendHandler::new();
|
||||||
mock.expect_bind()
|
mock.expect_bind()
|
||||||
.with(eq(crate::domain::handler::BindRequest {
|
.with(eq(crate::domain::handler::BindRequest {
|
||||||
@ -273,7 +352,7 @@ mod tests {
|
|||||||
|
|
||||||
let request = SimpleBindRequest {
|
let request = SimpleBindRequest {
|
||||||
msgid: 2,
|
msgid: 2,
|
||||||
dn: "test".to_string(),
|
dn: "cn=test,dc=example,dc=com".to_string(),
|
||||||
pw: "pass".to_string(),
|
pw: "pass".to_string(),
|
||||||
};
|
};
|
||||||
assert_eq!(ldap_handler.do_bind(&request).await, request.gen_success());
|
assert_eq!(ldap_handler.do_bind(&request).await, request.gen_success());
|
||||||
@ -281,7 +360,7 @@ mod tests {
|
|||||||
let request = WhoamiRequest { msgid: 3 };
|
let request = WhoamiRequest { msgid: 3 };
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ldap_handler.do_whoami(&request),
|
ldap_handler.do_whoami(&request),
|
||||||
request.gen_success("dn: test")
|
request.gen_success("dn: cn=test,dc=example,dc=com")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +385,7 @@ mod tests {
|
|||||||
|
|
||||||
let request = SimpleBindRequest {
|
let request = SimpleBindRequest {
|
||||||
msgid: 2,
|
msgid: 2,
|
||||||
dn: "test".to_string(),
|
dn: "cn=test,ou=people,dc=example,dc=com".to_string(),
|
||||||
pw: "pass".to_string(),
|
pw: "pass".to_string(),
|
||||||
};
|
};
|
||||||
assert_eq!(ldap_handler.do_bind(&request).await, request.gen_success());
|
assert_eq!(ldap_handler.do_bind(&request).await, request.gen_success());
|
||||||
@ -314,7 +393,7 @@ mod tests {
|
|||||||
let request = WhoamiRequest { msgid: 3 };
|
let request = WhoamiRequest { msgid: 3 };
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ldap_handler.do_whoami(&request),
|
ldap_handler.do_whoami(&request),
|
||||||
request.gen_success("dn: test")
|
request.gen_success("dn: cn=test,ou=people,dc=example,dc=com")
|
||||||
);
|
);
|
||||||
|
|
||||||
let request = SearchRequest {
|
let request = SearchRequest {
|
||||||
@ -333,6 +412,39 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_bind_invalid_dn() {
|
||||||
|
let mock = MockTestBackendHandler::new();
|
||||||
|
let mut ldap_handler =
|
||||||
|
LdapHandler::new(mock, "dc=example,dc=com".to_string(), "admin".to_string());
|
||||||
|
|
||||||
|
let request = SimpleBindRequest {
|
||||||
|
msgid: 2,
|
||||||
|
dn: "cn=bob,dc=example,dc=com".to_string(),
|
||||||
|
pw: "pass".to_string(),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.do_bind(&request).await,
|
||||||
|
request.gen_error(
|
||||||
|
LdapResultCode::NamingViolation,
|
||||||
|
r#"Wrong admin DN. Expected: "cn=admin,dc=example,dc=com""#.to_string()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let request = SimpleBindRequest {
|
||||||
|
msgid: 2,
|
||||||
|
dn: "cn=bob,ou=groups,dc=example,dc=com".to_string(),
|
||||||
|
pw: "pass".to_string(),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.do_bind(&request).await,
|
||||||
|
request.gen_error(
|
||||||
|
LdapResultCode::NamingViolation,
|
||||||
|
r#"Unexpected user DN format. Expected: "cn=username,ou=people,dc=example,dc=com""#
|
||||||
|
.to_string()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_subtree() {
|
fn test_is_subtree() {
|
||||||
let subtree1 = &[
|
let subtree1 = &[
|
||||||
|
Loading…
Reference in New Issue
Block a user