Skip to content

Commit 217f3aa

Browse files
committed
(docker-based-examples) Added simple_bind with start_tls and manager
account authentication example
1 parent f9b890a commit 217f3aa

File tree

5 files changed

+336
-155
lines changed

5 files changed

+336
-155
lines changed

Cargo.toml

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "openldap"
33
version = "1.2.2"
4-
authors = ["Josh Leverette <[email protected]>", "Ross Delinger <[email protected]>", "Stephen Holsapple <[email protected]>", "Yong Wen Chua <[email protected]>"]
4+
authors = ["Josh Leverette <[email protected]>", "Ross Delinger <[email protected]>", "Stephen Holsapple <[email protected]>", "Yong Wen Chua <[email protected]>", "Mathias Myrland <[email protected]>"]
55
license = "MIT"
66
readme = "README.md"
77
repository = "https://github.com/coder543/rust-cldap"
@@ -11,3 +11,8 @@ description = "Straightforward Rust bindings to the C openldap library. This is
1111

1212
[dependencies]
1313
libc = "0.2.10"
14+
15+
[workspace]
16+
members = [
17+
"examples/simple_bind_authentication"
18+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Simple Bind with start_tls
2+
3+
This example shows how to use simple_bind in combination with start_tls
4+
and a manager account to do a typical user authentication lookup.
5+
6+
Start TLS is the recommended way to do secure LDAP; ldaps:// on port 636 is deprecated.
7+
8+
## Running
9+
10+
Start the example docker using the start_example_server.sh script from
11+
the examples directory. Then, from the simple_bind directory, do
12+
13+
```shell script
14+
cargo run -- -u fry -p fry
15+
```
16+
17+
## Steps that are being performed
18+
19+
The first step is to set up the LDAPRust instance, and perform start_tls on it.
20+
This ensures that our communication is encrypted. Note that we are not verifying
21+
the server certificate in this example; this is something you should do in production.
22+
23+
The next step is to simple_bind using our manger accounts DN and password. This
24+
will allow us to perform an ldap_search later on.
25+
26+
Now, we take the incoming user name string, and perform an ldap_search for it.
27+
Note how we are matching either email or username. Our search yields the DN
28+
for the provided credentials.
29+
30+
The last step is to attempt a simple bind with the discovered DN and provided
31+
user password. If all goes well, we are authenticated, otherwise, something
32+
went wrong.
33+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#[macro_use]
2+
extern crate clap;
3+
4+
extern crate openldap;
5+
6+
use openldap::errors::*;
7+
use openldap::*;
8+
use std::ptr;
9+
10+
#[derive(Clap)]
11+
#[clap(
12+
name = "LDAP simple_bind_authentication with start_tls authentication example",
13+
author = "Mathias Myrland <[email protected]>",
14+
version = "0.1.0"
15+
)]
16+
struct AuthOpts {
17+
#[clap(short = "u")]
18+
user: String,
19+
20+
#[clap(short = "p")]
21+
password: String,
22+
}
23+
24+
fn ldap_with_start_tls(ldap_uri: &str) -> Result<RustLDAP, LDAPError> {
25+
let ldap = RustLDAP::new(ldap_uri).unwrap();
26+
27+
ldap.set_option(
28+
codes::options::LDAP_OPT_PROTOCOL_VERSION,
29+
&codes::versions::LDAP_VERSION3,
30+
);
31+
32+
// WARNING: Normally you would want to verify the server certificate to avoid
33+
// man in the middle attacks, but for this testing scenario we're using a
34+
// generated self signed certificate from the docker container.
35+
//
36+
// To set up certificate validation, use the LDAP_OPT_X_TLS_CACERT* options
37+
ldap.set_option(
38+
codes::options::LDAP_OPT_X_TLS_REQUIRE_CERT,
39+
&codes::options::LDAP_OPT_X_TLS_NEVER,
40+
);
41+
42+
ldap.set_option(openldap::codes::options::LDAP_OPT_X_TLS_NEWCTX, &0);
43+
44+
ldap.start_tls(None, None)?;
45+
46+
Ok(ldap)
47+
}
48+
49+
fn do_simple_bind(
50+
ldap: &RustLDAP,
51+
ldap_manager_user: &str,
52+
ldap_manager_pass: &str,
53+
) -> Result<(), LDAPError> {
54+
let bind_result = ldap.simple_bind(ldap_manager_user, ldap_manager_pass)?;
55+
56+
match bind_result {
57+
v if v == openldap::codes::results::LDAP_SUCCESS => Ok(()),
58+
_ => Err(LDAPError::from(String::from(
59+
"Authentication with simple bind failed",
60+
))),
61+
}
62+
}
63+
64+
fn ldap_dn_lookup(ldap: &RustLDAP, who: &str) -> Result<String, LDAPError> {
65+
// Show all DNs matching the description "Human"
66+
// ldap_search is a powerful query language, look at
67+
// https://confluence.atlassian.com/kb/how-to-write-ldap-search-filters-792496933.html
68+
// for an overview
69+
//
70+
// This particular filter allows the user to sign in with either
71+
// uid or email
72+
let filter = format!("(|(uid={})(mail={}))", who, who);
73+
74+
match ldap.ldap_search(
75+
"ou=people,dc=planetexpress,dc=com",
76+
codes::scopes::LDAP_SCOPE_SUBTREE,
77+
Some(filter.as_str()),
78+
Some(vec!["dn"]),
79+
true,
80+
None,
81+
None,
82+
ptr::null_mut(),
83+
-1,
84+
) {
85+
Ok(search_results) => {
86+
for result_map in search_results {
87+
for result_tuple in result_map {
88+
println!("Found result map with key {}", result_tuple.0);
89+
for result_data in result_tuple.1 {
90+
println!("\t {}", result_data);
91+
return Ok(result_data);
92+
}
93+
}
94+
}
95+
96+
Err(LDAPError::from(String::from(
97+
"Authentication with simple bind failed",
98+
)))
99+
}
100+
_ => Err(LDAPError::from(String::from(
101+
"Authentication with simple bind failed",
102+
))),
103+
}
104+
}
105+
106+
fn main() {
107+
let options = AuthOpts::parse();
108+
let user_to_authenticate = options.user;
109+
let pwd_to_authenticate = options.password;
110+
111+
let ldap_uri = "ldap://localhost:389";
112+
let ldap_manager_dn = "cn=Hubert J. Farnsworth,ou=people,dc=planetexpress,dc=com";
113+
let ldap_manager_pass = "professor";
114+
115+
let ldap = ldap_with_start_tls(ldap_uri).unwrap();
116+
117+
// Bind to the LDAP server with the manager account,
118+
// this is done to perform a search for the DN to
119+
// use when authenticating the user attempting to
120+
// sign in. Obviously, the manager credentials should
121+
// be kept secret, and not be put under version control.
122+
// In our test scenario, the professor is the manager.
123+
do_simple_bind(&ldap, ldap_manager_dn, ldap_manager_pass).unwrap();
124+
125+
if let Ok(fry_dn) = ldap_dn_lookup(&ldap, user_to_authenticate.as_str()) {
126+
// Now, perform a bind with the DN we found matching the user attempting to sign in
127+
// and the password provided in the authentication request
128+
do_simple_bind(&ldap, fry_dn.as_str(), pwd_to_authenticate.as_str()).unwrap();
129+
130+
println!("Successfully signed in as fry");
131+
}
132+
}

examples/start_example_server.sh

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#! /usr/bin/env bash
2+
3+
docker run -p 389:389 -p 636:636 rroemhild/test-openldap

0 commit comments

Comments
 (0)