This extensions adds a simple LDAP-Auth mechanism for your yii2 application
- Tries to bind to selected domain with username/credential entered in LoginForm
- Read user data attributes after successful bind to retrieve sid and other values
- User login via LDAP
- Read self defined LDAP attributes
- Domain autodetection based on IPFilter.
- Filter out results by checking every results
sidHistory
- Optional query caching
Preferred way to install, is through composer:
composer require commifreak/yii2-ldap-auth
Either you use it as standalone or add this as component:
[
'components' => [
'ldap' => [
'class' => 'commifreak\yii2\LdapAuth',
'filterBySidhistory' => false, // Filter by checking sidHistory?
'enableCache' => false,
'forceApcuCache' => false,
'apcuCacheTtl' => 3600,
'domains' => [
['name' => 'Domain1', 'hostname' => 'domain1.tld', 'autodetectIps' => ['172.31.0.0/16', '192.168.178.0/24', '127.0.0.1'], 'baseDn' => 'DC=Domain1,DC=tld', 'publicSearchUser' => 'example@domain', 'publicSearchUserPassword' => 'secret'],
['name' => 'Domain2', 'hostname' => '192.168.178.14', 'autodetectIps' => ['192.168.178.55'], 'baseDn' => 'DC=Domain2,DC=tld', 'publicSearchUser' => 'example@domain', 'publicSearchUserPassword' => 'secret'],
//...
],
],
]
]
You can omit autodetectIps
if you don't want Ips for a specific domain.
You can set useSSL
if you want to use encryption.
Attention! You need to define baseDn
. This defines the baseDN in where the function will search for the user data!
There are 5 basic functions:
autoDetect($overrideIp)
- Tries to detect the User's client IP (with Proxy support) and determines the Domain to use
login($username, $password, $domainKey, $fetchUserDN)
- Tries to connect to domain and bind to it as
$username
with$password
$domainKey
defines the domain to use (either detected byautoDetect
or by passing the key number of the configuration array)- If you set it to
false
or pass nothing, the login function loops through every domain and tries to log you in ( default).
- If you set it to
$fetchUserDN
determines the user DN, in case you want a bind via a users DN instead of username@hostname
- Tries to connect to domain and bind to it as
fetchUserData($attributes)
- Queries the LDAP for the logged in user and gets some attributes (adjustable list of attributes)
searchUser($searchFor, $attributes, $searchFilter, $domainKey, $onlyActiveAccounts, $allDomainsHaveToBeReachable)
- Searches for a user in the LDAP-Directory. This requires a search-user which is configured in the component options.
- The options let you define what attributes you want back and in which you are searching (defaults to lastname, firstname, username and class=person).
$domainKey
lets you set a fixed domain (from autoDetect as example) to search. Otherwise, it searches in every domain$onlyActiveAccounts
lets you decide whether you only want active or all accounts to be returned. defaults to false!$allDomainsHaveToBeReachable
True: All configured domains need to be reachable in order to get a result. If one is not reachable, false will be returned
updateAttributes
lets you update the user attributes$attributes
The attribute (array keys are the attribute names, the array values are the attribute values)$dn
The DN which should be updated - if not provided, the eventually previous examined one will be used.
I've added a new attribute to LoginForm location
which holds the domain-key.
<?= $form->field($model, 'location')->widget(\kartik\select2\Select2::className(), [
'data' => \yii\helpers\ArrayHelper::getColumn(Yii::$app->ldap->domains, 'name'),
'options' => ['placeholder' => 'Select a domain ...'],
'pluginOptions' => [
'allowClear' => true
],
]) ?>
To use the autoDetect feature, you have now nothing else to do! The plugin takes care of it automatically! You just want to set the location dropdown to 0, by setting the following inside your LoginForm:
public function init()
{
parent::init(); // TODO: Change the autogenerated stub
$this->location = 0; // Set it to the first entry, which is now always autodetected
}
This is based on the default LoginForm->login() function
public function login()
{
if (!$this->validate()) {
return false;
}
// At this point we filled the model - now we ask the LDAP if the entered data is correct
/** @var LdapAuth $ldap */
$ldap = Yii::$app->ldap;
if ($ldap->login($this->username, $this->password, $this->location)) { // or if ($ldap->login($this->username, $this->password, $this->location, true)) if you want to use `$fetchUserDN`-feature!
$userData = $ldap->fetchUserData();
if ($userData) {
$user = User::find()->where(['sid' => $userData['sid']])->one();
if (!$user) {
$user = new User();
$user->sid = $userData['sid'];
}
$user->email = $userData['mail'];
$user->firstname = $userData['givenname'];
$user->lastname = $userData['sn'];
$user->phone = $userData['telephonenumber'];
$user->validate();
$user->save();
return Yii::$app->user->login($user, $this->rememberMe ? 3600 * 24 * 30 : 0);
}
} else {
Yii::$app->session->setFlash('error', 'Login gescheitert!');
}
}
This also tries to sync user's data with the LDAP.
/** @var LdapAuth $ldap */
$ldap = \Yii::$app->ldap;
if (!$ldap->login(\Yii::$app->user->identity->sid, $this->userPassword, false, true)) {
return false;
}
$updateAttrs = [];
if (!empty($this->phone)) {
$updateAttrs['telephonenumber'] = $this->phone;
}
if (!empty($this->room)) {
$updateAttrs['physicaldeliveryofficename'] = $this->room;
}
if (!$ldap->updateAttributes($updateAttrs)) {
return false;
}
I've added/changed the user table as follow, to add a sid, firstname, lastname and phone column:
CREATE TABLE `user`
(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`sid` varchar(255) DEFAULT NULL,
`email` varchar(256) NOT NULL,
`firstname` varchar(255) DEFAULT NULL,
`lastname` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`create_user` int(11) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`update_user_id` int(11) DEFAULT NULL,
`auth_key` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `auth_key_UNIQUE` (`auth_key`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;