├── phpunit.xml.dist ├── tests ├── base │ ├── bootstrap.php │ ├── TestVariables.php │ ├── TestConfigSample.php │ └── TestCase.php ├── InitialTest.php └── UserModelTest.php ├── docs ├── SSL_TLS_AD.md ├── USAGE_WITHOUT_USER_MODEL.md ├── CREATE_MODIFY_DELETE_OBJECTS.md └── USAGE_WITH_USER_MODEL.md ├── composer.json ├── src ├── migrations │ └── m161220_101944_create_user_table.php ├── Adldap2Wrapper.php ├── commands │ └── LdapController.php └── model │ └── UserDbLdap.php ├── LICENSE.md ├── .gitignore └── readme.md /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | ./tests 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/base/bootstrap.php: -------------------------------------------------------------------------------- 1 | 636, 9 | //'use_ssl' => true, 10 | //'use_tls' => true, 11 | ... 12 | ``` 13 | 14 | ## Windows 15 | **Links:** 16 | * http://se2.php.net/manual/en/ref.ldap.php#47427 17 | * https://stackoverflow.com/questions/5258556/problems-with-secure-bind-to-active-directory-using-php 18 | 19 | A possible reason could be that the client cannot verify the server cert. 20 | To disable the server cert verification create a file 21 | 22 | **Solution:** 23 | * create the file c:\openldap\sysconf\ldap.conf 24 | * And put into it: 25 | 26 | ``` 27 | TLS_REQCERT never 28 | ``` -------------------------------------------------------------------------------- /tests/base/TestVariables.php: -------------------------------------------------------------------------------- 1 | db->driverName === 'mysql') { 23 | // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci 24 | $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; 25 | } 26 | $this->createTable('{{%user}}', [ 27 | 'id' => $this->primaryKey(), 28 | 'username' => $this->string()->notNull()->unique(), 29 | 'auth_key' => $this->string(32)->notNull()->unique(), 30 | 'status' => $this->smallInteger()->notNull()->defaultValue(10), 31 | 'created_at' => $this->integer()->notNull(), 32 | 'updated_at' => $this->integer()->notNull(), 33 | ], $tableOptions); 34 | } 35 | public function down() 36 | { 37 | $this->dropTable('{{%user}}'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Matthias Maderer (http://www.edvler-blog.de) 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /tests/base/TestConfigSample.php: -------------------------------------------------------------------------------- 1 | 'testapp', 5 | 'components' => [ 6 | 'request' => [ 7 | 'cookieValidationKey' => 'wefJDF8sfdsfSDefwqdxj9oq', 8 | 'scriptFile' => __DIR__ . '/index.php', 9 | 'scriptUrl' => '/index.php', 10 | ], 11 | 'authManager' => [ 12 | 'class' => 'yii\rbac\DbManager', 13 | ], 14 | 'user' => [ 15 | 'identityClass' => 'Edvlerblog\Adldap2\model\UserDbLdap', 16 | 'enableAutoLogin' => true, 17 | ], 18 | 'ad' => [ 19 | 'class' => 'Edvlerblog\Adldap2\Adldap2Wrapper', 20 | 'providers' => [ 21 | 'default' => [ 22 | 'autoconnect' => true, 23 | 'config' => [ 24 | 'account_suffix' => 'test.lan', 25 | 'hosts' => ['srv1.test.lan'], 26 | 'base_dn' => 'dc=test,dc=lan', 27 | 'username' => 'yii2binduser@test.lan', 28 | 'password' => 'PWD_GOES_HERE', 29 | // See docs/SSL_TLS_AD.md 30 | // SSL and TLS needed to create a user with password 31 | 'port' => 636, 32 | 'use_ssl' => true, 33 | 'use_tls' => true, 34 | ] 35 | ], 36 | ], 37 | ], 38 | 'db' => [ 39 | 'class' => 'yii\db\Connection', 40 | 'dsn' => 'mysql:host=localhost;dbname=adldap-test', 41 | 'username' => 'root', 42 | 'password' => 'PWD_GOES_HERE', 43 | 'charset' => 'utf8' 44 | ], 45 | ] 46 | ]; 47 | 48 | // variables are used by tests suites 49 | public static $TEST_USER_PASSWORD = 'PWD_GOES_HERE'; 50 | public static $TEST_USER_PRINCIPAL_NAME = 'snoopstein@test.lan'; 51 | } 52 | -------------------------------------------------------------------------------- /src/Adldap2Wrapper.php: -------------------------------------------------------------------------------- 1 | adLdapInstance)) { 44 | $this->adLdapInstance = new Adldap(); 45 | } 46 | 47 | foreach($this->providers as $providerName=>$prodivderSettings) { 48 | $config = new \Adldap\Connections\Provider($prodivderSettings['config']); 49 | $this->adLdapInstance->addProvider($config, $providerName); 50 | 51 | if (array_key_exists('schema',$prodivderSettings) && is_object($prodivderSettings['schema'])) { 52 | $this->adLdapInstance->getProvider($providerName)->setSchema($prodivderSettings['schema']); 53 | } 54 | 55 | if($prodivderSettings['autoconnect'] == true) { 56 | $this->adLdapInstance->connect($providerName); 57 | } 58 | } 59 | 60 | $providers = $this->adLdapInstance->getProviders(); 61 | 62 | if (array_key_exists($this->defaultProvider, $providers)) { 63 | $this->adLdapInstance->setDefaultProvider($this->defaultProvider); 64 | } else { 65 | throw new \yii\base\Exception("The given defaultprovder with the name " . $this->defaultProvider . " could not be found. See https://github.com/edvler/yii2-adldap-module/blob/master/readme.md"); 66 | } 67 | } 68 | 69 | 70 | /** 71 | * Use magic PHP function __call to route ALL function calls to the Adldap class. 72 | * Look into the Adldap class for possible functions. 73 | * 74 | * @param string $methodName Method name from Adldap class 75 | * @param array $methodParams Parameters pass to method 76 | * @return mixed 77 | */ 78 | public function __call($methodName, $methodParams) 79 | { 80 | return call_user_func_array([$this->adLdapInstance, $methodName], $methodParams); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/base/TestCase.php: -------------------------------------------------------------------------------- 1 | mockApplication(); 15 | } 16 | 17 | /** 18 | * Clean up after test. 19 | * By default the application created with [[mockApplication]] will be destroyed. 20 | */ 21 | protected function tearDown() { 22 | parent::tearDown(); 23 | $this->destroyApplication(); 24 | } 25 | 26 | /** 27 | * Populates Yii::$app with a new application 28 | * The application will be destroyed on tearDown() automatically. 29 | * @param array $config The application configuration, if needed 30 | * @param string $appClass name of the application class to create 31 | */ 32 | protected function mockApplication($appClass = '\yii\web\Application') { 33 | $config = TestConfig::$ADLDAP_CONFIG; 34 | 35 | $config['basePath'] = __DIR__; 36 | $config['vendorPath'] = dirname(__DIR__) . '/vendor'; 37 | 38 | new $appClass($config); 39 | } 40 | 41 | /** 42 | * Destroys application in Yii::$app by setting it to null. 43 | */ 44 | protected function destroyApplication() { 45 | Yii::$app = null; 46 | Yii::$container = new Container(); 47 | } 48 | 49 | protected function sendtoStdErr($msg) { 50 | fwrite(STDERR, print_r($msg, TRUE)); 51 | } 52 | 53 | public function checkAndDeleteUser() { 54 | $allokay = true; 55 | 56 | // check if account exists 57 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', TestVariables::$TEST_USER_ACCOUNT_NAME); 58 | if($userObject != null && $userObject->exists) { 59 | // delete if exists 60 | if ($userObject->delete() == false) { 61 | $allokay = false; 62 | } 63 | } 64 | 65 | // check if group exists 66 | $groupObject = \Yii::$app->ad->search()->findBy('cn', TestVariables::$TEST_GROUP_NAME); 67 | if($groupObject != null && $groupObject->exists) { 68 | // delete if exists 69 | if ($groupObject->delete() == false) { 70 | $allokay = false; 71 | } 72 | } 73 | 74 | // check if nested group exists 75 | $nestedgroupObject = \Yii::$app->ad->search()->findBy('cn', TestVariables::$TEST_NESTED_GROUP_NAME); 76 | if($nestedgroupObject != null && $nestedgroupObject->exists) { 77 | // delete if exists 78 | if ($nestedgroupObject->delete() == false) { 79 | $allokay = false; 80 | } 81 | } 82 | 83 | return $allokay; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Ignore TestConfig 3 | ################# 4 | /tests/base/TestConfig.php 5 | 6 | 7 | .git 8 | .gitattributes 9 | 10 | ################# 11 | ## Eclipse & Zend Studio 12 | ################# 13 | 14 | *.pydevproject 15 | .project 16 | .metadata 17 | .settings 18 | bin/ 19 | tmp/ 20 | *.tmp 21 | *.bak 22 | *.swp 23 | *~.nib 24 | local.properties 25 | .classpath 26 | .settings/ 27 | .loadpath 28 | 29 | # External tool builders 30 | .externalToolBuilders/ 31 | 32 | # Locally stored "Eclipse launch configurations" 33 | *.launch 34 | 35 | # CDT-specific 36 | .cproject 37 | 38 | # PDT-specific 39 | .buildpath 40 | 41 | 42 | ################# 43 | ## PhpStorm 44 | ################# 45 | 46 | .idea 47 | 48 | 49 | ################# 50 | ## Netbeans 51 | ################# 52 | 53 | nbproject 54 | 55 | 56 | ################# 57 | ## Visual Studio 58 | ################# 59 | 60 | ## Ignore Visual Studio temporary files, build results, and 61 | ## files generated by popular Visual Studio add-ons. 62 | 63 | # User-specific files 64 | *.suo 65 | *.user 66 | *.sln.docstates 67 | 68 | # Build results 69 | [Dd]ebug/ 70 | [Rr]elease/ 71 | *_i.c 72 | *_p.c 73 | *.ilk 74 | *.meta 75 | *.obj 76 | *.pch 77 | *.pdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.vspscc 86 | .builds 87 | *.dotCover 88 | 89 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 90 | #packages/ 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opensdf 97 | *.sdf 98 | 99 | # Visual Studio profiler 100 | *.psess 101 | *.vsp 102 | 103 | # ReSharper is a .NET coding add-in 104 | _ReSharper* 105 | 106 | # Installshield output folder 107 | [Ee]xpress 108 | 109 | # DocProject is a documentation generator add-in 110 | DocProject/buildhelp/ 111 | DocProject/Help/*.HxT 112 | DocProject/Help/*.HxC 113 | DocProject/Help/*.hhc 114 | DocProject/Help/*.hhk 115 | DocProject/Help/*.hhp 116 | DocProject/Help/Html2 117 | DocProject/Help/html 118 | 119 | # Click-Once directory 120 | publish 121 | 122 | # Others 123 | [Bb]in 124 | [Oo]bj 125 | sql 126 | TestResults 127 | *.Cache 128 | ClientBin 129 | stylecop.* 130 | ~$* 131 | *.dbmdl 132 | Generated_Code #added for RIA/Silverlight projects 133 | 134 | # Backup & report files from converting an old project file to a newer 135 | # Visual Studio version. Backup files are not needed, because we have git ;-) 136 | _UpgradeReport_Files/ 137 | Backup*/ 138 | UpgradeLog*.XML 139 | 140 | 141 | ############ 142 | ## Windows 143 | ############ 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | 148 | # Folder config file 149 | Desktop.ini 150 | 151 | 152 | ############# 153 | ## Python 154 | ############# 155 | 156 | *.py[co] 157 | 158 | # Packages 159 | *.egg 160 | *.egg-info 161 | dist 162 | build 163 | eggs 164 | parts 165 | bin 166 | var 167 | sdist 168 | develop-eggs 169 | .installed.cfg 170 | 171 | # Installer logs 172 | pip-log.txt 173 | 174 | # Unit test / coverage reports 175 | .coverage 176 | .tox 177 | 178 | #Translations 179 | *.mo 180 | 181 | #Mr Developer 182 | .mr.developer.cfg 183 | 184 | # Mac crap 185 | .DS_Store 186 | -------------------------------------------------------------------------------- /src/commands/LdapController.php: -------------------------------------------------------------------------------- 1 | authManager; 30 | 31 | // add "permissionDisplayDetailedAbout" permission 32 | $displayDetailedAbout = $auth->createPermission('permissionDisplayDetailedAbout'); 33 | $displayDetailedAbout->description = 'Permission to display detailed about informations'; 34 | $auth->add($displayDetailedAbout); 35 | 36 | // add "permissionToUseContanctPage" permission 37 | $useContactPage = $auth->createPermission('permissionToUseContanctPage'); 38 | $useContactPage->description = 'Permission to use the contanct page'; 39 | $auth->add($useContactPage); 40 | 41 | // add "yii2_example_group" role and give this role the "permissionDisplayDetailedAbout" and "permissionToUseContanctPage" permission 42 | $yii2ExampleGroup = $auth->createRole('yii2_example_group'); 43 | $auth->add($yii2ExampleGroup); 44 | $auth->addChild($yii2ExampleGroup, $displayDetailedAbout); 45 | $auth->addChild($yii2ExampleGroup, $useContactPage); 46 | 47 | 48 | 49 | // add "permissionToUseContanctPage" permission 50 | $useHomePage = $auth->createPermission('permissionToSeeHome'); 51 | $useHomePage->description = 'Permission to use the home page'; 52 | $auth->add($useHomePage); 53 | 54 | // add "yii2_see_home_group" role and give this role the "permissionToSeeHome" permission 55 | $yii2HomeGroup = $auth->createRole('yii2_see_home_group'); 56 | $auth->add($yii2HomeGroup); 57 | $auth->addChild($yii2HomeGroup, $useHomePage); 58 | 59 | echo "\n\n!!!! TODO !!!!\nTow roles with the name yii2_example_group and yii2_see_home_group were created in yii2.\nPlease create the groups with the same name in Active Directory.\nAssign the user you are using for the login to this groups in Active Directory.\n"; 60 | } 61 | 62 | /** 63 | * Import all users from LDAP, assign roles and account status. Run this command with cron or another scheduler every X minuten/hours/days. 64 | */ 65 | public function actionImportAllUsers() 66 | { 67 | \Yii::warning("-- Starting import from Active Directory --"); 68 | $results = \Yii::$app->ad->getDefaultProvider()-> 69 | search()-> 70 | select("samaccountname")-> 71 | where('objectClass','=','user')-> 72 | where('objectCategory','=','person')-> 73 | paginate(999); 74 | 75 | $userNamesToAdd = []; 76 | foreach($results->getResults() as $ldapUser) { 77 | $accountName = $ldapUser->getAttribute("samaccountname",0); 78 | if ($accountName != null) { 79 | array_push($userNamesToAdd, $accountName); 80 | } 81 | } 82 | 83 | 84 | $c=0; 85 | \Yii::warning("-- Found " . count ($userNamesToAdd) . " users --"); 86 | foreach ($userNamesToAdd as $userName) { 87 | $c++; 88 | 89 | \Yii::warning("-- Working on user " . $userName . " --"); 90 | $userObject = \Edvlerblog\Adldap2\model\UserDbLdap::createOrRefreshUser($userName); 91 | 92 | if ($userObject != null) { 93 | \Yii::warning("User " . $userName . " created"); 94 | } else { 95 | \Yii::warning("User " . $userName . " NOT created"); 96 | } 97 | 98 | } 99 | \Yii::warning("-- End import from Active Directory --"); 100 | 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /docs/USAGE_WITHOUT_USER_MODEL.md: -------------------------------------------------------------------------------- 1 | # Usage method 1: Simple usage without a user model 2 | 3 | ## This is only a Quick-Start-Guide! 4 | yii2-adldap-module is only a wrapper class. The examples below are all taken from the official documentation of the Adldap2 repository. 5 | 6 | You can find the documentation here: https://github.com/Adldap2/Adldap2/tree/master/docs 7 | 8 | ## Syntax basics 9 | 10 | ### Different ways to call Adldap2 functions 11 | ```php 12 | //... 13 | $un = 'testuser'; 14 | 15 | /* 16 | There are three ways available to call Adldap2 function. 17 | If you use more providers (multiple Active Directory connections) 18 | you make one as default and you can call this one with Method1 or Method2 19 | and the second one will be called with Method3. 20 | */ 21 | 22 | // Method 1: uses the default provider given in the configuration above (array key defaultProvider) 23 | $user = \Yii::$app->ad->search()->findBy('sAMAccountname', $un); 24 | // Method 2: uses the default provider given in the configuration above (array key defaultProvider) 25 | $user = \Yii::$app->ad->getDefaultProvider()->search()->findBy('sAMAccountname', $un); 26 | // Method 3: get the provider by name (here name default is used). 27 | $user = \Yii::$app->ad->getProvider('default')->search()->findBy('sAMAccountname', $un); 28 | print_r($user); //print all informations retrieved from Active Directory 29 | //... 30 | ``` 31 | 32 | ### Multiline example 33 | For almost all operations you need a provider. You can access the provider in the following ways. 34 | ```php 35 | $provider = \Yii::$app->ad->getDefaultProvider(); 36 | $search = $provider->search(); //start a search 37 | $search = $search->select(['cn', 'samaccountname', 'telephone', 'mail']); //Only query this attributes 38 | $search = $search->where('samaccountname', '=', 'matthias'); 39 | $result = $search->get(); 40 | 41 | echo '
';
 42 | echo print_r($result,true);
 43 | echo '
