Large diffs are not rendered by default.

@@ -0,0 +1,72 @@
## Description

> **ATTENTION**: Version v5.0.0 is in heavy development, however it is close to completion. Use 'dev-master' at you're own risk.
adLDAP is a tested PHP class library that provides LDAP authentication and Active Directory management tools.

## Index

> **Note:** Documentation is incomplete as Adldap is currently in the upgrade process to version `5.0.0`. They will be fully featured and complete in the coming weeks.
- [Installation](#installation)
- [Testing With A Public AD Server](#need-to-test-an-ldap-connection)
- [Upgrading to v5 from v4](docs/UPGRADING.md)
- [Getting Started](docs/GETTING-STARTED.md)
- Usage
- [Search Functions](docs/SEARCH-FUNCTIONS.md)
- [Computer Functions](docs/COMPUTER-FUNCTIONS.md)
- [Contact Functions](docs/CONTACT-FUNCTIONS.md)
- [Exchange Functions](docs/EXCHANGE-FUNCTIONS.md)
- [Folder Functions](docs/FOLDER-FUNCTIONS.md)
- [Group Functions](docs/GROUP-FUNCTIONS.md)
- [User Functions](docs/USER-FUNCTIONS.md)

## Requirements

To use adLDAP, your sever must support:

- PHP 5.4 or greater
- PHP LDAP Extension


## Optional Requirements

If your AD server requires SSL, your server must support the following libraries:

- PHP SSL Libraries (http://php.net/openssl)

## Installation

adLDAP has moved to a composer based installation. If you'd like to use adLDAP without an auto-loader, you'll
have to require the files inside the project `src/` directory yourself.

Insert Adldap into your `composer.json` file:

"adldap/adldap": "5.0.*"

Run `composer update`

You're good to go!

## Need to test an LDAP connection?

If you need to test something with access to an LDAP server, the generous folks at [Georgia Tech](http://drupal.gatech.edu/handbook/public-ldap-server) have you covered.

Use the following configuration:

$config = array(
'account_suffix' => "@gatech.edu",

'domain_controllers' => array("whitepages.gatech.edu"),

'base_dn' => 'dc=whitepages,dc=gatech,dc=edu',

'admin_username' => '',

'admin_password' => '',
);

$ad = new Adldap($config);

However while useful for basic testing, the queryable data only includes user data, so if you're looking for testing with any other information
or functionality such as modification, you'll have to use you're own server.
@@ -0,0 +1,53 @@
{
"name": "adldap/adldap",
"type": "library",
"description": "A PHP LDAP Library for Active Directory Manipulation",
"keywords": [
"active directory",
"ldap",
"windows"
],
"license": "LGPL-2.1",
"support": {
"issues": "https://github.com/adldap/adLDAP/issues",
"source": "https://github.com/adldap/adLDAP"
},
"authors": [
{
"name": "Richard Hyland",
"email": "adldap@richardhyland.com",
"role": "Developer"
},
{
"name": "Scott Barnett",
"email": "scott@wiggumworld.com",
"role": "Developer"
},
{
"name": "Steve Bauman",
"email": "steven_bauman@outlook.com",
"role": "Developer"
}
],
"require": {
"php": ">=5.4.0",
"ext-ldap": "*"
},
"require-dev": {
"phpunit/phpunit": "4.6.*",
"mockery/mockery": "0.9.*"
},
"archive": {
"exclude": ["/examples", "/tests"]
},
"autoload": {
"psr-4": {
"Adldap\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Adldap\\Tests\\": "tests/"
}
}
}
@@ -0,0 +1,51 @@
## Computer Functions

### All

To retrieve all computers on your AD server, use the `all()` method:

$results = $ad->computer()->all();

You can also customize your results by providing some paramters inside the function like so:

$fields = array('operatingsystem');

$results = $ad->computer()->all($fields, $sorted = true, $sortBy = 'cn', $sortByDirection = 'asc');

### Find

To retrieve information on a specific computer, use the `find()` method:

$computer = $ad->computer->find('WIN-PC');

You can also customize the fields that are returned by passing in field array in the second parameter:

$fields = array('operatingAystem', 'operatingsystemversion');

$computer = $ad->computer()->find('WIN-PC', $fields);

### Info

To preserve backwards compatibility, the `info()` function is an alias for the `find()` method:

$computer = $ad->computer()->info('WIN-PC', $fields);

### DN

To retrieve a computers distinguished name, use the `dn()` method:

$computerDn = $ad->computer()->dn('WIN-PC');

echo $computerDn;

### Groups

To retrieve all the groups that a computer is in, use the `groups()` method:

$groups = $ad->computer()->groups('WIN-PC');

### In Group

To determine if a computer is in a group, use the `inGroup()` method:

$inGroup = $ad->computer()->inGroup('WIN-PC', 'Accounting'); // Returns true / false
@@ -0,0 +1,2 @@
## Configuration

@@ -0,0 +1,55 @@
## Contact Functions

### All

To retrieve all contacts from AD, use the `all()` method:

$contacts = $ad->contact()->all();

### Find

To retrieve information on a specific contact, use the `find()` method:

$contact = $ad->contact()->find('John Doe');

If you're only interested in certain LDAP fields, insert your fields in the second parameter:

$fields = [
'cn'
];

$contact = $ad->contact()->find('John Doe', $fields);

### Info

The `info()` method is an alias for the `find()` method:

$contact = $ad->contact()->info('John Doe', $fields);

### DN

To retrieve a contacts DN, use the `dn()` method:

$contactDn = $ad->contact()->dn('John Doe');

echo $contactDn;

### Create

### Modify

### Delete

To delete a contact, use the `delete()` method:

$contactDn = $ad->contact()->dn('John Doe');

if($contactDn) {
$deleted = $ad->contact()->delete($contactDn);
}

### Groups

### In Group

### Contact Mail Enable
@@ -0,0 +1,41 @@
## Exchange Functions

### All

To retrieve all exchange servers on your AD, use the `all()` method:

$servers = $ad->exchange()->all();

You can also sort your results:

$fields = ['cn', 'distinguishedname'];

$sorted = true;

$sortBy = 'cn';

$sortDirection = 'desc';

$servers = $ad->exchange()->all($fields, $sorted, $sortBy, $sortDirection);

### Find

To retrieve information on an exchange server, use the `find()` method:

$serverName = 'EXCH-CORP';

$server = $ad->exchange()->find($serverName);

### Create Mailbox

### Storage Groups

### Servers

The `servers()` method is an alias for the `all()` method, this is purely for backwards compatibility.

$servers = $ad->exchange()->servers();

### Add Address

### Add X 400
@@ -0,0 +1,69 @@
## Folder Functions

Folder functions are used for managing OU's (organizational units).

### All

To retrieve all OU's from AD, use the `all()` method:

$folders = $ad->folder()->all();

Keep in mind that this query is by default recursive, and your results may be limited depending on your server.

### Listing

listing($folders = array(), $dnType = Adldap::ADLDAP_FOLDER, $recursive = NULL, $type = NULL)

To retrieve an OU's (organizational unit(s)) listing, use the `listing()` function. However keep in mind that this query is by default recursive!
Calling this function without listing an OU will retrieve all entries recursively.

Retrieving all data from your base DN (**not recommended if you have a large AD**):

$results = $ad->folder()->listing();

Retrieving all data from the OU `Acme`:

$results = $ad->folder()->listing(array('Acme'));

To navigate an OU, pass in deeper OUs into the array. For example:

$results = $ad->folder()->listing(array('Acme', 'Users', 'Accounting'));

// The searched DN would be: 'OU=Accounting,OU=Users,OU=Acme,DC=corp,DC=acme,DC=com'

It's also good to note, that **OUs must be spelt exact**. If an OU is not spelled correctly, you will receive zero results.

### Find

To find a folder, use the `find()` method:

$folder = $ad->folder()->find('Accounting');

### Create

To create a folder, use the `create()` method:

$attributes = [
'ou_name' => 'Employees',
'container' => [
'Users'
]
];

### Delete

To delete a distinguished name, use the `delete()` method:

$distinguishedName = 'OU=Accounting,OU=User Accounts,DC=corp,DC=acme,DC=com';

$deleted = $ad->folder()->delete($distinguishedName);

You can easily delete a found folder like so:

$folder = $ad->folder()->find('Accounting');

if(is_array($folder) && array_key_exists('dn', $folder)) {

$ad->folder()->delete($folder['dn']);

}
@@ -0,0 +1,122 @@
## Getting Started

### Basic Functionality

To connect to your Active Directory server, you'll have to provide a configuration array to a new Adldap instance.

This can be done like so:

use Adldap\Adldap;

$configuration = array(
'user_id_key' => 'samaccountname',
'account_suffix' => '@domain.local',
'person_filter' => array('category' => 'objectCategory', 'person' => 'person'),
'base_dn' => 'DC=domain,DC=local',
'domain_controllers' => array('DC1.domain.local', 'DC2.domain.local'),
'admin_username' => 'administrator',
'admin_password' => 'password',
'real_primarygroup' => true,
'use_ssl' => false,
'use_tls' => false,
'recursive_groups' => true,
'ad_port' => '389',
'sso' => false,
);

try
{
$ad = new Adldap($configuration);

echo "Awesome, we're connected!";
} catch(AdldapException $e)
{
echo "Uh oh, looks like we had an issue trying to connect: $e";
}

If you'd like to know what each configuration option means, please look at the [configuration page](CONFIGURATION.md).

When creating a new Adldap instance, it will automatically try and connect to your server, however this behavior
is completely configurable, and you can supply your own connection class to run LDAP queries off of if you wish.
See [Advanced Usage](#advanced-usage).

#### Authentication

Authenticating a user is easy, just call the `authenticate()` method like so:

$authenticated = $ad->authenticate('johndoe', 'password');

echo $authenticated; // Returns true

However, if you'd like to stay authenticated as this user, you'll have to pass in `true` as the third parameter like so:

$preventRebind = true;

$authenticated = $ad->authenticate('johndoe', 'password', $preventRebind);

Now, when you call methods on the Adldap object, you're authenticated as John Doe, instead of the administrator.

#### Retrieving the last message / error from LDAP:

To retrieve the last message or error from LDAP, call the `getLastError()` method like so:

echo $ad->getLastError();

### Advanced Usage

#### Using a different LDAP Connection

If you'd like to supply your own LDAP connection class, supply your own by inserting it into the second parameter in
the Adldap constructor. The custom class will need to implement the `Adldap\Interfaces\ConnectionInterface` OR extend
the main Connection class (`Adldap\Connections\Ldap`).

use Adldap\Connections\Ldap;

class CustomLdapConnection extends Ldap
{
public function connect($hostname, $port = '389')
{
// Connect to LDAP my own way
}
}

Then using your new class:

$connection = new CustomLdapConnection;

$ad = new Adldap($configuration, $connection);

#### Disabling auto-connect on construct

By default, when Adldap is constructed, it automatically tries to connect to your server, though you can disable this
by passing in `false` in the last construct parameter. You will have to manually call the connect method by doing this.

$ad = new Adldap($configuration, null, false);

$ad->connect();

#### Showing LDAP Warning / Errors

By default, LDAP warnings and errors are suppressed in favor of catchable exceptions thrown by Adldap. To display
warnings and errors, use the `showErrors()` method on the connection:

$ad->getLdapConnection()->showErrors();

// Now all Adldap methods will display LDAP warnings / errors if they are thrown
$ad->user()->all();

#### Overriding Adldap Classes and Methods

To override classes / methods, simply create a class that extends Adldap, and override them:

use Adldap\Adldap;

class MyAdldap extends Adldap {

// Overriding the user function to return your own User class
public function user()
{
return new MyUserClass();
}

}
@@ -0,0 +1,24 @@
## Group Functions

### Get Primary Group

To retrieve a users primary group (due to LDAP not returning the distinguished name of their primary group), use the
`getPrimaryGroup()` method:

// Get the users information
$user = $ad->user()->find('John Doe');

// Get their primary group ID
$primaryGroupId = $user['primarygroupid'];

// Get their object SID
$objectSid = $user['objectsid'];

/*
* Get the primary groups DN by
* passing in the users primary group ID
* and SID
*/
$groupDn = $ad->group()->getPrimaryGroup($primaryGroupId, $objectSid);

echo $groupDn; // Returns 'CN=Domain Users,CN=Users,DC=corp,DC=acme,DC=org'
@@ -0,0 +1,265 @@
## Searching Functions

The new Adldap Search functionality makes it really easy to query your AD server for exactly what you want. Let's get started.

To open a new search query, call the `search()` function on your AD object like so:

$ad = new Adldap($configuration);

$ad->search();

#### All

To retrieve all entries in your AD, use the all function:

$results = $ad->search()->all();

This will retrieve all entries from LDAP. However, be careful. AD has a limit of 1000 records per query (depending on your AD
server configuration of course), if your AD has more records than this, you will only see the 1000 records AD has
retrieved. LDAP will throw the following warning if this occurs:

Warning: ldap_search(): Partial search results returned: Sizelimit exceeded

However, LDAP functions are suppressed by default, so you won't see this message. If you'd like to show errors and warnings, call the `showErrors()` method
on the AD connection before performing the search like so:

$ad->getLdapConnection()->showErrors();

$results = $ad->search()->all();

#### Where

To perform a where clause on the search object, use the `where()` function:

$results = $ad->search()->where('cn', '=', 'John Doe')->get();

This query would look for an object with the common name of 'John Doe' and return the results.

We could also perform a search for all objects beginning with the common name of 'John' using the `starts_with` operator:

$results = $ad->search()->where('cn', 'starts_with', 'John')->get();

We can also search for all objects that end with the common name of `Doe` using the `ends_with` operator:

$results = $ad->search()->where('cn', 'ends_with', 'Doe')->get();

We can also search for all objects with a common name that contains `John Doe` using the `contains` operator:

$results = $ad->search()->where('cn', 'contains', 'John Doe')->get();

Or we can retrieve all objects that contain a common name attribute using the wildcard operator (`*`):

$results = $ad->search()->where('cn', '*')->get();

This type of filter syntax allows you to clearly see what your searching for.

Remember, fields are case insensitive, so it doesn't matter if you use `->where('CN', '*')` or `->where('cn', '*')`,
they would return the same result.

It's also good to know that all values inserted into a where, or an orWhere method,
<b>are escaped</b> by default into a hex string, so you don't need to worry about escaping them. For example:

$query = $ad->search()->where('cn', '=', 'test//un-escaping//')->getQuery();

// Returns '(cn=\74\65\73\74\2f\2f\75\6e\2d\65\73\63\61\70\69\6e\67\2f\2f)'

#### Or Where

To perform an 'or where' clause on the search object, use the `orWhere()` function. However, please be aware this
function performs differently than it would on a database. For example:

$results = $ad->search()
->where('cn', '=', 'John Doe')
->orWhere('cn' '=', 'Suzy Doe')
->get();

This query would return no results, because we're already defining that the common name (`cn`) must equal `John Doe`. Applying
the `orWhere()` does not amount to 'Look for an object with the common name as "John Doe" OR "Suzy Doe"'. This query would
actually amount to 'Look for an object with the common name that <b>equals</b> "John Doe" OR "Suzy Doe"

To solve the above problem, we would use `orWhere()` for both fields. For example:

$results = $ad->search()
->orWhere('cn', '=', 'John Doe')
->orWhere('cn' '=', 'Suzy Doe')
->get();

Now, we'll retrieve both John and Suzy's AD records, because the common name can equal either.

For another example, what if we wanted to retrieve

#### Select

If you'd like to include only certain fields in your search results, supply a string or an array to the `select()` method
like so:

// Selecting one field
$results = $ad->search()->select('cn')->all();

// Selecting multiple fields
$results = $ad->search()->select(array('cn', 'displayname'))->all();

All searches will return *all* information for each entry. Be sure to use `select($fields = array())` when you only
need a small amount of information.

#### Sort By

If you'd like to sort your returned results, call the `sortBy()` method like so:

// Returned results will be sorted by the common name in a descending order
$results = $ad->search()->where('cn', '=', 'John*')->sortBy('cn', 'desc')->get();

The function is case insensitive with directions, so don't worry if you use `DESC` or `desc`.

#### Query

To perform a raw LDAP query yourself, use the `query()` method:

$results = $ad->search()->query('(cn=John Doe)');

However, keep in mind the inserted query is not escaped. If you need to escape your values before the query, be sure
to do so using:

$escapedValue = $ad->getLdapConnection()->escape('John Doe');

Then you can perform the above query like so:

$results = $ad->search()->query("(cn=$escapedValue)");

#### Paginate

Pagination is useful when you have a limit on the returned results from LDAP. Using pagination, you will successfully be able
to view all LDAP results. To paginate your results, call the `paginate()` method:

$perPage = 25;

$currentPage = $_GET['page'];

$results = $ad->search()->where('objectClass', '=', 'person')->paginate($perPage, $currentPage);

<b>It's also good to know, that the current page starts at zero (zero being the first page).</b> If you'd like to present pages
differently, feel free to do so.

Paginating a search result will return a `Adldap\Objects\Paginator` instance. This object provides some handy functions:

$results = $ad->search()->where('objectClass', '=', 'person')->paginate($perPage, $currentPage);

$results->getPages(); // Returns total number of pages, int

$results->getCurrentPage(); // Returns current page number, int

$results->getPerPage(); // Returns the amount of entries allowed per page, int

$results->count(); // Returns the total amount of retrieved entries, int

// Iterate over the results like normal

foreach($results as $result)
{
echo $result['cn'];
}

#### Recursive

By default, all searches performed are recursive. If you'd like to disable recursive search, use the `recursive()` method:

$result = $ad->search()->recursive(false)->all();

This would perform an `ldap_listing()` instead of an `ldap_search()`.

#### Read

If you'd like to perform a read instead of a listing or a recursive search, use the `read()` method:

$result = $ad->search()->read(true)->where('objectClass', '*')->get();

This would perform an `ldap_read()` instead of an `ldap_listing()` or an `ldap_search()`.

#### Get Query

If you'd like to retrieve the current query to save or run it at another time, use the `getQuery()` method:

$query = $ad->search()->where('cn', '=', 'John Doe')->getQuery();

echo $query; // Returns '(cn=\4a\6f\68\6e\20\44\6f\65)'

#### Get Wheres

If you'd like to retrieve the current wheres on the search object, call the `getWheres()` method:

$wheres = $ad->search()->where('cn', '=', 'John')->getWheres();

var_dump($wheres);

#### Get Or Wheres

If you'd like to retrieve the current or wheres on the search object, call the `getOrWheres()` method:

$orWheres = $ad->search()->orWhere('cn', '=', 'John')->getOrWheres();

var_dump($orWheres);

#### Get Selects

To retrieve the current selected fields in on the search object, use the `getSelects()` method:

$selects = $ad->search()->select(array('cn', 'dn'))->getSelects();

var_dump($selects);

#### Has Selects

If you need to know if the search object currently contains selected fields, use the `hasSelects()` function:

echo $ad->search()->select('cn')->hasSelects(); // Returns true

### Examples

#### User Examples

Retrieving all users who <b>do not</b> have the common name of 'John':

$results = $ad->search()
->where('objectClass', '=', $ad->getUserIdKey())
->where('cn', '!', 'John')
->get();

Retrieving all users who do not have the common name of 'John' or 'Suzy':

$results = $ad->search()
->where('objectClass', '=', $ad->getUserIdKey())
->orWhere('cn', '!', 'John')
->orWhere('cn', '!', 'Suzy')
->get();

Retrieving all users who have a mail account:

$results = $ad->search()
->where('objectClass', '=', $ad->getUserIdKey())
->where('mail', '*')
->get();

#### Computer Examples

Retrieving a all computers:

$results = $ad->search()
->where('objectClass', '=', 'computer')
->get();

Retrieving all computers that run Windows 7:

$results = $ad->search()
->where('objectClass', '=', 'computer')
->where('operatingSystem', 'starts_with', 'Windows 7')
->get();

#### Folder (OU) examples

Retrieving a folder:

$folderName = 'Accounting';

$results = $this->adldap->search()
->where('OU', '=', $folderName)
->first();
@@ -0,0 +1,37 @@
## Upgrading to 5.0.0

Breaking changes:

- Requires have been removed from all classes, and if using without an auto-loader (such as composer) you must require all
necessary files yourself
- `adLDAP\adLDAPException` now has a new namespace: `adLDAP\Exceptions\adLDAPException`
- `$adldap->user()->modify()` now throws an `adLDAPException` when the username parameter is null
- Inserting null/empty values into the username and/or password inside the `authenticate($username, $password)` function will now
result in an `adLDAPException`, instead of returning false
- Inserting null into the group name parameter inside the method `$adLDAP->group()->info($groupName)` now throws an adLDAP exception
instead of returning false
- Inserting null into the username parameter inside the method `$adLDAP->user()->info($username)` now throws an adLDAP exception
instead of returning false
- If LDAP is not bound when running query methods (such as `$adLDAP->search()`) then an `adLDAPException` will be thrown instead
of previously returning false.
- `pingController()` method removed


The arguments for finding a user has been changed from:

$adldap->user()->find($includeDescription = false, $searchField = false, $searchFilter = false, $sorted = true)

To:

$adldap->user()->find($includeDescription = false, $searchArray = array(), $sorted = true))

This allows you to search for multiple parameters when looking for a user. [Thanks To](https://github.com/adldap/adLDAP/pull/17)


$adldap->group()->search($sAMAaccountType = Adldap::ADLDAP_SECURITY_GLOBAL_GROUP, $includeDescription = false, $search = "*", $sorted = true);

Has Changed to:

$adldap->group()->search($sAMAaccountType = Adldap::ADLDAP_SECURITY_GLOBAL_GROUP, $select = array(), $sorted = true);

Removed function $adldap->group()->cn();
@@ -0,0 +1,96 @@
## User Functions

### All

To retrieve all users on AD, use the `all()` method:

$users = $ad->user()->all();

### Find

To retrieve all information on a user, use `find()` method:

$username = 'jdoe';

$user = $ad->user()->find($username);

If you're only interested in certain LDAP fields, insert your fields in the second parameter:

$username = 'jdoe';

$fields = [
'cn',
'memberof'
];

$user = $ad->user()->find($username, $fields);

echo $user['cn'];
echo $user['memberof'];

### Info

The `info()` method is an alias for the `find()` method, this exists for backwards compatibility.

$username = 'jdoe';

$user = $ad->user()->info($username);

### DN

To retrieve a users full distinguished name, use the `dn()` method:

$username = 'jdoe';

$dn = $ad->user()->dn($username);

### Create

### Modify

### Delete

To delete a user, use the `delete()` method:

$username = 'jdoe';

$deleted = $ad->user()->delete($username);

### Change Password

To change a users password, use the `changePassword()` method:

try
{
$newPassword = 'newpassword123';

$oldPassword = 'oldpassword123';

$changed = $ad->user()->changePassword('jdoe', $newPassword, $oldPassword);

} catch(Adldap\Exceptions\WrongPasswordException $e)
{
return "Uh oh, you've entered the wrong old password!";
} catch(Adldap\Exceptions\PasswordPolicyException $e)
{
return "Looks like your new password doesn't meet our requirements. Try again."
}

### Password Expiry

To retrieve a users password expiry date, use the `passwordExpiry()` method:

$results = $ad->user()->passwordExpiry('jdoe'); // Returns array|bool

$results['expires']; // Returns true / false if the users password expires
$results['has_expired']; // Returns true / false if the users password **has** expired
$results['expiry_timestamp']; // Returns the users password expiry date in unix time
$results['expiry_formatted']; // Returns the users password expiry date in a formatted string ('YYYY-MM-DD HH:MM:SS')

### Get Last Logon

To retrieve a users last login time, use the `getLastLogon()` method:

$time = $ad->user()->getLastLogon('jdoe'); // Returns in Unix time

$date = date('Y-m-d h:i:s', $time);
@@ -0,0 +1,139 @@
<?php

use adLDAP\adLDAP;

/*
Examples file
To test any of the functions, just change the 0 to a 1.
*/

//error_reporting(E_ALL ^ E_NOTICE);

include dirname(__FILE__).'/../lib/adLDAP/adLDAP.php';

try {
$adldap = new adLDAP($options);
} catch (adLDAPException $e) {
echo $e;
exit();
}
//var_dump($ldap);

echo("<pre>\n");

// authenticate a username/password
if (0) {
$result = $adldap->authenticate('username', 'password');
var_dump($result);
}

// add a group to a group
if (0) {
$result = $adldap->group()->addGroup('Parent Group Name', 'Child Group Name');
var_dump($result);
}

// add a user to a group
if (0) {
$result = $adldap->group()->addUser('Group Name', 'username');
var_dump($result);
}

// create a group
if (0) {
$attributes = array(
'group_name' => 'Test Group',
'description' => 'Just Testing',
'container' => array('Groups','A Container'),
);
$result = $adldap->group()->create($attributes);
var_dump($result);
}

// retrieve information about a group
if (0) {
// Raw data array returned
$result = $adldap->group()->info('Group Name');
var_dump($result);
}

// create a user account
if (0) {
$attributes = array(
'username' => 'freds',
'logon_name' => 'freds@mydomain.local',
'firstname' => 'Fred',
'surname' => 'Smith',
'company' => 'My Company',
'department' => 'My Department',
'email' => 'freds@mydomain.local',
'container' => array('Container Parent','Container Child'),
'enabled' => 1,
'password' => 'Password123',
);

try {
$result = $adldap->user()->create($attributes);
var_dump($result);
} catch (adLDAPException $e) {
echo $e;
exit();
}
}

// retrieve the group membership for a user
if (0) {
$result = $adldap->user()->groups('username');
print_r($result);
}

// retrieve information about a user
if (0) {
// Raw data array returned
$result = $adldap->user()->info('username');
print_r($result);
}

// check if a user is a member of a group
if (0) {
$result = $adldap->user()->inGroup('username', 'Group Name');
var_dump($result);
}

// modify a user account (this example will set "user must change password at next logon")
if (0) {
$attributes = array(
'change_password' => 1,
);
$result = $adldap->user()->modify('username', $attributes);
var_dump($result);
}

// change the password of a user. It must meet your domain's password policy
if (0) {
try {
$result = $adldap->user()->password('username', 'Password123');
var_dump($result);
} catch (adLDAPException $e) {
echo $e;
exit();
}
}

// see a user's last logon time
if (0) {
try {
$result = $adldap->user()->getLastLogon('username');
var_dump(date('Y-m-d H:i:s', $result));
} catch (adLDAPException $e) {
echo $e;
exit();
}
}

// list the contents of the Users OU
if (0) {
$result = $adldap->folder()->listing(array('Users'), adLDAP::ADLDAP_FOLDER, false);
var_dump($result);
}
@@ -0,0 +1,22 @@
<?php

/*
Test for the new user collections object
*/

//error_reporting(E_ALL ^ E_NOTICE);

include dirname(__FILE__).'/../lib/adLDAP/adLDAP.php';
try {
$adldap = new adLDAP($options);
} catch (adLDAPException $e) {
echo $e;
exit();
}

echo("<pre>\n");

$collection = $adldap->group()->infoCollection('groupname');

print_r($collection->member);
print_r($collection->description);
@@ -0,0 +1,71 @@
<?php

/**
* This file contains a working example of adLDAP in operation.
*
* @file
*/
if (!file_exists(__DIR__.'/../vendor/autoload.php')) {
echo 'Before using this demo, please run <code>composer dump-autoload</code>';
exit(1);
}
require_once __DIR__.'/../vendor/autoload.php';

use adLDAP\adLDAP;
use adLDAP\Exceptions\adLDAPException;

// Set up all options.
$options = [
'account_suffix' => '',
'base_dn' => null,
'domain_controllers' => [''],
'admin_username' => null,
'admin_password' => null,
'real_primarygroup' => '',
'use_ssl' => false,
'use_tls' => false,
'recursive_groups' => true,
'ad_port' => adLDAP::ADLDAP_LDAP_PORT,
'sso' => '',
];
// Update options from $_POST.
foreach ($options as $optName => $defaultValue) {
if (isset($_POST[$optName])) {
$options[$optName] = $_POST[$optName];
}
}
// Validate options.
$options['domain_controllers'] = array_filter($options['domain_controllers']);

// Try to bind.
$adldap = false;
$exception = false;
if (is_array($options['domain_controllers']) && !empty($options['domain_controllers'][0])) {
try {
$adldap = new adLDAP($options);
// To pass through to the form:
$options['base_dn'] = $adldap->getBaseDn();
$options['ad_port'] = $adldap->getPort();
} catch (adLDAPException $e) {
$exception = $e;
}
}

// Handle log in.
$username = (!empty($_POST['username'])) ? $_POST['username'] : '';
$info = false;
if ($adldap && !empty($username)) {
$password = $_POST['password'];
try {
$adldap->authenticate($username, $password);
$info = $adldap->user()->info($username, ['*']);
if (isset($info[0])) {
$info = $info[0];
}
} catch (\adLDAP\Exceptions\adLDAPException $e) {
$exception = $e;
}
}

// Hand everything over to the view for display.
require 'view.html.php';
@@ -0,0 +1,22 @@
<?php

/*
Test for the new user collections object
*/

//error_reporting(E_ALL ^ E_NOTICE);

include dirname(__FILE__).'/../lib/adLDAP/adLDAP.php';
try {
$adldap = new adLDAP($options);
} catch (adLDAPException $e) {
echo $e;
exit();
}

echo("<pre>\n");

$collection = $adldap->user()->infoCollection('username');

print_r($collection->memberOf);
print_r($collection->displayName);
@@ -0,0 +1,198 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>adLDAP example</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<h1><a href="index.php">adLDAP example</a></h1>

<?php if ($exception): ?>
<p class="alert alert-danger"><?php echo $exception->getMessage() ?></p>
<?php endif ?>

<form method="post" action="index.php" class="inline">
<div class="row">
<div class="col-md-6">
<h2>Options</h2>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>account_suffix</code></label>
</div>
<div class="col-md-8">
<input type="text" name="account_suffix" value="<?php echo $options['account_suffix'] ?>"
class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>base_dn</code></label>
</div>
<div class="col-md-8">
<input type="text" name="base_dn" value="<?php echo $options['base_dn'] ?>"
class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>domain_controllers</code></label>
</div>
<div class="col-md-8">
<?php foreach ($options['domain_controllers'] as $dc): ?>
<input type="text" name="domain_controllers[]" class="form-control" value="<?php echo $dc ?>" />
<?php endforeach ?>
<input type="text" name="domain_controllers[]" class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>admin_username</code></label>
</div>
<div class="col-md-8">
<input type="text" name="admin_username" value="<?php echo $options['admin_username'] ?>"
class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>admin_password</code></label>
</div>
<div class="col-md-8">
<input type="password" name="admin_password" value="<?php echo $options['admin_password'] ?>"
class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>real_primarygroup</code></label>
</div>
<div class="col-md-8">
<input type="text" name="real_primarygroup" value="<?php echo $options['real_primarygroup'] ?>"
class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>use_ssl</code></label>
</div>
<div class="col-md-8">
<input type="checkbox" name="use_ssl" value="<?php echo $options['use_ssl'] ?>"
class="form-control" <?php if ($options['use_ssl']) {
echo 'checked';
} ?> />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>use_tls</code></label>
</div>
<div class="col-md-8">
<input type="checkbox" name="use_tls" value="<?php echo $options['use_tls'] ?>"
class="form-control" <?php if ($options['use_tls']) {
echo 'checked';
} ?> />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>recursive_groups</code></label>
</div>
<div class="col-md-8">
<input type="checkbox" name="recursive_groups" value="<?php echo $options['recursive_groups'] ?>"
class="form-control" <?php if ($options['recursive_groups']) {
echo 'checked';
} ?> />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>ad_port</code></label>
</div>
<div class="col-md-8">
<input type="text" name="ad_port" value="<?php echo $options['ad_port'] ?>"
class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="inline"><code>sso</code></label>
</div>
<div class="col-md-8">
<input type="checkbox" name="sso" value="<?php echo $options['sso'] ?>"
class="form-control" <?php if ($options['sso']) {
echo 'checked';
} ?> />
</div>
</div>

<h2>Log In</h2>
<div class="row">
<div class="col-md-4">
<label class="" for="username">Username:</label>
</div>
<div class="col-md-8">
<input type="text" name="username" value="<?php echo $username ?>" id="username"
class="form-control" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<label class="" for="password">Password:</label>
</div>
<div class="col-md-8">
<input type="password" name="password" id="password" class="form-control" />
</div>
</div>
</div>


<div class="col-md-6">
<h2>
Info
<input type="submit" class="btn btn-primary" value="Get Info" />
<a href="index.php" class="btn btn-default">Reset</a>
</h2>
<?php if (!$adldap): ?>
<p class="alert alert-info">
Please enter at least a domain controller at left,
then hit 'Get Info' above.
</p>
<?php endif ?>
<?php if ($adldap && $adldap->getLdapBind()): ?>
<p class="alert alert-success">
Bound successfully.
</p>
<?php endif ?>
<?php if ($info): ?>
<h3>User info:</h3>
<dl>
<?php foreach ($info as $key => $val): ?>
<?php if (!is_string($key)) {
continue;
} ?>
<dt><?php echo $key ?></dt>
<dd><?php var_dump($val) ?></dd>
<?php endforeach ?>
</dl>
<?php endif ?>
</div>

</div>
</form>


</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
</body>
</html>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="adLDAP Test Suite">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

Large diffs are not rendered by default.

@@ -0,0 +1,53 @@
<?php

namespace Adldap\Classes;

use Adldap\Adldap;

/**
* The Base Adldap class.
*
* Class AdldapBase
*/
abstract class AbstractAdldapBase
{
/**
* The current Adldap connection via dependency injection.
*
* @var Adldap
*/
protected $adldap;

/**
* The current Adldap connection.
*
* @var \Adldap\Interfaces\ConnectionInterface
*/
protected $connection;

/**
* Constructor.
*
* @param Adldap $adldap
*/
public function __construct(Adldap $adldap)
{
$this->adldap = $adldap;

$connection = $adldap->getLdapConnection();

if ($connection) {
$this->connection = $connection;
}
}

/**
* Returns the current Adldap instance.
*
* @return Adldap
*/
public function getAdldap()
{
return $this->adldap;
}
}
@@ -0,0 +1,182 @@
<?php

namespace Adldap\Classes;

/**
* The AdldapQueryable class. This class provides
* some standard methods to queryable classes that
* extend this class.
*
* A 'queryable' class means that any Class that extends
* this class must query and return information from
* LDAP based on it's object class property.
*
* Class AdldapQueryable
*/
abstract class AbstractAdldapQueryable extends AbstractAdldapBase
{
/**
* The LDAP objects class name.
*
* @var string
*/
public $objectClass = '';

/**
* Returns all entries with the current object class.
*
* @param array $fields
* @param bool $sorted
* @param string $sortBy
* @param string $sortByDirection
*
* @return array|bool
*/
public function all($fields = [], $sorted = true, $sortBy = 'cn', $sortByDirection = 'asc')
{
$search = $this->adldap->search()
->select($fields)
->where('objectClass', '=', $this->objectClass);

if ($sorted) {
$search->sortBy($sortBy, $sortByDirection);
}

return $search->get();
}

/**
* Finds a single entry using the objects current class
* and the specified common name. If fields are specified,
* then only those fields are returned in the result array.
*
* @param string $name
* @param array $fields
*
* @return array|bool
*/
public function find($name, $fields = [])
{
$results = $this->adldap->search()
->select($fields)
->where('objectClass', '=', $this->objectClass)
->where('anr', '=', $name)
->first();

if (count($results) > 0) {
return $results;
}

return false;
}

/**
* Returns the DN of the current object class.
*
* @param string $name
*
* @return string|bool
*/
public function dn($name)
{
$info = $this->find($name);

if (is_array($info) && array_key_exists('dn', $info)) {
return $info['dn'];
}

return false;
}

/**
* Delete a distinguished name from Active Directory.
*
* @param string $dn The distinguished name to delete
*
* @return bool
*/
public function delete($dn)
{
$this->adldap->utilities()->validateNotNullOrEmpty('Distinguished Name [dn]', $dn);

return $this->connection->delete($dn);
}

/**
* Alias for the find() method.
*
* @param string $name The name of the computer
* @param array $fields Attributes to return
*
* @return array|bool
*/
public function info($name, $fields = [])
{
return $this->find($name, $fields);
}

/**
* Returns true / false if the specified group exists
* on the found LDAP entry.
*
* @param string $name
* @param string $group
* @param null $recursive
*
* @return bool
*/
public function inGroup($name, $group, $recursive = null)
{
if ($recursive === null) {
$recursive = $this->adldap->getRecursiveGroups();
}

// Get a list of the groups
$groups = $this->groups($name, $recursive);

// Return true if the specified group is in the group list
if (is_array($groups) && in_array($group, $groups)) {
return true;
}

return false;
}

/**
* Finds the LDAP entry with the specified name, and returns
* the groups that it is a member of.
*
* @param string $name
* @param null $recursive
*
* @return array|bool
*/
public function groups($name, $recursive = null)
{
if ($recursive === null) {
$recursive = $this->adldap->getRecursiveGroups();
}

$info = $this->find($name);

if (is_array($info) && array_key_exists('memberof', $info)) {
$groups = $this->adldap->utilities()->niceNames($info['memberof']);

if ($recursive === true) {
foreach ($groups as $id => $groupName) {
$extraGroups = $this->adldap->group()->recursiveGroups($groupName);

$groups = array_merge($groups, $extraGroups);
}
}

/*
* We'll return a filtered array and
* make sure every entry is unique
*/
return array_unique($groups);
}

return false;
}
}
@@ -0,0 +1,51 @@
<?php

namespace Adldap\Classes;

/**
* Ldap Computer Management.
*
* Class AdldapComputers
*/
class AdldapComputers extends AbstractAdldapQueryable
{
/**
* The computers object class name.
*
* @var string
*/
public $objectClass = 'computer';

/**
* Get the groups a computer is in.
*
* @param string $computerName The name of the computer
* @param null $recursive Whether to check recursively
*
* @return array|bool
*/
public function groups($computerName, $recursive = null)
{
if ($recursive === null) {
$recursive = $this->adldap->getRecursiveGroups();
}

$info = $this->find($computerName);

if (is_array($info) && array_key_exists('memberof', $info)) {
$groups = $this->adldap->utilities()->niceNames($info['memberof']);

if ($recursive === true) {
foreach ($groups as $id => $groupName) {
$extraGroups = $this->adldap->group()->recursiveGroups($groupName);

$groups = array_merge($groups, $extraGroups);
}
}

return $groups;
}

return false;
}
}
@@ -0,0 +1,108 @@
<?php

namespace Adldap\Classes;

use Adldap\Objects\Contact;

/**
* Ldap Contacts Management.
*
* Class AdldapContacts
*/
class AdldapContacts extends AbstractAdldapQueryable
{
/**
* The contacts object class name.
*
* @var string
*/
public $objectClass = 'contact';

/**
* Create a contact.
*
* @param array $attributes The attributes to set to the contact
*
* @return bool|string
*/
public function create(array $attributes)
{
$contact = new Contact($attributes);

$contact->validateRequired();

// Translate the schema
$add = $this->adldap->ldapSchema($attributes);

// Set the cn to the contacts display name
$add['cn'][0] = $contact->{'display_name'};

$add['objectclass'][0] = 'top';
$add['objectclass'][1] = 'person';
$add['objectclass'][2] = 'organizationalPerson';
$add['objectclass'][3] = 'contact';

if (!$contact->hasAttribute('exchange_hidefromlists')) {
$add['msExchHideFromAddressLists'][0] = 'TRUE';
}

// Determine the container
$attributes['container'] = array_reverse($attributes['container']);

$container = 'OU='.implode(',OU=', $attributes['container']);

$dn = 'CN='.$this->adldap->utilities()->escapeCharacters($add['cn'][0]).', '.$container.','.$this->adldap->getBaseDn();

// Add the entry
return $this->connection->add($dn, $add);
}

/**
* Modify a contact. Note if you set the enabled
* attribute you must not specify any other attributes.
*
* @param string $contactName The contact to query
* @param array $attributes The attributes to modify
*
* @return bool|string
*/
public function modify($contactName, $attributes)
{
$contactDn = $this->dn($contactName);

if ($contactDn) {
// Translate the update to the LDAP schema
$mod = $this->adldap->ldapSchema($attributes);

// Check to see if this is an enabled status update
if (!$mod) {
return false;
}

// Do the update
return $this->connection->modify($contactDn, $mod);
}

return false;
}

/**
* Mail enable a contact. Allows email to be sent to them through Exchange.
*
* @param string $contactName The contacts name
* @param string $emailAddress The contacts email address
* @param null $mailNickname
*
* @return bool
*/
public function contactMailEnable($contactName, $emailAddress, $mailNickname = null)
{
$contactDn = $this->dn($contactName);

if ($contactDn) {
return $this->adldap->exchange()->contactMailEnable($contactDn, $emailAddress, $mailNickname);
}

return false;
}
}

Large diffs are not rendered by default.

@@ -0,0 +1,143 @@
<?php

namespace Adldap\Classes;

use Adldap\Objects\Folder;
use Adldap\Adldap;

/**
* Ldap Folder / OU management.
*
* Class AdldapFolders
*/
class AdldapFolders extends AbstractAdldapQueryable
{
/**
* Returns all entries with the current object class.
*
* @param array $fields
* @param bool $sorted
* @param string $sortBy
* @param string $sortByDirection
*
* @return array|bool
*/
public function all($fields = [], $sorted = true, $sortBy = 'name', $sortByDirection = 'asc')
{
$search = $this->adldap->search()
->select($fields)
->where('objectClass', '*')
->where('distinguishedname', '!', $this->adldap->getBaseDn());

if ($sorted) {
$search->sortBy($sortBy, $sortByDirection);
}

return $search->get();
}

/**
* Finds a single entry using the objects current class
* and the specified common name.
*
* @param string $name
* @param array $fields
*
* @return array|bool
*/
public function find($name, $fields = [])
{
$results = $this->adldap->search()
->select($fields)
->where('OU', '=', $name)
->first();

if (count($results) > 0) {
return $results;
}

return false;
}

/**
* Returns a folder listing for a specific OU.
* See http://adldap.sourceforge.net/wiki/doku.php?id=api_folder_functions.
*
* If folderName is set to NULL this will list the root, strongly recommended
* to set $recursive to false in that instance!
*
* @param array $folders
* @param string $dnType
* @param null $recursive
* @param null $type
*
* @return array|bool
*/
public function listing($folders = [], $dnType = Adldap::ADLDAP_FOLDER, $recursive = null, $type = null)
{
$search = $this->adldap->search();

if (is_array($folders) && count($folders) > 0) {
/*
* Reverse the folder array so it's more
* akin to navigating a folder structure
*/
$folders = array_reverse($folders);

/*
* Get the combined OU string for the search.
*
* ex. OU=Users,OU=Acme
*/
$ou = $dnType.'='.implode(','.$dnType.'=', $folders);

$search->where('distinguishedname', '!', $ou.$this->adldap->getBaseDn());

// Apply the OU to the base DN
$dn = $ou.','.$this->adldap->getBaseDn();

$search->setDn($dn);
} else {
$search->where('distinguishedname', '!', $this->adldap->getBaseDn());
}

if ($type === null) {
$search->where('objectClass', '*');
} else {
$search->where('objectClass', '=', $type);
}

if ($recursive === false) {
$search->recursive(false);
}

return $search->get();
}

/**
* Create an organizational unit.
*
* @param array $attributes Default attributes of the ou
*
* @return bool|string
*/
public function create(array $attributes)
{
$folder = new Folder($attributes);

$folder->validateRequired();

$folder->setAttribute('container', array_reverse($folder->getAttribute('container')));

$add = [];

$add['objectClass'] = 'organizationalUnit';
$add['OU'] = $folder->getAttribute('ou_name');

$containers = 'OU='.implode(',OU=', $folder->getAttribute('container'));

$dn = 'OU='.$add['OU'].', '.$containers.$this->adldap->getBaseDn();

return $this->connection->add($dn, $add);
}
}
@@ -0,0 +1,373 @@
<?php

namespace Adldap\Classes;

use Adldap\Objects\Group;
use Adldap\Adldap;

/**
* Ldap Group management.
*
* Class AdldapGroups
*/
class AdldapGroups extends AbstractAdldapQueryable
{
/**
* The groups object category string.
*
* @var string
*/
public $objectCategory = 'group';

/**
* The groups object class string.
*
* @var string
*/
public $objectClass = 'group';

/**
* Returns a complete list of the groups in AD based on a SAM Account Type.
*
* @param int $sAMAaccountType The account type to return
* @param array $select The fields you want to retrieve for each
* @param bool $sorted Whether to sort the results
*
* @return array|bool
*/
public function search($sAMAaccountType = Adldap::ADLDAP_SECURITY_GLOBAL_GROUP, $select = [], $sorted = true)
{
$search = $this->adldap->search()
->select($select)
->where('objectCategory', '=', 'group');

if ($sAMAaccountType !== null) {
$search->where('samaccounttype', '=', $sAMAaccountType);
}

if ($sorted) {
$search->sortBy('samaccountname', 'asc');
}

return $search->get();
}

/**
* Add a group to a group.
*
* @param string $parent The parent group name
* @param string $child The child group name
*
* @return bool
*/
public function addGroup($parent, $child)
{
// Find the parent group's dn
$parentDn = $this->dn($parent);

$childDn = $this->dn($child);

if ($parentDn && $childDn) {
$add['member'] = $childDn;

// Add the child to the parent group and return the result
return $this->connection->modAdd($parentDn, $add);
}

return false;
}

/**
* Add a user to a group.
*
* @param string $groupName The group to add the user to
* @param string $username The user to add to the group
*
* @return bool
*/
public function addUser($groupName, $username)
{
$groupDn = $this->dn($groupName);

$userDn = $this->adldap->user()->dn($username);

if ($groupDn && $userDn) {
$add['member'] = $userDn;

return $this->connection->modAdd($groupDn, $add);
}

return false;
}

/**
* Add a contact to a group.
*
* @param string $groupName The group to add the contact to
* @param string $contactDn The DN of the contact to add
*
* @return bool
*/
public function addContact($groupName, $contactDn)
{
$groupDn = $this->dn($groupName);

if ($groupDn && $contactDn) {
$add = [];
$add['member'] = $contactDn;

return $this->connection->modAdd($groupDn, $add);
}

return false;
}

/**
* Create a group.
*
* @param array $attributes Default attributes of the group
*
* @return bool|string
*/
public function create(array $attributes)
{
$group = new Group($attributes);

$group->validateRequired();

// Reset the container by reversing the current container
$group->setAttribute('container', array_reverse($group->getAttribute('container')));

$add['cn'] = $group->getAttribute('group_name');
$add['samaccountname'] = $group->getAttribute('group_name');
$add['objectClass'] = 'Group';
$add['description'] = $group->getAttribute('description');

$container = 'OU='.implode(',OU=', $group->getAttribute('container'));

$dn = 'CN='.$add['cn'].', '.$container.','.$this->adldap->getBaseDn();

return $this->connection->add($dn, $add);
}

/**
* Rename a group.
*
* @param string $groupName The group to rename
* @param string $newName The new name to give the group
* @param array $container
*
* @return bool
*/
public function rename($groupName, $newName, $container)
{
$groupDn = $this->dn($groupName);

if ($groupDn) {
$newRDN = 'CN='.$newName;

// Determine the container
$container = array_reverse($container);
$container = 'OU='.implode(', OU=', $container);

$dn = $container.', '.$this->adldap->getBaseDn();

return $this->connection->rename($groupDn, $newRDN, $dn, true);
}

return false;
}

/**
* Remove a group from a group.
*
* @param string $parentName The parent group name
* @param string $childName The child group name
*
* @return bool
*/
public function removeGroup($parentName, $childName)
{
$parentDn = $this->dn($parentName);

$childDn = $this->dn($childName);

if (is_string($parentDn) && is_string($childDn)) {
$del = [];
$del['member'] = $childDn;

return $this->connection->modDelete($parentDn, $del);
}

return false;
}

/**
* Remove a user from a group.
*
* @param string $groupName The group to remove a user from
* @param string $username The AD user to remove from the group
*
* @return bool
*/
public function removeUser($groupName, $username)
{
$groupDn = $this->dn($groupName);

$userDn = $this->adldap->user()->dn($username);

if (is_string($groupDn) && is_string($userDn)) {
$del = [];
$del['member'] = $userDn;

return $this->connection->modDelete($groupDn, $del);
}

return false;
}

/**
* Remove a contact from a group.
*
* @param string $group The group to remove the contact from
* @param string $contactName The contact to remove
*
* @return bool
*/
public function removeContact($group, $contactName)
{
// Find the parent dn
$groupDn = $this->dn($group);

$contactDn = $this->adldap->contact()->dn($contactName);

if (is_string($groupDn) && is_string($contactDn)) {
$del = [];
$del['member'] = $contactDn;

return $this->connection->modDelete($groupDn, $del);
}

return false;
}

/**
* Return a list of members in a group.
*
* @param string $group The group to query
* @param array $fields The fields to retrieve for each member
*
* @return array|bool
*/
public function members($group, $fields = [])
{
$group = $this->find($group);

if (is_array($group) && array_key_exists('member', $group)) {
$members = [];

foreach ($group['member'] as $member) {
$members[] = $this->adldap->search()
->setDn($member)
->select($fields)
->where('objectClass', '=', 'user')
->where('objectClass', '=', 'person')
->first();
}

return $members;
}

return false;
}

/**
* Return a complete list of "groups in groups".
*
* @param string $groupName The group to get the list from
*
* @return array|bool
*/
public function recursiveGroups($groupName)
{
$groups = [];

$info = $this->find($groupName);

if (is_array($info) && array_key_exists('cn', $info)) {
$groups[] = $info['cn'];

if (array_key_exists('memberof', $info)) {
if (is_array($info['memberof'])) {
foreach ($info['memberof'] as $group) {
$explodedDn = $this->connection->explodeDn($group);

$groups = array_merge($groups, $this->recursiveGroups($explodedDn[0]));
}
}
}
}

return $groups;
}

/**
* Returns a complete list of security groups in AD.
*
* @param bool $includeDescription Whether to return a description
* @param string $search Search parameters
* @param bool $sorted Whether to sort the results
*
* @return array|bool
*/
public function allSecurity($includeDescription = false, $search = '*', $sorted = true)
{
return $this->search(Adldap::ADLDAP_SECURITY_GLOBAL_GROUP, $includeDescription, $search, $sorted);
}

/**
* Returns a complete list of distribution lists in AD.
*
* @param bool $includeDescription Whether to return a description
* @param string $search Search parameters
* @param bool $sorted Whether to sort the results
*
* @return array|bool
*/
public function allDistribution($includeDescription = false, $search = '*', $sorted = true)
{
return $this->search(Adldap::ADLDAP_DISTRIBUTION_GROUP, $includeDescription, $search, $sorted);
}

/**
* Coping with AD not returning the primary group
* http://support.microsoft.com/?kbid=321360.
*
* This is a re-write based on code submitted by Bruce which prevents the
* need to search each security group to find the true primary group
*
* @param string $groupId Group ID
* @param string $userId User's Object SID
*
* @return bool
*/
public function getPrimaryGroup($groupId, $userId)
{
$this->adldap->utilities()->validateNotNull('Group ID', $groupId);
$this->adldap->utilities()->validateNotNull('User ID', $userId);

$groupId = substr_replace($userId, pack('V', $groupId), strlen($userId) - 4, 4);

$sid = $this->adldap->utilities()->getTextSID($groupId);

$result = $this->adldap->search()
->where('objectsid', '=', $sid)
->first();

if (is_array($result) && array_key_exists('dn', $result)) {
return $result['dn'];
}

return false;
}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,355 @@
<?php

namespace Adldap\Classes;

use Adldap\Exceptions\AdldapException;

/**
* AdLDAP Utility Functions.
*
* Class AdldapUtils
*/
class AdldapUtils extends AbstractAdldapBase
{
/**
* Take an LDAP query and return the nice names, without all the LDAP prefixes (eg. CN, DN).
*
* @param array $groups
*
* @return array
*/
public function niceNames($groups)
{
$groupNames = [];

if (is_array($groups) && count($groups) > 0) {
foreach ($groups as $group) {
$explodedDn = $this->connection->explodeDn($group);

// Assuming the zero key is the CN
$groupNames[] = $explodedDn[0];
}
} elseif (is_string($groups)) {
// If there's a single entry, groups will be a string
$explodedDn = $this->connection->explodeDn($groups);

$groupNames[] = $explodedDn[0];
}

return $groupNames;
}

/**
* Escape characters for use in an ldap_create function.
*
* @param string $str
*
* @return string
*/
public function escapeCharacters($str)
{
return str_replace(',', "\,", $str);
}

/**
* Converts a string GUID to a hexdecimal value so it can be queried.
*
* @param string $strGUID A string representation of a GUID
*
* @return string
*/
public function strGuidToHex($strGUID)
{
$strGUID = str_replace('-', '', $strGUID);

$octet_str = '\\'.substr($strGUID, 6, 2);
$octet_str .= '\\'.substr($strGUID, 4, 2);
$octet_str .= '\\'.substr($strGUID, 2, 2);
$octet_str .= '\\'.substr($strGUID, 0, 2);
$octet_str .= '\\'.substr($strGUID, 10, 2);
$octet_str .= '\\'.substr($strGUID, 8, 2);
$octet_str .= '\\'.substr($strGUID, 14, 2);
$octet_str .= '\\'.substr($strGUID, 12, 2);

$length = (strlen($strGUID) - 2);

for ($i = 16; $i <= $length; $i++) {
if (($i % 2) == 0) {
$octet_str .= '\\'.substr($strGUID, $i, 2);
}
}

return $octet_str;
}

/**
* Convert a binary SID to a text SID.
*
* @param string $binsid A Binary SID
*
* @return string
*/
public function getTextSID($binsid)
{
$hex_sid = bin2hex($binsid);

$rev = hexdec(substr($hex_sid, 0, 2));

$subcount = hexdec(substr($hex_sid, 2, 2));

$auth = hexdec(substr($hex_sid, 4, 12));

$result = "$rev-$auth";

$subauth = [];

for ($x = 0;$x < $subcount; $x++) {
$subauth[$x] = hexdec($this->littleEndian(substr($hex_sid, 16 + ($x * 8), 8)));

$result .= '-'.$subauth[$x];
}

// Cheat by tacking on the S-
return 'S-'.$result;
}

/**
* Converts a little-endian hex number to one that hexdec() can convert.
*
* @param string $hex A hex code
*
* @return string
*/
public function littleEndian($hex)
{
$result = '';

for ($x = strlen($hex) - 2; $x >= 0; $x = $x - 2) {
$result .= substr($hex, $x, 2);
}

return $result;
}

/**
* Converts a binary attribute to a string.
*
* @param string $bin A binary LDAP attribute
*
* @return string
*/
public function binaryToText($bin)
{
$hex_guid = bin2hex($bin);

$hex_guid_to_guid_str = '';

for ($k = 1; $k <= 4; ++$k) {
$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
}

$hex_guid_to_guid_str .= '-';

for ($k = 1; $k <= 2; ++$k) {
$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
}

$hex_guid_to_guid_str .= '-';

for ($k = 1; $k <= 2; ++$k) {
$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
}

$hex_guid_to_guid_str .= '-'.substr($hex_guid, 16, 4);

$hex_guid_to_guid_str .= '-'.substr($hex_guid, 20);

return strtoupper($hex_guid_to_guid_str);
}

/**
* Converts a binary GUID to a string GUID.
*
* @param string $binaryGuid The binary GUID attribute to convert
*
* @return string
*/
public function decodeGuid($binaryGuid)
{
$this->validateNotNull('Binary GUID', $binaryGuid);

$strGUID = $this->binaryToText($binaryGuid);

return $strGUID;
}

/**
* Convert a boolean value to a string.
* You should never need to call this yourself.
*
* @param bool $bool Boolean value
*
* @return string
*/
public function boolToStr($bool)
{
return ($bool) ? 'TRUE' : 'FALSE';
}

/**
* Convert 8bit characters e.g. accented characters to UTF8 encoded characters.
*
* @param $item
* @param $key
*/
public function encode8Bit(&$item, $key)
{
$encode = false;

if (is_string($item)) {
$length = strlen($item);

for ($i = 0; $i < $length; $i++) {
if (ord($item[$i]) >> 7) {
$encode = true;
}
}
}

if ($encode === true && $key != 'password') {
$item = utf8_encode($item);
}
}

/**
* 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 DN string to array.
*
* @param $dnStr
* @param bool $excludeBaseDn exclude base DN from results
*
* @return array
*/
public function dnStrToArr($dnStr, $excludeBaseDn = true, $includeAttributes = false)
{
if ($excludeBaseDn) {
return ldap_explode_dn($dnStr, ($includeAttributes ? 0 : 1));
} else {
return ldap_explode_dn($this->adldap->getBaseDn().$dnStr, ($includeAttributes ? 0 : 1));
}
}

/**
* Validates if the function Bcmod exists.
* Throws an exception otherwise.
*
* @return bool
*
* @throws AdldapException
*/
public function validateBcmodExists()
{
if (!function_exists('bcmod')) {
$message = 'Missing function support [bcmod] http://php.net/manual/en/function.bcmod.php';

throw new AdldapException($message);
}

return true;
}

/**
* Validates that the current LDAP connection is bound. This
* will throw an AdldapException otherwise.
*
* @return bool
*
* @throws AdldapException
*/
public function validateLdapIsBound()
{
if ($this->adldap->getLdapBind()) {
return true;
}

$message = 'No LDAP connection is currently bound.';

throw new AdldapException($message);
}

/**
* Validates that the inserted value is not null or empty. This
* will throw an AdldapException otherwise.
*
* @param string $parameter
* @param string $value
*
* @return bool
*
* @throws AdldapException
*/
public function validateNotNullOrEmpty($parameter, $value)
{
$this->validateNotNull($parameter, $value);

$this->validateNotEmpty($parameter, $value);

return true;
}

/**
* Validates that the inserted value of the specified parameter
* is not null. This will throw an AdldapException otherwise.
*
* @param string $parameter
* @param mixed $value
*
* @return bool
*
* @throws AdldapException
*/
public function validateNotNull($parameter, $value)
{
if ($value !== null) {
return true;
}

$message = sprintf('Parameter: %s cannot be null.', $parameter);

throw new AdldapException($message);
}

/**
* Validates that the inserted value of the specified parameter
* is not empty. This will throw an AdldapException otherwise.
*
* @param string $parameter
* @param mixed $value
*
* @return bool
*
* @throws AdldapException
*/
public function validateNotEmpty($parameter, $value)
{
if (!empty($value)) {
return true;
}

$message = sprintf('Parameter: %s cannot be empty.', $parameter);

throw new AdldapException($message);
}
}