Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ldaprecord Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require directorytree/ldaprecord-laravel
    

    Publish the configuration:

    php artisan vendor:publish --provider="DirectoryTree\LdapRecord\Laravel\LdapRecordServiceProvider" --tag="ldaprecord-config"
    
  2. 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,
        ],
    ],
    
  3. 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',
        ];
    }
    
  4. First Use Case: Query users with a specific email domain:

    $users = User::where('mail', 'like', '%@company.com')->get();
    

Implementation Patterns

Core Workflows

1. Authentication

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']);
}

2. Relationships

Define LDAP relationships (e.g., memberOf):

class Group extends LaravelModel
{
    public function members()
    {
        return $this->hasMany(User::class, 'memberOf', 'dn');
    }
}

3. Batch Operations

Use chunking for large datasets:

User::where('accountStatus', false)
    ->chunk(100, function ($users) {
        foreach ($users as $user) {
            $user->accountStatus = true;
            $user->save();
        }
    });

4. Custom Filters

Build complex LDAP filters:

$query = User::query()
    ->where('department', 'IT')
    ->orWhere(function ($q) {
        $q->where('title', 'like', '%Developer%')
          ->where('office', 'Remote');
    });

5. Model Events

Observe LDAP model events:

User::observe(UserObserver::class);

class UserObserver
{
    public function saved(User $user)
    {
        Log::info("User created: {$user->dn}");
    }
}

Integration Tips

Laravel Service Container

Bind custom LDAP connections dynamically:

$this->app->bind('ldap.connections.custom', function () {
    return [
        'host' => config('ldap.custom_host'),
        'port' => 636,
        'use_ssl' => true,
        // ...
    ];
});

Caching

Cache frequent LDAP queries:

$users = Cache::remember('active_users', now()->addHours(1), function () {
    return User::where('accountStatus', true)->get();
});

Testing

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');
}

Gotchas and Tips

Pitfalls

  1. Base DN Substitution:

    • Issue: Forgetting {base} in whereMemberOf or rename operations.
    • Fix: Use {base} for dynamic DN resolution:
      User::whereMemberOf('CN=Developers,{base}')->get();
      
  2. Timestamp Handling:

    • Issue: 32-bit PHP timestamp errors (fixed in v4.0.4).
    • Fix: Ensure ldap:timestamp casting is used for date attributes:
      protected $casts = [
          'lastLogin' => 'ldap:timestamp',
      ];
      
  3. Empty whereIn Queries:

    • Issue: Returns all records if whereIn is called with an empty array (fixed in v3.8.5).
    • Fix: Validate input arrays before querying.
  4. TLS/SSL Configuration:

    • Issue: ldap_start_tls() failures if use_ssl is misconfigured.
    • Fix: Set allow_insecure_password_changes to false in config for secure environments.
  5. Attribute Casting:

    • Issue: Binary data (e.g., objectSid) may not cast correctly.
    • Fix: Use custom casts:
      protected $casts = [
          'objectSid' => 'binary',
      ];
      
  6. Nested Filters:

    • Issue: Regression in nested andFilter/orFilter (fixed in v4.0.1).
    • Fix: Update to v4.x for stable behavior.

Debugging Tips

  1. Enable Debugging:

    // config/ldaprecord.php
    'debug' => [
        'level' => Ldap::DEBUG_FILTER | Ldap::DEBUG_PACKETS,
    ],
    
  2. Log LDAP Queries: Use the DirectoryFake to inspect queries in tests:

    DirectoryFake::assertSearched('cn=users,dc=example,dc=com', '(mail=*)');
    
  3. 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());
    }
    
  4. Check for Deprecated Methods:

    • v3 → v4: Methods like select() were corrected in v4.0.1. Review the upgrade guide.

Extension Points

  1. 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]);
        }
    }
    
  2. Override Model Behavior: Extend LaravelModel for custom logic:

    class CustomUser extends LaravelModel
    {
        public function getFullNameAttribute()
        {
            return "{$this->givenName} {$this->sn}";
        }
    }
    
  3. Add LDAP Controls: Use orderBy with custom controls:

    User::orderBy('sn', 'asc', ['control' => LDAP_CONTROL_SORTREQUEST]);
    
  4. 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
        }
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai