Skip to content

Instantly share code, notes, and snippets.

@jameskraus
Created February 9, 2017 21:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jameskraus/fdffe109fac1d056eb7d2f01530a59f0 to your computer and use it in GitHub Desktop.
Save jameskraus/fdffe109fac1d056eb7d2f01530a59f0 to your computer and use it in GitHub Desktop.
Modified Adldap2-Larvel Command
<?php
namespace App\Console\Commands;
use Adldap\Models\Entry;
use Adldap\Models\Group;
use Illuminate\Console\Command;
class ImportGroups extends Command
{
use ImportsModels;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'groups:import
{group? : The group to import}
{--filter= : The filter to use}
{--no-membership : Do not import the member<->group relationships}
{--log= : Whether or not to log}
{--connection= : The connection to use}
';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import the Groups from LDAP';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Retrieve the Adldap instance.
$adldap = $this->getAdldap($this->option('connection'));
if (!$adldap->getConnection()->isBound()) {
// If the connection isn't bound yet, we'll connect to the server manually.
$adldap->connect();
}
// Generate a new user search.
$search = $adldap->search()->groups();
if ($filter = $this->getFilter()) {
// If the filter option was given, we'll
// insert it into our search query.
$search->rawFilter($filter);
}
if ($group = $this->argument('group')) {
$groups = [$search->findOrFail($group)];
$this->info("Found group '{$groups[0]->getCommonName()}'. Importing...");
} else {
// Retrieve all users. We'll paginate our search in case we hit
// the 1000 record hard limit of active directory.
$groups = $search->paginate()->getResults();
$count = count($groups);
$this->info("Found {$count} groups(s). Importing...");
}
$this->info("\nSuccessfully imported / synchronized {$this->import_groups($groups)} groups(s).");
}
public function import_groups(array $groups = [])
{
// Entries and models imported
$imported_entries_and_models = $this->import($groups, Group::class, \App\Group::class);
$should_import_membership = !$this->option('no-membership');
$this->info("\n");
if ($should_import_membership) {
foreach ($imported_entries_and_models as ['entry' => $entry, 'model' => $group_model]){
$this->info("Importing members for {$entry->getCommonName()}");
$db_users = $group_model->users->keyBy(function ($u) {
return bin2hex($u->guid);
})->toArray();
$ldap_users = $entry->getMembers()->keyBy(function ($u) {
return bin2hex($u->objectguid[0]);
})->toArray();
$db_users_to_remove = array_filter($db_users, function ($user_guid) use ($ldap_users) {
return !array_key_exists($user_guid, $ldap_users);
}, ARRAY_FILTER_USE_KEY);
$ldap_users_to_add = array_filter($ldap_users, function ($user_guid) use ($db_users) {
return !array_key_exists($user_guid, $db_users);
}, ARRAY_FILTER_USE_KEY);
foreach ($ldap_users_to_add as $user){
$user_model = $this->getModelFromAdldap($user, \App\User::class);
$this->info("...Connecting {$user_model->name} to {$group_model->name}");
$user_model->groups()->save($group_model);
}
foreach( $db_users_to_remove as $user ){
$this->info("...Detaching {$user['name']} from {$group_model->name}");
$group_model->users()->detach($user['id']);
}
// Does all the detaching in one step
//$group_model->users()->detach($user_ids_to_remove);
}
}
return count($imported_entries_and_models);
}
/**
* Imports the specified groups and returns the total
* number of groups successfully imported.
*
* @param Entry[] $entries
*
* @param string $ldap_type Class name for Adldap model
* @param string $model_type Class name for eloquent model
* @return array
*/
public function import(array $entries = [], $ldap_type, $model_type)
{
$imported = [];
// We need to filter our results to make sure they are
// only users. In some cases, Contact models may be
// returned due the possibility of the
// existing in the same scope.
$entries = collect($entries)->filter(function ($entry) use ($ldap_type) {
return $entry instanceof $ldap_type;
});
$bar = $this->output->createProgressBar(count($entries));
foreach ($entries as $entry) {
try {
// Import the group and retrieve it's model.
$model = $this->getModelFromAdldap($entry, $model_type);
// Save the returned model.
$this->save($entry, $model);
$imported[] = [
'entry' => $entry,
'model' => $model
];
} catch (\Exception $e) {
// Log the unsuccessful import.
if ($this->isLogging()) {
logger()->error("Unable to import group {$entry->getCommonName()}. {$e->getMessage()}");
}
}
$bar->advance();
}
return $imported;
}
/**
* Returns true / false if the current import is being logged.
*
* @return bool
*/
public function isLogging()
{
return $this->option('log') == 'true';
}
/**
* Returns true / false if users are being deleted if they are disabled in AD.
*
* @return bool
*/
public function isDeleting()
{
return $this->option('delete') == 'true';
}
/**
* Returns the limitation filter for the group query.
*
* @return string
*/
public function getFilter()
{
return $this->getLimitationFilter() ?: $this->option('filter');
}
}
<?php
namespace App\Console\Commands;
use Adldap\Laravel\Facades\Adldap;
use Adldap\Models\Entry;
use Illuminate\Database\Eloquent\Model;
trait ImportsModels
{
/**
* Returns an existing or new Eloquent user from the specified Adldap user instance.
*
* @param Entry $entry
* @param string $model_class
* @return Model
* @internal param Entry $entity
*
*/
protected function getModelFromAdldap(Entry $entry, $model_class)
{
$model = $this->findOrCreateModelFromAdldap($entry, $model_class);
// Synchronize other active directory attributes on the model.
$model = $this->syncModelFromAdldap($entry, $model);
return $model;
}
/**
* Returns a new Eloquent user query.
*
* @param string $key
* @param string $value
* @param string $model_class
*
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function newEloquentQuery($key, $value, $model_class)
{
$model = new $model_class();
if (method_exists($model, 'trashed')) {
// If the trashed method exists on our User model, then we must be
// using soft deletes. We need to make sure we include these
// results so we don't create duplicate user records.
$model = $model->withTrashed();
}
return $model->where([$key => $value]);
}
/**
* Finds an Eloquent model from the specified Adldap user.
*
* @param Entry $entry
*
* @param string $model_class
* @return Model
*/
protected function findOrCreateModelFromAdldap(Entry $entry, $model_class)
{
// Get the model key.
$attributes = $this->getGUIDAttribute();
// Get the model key.
$key = key($attributes);
// Get the guid from the AD model.
$guid = $entry->{$attributes[$key]};
// Make sure we retrieve the first guid result if it's an array.
$guid = (is_array($guid) ? array_get($guid, 0) : $guid);
// Try to find the local database group record.
$model = $this->newEloquentQuery($key, $guid, $model_class)->first();
// Create a new model instance of it isn't found.
$model = ($model instanceof Model ? $model : new $model_class() );
// Set the guid
$model->{$key} = $guid;
return $model;
}
/**
* Saves the specified model.
*
* @param Model $model
*
* @return bool
*/
protected function saveModel(Model $model)
{
return $model->save();
}
/**
* Saves the specified user with its model.
*
* @param Entry $entry
* @param Model $model
*
* @return bool
*/
protected function save(Entry $entry, Model $model)
{
$imported = false;
if ($this->saveModel($model) && $model->wasRecentlyCreated) {
$imported = true;
// Log the successful import.
if ($this->isLogging()) {
logger()->info("Imported {$entry->getObjectClass()} {$entry->getCommonName()}");
}
}
return $imported;
}
/**
* Fills a models attributes by the specified Users attributes.
*
* @param Entry $entry
* @param Model $model
*
* @return Model
*/
protected function syncModelFromAdldap(Entry $entry, Model $model)
{
foreach ($this->getSyncAttributes() as $modelField => $adField) {
$value = $this->isAttributeCallback($adField) ?
$this->handleAttributeCallback($entry, $adField) :
$this->handleAttributeRetrieval($entry, $adField);
$model->{$modelField} = $value;
}
return $model;
}
/**
* Handles retrieving the specified field from the User model.
*
* @param Entry $entry
* @param string $field
*
* @return string|null
*/
protected function handleAttributeRetrieval(Entry $entry, $field)
{
// Disabled check
//if (false && $field === $this->getSchema()->thumbnail()) {
// If the field we're retrieving is the groups thumbnail photo, we need
// to retrieve it encoded so we're able to save it to the database.
// $value = $entry->getThumbnailEncoded();
//} else {
$value = $entry->{$field};
// If the AD Value is an array, we'll
// retrieve the first value.
$value = (is_array($value) ? array_get($value, 0) : $value);
//}
return $value;
}
/**
* Handles retrieving the value from an attribute callback.
*
* @param Entry $entry
* @param string $callback
*
* @return mixed
*/
protected function handleAttributeCallback(Entry $entry, $callback)
{
// Explode the callback into its class and method.
list($class, $method) = explode('@', $callback);
// Create the handler.
$handler = app($class);
// Call the attribute handler method and return the result.
return call_user_func_array([$handler, $method], [$entry]);
}
/**
* Returns true / false if the specified string
* is a callback for an attribute handler.
*
* @param string $string
*
* @return bool
*/
protected function isAttributeCallback($string)
{
$matches = preg_grep("/(\w)@(\w)/", explode("\n", $string));
return count($matches) > 0;
}
/**
* Returns the root Adldap provider instance.
*
* @param string $provider
*
* @return \Adldap\Contracts\Connections\ProviderInterface
*/
protected function getAdldap($provider = null)
{
$provider = $provider ?: $this->getDefaultConnectionName();
return Adldap::getManager()->get($provider);
}
/**
* Returns Adldap's current attribute schema.
*
* @return \Adldap\Contracts\Schemas\SchemaInterface
*/
protected function getSchema()
{
return $this->getAdldap()->getSchema();
}
/**
* Returns the configured login limitation filter.
*
* @return string|null
*/
protected function getLimitationFilter()
{
return config('app.ldap.group.limitation_filter');
}
/**
* Returns the configured guid attribute for discovering LDAP users.
*
* @return array
*/
protected function getGUIDAttribute()
{
return config('app.ldap.group.guidAttribute', ['guid' => $this->getSchema()->objectGuid()]);
}
/**
* Returns the configured sync attributes for filling the
* Laravel user model with active directory fields.
*
* @return array
*/
protected function getSyncAttributes()
{
return config('app.ldap.group.sync_attributes', ['name' => $this->getSchema()->commonName()]);
}
/**
* Returns the configured default connection name.
*
* @return mixed
*/
protected function getDefaultConnectionName()
{
return config('adldap_auth.connection', 'default');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment