Environment (please complete the following information):
Describe the bug:
Hi, I'm trying to make a Laravel Artisan command that retrieves all computers in my Active Directory and inserts them in my database.
To do so, I am using a Synchronizer, but when I try to run the run() function, it gives me the following Exception:
LdapRecord\Laravel\Import\ImportException: Attribute [objectguid] does not exist on LDAP model [LdapRecord\Models\ActiveDirectory\Computer] object [...]
I don't have any issues with neither my database nor my ActiveDirectory, so perhaps the problem comes from my Eloquent Model, named "Machine".
Here are the code samples of the Artisan Command GetMachines.php and my Machine.php model:
GetMachines.php
<?php
namespace App\Console\Commands;
use App\Models\Machine as EloquentMachine;
use Illuminate\Console\Command;
use LdapRecord\Laravel\Import\Synchronizer;
class GetMachines extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ssit:machines';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Récupère les machines présentes dans le LDAP.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
// $this->info("Début de l'importation de l'AD dans la base de données.");
$synchronizer = new Synchronizer(EloquentMachine::class, $config = [
'sync_attributes' => [
'machine_name' => 'name',
'os' => 'operatingsystem',
'created_at' => 'whencreated',
'updated_at' => 'whenchanged',
'last_connection' => 'lastlogondate',
'last_connection_time_stamp' => 'lastlogontimestamp',
'guid' => 'objectguid',
'chu_group' => 'chu_group'
],
]);
// TODO: trouver d'où vient cette histoire de objectguid not found + pourquoi ça se met pas dans la bd
foreach (EloquentMachine::getAllMachines() as $machine) {
try {
$synchronizer->run($machine)->save();
} catch (\Exception $e) {
echo $e;
die();
}
}
return 0;
}
}
Machine.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use LdapRecord\Laravel\LdapImportable;
use LdapRecord\Laravel\ImportableFromLdap;
use LdapRecord\Models\ActiveDirectory\Computer;
use LdapRecord\Models\Collection;
class Machine extends Model implements LdapImportable
{
use HasFactory, ImportableFromLdap;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'machine_name',
'chu_group',
'os',
'last_connection',
'objectguid'
];
/**
* The attributes that are object classes.
*
* @var array
*/
protected array $objectClasses = [
'machine_name',
'os',
'created_at'
'updated_at',
'last_connection',
'is_active',
'objectguid'
];
/**
* The Organisational Units used to search the computers.
*
* @var array
*/
protected static array $ous = [
"admin",
"regular",
"other"
];
/**
* Retrieves all the machines based on the OUs declared above.
*
* @return Collection
*/
public static function getAllMachines(): Collection
{
$allMachines = Collection::empty();
foreach (self::$ous as $ou) {
$allMachines = $allMachines->concat(self::getMachinesByOU($ou));
}
return $allMachines;
}
/**
* Retrieves all the machines based on the OU given as parameter.
*
* @param $ou
* @return Collection|array
*/
protected static function getMachinesByOU($ou): Collection|array
{
$results = Computer::query()->in("ou=$ou," . env("LDAP_BASE_DN"))->paginate();
foreach ($results as $result) {
$result->setAttribute('chu_group', $ou);
}
return $results;
}
}
And to make sure it was not the LDAP object's fault, I made sure to verify it had an "objectguid" attribute using the ldap:browse command.
If you need more information, I'll be happy to provide it to you.
Thanks in advance for your help.
Hi @yungmides!
Hmmm this is strange for sure.
Can you try retrieving a computer from that OU and then attempt pulling the converted GUID from it?
For example:
$computer = Computer::query()->in("ou=$ou," . env("LDAP_BASE_DN"))->first();
dd($computer->getConvertedGuid());
Let me know what it returns and we'll go from there! 👍
Hello again @stevebauman , sorry for the delayed response ! (y'know, timezones. 😅)
After doing exactly what you suggested, this string was returned:
54188727-5c46-45ba-a8ce-21ecc5c77016
Seems that the computer does have a guid in fact, quite strange indeed.
Let me know if you need me to do anything else !
Haha no worries @yungmides!
It seems that there could be one computer that is potentially returning no Object GUID in the collection -- let's find it.
Can you try executing the below?
// Get all the computers in the OU that do not have an Object GUID:
$computers = Computer::query()->in("ou=$ou," . env("LDAP_BASE_DN"))->paginate()->filter(function ($computer) {
return ! $computer->getConvertedGuid();
});
// Return a list of the computers name's:
dd($computers->map->getName());
Oops, seems like I sent a comment by mistake !
Back to what I was saying,
When I executed the code you provided me, it returned me an array containing the name of ALL the computers in the OU.
Kinda baffled by this, I first thought : "Wait, does that mean ALL my computers do not have an Object GUID ?" and I decided to check one of the computers from the array and make sure it did not have one.
I took one computer (named "CO-TL69310"), inspected it using the php artisan ldap:browse command and asked for the objectguid attribute.
It returned me this mess of a string:
+------------------+
| objectguid |
+------------------+
| YQ+µ~`xA”œ;›P |
| é |
+------------------+
And as a final test, I went and executed the following:
// Find a computer named CO-TL69310
$test = Computer::query()->in("ou=$ou," . env("LDAP_BASE_DN"))->findBy('cn', 'CO-TL69310');
// Check for the presence of a GUID
dd($test->getConvertedGuid());
and it gave me this
"b52b5159-607e-4178-949c-3b9b500ae997"
So the computer does have an Object GUID it seems, but for some reason, when paginated, it seems to either have lost it in the process or prevent LDAPRecord from using it.
I also tried this:
// Get all computers in the OU.
$results = Computer::query()->in("ou=$ou," . env("LDAP_BASE_DN"))->paginate();
//Dump to the console each machine's GUID.
foreach($results as $result) {
dump($result->getConvertedGuid());
}
die();
... and it returned me a bunch of nulls.
I'm going to investigate my ActiveDirectory server to see if it is an issue on my end and also do a whole lot of debugging to see what's causing the disappearance of the GUIDs, but I have to admit, I'm kind of puzzled over this.
It returned me this mess of a string:
Active Directory stores object GUID's in binary, so they must be converted to a string -- that's why you're seeing a bunch of random symbols.
This is why you must use the getConvertedGuid() method on the model -- as it will retrieve the string representation of the object's GUID.
I see you have added guid in your sync_attributes array in the Synchronizer -- the synchronizer applies the converted object's GUID for you -- you do not need to add this attribute in the sync attributes array.
... and it returned me a bunch of nulls.
Can you try using get() instead for the Computer::query()? For example:
// Get all computers in the OU.
$results = Computer::query()->in("ou=$ou," . env("LDAP_BASE_DN"))->get();
//Dump to the console each machine's GUID.
foreach($results as $result) {
dump($result->getConvertedGuid());
}
die();
Does that display any differently?
Active Directory stores object GUID's in binary, so they must be converted to a string -- that's why you're seeing a bunch of random symbols.
Oh thanks, I didn't know that, I originally thought it was a regular string that got corrupted for some reason. Glad it's not something to worry about.
I see you have added guid in your sync_attributes array in the Synchronizer -- the synchronizer applies the converted object's GUID for you -- you do not need to add this attribute in the sync attributes array.
Didn't know that either, I'll keep that in mind next time I use a Synchronizer, thanks again for the knowledge drop.
Can you try using get() instead for the Computer::query()?
I just did, and surprisingly, this time, I got actual converted GUID, and none of them were null ! I only got 1000 results though because of the get() function, but that's very promising !
Happy to help @yungmides! 😄
I just did, and surprisingly, this time, I got actual converted GUID, and none of them were null ! I only got 1000 results though because of the get() function, but that's very promising !
Okay, this is likely a bug due to the LdapRecord core (introduced by yours truly -- of course 😅). I released a patch yesterday for this:
https://github.com/DirectoryTree/LdapRecord/releases/tag/v2.2.1
Can you run composer update and try your Computer::query() again with paginate()?
Can you run composer update and try your Computer::query() again with paginate()?
Done ! My version of ldaprecord is v2.2.2 and ldaprecord-laravel is v2.1.2 ! 😁
Bad news though, despite updating every single dependency in my project, my Computer::query() with paginate() still gives me nulls. 😔
Thanks @yungmides! I'm able to reproduce this locally -- it seems this is an issue that has always been present in LdapRecord (I've tested this on the v1.0 branch).
When using the paginate() method, all attributes are not being requested in the query's "select" (*) by default:
// Will not include objectguid:
$results = Computer::query()->in("ou=$ou," . env("LDAP_BASE_DN"))->paginate();
// Will include objectguid:
$results = Computer::query()->in("ou=$ou," . env("LDAP_BASE_DN"))->select('*')->paginate();
The above should work -- can you give it a shot?
I've also just pushed a patch for this in the core LdapRecord repository. I'm also going to patch the v1 branch with this fix.
Once again, my most sincere apologies for the delayed response !
I'm happy to tell you that it works like a charm now ! I'll close the issue now ! 😄
Thank you so much for your tremendous help and for this amazing package @stevebauman ! Working with LDAP has always been an issue for me but LdapRecord makes it a whole lot easier !
Keep up the great work ! 👍
Oh no worries @yungmides! Haha I totally understand 👍
I'm so glad that you're up and running now! 🎉
Thank you so much for your kind words, I really do appreciate it! ❤️
Please feel free to drop by and create an issue here anytime you have further questions 😄