LDAP-Beispiele

LDAP (Lightweight Directory Access Protocol) Implementierungsbeispiele einschließlich Benutzerauthentifizierung, Verzeichnisoperationen und Active Directory Integration

💻 LDAP-Benutzerauthentifizierung python

🟡 intermediate ⭐⭐⭐⭐

Vollständiges LDAP-Authentifizierungssystem mit Benutzeranmeldung, Passwortüberprüfung und Sitzungsverwaltung

⏱️ 40 min 🏷️ ldap, authentication, security, directory
Prerequisites: LDAP concepts, Python programming, Directory services
#!/usr/bin/env python3
"""
LDAP User Authentication System
Complete implementation for authenticating users against LDAP/Active Directory
"""

import ldap
import ldap.modlist
import hashlib
import secrets
import time
from typing import Dict, Optional, List, Any
from dataclasses import dataclass
from datetime import datetime, timedelta
import ssl
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@dataclass
class LDAPConfig:
    """LDAP configuration"""
    server: str  # LDAP server hostname/IP
    port: int = 389  # Default LDAP port
    use_ssl: bool = False  # Use LDAPS
    use_tls: bool = True  # Start TLS
    bind_dn: str  # Bind DN for authentication
    bind_password: str  # Bind password
    base_dn: str  # Base DN for searches
    user_search_filter: str = '(uid={username})'  # User search filter
    user_attributes: List[str] = None  # Attributes to retrieve
    group_search_filter: str = '(member={user_dn})'  # Group search filter

    def __post_init__(self):
        if self.user_attributes is None:
            self.user_attributes = ['uid', 'cn', 'mail', 'memberOf', 'sn', 'givenName']

class LDAPAuthenticator:
    """LDAP Authentication Handler"""

    def __init__(self, config: LDAPConfig):
        self.config = config
        self.connection = None
        self._initialize_connection()

    def _initialize_connection(self):
        """Initialize LDAP connection"""
        try:
            # Create connection
            if self.config.use_ssl:
                # LDAPS connection
                ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)
                self.connection = ldap.initialize(f"ldaps://{self.config.server}:{self.config.port}")
            else:
                # Standard LDAP connection
                self.connection = ldap.initialize(f"ldap://{self.config.server}:{self.config.port}")

                # Configure TLS if needed
                if self.config.use_tls:
                    ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)
                    self.connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0)

            # Set connection options
            self.connection.set_option(ldap.OPT_REFERRALS, 0)
            self.connection.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
            self.connection.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)

            logger.info(f"LDAP connection initialized to {self.config.server}:{self.config.port}")

        except Exception as e:
            logger.error(f"Failed to initialize LDAP connection: {str(e)}")
            raise LDAPConnectionError(f"Connection failed: {str(e)}")

    def bind(self):
        """Bind to LDAP server with service account"""
        try:
            self.connection.simple_bind_s(self.config.bind_dn, self.config.bind_password)
            logger.info("Successfully bound to LDAP server")
        except ldap.INVALID_CREDENTIALS:
            raise LDAPAuthenticationError("Invalid bind credentials")
        except ldap.LDAPError as e:
            raise LDAPConnectionError(f"Bind failed: {str(e)}")

    def authenticate_user(self, username: str, password: str) -> Dict[str, Any]:
        """
        Authenticate user against LDAP

        Args:
            username: Username to authenticate
            password: User password

        Returns:
            Dictionary containing user information

        Raises:
            LDAPAuthenticationError: If authentication fails
        """
        try:
            # First, bind with service account to search for user
            self.bind()

            # Search for user
            user_dn = self._find_user_dn(username)
            if not user_dn:
                raise LDAPAuthenticationError("User not found")

            # Verify user password by binding as the user
            try:
                user_connection = ldap.initialize(f"ldap://{self.config.server}:{self.config.port}")
                user_connection.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
                user_connection.set_option(ldap.OPT_REFERRALS, 0)

                if self.config.use_tls:
                    user_connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)
                    user_connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
                    user_connection.start_tls_s()

                user_connection.simple_bind_s(user_dn, password)
                user_connection.unbind()

                logger.info(f"User {username} authenticated successfully")

            except ldap.INVALID_CREDENTIALS:
                raise LDAPAuthenticationError("Invalid password")
            except Exception as e:
                raise LDAPAuthenticationError(f"User bind failed: {str(e)}")

            # Get user attributes
            user_info = self._get_user_attributes(user_dn, username)

            # Get user groups
            user_groups = self._get_user_groups(user_dn)

            # Create session token
            session_token = self._create_session_token(username, user_dn)

            return {
                'username': username,
                'dn': user_dn,
                'attributes': user_info,
                'groups': user_groups,
                'session_token': session_token,
                'authenticated_at': datetime.utcnow().isoformat()
            }

        finally:
            if self.connection:
                try:
                    self.connection.unbind()
                except:
                    pass

    def _find_user_dn(self, username: str) -> Optional[str]:
        """Find user Distinguished Name"""
        try:
            search_filter = self.config.user_search_filter.format(username=username)

            result = self.connection.search_s(
                self.config.base_dn,
                ldap.SCOPE_SUBTREE,
                search_filter,
                ['dn']
            )

            if result and len(result) > 0:
                return result[0][0]  # Return the DN
            return None

        except Exception as e:
            logger.error(f"Error finding user DN: {str(e)}")
            return None

    def _get_user_attributes(self, user_dn: str, username: str) -> Dict[str, Any]:
        """Get user attributes from LDAP"""
        try:
            search_filter = self.config.user_search_filter.format(username=username)

            result = self.connection.search_s(
                self.config.base_dn,
                ldap.SCOPE_SUBTREE,
                search_filter,
                self.config.user_attributes
            )

            if result and len(result) > 0:
                attributes = result[0][1]  # Get attributes dictionary

                # Convert byte strings to regular strings
                user_attrs = {}
                for key, values in attributes.items():
                    if isinstance(values, list):
                        user_attrs[key] = [v.decode('utf-8') if isinstance(v, bytes) else v for v in values]
                    elif isinstance(values, bytes):
                        user_attrs[key] = values.decode('utf-8')
                    else:
                        user_attrs[key] = values

                return user_attrs

            return {}

        except Exception as e:
            logger.error(f"Error getting user attributes: {str(e)}")
            return {}

    def _get_user_groups(self, user_dn: str) -> List[str]:
        """Get user group memberships"""
        try:
            search_filter = self.config.group_search_filter.format(user_dn=user_dn)

            result = self.connection.search_s(
                self.config.base_dn,
                ldap.SCOPE_SUBTREE,
                search_filter,
                ['cn', 'dn']
            )

            groups = []
            for group_dn, group_attrs in result:
                if 'cn' in group_attrs:
                    cn_values = group_attrs['cn']
                    if isinstance(cn_values, list):
                        groups.extend([cn.decode('utf-8') if isinstance(cn, bytes) else cn for cn in cn_values])
                    else:
                        groups.append(cn_values.decode('utf-8') if isinstance(cn_values, bytes) else cn_values)

            return groups

        except Exception as e:
            logger.error(f"Error getting user groups: {str(e)}")
            return []

    def _create_session_token(self, username: str, user_dn: str) -> str:
        """Create session token for authenticated user"""
        token_data = {
            'username': username,
            'user_dn': user_dn,
            'timestamp': int(time.time()),
            'expires': int(time.time()) + 3600,  # 1 hour
            'nonce': secrets.token_hex(16)
        }

        # Create hash-based token (in production, use JWT)
        token_string = f"{token_data['username']}:{token_data['timestamp']}:{token_data['expires']}:{token_data['nonce']}"
        token_hash = hashlib.sha256(token_string.encode()).hexdigest()

        return f"{token_string}:{token_hash}"

    def validate_session_token(self, token: str) -> Dict[str, Any]:
        """Validate session token"""
        try:
            parts = token.split(':')
            if len(parts) != 5:
                return {'valid': False, 'error': 'Invalid token format'}

            username, timestamp_str, expires_str, nonce, token_hash = parts

            # Verify hash
            token_string = f"{username}:{timestamp_str}:{expires_str}:{nonce}"
            calculated_hash = hashlib.sha256(token_string.encode()).hexdigest()

            if calculated_hash != token_hash:
                return {'valid': False, 'error': 'Invalid token hash'}

            # Check expiration
            expires = int(expires_str)
            if time.time() > expires:
                return {'valid': False, 'error': 'Token expired'}

            return {
                'valid': True,
                'username': username,
                'timestamp': int(timestamp_str),
                'expires': expires
            }

        except Exception as e:
            return {'valid': False, 'error': str(e)}

    def search_users(self, query: str, limit: int = 50) -> List[Dict[str, Any]]:
        """Search for users in LDAP"""
        try:
            self.bind()

            search_filter = f"(|(uid=*{query}*)(cn=*{query}*)(mail=*{query}*))"

            result = self.connection.search_s(
                self.config.base_dn,
                ldap.SCOPE_SUBTREE,
                search_filter,
                self.config.user_attributes,
                attrlist=self.config.user_attributes,
                sizelimit=limit
            )

            users = []
            for user_dn, user_attrs in result:
                user_info = {'dn': user_dn}

                # Convert attributes
                for key, values in user_attrs.items():
                    if isinstance(values, list):
                        user_info[key] = [v.decode('utf-8') if isinstance(v, bytes) else v for v in values]
                    elif isinstance(values, bytes):
                        user_info[key] = values.decode('utf-8')
                    else:
                        user_info[key] = values

                users.append(user_info)

            return users

        except Exception as e:
            logger.error(f"Error searching users: {str(e)}")
            return []

        finally:
            if self.connection:
                try:
                    self.connection.unbind()
                except:
                    pass

    def create_user(self, user_data: Dict[str, Any], password: str) -> bool:
        """Create a new user in LDAP"""
        try:
            self.bind()

            # Build user DN
            username = user_data.get('uid')
            if not username:
                raise ValueError("Username (uid) is required")

            user_dn = f"uid={username},{self.config.base_dn}"

            # Build user attributes
            attributes = {}
            for key, value in user_data.items():
                if key != 'uid':  # uid is already in DN
                    if isinstance(value, list):
                        attributes[key] = [v.encode('utf-8') if isinstance(v, str) else v for v in value]
                    else:
                        attributes[key] = value.encode('utf-8') if isinstance(value, str) else value

            # Required object classes
            attributes['objectClass'] = [
                'top',
                'person',
                'organizationalPerson',
                'inetOrgPerson'
            ]

            # Add user password
            import passlib.hash
            hashed_password = passlib.hash.ldap_salted_sha1.hash(password)
            attributes['userPassword'] = hashed_password.encode('utf-8')

            # Add the user
            ldif = ldap.modlist.addModlist(attributes)
            self.connection.add_s(user_dn, ldif)

            logger.info(f"User {username} created successfully")
            return True

        except Exception as e:
            logger.error(f"Error creating user: {str(e)}")
            return False

        finally:
            if self.connection:
                try:
                    self.connection.unbind()
                except:
                    pass

