[Web] Fix mailbox editing when password is unchanged, fix adding new administrator (fixes #4054, fixes #4053); [Web] Update libs, add LDAP for future admin/domain admin authentication
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap;
|
||||
|
||||
use Adldap\Log\EventLogger;
|
||||
use Adldap\Connections\Ldap;
|
||||
use InvalidArgumentException;
|
||||
use Adldap\Log\LogsInformation;
|
||||
use Adldap\Connections\Provider;
|
||||
use Adldap\Events\DispatchesEvents;
|
||||
use Adldap\Connections\ProviderInterface;
|
||||
use Adldap\Connections\ConnectionInterface;
|
||||
use Adldap\Configuration\DomainConfiguration;
|
||||
|
||||
class Adldap implements AdldapInterface
|
||||
{
|
||||
use DispatchesEvents;
|
||||
use LogsInformation;
|
||||
/**
|
||||
* The default provider name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $default = 'default';
|
||||
|
||||
/**
|
||||
* The connection providers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $providers = [];
|
||||
|
||||
/**
|
||||
* The events to register listeners for during initialization.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [
|
||||
'Adldap\Auth\Events\*',
|
||||
'Adldap\Query\Events\*',
|
||||
'Adldap\Models\Events\*',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $providers = [])
|
||||
{
|
||||
foreach ($providers as $name => $config) {
|
||||
$this->addProvider($config, $name);
|
||||
}
|
||||
|
||||
if ($default = key($providers)) {
|
||||
$this->setDefaultProvider($default);
|
||||
}
|
||||
|
||||
$this->initEventLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addProvider($config, $name = 'default', ConnectionInterface $connection = null)
|
||||
{
|
||||
if ($this->isValidConfig($config)) {
|
||||
$config = new Provider($config, $connection ?? new Ldap($name));
|
||||
}
|
||||
|
||||
if ($config instanceof ProviderInterface) {
|
||||
$this->providers[$name] = $config;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(
|
||||
"You must provide a configuration array or an instance of Adldap\Connections\ProviderInterface."
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given config is valid.
|
||||
*
|
||||
* @param mixed $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidConfig($config)
|
||||
{
|
||||
return is_array($config) || $config instanceof DomainConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProviders()
|
||||
{
|
||||
return $this->providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProvider($name)
|
||||
{
|
||||
if (array_key_exists($name, $this->providers)) {
|
||||
return $this->providers[$name];
|
||||
}
|
||||
|
||||
throw new AdldapException("The connection provider '$name' does not exist.");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDefaultProvider($name = 'default')
|
||||
{
|
||||
if ($this->getProvider($name) instanceof ProviderInterface) {
|
||||
$this->default = $name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultProvider()
|
||||
{
|
||||
return $this->getProvider($this->default);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeProvider($name)
|
||||
{
|
||||
unset($this->providers[$name]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function connect($name = null, $username = null, $password = null)
|
||||
{
|
||||
$provider = $name ? $this->getProvider($name) : $this->getDefaultProvider();
|
||||
|
||||
return $provider->connect($username, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
$provider = $this->getDefaultProvider();
|
||||
|
||||
if (! $provider->getConnection()->isBound()) {
|
||||
$provider->connect();
|
||||
}
|
||||
|
||||
return call_user_func_array([$provider, $method], $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the event logger.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initEventLogger()
|
||||
{
|
||||
$dispatcher = static::getEventDispatcher();
|
||||
|
||||
$logger = $this->newEventLogger();
|
||||
|
||||
// We will go through each of our event wildcards and register their listener.
|
||||
foreach ($this->listen as $event) {
|
||||
$dispatcher->listen($event, function ($eventName, $events) use ($logger) {
|
||||
foreach ($events as $event) {
|
||||
$logger->log($event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new event logger instance.
|
||||
*
|
||||
* @return EventLogger
|
||||
*/
|
||||
protected function newEventLogger()
|
||||
{
|
||||
return new EventLogger(static::getLogger());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap;
|
||||
|
||||
class AdldapException extends \Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap;
|
||||
|
||||
use Adldap\Connections\ProviderInterface;
|
||||
use Adldap\Connections\ConnectionInterface;
|
||||
|
||||
interface AdldapInterface
|
||||
{
|
||||
/**
|
||||
* Add a provider by the specified name.
|
||||
*
|
||||
* @param mixed $configuration
|
||||
* @param string $name
|
||||
* @param ConnectionInterface $connection
|
||||
*
|
||||
* @throws \InvalidArgumentException When an invalid type is given as the configuration argument.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addProvider($configuration, $name, ConnectionInterface $connection = null);
|
||||
|
||||
/**
|
||||
* Returns all of the connection providers.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getProviders();
|
||||
|
||||
/**
|
||||
* Retrieves a Provider using its specified name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws AdldapException When the specified provider does not exist.
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
public function getProvider($name);
|
||||
|
||||
/**
|
||||
* Sets the default provider.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws AdldapException When the specified provider does not exist.
|
||||
*/
|
||||
public function setDefaultProvider($name);
|
||||
|
||||
/**
|
||||
* Retrieves the first default provider.
|
||||
*
|
||||
* @throws AdldapException When no default provider exists.
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
public function getDefaultProvider();
|
||||
|
||||
/**
|
||||
* Removes a provider by the specified name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeProvider($name);
|
||||
|
||||
/**
|
||||
* Connects to the specified provider.
|
||||
*
|
||||
* If no username and password is given, then the providers
|
||||
* configured admin credentials are used.
|
||||
*
|
||||
* @param string|null $name
|
||||
* @param string|null $username
|
||||
* @param string|null $password
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
public function connect($name = null, $username = null, $password = null);
|
||||
|
||||
/**
|
||||
* Call methods upon the default provider dynamically.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $parameters);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
use Adldap\Connections\DetailedError;
|
||||
|
||||
/**
|
||||
* Class BindException.
|
||||
*
|
||||
* Thrown when binding to an LDAP connection fails.
|
||||
*/
|
||||
class BindException extends AdldapException
|
||||
{
|
||||
/**
|
||||
* The detailed LDAP error.
|
||||
*
|
||||
* @var DetailedError
|
||||
*/
|
||||
protected $detailedError;
|
||||
|
||||
/**
|
||||
* Sets the detailed error.
|
||||
*
|
||||
* @param DetailedError|null $error
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDetailedError(DetailedError $error = null)
|
||||
{
|
||||
$this->detailedError = $error;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the detailed error.
|
||||
*
|
||||
* @return DetailedError|null
|
||||
*/
|
||||
public function getDetailedError()
|
||||
{
|
||||
return $this->detailedError;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth\Events;
|
||||
|
||||
class Attempting extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth\Events;
|
||||
|
||||
class Binding extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth\Events;
|
||||
|
||||
class Bound extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth\Events;
|
||||
|
||||
use Adldap\Connections\ConnectionInterface;
|
||||
|
||||
abstract class Event
|
||||
{
|
||||
/**
|
||||
* The connection that the username and password is being bound on.
|
||||
*
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The username that is being used for binding.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* The password that is being used for binding.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*/
|
||||
public function __construct(ConnectionInterface $connection, $username, $password)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the events connection.
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication events username.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication events password.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth\Events;
|
||||
|
||||
class Failed extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth\Events;
|
||||
|
||||
class Passed extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
use Adldap\Auth\Events\Bound;
|
||||
use Adldap\Auth\Events\Failed;
|
||||
use Adldap\Auth\Events\Passed;
|
||||
use Adldap\Auth\Events\Binding;
|
||||
use Adldap\Auth\Events\Attempting;
|
||||
use Adldap\Events\DispatcherInterface;
|
||||
use Adldap\Connections\ConnectionInterface;
|
||||
use Adldap\Configuration\DomainConfiguration;
|
||||
|
||||
/**
|
||||
* Class Guard.
|
||||
*
|
||||
* Binds users to the current connection.
|
||||
*/
|
||||
class Guard implements GuardInterface
|
||||
{
|
||||
/**
|
||||
* The connection to bind to.
|
||||
*
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The domain configuration to utilize.
|
||||
*
|
||||
* @var DomainConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* The event dispatcher.
|
||||
*
|
||||
* @var DispatcherInterface
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(ConnectionInterface $connection, DomainConfiguration $configuration)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function attempt($username, $password, $bindAsUser = false)
|
||||
{
|
||||
$this->validateCredentials($username, $password);
|
||||
|
||||
$this->fireAttemptingEvent($username, $password);
|
||||
|
||||
try {
|
||||
$this->bind(
|
||||
$this->applyPrefixAndSuffix($username),
|
||||
$password
|
||||
);
|
||||
|
||||
$result = true;
|
||||
|
||||
$this->firePassedEvent($username, $password);
|
||||
} catch (BindException $e) {
|
||||
// We'll catch the BindException here to allow
|
||||
// developers to use a simple if / else
|
||||
// using the attempt method.
|
||||
$result = false;
|
||||
}
|
||||
|
||||
// If we're not allowed to bind as the user,
|
||||
// we'll rebind as administrator.
|
||||
if ($bindAsUser === false) {
|
||||
// We won't catch any BindException here so we can
|
||||
// catch rebind failures. However this shouldn't
|
||||
// occur if our credentials are correct
|
||||
// in the first place.
|
||||
$this->bindAsAdministrator();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function bind($username = null, $password = null)
|
||||
{
|
||||
$this->fireBindingEvent($username, $password);
|
||||
|
||||
try {
|
||||
if (@$this->connection->bind($username, $password) === true) {
|
||||
$this->fireBoundEvent($username, $password);
|
||||
} else {
|
||||
throw new Exception($this->connection->getLastError(), $this->connection->errNo());
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$this->fireFailedEvent($username, $password);
|
||||
|
||||
throw (new BindException($e->getMessage(), $e->getCode(), $e))
|
||||
->setDetailedError($this->connection->getDetailedError());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function bindAsAdministrator()
|
||||
{
|
||||
$this->bind(
|
||||
$this->configuration->get('username'),
|
||||
$this->configuration->get('password')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event dispatcher instance.
|
||||
*
|
||||
* @return DispatcherInterface
|
||||
*/
|
||||
public function getDispatcher()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event dispatcher instance.
|
||||
*
|
||||
* @param DispatcherInterface $dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDispatcher(DispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->events = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the prefix and suffix to the given username.
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
* @throws \Adldap\Configuration\ConfigurationException If account_suffix or account_prefix do not
|
||||
* exist in the providers domain configuration
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function applyPrefixAndSuffix($username)
|
||||
{
|
||||
$prefix = $this->configuration->get('account_prefix');
|
||||
$suffix = $this->configuration->get('account_suffix');
|
||||
|
||||
return $prefix.$username.$suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the specified username and password from being empty.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @throws PasswordRequiredException When the given password is empty.
|
||||
* @throws UsernameRequiredException When the given username is empty.
|
||||
*/
|
||||
protected function validateCredentials($username, $password)
|
||||
{
|
||||
if (empty($username)) {
|
||||
// Check for an empty username.
|
||||
throw new UsernameRequiredException('A username must be specified.');
|
||||
}
|
||||
|
||||
if (empty($password)) {
|
||||
// Check for an empty password.
|
||||
throw new PasswordRequiredException('A password must be specified.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the attempting event.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function fireAttemptingEvent($username, $password)
|
||||
{
|
||||
if (isset($this->events)) {
|
||||
$this->events->fire(new Attempting($this->connection, $username, $password));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the passed event.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function firePassedEvent($username, $password)
|
||||
{
|
||||
if (isset($this->events)) {
|
||||
$this->events->fire(new Passed($this->connection, $username, $password));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the failed event.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function fireFailedEvent($username, $password)
|
||||
{
|
||||
if (isset($this->events)) {
|
||||
$this->events->fire(new Failed($this->connection, $username, $password));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the binding event.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function fireBindingEvent($username, $password)
|
||||
{
|
||||
if (isset($this->events)) {
|
||||
$this->events->fire(new Binding($this->connection, $username, $password));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the bound event.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function fireBoundEvent($username, $password)
|
||||
{
|
||||
if (isset($this->events)) {
|
||||
$this->events->fire(new Bound($this->connection, $username, $password));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth;
|
||||
|
||||
use Adldap\Connections\ConnectionInterface;
|
||||
use Adldap\Configuration\DomainConfiguration;
|
||||
|
||||
interface GuardInterface
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param DomainConfiguration $configuration
|
||||
*/
|
||||
public function __construct(ConnectionInterface $connection, DomainConfiguration $configuration);
|
||||
|
||||
/**
|
||||
* Authenticates a user using the specified credentials.
|
||||
*
|
||||
* @param string $username The users LDAP username.
|
||||
* @param string $password The users LDAP password.
|
||||
* @param bool $bindAsUser Whether or not to bind as the user.
|
||||
*
|
||||
* @throws \Adldap\Auth\BindException When re-binding to your LDAP server fails.
|
||||
* @throws \Adldap\Auth\UsernameRequiredException When username is empty.
|
||||
* @throws \Adldap\Auth\PasswordRequiredException When password is empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function attempt($username, $password, $bindAsUser = false);
|
||||
|
||||
/**
|
||||
* Binds to the current connection using the inserted credentials.
|
||||
*
|
||||
* @param string|null $username
|
||||
* @param string|null $password
|
||||
*
|
||||
* @throws \Adldap\Auth\BindException If binding to the LDAP server fails.
|
||||
* @throws \Adldap\Connections\ConnectionException If upgrading the connection to TLS fails
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bind($username = null, $password = null);
|
||||
|
||||
/**
|
||||
* Binds to the current LDAP server using the
|
||||
* configuration administrator credentials.
|
||||
*
|
||||
* @throws \Adldap\Auth\BindException When binding as your administrator account fails.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bindAsAdministrator();
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
|
||||
class PasswordRequiredException extends AdldapException
|
||||
{
|
||||
//
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Auth;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
|
||||
class UsernameRequiredException extends AdldapException
|
||||
{
|
||||
//
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Configuration;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
|
||||
/**
|
||||
* Class ConfigurationException.
|
||||
*
|
||||
* Thrown when a configuration value does not exist, or a
|
||||
* configuration value being set is not valid.
|
||||
*/
|
||||
class ConfigurationException extends AdldapException
|
||||
{
|
||||
//
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Configuration;
|
||||
|
||||
use Adldap\Schemas\ActiveDirectory;
|
||||
use Adldap\Connections\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* Class DomainConfiguration.
|
||||
*
|
||||
* Contains an array of configuration options for a single LDAP connection.
|
||||
*/
|
||||
class DomainConfiguration
|
||||
{
|
||||
/**
|
||||
* The configuration options array.
|
||||
*
|
||||
* The default values for each key indicate the type of value it requires.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
// An array of LDAP hosts.
|
||||
'hosts' => [],
|
||||
|
||||
// The global LDAP operation timeout limit in seconds.
|
||||
'timeout' => 5,
|
||||
|
||||
// The LDAP version to utilize.
|
||||
'version' => 3,
|
||||
|
||||
// The port to use for connecting to your hosts.
|
||||
'port' => ConnectionInterface::PORT,
|
||||
|
||||
// The schema to use for your LDAP connection.
|
||||
'schema' => ActiveDirectory::class,
|
||||
|
||||
// The base distinguished name of your domain.
|
||||
'base_dn' => '',
|
||||
|
||||
// The username to connect to your hosts with.
|
||||
'username' => '',
|
||||
|
||||
// The password that is utilized with the above user.
|
||||
'password' => '',
|
||||
|
||||
// The account prefix to use when authenticating users.
|
||||
'account_prefix' => null,
|
||||
|
||||
// The account suffix to use when authenticating users.
|
||||
'account_suffix' => null,
|
||||
|
||||
// Whether or not to use SSL when connecting to your hosts.
|
||||
'use_ssl' => false,
|
||||
|
||||
// Whether or not to use TLS when connecting to your hosts.
|
||||
'use_tls' => false,
|
||||
|
||||
// Whether or not follow referrals is enabled when performing LDAP operations.
|
||||
'follow_referrals' => false,
|
||||
|
||||
// Custom LDAP options that you'd like to utilize.
|
||||
'custom_options' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @throws ConfigurationException When an option value given is an invalid type.
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
foreach ($options as $key => $value) {
|
||||
$this->set($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a configuration option.
|
||||
*
|
||||
* Throws an exception if the specified option does
|
||||
* not exist, or if it's an invalid type.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws ConfigurationException When an option value given is an invalid type.
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
if ($this->validate($key, $value)) {
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the specified configuration options.
|
||||
*
|
||||
* Throws an exception if the specified option does not exist.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @throws ConfigurationException When the option specified does not exist.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
if ($this->has($key)) {
|
||||
return $this->options[$key];
|
||||
}
|
||||
|
||||
throw new ConfigurationException("Option {$key} does not exist.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a configuration option exists.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists($key, $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the new configuration option against its
|
||||
* default value to ensure it's the correct type.
|
||||
*
|
||||
* If an invalid type is given, an exception is thrown.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws ConfigurationException When an option value given is an invalid type.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validate($key, $value)
|
||||
{
|
||||
$default = $this->get($key);
|
||||
|
||||
if (is_array($default)) {
|
||||
$validator = new Validators\ArrayValidator($key, $value);
|
||||
} elseif (is_int($default)) {
|
||||
$validator = new Validators\IntegerValidator($key, $value);
|
||||
} elseif (is_bool($default)) {
|
||||
$validator = new Validators\BooleanValidator($key, $value);
|
||||
} elseif (class_exists($default)) {
|
||||
$validator = new Validators\ClassValidator($key, $value);
|
||||
} else {
|
||||
$validator = new Validators\StringOrNullValidator($key, $value);
|
||||
}
|
||||
|
||||
return $validator->validate();
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Configuration\Validators;
|
||||
|
||||
use Adldap\Configuration\ConfigurationException;
|
||||
|
||||
/**
|
||||
* Class ArrayValidator.
|
||||
*
|
||||
* Validates that the configuration value is an array.
|
||||
*/
|
||||
class ArrayValidator extends Validator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!is_array($this->value)) {
|
||||
throw new ConfigurationException("Option {$this->key} must be an array.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Configuration\Validators;
|
||||
|
||||
use Adldap\Configuration\ConfigurationException;
|
||||
|
||||
/**
|
||||
* Class BooleanValidator.
|
||||
*
|
||||
* Validates that the configuration value is a boolean.
|
||||
*/
|
||||
class BooleanValidator extends Validator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!is_bool($this->value)) {
|
||||
throw new ConfigurationException("Option {$this->key} must be a boolean.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Configuration\Validators;
|
||||
|
||||
use Adldap\Configuration\ConfigurationException;
|
||||
|
||||
class ClassValidator extends Validator
|
||||
{
|
||||
/**
|
||||
* Validates the configuration value.
|
||||
*
|
||||
* @throws ConfigurationException When the value given fails validation.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!class_exists($this->value)) {
|
||||
throw new ConfigurationException("Option {$this->key} must be a valid class.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Configuration\Validators;
|
||||
|
||||
use Adldap\Configuration\ConfigurationException;
|
||||
|
||||
/**
|
||||
* Class IntegerValidator.
|
||||
*
|
||||
* Validates that the configuration value is an integer / number.
|
||||
*/
|
||||
class IntegerValidator extends Validator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!is_numeric($this->value)) {
|
||||
throw new ConfigurationException("Option {$this->key} must be an integer.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Configuration\Validators;
|
||||
|
||||
use Adldap\Configuration\ConfigurationException;
|
||||
|
||||
/**
|
||||
* Class StringOrNullValidator.
|
||||
*
|
||||
* Validates that the configuration value is a string or null.
|
||||
*/
|
||||
class StringOrNullValidator extends Validator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (is_string($this->value) || is_null($this->value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new ConfigurationException("Option {$this->key} must be a string or null.");
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Configuration\Validators;
|
||||
|
||||
/**
|
||||
* Class Validator.
|
||||
*
|
||||
* Validates configuration values.
|
||||
*/
|
||||
abstract class Validator
|
||||
{
|
||||
/**
|
||||
* The configuration key under validation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* The configuration value under validation.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct($key, $value)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the configuration value.
|
||||
*
|
||||
* @throws \Adldap\Configuration\ConfigurationException When the value given fails validation.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function validate();
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Connections;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
|
||||
class ConnectionException extends AdldapException
|
||||
{
|
||||
//
|
||||
}
|
||||
+539
@@ -0,0 +1,539 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Connections;
|
||||
|
||||
/**
|
||||
* The Connection interface used for making connections. Implementing
|
||||
* this interface on connection classes helps unit and functional
|
||||
* test classes that require a connection.
|
||||
*
|
||||
* Interface ConnectionInterface
|
||||
*/
|
||||
interface ConnectionInterface
|
||||
{
|
||||
/**
|
||||
* The SSL LDAP protocol string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PROTOCOL_SSL = 'ldaps://';
|
||||
|
||||
/**
|
||||
* The standard LDAP protocol string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PROTOCOL = 'ldap://';
|
||||
|
||||
/**
|
||||
* The LDAP SSL port number.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PORT_SSL = 636;
|
||||
|
||||
/**
|
||||
* The standard LDAP port number.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PORT = 389;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string|null $name The connection name.
|
||||
*/
|
||||
public function __construct($name = null);
|
||||
|
||||
/**
|
||||
* Returns true / false if the current connection instance is using SSL.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isUsingSSL();
|
||||
|
||||
/**
|
||||
* Returns true / false if the current connection instance is using TLS.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isUsingTLS();
|
||||
|
||||
/**
|
||||
* Returns true / false if the current connection is able to modify passwords.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canChangePasswords();
|
||||
|
||||
/**
|
||||
* Returns true / false if the current connection is bound.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isBound();
|
||||
|
||||
/**
|
||||
* Sets the current connection to use SSL.
|
||||
*
|
||||
* @param bool $enabled
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
public function ssl($enabled = true);
|
||||
|
||||
/**
|
||||
* Sets the current connection to use TLS.
|
||||
*
|
||||
* @param bool $enabled
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
public function tls($enabled = true);
|
||||
|
||||
/**
|
||||
* Returns the full LDAP host URL.
|
||||
*
|
||||
* Ex: ldap://192.168.1.1:386
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHost();
|
||||
|
||||
/**
|
||||
* Returns the connections name.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Get the current connection.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConnection();
|
||||
|
||||
/**
|
||||
* Retrieve the entries from a search result.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-get-entries.php
|
||||
*
|
||||
* @param $searchResult
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEntries($searchResult);
|
||||
|
||||
/**
|
||||
* Returns the number of entries from a search result.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-count-entries.php
|
||||
*
|
||||
* @param $searchResult
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countEntries($searchResult);
|
||||
|
||||
/**
|
||||
* Compare value of attribute found in entry specified with DN.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-compare.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param string $attribute
|
||||
* @param string $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function compare($dn, $attribute, $value);
|
||||
|
||||
/**
|
||||
* Retrieves the first entry from a search result.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-first-entry.php
|
||||
*
|
||||
* @param $searchResult
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFirstEntry($searchResult);
|
||||
|
||||
/**
|
||||
* Retrieves the next entry from a search result.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-next-entry.php
|
||||
*
|
||||
* @param $entry
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextEntry($entry);
|
||||
|
||||
/**
|
||||
* Retrieves the ldap entry's attributes.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-get-attributes.php
|
||||
*
|
||||
* @param $entry
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttributes($entry);
|
||||
|
||||
/**
|
||||
* Retrieve the last error on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-error.php
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastError();
|
||||
|
||||
/**
|
||||
* Return detailed information about an error.
|
||||
*
|
||||
* Returns false when there was a successful last request.
|
||||
*
|
||||
* Returns DetailedError when there was an error.
|
||||
*
|
||||
* @return DetailedError|null
|
||||
*/
|
||||
public function getDetailedError();
|
||||
|
||||
/**
|
||||
* Get all binary values from the specified result entry.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-get-values-len.php
|
||||
*
|
||||
* @param $entry
|
||||
* @param $attribute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValuesLen($entry, $attribute);
|
||||
|
||||
/**
|
||||
* Sets an option on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-set-option.php
|
||||
*
|
||||
* @param int $option
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setOption($option, $value);
|
||||
|
||||
/**
|
||||
* Sets options on the current connection.
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setOptions(array $options = []);
|
||||
|
||||
/**
|
||||
* Set a callback function to do re-binds on referral chasing.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-set-rebind-proc.php
|
||||
*
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setRebindCallback(callable $callback);
|
||||
|
||||
/**
|
||||
* Connects to the specified hostname using the specified port.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-start-tls.php
|
||||
*
|
||||
* @param string|array $hostname
|
||||
* @param int $port
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function connect($hostname = [], $port = 389);
|
||||
|
||||
/**
|
||||
* Starts a connection using TLS.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-start-tls.php
|
||||
*
|
||||
* @throws ConnectionException If starting TLS fails.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function startTLS();
|
||||
|
||||
/**
|
||||
* Binds to the current connection using the specified username and password.
|
||||
* If sasl is true, the current connection is bound using SASL.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-bind.php
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param bool $sasl
|
||||
*
|
||||
* @throws ConnectionException If starting TLS fails.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function bind($username, $password, $sasl = false);
|
||||
|
||||
/**
|
||||
* Closes the current connection.
|
||||
*
|
||||
* Returns false if no connection is present.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-close.php
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function close();
|
||||
|
||||
/**
|
||||
* Performs a search on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-search.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param string $filter
|
||||
* @param array $fields
|
||||
* @param bool $onlyAttributes
|
||||
* @param int $size
|
||||
* @param int $time
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0);
|
||||
|
||||
/**
|
||||
* Reads an entry on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-read.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param $filter
|
||||
* @param array $fields
|
||||
* @param bool $onlyAttributes
|
||||
* @param int $size
|
||||
* @param int $time
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0);
|
||||
|
||||
/**
|
||||
* Performs a single level search on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-list.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param string $filter
|
||||
* @param array $attributes
|
||||
* @param bool $onlyAttributes
|
||||
* @param int $size
|
||||
* @param int $time
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function listing($dn, $filter, array $attributes, $onlyAttributes = false, $size = 0, $time = 0);
|
||||
|
||||
/**
|
||||
* Adds an entry to the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-add.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param array $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function add($dn, array $entry);
|
||||
|
||||
/**
|
||||
* Deletes an entry on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-delete.php
|
||||
*
|
||||
* @param string $dn
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($dn);
|
||||
|
||||
/**
|
||||
* Modify the name of an entry on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-rename.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param string $newRdn
|
||||
* @param string $newParent
|
||||
* @param bool $deleteOldRdn
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false);
|
||||
|
||||
/**
|
||||
* Modifies an existing entry on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-modify.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param array $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function modify($dn, array $entry);
|
||||
|
||||
/**
|
||||
* Batch modifies an existing entry on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-modify-batch.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param array $values
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function modifyBatch($dn, array $values);
|
||||
|
||||
/**
|
||||
* Add attribute values to current attributes.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-mod-add.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param array $entry
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function modAdd($dn, array $entry);
|
||||
|
||||
/**
|
||||
* Replaces attribute values with new ones.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-mod-replace.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param array $entry
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function modReplace($dn, array $entry);
|
||||
|
||||
/**
|
||||
* Delete attribute values from current attributes.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-mod-del.php
|
||||
*
|
||||
* @param string $dn
|
||||
* @param array $entry
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function modDelete($dn, array $entry);
|
||||
|
||||
/**
|
||||
* Send LDAP pagination control.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-control-paged-result.php
|
||||
*
|
||||
* @param int $pageSize
|
||||
* @param bool $isCritical
|
||||
* @param string $cookie
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '');
|
||||
|
||||
/**
|
||||
* Retrieve the LDAP pagination cookie.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-control-paged-result-response.php
|
||||
*
|
||||
* @param $result
|
||||
* @param string $cookie
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function controlPagedResultResponse($result, &$cookie);
|
||||
|
||||
/**
|
||||
* Frees up the memory allocated internally to store the result.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.ldap-free-result.php
|
||||
*
|
||||
* @param resource $result
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function freeResult($result);
|
||||
|
||||
/**
|
||||
* Returns the error number of the last command
|
||||
* executed on the current connection.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-errno.php
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function errNo();
|
||||
|
||||
/**
|
||||
* Returns the extended error string of the last command.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtendedError();
|
||||
|
||||
/**
|
||||
* Returns the extended error hex code of the last command.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getExtendedErrorHex();
|
||||
|
||||
/**
|
||||
* Returns the extended error code of the last command.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtendedErrorCode();
|
||||
|
||||
/**
|
||||
* Returns the error string of the specified
|
||||
* error number.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.ldap-err2str.php
|
||||
*
|
||||
* @param int $number
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function err2Str($number);
|
||||
|
||||
/**
|
||||
* Return the diagnostic Message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiagnosticMessage();
|
||||
|
||||
/**
|
||||
* Extract the diagnostic code from the message.
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public function extractDiagnosticCode($message);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Connections;
|
||||
|
||||
class DetailedError
|
||||
{
|
||||
/**
|
||||
* The error code from ldap_errno.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $errorCode;
|
||||
|
||||
/**
|
||||
* The error message from ldap_error.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $errorMessage;
|
||||
|
||||
/**
|
||||
* The diagnostic message when retrieved after an ldap_error.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $diagnosticMessage;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $errorCode
|
||||
* @param string $errorMessage
|
||||
* @param string $diagnosticMessage
|
||||
*/
|
||||
public function __construct($errorCode, $errorMessage, $diagnosticMessage)
|
||||
{
|
||||
$this->errorCode = $errorCode;
|
||||
$this->errorMessage = $errorMessage;
|
||||
$this->diagnosticMessage = $diagnosticMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LDAP error code.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getErrorCode()
|
||||
{
|
||||
return $this->errorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LDAP error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage()
|
||||
{
|
||||
return $this->errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LDAP diagnostic message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiagnosticMessage()
|
||||
{
|
||||
return $this->diagnosticMessage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,545 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Connections;
|
||||
|
||||
/**
|
||||
* Class Ldap.
|
||||
*
|
||||
* A class that abstracts PHP's LDAP functions and stores the bound connection.
|
||||
*/
|
||||
class Ldap implements ConnectionInterface
|
||||
{
|
||||
/**
|
||||
* The connection name.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The LDAP host that is currently connected.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* The active LDAP connection.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The bound status of the connection.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $bound = false;
|
||||
|
||||
/**
|
||||
* Whether the connection must be bound over SSL.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $useSSL = false;
|
||||
|
||||
/**
|
||||
* Whether the connection must be bound over TLS.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $useTLS = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isUsingSSL()
|
||||
{
|
||||
return $this->useSSL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isUsingTLS()
|
||||
{
|
||||
return $this->useTLS;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isBound()
|
||||
{
|
||||
return $this->bound;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canChangePasswords()
|
||||
{
|
||||
return $this->isUsingSSL() || $this->isUsingTLS();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function ssl($enabled = true)
|
||||
{
|
||||
$this->useSSL = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tls($enabled = true)
|
||||
{
|
||||
$this->useTLS = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEntries($searchResults)
|
||||
{
|
||||
return ldap_get_entries($this->connection, $searchResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFirstEntry($searchResults)
|
||||
{
|
||||
return ldap_first_entry($this->connection, $searchResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNextEntry($entry)
|
||||
{
|
||||
return ldap_next_entry($this->connection, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttributes($entry)
|
||||
{
|
||||
return ldap_get_attributes($this->connection, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countEntries($searchResults)
|
||||
{
|
||||
return ldap_count_entries($this->connection, $searchResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compare($dn, $attribute, $value)
|
||||
{
|
||||
return ldap_compare($this->connection, $dn, $attribute, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLastError()
|
||||
{
|
||||
return ldap_error($this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDetailedError()
|
||||
{
|
||||
// If the returned error number is zero, the last LDAP operation
|
||||
// succeeded. We won't return a detailed error.
|
||||
if ($number = $this->errNo()) {
|
||||
ldap_get_option($this->connection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $message);
|
||||
|
||||
return new DetailedError($number, $this->err2Str($number), $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValuesLen($entry, $attribute)
|
||||
{
|
||||
return ldap_get_values_len($this->connection, $entry, $attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOption($option, $value)
|
||||
{
|
||||
return ldap_set_option($this->connection, $option, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOptions(array $options = [])
|
||||
{
|
||||
foreach ($options as $option => $value) {
|
||||
$this->setOption($option, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRebindCallback(callable $callback)
|
||||
{
|
||||
return ldap_set_rebind_proc($this->connection, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startTLS()
|
||||
{
|
||||
try {
|
||||
return ldap_start_tls($this->connection);
|
||||
} catch (\ErrorException $e) {
|
||||
throw new ConnectionException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function connect($hosts = [], $port = 389)
|
||||
{
|
||||
$this->host = $this->getConnectionString($hosts, $this->getProtocol(), $port);
|
||||
|
||||
// Reset the bound status if reinitializing the connection.
|
||||
$this->bound = false;
|
||||
|
||||
return $this->connection = ldap_connect($this->host);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$connection = $this->connection;
|
||||
|
||||
$result = is_resource($connection) ? ldap_close($connection) : false;
|
||||
|
||||
$this->bound = false;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0)
|
||||
{
|
||||
return ldap_search($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function listing($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0)
|
||||
{
|
||||
return ldap_list($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0)
|
||||
{
|
||||
return ldap_read($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract information from an LDAP result.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.ldap-parse-result.php
|
||||
*
|
||||
* @param resource $result
|
||||
* @param int $errorCode
|
||||
* @param string $dn
|
||||
* @param string $errorMessage
|
||||
* @param array $referrals
|
||||
* @param array $serverControls
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function parseResult($result, &$errorCode, &$dn, &$errorMessage, &$referrals, &$serverControls = [])
|
||||
{
|
||||
return $this->supportsServerControlsInMethods() && !empty($serverControls) ?
|
||||
ldap_parse_result($this->connection, $result, $errorCode, $dn, $errorMessage, $referrals, $serverControls) :
|
||||
ldap_parse_result($this->connection, $result, $errorCode, $dn, $errorMessage, $referrals);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function bind($username, $password, $sasl = false)
|
||||
{
|
||||
// Prior to binding, we will upgrade our connectivity to TLS on our current
|
||||
// connection and ensure we are not already bound before upgrading.
|
||||
// This is to prevent subsequent upgrading on several binds.
|
||||
if ($this->isUsingTLS() && !$this->isBound()) {
|
||||
$this->startTLS();
|
||||
}
|
||||
|
||||
if ($sasl) {
|
||||
return $this->bound = ldap_sasl_bind($this->connection, null, null, 'GSSAPI');
|
||||
}
|
||||
|
||||
return $this->bound = ldap_bind(
|
||||
$this->connection,
|
||||
$username,
|
||||
html_entity_decode($password)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add($dn, array $entry)
|
||||
{
|
||||
return ldap_add($this->connection, $dn, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($dn)
|
||||
{
|
||||
return ldap_delete($this->connection, $dn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false)
|
||||
{
|
||||
return ldap_rename($this->connection, $dn, $newRdn, $newParent, $deleteOldRdn);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function modify($dn, array $entry)
|
||||
{
|
||||
return ldap_modify($this->connection, $dn, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function modifyBatch($dn, array $values)
|
||||
{
|
||||
return ldap_modify_batch($this->connection, $dn, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function modAdd($dn, array $entry)
|
||||
{
|
||||
return ldap_mod_add($this->connection, $dn, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function modReplace($dn, array $entry)
|
||||
{
|
||||
return ldap_mod_replace($this->connection, $dn, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function modDelete($dn, array $entry)
|
||||
{
|
||||
return ldap_mod_del($this->connection, $dn, $entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '')
|
||||
{
|
||||
return ldap_control_paged_result($this->connection, $pageSize, $isCritical, $cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function controlPagedResultResponse($result, &$cookie)
|
||||
{
|
||||
return ldap_control_paged_result_response($this->connection, $result, $cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function freeResult($result)
|
||||
{
|
||||
return ldap_free_result($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function errNo()
|
||||
{
|
||||
return ldap_errno($this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExtendedError()
|
||||
{
|
||||
return $this->getDiagnosticMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExtendedErrorHex()
|
||||
{
|
||||
if (preg_match("/(?<=data\s).*?(?=\,)/", $this->getExtendedError(), $code)) {
|
||||
return $code[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExtendedErrorCode()
|
||||
{
|
||||
return $this->extractDiagnosticCode($this->getExtendedError());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function err2Str($number)
|
||||
{
|
||||
return ldap_err2str($number);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDiagnosticMessage()
|
||||
{
|
||||
ldap_get_option($this->connection, LDAP_OPT_ERROR_STRING, $message);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractDiagnosticCode($message)
|
||||
{
|
||||
preg_match('/^([\da-fA-F]+):/', $message, $matches);
|
||||
|
||||
return isset($matches[1]) ? $matches[1] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LDAP protocol to utilize for the current connection.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProtocol()
|
||||
{
|
||||
return $this->isUsingSSL() ? $this::PROTOCOL_SSL : $this::PROTOCOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current PHP version supports server controls.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportsServerControlsInMethods()
|
||||
{
|
||||
return version_compare(PHP_VERSION, '7.3.0') >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an LDAP connection string for each host given.
|
||||
*
|
||||
* @param string|array $hosts
|
||||
* @param string $protocol
|
||||
* @param string $port
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConnectionString($hosts, $protocol, $port)
|
||||
{
|
||||
// If we are using SSL and using the default port, we
|
||||
// will override it to use the default SSL port.
|
||||
if ($this->isUsingSSL() && $port == 389) {
|
||||
$port = self::PORT_SSL;
|
||||
}
|
||||
|
||||
// Normalize hosts into an array.
|
||||
$hosts = is_array($hosts) ? $hosts : [$hosts];
|
||||
|
||||
$hosts = array_map(function ($host) use ($protocol, $port) {
|
||||
return "{$protocol}{$host}:{$port}";
|
||||
}, $hosts);
|
||||
|
||||
return implode(' ', $hosts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Connections;
|
||||
|
||||
use Adldap\Adldap;
|
||||
use Adldap\Auth\Guard;
|
||||
use Adldap\Query\Cache;
|
||||
use InvalidArgumentException;
|
||||
use Adldap\Auth\GuardInterface;
|
||||
use Adldap\Schemas\ActiveDirectory;
|
||||
use Adldap\Schemas\SchemaInterface;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Adldap\Models\Factory as ModelFactory;
|
||||
use Adldap\Query\Factory as SearchFactory;
|
||||
use Adldap\Configuration\DomainConfiguration;
|
||||
|
||||
/**
|
||||
* Class Provider.
|
||||
*
|
||||
* Contains the LDAP connection and domain configuration to
|
||||
* instantiate factories for retrieving and creating
|
||||
* LDAP records as well as authentication (binding).
|
||||
*/
|
||||
class Provider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The providers connection.
|
||||
*
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The providers configuration.
|
||||
*
|
||||
* @var DomainConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* The providers schema.
|
||||
*
|
||||
* @var SchemaInterface
|
||||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* The providers auth guard instance.
|
||||
*
|
||||
* @var GuardInterface
|
||||
*/
|
||||
protected $guard;
|
||||
|
||||
/**
|
||||
* The providers cache instance.
|
||||
*
|
||||
* @var Cache|null
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($configuration = [], ConnectionInterface $connection = null)
|
||||
{
|
||||
$this->setConfiguration($configuration)
|
||||
->setConnection($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. Implemented in order to remain backwards compatible.
|
||||
*
|
||||
* @deprecated since v10.3.0
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConfiguration($configuration = [])
|
||||
{
|
||||
if (is_array($configuration)) {
|
||||
$configuration = new DomainConfiguration($configuration);
|
||||
}
|
||||
|
||||
if ($configuration instanceof DomainConfiguration) {
|
||||
$this->configuration = $configuration;
|
||||
|
||||
$schema = $configuration->get('schema');
|
||||
|
||||
// We will update our schema here when our configuration is set.
|
||||
$this->setSchema(new $schema());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$class = DomainConfiguration::class;
|
||||
|
||||
throw new InvalidArgumentException(
|
||||
"Configuration must be array or instance of $class"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConnection(ConnectionInterface $connection = null)
|
||||
{
|
||||
// We will create a standard connection if one isn't given.
|
||||
$this->connection = $connection ?: new Ldap();
|
||||
|
||||
// Prepare the connection.
|
||||
$this->prepareConnection();
|
||||
|
||||
// Instantiate the LDAP connection.
|
||||
$this->connection->connect(
|
||||
$this->configuration->get('hosts'),
|
||||
$this->configuration->get('port')
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSchema(SchemaInterface $schema = null)
|
||||
{
|
||||
$this->schema = $schema ?: new ActiveDirectory();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setGuard(GuardInterface $guard)
|
||||
{
|
||||
$this->guard = $guard;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache store.
|
||||
*
|
||||
* @param CacheInterface $store
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCache(CacheInterface $store)
|
||||
{
|
||||
$this->cache = new Cache($store);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfiguration()
|
||||
{
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSchema()
|
||||
{
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getGuard()
|
||||
{
|
||||
if (!$this->guard instanceof GuardInterface) {
|
||||
$this->setGuard($this->getDefaultGuard($this->connection, $this->configuration));
|
||||
}
|
||||
|
||||
return $this->guard;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultGuard(ConnectionInterface $connection, DomainConfiguration $configuration)
|
||||
{
|
||||
$guard = new Guard($connection, $configuration);
|
||||
|
||||
$guard->setDispatcher(Adldap::getEventDispatcher());
|
||||
|
||||
return $guard;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function make()
|
||||
{
|
||||
return new ModelFactory(
|
||||
$this->search()->newQuery()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
$factory = new SearchFactory(
|
||||
$this->connection,
|
||||
$this->schema,
|
||||
$this->configuration->get('base_dn')
|
||||
);
|
||||
|
||||
if ($this->cache) {
|
||||
$factory->setCache($this->cache);
|
||||
}
|
||||
|
||||
return $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
return $this->getGuard();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function connect($username = null, $password = null)
|
||||
{
|
||||
// Get the default guard instance.
|
||||
$guard = $this->getGuard();
|
||||
|
||||
if (is_null($username) && is_null($password)) {
|
||||
// If both the username and password are null, we'll connect to the server
|
||||
// using the configured administrator username and password.
|
||||
$guard->bindAsAdministrator();
|
||||
} else {
|
||||
// Bind to the server with the specified username and password otherwise.
|
||||
$guard->bind($username, $password);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the connection by setting configured parameters.
|
||||
*
|
||||
* @throws \Adldap\Configuration\ConfigurationException When configuration options requested do not exist
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareConnection()
|
||||
{
|
||||
if ($this->configuration->get('use_ssl')) {
|
||||
$this->connection->ssl();
|
||||
} elseif ($this->configuration->get('use_tls')) {
|
||||
$this->connection->tls();
|
||||
}
|
||||
|
||||
$options = array_replace(
|
||||
$this->configuration->get('custom_options'),
|
||||
[
|
||||
LDAP_OPT_PROTOCOL_VERSION => $this->configuration->get('version'),
|
||||
LDAP_OPT_NETWORK_TIMEOUT => $this->configuration->get('timeout'),
|
||||
LDAP_OPT_REFERRALS => $this->configuration->get('follow_referrals'),
|
||||
]
|
||||
);
|
||||
|
||||
$this->connection->setOptions($options);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Connections;
|
||||
|
||||
use Adldap\Auth\GuardInterface;
|
||||
use Adldap\Schemas\SchemaInterface;
|
||||
use Adldap\Configuration\DomainConfiguration;
|
||||
|
||||
interface ProviderInterface
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array|DomainConfiguration $configuration
|
||||
* @param ConnectionInterface $connection
|
||||
*/
|
||||
public function __construct($configuration, ConnectionInterface $connection);
|
||||
|
||||
/**
|
||||
* Returns the current connection instance.
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
public function getConnection();
|
||||
|
||||
/**
|
||||
* Returns the current configuration instance.
|
||||
*
|
||||
* @return DomainConfiguration
|
||||
*/
|
||||
public function getConfiguration();
|
||||
|
||||
/**
|
||||
* Returns the current Guard instance.
|
||||
*
|
||||
* @return \Adldap\Auth\Guard
|
||||
*/
|
||||
public function getGuard();
|
||||
|
||||
/**
|
||||
* Returns a new default Guard instance.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param DomainConfiguration $configuration
|
||||
*
|
||||
* @return \Adldap\Auth\Guard
|
||||
*/
|
||||
public function getDefaultGuard(ConnectionInterface $connection, DomainConfiguration $configuration);
|
||||
|
||||
/**
|
||||
* Sets the current connection.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setConnection(ConnectionInterface $connection = null);
|
||||
|
||||
/**
|
||||
* Sets the current configuration.
|
||||
*
|
||||
* @param DomainConfiguration|array $configuration
|
||||
*
|
||||
* @throws \Adldap\Configuration\ConfigurationException
|
||||
*/
|
||||
public function setConfiguration($configuration = []);
|
||||
|
||||
/**
|
||||
* Sets the current LDAP attribute schema.
|
||||
*
|
||||
* @param SchemaInterface|null $schema
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSchema(SchemaInterface $schema = null);
|
||||
|
||||
/**
|
||||
* Returns the current LDAP attribute schema.
|
||||
*
|
||||
* @return SchemaInterface
|
||||
*/
|
||||
public function getSchema();
|
||||
|
||||
/**
|
||||
* Sets the current Guard instance.
|
||||
*
|
||||
* @param GuardInterface $guard
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setGuard(GuardInterface $guard);
|
||||
|
||||
/**
|
||||
* Returns a new Model factory instance.
|
||||
*
|
||||
* @return \Adldap\Models\Factory
|
||||
*/
|
||||
public function make();
|
||||
|
||||
/**
|
||||
* Returns a new Search factory instance.
|
||||
*
|
||||
* @return \Adldap\Query\Factory
|
||||
*/
|
||||
public function search();
|
||||
|
||||
/**
|
||||
* Returns a new Auth Guard instance.
|
||||
*
|
||||
* @return \Adldap\Auth\Guard
|
||||
*/
|
||||
public function auth();
|
||||
|
||||
/**
|
||||
* Connects and Binds to the Domain Controller.
|
||||
*
|
||||
* If no username or password is specified, then the
|
||||
* configured administrator credentials are used.
|
||||
*
|
||||
* @param string|null $username
|
||||
* @param string|null $password
|
||||
*
|
||||
* @throws \Adldap\Auth\BindException If binding to the LDAP server fails.
|
||||
* @throws ConnectionException If upgrading the connection to TLS fails
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
public function connect($username = null, $password = null);
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Events;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class Dispatcher.
|
||||
*
|
||||
* Handles event listening and dispatching.
|
||||
*
|
||||
* This code was taken out of the Laravel Framework core
|
||||
* with broadcasting and queuing omitted to remove
|
||||
* an extra dependency that would be required.
|
||||
*
|
||||
* @author Taylor Otwell
|
||||
*
|
||||
* @see https://github.com/laravel/framework
|
||||
*/
|
||||
class Dispatcher implements DispatcherInterface
|
||||
{
|
||||
/**
|
||||
* The registered event listeners.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listeners = [];
|
||||
|
||||
/**
|
||||
* The wildcard listeners.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $wildcards = [];
|
||||
|
||||
/**
|
||||
* The cached wildcard listeners.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $wildcardsCache = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function listen($events, $listener)
|
||||
{
|
||||
foreach ((array) $events as $event) {
|
||||
if (strpos($event, '*') !== false) {
|
||||
$this->setupWildcardListen($event, $listener);
|
||||
} else {
|
||||
$this->listeners[$event][] = $this->makeListener($listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a wildcard listener callback.
|
||||
*
|
||||
* @param string $event
|
||||
* @param mixed $listener
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setupWildcardListen($event, $listener)
|
||||
{
|
||||
$this->wildcards[$event][] = $this->makeListener($listener, true);
|
||||
|
||||
$this->wildcardsCache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasListeners($eventName)
|
||||
{
|
||||
return isset($this->listeners[$eventName]) || isset($this->wildcards[$eventName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function until($event, $payload = [])
|
||||
{
|
||||
return $this->dispatch($event, $payload, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fire($event, $payload = [], $halt = false)
|
||||
{
|
||||
return $this->dispatch($event, $payload, $halt);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dispatch($event, $payload = [], $halt = false)
|
||||
{
|
||||
// When the given "event" is actually an object we will assume it is an event
|
||||
// object and use the class as the event name and this event itself as the
|
||||
// payload to the handler, which makes object based events quite simple.
|
||||
list($event, $payload) = $this->parseEventAndPayload(
|
||||
$event,
|
||||
$payload
|
||||
);
|
||||
|
||||
$responses = [];
|
||||
|
||||
foreach ($this->getListeners($event) as $listener) {
|
||||
$response = $listener($event, $payload);
|
||||
|
||||
// If a response is returned from the listener and event halting is enabled
|
||||
// we will just return this response, and not call the rest of the event
|
||||
// listeners. Otherwise we will add the response on the response list.
|
||||
if ($halt && !is_null($response)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// If a boolean false is returned from a listener, we will stop propagating
|
||||
// the event to any further listeners down in the chain, else we keep on
|
||||
// looping through the listeners and firing every one in our sequence.
|
||||
if ($response === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
$responses[] = $response;
|
||||
}
|
||||
|
||||
return $halt ? null : $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given event and payload and prepare them for dispatching.
|
||||
*
|
||||
* @param mixed $event
|
||||
* @param mixed $payload
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function parseEventAndPayload($event, $payload)
|
||||
{
|
||||
if (is_object($event)) {
|
||||
list($payload, $event) = [[$event], get_class($event)];
|
||||
}
|
||||
|
||||
return [$event, Arr::wrap($payload)];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getListeners($eventName)
|
||||
{
|
||||
$listeners = $this->listeners[$eventName] ?? [];
|
||||
|
||||
$listeners = array_merge(
|
||||
$listeners,
|
||||
$this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName)
|
||||
);
|
||||
|
||||
return class_exists($eventName, false)
|
||||
? $this->addInterfaceListeners($eventName, $listeners)
|
||||
: $listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wildcard listeners for the event.
|
||||
*
|
||||
* @param string $eventName
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getWildcardListeners($eventName)
|
||||
{
|
||||
$wildcards = [];
|
||||
|
||||
foreach ($this->wildcards as $key => $listeners) {
|
||||
if ($this->wildcardContainsEvent($key, $eventName)) {
|
||||
$wildcards = array_merge($wildcards, $listeners);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->wildcardsCache[$eventName] = $wildcards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the wildcard matches or contains the given event.
|
||||
*
|
||||
* This function is a direct excerpt from Laravel's Str::is().
|
||||
*
|
||||
* @param string $wildcard
|
||||
* @param string $eventName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function wildcardContainsEvent($wildcard, $eventName)
|
||||
{
|
||||
$patterns = Arr::wrap($wildcard);
|
||||
|
||||
if (empty($patterns)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
// If the given event is an exact match we can of course return true right
|
||||
// from the beginning. Otherwise, we will translate asterisks and do an
|
||||
// actual pattern match against the two strings to see if they match.
|
||||
if ($pattern == $eventName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$pattern = preg_quote($pattern, '#');
|
||||
|
||||
// Asterisks are translated into zero-or-more regular expression wildcards
|
||||
// to make it convenient to check if the strings starts with the given
|
||||
// pattern such as "library/*", making any string check convenient.
|
||||
$pattern = str_replace('\*', '.*', $pattern);
|
||||
|
||||
if (preg_match('#^'.$pattern.'\z#u', $eventName) === 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the listeners for the event's interfaces to the given array.
|
||||
*
|
||||
* @param string $eventName
|
||||
* @param array $listeners
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function addInterfaceListeners($eventName, array $listeners = [])
|
||||
{
|
||||
foreach (class_implements($eventName) as $interface) {
|
||||
if (isset($this->listeners[$interface])) {
|
||||
foreach ($this->listeners[$interface] as $names) {
|
||||
$listeners = array_merge($listeners, (array) $names);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an event listener with the dispatcher.
|
||||
*
|
||||
* @param \Closure|string $listener
|
||||
* @param bool $wildcard
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
public function makeListener($listener, $wildcard = false)
|
||||
{
|
||||
if (is_string($listener)) {
|
||||
return $this->createClassListener($listener, $wildcard);
|
||||
}
|
||||
|
||||
return function ($event, $payload) use ($listener, $wildcard) {
|
||||
if ($wildcard) {
|
||||
return $listener($event, $payload);
|
||||
}
|
||||
|
||||
return $listener(...array_values($payload));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a class based listener.
|
||||
*
|
||||
* @param string $listener
|
||||
* @param bool $wildcard
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function createClassListener($listener, $wildcard = false)
|
||||
{
|
||||
return function ($event, $payload) use ($listener, $wildcard) {
|
||||
if ($wildcard) {
|
||||
return call_user_func($this->parseListenerCallback($listener), $event, $payload);
|
||||
}
|
||||
|
||||
return call_user_func_array(
|
||||
$this->parseListenerCallback($listener),
|
||||
$payload
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the class listener into class and method.
|
||||
*
|
||||
* @param string $listener
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function parseListenerCallback($listener)
|
||||
{
|
||||
return strpos($listener, '@') !== false ?
|
||||
explode('@', $listener, 2) :
|
||||
[$listener, 'handle'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function forget($event)
|
||||
{
|
||||
if (strpos($event, '*') !== false) {
|
||||
unset($this->wildcards[$event]);
|
||||
} else {
|
||||
unset($this->listeners[$event]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Events;
|
||||
|
||||
interface DispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Register an event listener with the dispatcher.
|
||||
*
|
||||
* @param string|array $events
|
||||
* @param mixed $listener
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function listen($events, $listener);
|
||||
|
||||
/**
|
||||
* Determine if a given event has listeners.
|
||||
*
|
||||
* @param string $eventName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasListeners($eventName);
|
||||
|
||||
/**
|
||||
* Fire an event until the first non-null response is returned.
|
||||
*
|
||||
* @param string|object $event
|
||||
* @param mixed $payload
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function until($event, $payload = []);
|
||||
|
||||
/**
|
||||
* Fire an event and call the listeners.
|
||||
*
|
||||
* @param string|object $event
|
||||
* @param mixed $payload
|
||||
* @param bool $halt
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire($event, $payload = [], $halt = false);
|
||||
|
||||
/**
|
||||
* Fire an event and call the listeners.
|
||||
*
|
||||
* @param string|object $event
|
||||
* @param mixed $payload
|
||||
* @param bool $halt
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function dispatch($event, $payload = [], $halt = false);
|
||||
|
||||
/**
|
||||
* Get all of the listeners for a given event name.
|
||||
*
|
||||
* @param string $eventName
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getListeners($eventName);
|
||||
|
||||
/**
|
||||
* Remove a set of listeners from the dispatcher.
|
||||
*
|
||||
* @param string $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function forget($event);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Events;
|
||||
|
||||
trait DispatchesEvents
|
||||
{
|
||||
/**
|
||||
* The event dispatcher instance.
|
||||
*
|
||||
* @var DispatcherInterface
|
||||
*/
|
||||
protected static $dispatcher;
|
||||
|
||||
/**
|
||||
* Get the event dispatcher instance.
|
||||
*
|
||||
* @return DispatcherInterface
|
||||
*/
|
||||
public static function getEventDispatcher()
|
||||
{
|
||||
// If no event dispatcher has been set, well instantiate and
|
||||
// set one here. This will be our singleton instance.
|
||||
if (!isset(static::$dispatcher)) {
|
||||
static::setEventDispatcher(new Dispatcher());
|
||||
}
|
||||
|
||||
return static::$dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the event dispatcher instance.
|
||||
*
|
||||
* @param DispatcherInterface $dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setEventDispatcher(DispatcherInterface $dispatcher)
|
||||
{
|
||||
static::$dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset the event dispatcher instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function unsetEventDispatcher()
|
||||
{
|
||||
static::$dispatcher = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Log;
|
||||
|
||||
use ReflectionClass;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Adldap\Auth\Events\Failed;
|
||||
use Adldap\Auth\Events\Event as AuthEvent;
|
||||
use Adldap\Models\Events\Event as ModelEvent;
|
||||
use Adldap\Query\Events\QueryExecuted as QueryEvent;
|
||||
|
||||
class EventLogger
|
||||
{
|
||||
/**
|
||||
* The logger instance.
|
||||
*
|
||||
* @var LoggerInterface|null
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger = null)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given event.
|
||||
*
|
||||
* @param mixed $event
|
||||
*/
|
||||
public function log($event)
|
||||
{
|
||||
if ($event instanceof AuthEvent) {
|
||||
$this->auth($event);
|
||||
} elseif ($event instanceof ModelEvent) {
|
||||
$this->model($event);
|
||||
} elseif ($event instanceof QueryEvent) {
|
||||
$this->query($event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an authentication event.
|
||||
*
|
||||
* @param AuthEvent $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function auth(AuthEvent $event)
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$connection = $event->getConnection();
|
||||
|
||||
$message = "LDAP ({$connection->getHost()})"
|
||||
." - Connection: {$connection->getName()}"
|
||||
." - Operation: {$this->getOperationName($event)}"
|
||||
." - Username: {$event->getUsername()}";
|
||||
|
||||
$result = null;
|
||||
$type = 'info';
|
||||
|
||||
if (is_a($event, Failed::class)) {
|
||||
$type = 'warning';
|
||||
$result = " - Reason: {$connection->getLastError()}";
|
||||
}
|
||||
|
||||
$this->logger->$type($message.$result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a model event.
|
||||
*
|
||||
* @param ModelEvent $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function model(ModelEvent $event)
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$model = $event->getModel();
|
||||
|
||||
$on = get_class($model);
|
||||
|
||||
$connection = $model->getQuery()->getConnection();
|
||||
|
||||
$message = "LDAP ({$connection->getHost()})"
|
||||
." - Connection: {$connection->getName()}"
|
||||
." - Operation: {$this->getOperationName($event)}"
|
||||
." - On: {$on}"
|
||||
." - Distinguished Name: {$model->getDn()}";
|
||||
|
||||
$this->logger->info($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a query event.
|
||||
*
|
||||
* @param QueryEvent $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function query(QueryEvent $event)
|
||||
{
|
||||
if (isset($this->logger)) {
|
||||
$query = $event->getQuery();
|
||||
|
||||
$connection = $query->getConnection();
|
||||
|
||||
$selected = implode(',', $query->getSelects());
|
||||
|
||||
$message = "LDAP ({$connection->getHost()})"
|
||||
." - Connection: {$connection->getName()}"
|
||||
." - Operation: {$this->getOperationName($event)}"
|
||||
." - Base DN: {$query->getDn()}"
|
||||
." - Filter: {$query->getUnescapedQuery()}"
|
||||
." - Selected: ({$selected})"
|
||||
." - Time Elapsed: {$event->getTime()}";
|
||||
|
||||
$this->logger->info($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operational name of the given event.
|
||||
*
|
||||
* @param mixed $event
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getOperationName($event)
|
||||
{
|
||||
return (new ReflectionClass($event))->getShortName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Log;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
trait LogsInformation
|
||||
{
|
||||
/**
|
||||
* The logger instance.
|
||||
*
|
||||
* @var LoggerInterface|null
|
||||
*/
|
||||
protected static $logger;
|
||||
|
||||
/**
|
||||
* Get the logger instance.
|
||||
*
|
||||
* @return LoggerInterface|null
|
||||
*/
|
||||
public static function getLogger()
|
||||
{
|
||||
return static::$logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the logger instance.
|
||||
*
|
||||
* @param LoggerInterface $logger
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setLogger(LoggerInterface $logger)
|
||||
{
|
||||
static::$logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset the logger instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function unsetLogger()
|
||||
{
|
||||
static::$logger = null;
|
||||
}
|
||||
}
|
||||
+458
@@ -0,0 +1,458 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Attributes;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* The Account Control class.
|
||||
*
|
||||
* This class is for easily building a user account control value.
|
||||
*
|
||||
* @link https://support.microsoft.com/en-us/kb/305144
|
||||
*/
|
||||
class AccountControl
|
||||
{
|
||||
const SCRIPT = 1;
|
||||
|
||||
const ACCOUNTDISABLE = 2;
|
||||
|
||||
const HOMEDIR_REQUIRED = 8;
|
||||
|
||||
const LOCKOUT = 16;
|
||||
|
||||
const PASSWD_NOTREQD = 32;
|
||||
|
||||
const ENCRYPTED_TEXT_PWD_ALLOWED = 128;
|
||||
|
||||
const TEMP_DUPLICATE_ACCOUNT = 256;
|
||||
|
||||
const NORMAL_ACCOUNT = 512;
|
||||
|
||||
const INTERDOMAIN_TRUST_ACCOUNT = 2048;
|
||||
|
||||
const WORKSTATION_TRUST_ACCOUNT = 4096;
|
||||
|
||||
const SERVER_TRUST_ACCOUNT = 8192;
|
||||
|
||||
const DONT_EXPIRE_PASSWORD = 65536;
|
||||
|
||||
const MNS_LOGON_ACCOUNT = 131072;
|
||||
|
||||
const SMARTCARD_REQUIRED = 262144;
|
||||
|
||||
const TRUSTED_FOR_DELEGATION = 524288;
|
||||
|
||||
const NOT_DELEGATED = 1048576;
|
||||
|
||||
const USE_DES_KEY_ONLY = 2097152;
|
||||
|
||||
const DONT_REQ_PREAUTH = 4194304;
|
||||
|
||||
const PASSWORD_EXPIRED = 8388608;
|
||||
|
||||
const TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216;
|
||||
|
||||
const PARTIAL_SECRETS_ACCOUNT = 67108864;
|
||||
|
||||
/**
|
||||
* Stores the values to be added together to
|
||||
* build the user account control integer.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $values = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $flag
|
||||
*/
|
||||
public function __construct($flag = null)
|
||||
{
|
||||
if (!is_null($flag)) {
|
||||
$this->apply($flag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value when casted to string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value when casted to int.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function __toInt()
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the value to the account control values.
|
||||
*
|
||||
* @param int $value
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function add($value)
|
||||
{
|
||||
// Use the value as a key so if the same value
|
||||
// is used, it will always be overwritten
|
||||
$this->values[$value] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the value from the account control.
|
||||
*
|
||||
* @param int $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function remove($value)
|
||||
{
|
||||
unset($this->values[$value]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and apply the flag.
|
||||
*
|
||||
* @param int $flag
|
||||
*/
|
||||
public function apply($flag)
|
||||
{
|
||||
$this->setValues($this->extractFlags($flag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current AccountControl object contains the given UAC flag(s).
|
||||
*
|
||||
* @param int $flag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($flag)
|
||||
{
|
||||
// We'll extract the given flag into an array of possible flags, and
|
||||
// see if our AccountControl object contains any of them.
|
||||
$flagsUsed = array_intersect($this->extractFlags($flag), $this->values);
|
||||
|
||||
return in_array($flag, $flagsUsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* The logon script will be run.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function runLoginScript()
|
||||
{
|
||||
return $this->add(static::SCRIPT);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user account is locked.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsLocked()
|
||||
{
|
||||
return $this->add(static::LOCKOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user account is disabled.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsDisabled()
|
||||
{
|
||||
return $this->add(static::ACCOUNTDISABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an account for users whose primary account is in another domain.
|
||||
*
|
||||
* This account provides user access to this domain, but not to any domain that
|
||||
* trusts this domain. This is sometimes referred to as a local user account.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsTemporary()
|
||||
{
|
||||
return $this->add(static::TEMP_DUPLICATE_ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a default account type that represents a typical user.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsNormal()
|
||||
{
|
||||
return $this->add(static::NORMAL_ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a permit to trust an account for a system domain that trusts other domains.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsForInterdomain()
|
||||
{
|
||||
return $this->add(static::INTERDOMAIN_TRUST_ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a computer account for a computer that is running Microsoft
|
||||
* Windows NT 4.0 Workstation, Microsoft Windows NT 4.0 Server, Microsoft
|
||||
* Windows 2000 Professional, or Windows 2000 Server and is a member of this domain.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsForWorkstation()
|
||||
{
|
||||
return $this->add(static::WORKSTATION_TRUST_ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a computer account for a domain controller that is a member of this domain.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsForServer()
|
||||
{
|
||||
return $this->add(static::SERVER_TRUST_ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an MNS logon account.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsMnsLogon()
|
||||
{
|
||||
return $this->add(static::MNS_LOGON_ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Windows 2000/Windows Server 2003) This account does
|
||||
* not require Kerberos pre-authentication for logging on.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountDoesNotRequirePreAuth()
|
||||
{
|
||||
return $this->add(static::DONT_REQ_PREAUTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* When this flag is set, it forces the user to log on by using a smart card.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountRequiresSmartCard()
|
||||
{
|
||||
return $this->add(static::SMARTCARD_REQUIRED);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Windows Server 2008/Windows Server 2008 R2) The account is a read-only domain controller (RODC).
|
||||
*
|
||||
* This is a security-sensitive setting. Removing this setting from an RODC compromises security on that server.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function accountIsReadOnly()
|
||||
{
|
||||
return $this->add(static::PARTIAL_SECRETS_ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* The home folder is required.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function homeFolderIsRequired()
|
||||
{
|
||||
return $this->add(static::HOMEDIR_REQUIRED);
|
||||
}
|
||||
|
||||
/**
|
||||
* No password is required.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function passwordIsNotRequired()
|
||||
{
|
||||
return $this->add(static::PASSWD_NOTREQD);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user cannot change the password. This is a permission on the user's object.
|
||||
*
|
||||
* For information about how to programmatically set this permission, visit the following link:
|
||||
*
|
||||
* @link http://msdn2.microsoft.com/en-us/library/aa746398.aspx
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function passwordCannotBeChanged()
|
||||
{
|
||||
return $this->add(static::PASSWD_NOTREQD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the password, which should never expire on the account.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function passwordDoesNotExpire()
|
||||
{
|
||||
return $this->add(static::DONT_EXPIRE_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Windows 2000/Windows Server 2003) The user's password has expired.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function passwordIsExpired()
|
||||
{
|
||||
return $this->add(static::PASSWORD_EXPIRED);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user can send an encrypted password.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function allowEncryptedTextPassword()
|
||||
{
|
||||
return $this->add(static::ENCRYPTED_TEXT_PWD_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* When this flag is set, the service account (the user or computer account)
|
||||
* under which a service runs is trusted for Kerberos delegation.
|
||||
*
|
||||
* Any such service can impersonate a client requesting the service.
|
||||
*
|
||||
* To enable a service for Kerberos delegation, you must set this
|
||||
* flag on the userAccountControl property of the service account.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function trustForDelegation()
|
||||
{
|
||||
return $this->add(static::TRUSTED_FOR_DELEGATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Windows 2000/Windows Server 2003) The account is enabled for delegation.
|
||||
*
|
||||
* This is a security-sensitive setting. Accounts that have this option enabled
|
||||
* should be tightly controlled. This setting lets a service that runs under the
|
||||
* account assume a client's identity and authenticate as that user to other remote
|
||||
* servers on the network.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function trustToAuthForDelegation()
|
||||
{
|
||||
return $this->add(static::TRUSTED_TO_AUTH_FOR_DELEGATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* When this flag is set, the security context of the user is not delegated to a
|
||||
* service even if the service account is set as trusted for Kerberos delegation.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function doNotTrustForDelegation()
|
||||
{
|
||||
return $this->add(static::NOT_DELEGATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Windows 2000/Windows Server 2003) Restrict this principal to
|
||||
* use only Data Encryption Standard (DES) encryption types for keys.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function useDesKeyOnly()
|
||||
{
|
||||
return $this->add(static::USE_DES_KEY_ONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account control value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return array_sum($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account control flag values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the account control values.
|
||||
*
|
||||
* @param array $flags
|
||||
*/
|
||||
public function setValues(array $flags)
|
||||
{
|
||||
$this->values = $flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all possible account control flags.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllFlags()
|
||||
{
|
||||
return (new ReflectionClass(__CLASS__))->getConstants();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the given flag into an array of flags used.
|
||||
*
|
||||
* @param int $flag
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extractFlags($flag)
|
||||
{
|
||||
$flags = [];
|
||||
|
||||
for ($i = 0; $i <= 26; $i++) {
|
||||
if ((int) $flag & (1 << $i)) {
|
||||
$flags[1 << $i] = 1 << $i;
|
||||
}
|
||||
}
|
||||
|
||||
return $flags;
|
||||
}
|
||||
}
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Attributes;
|
||||
|
||||
use Adldap\Utilities;
|
||||
|
||||
class DistinguishedName
|
||||
{
|
||||
/**
|
||||
* The distinguished name components (in order of assembly).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $components = [
|
||||
'cn' => [],
|
||||
'uid' => [],
|
||||
'ou' => [],
|
||||
'dc' => [],
|
||||
'o' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param mixed $baseDn
|
||||
*/
|
||||
public function __construct($baseDn = null)
|
||||
{
|
||||
$this->setBase($baseDn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete distinguished name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete distinguished name by assembling the RDN components.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$components = [];
|
||||
|
||||
// We'll go through each component type and assemble its RDN.
|
||||
foreach ($this->components as $component => $values) {
|
||||
array_map(function ($value) use ($component, &$components) {
|
||||
// Assemble the component and escape the value.
|
||||
$components[] = sprintf('%s=%s', $component, ldap_escape($value, '', 2));
|
||||
}, $values);
|
||||
}
|
||||
|
||||
return implode(',', $components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a domain component.
|
||||
*
|
||||
* @param string $dc
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function addDc($dc)
|
||||
{
|
||||
$this->addComponent('dc', $dc);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a domain component.
|
||||
*
|
||||
* @param string $dc
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function removeDc($dc)
|
||||
{
|
||||
$this->removeComponent('dc', $dc);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an organization name.
|
||||
*
|
||||
* @param string $o
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addO($o)
|
||||
{
|
||||
$this->addComponent('o', $o);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an organization name.
|
||||
*
|
||||
* @param string $o
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function removeO($o)
|
||||
{
|
||||
$this->removeComponent('o', $o);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a user identifier.
|
||||
*
|
||||
* @param string $uid
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function addUid($uid)
|
||||
{
|
||||
$this->addComponent('uid', $uid);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a user identifier.
|
||||
*
|
||||
* @param string $uid
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function removeUid($uid)
|
||||
{
|
||||
$this->removeComponent('uid', $uid);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a common name.
|
||||
*
|
||||
* @param string $cn
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function addCn($cn)
|
||||
{
|
||||
$this->addComponent('cn', $cn);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a common name.
|
||||
*
|
||||
* @param string $cn
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function removeCn($cn)
|
||||
{
|
||||
$this->removeComponent('cn', $cn);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an organizational unit.
|
||||
*
|
||||
* @param string $ou
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function addOu($ou)
|
||||
{
|
||||
$this->addComponent('ou', $ou);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an organizational unit.
|
||||
*
|
||||
* @param string $ou
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function removeOu($ou)
|
||||
{
|
||||
$this->removeComponent('ou', $ou);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base RDN of the distinguished name.
|
||||
*
|
||||
* @param string|DistinguishedName $base
|
||||
*
|
||||
* @return DistinguishedName
|
||||
*/
|
||||
public function setBase($base)
|
||||
{
|
||||
// Typecast base to string in case we've been given
|
||||
// an instance of the distinguished name object.
|
||||
$base = (string) $base;
|
||||
|
||||
// If the base DN isn't null we'll try to explode it.
|
||||
$base = Utilities::explodeDn($base, false) ?: [];
|
||||
|
||||
// Remove the count key from the exploded distinguished name.
|
||||
unset($base['count']);
|
||||
|
||||
foreach ($base as $key => $rdn) {
|
||||
// We'll break the RDN into pieces
|
||||
$pieces = explode('=', $rdn) ?: [];
|
||||
|
||||
// If there's exactly 2 pieces, then we can work with it.
|
||||
if (count($pieces) === 2) {
|
||||
$attribute = ucfirst(strtolower($pieces[0]));
|
||||
|
||||
$method = 'add'.$attribute;
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
// We see what type of RDN it is and add each accordingly.
|
||||
call_user_func_array([$this, $method], [$pieces[1]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all components in the distinguished name.
|
||||
*
|
||||
* If a component name is given ('cn', 'dc' for example) then
|
||||
* the values of that component will be returned.
|
||||
*
|
||||
* @param string|null $component The component to retrieve values of
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getComponents($component = null)
|
||||
{
|
||||
if (is_null($component)) {
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
$this->validateComponentExists($component);
|
||||
|
||||
return $this->components[$component];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a component to the distinguished name.
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $value
|
||||
*
|
||||
* @throws \UnexpectedValueException When the given name does not exist.
|
||||
*/
|
||||
protected function addComponent($component, $value)
|
||||
{
|
||||
$this->validateComponentExists($component);
|
||||
|
||||
// We need to make sure the value we're given isn't empty before adding it into our components.
|
||||
if (!empty($value)) {
|
||||
$this->components[$component][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given value from the given component.
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $value
|
||||
*
|
||||
* @throws \UnexpectedValueException When the given component does not exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeComponent($component, $value)
|
||||
{
|
||||
$this->validateComponentExists($component);
|
||||
|
||||
$this->components[$component] = array_diff($this->components[$component], [$value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given component exists in the available components.
|
||||
*
|
||||
* @param string $component The name of the component to validate.
|
||||
*
|
||||
* @throws \UnexpectedValueException When the given component does not exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function validateComponentExists($component)
|
||||
{
|
||||
if (!array_key_exists($component, $this->components)) {
|
||||
throw new \UnexpectedValueException("The RDN component '$component' does not exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Attributes;
|
||||
|
||||
use Adldap\Utilities;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Guid
|
||||
{
|
||||
/**
|
||||
* The string GUID value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* The guid structure in order by section to parse using substr().
|
||||
*
|
||||
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
|
||||
*
|
||||
* @link https://github.com/ldaptools/ldaptools
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guidSections = [
|
||||
[[-26, 2], [-28, 2], [-30, 2], [-32, 2]],
|
||||
[[-22, 2], [-24, 2]],
|
||||
[[-18, 2], [-20, 2]],
|
||||
[[-16, 4]],
|
||||
[[-12, 12]],
|
||||
];
|
||||
|
||||
/**
|
||||
* The hexadecimal octet order based on string position.
|
||||
*
|
||||
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
|
||||
*
|
||||
* @link https://github.com/ldaptools/ldaptools
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $octetSections = [
|
||||
[6, 4, 2, 0],
|
||||
[10, 8],
|
||||
[14, 12],
|
||||
[16, 18, 20, 22, 24, 26, 28, 30],
|
||||
];
|
||||
|
||||
/**
|
||||
* Determines if the specified GUID is valid.
|
||||
*
|
||||
* @param string $guid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($guid)
|
||||
{
|
||||
return Utilities::isValidGuid($guid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
if (static::isValid($value)) {
|
||||
$this->value = $value;
|
||||
} elseif ($value = $this->binaryGuidToString($value)) {
|
||||
$this->value = $value;
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid Binary / String GUID.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string value of the GUID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string value of the SID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the binary representation of the GUID string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBinary()
|
||||
{
|
||||
$data = '';
|
||||
|
||||
$guid = str_replace('-', '', $this->value);
|
||||
|
||||
foreach ($this->octetSections as $section) {
|
||||
$data .= $this->parseSection($guid, $section, true);
|
||||
}
|
||||
|
||||
return hex2bin($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string variant of a binary GUID.
|
||||
*
|
||||
* @param string $binary
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function binaryGuidToString($binary)
|
||||
{
|
||||
return Utilities::binaryGuidToString($binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specified section of the hexadecimal string.
|
||||
*
|
||||
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
|
||||
*
|
||||
* @link https://github.com/ldaptools/ldaptools
|
||||
*
|
||||
* @param string $hex The full hex string.
|
||||
* @param array $sections An array of start and length (unless octet is true, then length is always 2).
|
||||
* @param bool $octet Whether this is for octet string form.
|
||||
*
|
||||
* @return string The concatenated sections in upper-case.
|
||||
*/
|
||||
protected function parseSection($hex, array $sections, $octet = false)
|
||||
{
|
||||
$parsedString = '';
|
||||
|
||||
foreach ($sections as $section) {
|
||||
$start = $octet ? $section : $section[0];
|
||||
|
||||
$length = $octet ? 2 : $section[1];
|
||||
|
||||
$parsedString .= substr($hex, $start, $length);
|
||||
}
|
||||
|
||||
return $parsedString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Attributes;
|
||||
|
||||
class MbString
|
||||
{
|
||||
/**
|
||||
* Get the integer value of a specific character.
|
||||
*
|
||||
* @param $string
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function ord($string)
|
||||
{
|
||||
if (self::isLoaded()) {
|
||||
$result = unpack('N', mb_convert_encoding($string, 'UCS-4BE', 'UTF-8'));
|
||||
|
||||
if (is_array($result) === true) {
|
||||
return $result[1];
|
||||
}
|
||||
}
|
||||
|
||||
return ord($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the character for a specific integer value.
|
||||
*
|
||||
* @param $int
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function chr($int)
|
||||
{
|
||||
if (self::isLoaded()) {
|
||||
return mb_convert_encoding(pack('n', $int), 'UTF-8', 'UTF-16BE');
|
||||
}
|
||||
|
||||
return chr($int);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a string into its individual characters and return it as an array.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function split($value)
|
||||
{
|
||||
return preg_split('/(?<!^)(?!$)/u', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if the given string is UTF 8.
|
||||
*
|
||||
* @param $string
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public static function isUtf8($string)
|
||||
{
|
||||
if (self::isLoaded()) {
|
||||
return mb_detect_encoding($string, 'UTF-8', $strict = true);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the mbstring extension is enabled in PHP.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isLoaded()
|
||||
{
|
||||
return extension_loaded('mbstring');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Attributes;
|
||||
|
||||
use Adldap\Utilities;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class Sid
|
||||
{
|
||||
/**
|
||||
* The string SID value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Determines if the specified SID is valid.
|
||||
*
|
||||
* @param string $sid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($sid)
|
||||
{
|
||||
return Utilities::isValidSid($sid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
if (static::isValid($value)) {
|
||||
$this->value = $value;
|
||||
} elseif ($value = $this->binarySidToString($value)) {
|
||||
$this->value = $value;
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid Binary / String SID.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string value of the SID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string value of the SID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the binary variant of the SID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBinary()
|
||||
{
|
||||
$sid = explode('-', ltrim($this->value, 'S-'));
|
||||
|
||||
$level = (int) array_shift($sid);
|
||||
|
||||
$authority = (int) array_shift($sid);
|
||||
|
||||
$subAuthorities = array_map('intval', $sid);
|
||||
|
||||
$params = array_merge(
|
||||
['C2xxNV*', $level, count($subAuthorities), $authority],
|
||||
$subAuthorities
|
||||
);
|
||||
|
||||
return call_user_func_array('pack', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string variant of a binary SID.
|
||||
*
|
||||
* @param string $binary
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function binarySidToString($binary)
|
||||
{
|
||||
return Utilities::binarySidToString($binary);
|
||||
}
|
||||
}
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Attributes;
|
||||
|
||||
class TSProperty
|
||||
{
|
||||
/**
|
||||
* Nibble control values. The first value for each is if the nibble is <= 9, otherwise the second value is used.
|
||||
*/
|
||||
const NIBBLE_CONTROL = [
|
||||
'X' => ['001011', '011010'],
|
||||
'Y' => ['001110', '011010'],
|
||||
];
|
||||
|
||||
/**
|
||||
* The nibble header.
|
||||
*/
|
||||
const NIBBLE_HEADER = '1110';
|
||||
|
||||
/**
|
||||
* Conversion factor needed for time values in the TSPropertyArray (stored in microseconds).
|
||||
*/
|
||||
const TIME_CONVERSION = 60 * 1000;
|
||||
|
||||
/**
|
||||
* A simple map to help determine how the property needs to be decoded/encoded from/to its binary value.
|
||||
*
|
||||
* There are some names that are simple repeats but have 'W' at the end. Not sure as to what that signifies. I
|
||||
* cannot find any information on them in Microsoft documentation. However, their values appear to stay in sync with
|
||||
* their non 'W' counterparts. But not doing so when manipulating the data manually does not seem to affect anything.
|
||||
* This probably needs more investigation.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $propTypes = [
|
||||
'string' => [
|
||||
'CtxWFHomeDir',
|
||||
'CtxWFHomeDirW',
|
||||
'CtxWFHomeDirDrive',
|
||||
'CtxWFHomeDirDriveW',
|
||||
'CtxInitialProgram',
|
||||
'CtxInitialProgramW',
|
||||
'CtxWFProfilePath',
|
||||
'CtxWFProfilePathW',
|
||||
'CtxWorkDirectory',
|
||||
'CtxWorkDirectoryW',
|
||||
'CtxCallbackNumber',
|
||||
],
|
||||
'time' => [
|
||||
'CtxMaxDisconnectionTime',
|
||||
'CtxMaxConnectionTime',
|
||||
'CtxMaxIdleTime',
|
||||
],
|
||||
'int' => [
|
||||
'CtxCfgFlags1',
|
||||
'CtxCfgPresent',
|
||||
'CtxKeyboardLayout',
|
||||
'CtxMinEncryptionLevel',
|
||||
'CtxNWLogonServer',
|
||||
'CtxShadow',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The property name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The property value.
|
||||
*
|
||||
* @var string|int
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* The property value type.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $valueType = 1;
|
||||
|
||||
/**
|
||||
* Pass binary TSProperty data to construct its object representation.
|
||||
*
|
||||
* @param string|null $value
|
||||
*/
|
||||
public function __construct($value = null)
|
||||
{
|
||||
if ($value) {
|
||||
$this->decode(bin2hex($value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name for the TSProperty.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return TSProperty
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name for the TSProperty.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for the TSProperty.
|
||||
*
|
||||
* @param string|int $value
|
||||
*
|
||||
* @return TSProperty
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for the TSProperty.
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the TSProperty name/value back to its binary
|
||||
* representation for the userParameters blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toBinary()
|
||||
{
|
||||
$name = bin2hex($this->name);
|
||||
|
||||
$binValue = $this->getEncodedValueForProp($this->name, $this->value);
|
||||
|
||||
$valueLen = strlen(bin2hex($binValue)) / 3;
|
||||
|
||||
$binary = hex2bin(
|
||||
$this->dec2hex(strlen($name))
|
||||
.$this->dec2hex($valueLen)
|
||||
.$this->dec2hex($this->valueType)
|
||||
.$name
|
||||
);
|
||||
|
||||
return $binary.$binValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a TSProperty blob, decode the name/value/type/etc.
|
||||
*
|
||||
* @param string $tsProperty
|
||||
*/
|
||||
protected function decode($tsProperty)
|
||||
{
|
||||
$nameLength = hexdec(substr($tsProperty, 0, 2));
|
||||
|
||||
// 1 data byte is 3 encoded bytes
|
||||
$valueLength = hexdec(substr($tsProperty, 2, 2)) * 3;
|
||||
|
||||
$this->valueType = hexdec(substr($tsProperty, 4, 2));
|
||||
$this->name = pack('H*', substr($tsProperty, 6, $nameLength));
|
||||
$this->value = $this->getDecodedValueForProp($this->name, substr($tsProperty, 6 + $nameLength, $valueLength));
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the property name/value in question, get its encoded form.
|
||||
*
|
||||
* @param string $propName
|
||||
* @param string|int $propValue
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getEncodedValueForProp($propName, $propValue)
|
||||
{
|
||||
if (in_array($propName, $this->propTypes['string'])) {
|
||||
// Simple strings are null terminated. Unsure if this is
|
||||
// needed or simply a product of how ADUC does stuff?
|
||||
$value = $this->encodePropValue($propValue."\0", true);
|
||||
} elseif (in_array($propName, $this->propTypes['time'])) {
|
||||
// Needs to be in microseconds (assuming it is in minute format)...
|
||||
$value = $this->encodePropValue($propValue * self::TIME_CONVERSION);
|
||||
} else {
|
||||
$value = $this->encodePropValue($propValue);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the property name in question, get its actual value from the binary blob value.
|
||||
*
|
||||
* @param string $propName
|
||||
* @param string $propValue
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
protected function getDecodedValueForProp($propName, $propValue)
|
||||
{
|
||||
if (in_array($propName, $this->propTypes['string'])) {
|
||||
// Strip away null terminators. I think this should
|
||||
// be desired, otherwise it just ends in confusion.
|
||||
$value = str_replace("\0", '', $this->decodePropValue($propValue, true));
|
||||
} elseif (in_array($propName, $this->propTypes['time'])) {
|
||||
// Convert from microseconds to minutes (how ADUC displays
|
||||
// it anyway, and seems the most practical).
|
||||
$value = hexdec($this->decodePropValue($propValue)) / self::TIME_CONVERSION;
|
||||
} elseif (in_array($propName, $this->propTypes['int'])) {
|
||||
$value = hexdec($this->decodePropValue($propValue));
|
||||
} else {
|
||||
$value = $this->decodePropValue($propValue);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the property by inspecting the nibbles of each blob, checking
|
||||
* the control, and adding up the results into a final value.
|
||||
*
|
||||
* @param string $hex
|
||||
* @param bool $string Whether or not this is simple string data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function decodePropValue($hex, $string = false)
|
||||
{
|
||||
$decodePropValue = '';
|
||||
|
||||
$blobs = str_split($hex, 6);
|
||||
|
||||
foreach ($blobs as $blob) {
|
||||
$bin = decbin(hexdec($blob));
|
||||
|
||||
$controlY = substr($bin, 4, 6);
|
||||
$nibbleY = substr($bin, 10, 4);
|
||||
$controlX = substr($bin, 14, 6);
|
||||
$nibbleX = substr($bin, 20, 4);
|
||||
|
||||
$byte = $this->nibbleControl($nibbleX, $controlX).$this->nibbleControl($nibbleY, $controlY);
|
||||
|
||||
if ($string) {
|
||||
$decodePropValue .= MbString::chr(bindec($byte));
|
||||
} else {
|
||||
$decodePropValue = $this->dec2hex(bindec($byte)).$decodePropValue;
|
||||
}
|
||||
}
|
||||
|
||||
return $decodePropValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encoded property value as a binary blob.
|
||||
*
|
||||
* @param string $value
|
||||
* @param bool $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function encodePropValue($value, $string = false)
|
||||
{
|
||||
// An int must be properly padded. (then split and reversed).
|
||||
// For a string, we just split the chars. This seems
|
||||
// to be the easiest way to handle UTF-8 characters
|
||||
// instead of trying to work with their hex values.
|
||||
$chars = $string ? MbString::split($value) : array_reverse(str_split($this->dec2hex($value, 8), 2));
|
||||
|
||||
$encoded = '';
|
||||
|
||||
foreach ($chars as $char) {
|
||||
// Get the bits for the char. Using this method to ensure it is fully padded.
|
||||
$bits = sprintf('%08b', $string ? MbString::ord($char) : hexdec($char));
|
||||
$nibbleX = substr($bits, 0, 4);
|
||||
$nibbleY = substr($bits, 4, 4);
|
||||
|
||||
// Construct the value with the header, high nibble, then low nibble.
|
||||
$value = self::NIBBLE_HEADER;
|
||||
|
||||
foreach (['Y' => $nibbleY, 'X' => $nibbleX] as $nibbleType => $nibble) {
|
||||
$value .= $this->getNibbleWithControl($nibbleType, $nibble);
|
||||
}
|
||||
|
||||
// Convert it back to a binary bit stream
|
||||
foreach ([0, 8, 16] as $start) {
|
||||
$encoded .= $this->packBitString(substr($value, $start, 8), 8);
|
||||
}
|
||||
}
|
||||
|
||||
return $encoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP's pack() function has no 'b' or 'B' template. This is
|
||||
* a workaround that turns a literal bit-string into a
|
||||
* packed byte-string with 8 bits per byte.
|
||||
*
|
||||
* @param string $bits
|
||||
* @param bool $len
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function packBitString($bits, $len)
|
||||
{
|
||||
$bits = substr($bits, 0, $len);
|
||||
// Pad input with zeros to next multiple of 4 above $len
|
||||
$bits = str_pad($bits, 4 * (int) (($len + 3) / 4), '0');
|
||||
|
||||
// Split input into chunks of 4 bits, convert each to hex and pack them
|
||||
$nibbles = str_split($bits, 4);
|
||||
foreach ($nibbles as $i => $nibble) {
|
||||
$nibbles[$i] = base_convert($nibble, 2, 16);
|
||||
}
|
||||
|
||||
return pack('H*', implode('', $nibbles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the control, adjust the nibble accordingly.
|
||||
*
|
||||
* @param string $nibble
|
||||
* @param string $control
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function nibbleControl($nibble, $control)
|
||||
{
|
||||
// This control stays constant for the low/high nibbles,
|
||||
// so it doesn't matter which we compare to
|
||||
if ($control == self::NIBBLE_CONTROL['X'][1]) {
|
||||
$dec = bindec($nibble);
|
||||
$dec += 9;
|
||||
$nibble = str_pad(decbin($dec), 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return $nibble;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nibble value with the control prefixed.
|
||||
*
|
||||
* If the nibble dec is <= 9, the control X equals 001011 and Y equals 001110, otherwise if the nibble dec is > 9
|
||||
* the control for X or Y equals 011010. Additionally, if the dec value of the nibble is > 9, then the nibble value
|
||||
* must be subtracted by 9 before the final value is constructed.
|
||||
*
|
||||
* @param string $nibbleType Either X or Y
|
||||
* @param string $nibble
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getNibbleWithControl($nibbleType, $nibble)
|
||||
{
|
||||
$dec = bindec($nibble);
|
||||
|
||||
if ($dec > 9) {
|
||||
$dec -= 9;
|
||||
$control = self::NIBBLE_CONTROL[$nibbleType][1];
|
||||
} else {
|
||||
$control = self::NIBBLE_CONTROL[$nibbleType][0];
|
||||
}
|
||||
|
||||
return $control.sprintf('%04d', decbin($dec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Need to make sure hex values are always an even length, so pad as needed.
|
||||
*
|
||||
* @param int $int
|
||||
* @param int $padLength The hex string must be padded to this length (with zeros).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function dec2hex($int, $padLength = 2)
|
||||
{
|
||||
return str_pad(dechex($int), $padLength, 0, STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Attributes;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class TSPropertyArray
|
||||
{
|
||||
/**
|
||||
* Represents that the TSPropertyArray data is valid.
|
||||
*/
|
||||
const VALID_SIGNATURE = 'P';
|
||||
|
||||
/**
|
||||
* The default values for the TSPropertyArray structure.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const DEFAULTS = [
|
||||
'CtxCfgPresent' => 2953518677,
|
||||
'CtxWFProfilePath' => '',
|
||||
'CtxWFProfilePathW' => '',
|
||||
'CtxWFHomeDir' => '',
|
||||
'CtxWFHomeDirW' => '',
|
||||
'CtxWFHomeDirDrive' => '',
|
||||
'CtxWFHomeDirDriveW' => '',
|
||||
'CtxShadow' => 1,
|
||||
'CtxMaxDisconnectionTime' => 0,
|
||||
'CtxMaxConnectionTime' => 0,
|
||||
'CtxMaxIdleTime' => 0,
|
||||
'CtxWorkDirectory' => '',
|
||||
'CtxWorkDirectoryW' => '',
|
||||
'CtxCfgFlags1' => 2418077696,
|
||||
'CtxInitialProgram' => '',
|
||||
'CtxInitialProgramW' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string The default data that occurs before the TSPropertyArray (CtxCfgPresent with a bunch of spaces...?)
|
||||
*/
|
||||
protected $defaultPreBinary = '43747843666750726573656e742020202020202020202020202020202020202020202020202020202020202020202020';
|
||||
|
||||
/**
|
||||
* @var TSProperty[]
|
||||
*/
|
||||
protected $tsProperty = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = self::VALID_SIGNATURE;
|
||||
|
||||
/**
|
||||
* Binary data that occurs before the TSPropertyArray data in userParameters.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $preBinary = '';
|
||||
|
||||
/**
|
||||
* Binary data that occurs after the TSPropertyArray data in userParameters.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $postBinary = '';
|
||||
|
||||
/**
|
||||
* Construct in one of the following ways:.
|
||||
*
|
||||
* - Pass an array of TSProperty key => value pairs (See DEFAULTS constant).
|
||||
* - Pass the userParameters binary value. The object representation of that will be decoded and constructed.
|
||||
* - Pass nothing and a default set of TSProperty key => value pairs will be used (See DEFAULTS constant).
|
||||
*
|
||||
* @param mixed $tsPropertyArray
|
||||
*/
|
||||
public function __construct($tsPropertyArray = null)
|
||||
{
|
||||
$this->preBinary = hex2bin($this->defaultPreBinary);
|
||||
|
||||
if (is_null($tsPropertyArray) || is_array($tsPropertyArray)) {
|
||||
$tsPropertyArray = $tsPropertyArray ?: self::DEFAULTS;
|
||||
|
||||
foreach ($tsPropertyArray as $key => $value) {
|
||||
$tsProperty = new TSProperty();
|
||||
|
||||
$this->tsProperty[$key] = $tsProperty->setName($key)->setValue($value);
|
||||
}
|
||||
} else {
|
||||
$this->decodeUserParameters($tsPropertyArray);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific TSProperty exists by its property name.
|
||||
*
|
||||
* @param string $propName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($propName)
|
||||
{
|
||||
return array_key_exists(strtolower($propName), array_change_key_case($this->tsProperty));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a TSProperty object by its property name (ie. CtxWFProfilePath).
|
||||
*
|
||||
* @param string $propName
|
||||
*
|
||||
* @return TSProperty
|
||||
*/
|
||||
public function get($propName)
|
||||
{
|
||||
$this->validateProp($propName);
|
||||
|
||||
return $this->getTsPropObj($propName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a TSProperty object. If it already exists, it will be overwritten.
|
||||
*
|
||||
* @param TSProperty $tsProperty
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function add(TSProperty $tsProperty)
|
||||
{
|
||||
$this->tsProperty[$tsProperty->getName()] = $tsProperty;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a TSProperty by its property name (ie. CtxMinEncryptionLevel).
|
||||
*
|
||||
* @param string $propName
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function remove($propName)
|
||||
{
|
||||
foreach (array_keys($this->tsProperty) as $property) {
|
||||
if (strtolower($propName) == strtolower($property)) {
|
||||
unset($this->tsProperty[$property]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for a specific TSProperty by its name.
|
||||
*
|
||||
* @param string $propName
|
||||
* @param mixed $propValue
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set($propName, $propValue)
|
||||
{
|
||||
$this->validateProp($propName);
|
||||
|
||||
$this->getTsPropObj($propName)->setValue($propValue);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full binary representation of the userParameters containing the TSPropertyArray data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toBinary()
|
||||
{
|
||||
$binary = $this->preBinary;
|
||||
|
||||
$binary .= hex2bin(str_pad(dechex(MbString::ord($this->signature)), 2, 0, STR_PAD_LEFT));
|
||||
|
||||
$binary .= hex2bin(str_pad(dechex(count($this->tsProperty)), 2, 0, STR_PAD_LEFT));
|
||||
|
||||
foreach ($this->tsProperty as $tsProperty) {
|
||||
$binary .= $tsProperty->toBinary();
|
||||
}
|
||||
|
||||
return $binary.$this->postBinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a simple associative array containing of all TSProperty names and values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
$userParameters = [];
|
||||
|
||||
foreach ($this->tsProperty as $property => $tsPropObj) {
|
||||
$userParameters[$property] = $tsPropObj->getValue();
|
||||
}
|
||||
|
||||
return $userParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all TSProperty objects.
|
||||
*
|
||||
* @return TSProperty[]
|
||||
*/
|
||||
public function getTSProperties()
|
||||
{
|
||||
return $this->tsProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given property name exists.
|
||||
*
|
||||
* @param string $propName
|
||||
*/
|
||||
protected function validateProp($propName)
|
||||
{
|
||||
if (!$this->has($propName)) {
|
||||
throw new InvalidArgumentException(sprintf('TSProperty for "%s" does not exist.', $propName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $propName
|
||||
*
|
||||
* @return TSProperty
|
||||
*/
|
||||
protected function getTsPropObj($propName)
|
||||
{
|
||||
return array_change_key_case($this->tsProperty)[strtolower($propName)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an associative array with all of the userParameters property names and values.
|
||||
*
|
||||
* @param string $userParameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function decodeUserParameters($userParameters)
|
||||
{
|
||||
$userParameters = bin2hex($userParameters);
|
||||
|
||||
// Save the 96-byte array of reserved data, so as to not ruin anything that may be stored there.
|
||||
$this->preBinary = hex2bin(substr($userParameters, 0, 96));
|
||||
// The signature is a 2-byte unicode character at the front
|
||||
$this->signature = MbString::chr(hexdec(substr($userParameters, 96, 2)));
|
||||
// This asserts the validity of the tsPropertyArray data. For some reason 'P' means valid...
|
||||
if ($this->signature != self::VALID_SIGNATURE) {
|
||||
throw new InvalidArgumentException('Invalid TSPropertyArray data');
|
||||
}
|
||||
|
||||
// The property count is a 2-byte unsigned integer indicating the number of elements for the tsPropertyArray
|
||||
// It starts at position 98. The actual variable data begins at position 100.
|
||||
$length = $this->addTSPropData(substr($userParameters, 100), hexdec(substr($userParameters, 98, 2)));
|
||||
|
||||
// Reserved data length + (count and sig length == 4) + the added lengths of the TSPropertyArray
|
||||
// This saves anything after that variable TSPropertyArray data, so as to not squash anything stored there
|
||||
if (strlen($userParameters) > (96 + 4 + $length)) {
|
||||
$this->postBinary = hex2bin(substr($userParameters, (96 + 4 + $length)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the start of TSPropertyArray hex data, and the count for the number
|
||||
* of TSProperty structures in contains, parse and split out the
|
||||
* individual TSProperty structures. Return the full length
|
||||
* of the TSPropertyArray data.
|
||||
*
|
||||
* @param string $tsPropertyArray
|
||||
* @param int $tsPropCount
|
||||
*
|
||||
* @return int The length of the data in the TSPropertyArray
|
||||
*/
|
||||
protected function addTSPropData($tsPropertyArray, $tsPropCount)
|
||||
{
|
||||
$length = 0;
|
||||
|
||||
for ($i = 0; $i < $tsPropCount; $i++) {
|
||||
// Prop length = name length + value length + type length + the space for the length data.
|
||||
$propLength = hexdec(substr($tsPropertyArray, $length, 2)) + (hexdec(substr($tsPropertyArray, $length + 2, 2)) * 3) + 6;
|
||||
|
||||
$tsProperty = new TSProperty(hex2bin(substr($tsPropertyArray, $length, $propLength)));
|
||||
|
||||
$this->tsProperty[$tsProperty->getName()] = $tsProperty;
|
||||
|
||||
$length += $propLength;
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class BatchModification.
|
||||
*
|
||||
* A utility class to assist in the creation of LDAP
|
||||
* batch modifications and ensure their validity.
|
||||
*/
|
||||
class BatchModification
|
||||
{
|
||||
/**
|
||||
* The array keys to be used in batch modifications.
|
||||
*/
|
||||
const KEY_ATTRIB = 'attrib';
|
||||
const KEY_MODTYPE = 'modtype';
|
||||
const KEY_VALUES = 'values';
|
||||
|
||||
/**
|
||||
* The original value of the attribute before modification.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
protected $original = null;
|
||||
|
||||
/**
|
||||
* The attribute of the modification.
|
||||
*
|
||||
* @var int|string
|
||||
*/
|
||||
protected $attribute;
|
||||
|
||||
/**
|
||||
* The values of the modification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $values = [];
|
||||
|
||||
/**
|
||||
* The modtype integer of the batch modification.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string|null $attribute
|
||||
* @param string|int|null $type
|
||||
* @param array $values
|
||||
*/
|
||||
public function __construct($attribute = null, $type = null, $values = [])
|
||||
{
|
||||
$this->setAttribute($attribute)
|
||||
->setType($type)
|
||||
->setValues($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the original value of the attribute before modification.
|
||||
*
|
||||
* @param mixed $original
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOriginal($original = null)
|
||||
{
|
||||
$this->original = $original;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original value of the attribute before modification.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOriginal()
|
||||
{
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the attribute of the modification.
|
||||
*
|
||||
* @param string $attribute
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($attribute)
|
||||
{
|
||||
$this->attribute = $attribute;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attribute of the modification.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAttribute()
|
||||
{
|
||||
return $this->attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the values of the modification.
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValues(array $values = [])
|
||||
{
|
||||
$this->values = array_map(function ($value) {
|
||||
// We need to make sure all values given to a batch modification are
|
||||
// strings, otherwise we'll receive an LDAP exception when
|
||||
// we try to process the modification.
|
||||
return (string) $value;
|
||||
}, $values);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the modification.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the modification.
|
||||
*
|
||||
* @param int|null $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type = null)
|
||||
{
|
||||
if (!is_null($type) && !$this->isValidType($type)) {
|
||||
throw new InvalidArgumentException('Given batch modification type is invalid.');
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the modification.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the batch modification
|
||||
* is valid in its current state.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return !is_null($this->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the type of modification automatically
|
||||
* based on the current and original values.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$filtered = array_diff(
|
||||
array_map('trim', $this->values),
|
||||
['']
|
||||
);
|
||||
|
||||
if (is_null($this->original)) {
|
||||
// If the original value is null, we'll assume
|
||||
// that the attribute doesn't exist yet.
|
||||
if (!empty($filtered)) {
|
||||
// If the filtered array is not empty, we can
|
||||
// assume we're looking to add values
|
||||
// to the current attribute.
|
||||
$this->setType(LDAP_MODIFY_BATCH_ADD);
|
||||
}
|
||||
|
||||
// If the filtered array is empty and there is no original
|
||||
// value, then we can ignore this attribute since
|
||||
// we can't push null values to the server.
|
||||
} else {
|
||||
if (empty($filtered)) {
|
||||
// If there's an original value and the array is
|
||||
// empty then we can assume we are looking
|
||||
// to completely remove all values
|
||||
// of the current attribute.
|
||||
$this->setType(LDAP_MODIFY_BATCH_REMOVE_ALL);
|
||||
} else {
|
||||
// If the array isn't empty then we can assume
|
||||
// we're looking to replace all attributes.
|
||||
$this->setType(LDAP_MODIFY_BATCH_REPLACE);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built batch modification array.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
switch ($this->type) {
|
||||
case LDAP_MODIFY_BATCH_REMOVE_ALL:
|
||||
// A values key cannot be provided when
|
||||
// a remove all type is selected.
|
||||
return [
|
||||
static::KEY_ATTRIB => $this->attribute,
|
||||
static::KEY_MODTYPE => $this->type,
|
||||
];
|
||||
case LDAP_MODIFY_BATCH_REMOVE:
|
||||
// Fallthrough.
|
||||
case LDAP_MODIFY_BATCH_ADD:
|
||||
// Fallthrough.
|
||||
case LDAP_MODIFY_BATCH_REPLACE:
|
||||
return [
|
||||
static::KEY_ATTRIB => $this->attribute,
|
||||
static::KEY_MODTYPE => $this->type,
|
||||
static::KEY_VALUES => $this->values,
|
||||
];
|
||||
default:
|
||||
// If the modtype isn't recognized, we'll return null.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given modtype is valid.
|
||||
*
|
||||
* @param int $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidType($type)
|
||||
{
|
||||
return in_array($type, [
|
||||
LDAP_MODIFY_BATCH_REMOVE_ALL,
|
||||
LDAP_MODIFY_BATCH_REMOVE,
|
||||
LDAP_MODIFY_BATCH_REPLACE,
|
||||
LDAP_MODIFY_BATCH_ADD,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
/**
|
||||
* Class Computer.
|
||||
*
|
||||
* Represents an LDAP computer / server.
|
||||
*/
|
||||
class Computer extends Entry
|
||||
{
|
||||
use Concerns\HasMemberOf;
|
||||
use Concerns\HasDescription;
|
||||
use Concerns\HasLastLogonAndLogOff;
|
||||
use Concerns\HasUserAccountControl;
|
||||
use Concerns\HasCriticalSystemObject;
|
||||
|
||||
/**
|
||||
* Returns the computers operating system.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679076(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOperatingSystem()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->operatingSystem());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the computers operating system version.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679079(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOperatingSystemVersion()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->operatingSystemVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the computers operating system service pack.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679078(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOperatingSystemServicePack()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->operatingSystemServicePack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the computers DNS host name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDnsHostName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->dnsHostName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the computers bad password time.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675243(v=vs.85).aspx
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getBadPasswordTime()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->badPasswordTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the computers account expiry date.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675098(v=vs.85).aspx
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAccountExpiry()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->accountExpires());
|
||||
}
|
||||
}
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Concerns;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
trait HasAttributes
|
||||
{
|
||||
/**
|
||||
* The default output date format for all time related methods.
|
||||
*
|
||||
* Default format is suited for MySQL timestamps.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $dateFormat = 'Y-m-d H:i:s';
|
||||
|
||||
/**
|
||||
* The format that is used to convert timestamps to unix timestamps.
|
||||
*
|
||||
* Currently set for compatibility with Active Directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $timestampFormat = 'YmdHis.0Z';
|
||||
|
||||
/**
|
||||
* The models attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* The models original attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $original = [];
|
||||
|
||||
/**
|
||||
* Dynamically retrieve attributes on the object.
|
||||
*
|
||||
* @param mixed $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->getAttribute($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically set attributes on the object.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
return $this->setAttribute($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes the models original attributes
|
||||
* with the model's current attributes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function syncOriginal()
|
||||
{
|
||||
$this->original = $this->attributes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the models attribute with the specified key.
|
||||
*
|
||||
* If a sub-key is specified, it will try and
|
||||
* retrieve it from the parent keys array.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @param int|string $subKey
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute($key, $subKey = null)
|
||||
{
|
||||
if (!$key) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We'll normalize the given key to prevent case sensitivity issues.
|
||||
$key = $this->normalizeAttributeKey($key);
|
||||
|
||||
if (is_null($subKey) && $this->hasAttribute($key)) {
|
||||
return $this->attributes[$key];
|
||||
} elseif ($this->hasAttribute($key, $subKey)) {
|
||||
return $this->attributes[$key][$subKey];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first attribute by the specified key.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFirstAttribute($key)
|
||||
{
|
||||
return $this->getAttribute($key, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the models attributes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the entry with the supplied attributes.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fill(array $attributes = [])
|
||||
{
|
||||
foreach ($attributes as $key => $value) {
|
||||
$this->setAttribute($key, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attributes value by the specified key and sub-key.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @param mixed $value
|
||||
* @param int|string $subKey
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($key, $value, $subKey = null)
|
||||
{
|
||||
// Normalize key.
|
||||
$key = $this->normalizeAttributeKey($key);
|
||||
|
||||
// If the key is equal to 'dn', we'll automatically
|
||||
// change it to the full attribute name.
|
||||
$key = ($key == 'dn' ? $this->schema->distinguishedName() : $key);
|
||||
|
||||
if (is_null($subKey)) {
|
||||
// We need to ensure all attributes are set as arrays so all
|
||||
// of our model methods retrieve attributes correctly.
|
||||
$this->attributes[$key] = is_array($value) ? $value : [$value];
|
||||
} else {
|
||||
$this->attributes[$key][$subKey] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the first attributes value by the specified key.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFirstAttribute($key, $value)
|
||||
{
|
||||
return $this->setAttribute($key, $value, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the attributes property.
|
||||
*
|
||||
* Used when constructing an existing LDAP record.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRawAttributes(array $attributes = [])
|
||||
{
|
||||
// We'll filter out those annoying 'count' keys returned with LDAP results,
|
||||
// and lowercase all root array keys to prevent any casing issues.
|
||||
$this->attributes = array_change_key_case($this->filterRawAttributes($attributes), CASE_LOWER);
|
||||
|
||||
// We'll pull out the distinguished name from our raw attributes
|
||||
// and set it into our attributes array with the full attribute
|
||||
// definition. This allows us to normalize distinguished
|
||||
// names across different LDAP variants.
|
||||
if ($dn = Arr::get($attributes, 'dn')) {
|
||||
// In some LDAP variants, the distinguished
|
||||
// name is returned as an array.
|
||||
if (is_array($dn)) {
|
||||
$dn = Arr::first($dn);
|
||||
}
|
||||
|
||||
$this->setDistinguishedName($dn);
|
||||
}
|
||||
|
||||
$this->syncOriginal();
|
||||
|
||||
// Set exists to true since raw attributes are only
|
||||
// set in the case of attributes being loaded by
|
||||
// query results.
|
||||
$this->exists = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the count key recursively from raw LDAP attributes.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param array|string $keys
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filterRawAttributes(array $attributes = [], $keys = ['count', 'dn'])
|
||||
{
|
||||
$attributes = Arr::except($attributes, $keys);
|
||||
|
||||
array_walk($attributes, function (&$value) use ($keys) {
|
||||
$value = is_array($value) ?
|
||||
$this->filterRawAttributes($value, $keys) :
|
||||
$value;
|
||||
});
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true / false if the specified attribute
|
||||
* exists in the attributes array.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @param int|string $subKey
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAttribute($key, $subKey = null)
|
||||
{
|
||||
// Normalize key.
|
||||
$key = $this->normalizeAttributeKey($key);
|
||||
|
||||
if (is_null($subKey)) {
|
||||
return Arr::has($this->attributes, $key);
|
||||
}
|
||||
|
||||
return Arr::has($this->attributes, "$key.$subKey");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of attributes inside
|
||||
* the attributes property.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countAttributes()
|
||||
{
|
||||
return count($this->getAttributes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the models original attributes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOriginal()
|
||||
{
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attributes that have been changed since last sync.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDirty()
|
||||
{
|
||||
$dirty = [];
|
||||
|
||||
foreach ($this->attributes as $key => $value) {
|
||||
if (!$this->originalIsEquivalent($key)) {
|
||||
// We need to reset the array's indices using array_values due to
|
||||
// LDAP requiring consecutive indices (0, 1, 2 etc.)
|
||||
$dirty[$key] = array_values($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized attribute key.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function normalizeAttributeKey($key)
|
||||
{
|
||||
return strtolower($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the new and old values for a given key are equivalent.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function originalIsEquivalent($key)
|
||||
{
|
||||
if (!array_key_exists($key, $this->original)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$current = $this->attributes[$key];
|
||||
|
||||
$original = $this->original[$key];
|
||||
|
||||
if ($current === $original) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return is_numeric($current) &&
|
||||
is_numeric($original) &&
|
||||
strcmp((string) $current, (string) $original) === 0;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Concerns;
|
||||
|
||||
trait HasCriticalSystemObject
|
||||
{
|
||||
/**
|
||||
* Returns true / false if the entry is a critical system object.
|
||||
*
|
||||
* @return null|bool
|
||||
*/
|
||||
public function isCriticalSystemObject()
|
||||
{
|
||||
$attribute = $this->getFirstAttribute($this->schema->isCriticalSystemObject());
|
||||
|
||||
return $this->convertStringToBool($attribute);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Concerns;
|
||||
|
||||
trait HasDescription
|
||||
{
|
||||
/**
|
||||
* Returns the models's description.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675492(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->description());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the models's description.
|
||||
*
|
||||
* @param string $description
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->description(), $description);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Concerns;
|
||||
|
||||
use Adldap\Adldap;
|
||||
use Adldap\Models\Events\Event;
|
||||
|
||||
trait HasEvents
|
||||
{
|
||||
/**
|
||||
* Fires the specified model event.
|
||||
*
|
||||
* @param Event $event
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fireModelEvent(Event $event)
|
||||
{
|
||||
return Adldap::getEventDispatcher()->fire($event);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Concerns;
|
||||
|
||||
trait HasLastLogonAndLogOff
|
||||
{
|
||||
/**
|
||||
* Returns the models's last log off date.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms676822(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastLogOff()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->lastLogOff());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the models's last log on date.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms676823(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastLogon()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->lastLogOn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the models's last log on timestamp.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms676824(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastLogonTimestamp()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->lastLogOnTimestamp());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Concerns;
|
||||
|
||||
use Adldap\Utilities;
|
||||
use Adldap\Models\User;
|
||||
use Adldap\Models\Group;
|
||||
use Adldap\Query\Collection;
|
||||
|
||||
trait HasMemberOf
|
||||
{
|
||||
/**
|
||||
* Returns an array of distinguished names of groups that the current model belongs to.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms677099(v=vs.85).aspx
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMemberOf()
|
||||
{
|
||||
$dns = $this->getAttribute($this->schema->memberOf());
|
||||
|
||||
// Normalize returned distinguished names if the attribute is null.
|
||||
return is_array($dns) ? $dns : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current model to the specified group.
|
||||
*
|
||||
* @param string|Group $group
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function addGroup($group)
|
||||
{
|
||||
if (is_string($group)) {
|
||||
// If the group is a string, we'll assume the dev is passing
|
||||
// in a DN string of the group. We'll try to locate it.
|
||||
$group = $this->query->newInstance()->findByDn($group);
|
||||
}
|
||||
|
||||
if ($group instanceof Group) {
|
||||
// If the group is Group model instance, we can
|
||||
// add the current models DN to the group.
|
||||
return $group->addMember($this->getDn());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current model from the specified group.
|
||||
*
|
||||
* @param string|Group $group
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeGroup($group)
|
||||
{
|
||||
if (is_string($group)) {
|
||||
// If the group is a string, we'll assume the dev is passing
|
||||
// in a DN string of the group. We'll try to locate it.
|
||||
$group = $this->query->newInstance()->findByDn($group);
|
||||
}
|
||||
|
||||
if ($group instanceof Group) {
|
||||
// If the group is Group model instance, we can
|
||||
// remove the current models DN from the group.
|
||||
return $group->removeMember($this->getDn());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current model from all groups.
|
||||
*
|
||||
* @return array The group distinguished names that were successfully removed
|
||||
*/
|
||||
public function removeAllGroups()
|
||||
{
|
||||
$removed = [];
|
||||
|
||||
foreach ($this->getMemberOf() as $group) {
|
||||
if ($this->removeGroup($group)) {
|
||||
$removed[] = $group;
|
||||
}
|
||||
}
|
||||
|
||||
return $removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the models groups that it is apart of.
|
||||
*
|
||||
* If a recursive option is given, groups of groups
|
||||
* are retrieved and then merged with
|
||||
* the resulting collection.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms677099(v=vs.85).aspx
|
||||
*
|
||||
* @param array $fields
|
||||
* @param bool $recursive
|
||||
* @param array $visited
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getGroups(array $fields = ['*'], $recursive = false, array $visited = [])
|
||||
{
|
||||
if (!in_array($this->schema->memberOf(), $fields)) {
|
||||
// We want to make sure that we always select the memberof
|
||||
// field in case developers want recursive members.
|
||||
$fields = array_merge($fields, [$this->schema->memberOf()]);
|
||||
}
|
||||
|
||||
$groups = $this->getGroupsByNames($this->getMemberOf(), $fields);
|
||||
|
||||
// We need to check if we're working with a User model. Only users
|
||||
// contain a primary group. If we are, we'll merge the users
|
||||
// primary group into the resulting collection.
|
||||
if ($this instanceof User && $primary = $this->getPrimaryGroup()) {
|
||||
$groups->push($primary);
|
||||
}
|
||||
|
||||
// If recursive results are requested, we'll ask each group
|
||||
// for their groups, and merge the resulting collection.
|
||||
if ($recursive) {
|
||||
/** @var Group $group */
|
||||
foreach ($groups as $group) {
|
||||
// We need to validate that we haven't already queried
|
||||
// for this group's members so we don't allow
|
||||
// infinite recursion in case of circular
|
||||
// group dependencies in LDAP.
|
||||
if (!in_array($group->getDn(), $visited)) {
|
||||
$visited[] = $group->getDn();
|
||||
|
||||
$members = $group->getGroups($fields, $recursive, $visited);
|
||||
|
||||
/** @var Group $member */
|
||||
foreach ($members as $member) {
|
||||
$visited[] = $member->getDn();
|
||||
}
|
||||
|
||||
$groups = $groups->merge($members);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the models groups names in a single dimension array.
|
||||
*
|
||||
* If a recursive option is given, groups of groups
|
||||
* are retrieved and then merged with
|
||||
* the resulting collection.
|
||||
*
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupNames($recursive = false)
|
||||
{
|
||||
$fields = [$this->schema->commonName(), $this->schema->memberOf()];
|
||||
|
||||
$names = $this->getGroups($fields, $recursive)->map(function (Group $group) {
|
||||
return $group->getCommonName();
|
||||
})->toArray();
|
||||
|
||||
return array_unique($names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current model is a member of the specified group(s).
|
||||
*
|
||||
* @param mixed $group
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inGroup($group, $recursive = false)
|
||||
{
|
||||
$memberOf = $this->getGroups(['cn'], $recursive);
|
||||
|
||||
if ($group instanceof Collection) {
|
||||
// If we've been given a collection then we'll convert
|
||||
// it to an array to normalize the value.
|
||||
$group = $group->toArray();
|
||||
}
|
||||
|
||||
$groups = is_array($group) ? $group : [$group];
|
||||
|
||||
foreach ($groups as $group) {
|
||||
// We need to iterate through each given group that the
|
||||
// model must be apart of, then go through the models
|
||||
// actual groups and perform validation.
|
||||
$exists = $memberOf->filter(function (Group $parent) use ($group) {
|
||||
return $this->groupIsParent($group, $parent);
|
||||
})->count() !== 0;
|
||||
|
||||
if (!$exists) {
|
||||
// If the current group isn't at all contained
|
||||
// in the memberOf collection, we'll
|
||||
// return false here.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves groups by their distinguished name.
|
||||
*
|
||||
* @param array $dns
|
||||
* @param array $fields
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function getGroupsByNames(array $dns = [], $fields = [])
|
||||
{
|
||||
$query = $this->query->newInstance();
|
||||
|
||||
return $query->newCollection($dns)->map(function ($dn) use ($query, $fields) {
|
||||
return $query->select($fields)->clearFilters()->findByDn($dn);
|
||||
})->filter(function ($group) {
|
||||
return $group instanceof Group;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the specified group is the given parent instance.
|
||||
*
|
||||
* @param Group|string $group
|
||||
* @param Group $parent
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function groupIsParent($group, Group $parent)
|
||||
{
|
||||
if ($group instanceof Group) {
|
||||
// We've been given a group instance, we'll compare their DNs.
|
||||
return $parent->getDn() === $group->getDn();
|
||||
}
|
||||
|
||||
if (Utilities::explodeDn($group)) {
|
||||
// We've been given a DN, we'll compare it to the parents.
|
||||
return $parent->getDn() === $group;
|
||||
}
|
||||
|
||||
if (!empty($group)) {
|
||||
// We've been given just a string, we'll
|
||||
// compare it to the parents name.
|
||||
return $parent->getCommonName() === $group;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Concerns;
|
||||
|
||||
use Adldap\Models\Attributes\AccountControl;
|
||||
|
||||
trait HasUserAccountControl
|
||||
{
|
||||
/**
|
||||
* Returns the users user account control integer.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserAccountControl()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->userAccountControl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users user account control as an AccountControl object.
|
||||
*
|
||||
* @return AccountControl
|
||||
*/
|
||||
public function getUserAccountControlObject()
|
||||
{
|
||||
return new AccountControl($this->getUserAccountControl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users account control property.
|
||||
*
|
||||
* @param int|string|AccountControl $accountControl
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUserAccountControl($accountControl)
|
||||
{
|
||||
return $this->setAttribute($this->schema->userAccountControl(), (string) $accountControl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the user is disabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDisabled()
|
||||
{
|
||||
return ($this->getUserAccountControl() & AccountControl::ACCOUNTDISABLE) === AccountControl::ACCOUNTDISABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the user is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->getUserAccountControl() === null ? false : !$this->isDisabled();
|
||||
}
|
||||
}
|
||||
+453
@@ -0,0 +1,453 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Concerns;
|
||||
|
||||
trait HasUserProperties
|
||||
{
|
||||
/**
|
||||
* Returns the users country.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCountry()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->country());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users country.
|
||||
*
|
||||
* @param string $country
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCountry($country)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->country(), $country);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users department.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675490(v=vs.85).aspx
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDepartment()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->department());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users department.
|
||||
*
|
||||
* @param string $department
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDepartment($department)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->department(), $department);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users email address.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms676855(v=vs.85).aspx
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->email());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users email.
|
||||
*
|
||||
* Keep in mind this will remove all other
|
||||
* email addresses the user currently has.
|
||||
*
|
||||
* @param string $email
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEmail($email)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->email(), $email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users facsimile number.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675675(v=vs.85).aspx
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFacsimileNumber()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->facsimile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users facsimile number.
|
||||
*
|
||||
* @param string $number
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFacsimileNumber($number)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->facsimile(), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users first name.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675719(v=vs.85).aspx
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFirstName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->firstName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users first name.
|
||||
*
|
||||
* @param string $firstName
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFirstName($firstName)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->firstName(), $firstName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users initials.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getInitials()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->initials());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users initials.
|
||||
*
|
||||
* @param string $initials
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setInitials($initials)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->initials(), $initials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users IP Phone.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getIpPhone()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->ipPhone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users IP phone.
|
||||
*
|
||||
* @param string $ip
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIpPhone($ip)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->ipPhone(), $ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users last name.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679872(v=vs.85).aspx
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLastName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->lastName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users last name.
|
||||
*
|
||||
* @param string $lastName
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLastName($lastName)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->lastName(), $lastName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users postal code.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPostalCode()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->postalCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users postal code.
|
||||
*
|
||||
* @param string $postalCode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPostalCode($postalCode)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->postalCode(), $postalCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the users post office box.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPostOfficeBox()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->postOfficeBox());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users post office box.
|
||||
*
|
||||
* @param string|int $box
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPostOfficeBox($box)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->postOfficeBox(), $box);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users proxy addresses.
|
||||
*
|
||||
* This will remove all proxy addresses on the user and insert the specified addresses.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679424(v=vs.85).aspx
|
||||
*
|
||||
* @param array $addresses
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProxyAddresses(array $addresses = [])
|
||||
{
|
||||
return $this->setAttribute($this->schema->proxyAddresses(), $addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add's a single proxy address to the user.
|
||||
*
|
||||
* @param string $address
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addProxyAddress($address)
|
||||
{
|
||||
$addresses = $this->getProxyAddresses();
|
||||
|
||||
$addresses[] = $address;
|
||||
|
||||
return $this->setAttribute($this->schema->proxyAddresses(), $addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users proxy addresses.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679424(v=vs.85).aspx
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getProxyAddresses()
|
||||
{
|
||||
return $this->getAttribute($this->schema->proxyAddresses()) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users street address.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getStreetAddress()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->streetAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users street address.
|
||||
*
|
||||
* @param string $address
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setStreetAddress($address)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->streetAddress(), $address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users title.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms680037(v=vs.85).aspx
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->title());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users title.
|
||||
*
|
||||
* @param string $title
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->title(), $title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users telephone number.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms680027(v=vs.85).aspx
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTelephoneNumber()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->telephone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users telephone number.
|
||||
*
|
||||
* @param string $number
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTelephoneNumber($number)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->telephone(), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users primary mobile phone number.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMobileNumber()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->mobile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users primary mobile phone number.
|
||||
*
|
||||
* @param string $number
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMobileNumber($number)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->mobile(), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users secondary (other) mobile phone number.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOtherMobileNumber()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->otherMobile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users secondary (other) mobile phone number.
|
||||
*
|
||||
* @param string $number
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOtherMobileNumber($number)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->otherMobile(), $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users other mailbox attribute.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679091(v=vs.85).aspx
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOtherMailbox()
|
||||
{
|
||||
return $this->getAttribute($this->schema->otherMailbox());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the users other mailboxes.
|
||||
*
|
||||
* @param array $otherMailbox
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOtherMailbox($otherMailbox = [])
|
||||
{
|
||||
return $this->setAttribute($this->schema->otherMailbox(), $otherMailbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distinguished name of the user who is the user's manager.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getManager()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->manager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the distinguished name of the user who is the user's manager.
|
||||
*
|
||||
* @param string $managerDn
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setManager($managerDn)
|
||||
{
|
||||
return $this->setFirstAttribute($this->schema->manager(), $managerDn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users mail nickname.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMailNickname()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->emailNickname());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
/**
|
||||
* Class Contact.
|
||||
*
|
||||
* Represents an LDAP contact.
|
||||
*/
|
||||
class Contact extends Entry
|
||||
{
|
||||
use Concerns\HasMemberOf;
|
||||
use Concerns\HasUserProperties;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
/**
|
||||
* Class Container.
|
||||
*
|
||||
* Represents an LDAP container.
|
||||
*/
|
||||
class Container extends Entry
|
||||
{
|
||||
use Concerns\HasDescription;
|
||||
use Concerns\HasCriticalSystemObject;
|
||||
|
||||
/**
|
||||
* Returns the containers system flags integer.
|
||||
*
|
||||
* An integer value that contains flags that define additional properties of the class.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms680022(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSystemFlags()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->systemFlags());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
/**
|
||||
* Class Entry.
|
||||
*
|
||||
* Represents an LDAP record that could not be identified as another type of model.
|
||||
*/
|
||||
class Entry extends Model
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
class Created extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
class Creating extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
class Deleted extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
class Deleting extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
use Adldap\Models\Model;
|
||||
|
||||
abstract class Event
|
||||
{
|
||||
/**
|
||||
* The model that the event is being triggered on.
|
||||
*
|
||||
* @var Model
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Model $model
|
||||
*/
|
||||
public function __construct(Model $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model that generated the event.
|
||||
*
|
||||
* @return Model
|
||||
*/
|
||||
public function getModel()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
class Saved extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
class Saving extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
class Updated extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models\Events;
|
||||
|
||||
class Updating extends Event
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
use Adldap\Query\Builder;
|
||||
use Adldap\Schemas\ActiveDirectory;
|
||||
use Adldap\Schemas\SchemaInterface;
|
||||
|
||||
/**
|
||||
* Class Factory.
|
||||
*
|
||||
* Creates new LDAP models.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
/**
|
||||
* The LDAP query builder.
|
||||
*
|
||||
* @var Builder
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* The LDAP schema.
|
||||
*
|
||||
* @var SchemaInterface
|
||||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Builder $builder
|
||||
*/
|
||||
public function __construct(Builder $builder)
|
||||
{
|
||||
$this->setQuery($builder)
|
||||
->setSchema($builder->getSchema());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current query builder.
|
||||
*
|
||||
* @param Builder $builder
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setQuery(Builder $builder)
|
||||
{
|
||||
$this->query = $builder;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current schema.
|
||||
*
|
||||
* If null is given, a default ActiveDirectory schema is set.
|
||||
*
|
||||
* @param SchemaInterface|null $schema
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSchema(SchemaInterface $schema = null)
|
||||
{
|
||||
$this->schema = $schema ?: new ActiveDirectory();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new generic LDAP entry instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return Entry
|
||||
*/
|
||||
public function entry(array $attributes = [])
|
||||
{
|
||||
$model = $this->schema->entryModel();
|
||||
|
||||
return new $model($attributes, $this->query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function user(array $attributes = [])
|
||||
{
|
||||
$model = $this->schema->userModel();
|
||||
|
||||
return (new $model($attributes, $this->query))
|
||||
->setAttribute($this->schema->objectClass(), $this->schema->userObjectClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new organizational unit instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return OrganizationalUnit
|
||||
*/
|
||||
public function ou(array $attributes = [])
|
||||
{
|
||||
$model = $this->schema->organizationalUnitModel();
|
||||
|
||||
return (new $model($attributes, $this->query))
|
||||
->setAttribute($this->schema->objectClass(), [
|
||||
$this->schema->top(),
|
||||
$this->schema->organizationalUnit(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new organizational unit instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return Organization
|
||||
*/
|
||||
public function organization(array $attributes = [])
|
||||
{
|
||||
$model = $this->schema->organizationModel();
|
||||
|
||||
return (new $model($attributes, $this->query))
|
||||
->setAttribute($this->schema->objectClass(), [
|
||||
$this->schema->top(),
|
||||
$this->schema->organization(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new group instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return Group
|
||||
*/
|
||||
public function group(array $attributes = [])
|
||||
{
|
||||
$model = $this->schema->groupModel();
|
||||
|
||||
return (new $model($attributes, $this->query))
|
||||
->setAttribute($this->schema->objectClass(), [
|
||||
$this->schema->top(),
|
||||
$this->schema->objectCategoryGroup(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new organizational unit instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return Container
|
||||
*/
|
||||
public function container(array $attributes = [])
|
||||
{
|
||||
$model = $this->schema->containerModel();
|
||||
|
||||
return (new $model($attributes, $this->query))
|
||||
->setAttribute($this->schema->objectClass(), $this->schema->objectClassContainer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user instance as a contact.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function contact(array $attributes = [])
|
||||
{
|
||||
$model = $this->schema->contactModel();
|
||||
|
||||
return (new $model($attributes, $this->query))
|
||||
->setAttribute($this->schema->objectClass(), [
|
||||
$this->schema->top(),
|
||||
$this->schema->person(),
|
||||
$this->schema->organizationalPerson(),
|
||||
$this->schema->contact(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new computer instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return Computer
|
||||
*/
|
||||
public function computer(array $attributes = [])
|
||||
{
|
||||
$model = $this->schema->computerModel();
|
||||
|
||||
return (new $model($attributes, $this->query))
|
||||
->setAttribute($this->schema->objectClass(), [
|
||||
$this->schema->top(),
|
||||
$this->schema->person(),
|
||||
$this->schema->organizationalPerson(),
|
||||
$this->schema->user(),
|
||||
$this->schema->computer(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
/**
|
||||
* Class ForeignSecurityPrincipal.
|
||||
*
|
||||
* Represents an LDAP ForeignSecurityPrincipal.
|
||||
*/
|
||||
class ForeignSecurityPrincipal extends Entry
|
||||
{
|
||||
use Concerns\HasMemberOf;
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
use Adldap\Utilities;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class Group.
|
||||
*
|
||||
* Represents an LDAP group (security / distribution).
|
||||
*/
|
||||
class Group extends Entry
|
||||
{
|
||||
use Concerns\HasMemberOf;
|
||||
use Concerns\HasDescription;
|
||||
|
||||
/**
|
||||
* Returns all users apart of the current group.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms677097(v=vs.85).aspx
|
||||
*
|
||||
* @return \Adldap\Query\Collection
|
||||
*/
|
||||
public function getMembers()
|
||||
{
|
||||
$members = $this->getMembersFromAttribute($this->schema->member());
|
||||
|
||||
if (count($members) === 0) {
|
||||
$members = $this->getPaginatedMembers();
|
||||
}
|
||||
|
||||
return $this->newCollection($members);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group's member names only.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMemberNames()
|
||||
{
|
||||
$members = [];
|
||||
|
||||
$dns = $this->getAttribute($this->schema->member()) ?: [];
|
||||
|
||||
foreach ($dns as $dn) {
|
||||
$exploded = Utilities::explodeDn($dn);
|
||||
|
||||
if (array_key_exists(0, $exploded)) {
|
||||
$members[] = $exploded[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the groups members using an array of user DNs.
|
||||
*
|
||||
* @param array $entries
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMembers(array $entries)
|
||||
{
|
||||
return $this->setAttribute($this->schema->member(), $entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple entries to the current group.
|
||||
*
|
||||
* @param array $members
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function addMembers(array $members)
|
||||
{
|
||||
$members = array_map(function ($member) {
|
||||
return $member instanceof Model
|
||||
? $member->getDn()
|
||||
: $member;
|
||||
}, $members);
|
||||
|
||||
$mod = $this->newBatchModification(
|
||||
$this->schema->member(),
|
||||
LDAP_MODIFY_BATCH_ADD,
|
||||
$members
|
||||
);
|
||||
|
||||
return $this->addModification($mod)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry to the current group.
|
||||
*
|
||||
* @param string|Entry $member
|
||||
*
|
||||
* @throws InvalidArgumentException When the given entry is empty or contains no distinguished name.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function addMember($member)
|
||||
{
|
||||
$member = ($member instanceof Model ? $member->getDn() : $member);
|
||||
|
||||
if (is_null($member)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Cannot add member to group. The members distinguished name cannot be null.'
|
||||
);
|
||||
}
|
||||
|
||||
$mod = $this->newBatchModification(
|
||||
$this->schema->member(),
|
||||
LDAP_MODIFY_BATCH_ADD,
|
||||
[$member]
|
||||
);
|
||||
|
||||
return $this->addModification($mod)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an entry from the current group.
|
||||
*
|
||||
* @param string|Entry $member
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeMember($member)
|
||||
{
|
||||
$member = ($member instanceof Model ? $member->getDn() : $member);
|
||||
|
||||
if (is_null($member)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Cannot remove member to group. The members distinguished name cannot be null.'
|
||||
);
|
||||
}
|
||||
|
||||
$mod = $this->newBatchModification(
|
||||
$this->schema->member(),
|
||||
LDAP_MODIFY_BATCH_REMOVE,
|
||||
[$member]
|
||||
);
|
||||
|
||||
return $this->addModification($mod)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all members from the current group.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeMembers()
|
||||
{
|
||||
$mod = $this->newBatchModification(
|
||||
$this->schema->member(),
|
||||
LDAP_MODIFY_BATCH_REMOVE_ALL
|
||||
);
|
||||
|
||||
return $this->addModification($mod)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group type integer.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675935(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupType()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->groupType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves group members by the specified model attribute.
|
||||
*
|
||||
* @param $attribute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getMembersFromAttribute($attribute)
|
||||
{
|
||||
$members = [];
|
||||
|
||||
$entries = $this->getAttribute($attribute) ?: [];
|
||||
|
||||
$query = $this->query->newInstance();
|
||||
|
||||
// Retrieving the member identifier to allow
|
||||
// compatibility with LDAP variants.
|
||||
$identifier = $this->schema->memberIdentifier();
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
// If our identifier is a distinguished name, then we need to
|
||||
// use an alternate query method, as we can't locate records
|
||||
// by distinguished names using an LDAP filter.
|
||||
if ($identifier == 'dn' || $identifier == 'distinguishedname') {
|
||||
$member = $query->findByDn($entry);
|
||||
} else {
|
||||
// We'll ensure we clear our filters when retrieving each member,
|
||||
// so we can continue fetching the next one in line.
|
||||
$member = $query->clearFilters()->findBy($identifier, $entry);
|
||||
}
|
||||
|
||||
// We'll double check that we've received a model from
|
||||
// our query before adding it into our results.
|
||||
if ($member instanceof Model) {
|
||||
$members[] = $member;
|
||||
}
|
||||
}
|
||||
|
||||
return $members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves members that are contained in a member range.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getPaginatedMembers()
|
||||
{
|
||||
$members = [];
|
||||
|
||||
$keys = array_keys($this->attributes);
|
||||
|
||||
// We need to filter out the model attributes so
|
||||
// we only retrieve the member range.
|
||||
$attributes = array_values(array_filter($keys, function ($key) {
|
||||
return strpos($key, 'member;range') !== false;
|
||||
}));
|
||||
|
||||
// We'll grab the member range key so we can run a
|
||||
// regex on it to determine the range.
|
||||
$key = reset($attributes);
|
||||
|
||||
preg_match_all(
|
||||
'/member;range\=([0-9]{1,4})-([0-9*]{1,4})/',
|
||||
$key,
|
||||
$matches
|
||||
);
|
||||
|
||||
if ($key && count($matches) == 3) {
|
||||
// Retrieve the ending range number.
|
||||
$to = $matches[2][0];
|
||||
|
||||
// Retrieve the current groups members from the
|
||||
// current range string (ex. 'member;0-50').
|
||||
$members = $this->getMembersFromAttribute($key);
|
||||
|
||||
// If the query already included all member results (indicated
|
||||
// by the '*'), then we can return here. Otherwise we need
|
||||
// to continue on and retrieve the rest.
|
||||
if ($to === '*') {
|
||||
return $members;
|
||||
}
|
||||
|
||||
// Determine the amount of members we're requesting per query.
|
||||
$range = $to - $matches[1][0];
|
||||
|
||||
// Set our starting range to our last end range plus one.
|
||||
$from = $to + 1;
|
||||
|
||||
// We'll determine the new end range by adding the
|
||||
// total range to our new starting range.
|
||||
$to = $from + $range;
|
||||
|
||||
// We'll need to query for the current model again but with
|
||||
// a new range to retrieve the other members.
|
||||
/** @var Group $group */
|
||||
$group = $this->query->newInstance()->findByDn(
|
||||
$this->getDn(),
|
||||
[$this->query->getSchema()->memberRange($from, $to)]
|
||||
);
|
||||
|
||||
// Finally, we'll merge our current members
|
||||
// with the newly returned members.
|
||||
$members = array_merge(
|
||||
$members,
|
||||
$group->getMembers()->toArray()
|
||||
);
|
||||
}
|
||||
|
||||
return $members;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
|
||||
/**
|
||||
* Class ModelDoesNotExistException.
|
||||
*
|
||||
* Thrown when a model being saved / updated does not actually exist.
|
||||
*/
|
||||
class ModelDoesNotExistException extends AdldapException
|
||||
{
|
||||
/**
|
||||
* The class name of the model that does not exist.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* Sets the model that does not exist.
|
||||
*
|
||||
* @param string $model
|
||||
*
|
||||
* @return ModelDoesNotExistException
|
||||
*/
|
||||
public function setModel($model)
|
||||
{
|
||||
$this->model = $model;
|
||||
|
||||
$this->message = "Model [{$model}] does not exist.";
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
|
||||
/**
|
||||
* Class ModelNotFoundException.
|
||||
*
|
||||
* Thrown when an LDAP record is not found.
|
||||
*/
|
||||
class ModelNotFoundException extends AdldapException
|
||||
{
|
||||
/**
|
||||
* The query filter that was used.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* The base DN of the query that was used.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseDn;
|
||||
|
||||
/**
|
||||
* Sets the query that was used.
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $baseDn
|
||||
*
|
||||
* @return ModelNotFoundException
|
||||
*/
|
||||
public function setQuery($query, $baseDn)
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->baseDn = $baseDn;
|
||||
|
||||
$this->message = "No LDAP query results for filter: [{$query}] in: [{$baseDn}]";
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
/**
|
||||
* Class Organization.
|
||||
*
|
||||
* Represents an LDAP organization.
|
||||
*/
|
||||
class Organization extends Entry
|
||||
{
|
||||
use Concerns\HasDescription;
|
||||
|
||||
/**
|
||||
* Retrieves the organization units OU attribute.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrganization()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->organizationName());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCreatableDn()
|
||||
{
|
||||
return $this->getDnBuilder()->addO($this->getOrganization());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
/**
|
||||
* Class OrganizationalUnit.
|
||||
*
|
||||
* Represents an LDAP organizational unit.
|
||||
*/
|
||||
class OrganizationalUnit extends Entry
|
||||
{
|
||||
use Concerns\HasDescription;
|
||||
|
||||
/**
|
||||
* Retrieves the organization units OU attribute.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOu()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->organizationalUnitShort());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCreatableDn()
|
||||
{
|
||||
return $this->getDnBuilder()->addOU($this->getOu());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
/**
|
||||
* Class Printer.
|
||||
*
|
||||
* Represents an LDAP printer.
|
||||
*/
|
||||
class Printer extends Entry
|
||||
{
|
||||
/**
|
||||
* Returns the printers name.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679385(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrinterName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers share name.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679408(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrinterShareName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerShareName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers memory.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679396(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMemory()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerMemory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->url());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers location.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms676839(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocation()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->location());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server name that the
|
||||
* current printer is connected to.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679772(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getServerName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->serverName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true / false if the printer can print in color.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679382(v=vs.85).aspx
|
||||
*
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getColorSupported()
|
||||
{
|
||||
return $this->convertStringToBool(
|
||||
$this->getFirstAttribute(
|
||||
$this->schema->printerColorSupported()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true / false if the printer supports duplex printing.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679383(v=vs.85).aspx
|
||||
*
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getDuplexSupported()
|
||||
{
|
||||
return $this->convertStringToBool(
|
||||
$this->getFirstAttribute(
|
||||
$this->schema->printerDuplexSupported()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of printer paper types that the printer supports.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679395(v=vs.85).aspx
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMediaSupported()
|
||||
{
|
||||
return $this->getAttribute($this->schema->printerMediaSupported());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true / false if the printer supports stapling.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679410(v=vs.85).aspx
|
||||
*
|
||||
* @return null|bool
|
||||
*/
|
||||
public function getStaplingSupported()
|
||||
{
|
||||
return $this->convertStringToBool(
|
||||
$this->getFirstAttribute(
|
||||
$this->schema->printerStaplingSupported()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the printers bin names.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679380(v=vs.85).aspx
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPrintBinNames()
|
||||
{
|
||||
return $this->getAttribute($this->schema->printerBinNames());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers maximum resolution.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679391(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrintMaxResolution()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerMaxResolutionSupported());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers orientations supported.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679402(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrintOrientations()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerOrientationSupported());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the driver name of the printer.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675652(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDriverName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->driverName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printer drivers version number.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms675653(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDriverVersion()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->driverVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority number of the printer.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679413(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPriority()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->priority());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers start time.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679411(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrintStartTime()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerStartTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers end time.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679384(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrintEndTime()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerEndTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port name of printer.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679131(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPortName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->portName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printers version number.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms680897(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersionNumber()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->versionNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the print rate.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679405(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrintRate()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerPrintRate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the print rate unit.
|
||||
*
|
||||
* @link https://msdn.microsoft.com/en-us/library/ms679406(v=vs.85).aspx
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrintRateUnit()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->printerPrintRateUnit());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Class RootDse.
|
||||
*
|
||||
* Represents the LDAP connections Root DSE record.
|
||||
*/
|
||||
class RootDse extends Model
|
||||
{
|
||||
/**
|
||||
* Returns the hosts current time in unix timestamp format.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCurrentTime()
|
||||
{
|
||||
$time = $this->getFirstAttribute($this->schema->currentTime());
|
||||
|
||||
return DateTime::createFromFormat($this->timestampFormat, $time)->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosts current time in the models date format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrentTimeDate()
|
||||
{
|
||||
return (new DateTime())->setTimestamp($this->getCurrentTime())->format($this->dateFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosts configuration naming context.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConfigurationNamingContext()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->configurationNamingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosts schema naming context.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSchemaNamingContext()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->schemaNamingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hosts DNS name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDnsHostName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->dnsHostName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current hosts server name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getServerName()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->serverName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DN of the root domain NC for this DC's forest.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRootDomainNamingContext()
|
||||
{
|
||||
return $this->getFirstAttribute($this->schema->rootDomainNamingContext());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
|
||||
/**
|
||||
* Class UserPasswordIncorrectException.
|
||||
*
|
||||
* Thrown when a users password is being changed
|
||||
* and their current password given is incorrect.
|
||||
*/
|
||||
class UserPasswordIncorrectException extends AdldapException
|
||||
{
|
||||
//
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Models;
|
||||
|
||||
use Adldap\AdldapException;
|
||||
|
||||
/**
|
||||
* Class UserPasswordPolicyException.
|
||||
*
|
||||
* Thrown when a users password is being changed but their new password
|
||||
* does not conform to the LDAP servers password policy.
|
||||
*/
|
||||
class UserPasswordPolicyException extends AdldapException
|
||||
{
|
||||
//
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query;
|
||||
|
||||
use Closure;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
|
||||
class Cache
|
||||
{
|
||||
/**
|
||||
* The cache driver.
|
||||
*
|
||||
* @var CacheInterface
|
||||
*/
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param CacheInterface $store
|
||||
*/
|
||||
public function __construct(CacheInterface $store)
|
||||
{
|
||||
$this->store = $store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from the cache.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return $this->store->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an item in the cache.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param \DateTimeInterface|\DateInterval|int|null $ttl
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function put($key, $value, $ttl = null)
|
||||
{
|
||||
return $this->store->set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from the cache, or execute the given Closure and store the result.
|
||||
*
|
||||
* @param string $key
|
||||
* @param \DateTimeInterface|\DateInterval|int|null $ttl
|
||||
* @param Closure $callback
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function remember($key, $ttl, Closure $callback)
|
||||
{
|
||||
$value = $this->get($key);
|
||||
|
||||
if (!is_null($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$this->put($key, $value = $callback(), $ttl);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an item from the cache.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @throws \Psr\Cache\InvalidArgumentException
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->store->delete($key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query;
|
||||
|
||||
use Adldap\Models\Model;
|
||||
use Illuminate\Support\Collection as BaseCollection;
|
||||
|
||||
class Collection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function valueRetriever($value)
|
||||
{
|
||||
if ($this->useAsCallable($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return function ($item) use ($value) {
|
||||
if ($item instanceof Model) {
|
||||
return $item->getFirstAttribute($value);
|
||||
}
|
||||
|
||||
return data_get($item, $value);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query\Events;
|
||||
|
||||
class Listing extends QueryExecuted
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query\Events;
|
||||
|
||||
class Paginate extends QueryExecuted
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query\Events;
|
||||
|
||||
use Adldap\Query\Builder;
|
||||
|
||||
class QueryExecuted
|
||||
{
|
||||
/**
|
||||
* The LDAP filter that was used for the query.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* The number of milliseconds it took to execute the query.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $time;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param null|float $time
|
||||
*/
|
||||
public function __construct(Builder $query, $time = null)
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LDAP filter that was used for the query.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds it took to execute the query.
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
return $this->time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query\Events;
|
||||
|
||||
class Read extends QueryExecuted
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query\Events;
|
||||
|
||||
class Search extends QueryExecuted
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query;
|
||||
|
||||
use Adldap\Models\RootDse;
|
||||
use Adldap\Schemas\ActiveDirectory;
|
||||
use Adldap\Schemas\SchemaInterface;
|
||||
use Adldap\Connections\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* Adldap2 Search Factory.
|
||||
*
|
||||
* Constructs new LDAP queries.
|
||||
*
|
||||
*
|
||||
* @mixin Builder
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Stores the current schema instance.
|
||||
*
|
||||
* @var SchemaInterface
|
||||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* The base DN to use for the search.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $base;
|
||||
|
||||
/**
|
||||
* The query cache.
|
||||
*
|
||||
* @var Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ConnectionInterface $connection The connection to use when constructing a new query.
|
||||
* @param SchemaInterface|null $schema The schema to use for the query and models located.
|
||||
* @param string $baseDn The base DN to use for all searches.
|
||||
*/
|
||||
public function __construct(ConnectionInterface $connection, SchemaInterface $schema = null, $baseDn = '')
|
||||
{
|
||||
$this->setConnection($connection)
|
||||
->setSchema($schema)
|
||||
->setBaseDn($baseDn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection property.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setConnection(ConnectionInterface $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the schema property.
|
||||
*
|
||||
* @param SchemaInterface|null $schema
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSchema(SchemaInterface $schema = null)
|
||||
{
|
||||
$this->schema = $schema ?: new ActiveDirectory();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base distinguished name to perform searches upon.
|
||||
*
|
||||
* @param string $base
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setBaseDn($base = '')
|
||||
{
|
||||
$this->base = $base;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache for storing query results.
|
||||
*
|
||||
* @param Cache $cache
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCache(Cache $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new query builder instance.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function newQuery()
|
||||
{
|
||||
return $this->newBuilder()->in($this->base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a global 'all' search query on the current
|
||||
* connection by performing a search for all entries
|
||||
* that contain a common name attribute.
|
||||
*
|
||||
* @return \Adldap\Query\Collection|array
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->newQuery()->whereHas($this->schema->commonName())->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query builder scoped to users.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
$wheres = [
|
||||
[$this->schema->objectClass(), Operator::$equals, $this->schema->objectClassUser()],
|
||||
[$this->schema->objectCategory(), Operator::$equals, $this->schema->objectCategoryPerson()],
|
||||
];
|
||||
|
||||
// OpenLDAP doesn't like specifying the omission of user objectclasses
|
||||
// equal to `contact`. We'll make sure we're working with
|
||||
// ActiveDirectory before adding this filter.
|
||||
if (is_a($this->schema, ActiveDirectory::class)) {
|
||||
$wheres[] = [$this->schema->objectClass(), Operator::$doesNotEqual, $this->schema->objectClassContact()];
|
||||
}
|
||||
|
||||
return $this->where($wheres);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query builder scoped to printers.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function printers()
|
||||
{
|
||||
return $this->where([
|
||||
$this->schema->objectClass() => $this->schema->objectClassPrinter(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query builder scoped to organizational units.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function ous()
|
||||
{
|
||||
return $this->where([
|
||||
$this->schema->objectClass() => $this->schema->objectClassOu(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query builder scoped to organizations.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function organizations()
|
||||
{
|
||||
return $this->where([
|
||||
$this->schema->objectClass() => $this->schema->objectClassOrganization(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query builder scoped to groups.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function groups()
|
||||
{
|
||||
return $this->where([
|
||||
$this->schema->objectClass() => $this->schema->objectClassGroup(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query builder scoped to containers.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function containers()
|
||||
{
|
||||
return $this->where([
|
||||
$this->schema->objectClass() => $this->schema->objectClassContainer(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query builder scoped to contacts.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function contacts()
|
||||
{
|
||||
return $this->where([
|
||||
$this->schema->objectClass() => $this->schema->objectClassContact(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query builder scoped to computers.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function computers()
|
||||
{
|
||||
return $this->where([
|
||||
$this->schema->objectClass() => $this->schema->objectClassComputer(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root DSE record.
|
||||
*
|
||||
* @return RootDse|null
|
||||
*/
|
||||
public function getRootDse()
|
||||
{
|
||||
$query = $this->newQuery();
|
||||
|
||||
$root = $query->in('')->read()->whereHas($this->schema->objectClass())->first();
|
||||
|
||||
if ($root) {
|
||||
return (new RootDse([], $query))
|
||||
->setRawAttributes($root->getAttributes());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dynamic method calls on the query builder object.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
return call_user_func_array([$this->newQuery(), $method], $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new query grammar instance.
|
||||
*
|
||||
* @return Grammar
|
||||
*/
|
||||
protected function newGrammar()
|
||||
{
|
||||
return new Grammar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new query builder instance.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function newBuilder()
|
||||
{
|
||||
$builder = new Builder($this->connection, $this->newGrammar(), $this->schema);
|
||||
|
||||
$builder->setCache($this->cache);
|
||||
|
||||
return $builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query;
|
||||
|
||||
class Grammar
|
||||
{
|
||||
/**
|
||||
* Wraps a query string in brackets.
|
||||
*
|
||||
* Produces: (query)
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $prefix
|
||||
* @param string $suffix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function wrap($query, $prefix = '(', $suffix = ')')
|
||||
{
|
||||
return $prefix.$query.$suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the Builder instance into an LDAP query string.
|
||||
*
|
||||
* @param Builder $builder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile(Builder $builder)
|
||||
{
|
||||
$ands = $builder->filters['and'];
|
||||
$ors = $builder->filters['or'];
|
||||
$raws = $builder->filters['raw'];
|
||||
|
||||
$query = $this->concatenate($raws);
|
||||
|
||||
$query = $this->compileWheres($ands, $query);
|
||||
|
||||
$query = $this->compileOrWheres($ors, $query);
|
||||
|
||||
// We need to check if the query is already nested, otherwise
|
||||
// we'll nest it here and return the result.
|
||||
if (!$builder->isNested()) {
|
||||
$total = count($ands) + count($raws);
|
||||
|
||||
// Make sure we wrap the query in an 'and' if using
|
||||
// multiple filters. We also need to check if only
|
||||
// one where is used with multiple orWheres, that
|
||||
// we wrap it in an `and` query.
|
||||
if ($total > 1 || (count($ands) === 1 && count($ors) > 0)) {
|
||||
$query = $this->compileAnd($query);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates filters into a single string.
|
||||
*
|
||||
* @param array $bindings
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function concatenate(array $bindings = [])
|
||||
{
|
||||
// Filter out empty query segments.
|
||||
$bindings = array_filter($bindings, function ($value) {
|
||||
return (string) $value !== '';
|
||||
});
|
||||
|
||||
return implode('', $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for equals.
|
||||
*
|
||||
* Produces: (field=value)
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileEquals($field, $value)
|
||||
{
|
||||
return $this->wrap($field.Operator::$equals.$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for does not equal.
|
||||
*
|
||||
* Produces: (!(field=value))
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileDoesNotEqual($field, $value)
|
||||
{
|
||||
return $this->compileNot($this->compileEquals($field, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for does not equal operator (!=) operator.
|
||||
*
|
||||
* Produces: (!(field=value))
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileDoesNotEqualAlias($field, $value)
|
||||
{
|
||||
return $this->compileDoesNotEqual($field, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for greater than or equals.
|
||||
*
|
||||
* Produces: (field>=value)
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileGreaterThanOrEquals($field, $value)
|
||||
{
|
||||
return $this->wrap($field.Operator::$greaterThanOrEquals.$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for less than or equals.
|
||||
*
|
||||
* Produces: (field<=value)
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileLessThanOrEquals($field, $value)
|
||||
{
|
||||
return $this->wrap($field.Operator::$lessThanOrEquals.$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for approximately equals.
|
||||
*
|
||||
* Produces: (field~=value)
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileApproximatelyEquals($field, $value)
|
||||
{
|
||||
return $this->wrap($field.Operator::$approximatelyEquals.$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for starts with.
|
||||
*
|
||||
* Produces: (field=value*)
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileStartsWith($field, $value)
|
||||
{
|
||||
return $this->wrap($field.Operator::$equals.$value.Operator::$has);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for does not start with.
|
||||
*
|
||||
* Produces: (!(field=*value))
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileNotStartsWith($field, $value)
|
||||
{
|
||||
return $this->compileNot($this->compileStartsWith($field, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for ends with.
|
||||
*
|
||||
* Produces: (field=*value)
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileEndsWith($field, $value)
|
||||
{
|
||||
return $this->wrap($field.Operator::$equals.Operator::$has.$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for does not end with.
|
||||
*
|
||||
* Produces: (!(field=value*))
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileNotEndsWith($field, $value)
|
||||
{
|
||||
return $this->compileNot($this->compileEndsWith($field, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for contains.
|
||||
*
|
||||
* Produces: (field=*value*)
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileContains($field, $value)
|
||||
{
|
||||
return $this->wrap($field.Operator::$equals.Operator::$has.$value.Operator::$has);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for does not contain.
|
||||
*
|
||||
* Produces: (!(field=*value*))
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileNotContains($field, $value)
|
||||
{
|
||||
return $this->compileNot($this->compileContains($field, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for a where has.
|
||||
*
|
||||
* Produces: (field=*)
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileHas($field)
|
||||
{
|
||||
return $this->wrap($field.Operator::$equals.Operator::$has);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query string for a where does not have.
|
||||
*
|
||||
* Produces: (!(field=*))
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileNotHas($field)
|
||||
{
|
||||
return $this->compileNot($this->compileHas($field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the inserted query inside an AND operator.
|
||||
*
|
||||
* Produces: (&query)
|
||||
*
|
||||
* @param string $query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileAnd($query)
|
||||
{
|
||||
return $query ? $this->wrap($query, '(&') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the inserted query inside an OR operator.
|
||||
*
|
||||
* Produces: (|query)
|
||||
*
|
||||
* @param string $query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileOr($query)
|
||||
{
|
||||
return $query ? $this->wrap($query, '(|') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the inserted query inside an NOT operator.
|
||||
*
|
||||
* @param string $query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compileNot($query)
|
||||
{
|
||||
return $query ? $this->wrap($query, '(!') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles all where clauses in the current wheres property.
|
||||
*
|
||||
* @param array $wheres
|
||||
* @param string $query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function compileWheres(array $wheres = [], $query = '')
|
||||
{
|
||||
foreach ($wheres as $where) {
|
||||
$query .= $this->compileWhere($where);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles all or where clauses in the current orWheres property.
|
||||
*
|
||||
* @param array $orWheres
|
||||
* @param string $query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function compileOrWheres(array $orWheres = [], $query = '')
|
||||
{
|
||||
$or = '';
|
||||
|
||||
foreach ($orWheres as $where) {
|
||||
$or .= $this->compileWhere($where);
|
||||
}
|
||||
|
||||
// Make sure we wrap the query in an 'or' if using multiple
|
||||
// orWheres. For example (|(QUERY)(ORWHEREQUERY)).
|
||||
if (($query && count($orWheres) > 0) || count($orWheres) > 1) {
|
||||
$query .= $this->compileOr($or);
|
||||
} else {
|
||||
$query .= $or;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles a single where query based
|
||||
* on its operator and returns it.
|
||||
*
|
||||
* @param array $where
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function compileWhere(array $where)
|
||||
{
|
||||
// Get the name of the operator.
|
||||
if ($name = array_search($where['operator'], Operator::all())) {
|
||||
// If the name was found we'll camel case it
|
||||
// to run it through the compile method.
|
||||
$method = 'compile'.ucfirst($name);
|
||||
|
||||
// Make sure the compile method exists for the operator.
|
||||
if (method_exists($this, $method)) {
|
||||
return $this->{$method}($where['field'], $where['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
class Operator
|
||||
{
|
||||
/**
|
||||
* The 'has' wildcard operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $has = '*';
|
||||
|
||||
/**
|
||||
* The custom `notHas` operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $notHas = '!*';
|
||||
|
||||
/**
|
||||
* The equals operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $equals = '=';
|
||||
|
||||
/**
|
||||
* The does not equal operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $doesNotEqual = '!';
|
||||
|
||||
/**
|
||||
* The does not equal operator (alias).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $doesNotEqualAlias = '!=';
|
||||
|
||||
/**
|
||||
* The greater than or equal to operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $greaterThanOrEquals = '>=';
|
||||
|
||||
/**
|
||||
* The less than or equal to operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $lessThanOrEquals = '<=';
|
||||
|
||||
/**
|
||||
* The approximately equal to operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $approximatelyEquals = '~=';
|
||||
|
||||
/**
|
||||
* The custom starts with operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $startsWith = 'starts_with';
|
||||
|
||||
/**
|
||||
* The custom not starts with operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $notStartsWith = 'not_starts_with';
|
||||
|
||||
/**
|
||||
* The custom ends with operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $endsWith = 'ends_with';
|
||||
|
||||
/**
|
||||
* The custom not ends with operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $notEndsWith = 'not_ends_with';
|
||||
|
||||
/**
|
||||
* The custom contains operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $contains = 'contains';
|
||||
|
||||
/**
|
||||
* The custom not contains operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $notContains = 'not_contains';
|
||||
|
||||
/**
|
||||
* Returns all available operators.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function all()
|
||||
{
|
||||
return (new ReflectionClass(new static()))->getStaticProperties();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query;
|
||||
|
||||
use Countable;
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
class Paginator implements Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* The complete results array.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $results = [];
|
||||
|
||||
/**
|
||||
* The total amount of pages.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $pages;
|
||||
|
||||
/**
|
||||
* The amount of entries per page.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $perPage;
|
||||
|
||||
/**
|
||||
* The current page number.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $currentPage;
|
||||
|
||||
/**
|
||||
* The current entry offset number.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $currentOffset;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $results
|
||||
* @param int $perPage
|
||||
* @param int $currentPage
|
||||
* @param int $pages
|
||||
*/
|
||||
public function __construct(array $results = [], $perPage = 50, $currentPage = 0, $pages = 0)
|
||||
{
|
||||
$this->setResults($results)
|
||||
->setPerPage($perPage)
|
||||
->setCurrentPage($currentPage)
|
||||
->setPages($pages)
|
||||
->setCurrentOffset(($this->getCurrentPage() * $this->getPerPage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator for the entries.
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$entries = array_slice($this->getResults(), $this->getCurrentOffset(), $this->getPerPage(), true);
|
||||
|
||||
return new ArrayIterator($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete results array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResults()
|
||||
{
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of pages
|
||||
* in a paginated result.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPages()
|
||||
{
|
||||
return $this->pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of entries
|
||||
* allowed per page.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPerPage()
|
||||
{
|
||||
return $this->perPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current page number.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCurrentPage()
|
||||
{
|
||||
return $this->currentPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current offset number.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCurrentOffset()
|
||||
{
|
||||
return $this->currentOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of results.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the results array property.
|
||||
*
|
||||
* @param array $results
|
||||
*
|
||||
* @return Paginator
|
||||
*/
|
||||
protected function setResults(array $results)
|
||||
{
|
||||
$this->results = $results;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total number of pages.
|
||||
*
|
||||
* @param int $pages
|
||||
*
|
||||
* @return Paginator
|
||||
*/
|
||||
protected function setPages($pages = 0)
|
||||
{
|
||||
$this->pages = (int) $pages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of entries per page.
|
||||
*
|
||||
* @param int $perPage
|
||||
*
|
||||
* @return Paginator
|
||||
*/
|
||||
protected function setPerPage($perPage = 50)
|
||||
{
|
||||
$this->perPage = (int) $perPage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current page number.
|
||||
*
|
||||
* @param int $currentPage
|
||||
*
|
||||
* @return Paginator
|
||||
*/
|
||||
protected function setCurrentPage($currentPage = 0)
|
||||
{
|
||||
$this->currentPage = (int) $currentPage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current offset number.
|
||||
*
|
||||
* @param int $offset
|
||||
*
|
||||
* @return Paginator
|
||||
*/
|
||||
protected function setCurrentOffset($offset = 0)
|
||||
{
|
||||
$this->currentOffset = (int) $offset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Query;
|
||||
|
||||
use Adldap\Models\Entry;
|
||||
use Adldap\Models\Model;
|
||||
use InvalidArgumentException;
|
||||
use Adldap\Schemas\SchemaInterface;
|
||||
use Adldap\Connections\ConnectionInterface;
|
||||
|
||||
class Processor
|
||||
{
|
||||
/**
|
||||
* @var Builder
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var SchemaInterface
|
||||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Builder $builder
|
||||
*/
|
||||
public function __construct(Builder $builder)
|
||||
{
|
||||
$this->builder = $builder;
|
||||
$this->schema = $builder->getSchema();
|
||||
$this->connection = $builder->getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes LDAP search results and constructs their model instances.
|
||||
*
|
||||
* @param array $entries The LDAP entries to process.
|
||||
*
|
||||
* @return Collection|array
|
||||
*/
|
||||
public function process($entries)
|
||||
{
|
||||
if ($this->builder->isRaw()) {
|
||||
// If the builder is asking for a raw
|
||||
// LDAP result, we can return here.
|
||||
return $entries;
|
||||
}
|
||||
|
||||
$models = [];
|
||||
|
||||
if (array_key_exists('count', $entries)) {
|
||||
for ($i = 0; $i < $entries['count']; $i++) {
|
||||
// We'll go through each entry and construct a new
|
||||
// model instance with the raw LDAP attributes.
|
||||
$models[] = $this->newLdapEntry($entries[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
// If the query contains paginated results, we'll return them here.
|
||||
if ($this->builder->isPaginated()) {
|
||||
return $models;
|
||||
}
|
||||
|
||||
// If the query is requested to be sorted, we'll perform
|
||||
// that here and return the resulting collection.
|
||||
if ($this->builder->isSorted()) {
|
||||
return $this->processSort($models);
|
||||
}
|
||||
|
||||
// Otherwise, we'll return a regular unsorted collection.
|
||||
return $this->newCollection($models);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes paginated LDAP results.
|
||||
*
|
||||
* @param array $pages
|
||||
* @param int $perPage
|
||||
* @param int $currentPage
|
||||
*
|
||||
* @return Paginator
|
||||
*/
|
||||
public function processPaginated(array $pages = [], $perPage = 50, $currentPage = 0)
|
||||
{
|
||||
$models = [];
|
||||
|
||||
foreach ($pages as $entries) {
|
||||
// Go through each page and process the results into an objects array.
|
||||
$models = array_merge($models, $this->process($entries));
|
||||
}
|
||||
|
||||
$models = $this->processSort($models)->toArray();
|
||||
|
||||
return $this->newPaginator($models, $perPage, $currentPage, count($pages));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new LDAP Entry instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return Entry
|
||||
*/
|
||||
public function newLdapEntry(array $attributes = [])
|
||||
{
|
||||
$objectClass = $this->schema->objectClass();
|
||||
|
||||
// We need to ensure the record contains an object class to be able to
|
||||
// determine its type. Otherwise, we create a default Entry model.
|
||||
if (array_key_exists($objectClass, $attributes) && array_key_exists(0, $attributes[$objectClass])) {
|
||||
// Retrieve all of the object classes from the LDAP
|
||||
// entry and lowercase them for comparisons.
|
||||
$classes = array_map('strtolower', $attributes[$objectClass]);
|
||||
|
||||
// Retrieve the model mapping.
|
||||
$models = $this->schema->objectClassModelMap();
|
||||
|
||||
// Retrieve the object class mappings (with strtolower keys).
|
||||
$mappings = array_map('strtolower', array_keys($models));
|
||||
|
||||
// Retrieve the model from the map using the entry's object class.
|
||||
$map = array_intersect($mappings, $classes);
|
||||
|
||||
if (count($map) > 0) {
|
||||
// Retrieve the model using the object class.
|
||||
$model = $models[current($map)];
|
||||
|
||||
// Construct and return a new model.
|
||||
return $this->newModel([], $model)
|
||||
->setRawAttributes($attributes);
|
||||
}
|
||||
}
|
||||
|
||||
// A default entry model if the object class isn't found.
|
||||
return $this->newModel()->setRawAttributes($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new model instance.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param string|null $model
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return mixed|Entry
|
||||
*/
|
||||
public function newModel($attributes = [], $model = null)
|
||||
{
|
||||
$model = (class_exists($model) ? $model : $this->schema->entryModel());
|
||||
|
||||
if (!is_subclass_of($model, $base = Model::class)) {
|
||||
throw new InvalidArgumentException("The given model class '{$model}' must extend the base model class '{$base}'");
|
||||
}
|
||||
|
||||
return new $model($attributes, $this->builder->newInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Paginator object instance.
|
||||
*
|
||||
* @param array $models
|
||||
* @param int $perPage
|
||||
* @param int $currentPage
|
||||
* @param int $pages
|
||||
*
|
||||
* @return Paginator
|
||||
*/
|
||||
public function newPaginator(array $models = [], $perPage = 25, $currentPage = 0, $pages = 1)
|
||||
{
|
||||
return new Paginator($models, $perPage, $currentPage, $pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new collection instance.
|
||||
*
|
||||
* @param array $items
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function newCollection(array $items = [])
|
||||
{
|
||||
return new Collection($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts LDAP search results.
|
||||
*
|
||||
* @param array $models
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function processSort(array $models = [])
|
||||
{
|
||||
$field = $this->builder->getSortByField();
|
||||
|
||||
$flags = $this->builder->getSortByFlags();
|
||||
|
||||
$direction = $this->builder->getSortByDirection();
|
||||
|
||||
$desc = ($direction === 'desc' ? true : false);
|
||||
|
||||
return $this->newCollection($models)->sortBy($field, $flags, $desc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Schemas;
|
||||
|
||||
class ActiveDirectory extends Schema
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedName()
|
||||
{
|
||||
return 'distinguishedname';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedNameSubKey()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterEnabled()
|
||||
{
|
||||
return '(!(UserAccountControl:1.2.840.113556.1.4.803:=2))';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterDisabled()
|
||||
{
|
||||
return '(UserAccountControl:1.2.840.113556.1.4.803:=2)';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lockoutTime()
|
||||
{
|
||||
return 'lockouttime';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassGroup()
|
||||
{
|
||||
return 'group';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassOu()
|
||||
{
|
||||
return 'organizationalunit';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassPerson()
|
||||
{
|
||||
return 'person';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuid()
|
||||
{
|
||||
return 'objectguid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuidRequiresConversion()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectCategory()
|
||||
{
|
||||
return 'objectcategory';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Schemas;
|
||||
|
||||
class Directory389 extends Schema
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accountName()
|
||||
{
|
||||
return 'uid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedName()
|
||||
{
|
||||
return 'dn';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedNameSubKey()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterEnabled()
|
||||
{
|
||||
return sprintf('(!(%s=*))', $this->lockoutTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterDisabled()
|
||||
{
|
||||
return sprintf('(%s=*)', $this->lockoutTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lockoutTime()
|
||||
{
|
||||
return 'pwdAccountLockedTime';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectCategory()
|
||||
{
|
||||
return 'objectclass';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassGroup()
|
||||
{
|
||||
return 'groupofnames';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassOu()
|
||||
{
|
||||
return 'organizationalUnit';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassPerson()
|
||||
{
|
||||
return 'inetorgperson';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassUser()
|
||||
{
|
||||
return 'inetorgperson';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuid()
|
||||
{
|
||||
return 'nsuniqueid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuidRequiresConversion()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Schemas;
|
||||
|
||||
class EDirectory extends Schema
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accountName()
|
||||
{
|
||||
return 'uid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedName()
|
||||
{
|
||||
return 'dn';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedNameSubKey()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterEnabled()
|
||||
{
|
||||
return sprintf('(!(%s=*))', $this->lockoutTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterDisabled()
|
||||
{
|
||||
return sprintf('(%s=*)', $this->lockoutTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lockoutTime()
|
||||
{
|
||||
return 'pwdAccountLockedTime';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectCategory()
|
||||
{
|
||||
return 'objectclass';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassGroup()
|
||||
{
|
||||
return 'groupofnames';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassOu()
|
||||
{
|
||||
return 'organizationalUnit';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassPerson()
|
||||
{
|
||||
return 'inetorgperson';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassUser()
|
||||
{
|
||||
return 'inetorgperson';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuid()
|
||||
{
|
||||
return 'guid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuidRequiresConversion()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Schemas;
|
||||
|
||||
class FreeIPA extends Schema
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accountName()
|
||||
{
|
||||
return 'uid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedName()
|
||||
{
|
||||
return 'dn';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectCategory()
|
||||
{
|
||||
return 'objectclass';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassGroup()
|
||||
{
|
||||
return 'ipausergroup';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function userPrincipalName()
|
||||
{
|
||||
return 'krbCanonicalName';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedNameSubKey()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterEnabled()
|
||||
{
|
||||
return '(!(UserAccountControl:1.2.840.113556.1.4.803:=2))';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterDisabled()
|
||||
{
|
||||
return '(UserAccountControl:1.2.840.113556.1.4.803:=2)';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lockoutTime()
|
||||
{
|
||||
return 'lockouttime';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function passwordLastSet()
|
||||
{
|
||||
return 'krbLastPwdChange';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassOu()
|
||||
{
|
||||
return 'organizationalunit';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassPerson()
|
||||
{
|
||||
return 'person';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassUser()
|
||||
{
|
||||
return 'organizationalPerson';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuid()
|
||||
{
|
||||
return 'ipaUniqueID';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuidRequiresConversion()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap\Schemas;
|
||||
|
||||
class OpenLDAP extends Schema
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accountName()
|
||||
{
|
||||
return 'uid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedName()
|
||||
{
|
||||
return 'dn';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinguishedNameSubKey()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterEnabled()
|
||||
{
|
||||
return sprintf('(!(%s=*))', $this->lockoutTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filterDisabled()
|
||||
{
|
||||
return sprintf('(%s=*)', $this->lockoutTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lockoutTime()
|
||||
{
|
||||
return 'pwdAccountLockedTime';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectCategory()
|
||||
{
|
||||
return 'objectclass';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassGroup()
|
||||
{
|
||||
return 'groupofnames';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassOu()
|
||||
{
|
||||
return 'organizationalUnit';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassPerson()
|
||||
{
|
||||
return 'inetorgperson';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectClassUser()
|
||||
{
|
||||
return 'inetorgperson';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuid()
|
||||
{
|
||||
return 'entryuuid';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function objectGuidRequiresConversion()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
namespace Adldap;
|
||||
|
||||
class Utilities
|
||||
{
|
||||
/**
|
||||
* Converts a DN string into an array of RDNs.
|
||||
*
|
||||
* This will also decode hex characters into their true
|
||||
* UTF-8 representation embedded inside the DN as well.
|
||||
*
|
||||
* @param string $dn
|
||||
* @param bool $removeAttributePrefixes
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public static function explodeDn($dn, $removeAttributePrefixes = true)
|
||||
{
|
||||
$dn = ldap_explode_dn($dn, ($removeAttributePrefixes ? 1 : 0));
|
||||
|
||||
if (is_array($dn) && array_key_exists('count', $dn)) {
|
||||
foreach ($dn as $rdn => $value) {
|
||||
$dn[$rdn] = self::unescape($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $dn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-escapes a hexadecimal string into
|
||||
* its original string representation.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function unescape($value)
|
||||
{
|
||||
return preg_replace_callback('/\\\([0-9A-Fa-f]{2})/', function ($matches) {
|
||||
return chr(hexdec($matches[1]));
|
||||
}, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a binary SID to a string SID.
|
||||
*
|
||||
* @author Chad Sikorra
|
||||
*
|
||||
* @link https://github.com/ChadSikorra
|
||||
* @link https://stackoverflow.com/questions/39533560/php-ldap-get-user-sid
|
||||
*
|
||||
* @param string $value The Binary SID
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function binarySidToString($value)
|
||||
{
|
||||
// Revision - 8bit unsigned int (C1)
|
||||
// Count - 8bit unsigned int (C1)
|
||||
// 2 null bytes
|
||||
// ID - 32bit unsigned long, big-endian order
|
||||
$sid = @unpack('C1rev/C1count/x2/N1id', $value);
|
||||
|
||||
if (!isset($sid['id']) || !isset($sid['rev'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$revisionLevel = $sid['rev'];
|
||||
|
||||
$identifierAuthority = $sid['id'];
|
||||
|
||||
$subs = isset($sid['count']) ? $sid['count'] : 0;
|
||||
|
||||
$sidHex = $subs ? bin2hex($value) : '';
|
||||
|
||||
$subAuthorities = [];
|
||||
|
||||
// The sub-authorities depend on the count, so only get as
|
||||
// many as the count, regardless of data beyond it.
|
||||
for ($i = 0; $i < $subs; $i++) {
|
||||
$data = implode('', array_reverse(
|
||||
str_split(
|
||||
substr($sidHex, 16 + ($i * 8), 8),
|
||||
2
|
||||
)
|
||||
));
|
||||
|
||||
$subAuthorities[] = hexdec($data);
|
||||
}
|
||||
|
||||
// Tack on the 'S-' and glue it all together...
|
||||
return 'S-'.$revisionLevel.'-'.$identifierAuthority.implode(
|
||||
preg_filter('/^/', '-', $subAuthorities)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a binary GUID to a string GUID.
|
||||
*
|
||||
* @param string $binGuid
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function binaryGuidToString($binGuid)
|
||||
{
|
||||
if (trim($binGuid) == '' || is_null($binGuid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hex = unpack('H*hex', $binGuid)['hex'];
|
||||
|
||||
$hex1 = substr($hex, -26, 2).substr($hex, -28, 2).substr($hex, -30, 2).substr($hex, -32, 2);
|
||||
$hex2 = substr($hex, -22, 2).substr($hex, -24, 2);
|
||||
$hex3 = substr($hex, -18, 2).substr($hex, -20, 2);
|
||||
$hex4 = substr($hex, -16, 4);
|
||||
$hex5 = substr($hex, -12, 12);
|
||||
|
||||
$guid = sprintf('%s-%s-%s-%s-%s', $hex1, $hex2, $hex3, $hex4, $hex5);
|
||||
|
||||
return $guid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string GUID to it's hex variant.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function stringGuidToHex($string)
|
||||
{
|
||||
$hex = '\\'.substr($string, 6, 2).'\\'.substr($string, 4, 2).'\\'.substr($string, 2, 2).'\\'.substr($string, 0, 2);
|
||||
$hex = $hex.'\\'.substr($string, 11, 2).'\\'.substr($string, 9, 2);
|
||||
$hex = $hex.'\\'.substr($string, 16, 2).'\\'.substr($string, 14, 2);
|
||||
$hex = $hex.'\\'.substr($string, 19, 2).'\\'.substr($string, 21, 2);
|
||||
$hex = $hex.'\\'.substr($string, 24, 2).'\\'.substr($string, 26, 2).'\\'.substr($string, 28, 2).'\\'.substr($string, 30, 2).'\\'.substr($string, 32, 2).'\\'.substr($string, 34, 2);
|
||||
|
||||
return $hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a password for transmission over LDAP.
|
||||
*
|
||||
* @param string $password The password to encode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function encodePassword($password)
|
||||
{
|
||||
return iconv('UTF-8', 'UTF-16LE', '"'.$password.'"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Salt and hash a password to make its SSHA OpenLDAP version.
|
||||
*
|
||||
* @param string $password The password to create
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function makeSSHAPassword($password)
|
||||
{
|
||||
mt_srand((float) microtime() * 1000000);
|
||||
$salt = pack('CCCC', mt_rand(), mt_rand(), mt_rand(), mt_rand());
|
||||
|
||||
return '{SSHA}'.base64_encode(pack('H*', sha1($password.$salt)).$salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round a Windows timestamp down to seconds and remove
|
||||
* the seconds between 1601-01-01 and 1970-01-01.
|
||||
*
|
||||
* @param float $windowsTime
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function convertWindowsTimeToUnixTime($windowsTime)
|
||||
{
|
||||
return round($windowsTime / 10000000) - 11644473600;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Unix timestamp to Windows timestamp.
|
||||
*
|
||||
* @param float $unixTime
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function convertUnixTimeToWindowsTime($unixTime)
|
||||
{
|
||||
return ($unixTime + 11644473600) * 10000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the inserted string is an object SID.
|
||||
*
|
||||
* @param string $sid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidSid($sid)
|
||||
{
|
||||
return (bool) preg_match("/^S-\d(-\d{1,10}){1,16}$/i", $sid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the inserted string is an object GUID.
|
||||
*
|
||||
* @param string $guid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidGuid($guid)
|
||||
{
|
||||
return (bool) preg_match('/^([0-9a-fA-F]){8}(-([0-9a-fA-F]){4}){3}-([0-9a-fA-F]){12}$|^([0-9a-fA-F]{8}-){3}[0-9a-fA-F]{8}$/', $guid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an ignore string into an array.
|
||||
*
|
||||
* @param string $ignore
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function ignoreStrToArray($ignore)
|
||||
{
|
||||
$ignore = trim($ignore);
|
||||
|
||||
return $ignore ? str_split($ignore) : [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user