'; 44 | ``` 45 | ### Oneline example 46 | ```php 47 | $result = \Yii::$app->ad->getDefaultProvider()->search()->select(['cn', 'samaccountname', 'telephone', 'mail'])->where('samaccountname', '=', 'matthias')->get(); 48 | 49 | echo '
';
 50 | echo print_r($result,true);
 51 | echo '
'; 52 | ``` 53 | 54 | ### Oneline example without getDefaultProvider() 55 | ```php 56 | $result = \Yii::$app->ad->search()->select(['cn', 'samaccountname', 'telephone', 'mail'])->where('samaccountname', '=', 'matthias')->get(); 57 | 58 | echo '
';
 59 | echo print_r($result,true);
 60 | echo '
'; 61 | ``` 62 | 63 | --- 64 | 65 | ## Examples 66 | 67 | ### Authenticate user 68 | https://adldap2.github.io/Adldap2/#/setup?id=authenticating 69 | ```php 70 | $un = 'testuser'; 71 | $pw = 'VeryStrongPw'; 72 | if(\Yii::$app->ad->auth()->attempt($un,$pw)) { 73 | echo 'User successfully authenticated'; 74 | } else { 75 | echo 'User or Password wrong'; 76 | } 77 | ``` 78 | 79 | ### Find records 80 | #### With findBy() function 81 | Finding a specific record by a specific attribute. We're looking for a record with the 'samaccountname' of 'testuser'. This euqals to the username in Active Directory. 82 | https://adldap2.github.io/Adldap2/#/searching?id=finding-a-record-by-a-specific-attribute 83 | ```php 84 | $un = 'testuser'; 85 | $user = \Yii::$app->ad->search()->findBy('sAMAccountname', $un); 86 | 87 | //print all informations of the user object 88 | echo '
';
 89 | echo print_r($user,true);
 90 | echo '