class LDAPConnectionError(Exception):
    """LDAP connection error"""
    pass

class LDAPAuthenticationError(Exception):
    """LDAP authentication error"""
    pass

# Example usage
if __name__ == "__main__":
    # Configuration
    ldap_config = LDAPConfig(
        server="ldap.example.com",
        port=389,
        use_ssl=False,
        use_tls=True,
        bind_dn="cn=admin,dc=example,dc=com",
        bind_password="admin_password",
        base_dn="dc=example,dc=com",
        user_search_filter="(uid={username})",
        user_attributes=["uid", "cn", "mail", "memberOf", "sn", "givenName", "telephoneNumber"],
        group_search_filter="(member={user_dn})"
    )

    # Create authenticator
    authenticator = LDAPAuthenticator(ldap_config)

    try:
        # Authenticate user
        user_info = authenticator.authenticate_user("johndoe", "user_password")
        print(f"User authenticated: {user_info['username']}")
        print(f"Email: {user_info['attributes'].get('mail', ['N/A'])[0]}")
        print(f"Groups: {user_info['groups']}")
        print(f"Session token: {user_info['session_token']}")

        # Validate session token
        token_validation = authenticator.validate_session_token(user_info['session_token'])
        print(f"Token valid: {token_validation['valid']}")

        # Search users
        users = authenticator.search_users("john", limit=5)
        print(f"Found {len(users)} users")

    except LDAPAuthenticationError as e:
        print(f"Authentication failed: {str(e)}")
    except LDAPConnectionError as e:
        print(f"Connection failed: {str(e)}")
    except Exception as e:
        print(f"Error: {str(e)}")

# Flask integration example
"""
from flask import Flask, request, jsonify, session
import functools

app = Flask(__name__)
app.secret_key = 'your-secret-key'

# Initialize LDAP authenticator
ldap_auth = LDAPAuthenticator(ldap_config)

def require_auth(f):
    @functools.wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token:
            return jsonify({'error': 'Missing authorization token'}), 401

        # Remove 'Bearer ' prefix if present
        if token.startswith('Bearer '):
            token = token[7:]

        validation = ldap_auth.validate_session_token(token)
        if not validation['valid']:
            return jsonify({'error': 'Invalid or expired token'}), 401

        return f(*args, **kwargs)
    return decorated

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')

    if not username or not password:
        return jsonify({'error': 'Username and password required'}), 400

    try:
        user_info = ldap_auth.authenticate_user(username, password)
        return jsonify({
            'message': 'Login successful',
            'user': {
                'username': user_info['username'],
                'email': user_info['attributes'].get('mail', [''])[0],
                'groups': user_info['groups']
            },
            'token': user_info['session_token']
        })
    except LDAPAuthenticationError as e:
        return jsonify({'error': str(e)}), 401

@app.route('/users/search')
@require_auth
def search_users():
    query = request.args.get('q', '')
    limit = int(request.args.get('limit', 10))

    users = ldap_auth.search_users(query, limit)
    return jsonify({'users': users})

@app.route('/profile')
@require_auth
def profile():
    token = request.headers.get('Authorization')[7:]  # Remove 'Bearer '
    validation = ldap_auth.validate_session_token(token)

    username = validation['username']
    user_info = ldap_auth._get_user_attributes(f"uid={username},{ldap_config.base_dn}", username)

    return jsonify({
        'username': username,
        'attributes': user_info
    })
"""

