Release v0.1.0
- Initial release of LDAP Docker development tool - Full .env configuration support with comprehensive documentation - Pre-configured test users and SSL/TLS support - Consolidated documentation in README
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
This directory contains example scripts and applications demonstrating how to use the LDAP server for authentication and user management.
|
||||
|
||||
> **📝 Note:** In the examples below, values shown as `{.env:VARIABLE_NAME}` are configurable via environment variables in your `.env` file. The actual default values are: `LDAP_PORT=389`, `LDAPS_PORT=636`, `LDAP_BASE_DN=dc=testing,dc=local`, and `LDAP_ADMIN_PASSWORD=admin_password`. See the main [Configuration](../README.md#configuration) section for all available options.
|
||||
|
||||
## Available Examples
|
||||
|
||||
### 1. Simple Authentication (`simple_auth.py`)
|
||||
@@ -9,6 +11,7 @@ This directory contains example scripts and applications demonstrating how to us
|
||||
A Python script demonstrating basic LDAP authentication and user information retrieval.
|
||||
|
||||
**Features:**
|
||||
|
||||
- Authenticate users with username/password
|
||||
- Retrieve detailed user information
|
||||
- Get user group memberships
|
||||
@@ -27,14 +30,14 @@ python examples/simple_auth.py --username jsmith --password password123
|
||||
python examples/simple_auth.py --list-users
|
||||
|
||||
# Use a different LDAP server
|
||||
python examples/simple_auth.py --server ldaps://localhost:636
|
||||
python examples/simple_auth.py --server ldaps://localhost:{.env:LDAPS_PORT}
|
||||
```
|
||||
|
||||
**Example Output:**
|
||||
|
||||
```
|
||||
🔐 LDAP Authentication Example
|
||||
Server: ldap://localhost:389
|
||||
LDAP Authentication Example
|
||||
Server: ldap://localhost:{.env:LDAP_PORT}
|
||||
|
||||
Attempting to authenticate user: jdoe
|
||||
✅ Authentication successful for user: jdoe
|
||||
@@ -52,7 +55,7 @@ Last Name: Doe
|
||||
Email: jdoe@testing.local
|
||||
UID Number: 10001
|
||||
GID Number: 10001
|
||||
DN: uid=jdoe,ou=people,dc=testing,dc=local
|
||||
DN: uid=jdoe,ou=people,{.env:LDAP_BASE_DN}
|
||||
==================================================
|
||||
|
||||
Fetching user groups...
|
||||
@@ -69,17 +72,17 @@ User belongs to 2 group(s):
|
||||
from ldap3 import Server, Connection
|
||||
|
||||
# Connect and authenticate
|
||||
server = Server('ldap://localhost:389')
|
||||
server = Server('ldap://localhost:{.env:LDAP_PORT}')
|
||||
conn = Connection(
|
||||
server,
|
||||
user='uid=jdoe,ou=people,dc=testing,dc=local',
|
||||
user='uid=jdoe,ou=people,{.env:LDAP_BASE_DN}',
|
||||
password='password123',
|
||||
auto_bind=True
|
||||
)
|
||||
|
||||
# Search for users
|
||||
conn.search(
|
||||
'dc=testing,dc=local',
|
||||
'{.env:LDAP_BASE_DN}',
|
||||
'(objectClass=inetOrgPerson)',
|
||||
attributes=['uid', 'cn', 'mail']
|
||||
)
|
||||
@@ -94,243 +97,17 @@ conn.unbind()
|
||||
|
||||
```bash
|
||||
# Search for a user
|
||||
ldapsearch -H ldap://localhost:389 \
|
||||
-D "cn=admin,dc=testing,dc=local" \
|
||||
-w admin_password \
|
||||
-b "dc=testing,dc=local" \
|
||||
ldapsearch -H ldap://localhost:{.env:LDAP_PORT} \
|
||||
-D "cn=admin,{.env:LDAP_BASE_DN}" \
|
||||
-w {.env:LDAP_ADMIN_PASSWORD} \
|
||||
-b "{.env:LDAP_BASE_DN}" \
|
||||
"(uid=jdoe)"
|
||||
|
||||
# List all users
|
||||
ldapsearch -H ldap://localhost:389 \
|
||||
-D "cn=admin,dc=testing,dc=local" \
|
||||
-w admin_password \
|
||||
-b "ou=people,dc=testing,dc=local" \
|
||||
ldapsearch -H ldap://localhost:{.env:LDAP_PORT} \
|
||||
-D "cn=admin,{.env:LDAP_BASE_DN}" \
|
||||
-w {.env:LDAP_ADMIN_PASSWORD} \
|
||||
-b "ou=people,{.env:LDAP_BASE_DN}" \
|
||||
"(objectClass=inetOrgPerson)" \
|
||||
uid cn mail
|
||||
```
|
||||
|
||||
### Web Application Integration
|
||||
|
||||
#### Flask Example
|
||||
|
||||
```python
|
||||
from flask import Flask, request, jsonify
|
||||
from ldap3 import Server, Connection
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/login', methods=['POST'])
|
||||
def login():
|
||||
username = request.json.get('username')
|
||||
password = request.json.get('password')
|
||||
|
||||
server = Server('ldap://localhost:389')
|
||||
user_dn = f'uid={username},ou=people,dc=testing,dc=local'
|
||||
|
||||
try:
|
||||
conn = Connection(server, user=user_dn, password=password)
|
||||
if conn.bind():
|
||||
return jsonify({'status': 'success', 'message': 'Authenticated'})
|
||||
else:
|
||||
return jsonify({'status': 'error', 'message': 'Invalid credentials'}), 401
|
||||
except:
|
||||
return jsonify({'status': 'error', 'message': 'Authentication failed'}), 401
|
||||
```
|
||||
|
||||
#### Django Example
|
||||
|
||||
```python
|
||||
# settings.py
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
|
||||
AUTH_LDAP_SERVER_URI = "ldap://localhost:389"
|
||||
AUTH_LDAP_BIND_DN = "cn=admin,dc=testing,dc=local"
|
||||
AUTH_LDAP_BIND_PASSWORD = "admin_password"
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(
|
||||
"ou=people,dc=testing,dc=local",
|
||||
ldap.SCOPE_SUBTREE,
|
||||
"(uid=%(user)s)"
|
||||
)
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'django_auth_ldap.backend.LDAPBackend',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
]
|
||||
```
|
||||
|
||||
## Common Integration Patterns
|
||||
|
||||
### 1. Simple Bind Authentication
|
||||
|
||||
The most straightforward approach - try to bind with user credentials:
|
||||
|
||||
```python
|
||||
def authenticate_user(username, password):
|
||||
server = Server('ldap://localhost:389')
|
||||
user_dn = f'uid={username},ou=people,dc=testing,dc=local'
|
||||
conn = Connection(server, user=user_dn, password=password)
|
||||
return conn.bind()
|
||||
```
|
||||
|
||||
### 2. Search and Bind
|
||||
|
||||
Search for the user first, then authenticate:
|
||||
|
||||
```python
|
||||
def authenticate_user(username, password):
|
||||
# First, search for the user with admin credentials
|
||||
server = Server('ldap://localhost:389')
|
||||
admin_conn = Connection(
|
||||
server,
|
||||
user='cn=admin,dc=testing,dc=local',
|
||||
password='admin_password',
|
||||
auto_bind=True
|
||||
)
|
||||
|
||||
admin_conn.search(
|
||||
'ou=people,dc=testing,dc=local',
|
||||
f'(uid={username})',
|
||||
attributes=['dn']
|
||||
)
|
||||
|
||||
if not admin_conn.entries:
|
||||
return False
|
||||
|
||||
user_dn = admin_conn.entries[0].entry_dn
|
||||
admin_conn.unbind()
|
||||
|
||||
# Now authenticate as the user
|
||||
user_conn = Connection(server, user=user_dn, password=password)
|
||||
return user_conn.bind()
|
||||
```
|
||||
|
||||
### 3. Group-Based Authorization
|
||||
|
||||
Check if user belongs to specific groups:
|
||||
|
||||
```python
|
||||
def user_has_role(username, required_group):
|
||||
server = Server('ldap://localhost:389')
|
||||
conn = Connection(
|
||||
server,
|
||||
user='cn=admin,dc=testing,dc=local',
|
||||
password='admin_password',
|
||||
auto_bind=True
|
||||
)
|
||||
|
||||
user_dn = f'uid={username},ou=people,dc=testing,dc=local'
|
||||
|
||||
conn.search(
|
||||
'ou=groups,dc=testing,dc=local',
|
||||
f'(&(objectClass=groupOfNames)(member={user_dn})(cn={required_group}))',
|
||||
attributes=['cn']
|
||||
)
|
||||
|
||||
return len(conn.entries) > 0
|
||||
```
|
||||
|
||||
## Testing Your Integration
|
||||
|
||||
### 1. Start the LDAP Server
|
||||
|
||||
```bash
|
||||
make start
|
||||
```
|
||||
|
||||
### 2. Test Connection
|
||||
|
||||
```bash
|
||||
python examples/simple_auth.py --list-users
|
||||
```
|
||||
|
||||
### 3. Test Authentication
|
||||
|
||||
```bash
|
||||
python examples/simple_auth.py --username jdoe --password password123
|
||||
```
|
||||
|
||||
### 4. Test with Your Application
|
||||
|
||||
Point your application to:
|
||||
- LDAP URL: `ldap://localhost:389`
|
||||
- LDAPS URL: `ldaps://localhost:636` (with SSL)
|
||||
- Base DN: `dc=testing,dc=local`
|
||||
|
||||
## Available Test Accounts
|
||||
|
||||
| Username | Password | Groups | Purpose |
|
||||
|----------|----------|--------|---------|
|
||||
| admin | password123 | admins | Administrative testing |
|
||||
| jdoe | password123 | developers, users | Regular user testing |
|
||||
| jsmith | password123 | developers, users | Regular user testing |
|
||||
| testuser | password123 | users | Basic user testing |
|
||||
|
||||
## SSL/TLS Configuration
|
||||
|
||||
For production-like testing with LDAPS:
|
||||
|
||||
```python
|
||||
import ssl
|
||||
from ldap3 import Server, Connection, Tls
|
||||
|
||||
tls = Tls(
|
||||
ca_certs_file='certs/ca.crt',
|
||||
validate=ssl.CERT_REQUIRED
|
||||
)
|
||||
|
||||
server = Server('ldaps://localhost:636', use_ssl=True, tls=tls)
|
||||
conn = Connection(server, user=user_dn, password=password, auto_bind=True)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Refused
|
||||
|
||||
```bash
|
||||
# Check if LDAP server is running
|
||||
make status
|
||||
|
||||
# Start if not running
|
||||
make start
|
||||
```
|
||||
|
||||
### Authentication Fails
|
||||
|
||||
```bash
|
||||
# Verify user exists
|
||||
make test-users
|
||||
|
||||
# Check LDAP logs
|
||||
make logs
|
||||
```
|
||||
|
||||
### Python ImportError
|
||||
|
||||
```bash
|
||||
# Install ldap3 library
|
||||
uv pip install ldap3
|
||||
# or
|
||||
pip install ldap3
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [ldap3 Documentation](https://ldap3.readthedocs.io/)
|
||||
- [LDAP Protocol Overview](https://ldap.com/ldap-protocol/)
|
||||
- [Django LDAP Authentication](https://django-auth-ldap.readthedocs.io/)
|
||||
- [Flask-LDAP3-Login](https://flask-ldap3-login.readthedocs.io/)
|
||||
|
||||
## Contributing Examples
|
||||
|
||||
Have an example for a specific framework or use case? Contributions are welcome!
|
||||
|
||||
Examples we'd love to see:
|
||||
- Express.js / Node.js authentication
|
||||
- Ruby on Rails integration
|
||||
- Go LDAP client
|
||||
- Java Spring Security LDAP
|
||||
- PHP authentication
|
||||
- Docker Compose with application stack
|
||||
|
||||
Submit a pull request with your example!
|
||||
@@ -13,11 +13,10 @@ Usage:
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
try:
|
||||
from ldap3 import ALL, Connection, Server
|
||||
from ldap3.core.exceptions import LDAPException, LDAPBindError
|
||||
from ldap3.core.exceptions import LDAPBindError, LDAPException
|
||||
except ImportError:
|
||||
print("Error: ldap3 library not found.")
|
||||
print("Install it with: uv pip install ldap3")
|
||||
@@ -27,9 +26,9 @@ except ImportError:
|
||||
# Configuration
|
||||
LDAP_PORT = os.environ.get("LDAP_PORT", "389")
|
||||
LDAP_SERVER = f"ldap://localhost:{LDAP_PORT}"
|
||||
LDAP_BASE_DN = "dc=testing,dc=local"
|
||||
LDAP_PEOPLE_OU = "ou=people,dc=testing,dc=local"
|
||||
LDAP_GROUPS_OU = "ou=groups,dc=testing,dc=local"
|
||||
LDAP_BASE_DN = os.environ.get("LDAP_BASE_DN", "dc=testing,dc=local")
|
||||
LDAP_PEOPLE_OU = f"ou=people,{LDAP_BASE_DN}"
|
||||
LDAP_GROUPS_OU = f"ou=groups,{LDAP_BASE_DN}"
|
||||
|
||||
|
||||
class LDAPAuthenticator:
|
||||
@@ -66,8 +65,8 @@ class LDAPAuthenticator:
|
||||
print(f"❌ Authentication failed for user: {username}")
|
||||
return False
|
||||
|
||||
except LDAPBindError as e:
|
||||
print(f"❌ Authentication failed: Invalid credentials")
|
||||
except LDAPBindError:
|
||||
print("❌ Authentication failed: Invalid credentials")
|
||||
return False
|
||||
except LDAPException as e:
|
||||
print(f"❌ LDAP error: {e}")
|
||||
@@ -76,7 +75,7 @@ class LDAPAuthenticator:
|
||||
print(f"❌ Unexpected error: {e}")
|
||||
return False
|
||||
|
||||
def get_user_info(self, username: str, admin_dn: str, admin_password: str) -> Optional[Dict]:
|
||||
def get_user_info(self, username: str, admin_dn: str, admin_password: str) -> dict | None:
|
||||
"""
|
||||
Retrieve detailed information about a user.
|
||||
|
||||
@@ -121,7 +120,7 @@ class LDAPAuthenticator:
|
||||
print(f"Error retrieving user info: {e}")
|
||||
return None
|
||||
|
||||
def get_user_groups(self, username: str, admin_dn: str, admin_password: str) -> List[str]:
|
||||
def get_user_groups(self, username: str, admin_dn: str, admin_password: str) -> list[str]:
|
||||
"""
|
||||
Get all groups that a user belongs to.
|
||||
|
||||
@@ -154,7 +153,7 @@ class LDAPAuthenticator:
|
||||
print(f"Error retrieving user groups: {e}")
|
||||
return []
|
||||
|
||||
def list_all_users(self, admin_dn: str, admin_password: str) -> List[Dict]:
|
||||
def list_all_users(self, admin_dn: str, admin_password: str) -> list[dict]:
|
||||
"""
|
||||
List all users in the directory.
|
||||
|
||||
@@ -192,7 +191,7 @@ class LDAPAuthenticator:
|
||||
return []
|
||||
|
||||
|
||||
def print_user_info(user_info: Dict):
|
||||
def print_user_info(user_info: dict):
|
||||
"""Pretty print user information."""
|
||||
print("\n" + "=" * 50)
|
||||
print("USER INFORMATION")
|
||||
@@ -225,10 +224,10 @@ def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
# Admin credentials for retrieving user info
|
||||
admin_dn = "cn=admin,dc=testing,dc=local"
|
||||
admin_password = "admin_password"
|
||||
admin_dn = os.environ.get("LDAP_ADMIN_DN", f"cn=admin,{LDAP_BASE_DN}")
|
||||
admin_password = os.environ.get("LDAP_ADMIN_PASSWORD", "admin_password")
|
||||
|
||||
print(f"\n🔐 LDAP Authentication Example")
|
||||
print("\nLDAP Authentication Example")
|
||||
print(f"Server: {args.server}\n")
|
||||
|
||||
# Initialize authenticator
|
||||
|
||||
Reference in New Issue
Block a user