directorytree/ldaprecord
Integrate LDAP into Laravel with a fluent, ActiveRecord-style API. LdapRecord handles connections, queries, authentication, and directory operations across AD and OpenLDAP. Includes Laravel-ready features for config, models, and user syncing.
Installation:
composer require directorytree/ldaprecord-laravel
Publish the configuration:
php artisan vendor:publish --provider="DirectoryTree\LdapRecord\Laravel\LdapRecordServiceProvider" --tag="ldaprecord-config"
Configure config/ldaprecord.php:
'connections' => [
'ad' => [
'host' => 'ldap.example.com',
'port' => 389,
'use_ssl' => false,
'base_dn' => 'dc=example,dc=com',
'username' => 'cn=admin,dc=example,dc=com',
'password' => 'password',
'timeout' => 5,
],
],
Define a Model:
use DirectoryTree\LdapRecord\Laravel\Models\LaravelModel;
class User extends LaravelModel
{
protected $connection = 'ad';
protected $dn = 'cn=users,dc=example,dc=com';
protected $attributes = [
'cn', 'mail', 'telephoneNumber', 'userPrincipalName'
];
protected $casts = [
'telephoneNumber' => 'string',
'accountStatus' => 'boolean',
];
}
First Use Case: Query users with a specific email domain:
$users = User::where('mail', 'like', '%@company.com')->get();
Extend Laravel’s auth with LDAP:
use DirectoryTree\LdapRecord\Laravel\Auth\CanAuthenticate;
class User extends LaravelModel implements CanAuthenticate
{
// ... model definition
}
// In AuthController:
public function login(Request $request)
{
$credentials = $request->only('username', 'password');
if (Auth::guard('ldap')->attempt($credentials)) {
return redirect()->intended('dashboard');
}
return back()->withErrors(['email' => 'Invalid credentials']);
}
Define LDAP relationships (e.g., memberOf):
class Group extends LaravelModel
{
public function members()
{
return $this->hasMany(User::class, 'memberOf', 'dn');
}
}
Use chunking for large datasets:
User::where('accountStatus', false)
->chunk(100, function ($users) {
foreach ($users as $user) {
$user->accountStatus = true;
$user->save();
}
});
Build complex LDAP filters:
$query = User::query()
->where('department', 'IT')
->orWhere(function ($q) {
$q->where('title', 'like', '%Developer%')
->where('office', 'Remote');
});
Observe LDAP model events:
User::observe(UserObserver::class);
class UserObserver
{
public function saved(User $user)
{
Log::info("User created: {$user->dn}");
}
}
Bind custom LDAP connections dynamically:
$this->app->bind('ldap.connections.custom', function () {
return [
'host' => config('ldap.custom_host'),
'port' => 636,
'use_ssl' => true,
// ...
];
});
Cache frequent LDAP queries:
$users = Cache::remember('active_users', now()->addHours(1), function () {
return User::where('accountStatus', true)->get();
});
Use DirectoryFake for unit tests:
public function test_user_creation()
{
DirectoryFake::fake();
$user = User::create(['cn' => 'Test User', 'mail' => 'test@example.com']);
$this->assertEquals('test@example.com', $user->mail);
DirectoryFake::assertCreated('cn=Test User,cn=users,dc=example,dc=com');
}
Base DN Substitution:
{base} in whereMemberOf or rename operations.{base} for dynamic DN resolution:
User::whereMemberOf('CN=Developers,{base}')->get();
Timestamp Handling:
ldap:timestamp casting is used for date attributes:
protected $casts = [
'lastLogin' => 'ldap:timestamp',
];
Empty whereIn Queries:
whereIn is called with an empty array (fixed in v3.8.5).TLS/SSL Configuration:
ldap_start_tls() failures if use_ssl is misconfigured.allow_insecure_password_changes to false in config for secure environments.Attribute Casting:
objectSid) may not cast correctly.protected $casts = [
'objectSid' => 'binary',
];
Nested Filters:
andFilter/orFilter (fixed in v4.0.1).Enable Debugging:
// config/ldaprecord.php
'debug' => [
'level' => Ldap::DEBUG_FILTER | Ldap::DEBUG_PACKETS,
],
Log LDAP Queries:
Use the DirectoryFake to inspect queries in tests:
DirectoryFake::assertSearched('cn=users,dc=example,dc=com', '(mail=*)');
Handle Connection Errors: Wrap LDAP operations in try-catch:
try {
$user = User::find('cn=Test User');
} catch (\DirectoryTree\LdapRecord\Exceptions\LdapException $e) {
Log::error("LDAP Error: " . $e->getMessage());
}
Check for Deprecated Methods:
select() were corrected in v4.0.1. Review the upgrade guide.Custom Query Types:
Extend the Builder for custom LDAP operations:
namespace App\Extensions;
use DirectoryTree\LdapRecord\Query\Builder;
class CustomBuilder extends Builder
{
public function customSearch($filter)
{
return $this->search($filter, ['scope' => SEARCH_SCOPE_SUBTREE]);
}
}
Override Model Behavior:
Extend LaravelModel for custom logic:
class CustomUser extends LaravelModel
{
public function getFullNameAttribute()
{
return "{$this->givenName} {$this->sn}";
}
}
Add LDAP Controls:
Use orderBy with custom controls:
User::orderBy('sn', 'asc', ['control' => LDAP_CONTROL_SORTREQUEST]);
Custom Authentication:
Extend CanAuthenticate for non-standard LDAP auth:
class CustomAuthenticatable extends LaravelModel implements CanAuthenticate
{
public function getAuthIdentifier()
{
return $this->userPrincipalName;
}
public function getAuthPassword()
{
return $this->password; // or fetch from LDAP
}
}
How can I help you explore Laravel packages today?