DirectoryTree / LdapRecord

A fully-featured LDAP framework.
https://ldaprecord.com
MIT License
515 stars 44 forks source link

[Bug] Unable to serialize and then `json_encode` models #429

Closed ellisonpatterson closed 2 years ago

ellisonpatterson commented 2 years ago

Environment:

Describe the bug: If I make a model (extending Laravel Model or the LdapDirectory Model) and add the notifiable trait, I am unable to notify that user if I have the notification queue-able. You get this error:

[2022-04-08 18:23:00] local.ERROR: Unable to JSON encode payload. Error code: 5 {"exception":"[object] (Illuminate\\Queue\\InvalidPayloadException(code: 0): Unable to JSON encode payload. Error code: 5 at /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Queue/Queue.php:108)
[stacktrace]
#0 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php(124): Illuminate\\Queue\\Queue->createPayload()
DirectoryTree/LdapRecord-Laravel#1 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(253): Illuminate\\Queue\\RedisQueue->push()
DirectoryTree/LdapRecord-Laravel#2 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(229): Illuminate\\Bus\\Dispatcher->pushCommandToQueue()
DirectoryTree/LdapRecord-Laravel#3 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(77): Illuminate\\Bus\\Dispatcher->dispatchToQueue()
DirectoryTree/LdapRecord-Laravel#4 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Notifications/NotificationSender.php(217): Illuminate\\Bus\\Dispatcher->dispatch()
DirectoryTree/LdapRecord-Laravel#5 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Notifications/NotificationSender.php(76): Illuminate\\Notifications\\NotificationSender->queueNotification()
DirectoryTree/LdapRecord-Laravel#6 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Notifications/ChannelManager.php(39): Illuminate\\Notifications\\NotificationSender->send()
DirectoryTree/LdapRecord-Laravel#7 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Notifications/RoutesNotifications.php(18): Illuminate\\Notifications\\ChannelManager->send()
DirectoryTree/LdapRecord-Laravel#8 /home/singlesignon/webapps/singlesignon/app/Jobs/PasswordExpiration.php(31): App\\Models\\User->notify()
DirectoryTree/LdapRecord-Laravel#9 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): App\\Jobs\\PasswordExpiration->handle()
DirectoryTree/LdapRecord-Laravel#10 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/Util.php(41): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
DirectoryTree/LdapRecord-Laravel#11 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(93): Illuminate\\Container\\Util::unwrapIfClosure()
DirectoryTree/LdapRecord-Laravel#12 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(37): Illuminate\\Container\\BoundMethod::callBoundMethod()
DirectoryTree/LdapRecord-Laravel#13 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/Container.php(653): Illuminate\\Container\\BoundMethod::call()
DirectoryTree/LdapRecord-Laravel#14 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(128): Illuminate\\Container\\Container->call()
DirectoryTree/LdapRecord-Laravel#15 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Bus\\Dispatcher->Illuminate\\Bus\\{closure}()
DirectoryTree/LdapRecord-Laravel#16 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
DirectoryTree/LdapRecord-Laravel#17 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(132): Illuminate\\Pipeline\\Pipeline->then()
DirectoryTree/LdapRecord-Laravel#18 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(119): Illuminate\\Bus\\Dispatcher->dispatchNow()
DirectoryTree/LdapRecord-Laravel#19 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(141): Illuminate\\Queue\\CallQueuedHandler->Illuminate\\Queue\\{closure}()
DirectoryTree/LdapRecord-Laravel#20 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(116): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
DirectoryTree/LdapRecord-Laravel#21 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(121): Illuminate\\Pipeline\\Pipeline->then()
DirectoryTree/LdapRecord-Laravel#22 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(69): Illuminate\\Queue\\CallQueuedHandler->dispatchThroughMiddleware()
DirectoryTree/LdapRecord-Laravel#23 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php(98): Illuminate\\Queue\\CallQueuedHandler->call()
DirectoryTree/LdapRecord-Laravel#24 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Queue/SyncQueue.php(43): Illuminate\\Queue\\Jobs\\Job->fire()
DirectoryTree/LdapRecord-Laravel#25 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(253): Illuminate\\Queue\\SyncQueue->push()
DirectoryTree/LdapRecord-Laravel#26 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(229): Illuminate\\Bus\\Dispatcher->pushCommandToQueue()
DirectoryTree/LdapRecord-Laravel#27 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(95): Illuminate\\Bus\\Dispatcher->dispatchToQueue()
DirectoryTree/LdapRecord-Laravel#28 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Foundation/Bus/Dispatchable.php(57): Illuminate\\Bus\\Dispatcher->dispatchSync()
DirectoryTree/LdapRecord-Laravel#29 /home/singlesignon/webapps/singlesignon/app/Console/Kernel.php(38): App\\Jobs\\PasswordExpiration::dispatchSync()
DirectoryTree/LdapRecord-Laravel#30 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(102): App\\Console\\Kernel->schedule()
DirectoryTree/LdapRecord-Laravel#31 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Support/helpers.php(302): Illuminate\\Foundation\\Console\\Kernel->Illuminate\\Foundation\\Console\\{closure}()
DirectoryTree/LdapRecord-Laravel#32 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(103): tap()
DirectoryTree/LdapRecord-Laravel#33 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/Container.php(873): Illuminate\\Foundation\\Console\\Kernel->Illuminate\\Foundation\\Console\\{closure}()
DirectoryTree/LdapRecord-Laravel#34 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/Container.php(758): Illuminate\\Container\\Container->build()
DirectoryTree/LdapRecord-Laravel#35 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(855): Illuminate\\Container\\Container->resolve()
DirectoryTree/LdapRecord-Laravel#36 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/Container.php(694): Illuminate\\Foundation\\Application->resolve()
DirectoryTree/LdapRecord-Laravel#37 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(840): Illuminate\\Container\\Container->make()
DirectoryTree/LdapRecord-Laravel#38 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(181): Illuminate\\Foundation\\Application->make()
DirectoryTree/LdapRecord-Laravel#39 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(124): Illuminate\\Container\\BoundMethod::addDependencyForCallParameter()
DirectoryTree/LdapRecord-Laravel#40 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): Illuminate\\Container\\BoundMethod::getMethodDependencies()
DirectoryTree/LdapRecord-Laravel#41 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/Util.php(41): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
DirectoryTree/LdapRecord-Laravel#42 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(93): Illuminate\\Container\\Util::unwrapIfClosure()
DirectoryTree/LdapRecord-Laravel#43 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(37): Illuminate\\Container\\BoundMethod::callBoundMethod()
DirectoryTree/LdapRecord-Laravel#44 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Container/Container.php(653): Illuminate\\Container\\BoundMethod::call()
DirectoryTree/LdapRecord-Laravel#45 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Console/Command.php(171): Illuminate\\Container\\Container->call()
DirectoryTree/LdapRecord-Laravel#46 /home/singlesignon/webapps/singlesignon/vendor/symfony/console/Command/Command.php(291): Illuminate\\Console\\Command->execute()
DirectoryTree/LdapRecord-Laravel#47 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Console/Command.php(156): Symfony\\Component\\Console\\Command\\Command->run()
DirectoryTree/LdapRecord-Laravel#48 /home/singlesignon/webapps/singlesignon/vendor/symfony/console/Application.php(989): Illuminate\\Console\\Command->run()
DirectoryTree/LdapRecord-Laravel#49 /home/singlesignon/webapps/singlesignon/vendor/symfony/console/Application.php(299): Symfony\\Component\\Console\\Application->doRunCommand()
DirectoryTree/LdapRecord-Laravel#50 /home/singlesignon/webapps/singlesignon/vendor/symfony/console/Application.php(171): Symfony\\Component\\Console\\Application->doRun()
DirectoryTree/LdapRecord-Laravel#51 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Console/Application.php(102): Symfony\\Component\\Console\\Application->run()
DirectoryTree/LdapRecord-Laravel#52 /home/singlesignon/webapps/singlesignon/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(129): Illuminate\\Console\\Application->run()
DirectoryTree/LdapRecord-Laravel#53 /home/singlesignon/webapps/singlesignon/artisan(37): Illuminate\\Foundation\\Console\\Kernel->handle()
DirectoryTree/LdapRecord-Laravel#54 {main}
"} 

I found that it is some encoding issue and have to put this before I use notify:

$user->objectguid = $user->getConvertedGuid();
$user->objectsid = $user->getConvertedSid();
$user->syncOriginal();

Some type of UTF8 issue or something, but running those three lines allows the user model to be queues successfully without issue.

Could this be handled in the library potentially?

Thank you!

stevebauman commented 2 years ago

Hi @ellisonpatterson!

Can you post your model code? If you extend the ActiveDirectory\Entry model, the objectsid conversion should happen automatically:

https://github.com/DirectoryTree/LdapRecord/blob/56cf46e9b7714d66d2d4018afcb100684016f783/src/Models/ActiveDirectory/Entry.php#L152-L165

The objectguid attribute should also be converted automatically in all models:

https://github.com/DirectoryTree/LdapRecord/blob/56cf46e9b7714d66d2d4018afcb100684016f783/src/Models/Model.php#L639-L651

Otherwise, you will have to convert these manually (they are binary values and will break serialization).

ellisonpatterson commented 2 years ago

@stevebauman

My model class is extending LdapRecord\Models\ActiveDirectory\User.

Here is my model class:

<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Routing\UrlRoutable;
use LdapRecord\Models\Concerns\CanAuthenticate;
use LdapRecord\Models\Attributes\AccountControl;
use LdapRecord\Models\ActiveDirectory\User as ActiveDirectory;
use Carbon\Carbon;

class User extends ActiveDirectory implements Authenticatable, UrlRoutable
{
    use CanAuthenticate, Notifiable;

    public function getId()
    {
        return $this->getConvertedGuid();
    }

    public function getFullName()
    {
        return $this->getFirstAttribute('name');
    }

    public function getEmail()
    {
        return $this->getFirstAttribute('userPrincipalName');
    }

    public function isDisabled()
    {
        return $this->userAccountControl()->has(AccountControl::ACCOUNTDISABLE);
    }

    public function mustChangePassword()
    {
        return !$this->pwdLastSet;
    }

    public function canChangePassword()
    {
        return !$this->userAccountControl()->has(AccountControl::PASSWD_CANT_CHANGE);
    }

    public function canPasswordExpire()
    {
        return !$this->userAccountControl()->has(AccountControl::DONT_EXPIRE_PASSWORD);
    }

    public function whenPasswordExpires()
    {
        $lastChange = Carbon::parse($this->pwdLastSet);
        return Carbon::parse($lastChange)->addDays(config('sso.password_max_age'));
    }

    public function hasPasswordExpired()
    {
        return Carbon::parse($this->whenPasswordExpires())->isPast();
    }

    public function userAccountControl(array $value = [])
    {
        $ac = new AccountControl($value[0] ?? null);
        $ac->setValues($ac->extractFlags($this->getFirstAttribute('userAccountControl')));
        return $ac;
    }

    public function getRouteKey()
    {
        return $this->getId();
    }

    public function getRouteKeyName()
    {
        return 'user';
    }

    public function resolveRouteBinding($value, $field = null)
    {
        return (new self)->findByGuid($value);
    }

    public function resolveChildRouteBinding($childType, $value, $field)
    {
        return null;
    }

    public function routeNotificationForMail($notification = null)
    {
        return $this->getEmail();
    }
}
stevebauman commented 2 years ago

Thanks @ellisonpatterson! I'm able to reproduce this error locally with my Active Directory instance. Patching now 👍

stevebauman commented 2 years ago

Moved this to the LdapRecord repository since this is a core issue 👍

stevebauman commented 2 years ago

Fixed in v2.12.0 🎉

ellisonpatterson commented 2 years ago

Thank you so much!