'; 91 | ``` 92 | 93 | #### With get() function 94 | ```php 95 | $un = 'testuser'; 96 | $user = \Yii::$app->ad->search()->where('sAMAccountName', '=', $un)->get(); 97 | 98 | //print all informations of the user object 99 | echo '
';
100 | echo print_r($user,true);
101 | echo '
'; 102 | ``` 103 | 104 | ### Group Membership 105 | See sourcecode function getGroups() or inGroup(). 106 | https://github.com/Adldap2/Adldap2/blob/master/src/Models/Concerns/HasMemberOf.php 107 | 108 | #### Check if user is in group with getGroups() function. 109 | ```php 110 | $un = 'testuser'; 111 | $user = \Yii::$app->ad->search()->findBy('sAMAccountname', $un); 112 | 113 | $gn = 'the-group-name'; 114 | foreach($user->getGroups() as $group) 115 | { 116 | if($gn == $group->getName()) { 117 | echo 'TRUE'; 118 | } 119 | } 120 | ``` 121 | #### Check if user is in group with inGroup() function. 122 | ```php 123 | $un = 'testuser'; 124 | $user = \Yii::$app->ad->search()->findBy('sAMAccountname', $un); 125 | 126 | $gn = 'the-group-name'; 127 | if ($user->inGroup($gn)) { 128 | echo 'User in Group ' . $gn; 129 | } else { 130 | echo 'User NOT in Group ' . $gn; 131 | } 132 | ``` 133 | 134 | ## More Examples 135 | yii2-adldap-module is only a wrapper class. The examples above are all taken from the official documentation of the Adldap2 repository. 136 | 137 | You can find the documentation here: https://github.com/Adldap2/Adldap2/tree/master/docs 138 | -------------------------------------------------------------------------------- /docs/CREATE_MODIFY_DELETE_OBJECTS.md: -------------------------------------------------------------------------------- 1 | # Usage method 3: Create and modify objects 2 | 3 | ## This is only a Quick-Start-Guide! 4 | yii2-adldap-module is only a wrapper class. The examples below are all taken from the official documentation of the Adldap2 repository. 5 | 6 | You can find the documentation here: https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md OR https://adldap2.github.io/Adldap2/#/searching 7 | 8 | **Don't forget, to set passwords on Active Directory users, you need a SSL/TLS connection!** 9 | See [/readme.md#configuration](/readme.md#configuration) ssl/tls parameters. 10 | 11 | ## Some examples (taken from testsuite [/tests/InitialTest.php](/tests/InitialTest.php)) 12 | 13 | ### Create user 14 | ```php 15 | // https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#saving 16 | // create user 17 | $user = \Yii::$app->ad->make()->user([ 18 | 'cn' => 'Snoop Einstein', 19 | ]); 20 | 21 | // set attributes with set... function 22 | $user->setAccountName('snoopstein'); 23 | $user->setDisplayName('Snoop Einstein'); 24 | $user->setFirstName('Snoop'); 25 | $user->setLastName('Einstein'); 26 | $user->setUserPrincipalName('snoopstein@test.lan'); 27 | $user->setInfo('Cat and dog since 06.12.2017'); 28 | 29 | // set attribute with setAttribute(..) 30 | $user->setAttribute('initials', 'ES'); 31 | 32 | // set attribute with fill 33 | $user->fill([ 34 | 'company' => 'Animal plc.', 35 | 'department' => 'Dept. C and D', 36 | 'description' => 'Einstein is a cat and Snoop is a dog!', 37 | ]); 38 | 39 | // create dn 40 | $dn = $user->getDnBuilder(); 41 | $dn->addCn($user->getCommonName()); 42 | $dn->addCN('Users'); 43 | $user->setDn($dn); 44 | 45 | // save an check return value 46 | if ($user->save()) { 47 | echo "// saved successfully."; 48 | } else { 49 | echo "// There was an issue saving this user."; 50 | } 51 | 52 | // Enable account (has to be an extra step!) 53 | // https://github.com/Adldap2/Adldap2/blob/master/src/Models/Attributes/AccountControl.php 54 | $ac = new Adldap\Models\Attributes\AccountControl(); 55 | $ac->accountIsNormal(); 56 | $user->setUserAccountControl($ac); 57 | $user->setPassword('VeryStrong123'); //Needs ssl/tls 58 | if ($user->save()) { 59 | echo "// saved successfully."; 60 | } else { 61 | echo "// There was an issue saving this user."; 62 | } 63 | 64 | // test if objects realy exists 65 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', 'snoopstein'); 66 | echo $userObject->exists; 67 | echo $userObject->getDisplayName(); 68 | echo '
' . print_r($userObject,true) . '
'; 69 | ``` 70 | 71 | ### Modify user 72 | ```php 73 | // Step 1: query the ldap object (via method 1 or method 2) 74 | $ldapObject = \Yii::$app->ad->getProvider('default')->search()->findBy('sAMAccountname', 'snoopstein'); 75 | 76 | // Step 2: Update the attribute 77 | // Further documentation: 78 | // https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#setting-attributes 79 | $ldapObject->setDisplayName('Fancy New Displayname'); 80 | // .... modify other attributes .... 81 | 82 | // Step 3: Save an check return value 83 | if ($ldapObject->save()) { 84 | echo "// Displayname successfully updated."; 85 | } else { 86 | echo "// There was an issue updating this user."; 87 | } 88 | ``` 89 | 90 | ### Delete user 91 | ```php 92 | // https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#deleting 93 | // check if account exists 94 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', 'snoopstein'); 95 | if($userObject != null && $userObject->exists) { 96 | // delete if exists 97 | if ($userObject->delete()) { 98 | echo "// deleted successfully."; 99 | } else { 100 | echo "// There was an issue deleting this user."; 101 | } 102 | } 103 | ``` 104 | 105 | ### Create a group 106 | ```php 107 | // https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#available-make-methods 108 | // create the group 109 | $group1 = \Yii::$app->ad->make()->group([ 110 | 'cn' => 'Cat and Dog', 111 | ]); 112 | 113 | // create dn 114 | $dn = $group1->getDnBuilder(); 115 | $dn->addCn($group1->getCommonName()); 116 | $dn->addCN('Users'); 117 | $group1->setDn($dn); 118 | 119 | if ($group1->save()) { 120 | echo "// created successfully."; 121 | } else { 122 | echo "// There was an issue creating this group."; 123 | } 124 | ``` 125 | 126 | ### Add user to group 127 | ```php 128 | // https://github.com/Adldap2/Adldap2/blob/master/docs/models/traits/has-member-of.md#adding-a-group 129 | // find user 130 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', 'snoopstein'); 131 | 132 | // find group 133 | $groupObject = \Yii::$app->ad->search()->findBy('cn', 'Cat and Dog'); 134 | 135 | // add group to user 136 | $userObject->addGroup($groupObject); 137 | 138 | if ($userObject->save()) { 139 | echo "// added successfully."; 140 | } else { 141 | echo "// There was an issue adding this group."; 142 | } 143 | ``` 144 | 145 | ### Delete group 146 | ```php 147 | // https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#deleting 148 | // check if group exists 149 | $groupObject = \Yii::$app->ad->search()->findBy('cn', 'Cat and Dog'); 150 | if($groupObject != null && $groupObject->exists) { 151 | // delete if exists 152 | if ($groupObject->delete()) { 153 | echo "// deleted successfully."; 154 | } else { 155 | echo "// There was an issue deleted this group."; 156 | } 157 | } 158 | ``` -------------------------------------------------------------------------------- /tests/InitialTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($this->checkAndDeleteUser()); 9 | } 10 | 11 | public function testCreateUser() { 12 | // https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#saving 13 | // create user 14 | $user = \Yii::$app->ad->make()->user([ 15 | 'cn' => TestVariables::$TEST_USER_CN, 16 | ]); 17 | // set attributes with set... function 18 | $user->setAccountName(TestVariables::$TEST_USER_ACCOUNT_NAME); 19 | $user->setInfo(TestVariables::$TEST_USER_INFO); 20 | $user->setDisplayName(TestVariables::$TEST_USER_DISPLAY_NAME); 21 | $user->setFirstName(TestVariables::$TEST_USER_GIVEN_NAME); 22 | $user->setLastName(TestVariables::$TEST_USER_SURNAME); 23 | $user->setTitle(TestVariables::$TEST_USER_TITLE); 24 | $user->setTelephoneNumber(TestVariables::$TEST_USER_PHONE); 25 | $user->setPassword(TestConfig::$TEST_USER_PASSWORD); //Needs ssl/tls 26 | $user->setStreetAddress(TestVariables::$TEST_USER_STREET); 27 | $user->setPostalCode(TestVariables::$TEST_USER_POSTALCODE); 28 | $user->setUserPrincipalName(TestConfig::$TEST_USER_PRINCIPAL_NAME); 29 | 30 | // set attribute with setAttribute(..) 31 | $user->setAttribute('initials', TestVariables::$TEST_USER_INITITALS); 32 | 33 | // set attribute with fill 34 | $user->fill([ 35 | 'company' => TestVariables::$TEST_USER_COMPANY, 36 | 'department' => TestVariables::$TEST_USER_DEPARTMENT, 37 | 'description' => TestVariables::$TEST_USER_DESCRIPTION, 38 | ]); 39 | 40 | // create dn 41 | $dn = $user->getDnBuilder(); 42 | $dn->addCn($user->getCommonName()); 43 | $dn->addCN('Users'); 44 | $user->setDn($dn); 45 | 46 | // save user 47 | $this->assertTrue($user->save(),'Create user failed'); 48 | 49 | // check if account exists 50 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', TestVariables::$TEST_USER_ACCOUNT_NAME); 51 | $this->assertTrue($userObject->exists,'User not found after create'); 52 | $this->assertEquals($userObject->getDisplayName(), TestVariables::$TEST_USER_DISPLAY_NAME, 'Display name is not as expected'); 53 | 54 | // set account control and set password! 55 | // set account control only on a user, which already exists 56 | // https://github.com/Adldap2/Adldap2/blob/master/src/Models/Attributes/AccountControl.php 57 | $ac = new Adldap\Models\Attributes\AccountControl(); 58 | $ac->accountIsNormal(); 59 | $userObject->setUserAccountControl($ac); 60 | $userObject->setPassword(TestConfig::$TEST_USER_PASSWORD); 61 | $this->assertTrue($userObject->save(),'Account cannot be activated'); 62 | } 63 | 64 | public function testCreateGroups() { 65 | // create the group 66 | $group1 = \Yii::$app->ad->make()->group([ 67 | 'cn' => TestVariables::$TEST_GROUP_NAME, 68 | ]); 69 | 70 | // create dn 71 | $dn = $group1->getDnBuilder(); 72 | $dn->addCn($group1->getCommonName()); 73 | $dn->addCN('Users'); 74 | $group1->setDn($dn); 75 | 76 | $this->assertTrue($group1->save(),'Create group ' . TestVariables::$TEST_GROUP_NAME . ' failed'); 77 | 78 | // create nested group 79 | $group_nested = \Yii::$app->ad->make()->group([ 80 | 'cn' => TestVariables::$TEST_NESTED_GROUP_NAME, 81 | ]); 82 | 83 | // create dn 84 | $dn = $group_nested->getDnBuilder(); 85 | $dn->addCn($group_nested->getCommonName()); 86 | $dn->addCN('Users'); 87 | $group_nested->setDn($dn); 88 | 89 | $this->assertTrue($group_nested->save(),'Create ' . TestVariables::$TEST_NESTED_GROUP_NAME . ' group failed'); 90 | } 91 | 92 | public function testAddUserToGroup() { 93 | // find user 94 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', TestVariables::$TEST_USER_ACCOUNT_NAME); 95 | $this->assertTrue($userObject->exists,'User not found after create'); 96 | $this->assertEquals($userObject->getDisplayName(), TestVariables::$TEST_USER_DISPLAY_NAME, 'Display name is not as expected'); 97 | 98 | // find group 99 | $groupObject = \Yii::$app->ad->search()->findBy('cn', TestVariables::$TEST_GROUP_NAME); 100 | 101 | // add group to user 102 | $userObject->addGroup($groupObject); 103 | $this->assertTrue($userObject->save(),'Group cannot be added to user'); 104 | } 105 | 106 | public function testAddNestedGroup() { 107 | // search groups 108 | $groupObject = \Yii::$app->ad->search()->findBy('cn', TestVariables::$TEST_GROUP_NAME); 109 | $nestedgroupObject = \Yii::$app->ad->search()->findBy('cn', TestVariables::$TEST_NESTED_GROUP_NAME); 110 | 111 | // add group to nested group 112 | $nestedgroupObject->addMember($groupObject); 113 | $this->assertTrue($groupObject->save(),'Group ' . TestVariables::$TEST_NESTED_GROUP_NAME . ' cannot be added to group ' . TestVariables::$TEST_GROUP_NAME); 114 | } 115 | 116 | public function testSimpleAuthentication() { 117 | // test simple auth 118 | $this->assertFalse(\Yii::$app->ad->auth()->attempt(TestVariables::$TEST_NOT_EXISTING_USERNAME,'Login with NOT existing user has to be false!')); 119 | $this->assertTrue(\Yii::$app->ad->auth()->attempt(TestVariables::$TEST_USER_ACCOUNT_NAME,TestConfig::$TEST_USER_PASSWORD),'Login with existing user failed! Does the user exists in Active Directory as described in top of the class SimpleUsageTest?'); 120 | } 121 | 122 | public function testUserObject() { 123 | // search for non existing user 124 | $this->assertFalse(\Yii::$app->ad->search()->findBy('sAMAccountname', TestVariables::$TEST_NOT_EXISTING_USERNAME),'User Object is NOT NULL'); //Try to get NOT existing user. 125 | 126 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', TestVariables::$TEST_USER_ACCOUNT_NAME); 127 | $this->assertNotNull($userObject,'User Object is NULL'); //User not found? 128 | $this->assertObjectHasAttribute('attributes',$userObject,'Attribute "attributes" of Class not found.'); //attributes exists 129 | $this->assertArrayHasKey('cn',$userObject['attributes'],'Key cn not found in Array of LDAP Attributes!'); //Key cn vorhanden 130 | $this->assertEquals($userObject['attributes']['sn'][0],TestVariables::$TEST_USER_SURNAME,'Key sn not found!'); //Surname is same as defined 131 | $this->assertEquals($userObject['attributes']['givenname'][0],TestVariables::$TEST_USER_GIVEN_NAME,'Key givenname not found!'); //Givenname is same as defined 132 | $this->assertTrue($userObject->inGroup(TestVariables::$TEST_GROUP_NAME),'User is not member of group ' . TestVariables::$TEST_GROUP_NAME); //Test if user is member of group 133 | $this->assertFalse($userObject->inGroup(TestVariables::$TEST_NESTED_GROUP_NAME),'User should no be in nested group ' . TestVariables::$TEST_NESTED_GROUP_NAME); //Test if user is member of group 134 | $this->assertTrue($userObject->inGroup(TestVariables::$TEST_NESTED_GROUP_NAME,true),'User should no be in nested group ' . TestVariables::$TEST_NESTED_GROUP_NAME); //Test if user is member of group 135 | } 136 | 137 | /** 138 | * Documentation 139 | * https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#setting-attributes 140 | */ 141 | public function testUpdateDisplayName() { 142 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', TestVariables::$TEST_USER_ACCOUNT_NAME); 143 | $this->assertEquals($userObject['attributes']['displayname'][0],TestVariables::$TEST_USER_DISPLAY_NAME,'Display name wrong!'); 144 | 145 | $userObject->setDisplayName(TestVariables::$TEST_USER_DISPLAY_NAME_MOD); 146 | $this->assertTrue($userObject->save(),'Update displayname failed'); 147 | $this->assertEquals($userObject['attributes']['displayname'][0],TestVariables::$TEST_USER_DISPLAY_NAME_MOD,'Display name wrong after Update!'); 148 | 149 | $userObject->setDisplayName(TestVariables::$TEST_USER_DISPLAY_NAME); 150 | $this->assertTrue($userObject->update(),'Update displayname failed'); 151 | $this->assertEquals($userObject['attributes']['displayname'][0],TestVariables::$TEST_USER_DISPLAY_NAME,'Display name wrong after Update!'); 152 | } 153 | 154 | 155 | /** 156 | * Documentation 157 | * https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#setting-attributes 158 | */ 159 | public function testMassUpdate() { 160 | $userObject = \Yii::$app->ad->search()->findBy('sAMAccountname', TestVariables::$TEST_USER_ACCOUNT_NAME); 161 | $this->assertEquals($userObject->getDisplayName(),TestVariables::$TEST_USER_DISPLAY_NAME,'Display name wrong!'); 162 | 163 | //Assume nothing is set 164 | $this->assertEquals($userObject->getFirstAttribute('mail'),'','Mail should not be set'); 165 | $this->assertEquals($userObject->getAttribute('homephone',0),'','Homephone should not be set'); 166 | 167 | // Mass setting attributes: 168 | $userObject->fill([ 169 | 'homephone' => '01234567', 170 | 'mail' => 'test@test.lan', 171 | ]); 172 | $this->assertTrue($userObject->save(),'Update attributes failed'); 173 | 174 | //Assume the values are set 175 | $this->assertEquals($userObject->getFirstAttribute('01234567'),'','Mail should not be set'); 176 | $this->assertEquals($userObject->getAttribute('test@test.lan',0),'','Homephone should not be set'); 177 | 178 | // Unset values 179 | $userObject->fill([ 180 | 'homephone' => null, 181 | 'mail' => null, 182 | ]); 183 | $this->assertTrue($userObject->save(),'Update attributes failed'); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /tests/UserModelTest.php: -------------------------------------------------------------------------------- 1 | db->createCommand()->truncateTable("auth_assignment")->execute(); 12 | \Yii::$app->db->createCommand()->truncateTable("user")->execute(); 13 | parent::tearDown(); 14 | } 15 | 16 | // before each test clear 17 | public function setUp() { 18 | parent::setUp(); 19 | \Yii::$app->db->createCommand()->truncateTable("auth_assignment")->execute(); 20 | \Yii::$app->db->createCommand()->truncateTable("user")->execute(); 21 | } 22 | 23 | // generic function to create user model used by test functions 24 | private function getNewUserModel() { 25 | $ldapmodel = new Edvlerblog\Adldap2\model\UserDbLdap(); 26 | $ldapmodel->username = TestVariables::$TEST_USER_ACCOUNT_NAME; 27 | return $ldapmodel; 28 | } 29 | 30 | // test if queryLdapUserObject works 31 | public function testQueryLdapUserObject() { 32 | $userObject = $this->getNewUserModel()->queryLdapUserObject(); 33 | $this->assertEquals($userObject['attributes']['givenname'][0],TestVariables::$TEST_USER_GIVEN_NAME,'Key givenname not found!'); //Givenname is same as defined 34 | } 35 | 36 | // test if group is added to user 37 | public function testQueryLdapUserGroupMembership() { 38 | $groups = $this->getNewUserModel()->getGroupsAssignedInLdap(); 39 | $this->assertContains(TestVariables::$TEST_GROUP_NAME, $groups,'Group ' . TestVariables::$TEST_GROUP_NAME . ' is missing!'); 40 | } 41 | 42 | // test if a query with a disabled username returns null 43 | public function testFindByUsernameDisabledUser() { 44 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByUsername(TestVariables::$TEST_DISABLED_USER); 45 | 46 | $this->assertNull($userObject,'A disabled user cannot be returned by findByUsername'); 47 | } 48 | 49 | // test if findByUsername returns the user 50 | public function testFindByUsername() { 51 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByUsername(TestVariables::$TEST_USER_ACCOUNT_NAME); 52 | $userName = $userObject->queryLdapUserObject()['attributes']['samaccountname'][0]; 53 | 54 | $this->assertEquals($userName,TestVariables::$TEST_USER_ACCOUNT_NAME,'No correct instance of the test user ' . TestVariables::$TEST_USER_ACCOUNT_NAME . ' returned by findByUsername'); 55 | } 56 | 57 | // test if query a non existing user id returns null 58 | public function testFindIdentityNotExistingUserId() { 59 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findIdentity(9999); 60 | 61 | $this->assertNull($userObject,'Not existing identity cannot be found!'); 62 | } 63 | 64 | // test if user is found by id 65 | public function testFindIdentityWithId() { 66 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByUsername(TestVariables::$TEST_USER_ACCOUNT_NAME); 67 | $userObjectById = Edvlerblog\Adldap2\model\UserDbLdap::findIdentity($userObject->getId()); 68 | 69 | $userName = $userObjectById->queryLdapUserObject()['attributes']['samaccountname'][0]; 70 | 71 | $this->assertEquals($userName,TestVariables::$TEST_USER_ACCOUNT_NAME,'No correct instance of the test user ' . TestVariables::$TEST_USER_ACCOUNT_NAME . ' returned by findByUsername'); 72 | } 73 | 74 | // test a disabled user 75 | public function testUpdateAccountStatus() { 76 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByUsername(TestVariables::$TEST_USER_ACCOUNT_NAME); 77 | $userObject->status = Edvlerblog\Adldap2\model\UserDbLdap::STATUS_DISABLED; 78 | $userObject->save(); 79 | 80 | $this->assertEquals($userObject->status,Edvlerblog\Adldap2\model\UserDbLdap::STATUS_DISABLED, 'User should be disabled.'); 81 | $userObject->updateAccountStatus(); 82 | 83 | $this->assertEquals($userObject->status,Edvlerblog\Adldap2\model\UserDbLdap::STATUS_ENABLED, 'User should be reenabled after updateAccountStatus.'); 84 | } 85 | 86 | public function testCheckAllowedToLoginWithNullUser() { 87 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByUsername('NOTEXISTINGACCOUNT'); 88 | $userObject2 = Edvlerblog\Adldap2\model\UserDbLdap::checkAllowedToLogin($userObject); 89 | 90 | $this->assertEquals($userObject,$userObject2, 'The object returned for a successfull login by checkAllowedToLogin has to be euqal to the instance given as parameter'); 91 | } 92 | 93 | public function testCheckAllowedToLogin() { 94 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByUsername(TestVariables::$TEST_USER_ACCOUNT_NAME); 95 | $userObject2 = Edvlerblog\Adldap2\model\UserDbLdap::checkAllowedToLogin($userObject); 96 | 97 | $this->assertEquals($userObject,$userObject2, 'The object returned for a successfull login by checkAllowedToLogin has to be euqal to the instance given as parameter'); 98 | } 99 | 100 | public function testUpdateGroupAssignment() { 101 | $auth = Yii::$app->authManager; 102 | 103 | if(is_null($auth->getPermission('permissionTestUnit'))) { 104 | // add "permissionToUseContanctPage" permission 105 | $permTestUnit = $auth->createPermission('permissionTestUnit'); 106 | $permTestUnit->description = 'Permission autocreated from test unit'; 107 | $auth->add($permTestUnit); 108 | } 109 | 110 | if(is_null($auth->getRole(TestVariables::$TEST_GROUP_NAME))) { 111 | // add "yii2_see_home_group" role and give this role the "permissionToSeeHome" permission 112 | $yii2RoleTestGroup = $auth->createRole(TestVariables::$TEST_GROUP_NAME); 113 | $auth->add($yii2RoleTestGroup); 114 | $auth->addChild($yii2RoleTestGroup, $permTestUnit); 115 | } 116 | 117 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByUsername(TestVariables::$TEST_USER_ACCOUNT_NAME); 118 | $yiiRolesAssignedToUser = Yii::$app->authManager->getRolesByUser($userObject->getId()); //Get all roles assigned to user 119 | 120 | //User has only group yii2_example_group assinged. 121 | $this->assertArrayHasKey(TestVariables::$TEST_GROUP_NAME,$yiiRolesAssignedToUser,'Role ' . TestVariables::$TEST_GROUP_NAME . ' has to be assigned.'); 122 | $this->assertArrayNotHasKey(TestVariables::$TEST_NESTED_GROUP_NAME,$yiiRolesAssignedToUser,'Nested group ' . TestVariables::$TEST_NESTED_GROUP_NAME . ' has NOT to be assigned.'); 123 | 124 | //Search for nested groups 125 | $userObject->setIndividualGroupAssignmentOptions( 126 | ['SEARCH_NESTED_GROUPS' => true] 127 | ); 128 | 129 | //Test nested Group search. 130 | //Nested group cannot bes assigned to user beacause no role in yii2 exists. 131 | $groupsPossible = $userObject->getGroupsAssignedInLdap(); 132 | 133 | $this->assertContains(TestVariables::$TEST_GROUP_NAME,$groupsPossible,'Group ' . TestVariables::$TEST_GROUP_NAME . ' has to be found in AD.'); 134 | $this->assertContains(TestVariables::$TEST_NESTED_GROUP_NAME,$groupsPossible,'Nested group ' . TestVariables::$TEST_NESTED_GROUP_NAME . ' has to be found in AD.'); 135 | } 136 | 137 | public function testFindByAttribute() { 138 | //A query with more than one result sould return null 139 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByAttribute('countryCode',0); 140 | $this->assertNull($userObject,'A attribute which is not suitable for unique identification should return null'); 141 | 142 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByAttribute('displayName',TestVariables::$TEST_USER_DISPLAY_NAME); 143 | $userName = $userObject->queryLdapUserObject()['attributes']['samaccountname'][0]; 144 | 145 | $userObject = Edvlerblog\Adldap2\model\UserDbLdap::findByAttribute('samaccountname',TestVariables::$TEST_USER_ACCOUNT_NAME); 146 | $userName = $userObject->queryLdapUserObject()['attributes']['samaccountname'][0]; 147 | 148 | $this->assertEquals($userName,TestVariables::$TEST_USER_ACCOUNT_NAME,'No correct instance of the test user ' . TestVariables::$TEST_USER_ACCOUNT_NAME . ' returned by findByUsername'); 149 | } 150 | 151 | /** 152 | * @runInSeparateProcess 153 | */ 154 | public function testUserTestLogin() { 155 | $model = new LoginForm(); 156 | 157 | //Test a real login 158 | $this->assertTrue($model->load(['LoginForm'=> ['username' => TestVariables::$TEST_USER_ACCOUNT_NAME, 'password' => TestConfig::$TEST_USER_PASSWORD]]),'Load data into model failed'); 159 | $this->assertTrue($model->login(),'Login with LDAP failed'); 160 | $this->assertFalse(Yii::$app->user->isGuest,'User is not logged in'); 161 | 162 | //Save id 163 | $idOfUser = Yii::$app->user->getId(); 164 | 165 | //Simulate a request after a successfull Login 166 | $userObjectById = Edvlerblog\Adldap2\model\UserDbLdap::findIdentity($idOfUser); 167 | $userName = $userObjectById->queryLdapUserObject()['attributes']['samaccountname'][0]; 168 | $this->assertEquals($userName,TestVariables::$TEST_USER_ACCOUNT_NAME,'No correct instance of the test user ' . TestVariables::$TEST_USER_ACCOUNT_NAME . ' returned by queryLdapUserObject'); 169 | 170 | //Logout 171 | Yii::$app->user->logout(); 172 | $this->assertTrue(Yii::$app->user->isGuest,'User is not a guest'); 173 | 174 | //Try a second login 175 | $this->assertTrue($model->load(['LoginForm'=> ['username' => TestVariables::$TEST_USER_ACCOUNT_NAME, 'password' => TestConfig::$TEST_USER_PASSWORD]]),'Load data into model failed'); 176 | $this->assertTrue($model->login(),'Login with LDAP failed'); 177 | 178 | //Try permissions 179 | $this->assertTrue(Yii::$app->user->can('permissionTestUnit'),'Permission cannot be found'); 180 | } 181 | 182 | public function testCleanup() { 183 | $this->assertTrue($this->checkAndDeleteUser()); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /docs/USAGE_WITH_USER_MODEL.md: -------------------------------------------------------------------------------- 1 | # yii2-adldap-module User model 2 | 3 | The User model adds the possibility to authenticate users against Active Directory in yii2 fashion. 4 | It's also possible to match assigned groups to a user in Active Directory to a role in yii2. 5 | 6 | You can manage your users **completly** over Active Directory without doing anything in yii2!! 7 | The only thing you have to do is assign groups in Active Directory where the group name starts with **yii2** (can be configured) to the user. 8 | The rest would be **magic**! 9 | 10 | But more details later. 11 | 12 | 13 | ## Task 1 - Basic installation 14 | ### 1. Configure yii2-adldap-module as described in the main README.md. 15 | This means your LDAP servers, base_dn and so on are set in the config/web.conf (basic template) OR common/config/main.conf (advanced template). 16 | 17 | ### 2. Configure Database. 18 | See http://www.yiiframework.com/doc-2.0/guide-start-databases.html#configuring-db-connection 19 | 20 | ### 3. Configure yii2 RBAC. Please use the yii\rbac\DbManager and don't forgett to apply the migrations mentioned in the docs. 21 | http://www.yiiframework.com/doc-2.0/guide-security-authorization.html#rbac 22 | 23 | This has to be done: (if you don't know why and what you are doing read the link above!!) 24 | 25 | - Add the authManager class to your config/web.conf (basic template) OR common/config/main.conf (advanced template). 26 | - Add the authManager class to your config/console.conf (basic template) OR console/config/main.conf (advanced template). 27 | 28 | ```php 29 | 'components' => [ 30 | //... 31 | 'authManager' => [ 32 | 'class' => 'yii\rbac\DbManager', 33 | ], 34 | //... 35 | ``` 36 | 37 | Execute the rbac migrations in a shell or cmd 38 | ``` 39 | cd C:\xampp\htdocs\basic 40 | yii migrate --migrationPath=@yii/rbac/migrations 41 | ``` 42 | 43 | 44 | 45 | ### 4. Apply UserDbLdap Migrations. Execute the following command on your shell or cmd 46 | ``` 47 | cd C:\xampp\htdocs\basic 48 | yii migrate --migrationPath=@Edvlerblog/Adldap2/migrations 49 | ``` 50 | 51 | ### 5. Change the identity class in your web.conf (basic template) / main.conf (advanced template). 52 | ```php 53 | 'components' => [ 54 | //user entry already exists! 55 | 'user' => [ 56 | 'identityClass' => 'Edvlerblog\Adldap2\model\UserDbLdap', 57 | //... 58 | ], 59 | //... 60 | ``` 61 | 62 | ### 6. In the basic template change the models/LoginForm.php to use the new identity class. 63 | **Version 1 with sAMAccountname attribute (login eg. mmaderer)** 64 | ```php 65 | //... 66 | public function getUser() 67 | { 68 | if ($this->_user === false) { 69 | $this->_user = \Edvlerblog\Adldap2\model\UserDbLdap::findByUsername($this->username); 70 | } 71 | 72 | return $this->_user; 73 | } 74 | //... 75 | ``` 76 | 77 | **Version 2 with another attribute (login eg. mmaderer@test.lan)** 78 | ```php 79 | //... 80 | public function getUser() 81 | { 82 | if ($this->_user === false) { 83 | $this->_user = \Edvlerblog\Adldap2\model\UserDbLdap::findByAttribute('userPrincipalName',$this->username); //With Principal Name 84 | //$this->_user =\Edvlerblog\Adldap2\model\UserDbLdap::findByAttribute('mail',$this->username); //With Mail 85 | } 86 | 87 | return $this->_user; 88 | } 89 | //... 90 | ``` 91 | 92 | ### 7. Add the LdapController to the controllerMap in the config/console.conf (basic template) OR console/config/main.conf (advanced template). 93 | Maybe the 'controllerMap' section is commented out. 94 | ```php 95 | 'controllerMap' => [ 96 | //... 97 | 'ldapcmd' => [ 98 | 'class' => 'Edvlerblog\Adldap2\commands\LdapController', 99 | ], 100 | //... 101 | ], 102 | ``` 103 | 104 | ### 8. Add the Active Directory configuration from Step 1 to the components in the config/console.conf (basic template) OR console/config/main.conf (advanced template). 105 | 106 | **Because the configurations settings from the web.conf or the main.conf are not used for the console, you have to add your configuration from Step1 also in your console config!!** 107 | 108 | 109 | ```php 110 | 'components' => [ 111 | //... 112 | 'ad' => [ 113 | //... 114 | // COPY YOUR SETTINGS FROM web.conf to this place 115 | //... 116 | ], 117 | //... 118 | ], 119 | ``` 120 | 121 | 122 | ### 9. Test cmd 123 | Open a shell or a cmd and change to the base directory of your yii2 installation (where the composer.json is located). 124 | A ldapcmd entry should be visible. 125 | ```cmd 126 | cd C:\xampp\htdocs\basic 127 | yii 128 | 129 | This is Yii version 2.0.10. 130 | 131 | The following commands are available: 132 | 133 | ... 134 | ... 135 | 136 | - ldapcmd 137 | ldapcmd/create-example-role Create a role with the name yii2_example_group 138 | and assign the permissions 139 | permissionDisplayDetailedAbout and 140 | permissionToUseContanctPage 141 | ldapcmd/import-all-users Import all users from LDAP, assign roles and 142 | account status. Run this command with cron or 143 | another scheduler every X minuten/hours/days. 144 | 145 | ... 146 | ... 147 | 148 | ``` 149 | 150 | 151 | ### 10. Test the Login of your basic app. 152 | Now you can go to the login in page of your yii installation (see upper right corner of the website). You can use any Active Directory user which is able to login on the windows login of your PC. 153 | If everythings okay you should see the username in upper right corner. 154 | 155 | > You can do 156 | > select * from users 157 | > to check if a user was inserted on login. 158 | 159 | > With 160 | > SELECT * FROM auth_assignment; 161 | > you can check if any roles where assigned to your user on login (it is normal that no roles assigned at this point!) 162 | 163 | 164 | ## Task 2 - Configuration 165 | ### General description 166 | Maybe you think: Configuration, what?? But there are severel possible ways to connect your Active Directory as you will see. 167 | 168 | **Before you start over there are some terms you have to understand:** 169 | **role** = This term is used for a role in yii2. If you don't know what a role is look at http://www.yiiframework.com/doc-2.0/guide-security-authorization.html#rbac 170 | **group** = This term is used for a group in Active Directory. 171 | **user** = Means a user which exists in Active Directory. 172 | **username or login** = Depending on Task 1 Number 6 the attribute you used for login. 173 | **assigned group** = Means that a user is member of a group in Active Directory 174 | **assigned role** = Means that a user is member of a role in yii2 175 | 176 | 177 | **How it works in short words with the default settings** 178 | If you successfully finished task 1 imagine the login form which you reach over the Login Button in the right upper corner. 179 | 180 | 181 | If you leave the default configuration, the following is happening on login (and I think it most suites): 182 | - On Login a LDAP query is issued to get the user from Active Directory, if it not exists in database the user is created. 183 | - On Login a LDAP query is issued to get the account status of the user, if the account status is **active** the login is possible. 184 | - On Login the group to role assignment is refreshed with the following settings 185 | - No role has to be assingned to the user for a successfull login 186 | - For Active Directory groups starting with **yii2** or **app** and matching a existing role name in yii2, the role is assigned to the user automatically 187 | - Only roles which are starting with **yii2** or **app** are added or removed from the user, other roles would not be touched 188 | 189 | For a working group to role assignment **you have to create the roles in yii2**! The roles would **NOT** be automatically created. 190 | 191 | ### Example for group configuration 192 | In Step 10 of Task 1 you are have already done a successfull login hopefully. But the problem is that every user in Active Directory with a valid password and active account now can login in yii2. Thats not a good solution! 193 | 194 | Before you continue read the the commets in source code starting at line 161 "Constants starting with GROUP_ASSIGNMENT_****" ([/src/model/UserDbLdap.php#L161](/src/model/UserDbLdap.php#L161)). 195 | 196 | #### Login only possible when a role is assigned to the user 197 | Now add the following to your config/params.php 198 | ```php 199 | return [ 200 | //... 201 | 'LDAP-Group-Assignment-Options' => [ 202 | 'LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX' => "/^(yii2|app)(.*)/", // a role has to be assigned, which is starting with yii2 or with app 203 | 'REGEX_GROUP_MATCH_IN_LDAP' => "/^(yii2|app)(.*)/", // Active Directory groups beginning with yii2 or app are filtered and if a yii2 role with the same name exists the role would be added to the user 204 | 'ADD_GROUPS_FROM_LDAP_MATCHING_REGEX' => true, //add matches between groups and roles to the user 205 | 'REMOVE_ALL_GROUPS_NOT_FOUND_IN_LDAP' => false, //Don't remove rules which does not belong to the regex above 206 | 'REMOVE_ONLY_GROUPS_MATCHING_REGEX' => true, //Only remove groups matching regex REGEX_GROUP_MATCH_IN_LDAP 207 | 'SEARCH_NESTED_GROUPS' => false, //Only check directly assigned groups to the Active Directory user object. 208 | ], 209 | //... 210 | ]; 211 | ``` 212 | The configuration does the same as the default configuration with **one exception!** 213 | - The LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX is not null. 214 | Now only users with roles assigned beginning with **yii2 OR app** can login! 215 | 216 | If you try to login again (please logout before) it will not work! 217 | Why? 218 | The answer is simple for two reasons: 219 | - You don't have a group in Active Directory which name is starting with yii2 and thus the user is not a member of such a group 220 | - yii2 has no corresponding role 221 | 222 | #### Create example role 223 | Look into the source code of the function actionCreateExampleRole ([/src/commands/LdapController.php](/src/commands/LdapController.php)). 224 | 225 | As you can see two permissions are created **(permissionDisplayDetailedAbout, permissionToUseContanctPage)** and assigend to the role 226 | **yii2_example_group**. 227 | One addiontinal permission **permissionToSeeHome** is created and assigned to the role **yii2_see_home_group**. 228 | 229 | Open a shell or a cmd and change to the base directory of your yii2 installation (where the composer.json is located). 230 | Type in your shell: 231 | ```cmd 232 | cd C:\xampp\htdocs\basic 233 | yii ldapcmd/create-example-role 234 | 235 | 236 | !!!! TODO !!!! 237 | Tow roles with the name yii2_example_group and yii2_see_home_group were created 238 | in yii2. 239 | Please create the groups with the same name in Active Directory. 240 | Assign the user you are using for the login to this groups in Active Directory. 241 | ``` 242 | 243 | #### Create Active Directory Group 244 | Now go to your Active Directory Management Console and create a group with the same name as the role (**yii2_example_group**). 245 | Make the user you are using for the login to a member of that group. 246 | 247 | 248 | #### Test the Login 249 | Now you can go to the login in page of your yii installation (see upper right corner of the website). Use the user which you are made to a member of the group **yii2_example_group** in Active Directory. 250 | If everythings okay you should see the username in upper right corner. 251 | 252 | #### Try some permission stuff 253 | ##### Special About text only visible for users with permission "permissionDisplayDetailedAbout" 254 | Open the file views/site/about.php 255 | Place the following after the closing **div** tag or somewhere else in the file 256 | ```php 257 | user->can('permissionDisplayDetailedAbout')) { 259 | echo "

Here are some details, that are only visible if the login is successfull and the user has assigned the role yii2_example_group. After a successfull setup the group was assigned automatically

"; 260 | } 261 | ?> 262 | ``` 263 | The result: If you are not logged in your About Page should not display the text above. If you are logged in, the "Here are some details, ..." should be displayed. 264 | 265 | 266 | ##### Change the access to contact and home screen 267 | Open the file controllers/SiteController.php 268 | Modify the function beaviors() as bellow: 269 | ```php 270 | public function behaviors() 271 | { 272 | return [ 273 | 'access' => [ 274 | 'class' => AccessControl::className(), 275 | 'only' => ['logout','contact','index'], 276 | 'rules' => [ 277 | [ 278 | 'actions' => ['logout'], 279 | 'allow' => true, 280 | 'roles' => ['@'], 281 | ], 282 | [ 283 | 'allow' => true, 284 | 'actions' => ['contact'], 285 | 'roles' => ['permissionToUseContanctPage'], 286 | ], 287 | [ 288 | 'allow' => true, 289 | 'actions' => ['index'], 290 | 'roles' => ['permissionToSeeHome'], 291 | ], 292 | ], 293 | ], 294 | //... 295 | ``` 296 | 297 | The result: If you are not logged in and click on Home or Login, you should be redirected to the login page. If you are successfully logged in, the contact page should be displayed! 298 | 299 | If you are logged in and click on Home, you should get a **403** error. 300 | Because the user is only member in **yii2_example_group**, which has the permission **(permissionDisplayDetailedAbout, permissionToUseContanctPage)** assigned. 301 | To view the Home screen the permission **permissionToSeeHome** is needed which is assigned to role **yii2_see_home_group**. 302 | 303 | To get this role go to your Active Directory Management Console and create a group with the same name as the role (**yii2_see_home_group**). 304 | Make the user you are using for the login to a member of that group. 305 | 306 | 307 | **Have fun with your Active Directory integration!** -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # yii2-adldap-module v6 (wrapper for Adldap2 v10) 2 | [Yii2](http://www.yiiframework.com) extension for Adldap2 (https://packagist.org/packages/adldap2/adldap2) 3 | 4 | [![Latest Stable Version](https://poser.pugx.org/edvlerblog/yii2-adldap-module/v/stable)](https://packagist.org/packages/edvlerblog/yii2-adldap-module) 5 | [![Total Downloads](https://poser.pugx.org/edvlerblog/yii2-adldap-module/downloads)](https://packagist.org/packages/edvlerblog/yii2-adldap-module) 6 | [![Monthly Downloads](https://poser.pugx.org/edvlerblog/yii2-adldap-module/d/monthly)](https://packagist.org/packages/edvlerblog/yii2-adldap-module) 7 | [![Daily Downloads](https://poser.pugx.org/edvlerblog/yii2-adldap-module/d/daily)](https://packagist.org/packages/edvlerblog/yii2-adldap-module) 8 | [![License](https://poser.pugx.org/phpunit/phpunit/license)](https://packagist.org/packages/edvlerblog/yii2-adldap-module) 9 | 10 | * Query Active Directory users, groups, computers, organizational units, ... 11 | * RBAC user model 12 | * Create/Update/Edit Active Directory objects 13 | * Extensive test suite 14 | 15 | ## Please read this if you upgrade from older versions to v5 or v6 16 | Adldap2 changed option keys in version 9. If you upgrade from a previous version you have to change your config/web.conf (basic template) OR common/config/main.conf (advanced template) and 17 | your config/console.conf (basic template) OR console/config/main.conf (advanced template). 18 | 19 | For all Adldap 2 options see https://adldap2.github.io/Adldap2/#/setup?id=array-example-with-all-options. 20 | 21 | The mandatory changed options are: 22 | * admin_username: renamed to username 23 | * admin_password: renamed to password 24 | * domain_controllers: renamed to hosts 25 | 26 | If you configure your username append your domain with **@domain.name**. Otherwise you maybe get 27 | **Adldap\Auth\Bindexception: Invalid Credentials**. 28 | 29 | ```php 30 | ... 31 | 'username' => 'username_ldap_access@example.lan', 32 | ... 33 | ``` 34 | See [Configuration](#configuration) section for example. 35 | 36 | ## Howto contribute or support the extension 37 | As you as delevoper know, it's **not only source code** that matters. The best code is worthless if no **documentation** exists. 38 | My focus is to provide a comprehensive documentation for this extension. This should help **YOU** to do your task fast and without struggle. 39 | Updating this extension take days starting with programming, writing the docs and write test for the code and the docs. 40 | 41 | **I'am glad to see that many persons use the plugin!** 42 | 43 | If you want to help you can do the following: 44 | * Extend or correct the docs and create a Pull-Request 45 | * Fix or extend the plugins source code and create a Pull-Request 46 | * Add further tests and create a Pull-Request 47 | * Open a issue for questions or problems 48 | 49 | **If this project help you reduce time to develop, you can spend me a cup of coffee :)** 50 | 51 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WVHQ2539QZGRU) 52 | 53 | ## List of content 54 | * **Overview** 55 | * [Version](#version) 56 | * [Functions of the extension](#functions-of-the-extension) 57 | 58 | * **Installation and configuration** 59 | * [Installation](#installation) 60 | * [Configuration](#configuration) 61 | 62 | * **Usage Methods** 63 | * [Method 1](#usage-method-1-simple-usage-without-a-user-model) Query informations 64 | * [Method 2](#usage-method-2-deep-integration-into-the-yii2-framework-with-a-user-model) RBAC user model 65 | * [Method 3](#usage-method-3-create-and-modify-active-directory-objects) Create and modify objects 66 | 67 | * **For developers** 68 | * [Testing](#testing) 69 | 70 | ## Version 71 | 72 | Current Version: 73 | yii2-adldap-module Releases beginning with tag v6.*.* are reserved for Adldap2 v10.* 74 | The corresponding Adldap2 repository is https://github.com/Adldap2/Adldap2/tree/master 75 | 76 | **Keep this in mind if you are browsing the GitHub Repository of Adldap2** 77 | 78 | ## Functions of the extension 79 | It has been a long way since 29. Jan 2014, many functions has been added. I noticed for myself that a short overview might help everyone to see whats possible. 80 | 81 | **The simple [Method 1](#usage-method-1-simple-usage-without-a-user-model)** 82 | * Query only informations from Active Directory. 83 | 84 | **The deep integration with [Method 2](#usage-method-2-deep-integration-into-the-yii2-framework-with-a-user-model)** 85 | * Sign in with a Active Directory User is possible **without doing anything in yii2**. The only action needed is creating a Active Directory User and add it to a group in Active Directory. 86 | * Full support of the RBAC-concept from yii2 87 | * Default is to login with the sAMAccountName [Edvlerblog\Adldap2\model\UserDbLdap.php::findByUsername($username)](src/model/UserDbLdap.php). But using any attribute is possible [Edvlerblog\Adldap2\model\UserDbLdap.php::findByAttribute($attribute,$searchValue)](src/model/UserDbLdap.php). 88 | * Default is, that on login the Active Directory Account status and the group assignments are checked. Based on the results the login is possible or not. 89 | * You can access every Active Directory attribute of the user. [Method 2](#usage-method-2-deep-integration-into-the-yii2-framework-with-a-user-model) 90 | * This yii2-extension is highly configurable. 91 | 92 | **Create, modify or delete Active Directory objects with [Method 3: docs/CREATE_MODIFY_DELETE_OBJECTS.md](docs/CREATE_MODIFY_DELETE_OBJECTS.md)** 93 | * Thanks to Adldap2, it's easy to create, modify or delete objects. 94 | 95 | **How to start??** 96 | * My suggestion is that you should start with Method 1. Start with a configration as described below and do some simple querys. If you see how it works, you can try Method 2. 97 | 98 | **If you have some questions...** 99 | * Please see the the separeted howto's for each Method. 100 | * [Method 1: docs/USAGE_WITHOUT_USER_MODEL.md](docs/USAGE_WITHOUT_USER_MODEL.md) 101 | * [Method 2: docs/USAGE_WITH_USER_MODEL.md](docs/USAGE_WITH_USER_MODEL.md) 102 | * [Method 3: docs/CREATE_MODIFY_DELETE_OBJECTS.md](docs/CREATE_MODIFY_DELETE_OBJECTS.md) 103 | * Open a issue or a pull request. 104 | 105 | ## Installation 106 | 107 | The preferred way to install this extension is through [Composer](http://getcomposer.org/). 108 | 109 | Either run 110 | ``` 111 | php composer.phar require edvlerblog/yii2-adldap-module "^6.0.0" 112 | ``` 113 | or add 114 | ``` 115 | "edvlerblog/yii2-adldap-module": "^6.0.0" 116 | ``` 117 | to the require section of your composer.json 118 | 119 | ## Configuration 120 | 121 | Add this code in your components section of the application configuration (eg. config/main.php for advanced template or config/web.php for basic template): 122 | ```php 123 | 'components' => [ 124 | //..... 125 | // other components ... 126 | //..... 127 | 'ad' => [ 128 | 'class' => 'Edvlerblog\Adldap2\Adldap2Wrapper', 129 | 130 | /* 131 | * Set the default provider to one of the providers defined in the 132 | * providers array. 133 | * 134 | * If this is commented out, the entry 'default' in the providers array is 135 | * used. 136 | * 137 | * See https://github.com/Adldap2/Adldap2/blob/master/docs/connecting.md 138 | * Setting a default connection 139 | * 140 | */ 141 | // 'defaultProvider' => 'another_provider', 142 | 143 | /* 144 | * Adlapd2 can handle multiple providers to different Active Directory sources. 145 | * Each provider has it's own config. 146 | * 147 | * In the providers section it's possible to define multiple providers as listed as example below. 148 | * But it's enough to only define the "default" provider! 149 | */ 150 | 'providers' => [ 151 | /* 152 | * Always add a default provider! 153 | * 154 | * You can get the provider with: 155 | * $provider = \Yii::$app->ad->getDefaultProvider(); 156 | * or with $provider = \Yii::$app->ad->getProvider('default'); 157 | */ 158 | 'default' => [ //Providername default 159 | // Connect this provider on initialisation of the LdapWrapper Class automatically 160 | 'autoconnect' => true, 161 | 162 | // The provider's schema. Default is \Adldap\Schemas\ActiveDirectory set in https://github.com/Adldap2/Adldap2/blob/master/src/Connections/Provider.php#L112 163 | // You can make your own https://github.com/Adldap2/Adldap2/blob/master/docs/schema.md or use one from https://github.com/Adldap2/Adldap2/tree/master/src/Schemas 164 | // Example to set it to OpenLDAP: 165 | // 'schema' => new \Adldap\Schemas\OpenLDAP(), 166 | 167 | // The config has to be defined as described in the Adldap2 documentation. 168 | // https://github.com/Adldap2/Adldap2/blob/master/docs/configuration.md 169 | 'config' => [ 170 | // Your account suffix, for example: matthias.maderer@example.lan 171 | 'account_suffix' => '@example.lan', 172 | 173 | // You can use the host name or the IP address of your controllers. 174 | 'hosts' => ['server01.example.lan', 'server02.example.lan'], 175 | 176 | // Your base DN. This is usually your account suffix. 177 | 'base_dn' => 'dc=example,dc=lan', 178 | 179 | // The account to use for querying / modifying users. This 180 | // does not need to be an actual admin account. 181 | 'username' => 'username_ldap_access@example.lan', 182 | 'password' => 'password_ldap_access!', 183 | 184 | // To enable SSL/TLS read the docs/SSL_TLS_AD.md and uncomment 185 | // the variables below 186 | //'port' => 636, 187 | //'use_ssl' => true, 188 | //'use_tls' => true, 189 | ] 190 | ], 191 | 192 | /* 193 | * Another Provider 194 | * You don't have to define another provider if you don't need it. It's just an example. 195 | * 196 | * You can get the provider with: 197 | * or with $provider = \Yii::$app->ad->getProvider('another_provider'); 198 | */ 199 | 'another_provider' => [ //Providername another_provider 200 | // Connect this provider on initialisation of the LdapWrapper Class automatically 201 | 'autoconnect' => false, 202 | 203 | // The provider's schema. Default is \Adldap\Schemas\ActiveDirectory set in https://github.com/Adldap2/Adldap2/blob/master/src/Connections/Provider.php#L112 204 | // You can make your own https://github.com/Adldap2/Adldap2/blob/master/docs/schema.md or use one from https://github.com/Adldap2/Adldap2/tree/master/src/Schemas 205 | // Example to set it to OpenLDAP: 206 | // 'schema' => new \Adldap\Schemas\OpenLDAP(), 207 | 208 | // The config has to be defined as described in the Adldap2 documentation. 209 | // https://github.com/Adldap2/Adldap2/blob/master/docs/configuration.md 210 | 'config' => [ 211 | // Your account suffix, for example: matthias.maderer@test.lan 212 | 'account_suffix' => '@test.lan', 213 | 214 | // You can use the host name or the IP address of your controllers. 215 | 'hosts' => ['server1.test.lan', 'server2'], 216 | 217 | // Your base DN. This is usually your account suffix. 218 | 'base_dn' => 'dc=test,dc=lan', 219 | 220 | // The account to use for querying / modifying users. This 221 | // does not need to be an actual admin account. 222 | 'username' => 'username_ldap_access@test.lan', 223 | 'password' => 'password_ldap_access', 224 | 225 | // To enable SSL/TLS read the docs/SSL_TLS_AD.md and uncomment 226 | // the variables below 227 | //'port' => 636, 228 | //'use_ssl' => true, 229 | //'use_tls' => true, 230 | ] // close config 231 | ], // close provider 232 | ], // close providers array 233 | ], //close ad 234 | ``` 235 | 236 | See official documentation for all config options. 237 | https://adldap2.github.io/Adldap2/#/setup?id=options 238 | 239 | ## Usage - Method 1, Method 2 and Method 3 240 | 241 | ### Usage method 1: Simple usage without a user model 242 | If you are need to query some informations for a user from the Active Directory this would be best way. 243 | No additional configuration is needed and the only thing to do is to add the [configuration](#configuration) as described above to your components section. 244 | 245 | You only use the extension in the regular Yii2 style: 246 | ```php 247 | //... 248 | $un = 'testuser'; 249 | 250 | /* 251 | There are three ways available to call Adldap2 function. 252 | If you use more providers (multiple Active Directory connections) 253 | you make one as default and you can call this one with Method1 or Method2 254 | and the second one will be called with Method3. 255 | */ 256 | 257 | //Get the Ldap object for the user. 258 | //$ldapObject holds a class of type Adldap\Models\User from the Adldap project! 259 | // Method 1: uses the default provider given in the configuration above (array key defaultProvider) 260 | $ldapObject = \Yii::$app->ad->search()->findBy('sAMAccountname', $un); 261 | // Method 2: uses the default provider given in the configuration above (array key defaultProvider) 262 | $ldapObject = \Yii::$app->ad->getDefaultProvider()->search()->findBy('sAMAccountname', $un); 263 | // Method 3: get the provider by name (here name default is used). 264 | $ldapObject = \Yii::$app->ad->getProvider('default')->search()->findBy('sAMAccountname', $un); 265 | 266 | //Examples 267 | //Please note that all fields from ldap are arrays! 268 | //Access it with ..[0] if it is a single value field. 269 | $givenName = $ldapObject['givenname'][0]; 270 | $surname = $ldapObject['sn'][0]; 271 | $displayname = $ldapObject['displayname'][0]; 272 | $telephone = $ldapObject['telephonenumber'][0]; 273 | 274 | echo 'gn: ' . $givenName . ' sn: ' . $surname . 275 | ' dispname: ' . $displayname . ' phone: ' . $telephone; 276 | 277 | //Print all possible attributes 278 | echo '
' . print_r($ldapObject,true) . '
'; 279 | 280 | // More ways to get attributes: 281 | // https://github.com/Adldap2/Adldap2/blob/master/docs/models/model.md#getting-attributes 282 | ``` 283 | 284 | **Further documentation with examples:** [docs/USAGE_WITHOUT_USER_MODEL.md](docs/USAGE_WITHOUT_USER_MODEL.md) 285 | 286 | Modify of attributes is also possible. See [Method 3](#usage-method-3-create-modify-and-delete-active-directory-objects). 287 | 288 | --- 289 | 290 | ### Usage method 2: Deep integration into the yii2 framework with a user model 291 | The second method gives you the ability to authenticate users against Active Directory with a special user model. It intgerates very well into the RBAC security concept of yii2 (http://www.yiiframework.com/doc-2.0/guide-security-authorization.html#rbac). 292 | 293 | You can use all features of the yii2 user integration. 294 | 295 | Some Examples: 296 | ```php 297 | //... 298 | //Has user a permission? 299 | $hasPermission = \Yii::$app->user->can('permissionDisplayDetailedAbout'); 300 | 301 | 302 | //Query informations from Active Directory. You can use it in a controller, a view, everywhere in yii2! 303 | if (!\Yii::$app->user->isGuest) { 304 | //Get the yii2 identitiy, which was set by the Yii::$app->user->login(..,..) function 305 | //See model/LoginForm.php in the basic template for the login logic 306 | $yii2IdentityObject = \Yii::$app->user->identity; 307 | 308 | $rolesOfUser = \Yii::$app->authManager->getRolesByUser($yii2IdentityObject->getId()); 309 | echo '
' . print_r($rolesOfUser,true) . '
'; 310 | 311 | //Get the Ldap object for the user. 312 | //$ldapObject holds a class of type Adldap\Models\User from the Adldap project! 313 | //No performance issues, because the queryLdapUserObject function uses a cache. 314 | $ldapObject = $yii2IdentityObject->queryLdapUserObject(); 315 | 316 | //Examples 317 | //Please note that all fields from ldap are arrays! 318 | //Access it with ..[0] if it is a single value field. 319 | $givenName = $ldapObject['givenname'][0]; 320 | $surname = $ldapObject['surname'][0]; 321 | $displayname = $ldapObject['displayname'][0]; 322 | $telephone = $ldapObject['telephonenumber'][0]; 323 | 324 | echo 'gn: ' . $givenName . ' sn: ' . $surname . 325 | ' dispname: ' . $displayname . ' phone: ' . $telephone; 326 | 327 | //Print all possible attributes 328 | echo '
' . print_r($ldapObject,true) . '
'; 329 | 330 | // More ways to get attributes of a user model: 331 | // https://adldap2.github.io/Adldap2/#/models/user 332 | } 333 | //... 334 | ``` 335 | 336 | If you use the [Edvlerblog\Adldap2\model\UserDbLdap.php](src/model/UserDbLdap.php) class you can do things like login with a user into yii2 **without createing them** in yii2. Tasks like creating a user, assigning roles and check password against Active Directory all automatically done from [Edvlerblog\Adldap2\model\UserDbLdap.php](src/model/UserDbLdap.php) class. 337 | 338 | For example imagine the following: 339 | - You create a user in Active Directory and assign this user to a group starting with **yii2_** (e.g. yii2_example_group). 340 | - In yii2 a role with the same name exists (yii2_example_group). The role has some permissions assigned. 341 | 342 | If you try to login with your new user, the user is created **automatically** in yii2 and role yii2_example_group is assigned **automatically** on login. 343 | For the human this is transparent. The only feedback to the human is a successfull login and that it is possible to use the functions which he has permissions to access. 344 | 345 | **Further documentation with setup and examples:** [docs/USAGE_WITH_USER_MODEL.md](docs/USAGE_WITH_USER_MODEL.md) 346 | 347 | --- 348 | 349 | ### Usage method 3: Create, modify and delete Active Directory objects 350 | Adldap2 offers the option to create, modify and delete Active Directory objects. 351 | See https://adldap2.github.io/Adldap2/#/models/model for documentation. 352 | 353 | **Prequesits** 354 | * To create or modify attributes of a Active Directory object use a bind user in your [configuration](#configuration) with rights to change the attributes of the objects (a dirty but **very discourraged** way is to add the bind user to the domain-admins group)! 355 | * For some actions, like change the password, you need a SSL/TLS connection. See [configuration](#configuration) for further hints. 356 | 357 | **One example:** Modify the displayname of a user 358 | 359 | ```php 360 | // https://adldap2.github.io/Adldap2/#/searching?id=finding-a-record-by-a-specific-attribute 361 | // Step 1: Query the ldap object (via method 1 or method 2) 362 | $un = 'testuser'; 363 | $ldapObject = \Yii::$app->ad->getProvider('default')->search()->findBy('sAMAccountname', $un); 364 | 365 | // Step 2: Update the attribute 366 | // 367 | $ldapObject->setDisplayName('Fancy New Displayname'); 368 | 369 | // Step 3: Save an check return value 370 | // https://adldap2.github.io/Adldap2/#/models/model?id=attributes 371 | // https://adldap2.github.io/Adldap2/#/models/model?id=updating-attributes 372 | if ($ldapObject->save()) { 373 | echo "// Displayname successfully updated."; 374 | } else { 375 | echo "// There was an issue updating this user."; 376 | } 377 | ``` 378 | 379 | **Further documentation:** [docs/CREATE_MODIFY_DELETE_OBJECTS.md](docs/CREATE_MODIFY_DELETE_OBJECTS.md) 380 | 381 | --- 382 | 383 | ### Testing 384 | This section is only for developers, that may extend the functionality. 385 | 386 | These test classes exists: 387 | * tests/InitialTest.php: Delete, create and modify users and groups and check results 388 | * tests/UserModelTest.php: Test the [src/model/UserDbLdap.php](src/model/UserDbLdap.php) 389 | 390 | For the UserModelTest test it's neccessary to setup the deep integration as described here: [docs/USAGE_WITH_USER_MODEL.md](docs/USAGE_WITH_USER_MODEL.md) 391 | 392 | **Usage:** 393 | * Use the phpunit from yii2. Its placed in vendor\bin\phpunit. 394 | * Create the config class tests\base\TestConfig.php from the template tests\base\TestConfigSample.php. 395 | 396 | Start the tests in windows with: 397 | ```cmd 398 | // WARNING!! NOT RUN ON PRODUCTION!! 399 | // TABLES ARE TRUNCATED AND ACTIVE DIRECTORY IS MODIFIED! 400 | // TAKE A LOOK AT THE SOURCE CODE BEFORE RUNNING THE TESTS. 401 | cd vendor/edvlerblog/yii2-adldap-module 402 | ..\..\bin\phpunit -v --debug 403 | ..\..\bin\phpunit --testdox 404 | ``` 405 | -------------------------------------------------------------------------------- /src/model/UserDbLdap.php: -------------------------------------------------------------------------------- 1 | Edvlerblog\Adldap2\model\UserDbLdap::SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK, 64 | * 'LDAP-Group-Assignment-Options' => Edvlerblog\Adldap2\model\UserDbLdap::GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX, 65 | * //... 66 | * ]; 67 | * 68 | * With complete own settings 69 | * 70 | * return [ 71 | * //... 72 | * 'LDAP-User-Sync-Options' => [ 73 | * 'ON_LOGIN_CREATE_USER' => false, 74 | * 'ON_LOGIN_REFRESH_GROUP_ASSIGNMENTS' => false, 75 | * 'ON_LOGIN_REFRESH_LDAP_ACCOUNT_STATUS' => true, 76 | * 'ON_REQUEST_REFRESH_LDAP_ACCOUNT_STATUS' => false, 77 | * ], 78 | * 'LDAP-Group-Assignment-Options' => Edvlerblog\Adldap2\model\UserDbLdap::GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX, 79 | * //... 80 | * ]; 81 | * 82 | */ 83 | 84 | 85 | /** 86 | * Constant SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK 87 | * 88 | * This constant is DEFAULT if you doesn't configure anything. 89 | * - On login a user is automatically created, if it not exists in database 90 | * - On login the group assignments are refreshed 91 | * - On login the account status is refreshed 92 | * 93 | * In simple words: 94 | * If you login with a Active Directory user, which is active, and the password matches you can login! 95 | * On every login the above mentioned points are checked. 96 | * For example: If a user is deactived, the next login would fail but the current session would be valid until logout. 97 | * 98 | * One word of caution! There are further options which beginning with GROUP_ASSIGNMENT_***. This are also influence the login behavior. 99 | */ 100 | const SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK = [ 101 | /* ON_LOGIN_CREATE_USER 102 | * If this is true and the user is not found in database (maybe first login or other reasons), 103 | * a LDAP query would search for in Active Directory. 104 | * 105 | * If the user is found a new UserDbLdap object would be created, the status is refreshed, 106 | * and the groups are assigned according to the rules defined in GROUP_ASSIGNMENT_**** settings. 107 | * 108 | * If the password matches a login is possible. For the user it seems like a normal login. 109 | */ 110 | 'ON_LOGIN_CREATE_USER' => true, 111 | 112 | /* 113 | * ON_LOGIN_REFRESH_GROUP_ASSIGNMENTS 114 | * If this is set to true. Evertime a user login is done, 115 | * the group assignments are refreshed according to the rules defined in GROUP_ASSIGNMENT_**** settings. 116 | */ 117 | 'ON_LOGIN_REFRESH_GROUP_ASSIGNMENTS' => true, 118 | 119 | /* 120 | * ON_LOGIN_REFRESH_LDAP_ACCOUNT_STATUS 121 | * If this is set to true. Evertime a user login is done, 122 | * the status of the user account in Active Directory is queried and stored in database. 123 | */ 124 | 'ON_LOGIN_REFRESH_LDAP_ACCOUNT_STATUS' => true, 125 | 126 | /* 127 | * ON_REQUEST_REFRESH_LDAP_ACCOUNT_STATUS 128 | * If this is set to true. Evertime a PAGE REFRESH is done, 129 | * the status of the user account in Active Directory is queried and stored in database. 130 | */ 131 | 'ON_REQUEST_REFRESH_LDAP_ACCOUNT_STATUS' => false, 132 | ]; 133 | 134 | /** 135 | * Constant SYNC_OPTIONS_TEMPLATE_ONLY_BACKEND_TASK 136 | * 137 | * On login nothing would be done. If the user doesn't exists or the roles assigned to 138 | * the user are not allowed to login. 139 | * 140 | * You has to configure the backend task, which imports all users from active directory. 141 | * Please configure the basic's as described here in the folder docs/USAGE_WITH_USER_MODEL.md. 142 | * 143 | * Follow this steps to run the backend task: 144 | * cmd> cd c:\xampp\htdocs\basic 145 | * cmd> yii ldapcmd/import-all-users 146 | * 147 | * This command should import all users, which are matching the configured group assignment options. 148 | * See GROUP_ASSIGNMENT_** options. 149 | * 150 | */ 151 | const SYNC_OPTIONS_TEMPLATE_ONLY_BACKEND_TASK = [ 152 | 'ON_LOGIN_CREATE_USER' => false, 153 | 'ON_LOGIN_REFRESH_LDAP_ACCOUNT_STATUS' => false, 154 | 'ON_LOGIN_REFRESH_GROUP_ASSIGNMENTS' => false, 155 | 'ON_REQUEST_REFRESH_LDAP_ACCOUNT_STATUS' => false 156 | ]; 157 | 158 | 159 | /** 160 | * Constants starting with GROUP_ASSIGNMENT_**** 161 | * This constant defines all options needed, that are influence the Active Directory group to yii2 role matching 162 | * of the UserDbLdap class. 163 | * 164 | * The first main purpose of this constant is to define, if a login without a assigned role is possible. 165 | * 166 | * The second purpose is to define which groups are matched to roles. This enables you to match only certain 167 | * groups to roles. DON'T forget to create a role with the same name as the group in Active Directory. 168 | * 169 | * The third purpose is to define how to deal with roles not matching a LDAP group name (remove them or don't touch them). 170 | * 171 | * 172 | * 173 | * You can configure your own settings in the config/params.php 174 | * 175 | * With predefined constant 176 | * 177 | * return [ 178 | * //... 179 | * 'LDAP-Group-Assignment-Options' => Edvlerblog\Adldap2\model\UserDbLdap::GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX_WITH_ROLE, 180 | * //... 181 | * ]; 182 | * 183 | * With complete own settings 184 | * 185 | * return [ 186 | * //... 187 | * 'LDAP-Group-Assignment-Options' => [ 188 | * 'LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX' => '/(.*)/', 189 | * 'REGEX_GROUP_MATCH_IN_LDAP' => '/^(yii2|app)(.*)/', // start with 190 | * 'ADD_GROUPS_FROM_LDAP_MATCHING_REGEX' => true, 191 | * 'REMOVE_ALL_GROUPS_NOT_FOUND_IN_LDAP' => true, 192 | * 'REMOVE_ONLY_GROUPS_MATCHING_REGEX' => false, 193 | * 'SEARCH_NESTED_GROUPS' => false 194 | * ], 195 | * //... 196 | * ]; 197 | * 198 | * 199 | * Some options use regex. Here are some exmaples for common use cases: 200 | * $regex = '/^(yii2)(.*)/'; // Evaluates to true if the beginning of the groupname is yii2. Example yii2_create_post gives true 201 | * $regex = '/^(yii2|app)(.*)/'; // Evaluates to true if the beginning of the groupname is yii2 OR app. Example yii2_create_post gives true, app_create_post gives true 202 | * $regex = '/(.*)(yii2)$/'; // Evaluates to true if the end of the groupname is yii2. Example create_post_yii2 gives true 203 | * $regex = '/(.*)(yii2|app)$/'; // Evaluates to true if the end of the groupname is yii2 OR app. Example create_post_yii2 gives true, create_post_app gives true 204 | * $regex = '/^(yii2_complete_group_name)$/'; // Evaluates to true if the complete groupname matches yii2_complete_group_name 205 | * $regex = '/^(yii2_complete_group_name|another_complete_group_name)$/'; // Evaluates to true if the complete groupname matches yii2_complete_group_name OR another_complete_group_name 206 | * $regex = '/(yii2)/'; // Evaluates to true if the groupname contains yii2. Example group_yii2_post gives true 207 | * $regex = '/(yii2|app)/'; // Evaluates to true if the groupname contains yii2. Example group_yii2_post gives true, Example group_app_post gives true, 208 | * $regex = '/(.*)/'; // Evaluates to true on every groupname 209 | */ 210 | 211 | 212 | /** 213 | * GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX constant 214 | * If you don't define your own settings the constant GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX is used as default. 215 | * 216 | * In short words the settings does the following: 217 | * - Login is possible without any role assinged 218 | * - Active Directory groups starting with yii2 and matching (name has to be euqal!) a yii2 role are added to the user 219 | * - Groups removed in Active Directory are removed in yii2 to. Groups not matching the REGEX_GROUP_MATCH_IN_LDAP are not touched. 220 | */ 221 | const GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX = [ 222 | /* 223 | * LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX 224 | * If the value of this key is null, a user can login without a role assinged! 225 | * 226 | * If a regex is given, a role has to be assinged that matches the regex given for this key to successfully login. 227 | * 228 | * For example with the regex 229 | * 'LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX' => '/(.*)/'; 230 | * a user can only login if a role is assigned. But the name can be anything. 231 | * 232 | * With this regex 233 | * 'LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX' => '/^(yii2)(.*)/'; 234 | * a user can only login if a role is assigned which name is starting with yii2. 235 | */ 236 | 'LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX' => null, // no role necceassarry for login 237 | 238 | /* 239 | * REGEX_GROUP_MATCH_IN_LDAP 240 | * If a role exists in yii2 which matches a LDAP group (names has to be the same!), it is assigned to the user. 241 | * The LDAP groups can be filtered with a regex to match only certain groups from LDAP. 242 | * 243 | * In this example only groups starting with yii2 and app would be assigned to the user 244 | * if a corresponding role (again names has to be the same!) exists in yii2. 245 | */ 246 | 'REGEX_GROUP_MATCH_IN_LDAP' => '/^(yii2|app)(.*)/', // groupname start with yii2 or app 247 | 248 | /* 249 | * ADD_GROUPS_FROM_LDAP_MATCHING_REGEX 250 | * Groups from Active Directory would be matched with roles in yii2 and added as described in REGEX_GROUP_MATCH_IN_LDAP. 251 | * 252 | * Simple words: 253 | * If you add a group in Active Directory it would be added in yii too. 254 | */ 255 | 'ADD_GROUPS_FROM_LDAP_MATCHING_REGEX' => true, 256 | 257 | /* 258 | * REMOVE_ALL_GROUPS_NOT_FOUND_IN_LDAP 259 | * If this option is true, all roles NOT matching a LDAP group would be remove from the 260 | * user. 261 | * 262 | * If a role does NOT exists in Active Directory it would be removed. 263 | * As as result the user only have roles assigned, which are exists as groups in Active Directory, the user is member of and which 264 | * matches a role in yii2. 265 | */ 266 | 'REMOVE_ALL_GROUPS_NOT_FOUND_IN_LDAP' => false, 267 | 268 | /* 269 | * REMOVE_ONLY_GROUPS_MATCHING_REGEX 270 | * If this option is true, only roles would be removed from the user which matches the regex given 271 | * under REGEX_GROUP_MATCH_IN_LDAP. 272 | * 273 | * This means the user always have the roles which are assingned as groups in Active Directory. 274 | * If you remove one, it would be removed in yii2 too. 275 | * But other roles assigned directly in yii2 would not be touched. 276 | */ 277 | 'REMOVE_ONLY_GROUPS_MATCHING_REGEX' => true, 278 | 279 | /* 280 | * Check for groups, which are not directly assigned to the user, 281 | * but assigned to another group in which the user is member of. 282 | * (keywords: nested groups, group tree, group as member of group). 283 | */ 284 | 'SEARCH_NESTED_GROUPS' => false 285 | ]; 286 | 287 | 288 | /** 289 | * GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX_WITH_ROLE constant 290 | * Same as GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX. 291 | * 292 | * The only differnce is, that a login is only allowed with a role assigned. 293 | */ 294 | const GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX_WITH_ROLE = [ 295 | 'LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX' => '/(.*)/', // a role has to be assign, the name could be everything 296 | 'REGEX_GROUP_MATCH_IN_LDAP' => '/^(yii2|app)(.*)/', // start with 297 | 'ADD_GROUPS_FROM_LDAP_MATCHING_REGEX' => true, 298 | 'REMOVE_ALL_GROUPS_NOT_FOUND_IN_LDAP' => true, 299 | 'REMOVE_ONLY_GROUPS_MATCHING_REGEX' => false, 300 | 'SEARCH_NESTED_GROUPS' => false 301 | ]; 302 | 303 | /** 304 | * GROUP_ASSIGNMENT_LDAP_MANDANTORY constant 305 | * All roles that are not found in active directory as group will be removed from the user object. 306 | * 307 | * The roles assigned to the user object are always the same as the member of attribute of the active directory user. 308 | */ 309 | const GROUP_ASSIGNMENT_LDAP_MANDANTORY = [ 310 | 'LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX' => '/(.*)/', 311 | 'REGEX_GROUP_MATCH_IN_LDAP' => '/^(yii2|app)(.*)/', // start with 312 | 'ADD_GROUPS_FROM_LDAP_MATCHING_REGEX' => true, 313 | 'REMOVE_ALL_GROUPS_NOT_FOUND_IN_LDAP' => true, 314 | 'REMOVE_ONLY_GROUPS_MATCHING_REGEX' => false, 315 | 'SEARCH_NESTED_GROUPS' => false 316 | ]; 317 | 318 | 319 | /** 320 | * Constants starting with EXTENSION_OPTIONS_**** 321 | * 322 | * This array defines the default options for the extension. 323 | * 324 | * You can configure your own settings in the config/params.php 325 | * 326 | * With predefined constant 327 | * 328 | * return [ 329 | * //... 330 | * 'yii2-adldap-extension-Options' => Edvlerblog\Adldap2\model\UserDbLdap::EXTENSION_OPTIONS_DEBUG, 331 | * //... 332 | * ]; 333 | * 334 | * With complete own settings 335 | * 336 | * return [ 337 | * //... 338 | * 'yii2-adldap-extension-Options' => [ 339 | * 'ENABLE_YII2_PROFILING' => false 340 | * ], 341 | * //... 342 | * ]; 343 | */ 344 | const EXTENSION_OPTIONS_DEFAULT = [ 345 | /* 346 | * Set the name of the adldap provider used. 347 | * The name is defined in you web.php or main.php 348 | */ 349 | 'adldap-providername' => '__USE_DEFAULT_PROVIDER__', 350 | 351 | /* 352 | * Disable/enable profiling (yii2-debugger tab Profiling or Timeline), which is used 353 | * to analyse time needed for each function in the yii2-adldap-module 354 | */ 355 | 'ENABLE_YII2_PROFILING' => false 356 | ]; 357 | 358 | const EXTENSION_OPTIONS_DEBUG = [ 359 | 'adldap-providername' => '__USE_DEFAULT_PROVIDER__', 360 | 'ENABLE_YII2_PROFILING' => true 361 | ]; 362 | 363 | /** 364 | * Constants for a enabeld/disabled which are saved to database. 365 | */ 366 | const STATUS_DISABLED = 0; 367 | const STATUS_ENABLED = 1; 368 | 369 | const YII2_PROFILE_NAME = 'Edvlerblog\Adldap2\model\UserDbLdap::'; 370 | 371 | private $ldapUserObject = null; 372 | private $individualSyncOptions = null; 373 | private $individualGroupAssignmentOptions = null; 374 | 375 | /** 376 | * @inheritdoc 377 | */ 378 | public static function tableName() 379 | { 380 | return '{{%user}}'; 381 | } 382 | 383 | /** 384 | * @inheritdoc 385 | */ 386 | public function behaviors() 387 | { 388 | return [ 389 | TimestampBehavior::className(), 390 | ]; 391 | } 392 | 393 | /** 394 | * @inheritdoc 395 | */ 396 | public function rules() 397 | { 398 | return [ 399 | ['status', 'default', 'value' => static::STATUS_DISABLED], 400 | ['status', 'in', 'range' => [static::STATUS_ENABLED, static::STATUS_DISABLED]], 401 | ]; 402 | } 403 | 404 | /** 405 | * @inheritdoc 406 | */ 407 | public static function findIdentityByAccessToken($token, $type = null) 408 | { 409 | throw new NotSupportedException('Edvlerblog\Adldap2\model\UserDbLdap::findIdentityByAccessToken($token, $type = null) is not implemented.'); 410 | } 411 | 412 | /** 413 | * @inheritdoc 414 | */ 415 | public static function findIdentity($id) 416 | { 417 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 418 | Yii::beginProfile('findIdentity', static::YII2_PROFILE_NAME . 'findIdentity'); 419 | } 420 | 421 | //Database check. If no dataset is found then the only possible return value is null. 422 | $userObjectDb = static::findOne(['id' => $id]); 423 | $checkedUserObjectDB = static::checkAllowedToLogin($userObjectDb); 424 | 425 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 426 | Yii::endProfile('findIdentity', static::YII2_PROFILE_NAME . 'findIdentity'); 427 | } 428 | return $checkedUserObjectDB; 429 | } 430 | 431 | 432 | /** 433 | * Find a user with a LDAP-attribute and a value. 434 | * 435 | * If you don't want to use the unsername (samaccountname) for login and 436 | * for example want to use LDAP-attribute userPrincipalName for the login, call this function with 437 | * \Edvlerblog\Adldap2\model\UserDbLdap::findByAttribute('userPrincipalName',$this->username); 438 | * You can use any LDAP-Attribute availiable. 439 | * 440 | * This function does a LDAP-Query to the Active Directory to retrive the 441 | * samaccountname. If exactly one result is returned, the function 442 | * static::findByUsername(QUERIED_SAM_ACCOUNT_NAME); is called. 443 | * 444 | * @param string $attribute Attribut for 445 | * @param string $searchValue The value, the attribute 446 | * @return Edvlerblog\Adldap2\model\UserDbLdap A User instance, if user is valid. Otherwise NULL. 447 | */ 448 | public static function findByAttribute($attribute, $searchValue) { 449 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 450 | Yii::beginProfile('Attribute: ' . $attribute . '; Value: ' . $searchValue, static::YII2_PROFILE_NAME . 'findByAttribute'); 451 | } 452 | 453 | $userObjectsFound = static::getAdldapProvider()->search()->select('samaccountname')->where($attribute, '=', $searchValue)->get(); 454 | 455 | $userObjectReturn = null; 456 | if(count($userObjectsFound) == 1) { 457 | $userObjectReturn = static::findByUsername($userObjectsFound[0]['samaccountname'][0]); 458 | } 459 | 460 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 461 | Yii::endProfile('Attribute: ' . $attribute . '; Value: ' . $searchValue, static::YII2_PROFILE_NAME . 'findByAttribute'); 462 | } 463 | return $userObjectReturn; 464 | } 465 | 466 | /** 467 | * Finds user by username (samaccountname) 468 | * 469 | * Depending on the synchronisation options additional LDAP querys are done. 470 | * 471 | * For a description of the options see the top of this class, where templates (e.g. SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK) are defined. 472 | * This Templates can be used directly in params or you can define each param by yourself. 473 | * 474 | * Example config/params.php 475 | * return [ 476 | * ... 477 | * 'LDAP-User-Sync-Options' => Edvlerblog\Adldap2\model\UserDbLdap::SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK, 478 | * ... 479 | * ]; 480 | * 481 | * If the user does not exists in database and the option [[ON_LOGIN_CREATE_USER]] is true 482 | * a LDAP query would be done to find the user in LDAP and sync it to database. 483 | * 484 | * If the [[ON_LOGIN_REFRESH_GROUP_ASSIGNMENTS]] option is true, the group assigment is 485 | * queryied from LDAP and stored in database on login. 486 | * 487 | * If the [[ON_LOGIN_REFRESH_LDAP_ACCOUNT_STATUS]] option is true, the account status is 488 | * queryied from LDAP and stored in database on login. 489 | * 490 | * @param string $username username of the user object 491 | * @return Edvlerblog\Adldap2\model\UserDbLdap A User instance, if user is valid. Otherwise NULL. 492 | */ 493 | public static function findByUsername($username) { 494 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 495 | Yii::beginProfile('findByUsername', static::YII2_PROFILE_NAME . 'findByUsername'); 496 | } 497 | 498 | $userObjectDb = static::findOne(['username' => $username]); 499 | 500 | //Create user if not found in db 501 | if ($userObjectDb == null) { 502 | //Just create to get synchronisation options 503 | $userObjectDb = new static(); 504 | 505 | if(static::getSyncOptions('ON_LOGIN_CREATE_USER', $userObjectDb->individualSyncOptions) == true) { 506 | $userObjectDb = static::createNewUser($username); 507 | } else { 508 | $userObjectDb = null; 509 | } 510 | } else { 511 | //Refresh group assignments of user if found in database 512 | if (static::getSyncOptions('ON_LOGIN_REFRESH_GROUP_ASSIGNMENTS', $userObjectDb->individualSyncOptions) == true) { 513 | $userObjectDb->updateGroupAssignment(); 514 | } 515 | 516 | //Refresh account status of user if found in database 517 | if (static::getSyncOptions('ON_LOGIN_REFRESH_LDAP_ACCOUNT_STATUS', $userObjectDb->individualSyncOptions) == true && 518 | static::getSyncOptions('ON_REQUEST_REFRESH_LDAP_ACCOUNT_STATUS', $userObjectDb->individualSyncOptions) == false) 519 | { 520 | $userObjectDb->updateAccountStatus(); 521 | } 522 | } 523 | 524 | $checkedUserObjectDB = static::checkAllowedToLogin($userObjectDb); 525 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 526 | Yii::endProfile('findByUsername', static::YII2_PROFILE_NAME . 'findByUsername'); 527 | } 528 | return $checkedUserObjectDB; 529 | } 530 | 531 | /** 532 | * Check if a [[Edvlerblog\Adldap2\model\UserDbLdap]] is allowed to login. 533 | * Two checks are done before a user object is returned. 534 | * 535 | * 1. Check if user is enabled 536 | * If [[ON_REQUEST_REFRESH_LDAP_ACCOUNT_STATUS]] option is true, the account status is 537 | * queryied ON EVERY REQUEST from LDAP and stored in database on login. 538 | * 539 | * 2. Check if the user has a role assigned which is allowed to login 540 | * See Parameter LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX 541 | * 542 | * @param Edvlerblog\Adldap2\model\UserDbLdap $userObjectDb User object to validate. 543 | * @return Edvlerblog\Adldap2\model\UserDbLdap A User instance if user is valid. Otherwise NULL. 544 | */ 545 | public static function checkAllowedToLogin($userObjectDb) { 546 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 547 | Yii::beginProfile('checkAllowedToLogin', static::YII2_PROFILE_NAME . 'checkAllowedToLogin'); 548 | } 549 | 550 | $userInstanceAfterLogin = null; 551 | 552 | if ($userObjectDb != null && 553 | $userObjectDb->username != null && 554 | $userObjectDb->getId() != null 555 | ) { 556 | //Refresh account status on every request? 557 | if (static::getSyncOptions('ON_REQUEST_REFRESH_LDAP_ACCOUNT_STATUS', $userObjectDb->individualSyncOptions) == true) { 558 | $userObjectDb->updateAccountStatus(); 559 | } 560 | 561 | //Login only possible if a role is assigned which matches the LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX regex 562 | if ($userObjectDb->status == static::STATUS_ENABLED && 563 | static::getGroupAssigmentOptions('LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX',$userObjectDb->individualGroupAssignmentOptions) != null) 564 | { 565 | $rolesAssignedToUser = Yii::$app->authManager->getRolesByUser($userObjectDb->getId()); 566 | 567 | foreach ($rolesAssignedToUser as $role) { 568 | if(preg_match(static::getGroupAssigmentOptions('LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX',$userObjectDb->individualGroupAssignmentOptions),$role->name) == true) { 569 | $userInstanceAfterLogin = $userObjectDb; 570 | break; 571 | } 572 | } 573 | } 574 | 575 | //Login possible if no role is assigned 576 | if ($userObjectDb->status == static::STATUS_ENABLED && 577 | static::getGroupAssigmentOptions('LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX',$userObjectDb->individualGroupAssignmentOptions) == null) 578 | { 579 | $userInstanceAfterLogin = $userObjectDb; 580 | } 581 | } 582 | 583 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 584 | Yii::endProfile('checkAllowedToLogin', static::YII2_PROFILE_NAME . 'checkAllowedToLogin'); 585 | } 586 | return $userInstanceAfterLogin; 587 | } 588 | 589 | /** 590 | * @inheritdoc 591 | */ 592 | public function getId() 593 | { 594 | return $this->getPrimaryKey(); 595 | } 596 | 597 | /** 598 | * @inheritdoc 599 | */ 600 | public function getAuthKey() 601 | { 602 | //Fallback if for some reason no auth key exists 603 | if ($this->auth_key == null) { 604 | $this->generateAuthKey(); 605 | $this->save(); 606 | } 607 | return $this->auth_key; 608 | } 609 | /** 610 | * @inheritdoc 611 | */ 612 | public function validateAuthKey($authKey) 613 | { 614 | return $this->getAuthKey() === $authKey; 615 | } 616 | 617 | /** 618 | * Validates password 619 | * 620 | * @param string $password password to validate 621 | * @return bool if password provided is valid for current user 622 | */ 623 | public function validatePassword($password) 624 | { 625 | //is yii2 profiling enabled? 626 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 627 | Yii::beginProfile('LDAP validatePassword function', static::YII2_PROFILE_NAME . 'validatePassword'); 628 | } 629 | 630 | $passwordValid = static::getAdldapProvider()->auth()->attempt($this->username,$password); 631 | 632 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 633 | Yii::endProfile('LDAP validatePassword function', static::YII2_PROFILE_NAME . 'validatePassword'); 634 | } 635 | return $passwordValid; 636 | } 637 | 638 | /** 639 | * Generates "remember me" authentication key 640 | */ 641 | public function generateAuthKey() 642 | { 643 | $this->auth_key = Yii::$app->security->generateRandomString(); 644 | } 645 | 646 | /** 647 | * Create a new object in database. 648 | * 649 | * @param string $username username of the LDAP user. 650 | * @return Edvlerblog\Adldap2\model\UserDbLdap object. Null if the username is not found in LDAP. 651 | */ 652 | public static function createNewUser($username,$individualGroupAssignmentOptions = null) { 653 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 654 | Yii::beginProfile('createNewUser', static::YII2_PROFILE_NAME . 'createNewUser'); 655 | } 656 | 657 | $userObjectDb = new static(); 658 | 659 | //Username has to be set before a LDAP query 660 | $userObjectDb->username = $username; 661 | $userObjectDb->setIndividualGroupAssignmentOptions($individualGroupAssignmentOptions); 662 | 663 | //Check if user exists in LDAP. 664 | if($userObjectDb->queryLdapUserObject() == null) { 665 | $userObjectDb = null; 666 | } else { 667 | $roles = $userObjectDb->updateGroupAssignment(); 668 | 669 | //When a group is needed for login and no roles are assigned to user, don't create one 670 | if (count($roles) > 0 || static::getGroupAssigmentOptions('LOGIN_POSSIBLE_WITH_ROLE_ASSIGNED_MATCHING_REGEX',$userObjectDb->individualGroupAssignmentOptions) == null) { 671 | $userObjectDb->generateAuthKey(); 672 | $userObjectDb->updateAccountStatus(); 673 | $userObjectDb->save(); 674 | } else { 675 | $userObjectDb = null; 676 | } 677 | } 678 | 679 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 680 | Yii::endProfile('createNewUser', static::YII2_PROFILE_NAME . 'createNewUser'); 681 | } 682 | return $userObjectDb; 683 | } 684 | 685 | /** 686 | * Check if a user exists. If the user exists the account status and group assigments are refreshed. 687 | * Otherwise a new user is created. 688 | * 689 | * @param string $username 690 | * @param array $individualGroupAssignmentOptions 691 | * @return Edvlerblog\Adldap2\model\UserDbLdap object. Null if the username is not found in LDAP. 692 | */ 693 | public static function createOrRefreshUser($username,$individualGroupAssignmentOptions = null) { 694 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 695 | Yii::beginProfile('createOrRefreshUser', static::YII2_PROFILE_NAME . 'createOrRefreshUser'); 696 | } 697 | 698 | $userObjectDb = static::findOne(['username' => $username]); 699 | 700 | //Create user if not found in db 701 | if ($userObjectDb == null) { 702 | $userObjectDb = static::createNewUser($username, $individualGroupAssignmentOptions); 703 | } else { 704 | $userObjectDb->setIndividualGroupAssignmentOptions($individualGroupAssignmentOptions); 705 | $userObjectDb->updateAccountStatus(); 706 | $userObjectDb->updateGroupAssignment(); 707 | } 708 | 709 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 710 | Yii::endProfile('createOrRefreshUser', static::YII2_PROFILE_NAME . 'createOrRefreshUser'); 711 | } 712 | return $userObjectDb; 713 | } 714 | 715 | /** 716 | * Query LDAP for the current user status and save the information to database. 717 | * 718 | * @return int Status after update 719 | */ 720 | public function updateAccountStatus() { 721 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 722 | Yii::beginProfile('LDAP updateAccountStatus function', static::YII2_PROFILE_NAME . 'updateAccountStatus'); 723 | } 724 | 725 | $ldapUser = $this->queryLdapUserObject(); 726 | 727 | if ($ldapUser == null) { 728 | //If no user is found in LDAP, disable in database. 729 | $this->status = static::STATUS_DISABLED; 730 | } else { 731 | //Query account status from active directory 732 | $ldapAccountState = $ldapUser->getUserAccountControl(); 733 | 734 | $disabledUser = ($ldapAccountState & AccountControl::ACCOUNTDISABLE) === AccountControl::ACCOUNTDISABLE; 735 | $lockedUser = ($ldapAccountState & AccountControl::LOCKOUT) === AccountControl::LOCKOUT; 736 | $pwExpired = ($ldapAccountState & AccountControl::PASSWORD_EXPIRED) === AccountControl::PASSWORD_EXPIRED; 737 | 738 | if($disabledUser == true || $lockedUser == true || $pwExpired == true) { 739 | $this->status = static::STATUS_DISABLED; 740 | } else { 741 | $this->status = static::STATUS_ENABLED; 742 | } 743 | } 744 | 745 | $this->save(); 746 | 747 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 748 | Yii::endProfile('LDAP updateAccountStatus function', static::YII2_PROFILE_NAME . 'updateAccountStatus'); 749 | } 750 | 751 | return $this->status; 752 | } 753 | 754 | 755 | /** 756 | * Update the group assignment of the user object 757 | * The Yii::$app->params['LDAP-Group-Assignment-Options'] has several options how to update the group assignment. 758 | * 759 | * Basicly a query to LDAP is done which returns the groups assigned to the user in the LDAP directory. 760 | * Depending on the settings in the params groups are added or removed from the user object. 761 | * 762 | * For a description of the options see the top of this class, where templates (e.g. GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX) are defined. 763 | * This Templates can be used directly in params or you can define each param by yourself. 764 | * 765 | * Example config/params.php 766 | * return [ 767 | * ... 768 | * 'LDAP-Group-Assignment-Options' => Edvlerblog\Adldap2\model\UserDbLdap::GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX, 769 | * ... 770 | * ]; 771 | * 772 | * @return Role[] all roles directly assigned to the user. The array is indexed by the role names. 773 | */ 774 | public function updateGroupAssignment() { 775 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 776 | Yii::beginProfile('updateGroupAssignment', static::YII2_PROFILE_NAME . 'updateGroupAssignment'); 777 | } 778 | 779 | $ldapGroupsAssignedToUser = $this->getGroupsAssignedInLdap(); //Query LDAP groups assigned to user 780 | $yiiRolesAssignedToUser = Yii::$app->authManager->getRolesByUser($this->getId()); //Get all roles assigned to user 781 | $yiiAvailableRoles = Yii::$app->authManager->getRoles(); //Get all avaliable roles in yii2 782 | 783 | //Map groups from LDAP to roles and add to user object. 784 | if (static::getGroupAssigmentOptions('ADD_GROUPS_FROM_LDAP_MATCHING_REGEX',$this->individualGroupAssignmentOptions) == true) { 785 | foreach ($ldapGroupsAssignedToUser as $gn) { 786 | if(preg_match(static::getGroupAssigmentOptions('REGEX_GROUP_MATCH_IN_LDAP',$this->individualGroupAssignmentOptions),$gn) == true) { 787 | if(array_key_exists($gn,$yiiAvailableRoles) && !array_key_exists($gn,$yiiRolesAssignedToUser)) { 788 | if ($this->isNewRecord) { 789 | $this->generateAuthKey(); 790 | $this->updateAccountStatus(); 791 | $this->save(); //Save to db to get id from database 792 | } 793 | $auth = Yii::$app->authManager; 794 | $role = $auth->getRole($gn); 795 | $auth->assign($role, $this->getId()); 796 | } 797 | } 798 | } 799 | } 800 | 801 | //Remove all roles from user object which are not in LDAP 802 | if (static::getGroupAssigmentOptions('REMOVE_ALL_GROUPS_NOT_FOUND_IN_LDAP',$this->individualGroupAssignmentOptions) == true && 803 | static::getGroupAssigmentOptions('REMOVE_ONLY_GROUPS_MATCHING_REGEX',$this->individualGroupAssignmentOptions) == false) { 804 | foreach ($yiiRolesAssignedToUser as $role) { 805 | if(in_array($role->name,$ldapGroupsAssignedToUser) == false) { 806 | $auth = Yii::$app->authManager; 807 | $auth->revoke($role, $this->getId()); 808 | } 809 | } 810 | } 811 | 812 | //Remove all roles from user object which are matching the regex and are not in LDAP 813 | if (static::getGroupAssigmentOptions('REMOVE_ONLY_GROUPS_MATCHING_REGEX',$this->individualGroupAssignmentOptions) == true) { 814 | foreach ($yiiRolesAssignedToUser as $role) { 815 | $roleName = $role->name; 816 | 817 | if(preg_match(static::getGroupAssigmentOptions('REGEX_GROUP_MATCH_IN_LDAP',$this->individualGroupAssignmentOptions),$roleName) == true && 818 | in_array($roleName,$ldapGroupsAssignedToUser) == false) { 819 | $auth = Yii::$app->authManager; 820 | $auth->revoke($role, $this->getId()); 821 | } 822 | } 823 | } 824 | 825 | $rolesAfterUpdate = Yii::$app->authManager->getRolesByUser($this->getId()); 826 | 827 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 828 | Yii::endProfile('updateGroupAssignment', static::YII2_PROFILE_NAME . 'updateGroupAssignment'); 829 | } 830 | 831 | //Return assigned roles. 832 | return $rolesAfterUpdate; 833 | } 834 | 835 | /** 836 | * Query all groups assigned to user from Active Directory. 837 | * If the parameter SEARCH_NESTED_GROUPS = true then all nested groups are 838 | * respected too. 839 | * Keep in mind, that a query for nested groups is much slower as a normal query. 840 | * 841 | * @return array with names of groups assigned to user. Empty if no groups found. 842 | */ 843 | public function getGroupsAssignedInLdap() { 844 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 845 | Yii::beginProfile('getGroupsAssignedInLdap', static::YII2_PROFILE_NAME . 'getGroupsAssignedInLdap'); 846 | } 847 | 848 | $ldapUser = $this->queryLdapUserObject(); 849 | 850 | $ldapGroupsConverted = []; //start with empty array of groups 851 | 852 | if ($ldapUser != null) { 853 | //check for nested groups? 854 | if (static::getGroupAssigmentOptions('SEARCH_NESTED_GROUPS',$this->individualGroupAssignmentOptions) == true) { 855 | //$ldapGroups=$ldapUser->getGroups(['cn'], $recursive=true); //alternate Query, but slower 856 | //1.2.840.113556.1.4.1941 = Specical OID to resolve chains 857 | $ldapGroups = static::getAdldapProvider()->search()->rawFilter('(member:1.2.840.113556.1.4.1941:=' . $ldapUser->getDn() . ')')->select('cn')->raw()->get(); 858 | if ($ldapGroups == null) { 859 | $ldapGroups = []; 860 | } 861 | 862 | //get cn of each group 863 | foreach ($ldapGroups as $groupDn) { 864 | if (is_array($groupDn) && array_key_exists('cn', $groupDn)) { 865 | array_push($ldapGroupsConverted, $groupDn['cn'][0]); 866 | } 867 | } 868 | } else { 869 | //get attribute memberof 870 | $ldapGroups = $ldapUser->getAttribute('memberof'); 871 | if ($ldapGroups == null) { 872 | $ldapGroups = []; 873 | } 874 | 875 | //get first part of dn 876 | foreach ($ldapGroups as $groupDn) { 877 | $n = Utilities::explodeDn($groupDn)[0]; 878 | array_push($ldapGroupsConverted, $n); 879 | } 880 | } 881 | } 882 | 883 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 884 | Yii::endProfile('getGroupsAssignedInLdap', static::YII2_PROFILE_NAME . 'getGroupsAssignedInLdap'); 885 | } 886 | return $ldapGroupsConverted; 887 | } 888 | 889 | /** 890 | * Querys the complete user object from LDAP. 891 | * The username of the object has to be set before a query! 892 | * 893 | * @return \Adldap\models\User 894 | * @throws \yii\base\Exception if the username is not set and thus no LDAP query based on username can be done. 895 | */ 896 | public function queryLdapUserObject() { 897 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 898 | Yii::beginProfile('queryLdapUserObject', static::YII2_PROFILE_NAME . 'queryLdapUserObject'); 899 | } 900 | 901 | if ($this->ldapUserObject == null) { 902 | if ($this->username == null) { 903 | throw new \yii\base\Exception('Please set username attribute before calling queryLdapUserObject() function.'); 904 | } 905 | 906 | $userObjectsFound = static::getAdldapProvider()->search()->where('sAMAccountname', '=', $this->username)->get(); 907 | 908 | if(count($userObjectsFound) != 1) { 909 | $this->ldapUserObject = null; 910 | } else { 911 | $this->ldapUserObject = $userObjectsFound[0]; 912 | } 913 | } 914 | 915 | if(static::getExtensionOptions('ENABLE_YII2_PROFILING') == true) { 916 | Yii::endProfile('queryLdapUserObject', static::YII2_PROFILE_NAME . 'queryLdapUserObject'); 917 | } 918 | 919 | return $this->ldapUserObject; 920 | } 921 | 922 | /** 923 | * Get the Adldap2 provider name 924 | * Since Version 3.0.5 use the static function getAdldapProvider() 925 | * 926 | * @deprecated since version 3.0.5 927 | */ 928 | private function getAdldap2Provider() { 929 | if(isset(Yii::$app->params['yii2-adldap-providername'])) { 930 | $provider =Yii::$app->ad->getProvider(Yii::$app->params['yii2-adldap-providername']); 931 | } else { 932 | $provider =Yii::$app->ad->getDefaultProvider(); 933 | } 934 | 935 | return $provider; 936 | } 937 | 938 | /** 939 | * Get the Adldap2 provider name from the extension options. 940 | * See constant variable EXTENSION_OPTIONS_DEFAULT at the top of the class 941 | */ 942 | public static function getAdldapProvider() { 943 | if(static::getExtensionOptions('adldap-providername') != '__USE_DEFAULT_PROVIDER__') { 944 | $provider = Yii::$app->ad->getProvider(static::getExtensionOptions('adldap-providername')); 945 | } else { 946 | $provider = Yii::$app->ad->getDefaultProvider(); 947 | } 948 | 949 | return $provider; 950 | } 951 | 952 | /** 953 | * Set a individual LDAP synchronisation configuration object for this object. 954 | * 955 | * @param type $individualSyncOptions See SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK in the code for possible options. 956 | */ 957 | public function setIndividualSyncOptions($individualSyncOptions) { 958 | $this->individualSyncOptions = $individualSyncOptions; 959 | } 960 | 961 | /** 962 | * Get a value of the synchronisation options by option key. 963 | * The options can be defined with the setIndividualSyncOptions 964 | * function of this class for a single object, or global in the yii2 params.php. 965 | * If nothing is defined the defaults from the varibale SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK 966 | * are used. 967 | * 968 | * @param string $optionName The option key of the value to retrive. 969 | * @param string $individualSyncOptions Array with individual sync options for this function call only 970 | * @return mixed The value of the option key 971 | * @throws \yii\base\Exception if option key is not found in the given option set. 972 | */ 973 | public static function getSyncOptions($optionName, $individualSyncOptions = null) { 974 | //try object specific settings 975 | if ($individualSyncOptions != null && 976 | is_array($individualSyncOptions) && 977 | array_key_exists($optionName, $individualSyncOptions)) 978 | { 979 | return $individualSyncOptions[$optionName]; 980 | } 981 | 982 | //try yii2 params 983 | else if (isset(Yii::$app->params['LDAP-User-Sync-Options']) && 984 | is_array(Yii::$app->params['LDAP-User-Sync-Options']) && 985 | array_key_exists($optionName, Yii::$app->params['LDAP-User-Sync-Options'])) 986 | { 987 | return Yii::$app->params['LDAP-User-Sync-Options'][$optionName]; 988 | } 989 | 990 | //default from distribution 991 | else if (array_key_exists($optionName, static::SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK)) { 992 | return static::SYNC_OPTIONS_TEMPLATE_WITHOUT_BACKEND_TASK[$optionName]; 993 | } 994 | 995 | //Exception 996 | else { 997 | throw new \yii\base\Exception('Sync-option ' . $optionName . ' not found. Please define settings in the config/params.php of the yii2 framework as described on top of the UserDbLdap.php'); 998 | } 999 | } 1000 | 1001 | /** 1002 | * Set a individual LDAP group assignment configuration object for this object. 1003 | * 1004 | * @param type $individualGroupAssignmentOptions See GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX in the code for possible options. 1005 | */ 1006 | public function setIndividualGroupAssignmentOptions($individualGroupAssignmentOptions) { 1007 | $this->individualGroupAssignmentOptions = $individualGroupAssignmentOptions; 1008 | } 1009 | 1010 | /** 1011 | * Get a value of the group assignment options by option key. 1012 | * The options can be defined with the setIndividualGroupAssignmentOptions 1013 | * function of this class for a single object, or global in the yii2 params.php. 1014 | * If nothing is defined the defaults from the varibale GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX 1015 | * are used. 1016 | * 1017 | * See function updateGroupAssignment for further explanation. 1018 | * 1019 | * @param string $optionName The option key of the value to retrive. 1020 | * @param string $individualSyncOptions Array with individual group assignment options for this function call only 1021 | * @return mixed The value of the option key 1022 | * @throws \yii\base\Exception if option key is not found in the given option set. 1023 | */ 1024 | public static function getGroupAssigmentOptions($optionName, $individualGroupAssignmentOptions = null) { 1025 | //try object specific settings 1026 | if ($individualGroupAssignmentOptions != null && 1027 | is_array($individualGroupAssignmentOptions) && 1028 | array_key_exists($optionName, $individualGroupAssignmentOptions)) 1029 | { 1030 | return $individualGroupAssignmentOptions[$optionName]; 1031 | } 1032 | 1033 | //try yii2 params 1034 | else if (isset(Yii::$app->params['LDAP-Group-Assignment-Options']) && 1035 | is_array(Yii::$app->params['LDAP-Group-Assignment-Options']) && 1036 | array_key_exists($optionName, Yii::$app->params['LDAP-Group-Assignment-Options'])) 1037 | { 1038 | return Yii::$app->params['LDAP-Group-Assignment-Options'][$optionName]; 1039 | } 1040 | 1041 | //default from distribution 1042 | else if (array_key_exists($optionName, static::GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX)) { 1043 | return static::GROUP_ASSIGNMENT_TOUCH_ONLY_MATCHING_REGEX[$optionName]; 1044 | } 1045 | 1046 | //Exception 1047 | else { 1048 | throw new \yii\base\Exception('Group-Option ' . $optionName . ' not found. Please define settings in the config/params.php of the yii2 framework as described on top of the UserDbLdap.php'); 1049 | } 1050 | } 1051 | 1052 | /** 1053 | * Get a value of the group assignment options by option key. 1054 | * The options can be defined global in the yii2 params.php. 1055 | * If nothing is defined the defaults from the varibale EXTENSION_OPTIONS_DEFAULT 1056 | * are used. 1057 | * 1058 | * @param string $optionName The option key of the value to retrive. 1059 | * @return mixed The value of the option key 1060 | * @throws \yii\base\Exception if option key is not found in the given option set. 1061 | */ 1062 | public static function getExtensionOptions($optionName) { 1063 | //try yii2 params 1064 | if (isset(Yii::$app->params['yii2-adldap-extension-Options']) && 1065 | is_array(Yii::$app->params['yii2-adldap-extension-Options']) && 1066 | array_key_exists($optionName, Yii::$app->params['yii2-adldap-extension-Options'])) 1067 | { 1068 | return Yii::$app->params['yii2-adldap-extension-Options'][$optionName]; 1069 | } 1070 | 1071 | //default from distribution 1072 | else if (array_key_exists($optionName, static::EXTENSION_OPTIONS_DEFAULT)) { 1073 | return static::EXTENSION_OPTIONS_DEFAULT[$optionName]; 1074 | } 1075 | 1076 | //Exception 1077 | else { 1078 | throw new \yii\base\Exception('Extension-Option ' . $optionName . ' not found. Please define settings in the config/params.php of the yii2 framework as described on top of the UserDbLdap.php'); 1079 | } 1080 | } 1081 | 1082 | /** 1083 | * Get the password expirytime 1084 | * See: https://msdn.microsoft.com/en-us/library/cc223410.aspx 1085 | */ 1086 | public function getPasswordExpiryDate() { 1087 | if ($this->username == null) { 1088 | throw new \yii\base\Exception('Please set username attribute before calling getPasswordExpiryDate() function.'); 1089 | } 1090 | 1091 | $result = static::getAdldapProvider()-> 1092 | search()-> 1093 | select(['msDS-UserPasswordExpiryTimeComputed'])-> 1094 | where('samaccountname', '=', $this->username)-> 1095 | get(); 1096 | return $result[0]->getFirstAttribute('msds-userpasswordexpirytimecomputed'); 1097 | } 1098 | } --------------------------------------------------------------------------------