directorytree/ldaprecord-laravel
Integrate LDAP authentication and directory access into Laravel with LdapRecord. Provides user sync, login, Eloquent-style models for LDAP entries, configuration for multiple connections, and utilities for Active Directory and OpenLDAP environments.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require directorytree/ldaprecord-laravel
Publish the config:
php artisan vendor:publish --provider="DirectoryTree\LdapRecordLaravel\LdapRecordLaravelServiceProvider" --tag="config"
Configure LDAP Connection:
Edit config/ldaprecord.php with your LDAP server details (host, base DN, bind DN, etc.).
Define an LDAP Model:
Extend DirectoryTree\LdapRecordLaravel\Eloquent\LdapModel:
use DirectoryTree\LdapRecordLaravel\Eloquent\LdapModel;
class User extends LdapModel
{
protected $ldapClass = 'user'; // LDAP object class
protected $ldapAttributes = ['uid', 'mail', 'givenName', 'sn'];
protected $ldapConnection = 'ldap'; // Config key
}
First Use Case: Authentication
Configure auth in config/auth.php:
'providers' => [
'ldap' => [
'driver' => 'ldap',
'model' => User::class,
],
],
Use in LoginController:
use DirectoryTree\LdapRecordLaravel\Auth\LdapAuthenticatable;
public function login(Request $request)
{
$credentials = $request->only(['username', 'password']);
if (Auth::attempt($credentials, $request->filled('remember'))) {
return redirect()->intended('dashboard');
}
return back()->withErrors(['credentials' => 'Invalid credentials']);
}
Sync with LDAP:
// Sync a single user
$user = User::find(1);
$user->sync();
// Sync all users (with scopes)
User::syncAll(['--scopes' => 'active,verified']);
Import from LDAP:
php artisan ldap:import User --scopes=active,verified
Or programmatically:
User::import(['--scopes' => 'active,verified']);
Basic Query:
// Find users by attribute
$users = User::where('mail', 'like', '%@example.com')->get();
// Virtual attributes (e.g., memberOf)
$users = User::with('memberOf')->get();
Directory Emulator (for testing):
// Enable emulator in config/ldaprecord.php
'emulator' => [
'enabled' => true,
'directory' => database_path('ldap_emulator'),
],
Custom Auth Logic:
// Extend LdapAuthenticatable for custom rules
class CustomLdapUser extends LdapAuthenticatable
{
public function validateCredentials($username, $password)
{
// Custom validation (e.g., check password expiry)
if ($this->isPasswordExpired()) {
return false;
}
return parent::validateCredentials($username, $password);
}
}
Events for Authentication:
// Listen for rule events
Auth::authenticate(function ($request) {
event(new \DirectoryTree\LdapRecordLaravel\Events\RulePassed($request));
}, function ($request, $exception) {
event(new \DirectoryTree\LdapRecordLaravel\Events\RuleFailed($request, $exception));
});
Dynamic Attribute Handling:
// In User model
protected $ldapAttributeHandlers = [
'fullName' => function ($value) {
return $value ? explode(' ', $value)[0] . ' ' . explode(' ', $value)[1] : null;
},
'isActive' => function ($value) {
return $value === 'TRUE';
},
];
Invokable Handlers (v3.0.5+):
protected $ldapAttributeHandlers = [
'formattedName' => \App\Handlers\FormatName::class,
];
CLI Tool:
php artisan ldap:browse --connection=ldap --base-dn="ou=users,dc=example,dc=com" --attributes="uid,mail"
Programmatic Browse:
$entries = \DirectoryTree\LdapRecordLaravel\Facades\LdapRecord::list('ou=users,dc=example,dc=com', ['uid', 'mail']);
Password Hashing:
password_column is set to false, do not rehash passwords (fixed in v3.3.2).rehashPasswordIfRequired is implemented in custom auth providers (v3.3.1).Directory Emulator Quirks:
LdapRecord updates (v3.1.2).detach() explicitly for memberof or other relations (v3.0.8).Logging:
logging.level (v3.1.0), but wildcard listeners are only registered if logging is enabled (v3.3.4).LogManager::channel() for custom logging channels (v3.2.2).Scopes in Imports:
LdapImporter must be comma-separated (v2.7.0).--delete-missing uses Eloquent’s deleted_at format (v3.4.1).UTF-8 Encoding:
utf8_encode; use mb_convert_encoding (v3.2.1).Laravel Version Gaps:
Enable LDAP Logging:
'logging' => [
'enabled' => true,
'level' => 'debug', // 'info', 'debug', 'error'
'channel' => 'single', // or custom channel name
],
View logs via php artisan ldap:browse --verbose.
Directory Emulator Debugging:
\DirectoryTree\LdapRecordLaravel\Facades\LdapRecord::emulator()->dump();
php artisan ldap:emulator:reset
Authentication Debugging:
RulePassed/RuleFailed events for failed logins.causedByInvalidCredentials() in LoginController (fixed in v3.1.1):
if (Auth::attempt($credentials) === false) {
if (Auth::causedByInvalidCredentials()) {
// Handle invalid credentials
}
}
Custom Attribute Handlers:
Override Sync Logic:
// In User model
public function sync()
{
// Custom sync logic before/after parent sync
$this->beforeSync();
parent::sync();
$this->afterSync();
}
Extend LdapAuthenticatable:
validateCredentials() or retrieveByCredentials() for custom auth logic.Custom LDAP Connections:
config/ldaprecord.php and use them in models:
protected $ldapConnection = 'ldap_backup';
Event Listeners:
LdapRecord\Events\* or package-specific events (e.g., RulePassed).Batch Imports:
--chunk in ldap:import to avoid memory issues:
php artisan ldap:import User --chunk=100
Selective Attribute Loading:
User::select(['uid', 'mail'])->get();
Caching:
$users = Cache::remember('ldap_users_active', now()->addHours(1), function () {
return User::where('isActive', true
How can I help you explore Laravel packages today?