The connection
option is the name of the LDAP connection to use for authentication that you've configured in your ldap.php
file.
Authentication providers allow you to choose how LDAP users are authenticated into your application.
There are two built in providers. Please view their documentation to see which one is right for you.
The DatabaseUserProvider
allows you to synchronize LDAP users to your applications database.
Note: This provider requires that you add an
objectguid
database column to yourusers
database table. Read more here.
To use it, insert it in your config/ldap_auth.php
in the provider
option:
'provider' => Adldap\Laravel\Auth\DatabaseUserProvider::class
Using this provider utilizes your configured Eloquent model in config/auth.php
:
'providers' => [
'users' => [
'driver' => 'ldap',
'model' => App\User::class,
],
],
When you've authenticated successfully, use the method Auth::user()
as you would normally to retrieve the currently authenticated user:
// Instance of \App\User.
$user = Auth::user();
echo $user->email;
The NoDatabaseUserProvider
allows you to authenticate LDAP users without synchronizing them.
This provider is great for smaller applications that don't require you to attach data to authenticating users and is great for simply authorizing access.
When using the database
session driver with the NoDatabaseUserProvider
, you must
change the user_id
data type in the generated Laravel sessions migration (database/migrations/2018_05_03_182019_create_sessions_table.php
)
to varchar
. This is because the identifier for LDAP records is
a GUID - which contains letters and dashes (incompatible with
the integer
type of databases).
Due to Laravel's generated blade views with the auth:make
command, any
views that utilize Eloquent User model attributes will need to be
re-written for compatibility with this provider.
For example, in the generated resources/views/layouts/app.blade.php
, you will
need to rewrite Auth::user()->name
to Auth::user()->getCommonName();
This is because the authenticated user will not be a standard Eloquent
model, it will be a Adldap\Models\User
instance.
You will receive exceptions otherwise.
To use it, insert it in your config/ldap_auth.php
in the provider
option:
'provider' => Adldap\Laravel\Auth\NoDatabaseUserProvider::class
Inside your config/auth.php
file, you can remove the model
key in your provider array since it won't be used:
'providers' => [
'users' => [
'driver' => 'ldap',
],
],
When you've authenticated successfully, use the method Auth::user()
as you would
normally to retrieve the currently authenticated user:
// Instance of \Adldap\Models\User.
$user = Auth::user();
echo $user->getCommonName();
echo $user->getAccountName();
Authentication rules allow you to restrict which LDAP users are able to authenticate, much like scopes, but with the ability to perform checks on the specific user authenticating, rather than a global scope.
To create a rule, it must extend the class Adldap\Laravel\Validation\Rules\Rule
.
Two properties will be available to you inside the rule. A $user
property that
contains the LDAP user model, as well as their Eloquent $model
Note: If you utilize the
NoDatabaseUserProvider
instead of the defaultDatabaseUserProvider
, then only the$user
property will be available.
We'll create a folder in our app
directory containing our rule named Rules
.
With this example rule, we only want to allow users to login if they are inside specific OU's:
namespace App\Rules;
use Adldap\Laravel\Validation\Rules\Rule;
use Illuminate\Support\Str; // Laravel >= 5.7
class OnlyManagersAndAccounting extends Rule
{
/**
* Determines if the user is allowed to authenticate.
*
* @return bool
*/
public function isValid()
{
$ous = [
'ou=Accounting,dc=acme,dc=org',
'ou=Managers,dc=acme,dc=org',
];
// Laravel <= 5.6
return str_contains($this->user->getDn(), $ous);
// Laravel >= 5.7
return Str::contains($this->user->getDn(), $ous)
}
}
To implement your new rule, you just need to insert it into your config/ldap_auth.php
file:
'rules' => [
App\Rules\OnlyManagersAndAccounting::class,
],
Now when you try to login, the LDAP user you login with will need to be apart of either the Accounting
or Managers
Organizational Unit.
To validate that an authenticating user is apart of one or more LDAP groups, we can perform this with a Rule
:
namespace App\Rules;
use Adldap\Models\User as LdapUser;
use Adldap\Laravel\Validation\Rules\Rule;
class IsAccountant extends Rule
{
/**
* Determines if the user is allowed to authenticate.
*
* Only allows users in the `Accounting` group to authenticate.
*
* @return bool
*/
public function isValid()
{
return $this->user->inGroup('Accounting');
}
}
Once you've implemented the above rule, only LDAP users that are apart of the Accounting
group, will be allowed to authenticate.
Scopes allow you to restrict which LDAP users are allowed to login to your application.
If you're familiar with Laravel's Query Scopes, then these will feel very similar.
To create a scope, it must implement the interface Adldap\Laravel\Scopes\ScopeInterface
.
For this example, we'll create a folder inside our app
directory containing our scope named: Scopes
.
Of course, you can place these scopes wherever you desire, but in this example, our final scope path will be:
../my-application/app/Scopes/AccountingScope.php
With this scope, we want to only allow members of an Active Directory group named: Accounting
:
namespace App\Scopes;
use Adldap\Query\Builder;
use Adldap\Laravel\Scopes\ScopeInterface;
class AccountingScope implements ScopeInterface
{
/**
* Apply the scope to a given LDAP query builder.
*
* @param Builder $query
*
* @return void
*/
public function apply(Builder $query)
{
// The distinguished name of our LDAP group.
$accounting = 'cn=Accounting,ou=Groups,dc=acme,dc=org';
$query->whereMemberOf($accounting);
}
}
Now that we've created our scope (app/Scopes/AccountingScope.php
), we can insert it into our config/ldap_auth.php
file:
'scopes' => [
// Only allows users with a user principal name to authenticate.
Adldap\Laravel\Scopes\UpnScope::class,
// Only allow members of 'Accounting' to login.
App\Scopes\AccountingScope::class,
],
Once you've inserted your scope into the configuration file, you will now only be able
to authenticate with users that are a member of the Accounting
group.
All other users will be denied authentication, even if their credentials are valid.
Note: If you're caching your configuration files, make sure you run
php artisan config:clear
to be able to use your new scope.
Inside your config/ldap_auth.php
file there is a configuration option named identifiers
:
'identifiers' => [
'ldap' => [
'locate_users_by' => 'userprincipalname',
'bind_users_by' => 'distinguishedname',
],
'database' => [
'guid_column' => 'objectguid',
'username_column' => 'email',
],
'windows' => [
'locate_users_by' => 'samaccountname',
'server_key' => 'AUTH_USER',
],
],
Let's go through each option with their meaning.
The LDAP array contains two elements each with a key and value.
The locate_users_by
key contains the LDAP users attribute you would like your authenticating users to be located by.
Note: If you're using the
NoDatabaseUserProvider
it is extremely important to know that this value is used as the key to retrieve the inputted username from theAuth::attempt()
credentials array.For example, if you're executing an
Auth::attempt(['username' => 'jdoe..'])
and you have alocate_users_by
value set touserprincipalname
then the Adldap2-Laravel auth driver will try to retrieve your users username from the given credentials array with the keyuserprincipalname
. This would generate an exception since this key does not exist in the above credentials array.
For example, executing the following:
Auth::attempt(['email' => 'jdoe@corp.com', 'password' => 'password'])
Will perform an LDAP search for a user with the userprincipalname
equal to jdoe@corp.com
.
If you change Auth::attempt()
email
key, you will need to change the database.username_column
key to match.
The authenticate
key contains the LDAP users attribute you would like to perform LDAP authentication on.
For example, executing the following:
Auth::attempt(['email' => 'jdoe@corp.com', 'password' => 'password'])
Will try to locate a user in your LDAP directory with a userprincipalname
equal to jdoe@corp.com
. Then, when an LDAP record of this user is located, their disintinguishedname
will be retrieved from this record, an be passed into an Adldap\Auth\Guard::attempt()
(ex Guard::attempt('cn=John Doe,ou=Users,dc=corp,dc=com', 'password')
).
Note: It's extremely important to know that your configured
account_suffix
andaccount_prefix
(located in yourconfig/ldap.php
file) will be appended or pre-pended onto this passed in username.
You can ignore the windows
configuration array, unless you're planning on using the included middleware for single sign on authentication.
The GUID column is a new configuration option added in v6.0 that allows you to set the database column that will store users Object GUID (Globally Unique Identifier).
The addition of this database column allows you to make username changes in your LDAP directory, and have them properly synchronize in your Laravel application.
This is usually the scenario when someone changes their marital status, or changes their name.
If you're upgrading from a previous Adldap2-Laravel version, simply create a new migration and add the nullable
column to your users
database table.
Ex. php artisan make:migration add_objectguid_column
Schema::table('users', function (Blueprint $table) {
$table->string('objectguid')->nullable()->after('id');
});
Otherwise, if you're starting from scratch, simply add the column to your create_users_table
migration:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('objectguid')->nullable(); // Added here.
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
If you have user records already inside your database with a null
objectguid
value, then
it will be set automatically if a user authenticates with the same username that
is contained in your configured in your username_column
option.
For example, lets say we have a user in our database with the following information:
+----+------------+---------------+
| id | objectguid | email |
+----+------------+---------------+
| 1 | NULL | jdoe@acme.org |
+----+------------+---------------+
When a user successfully authenticates with the username of jdoe@acme.org
, then his objectguid
column will automatically be set:
+----+--------------------------------------+---------------+
| id | objectguid | email |
+----+--------------------------------------+---------------+
| 1 | cc07cacc-5d9d-fa40-a9fb-3a4d50a172b0 | jdoe@acme.org |
+----+--------------------------------------+---------------+
The next time this user authenticates, the objectguid
will queried for first, then email
.
This is done using a simple or where
statement, so two queries are not executed for one login.
Note: If the users identifier changes (their email / username) prior to their
objectguid
from being synchronized to your local database and they login to your application, a new user record will be created.This is due to not being able to locate a local user record with the users new username.
It is recommended to keep your application in sync via scheduling the
adldap:import
command so that all users have a synchronizedobjectguid
.
The username_column
contains a value that should match the username column you have set up in your users
database table.
For example, if you're using a username
field instead of email
in your application, you will need to change this option to username
.
Note: If you're using the
DatabaseUserProvider
it is extremely important to know that this value is used as the key to retrieve the inputted username from theAuth::attempt()
credentials array.For example, if you're executing an
Auth::attempt(['username' => 'jdoe..'])
and you have anusername_column
value set toNote: Keep in mind you will also need to update your
create_users_table
migration to use a username field instead of email, as well as your LoginController.
For example, if you'd like to login users by their samaccountname
:
'usernames' => [
'ldap' => [
'locate_users_by' => 'samaccountname', // Changed from `userprincipalname`
'bind_users_by' => 'distinguishedname',
],
'database' => [
'guid_column' => 'objectguid',
'username_column' => 'username', // Changed from `email`
],
],
Be sure to update the sync_attributes
option to synchronize the users username
as well as this is not done automatically. You will receive a SQL exception otherwise.
'sync_attributes' => [
'username' => 'samaccountname',
'name' => 'cn',
],
Login a user regularly using Auth::attempt($credentials);
.
Once a user is authenticated, retrieve them as you would regularly:
Note: The below code is just an example. You should not need to modify the
login()
method on the defaultLoginController
, unless you require unique functionality.
public function login(Request $request)
{
if (Auth::attempt($request->only(['email', 'password']))) {
// Returns \App\User model configured in `config/auth.php`.
$user = Auth::user();
return redirect()->to('home')
->withMessage('Logged in!');
}
return redirect()->to('login')
->withMessage('Hmm... Your username or password is incorrect');
}
The password sync option allows you to automatically synchronize users LDAP passwords to your local database. These passwords are hashed natively by Laravel using the Hash::make()
method.
Enabling this option would also allow users to login to their accounts using the password last used when an LDAP connection was present.
If this option is disabled, the local database account is applied a random 16 character hashed password upon first login, and will lose access to this account upon loss of LDAP connectivity.
This option must be true or false and is only applicable
to the DatabaseUserProvider
.
The column option allows you to change the database column that contains the users password.
Change this if your database column is different than password
and you have enabled the above sync option.
The login fallback option allows you to login as a local database user using the default Eloquent authentication driver if LDAP authentication fails. This option is handy in environments where:
Note: If you would like users to be able to login if your LDAP server is unavailable, you must also enable the above Password Sync option. Otherwise, users will fail authentication because their password has not been synchronized, and therefore will be incorrect.
Users must have logged in once prior to your LDAP server going down, as their account will not yet exist in the database.
To enable it, simply set the option to true in your config/ldap_auth.php
configuration file:
'login_fallback' => env('LDAP_LOGIN_FALLBACK', true), // Set to true.
Inside your config/ldap_auth.php
file there is a configuration option named sync_attributes
. This
is an array of attributes where the key is the eloquent User
model attribute, and the
value is the active directory users attribute:
'sync_attributes' => [
'email' => 'userprincipalname',
'name' => 'cn',
],
By default, the User
models email
and name
attributes are synchronized to
the LDAP users userprincipalname
and cn
attributes.
This means, upon login, the users email
and name
attribute on Laravel User
Model will be set to the
LDAP users userprincipalname
and common name (cn
) attribute, then saved.
Feel free to add more attributes here, however be sure that your users
database table contains
the key you've entered, otherwise you will receive a SQL exception upon authentication, due
to the column not existing on your users database table.
If you're looking to synchronize an attribute from an Adldap2 model that contains an array or an object, or sync attributes yourself, you can use an attribute handler class to sync your model attributes manually. For example:
Note: The class must contain a
handle()
method. Otherwise you will receive an exception.Tip: Attribute handlers are constructed using the
app()
helper. This means you can type-hint any application dependencies you may need in the handlers constructor.
'sync_attributes' => [
App\Handlers\LdapAttributeHandler::class,
],
The LdapAttributeHandler
:
namespace App\Handlers;
use App\User as EloquentUser;
use Adldap\Models\User as LdapUser;
class LdapAttributeHandler
{
/**
* Synchronizes ldap attributes to the specified model.
*
* @param LdapUser $ldapUser
* @param EloquentUser $eloquentUser
*
* @return void
*/
public function handle(LdapUser $ldapUser, EloquentUser $eloquentUser)
{
$eloquentUser->name = $ldapUser->getCommonName();
}
}
The logging
array contains a list of the events to be logged when certain events occur using Adldap2-Laravel.
Each element in the array consists of the key (the occurring event) and the value (the listener that performs the logging of said event).
You can remove any or all of these if you'd prefer nothing to be logged for the event. No passwords are logged with any of the events.