export { LDAPAuthenticator, LDAPConfig, LDAPConnectionError, LDAPAuthenticationError };

💻 Active Directory Integration csharp

🟡 intermediate ⭐⭐⭐⭐

Integration mit Microsoft Active Directory für Benutzerauthentifizierung und Gruppenverwaltung

⏱️ 50 min 🏷️ active directory, ldap, authentication, windows
Prerequisites: Active Directory concepts, C# programming, Windows authentication
using System;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace LDAP.ActiveDirectory
{
    /// <summary>
    /// Active Directory integration service for authentication and user management
    /// </summary>
    public class ActiveDirectoryService : IDisposable
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger<ActiveDirectoryService> _logger;
        private readonly string _domain;
        private readonly string _container;
        private readonly string _serviceAccountUsername;
        private readonly string _serviceAccountPassword;
        private readonly string _ldapPath;

        // Principal context for Active Directory operations
        private PrincipalContext _context;

        public ActiveDirectoryService(IConfiguration configuration, ILogger<ActiveDirectoryService> logger)
        {
            _configuration = configuration;
            _logger = logger;

            // Load configuration
            _domain = configuration["ActiveDirectory:Domain"] ?? "corp.example.com";
            _container = configuration["ActiveDirectory:Container"] ?? "DC=corp,DC=example,DC=com";
            _serviceAccountUsername = configuration["ActiveDirectory:ServiceAccountUsername"] ?? "svc_ldap";
            _serviceAccountPassword = configuration["ActiveDirectory:ServiceAccountPassword"] ?? "";

            // Build LDAP path
            _ldapPath = $"LDAP://{_domain}/{_container}";

            InitializeContext();
        }

        private void InitializeContext()
        {
            try
            {
                // Create principal context with service account
                _context = new PrincipalContext(
                    ContextType.Domain,
                    _domain,
                    _container,
                    ContextOptions.Negotiate | ContextOptions.SecureSocketLayer,
                    _serviceAccountUsername,
                    _serviceAccountPassword
                );

                _logger.LogInformation($"Successfully connected to Active Directory domain: {_domain}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to connect to Active Directory");
                throw new ActiveDirectoryException("Failed to connect to Active Directory", ex);
            }
        }

        /// <summary>
        /// Authenticate user against Active Directory
        /// </summary>
        public async Task<AuthenticationResult> AuthenticateUserAsync(string username, string password)
        {
            try
            {
                // Validate credentials
                bool isValid = _context.ValidateCredentials(username, password, ContextOptions.Negotiate);

                if (!isValid)
                {
                    return new AuthenticationResult
                    {
                        Success = false,
                        ErrorMessage = "Invalid username or password"
                    };
                }

                // Find user principal
                UserPrincipal user = UserPrincipal.FindByIdentity(_context, username);
                if (user == null)
                {
                    return new AuthenticationResult
                    {
                        Success = false,
                        ErrorMessage = "User not found"
                    };
                }

                // Get user information
                var userInfo = await GetUserInfoAsync(user);

                // Generate session token
                var sessionToken = GenerateSessionToken(username, user.Sid.Value);

                _logger.LogInformation($"User {username} authenticated successfully");

                return new AuthenticationResult
                {
                    Success = true,
                    User = userInfo,
                    SessionToken = sessionToken,
                    ExpiresAt = DateTime.UtcNow.AddHours(8) // 8-hour session
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Authentication failed for user: {username}");
                return new AuthenticationResult
                {
                    Success = false,
                    ErrorMessage = "Authentication failed"
                };
            }
        }

        /// <summary>
        /// Get detailed user information from Active Directory
        /// </summary>
        public async Task<UserInfo> GetUserInfoAsync(string username)
        {
            try
            {
                UserPrincipal user = UserPrincipal.FindByIdentity(_context, username);
                if (user == null)
                {
                    throw new UserNotFoundException($"User {username} not found");
                }

                return await GetUserInfoAsync(user);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to get user info for: {username}");
                throw;
            }
        }

        private async Task<UserInfo> GetUserInfoAsync(UserPrincipal user)
        {
            var userInfo = new UserInfo
            {
                Username = user.SamAccountName,
                DisplayName = user.DisplayName,
                EmailAddress = user.EmailAddress,
                GivenName = user.GivenName,
                Surname = user.Surname,
                EmployeeId = user.EmployeeId,
                Enabled = user.Enabled ?? true,
                LastLogon = user.LastLogon,
                PasswordLastSet = user.LastPasswordSet,
                AccountExpirationDate = user.AccountExpirationDate,
                UserPrincipalName = user.UserPrincipalName
            };

            // Get directory entry for additional attributes
            DirectoryEntry directoryEntry = user.GetUnderlyingObject() as DirectoryEntry;
            if (directoryEntry != null)
            {
                // Extract additional properties
                userInfo.Title = GetPropertyValue(directoryEntry, "title");
                userInfo.Department = GetPropertyValue(directoryEntry, "department");
                userInfo.Company = GetPropertyValue(directoryEntry, "company");
                userInfo.Office = GetPropertyValue(directoryEntry, "physicalDeliveryOfficeName");
                userInfo.TelephoneNumber = GetPropertyValue(directoryEntry, "telephoneNumber");
                userInfo.MobilePhone = GetPropertyValue(directoryEntry, "mobile");
                userInfo.Manager = GetPropertyValue(directoryEntry, "manager");

                // Get group memberships
                userInfo.Groups = await GetUserGroupsAsync(user);

                // Get distinguished name
                userInfo.DistinguishedName = directoryEntry.Properties["distinguishedName"]?.Value?.ToString();
            }

            return userInfo;
        }

        private string GetPropertyValue(DirectoryEntry entry, string propertyName)
        {
            if (entry.Properties[propertyName]?.Value != null)
            {
                return entry.Properties[propertyName].Value.ToString();
            }
            return null;
        }

        /// <summary>
        /// Get user group memberships
        /// </summary>
        public async Task<List<string>> GetUserGroupsAsync(string username)
        {
            try
            {
                UserPrincipal user = UserPrincipal.FindByIdentity(_context, username);
                if (user == null)
                {
                    return new List<string>();
                }

                return await GetUserGroupsAsync(user);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to get groups for user: {username}");
                return new List<string>();
            }
        }

        private async Task<List<string>> GetUserGroupsAsync(UserPrincipal user)
        {
            var groups = new List<string>();

            try
            {
                // Get user's authorization groups
                var userGroups = user.GetAuthorizationGroups();

                foreach (var group in userGroups)
                {
                    if (group is GroupPrincipal groupPrincipal)
                    {
                        groups.Add(groupPrincipal.SamAccountName);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Some groups could not be retrieved due to permission issues");

                // Fallback to direct groups only
                var principalSearchResult = user.GetGroups();
                foreach (var group in principalSearchResult)
                {
                    if (group is GroupPrincipal groupPrincipal)
                    {
                        groups.Add(groupPrincipal.SamAccountName);
                    }
                }
            }

            return groups;
        }

        /// <summary>
        /// Search for users in Active Directory
        /// </summary>
        public async Task<List<UserInfo>> SearchUsersAsync(string searchTerm, int limit = 50)
        {
            try
            {
                var users = new List<UserInfo>();

                using (var searcher = new PrincipalSearcher(new UserPrincipal(_context)))
                {
                    // Set search criteria
                    searcher.QueryFilter = new UserPrincipal(_context)
                    {
                        // Search by display name, sam account name, or email
                        DisplayName = $"*{searchTerm}*",
                        EmailAddress = $"*{searchTerm}*"
                    };

                    searcher.QueryFilter = new UserPrincipal(_context)
                    {
                        DisplayName = $"*{searchTerm}*"
                    };

                    // Custom search filter for multiple attributes
                    using (var directorySearcher = new DirectorySearcher(new DirectoryEntry(_ldapPath)))
                    {
                        directorySearcher.Filter = $"(&(objectCategory=person)(objectClass=user)(|(displayName=*{searchTerm}*)(sAMAccountName=*{searchTerm}*)(mail=*{searchTerm}*)))";
                        directorySearcher.PropertiesToLoad.AddRange(new[]
                        {
                            "sAMAccountName", "displayName", "mail", "givenName", "sn",
                            "title", "department", "company", "telephoneNumber", "distinguishedName"
                        });
                        directorySearcher.SizeLimit = limit;

                        using (var searchResults = directorySearcher.FindAll())
                        {
                            foreach (SearchResult result in searchResults)
                            {
                                var userInfo = new UserInfo
                                {
                                    Username = GetPropertyFromResult(result, "sAMAccountName"),
                                    DisplayName = GetPropertyFromResult(result, "displayName"),
                                    EmailAddress = GetPropertyFromResult(result, "mail"),
                                    GivenName = GetPropertyFromResult(result, "givenName"),
                                    Surname = GetPropertyFromResult(result, "sn"),
                                    Title = GetPropertyFromResult(result, "title"),
                                    Department = GetPropertyFromResult(result, "department"),
                                    Company = GetPropertyFromResult(result, "company"),
                                    TelephoneNumber = GetPropertyFromResult(result, "telephoneNumber"),
                                    DistinguishedName = GetPropertyFromResult(result, "distinguishedName")
                                };

                                users.Add(userInfo);
                            }
                        }
                    }
                }

                return users;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to search users with term: {searchTerm}");
                return new List<UserInfo>();
            }
        }

        private string GetPropertyFromResult(SearchResult result, string propertyName)
        {
            if (result.Properties.Contains(propertyName) && result.Properties[propertyName].Count > 0)
            {
                return result.Properties[propertyName][0].ToString();
            }
            return null;
        }

        /// <summary>
        /// Check if user is member of specific group
        /// </summary>
        public async Task<bool> IsUserInGroupAsync(string username, string groupName)
        {
            try
            {
                UserPrincipal user = UserPrincipal.FindByIdentity(_context, username);
                if (user == null)
                {
                    return false;
                }

                GroupPrincipal group = GroupPrincipal.FindByIdentity(_context, groupName);
                if (group == null)
                {
                    return false;
                }

                return user.IsMemberOf(group);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to check group membership for {username} in {groupName}");
                return false;
            }
        }

        /// <summary>
        /// Generate secure session token
        /// </summary>
        private string GenerateSessionToken(string username, byte[] userSid)
        {
            var tokenData = $"{username}:{Convert.ToBase64String(userSid)}:{DateTime.UtcNow:O}";
            var tokenBytes = Encoding.UTF8.GetBytes(tokenData);

            using (var hmac = new HMACSHA256())
            {
                var hash = hmac.ComputeHash(tokenBytes);
                var token = Convert.ToBase64String(tokenBytes) + "." + Convert.ToBase64String(hash);
                return token;
            }
        }

        /// <summary>
        /// Validate session token
        /// </summary>
        public bool ValidateSessionToken(string token, out string username)
        {
            username = null;

            try
            {
                var parts = token.Split('.');
                if (parts.Length != 2)
                {
                    return false;
                }

                var tokenData = Convert.FromBase64String(parts[0]);
                var receivedHash = Convert.FromBase64String(parts[1]);

                using (var hmac = new HMACSHA256())
                {
                    var computedHash = hmac.ComputeHash(tokenData);

                    if (!cryptographicEqual(receivedHash, computedHash))
                    {
                        return false;
                    }
                }

                var dataString = Encoding.UTF8.GetString(tokenData);
                var dataParts = dataString.Split(':');
                if (dataParts.Length != 3)
                {
                    return false;
                }

                username = dataParts[0];
                var timestamp = DateTime.Parse(dataParts[2]);

                // Check if token is not older than 8 hours
                if (DateTime.UtcNow - timestamp > TimeSpan.FromHours(8))
                {
                    return false;
                }

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to validate session token");
                return false;
            }
        }

        private bool cryptographicEqual(byte[] a, byte[] b)
        {
            if (a.Length != b.Length)
                return false;

            int result = 0;
            for (int i = 0; i < a.Length; i++)
            {
                result |= a[i] ^ b[i];
            }

            return result == 0;
        }

        /// <summary>
        /// Get password policy information
        /// </summary>
        public async Task<PasswordPolicy> GetPasswordPolicyAsync()
        {
            try
            {
                using (var directoryEntry = new DirectoryEntry(_ldapPath))
                using (var directorySearcher = new DirectorySearcher(directoryEntry))
                {
                    directorySearcher.Filter = "(objectClass=domainDNS)";
                    directorySearcher.PropertiesToLoad.AddRange(new[]
                    {
                        "minPwdLength", "maxPwdAge", "minPwdAge", "pwdHistoryLength", "pwdProperties"
                    });

                    var result = directorySearcher.FindOne();
                    if (result != null)
                    {
                        var policy = new PasswordPolicy();

                        if (result.Properties.Contains("minPwdLength"))
                            policy.MinLength = Convert.ToInt32(result.Properties["minPwdLength"][0]);

                        if (result.Properties.Contains("maxPwdAge"))
                        {
                            var maxAge = Convert.ToInt64(result.Properties["maxPwdAge"][0]);
                            policy.MaxAge = maxAge > 0 ? TimeSpan.FromTicks(-maxAge) : TimeSpan.Zero;
                        }

                        if (result.Properties.Contains("minPwdAge"))
                        {
                            var minAge = Convert.ToInt64(result.Properties["minPwdAge"][0]);
                            policy.MinAge = minAge > 0 ? TimeSpan.FromTicks(-minAge) : TimeSpan.Zero;
                        }

                        if (result.Properties.Contains("pwdHistoryLength"))
                            policy.HistoryLength = Convert.ToInt32(result.Properties["pwdHistoryLength"][0]);

                        return policy;
                    }
                }

                return new PasswordPolicy(); // Default policy
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to get password policy");
                return new PasswordPolicy();
            }
        }

        public void Dispose()
        {
            _context?.Dispose();
        }
    }

    // Data models
    public class AuthenticationResult
    {
        public bool Success { get; set; }
        public string ErrorMessage { get; set; }
        public UserInfo User { get; set; }
        public string SessionToken { get; set; }
        public DateTime ExpiresAt { get; set; }
    }

    public class UserInfo
    {
        public string Username { get; set; }
        public string DisplayName { get; set; }
        public string EmailAddress { get; set; }
        public string GivenName { get; set; }
        public string Surname { get; set; }
        public string Title { get; set; }
        public string Department { get; set; }
        public string Company { get; set; }
        public string Office { get; set; }
        public string TelephoneNumber { get; set; }
        public string MobilePhone { get; set; }
        public string Manager { get; set; }
        public string EmployeeId { get; set; }
        public string DistinguishedName { get; set; }
        public string UserPrincipalName { get; set; }
        public bool Enabled { get; set; }
        public DateTime? LastLogon { get; set; }
        public DateTime? PasswordLastSet { get; set; }
        public DateTime? AccountExpirationDate { get; set; }
        public List<string> Groups { get; set; } = new List<string>();
    }

    public class PasswordPolicy
    {
        public int MinLength { get; set; } = 8;
        public TimeSpan MaxAge { get; set; } = TimeSpan.FromDays(42);
        public TimeSpan MinAge { get; set; } = TimeSpan.Zero;
        public int HistoryLength { get; set; } = 12;
    }

    public class ActiveDirectoryException : Exception
    {
        public ActiveDirectoryException(string message) : base(message) { }
        public ActiveDirectoryException(string message, Exception innerException) : base(message, innerException) { }
    }

    public class UserNotFoundException : Exception
    {
        public UserNotFoundException(string message) : base(message) { }
    }

    // Program.cs example usage
    /*
    using Microsoft.AspNetCore.Builder;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;

    var builder = WebApplication.CreateBuilder(args);

    // Add services
    builder.Services.AddControllers();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();

    // Add Active Directory service
    builder.Services.AddSingleton<ActiveDirectoryService>();

    var app = builder.Build();

    // Configure HTTP request pipeline
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }

    app.UseHttpsRedirection();
    app.UseAuthorization();
    app.MapControllers();

    app.Run();

    // Controllers/AuthController.cs
    [ApiController]
    [Route("api/[controller]")]
    public class AuthController : ControllerBase
    {
        private readonly ActiveDirectoryService _adService;

        public AuthController(ActiveDirectoryService adService)
        {
            _adService = adService;
        }

        [HttpPost("login")]
        public async Task<IActionResult> Login([FromBody] LoginRequest request)
        {
            try
            {
                var result = await _adService.AuthenticateUserAsync(request.Username, request.Password);

                if (result.Success)
                {
                    return Ok(new
                    {
                        success = true,
                        user = result.User,
                        token = result.SessionToken,
                        expiresAt = result.ExpiresAt
                    });
                }
                else
                {
                    return BadRequest(new { success = false, error = result.ErrorMessage });
                }
            }
            catch (Exception ex)
            {
                return StatusCode(500, new { success = false, error = "Authentication service unavailable" });
            }
        }

        [HttpPost("validate")]
        public async Task<IActionResult> ValidateToken([FromBody] TokenRequest request)
        {
            try
            {
                if (_adService.ValidateSessionToken(request.Token, out string username))
                {
                    var userInfo = await _adService.GetUserInfoAsync(username);
                    return Ok(new { success = true, user = userInfo });
                }
                else
                {
                    return Unauthorized(new { success = false, error = "Invalid or expired token" });
                }
            }
            catch (Exception ex)
            {
                return StatusCode(500, new { success = false, error = "Validation failed" });
            }
        }

        [HttpGet("users/search")]
        public async Task<IActionResult> SearchUsers([FromQuery] string q, [FromQuery] int limit = 10)
        {
            try
            {
                var users = await _adService.SearchUsersAsync(q, limit);
                return Ok(new { success = true, users = users });
            }
            catch (Exception ex)
            {
                return StatusCode(500, new { success = false, error = "Search failed" });
            }
        }
    }

    public class LoginRequest
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }

    public class TokenRequest
    {
        public string Token { get; set; }
    }
    */
}

💻 LDAP-Benutzerverwaltungsoperationen java

🔴 complex ⭐⭐⭐⭐⭐

CRUD-Operationen für die Benutzerverwaltung in LDAP-Verzeichnisdiensten einschließlich Erstellen, Aktualisieren, Löschen und Passwortverwaltung

⏱️ 60 min 🏷️ ldap, java, user management, directory
Prerequisites: LDAP concepts, Java programming, JNDI, Directory operations
package com.example.ldap;

import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
import java.util.*;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.Pattern;

/**
 * LDAP User Management Service
 * Provides comprehensive CRUD operations for managing users in LDAP directory
 */
public class LDAPUserManager {

    private static final String[] USER_ATTRIBUTES = {
        "uid", "cn", "sn", "givenName", "mail", "telephoneNumber",
        "departmentNumber", "employeeType", "description", "memberOf"
    };

    private final LdapContext context;
    private final String baseDN;
    private final String userContainer;
    private final SecureRandom random = new SecureRandom();

    public LDAPUserManager(LDAPConfig config) throws NamingException {
        this.baseDN = config.getBaseDN();
        this.userContainer = config.getUserContainer();

        // Create initial directory context
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, config.getLdapUrl());
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.SECURITY_PRINCIPAL, config.getBindDN());
        env.put(Context.SECURITY_CREDENTIALS, config.getBindPassword());

        // Enable connection pooling
        env.put("com.sun.jndi.ldap.connect.pool", "true");
        env.put("com.sun.jndi.ldap.connect.pool.protocol", "ssl");
        env.put("com.sun.jndi.ldap.connect.pool.maxsize", "10");

        // Set timeout values
        env.put("com.sun.jndi.ldap.connect.timeout", "5000");
        env.put("com.sun.jndi.ldap.read.timeout", "10000");

        // Enable TLS if configured
        if (config.isUseTLS()) {
            env.put(Context.SECURITY_PROTOCOL, "ssl");
            env.put("java.naming.ldap.factory.socket",
                   "com.example.ldap.CustomSSLSocketFactory");
        }

        this.context = new InitialLdapContext(env, null);
        System.out.println("Connected to LDAP server: " + config.getLdapUrl());
    }

    /**
     * Create a new user in LDAP
     */
    public User createUser(CreateUserRequest request) throws LDAPException {
        try {
            // Validate input
            validateUserRequest(request);

            // Check if user already exists
            if (userExists(request.getUid())) {
                throw new LDAPException("User with UID '" + request.getUid() + "' already exists");
            }

            // Build user DN
            String userDN = String.format("uid=%s,%s", request.getUid(), userContainer);

            // Create user attributes
            Attributes attributes = new BasicAttributes(true);
            Attribute oc = new BasicAttribute("objectClass");
            oc.add("top");
            oc.add("person");
            oc.add("organizationalPerson");
            oc.add("inetOrgPerson");
            attributes.put(oc);

            // Set required attributes
            attributes.put(new BasicAttribute("uid", request.getUid()));
            attributes.put(new BasicAttribute("cn", request.getCn()));
            attributes.put(new BasicAttribute("sn", request.getSn()));

            // Set optional attributes
            setOptionalAttribute(attributes, "givenName", request.getGivenName());
            setOptionalAttribute(attributes, "mail", request.getMail());
            setOptionalAttribute(attributes, "telephoneNumber", request.getTelephoneNumber());
            setOptionalAttribute(attributes, "departmentNumber", request.getDepartmentNumber());
            setOptionalAttribute(attributes, "employeeType", request.getEmployeeType());
            setOptionalAttribute(attributes, "description", request.getDescription());

            // Set password
            String hashedPassword = hashPassword(request.getPassword());
            attributes.put(new BasicAttribute("userPassword", hashedPassword));

            // Create the user entry
            context.createSubcontext(userDN, attributes);

            System.out.println("Created user: " + userDN);

            // Return created user information
            return getUser(request.getUid());

        } catch (NamingException e) {
            throw new LDAPException("Failed to create user: " + e.getMessage(), e);
        }
    }

    /**
     * Get user information
     */
    public User getUser(String uid) throws LDAPException {
        try {
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            controls.setReturningAttributes(USER_ATTRIBUTES);
            controls.setCountLimit(1);

            String filter = String.format("(uid=%s)", escapeLDAPSearchFilter(uid));

            NamingEnumeration<SearchResult> results = context.search(
                baseDN, filter, controls);

            if (results.hasMore()) {
                SearchResult result = results.next();
                return mapSearchResultToUser(result);
            } else {
                throw new UserNotFoundException("User not found: " + uid);
            }

        } catch (NamingException e) {
            throw new LDAPException("Failed to get user: " + e.getMessage(), e);
        }
    }

    /**
     * Update user information
     */
    public User updateUser(String uid, UpdateUserRequest request) throws LDAPException {
        try {
            // Check if user exists
            if (!userExists(uid)) {
                throw new UserNotFoundException("User not found: " + uid);
            }

            String userDN = String.format("uid=%s,%s", uid, userContainer);

            // Build modification list
            List<ModificationItem> modifications = new ArrayList<>();

            // Update attributes
            updateAttribute(modifications, "cn", request.getCn());
            updateAttribute(modifications, "sn", request.getSn());
            updateAttribute(modifications, "givenName", request.getGivenName());
            updateAttribute(modifications, "mail", request.getMail());
            updateAttribute(modifications, "telephoneNumber", request.getTelephoneNumber());
            updateAttribute(modifications, "departmentNumber", request.getDepartmentNumber());
            updateAttribute(modifications, "employeeType", request.getEmployeeType());
            updateAttribute(modifications, "description", request.getDescription());

            // Apply modifications if any
            if (!modifications.isEmpty()) {
                ModificationItem[] mods = modifications.toArray(new ModificationItem[0]);
                context.modifyAttributes(userDN, mods);
            }

            System.out.println("Updated user: " + uid);

            // Return updated user information
            return getUser(uid);

        } catch (NamingException e) {
            throw new LDAPException("Failed to update user: " + e.getMessage(), e);
        }
    }

    /**
     * Delete user from LDAP
     */
    public void deleteUser(String uid) throws LDAPException {
        try {
            if (!userExists(uid)) {
                throw new UserNotFoundException("User not found: " + uid);
            }

            String userDN = String.format("uid=%s,%s", uid, userContainer);

            context.destroySubcontext(userDN);
            System.out.println("Deleted user: " + uid);

        } catch (NamingException e) {
            throw new LDAPException("Failed to delete user: " + e.getMessage(), e);
        }
    }

    /**
     * Change user password
     */
    public void changePassword(String uid, String newPassword) throws LDAPException {
        try {
            if (!userExists(uid)) {
                throw new UserNotFoundException("User not found: " + uid);
            }

            // Validate password strength
            validatePassword(newPassword);

            String userDN = String.format("uid=%s,%s", uid, userContainer);

            // Create password modification
            ModificationItem[] mods = new ModificationItem[1];
            Attribute passwordAttr = new BasicAttribute("userPassword", hashPassword(newPassword));
            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, passwordAttr);

            context.modifyAttributes(userDN, mods);
            System.out.println("Password changed for user: " + uid);

        } catch (NamingException e) {
            throw new LDAPException("Failed to change password: " + e.getMessage(), e);
        }
    }

    /**
     * Enable or disable user account
     */
    public void setUserStatus(String uid, boolean enabled) throws LDAPException {
        try {
            if (!userExists(uid)) {
                throw new UserNotFoundException("User not found: " + uid);
            }

            String userDN = String.format("uid=%s,%s", uid, userContainer);

            // Some LDAP servers use different attributes for account status
            // This implementation assumes the use of pwdAccountLockedTime
            String lockValue = enabled ? "000001010000Z" : null;

            ModificationItem[] mods = new ModificationItem[1];
            Attribute lockAttr = new BasicAttribute("pwdAccountLockedTime", lockValue);

            if (enabled) {
                mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, lockAttr);
            } else {
                mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, lockAttr);
            }

            context.modifyAttributes(userDN, mods);
            System.out.println("User status changed: " + uid + " -> " + (enabled ? "enabled" : "disabled"));

        } catch (NamingException e) {
            throw new LDAPException("Failed to change user status: " + e.getMessage(), e);
        }
    }

    /**
     * Search for users
     */
    public List<User> searchUsers(SearchRequest request) throws LDAPException {
        try {
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            controls.setReturningAttributes(USER_ATTRIBUTES);
            controls.setCountLimit(request.getLimit() > 0 ? request.getLimit() : 100);

            // Build search filter
            StringBuilder filter = new StringBuilder("(&(objectClass=inetOrgPerson)");

            if (request.getUid() != null) {
                filter.append(String.format("(uid=*%s*)", escapeLDAPSearchFilter(request.getUid())));
            }
            if (request.getCn() != null) {
                filter.append(String.format("(cn=*%s*)", escapeLDAPSearchFilter(request.getCn())));
            }
            if (request.getMail() != null) {
                filter.append(String.format("(mail=*%s*)", escapeLDAPSearchFilter(request.getMail())));
            }
            if (request.getDepartmentNumber() != null) {
                filter.append(String.format("(departmentNumber=*%s*)", escapeLDAPSearchFilter(request.getDepartmentNumber())));
            }

            filter.append(")");

            NamingEnumeration<SearchResult> results = context.search(
                baseDN, filter.toString(), controls);

            List<User> users = new ArrayList<>();
            while (results.hasMore()) {
                SearchResult result = results.next();
                users.add(mapSearchResultToUser(result));
            }

            return users;

        } catch (NamingException e) {
            throw new LDAPException("Failed to search users: " + e.getMessage(), e);
        }
    }

    /**
     * Get user groups
     */
    public List<String> getUserGroups(String uid) throws LDAPException {
        try {
            String userDN = String.format("uid=%s,%s", uid, userContainer);

            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            controls.setReturningAttributes(new String[]{"cn"});

            String filter = String.format("(&(objectClass=groupOfNames)(member=%s))", escapeLDAPSearchFilter(userDN));

            NamingEnumeration<SearchResult> results = context.search(baseDN, filter, controls);

            List<String> groups = new ArrayList<>();
            while (results.hasMore()) {
                SearchResult result = results.next();
                Attribute cnAttr = result.getAttributes().get("cn");
                if (cnAttr != null) {
                    groups.add(cnAttr.get().toString());
                }
            }

            return groups;

        } catch (NamingException e) {
            throw new LDAPException("Failed to get user groups: " + e.getMessage(), e);
        }
    }

    /**
     * Add user to group
     */
    public void addUserToGroup(String uid, String groupCn) throws LDAPException {
        try {
            if (!userExists(uid)) {
                throw new UserNotFoundException("User not found: " + uid);
            }

            String userDN = String.format("uid=%s,%s", uid, userContainer);
            String groupDN = String.format("cn=%s,ou=groups,%s", groupCn, baseDN);

            // Add user to group
            ModificationItem[] mods = new ModificationItem[1];
            Attribute memberAttr = new BasicAttribute("member", userDN);
            mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, memberAttr);

            context.modifyAttributes(groupDN, mods);
            System.out.println("Added user " + uid + " to group " + groupCn);

        } catch (NamingException e) {
            throw new LDAPException("Failed to add user to group: " + e.getMessage(), e);
        }
    }

    /**
     * Remove user from group
     */
    public void removeUserFromGroup(String uid, String groupCn) throws LDAPException {
        try {
            String userDN = String.format("uid=%s,%s", uid, userContainer);
            String groupDN = String.format("cn=%s,ou=groups,%s", groupCn, baseDN);

            // Remove user from group
            ModificationItem[] mods = new ModificationItem[1];
            Attribute memberAttr = new BasicAttribute("member", userDN);
            mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, memberAttr);

            context.modifyAttributes(groupDN, mods);
            System.out.println("Removed user " + uid + " from group " + groupCn);

        } catch (NamingException e) {
            throw new LDAPException("Failed to remove user from group: " + e.getMessage(), e);
        }
    }

    // Helper methods

    private boolean userExists(String uid) throws LDAPException {
        try {
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            controls.setCountLimit(1);

            String filter = String.format("(uid=%s)", escapeLDAPSearchFilter(uid));

            NamingEnumeration<SearchResult> results = context.search(baseDN, filter, controls);
            return results.hasMore();

        } catch (NamingException e) {
            throw new LDAPException("Failed to check user existence: " + e.getMessage(), e);
        }
    }

    private void validateUserRequest(CreateUserRequest request) throws LDAPException {
        if (request.getUid() == null || request.getUid().trim().isEmpty()) {
            throw new LDAPException("UID is required");
        }
        if (request.getCn() == null || request.getCn().trim().isEmpty()) {
            throw new LDAPException("Common name (CN) is required");
        }
        if (request.getSn() == null || request.getSn().trim().isEmpty()) {
            throw new LDAPException("Surname (SN) is required");
        }
        if (request.getPassword() == null || request.getPassword().trim().isEmpty()) {
            throw new LDAPException("Password is required");
        }

        validatePassword(request.getPassword());
    }

    private void validatePassword(String password) throws LDAPException {
        if (password == null || password.length() < 8) {
            throw new LDAPException("Password must be at least 8 characters long");
        }

        // Check for password complexity
        if (!Pattern.compile("[A-Z]").matcher(password).find()) {
            throw new LDAPException("Password must contain at least one uppercase letter");
        }
        if (!Pattern.compile("[a-z]").matcher(password).find()) {
            throw new LDAPException("Password must contain at least one lowercase letter");
        }
        if (!Pattern.compile("[0-9]").matcher(password).find()) {
            throw new LDAPException("Password must contain at least one digit");
        }
    }

    private String hashPassword(String password) {
        // This is a simplified implementation
        // In production, use a proper password hashing library
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
            byte[] hash = md.digest(password.getBytes());
            return Base64.getEncoder().encodeToString(hash);
        } catch (Exception e) {
            throw new RuntimeException("Failed to hash password", e);
        }
    }

    private void setOptionalAttribute(Attributes attributes, String name, String value) {
        if (value != null && !value.trim().isEmpty()) {
            attributes.put(new BasicAttribute(name, value));
        }
    }

    private void updateAttribute(List<ModificationItem> modifications, String name, String value) {
        if (value != null) {
            Attribute attr = new BasicAttribute(name, value);
            modifications.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr));
        }
    }

    private User mapSearchResultToUser(SearchResult result) throws NamingException {
        Attributes attrs = result.getAttributes();
        User user = new User();

        user.setDn(result.getNameInNamespace());
        user.setUid(getAttributeValue(attrs, "uid"));
        user.setCn(getAttributeValue(attrs, "cn"));
        user.setSn(getAttributeValue(attrs, "sn"));
        user.setGivenName(getAttributeValue(attrs, "givenName"));
        user.setMail(getAttributeValue(attrs, "mail"));
        user.setTelephoneNumber(getAttributeValue(attrs, "telephoneNumber"));
        user.setDepartmentNumber(getAttributeValue(attrs, "departmentNumber"));
        user.setEmployeeType(getAttributeValue(attrs, "employeeType"));
        user.setDescription(getAttributeValue(attrs, "description"));

        return user;
    }

    private String getAttributeValue(Attributes attrs, String name) throws NamingException {
        Attribute attr = attrs.get(name);
        if (attr != null) {
            Object value = attr.get();
            return value != null ? value.toString() : null;
        }
        return null;
    }

    private String escapeLDAPSearchFilter(String filter) {
        if (filter == null) return "";
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < filter.length(); i++) {
            char c = filter.charAt(i);
            switch (c) {
                case '\\':
                    sb.append("\\5c");
                    break;
                case '*':
                    sb.append("\\2a");
                    break;
                case '(':
                    sb.append("\\28");
                    break;
                case ')':
                    sb.append("\\29");
                    break;
                case '\u0000':
                    sb.append("\\00");
                    break;
                default:
                    sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * Close LDAP context
     */
    public void close() {
        try {
            if (context != null) {
                context.close();
            }
        } catch (NamingException e) {
            System.err.println("Error closing LDAP context: " + e.getMessage());
        }
    }
}

// Data classes
class LDAPConfig {
    private String ldapUrl;
    private String baseDN;
    private String userContainer;
    private String bindDN;
    private String bindPassword;
    private boolean useTLS;

    // Getters and setters
    public String getLdapUrl() { return ldapUrl; }
    public String getBaseDN() { return baseDN; }
    public String getUserContainer() { return userContainer; }
    public String getBindDN() { return bindDN; }
    public String getBindPassword() { return bindPassword; }
    public boolean isUseTLS() { return useTLS; }

    // Builder pattern
    public static class Builder {
        private LDAPConfig config = new LDAPConfig();

        public Builder ldapUrl(String ldapUrl) { config.ldapUrl = ldapUrl; return this; }
        public Builder baseDN(String baseDN) { config.baseDN = baseDN; return this; }
        public Builder userContainer(String userContainer) { config.userContainer = userContainer; return this; }
        public Builder bindDN(String bindDN) { config.bindDN = bindDN; return this; }
        public Builder bindPassword(String bindPassword) { config.bindPassword = bindPassword; return this; }
        public Builder useTLS(boolean useTLS) { config.useTLS = useTLS; return this; }

        public LDAPConfig build() { return config; }
    }
}

class User {
    private String dn;
    private String uid;
    private String cn;
    private String sn;
    private String givenName;
    private String mail;
    private String telephoneNumber;
    private String departmentNumber;
    private String employeeType;
    private String description;

    // Getters and setters
    public String getDn() { return dn; }
    public void setDn(String dn) { this.dn = dn; }
    public String getUid() { return uid; }
    public void setUid(String uid) { this.uid = uid; }
    public String getCn() { return cn; }
    public void setCn(String cn) { this.cn = cn; }
    public String getSn() { return sn; }
    public void setSn(String sn) { this.sn = sn; }
    public String getGivenName() { return givenName; }
    public void setGivenName(String givenName) { this.givenName = givenName; }
    public String getMail() { return mail; }
    public void setMail(String mail) { this.mail = mail; }
    public String getTelephoneNumber() { return telephoneNumber; }
    public void setTelephoneNumber(String telephoneNumber) { this.telephoneNumber = telephoneNumber; }
    public String getDepartmentNumber() { return departmentNumber; }
    public void setDepartmentNumber(String departmentNumber) { this.departmentNumber = departmentNumber; }
    public String getEmployeeType() { return employeeType; }
    public void setEmployeeType(String employeeType) { this.employeeType = employeeType; }
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
}

class CreateUserRequest {
    private String uid;
    private String cn;
    private String sn;
    private String givenName;
    private String mail;
    private String telephoneNumber;
    private String departmentNumber;
    private String employeeType;
    private String description;
    private String password;

    // Getters and setters
    public String getUid() { return uid; }
    public String getCn() { return cn; }
    public String getSn() { return sn; }
    public String getGivenName() { return givenName; }
    public String getMail() { return mail; }
    public String getTelephoneNumber() { return telephoneNumber; }
    public String getDepartmentNumber() { return departmentNumber; }
    public String getEmployeeType() { return employeeType; }
    public String getDescription() { return description; }
    public String getPassword() { return password; }

    // Setters...
    public void setUid(String uid) { this.uid = uid; }
    public void setCn(String cn) { this.cn = cn; }
    public void setSn(String sn) { this.sn = sn; }
    public void setGivenName(String givenName) { this.givenName = givenName; }
    public void setMail(String mail) { this.mail = mail; }
    public void setTelephoneNumber(String telephoneNumber) { this.telephoneNumber = telephoneNumber; }
    public void setDepartmentNumber(String departmentNumber) { this.departmentNumber = departmentNumber; }
    public void setEmployeeType(String employeeType) { this.employeeType = employeeType; }
    public void setDescription(String description) { this.description = description; }
    public void setPassword(String password) { this.password = password; }
}

class UpdateUserRequest {
    private String cn;
    private String sn;
    private String givenName;
    private String mail;
    private String telephoneNumber;
    private String departmentNumber;
    private String employeeType;
    private String description;

    // Getters and setters...
    public String getCn() { return cn; }
    public String getSn() { return sn; }
    public String getGivenName() { return givenName; }
    public String getMail() { return mail; }
    public String getTelephoneNumber() { return telephoneNumber; }
    public String getDepartmentNumber() { return departmentNumber; }
    public String getEmployeeType() { return employeeType; }
    public String getDescription() { return description; }

    public void setCn(String cn) { this.cn = cn; }
    public void setSn(String sn) { this.sn = sn; }
    public void setGivenName(String givenName) { this.givenName = givenName; }
    public void setMail(String mail) { this.mail = mail; }
    public void setTelephoneNumber(String telephoneNumber) { this.telephoneNumber = telephoneNumber; }
    public void setDepartmentNumber(String departmentNumber) { this.departmentNumber = departmentNumber; }
    public void setEmployeeType(String employeeType) { this.employeeType = employeeType; }
    public void setDescription(String description) { this.description = description; }
}

class SearchRequest {
    private String uid;
    private String cn;
    private String mail;
    private String departmentNumber;
    private int limit;

    // Getters and setters...
    public String getUid() { return uid; }
    public String getCn() { return cn; }
    public String getMail() { return mail; }
    public String getDepartmentNumber() { return departmentNumber; }
    public int getLimit() { return limit; }

    public void setUid(String uid) { this.uid = uid; }
    public void setCn(String cn) { this.cn = cn; }
    public void setMail(String mail) { this.mail = mail; }
    public void setDepartmentNumber(String departmentNumber) { this.departmentNumber = departmentNumber; }
    public void setLimit(int limit) { this.limit = limit; }
}

class LDAPException extends Exception {
    public LDAPException(String message) { super(message); }
    public LDAPException(String message, Throwable cause) { super(message, cause); }
}

class UserNotFoundException extends LDAPException {
    public UserNotFoundException(String message) { super(message); }
}

// Example usage
/*
public class LDAPExample {
    public static void main(String[] args) {
        try {
            // Configure LDAP
            LDAPConfig config = new LDAPConfig.Builder()
                .ldapUrl("ldap://localhost:389")
                .baseDN("dc=example,dc=com")
                .userContainer("ou=users,dc=example,dc=com")
                .bindDN("cn=admin,dc=example,dc=com")
                .bindPassword("admin_password")
                .useTLS(false)
                .build();

            // Create user manager
            LDAPUserManager userManager = new LDAPUserManager(config);

            try {
                // Create a new user
                CreateUserRequest createRequest = new CreateUserRequest();
                createRequest.setUid("jdoe");
                createRequest.setCn("John Doe");
                createRequest.setSn("Doe");
                createRequest.setGivenName("John");
                createRequest.setMail("[email protected]");
                createRequest.setTelephoneNumber("+1-555-0123");
                createRequest.setDepartmentNumber("IT");
                createRequest.setEmployeeType("Full-time");
                createRequest.setDescription("New employee");
                createRequest.setPassword("SecureP@ss123");

                User createdUser = userManager.createUser(createRequest);
                System.out.println("Created user: " + createdUser.getUid());

                // Get user
                User user = userManager.getUser("jdoe");
                System.out.println("Retrieved user: " + user.getDisplayName());

                // Update user
                UpdateUserRequest updateRequest = new UpdateUserRequest();
                updateRequest.setTelephoneNumber("+1-555-0456");
                updateRequest.setDescription("Updated employee");

                User updatedUser = userManager.updateUser("jdoe", updateRequest);
                System.out.println("Updated user phone: " + updatedUser.getTelephoneNumber());

                // Search users
                SearchRequest searchRequest = new SearchRequest();
                searchRequest.setDepartmentNumber("IT");
                searchRequest.setLimit(10);

                List<User> users = userManager.searchUsers(searchRequest);
                System.out.println("Found " + users.size() + " users in IT department");

                // Add user to group
                userManager.addUserToGroup("jdoe", "developers");

                // Get user groups
                List<String> groups = userManager.getUserGroups("jdoe");
                System.out.println("User groups: " + groups);

            } finally {
                userManager.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
*/