Merge pull request #13978 from uberbrady/test_ldap

Test ldap
This commit is contained in:
snipe 2023-11-30 19:23:29 +00:00 committed by GitHub
commit 5730518fc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 456 additions and 1 deletions

View file

@ -74,12 +74,16 @@
"unicodeveloper/laravel-password": "^1.0", "unicodeveloper/laravel-password": "^1.0",
"watson/validating": "^6.1" "watson/validating": "^6.1"
}, },
"suggest": {
"ext-ldap": "*"
},
"require-dev": { "require-dev": {
"brianium/paratest": "^6.6", "brianium/paratest": "^6.6",
"fakerphp/faker": "^1.16", "fakerphp/faker": "^1.16",
"mockery/mockery": "^1.4", "mockery/mockery": "^1.4",
"nunomaduro/larastan": "^1.0", "nunomaduro/larastan": "^1.0",
"nunomaduro/phpinsights": "^2.7", "nunomaduro/phpinsights": "^2.7",
"php-mock/php-mock-phpunit": "^2.8",
"phpunit/php-token-stream": "^3.1", "phpunit/php-token-stream": "^3.1",
"phpunit/phpunit": "^9.0", "phpunit/phpunit": "^9.0",
"squizlabs/php_codesniffer": "^3.5", "squizlabs/php_codesniffer": "^3.5",

209
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "348f96db24a0f8dfb595ee38b38b34eb", "content-hash": "f4f3b6b02d044ed3e54cdd509b01c3dc",
"packages": [ "packages": [
{ {
"name": "alek13/slack", "name": "alek13/slack",
@ -14101,6 +14101,213 @@
}, },
"time": "2022-02-21T01:04:05+00:00" "time": "2022-02-21T01:04:05+00:00"
}, },
{
"name": "php-mock/php-mock",
"version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/php-mock/php-mock.git",
"reference": "6240b6f0a76d7b9d1ee4d70e686a7cc711619a9d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-mock/php-mock/zipball/6240b6f0a76d7b9d1ee4d70e686a7cc711619a9d",
"reference": "6240b6f0a76d7b9d1ee4d70e686a7cc711619a9d",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0 || ^8.0",
"phpunit/php-text-template": "^1 || ^2 || ^3"
},
"replace": {
"malkusch/php-mock": "*"
},
"require-dev": {
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.0 || ^9.0 || ^10.0",
"squizlabs/php_codesniffer": "^3.5"
},
"suggest": {
"php-mock/php-mock-phpunit": "Allows integration into PHPUnit testcase with the trait PHPMock."
},
"type": "library",
"autoload": {
"files": [
"autoload.php"
],
"psr-4": {
"phpmock\\": [
"classes/",
"tests/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"WTFPL"
],
"authors": [
{
"name": "Markus Malkusch",
"email": "markus@malkusch.de",
"homepage": "http://markus.malkusch.de",
"role": "Developer"
}
],
"description": "PHP-Mock can mock built-in PHP functions (e.g. time()). PHP-Mock relies on PHP's namespace fallback policy. No further extension is needed.",
"homepage": "https://github.com/php-mock/php-mock",
"keywords": [
"BDD",
"TDD",
"function",
"mock",
"stub",
"test",
"test double",
"testing"
],
"support": {
"issues": "https://github.com/php-mock/php-mock/issues",
"source": "https://github.com/php-mock/php-mock/tree/2.4.1"
},
"funding": [
{
"url": "https://github.com/michalbundyra",
"type": "github"
}
],
"time": "2023-06-12T20:48:52+00:00"
},
{
"name": "php-mock/php-mock-integration",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/php-mock/php-mock-integration.git",
"reference": "04f4a8d5442ca457b102b5204673f77323e3edb5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-mock/php-mock-integration/zipball/04f4a8d5442ca457b102b5204673f77323e3edb5",
"reference": "04f4a8d5442ca457b102b5204673f77323e3edb5",
"shasum": ""
},
"require": {
"php": ">=5.6",
"php-mock/php-mock": "^2.4",
"phpunit/php-text-template": "^1 || ^2 || ^3"
},
"require-dev": {
"phpunit/phpunit": "^5.7.27 || ^6 || ^7 || ^8 || ^9 || ^10"
},
"type": "library",
"autoload": {
"psr-4": {
"phpmock\\integration\\": "classes/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"WTFPL"
],
"authors": [
{
"name": "Markus Malkusch",
"email": "markus@malkusch.de",
"homepage": "http://markus.malkusch.de",
"role": "Developer"
}
],
"description": "Integration package for PHP-Mock",
"homepage": "https://github.com/php-mock/php-mock-integration",
"keywords": [
"BDD",
"TDD",
"function",
"mock",
"stub",
"test",
"test double"
],
"support": {
"issues": "https://github.com/php-mock/php-mock-integration/issues",
"source": "https://github.com/php-mock/php-mock-integration/tree/2.2.1"
},
"funding": [
{
"url": "https://github.com/michalbundyra",
"type": "github"
}
],
"time": "2023-02-13T09:51:29+00:00"
},
{
"name": "php-mock/php-mock-phpunit",
"version": "2.8.0",
"source": {
"type": "git",
"url": "https://github.com/php-mock/php-mock-phpunit.git",
"reference": "56edee85ad3232caa0202f98f2a3c899ab16bdb7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-mock/php-mock-phpunit/zipball/56edee85ad3232caa0202f98f2a3c899ab16bdb7",
"reference": "56edee85ad3232caa0202f98f2a3c899ab16bdb7",
"shasum": ""
},
"require": {
"php": ">=7",
"php-mock/php-mock-integration": "^2.2.1",
"phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17"
},
"require-dev": {
"mockery/mockery": "^1.3.6"
},
"type": "library",
"autoload": {
"files": [
"autoload.php"
],
"psr-4": {
"phpmock\\phpunit\\": "classes/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"WTFPL"
],
"authors": [
{
"name": "Markus Malkusch",
"email": "markus@malkusch.de",
"homepage": "http://markus.malkusch.de",
"role": "Developer"
}
],
"description": "Mock built-in PHP functions (e.g. time()) with PHPUnit. This package relies on PHP's namespace fallback policy. No further extension is needed.",
"homepage": "https://github.com/php-mock/php-mock-phpunit",
"keywords": [
"BDD",
"TDD",
"function",
"mock",
"phpunit",
"stub",
"test",
"test double",
"testing"
],
"support": {
"issues": "https://github.com/php-mock/php-mock-phpunit/issues",
"source": "https://github.com/php-mock/php-mock-phpunit/tree/2.8.0"
},
"funding": [
{
"url": "https://github.com/michalbundyra",
"type": "github"
}
],
"time": "2023-10-30T07:06:12+00:00"
},
{ {
"name": "php-parallel-lint/php-parallel-lint", "name": "php-parallel-lint/php-parallel-lint",
"version": "v1.3.2", "version": "v1.3.2",

View file

@ -3,6 +3,7 @@
namespace Tests\Support; namespace Tests\Support;
use App\Models\Setting; use App\Models\Setting;
use Illuminate\Support\Facades\Crypt;
class Settings class Settings
{ {
@ -67,6 +68,39 @@ class Settings
} }
public function enableLdap(): Settings
{
return $this->update([
'ldap_enabled' => 1,
'ldap_server' => 'ldaps://ldap.example.com',
'ldap_uname' => 'fake_username',
'ldap_pword' => Crypt::encrypt("fake_password"),
'ldap_basedn' => 'CN=Users,DC=ad,DC=example,Dc=com'
]);
}
public function enableAnonymousLdap(): Settings
{
return $this->update([
'ldap_enabled' => 1,
'ldap_server' => 'ldaps://ldap.example.com',
// 'ldap_uname' => 'fake_username',
'ldap_pword' => Crypt::encrypt("fake_password"),
'ldap_basedn' => 'CN=Users,DC=ad,DC=example,Dc=com'
]);
}
public function enableBadPasswordLdap(): Settings
{
return $this->update([
'ldap_enabled' => 1,
'ldap_server' => 'ldaps://ldap.example.com',
'ldap_uname' => 'fake_username',
'ldap_pword' => "badly_encrypted_password!",
'ldap_basedn' => 'CN=Users,DC=ad,DC=example,Dc=com'
]);
}
/** /**
* @param array $attributes Attributes to modify in the application's settings. * @param array $attributes Attributes to modify in the application's settings.
*/ */

210
tests/Unit/LdapTest.php Normal file
View file

@ -0,0 +1,210 @@
<?php
namespace Tests\Unit;
use App\Models\Ldap;
use Exception;
use Tests\Support\InteractsWithSettings;
use Tests\TestCase;
class LdapTest extends TestCase
{
use InteractsWithSettings;
use \phpmock\phpunit\PHPMock;
public function testConnect()
{
$this->settings->enableLdap();
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
$ldap_connect->expects($this->once())->willReturn('hello');
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
$ldap_set_option->expects($this->exactly(3));
$blah = Ldap::connectToLdap();
$this->assertEquals('hello',$blah,"LDAP_connect should return 'hello'");
}
// other test cases - with/without client-side certs?
// with/without LDAP version 3?
// with/without ignore cert validation?
// test (and mock) ldap_start_tls() ?
public function testBindAdmin()
{
$this->settings->enableLdap();
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
}
public function testBindBad()
{
$this->settings->enableLdap();
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(false);
$this->getFunctionMock("App\\Models","ldap_error")->expects($this->once())->willReturn("exception");
$this->expectExceptionMessage("Could not bind to LDAP:");
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
}
// other test cases - test donked password?
public function testAnonymousBind()
{
//todo - would be nice to introspect somehow to make sure the right parameters were passed?
$this->settings->enableAnonymousLdap();
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
}
public function testBadAnonymousBind()
{
$this->settings->enableAnonymousLdap();
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(false);
$this->getFunctionMock("App\\Models","ldap_error")->expects($this->once())->willReturn("exception");
$this->expectExceptionMessage("Could not bind to LDAP:");
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
}
public function testBadEncryptedPassword()
{
$this->settings->enableBadPasswordLdap();
$this->expectExceptionMessage("Your app key has changed");
$this->assertNull(Ldap::bindAdminToLdap("dummy"));
}
public function testFindAndBind()
{
$this->settings->enableLdap();
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
$ldap_connect->expects($this->once())->willReturn('hello');
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
$ldap_set_option->expects($this->exactly(3));
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
$this->getFunctionMock("App\\Models", "ldap_search")->expects($this->once())->willReturn(true);
$this->getFunctionMock("App\\Models", "ldap_first_entry")->expects($this->once())->willReturn(true);
$this->getFunctionMock("App\\Models", "ldap_get_attributes")->expects($this->once())->willReturn(
[
"count" => 1,
0 => [
'sn' => 'Surname',
'firstName' => 'FirstName'
]
]
);
$results = Ldap::findAndBindUserLdap("username","password");
$this->assertEqualsCanonicalizing(["count" =>1,0 =>['sn' => 'Surname','firstname' => 'FirstName']],$results);
}
public function testFindAndBindBadPassword()
{
$this->settings->enableLdap();
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
$ldap_connect->expects($this->once())->willReturn('hello');
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
$ldap_set_option->expects($this->exactly(3));
// note - we return FALSE first, to simulate a bad-bind, then TRUE the second time to simulate a successful admin bind
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->exactly(2))->willReturn(false, true);
// $this->getFunctionMock("App\\Models","ldap_error")->expects($this->once())->willReturn("exception");
// $this->expectExceptionMessage("exception");
$results = Ldap::findAndBindUserLdap("username","password");
$this->assertFalse($results);
}
public function testFindAndBindCannotFindSelf()
{
$this->settings->enableLdap();
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
$ldap_connect->expects($this->once())->willReturn('hello');
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
$ldap_set_option->expects($this->exactly(3));
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
$this->getFunctionMock("App\\Models", "ldap_search")->expects($this->once())->willReturn(false);
$this->expectExceptionMessage("Could not search LDAP:");
$results = Ldap::findAndBindUserLdap("username","password");
$this->assertFalse($results);
}
//maybe should do an AD test as well?
public function testFindLdapUsers()
{
$this->settings->enableLdap();
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
$ldap_connect->expects($this->once())->willReturn('hello');
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
$ldap_set_option->expects($this->exactly(3));
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
$this->getFunctionMock("App\\Models", "ldap_search")->expects($this->once())->willReturn(["stuff"]);
$this->getFunctionMock("App\\Models", "ldap_parse_result")->expects($this->once())->willReturn(true);
$this->getFunctionMock("App\\Models", "ldap_get_entries")->expects($this->once())->willReturn(["count" => 1]);
$results = Ldap::findLdapUsers();
$this->assertEqualsCanonicalizing(["count" => 1], $results);
}
public function testFindLdapUsersPaginated()
{
$this->settings->enableLdap();
$ldap_connect = $this->getFunctionMock("App\\Models", "ldap_connect");
$ldap_connect->expects($this->once())->willReturn('hello');
$ldap_set_option = $this->getFunctionMock("App\\Models", "ldap_set_option");
$ldap_set_option->expects($this->exactly(3));
$this->getFunctionMock("App\\Models", "ldap_bind")->expects($this->once())->willReturn(true);
$this->getFunctionMock("App\\Models", "ldap_search")->expects($this->exactly(2))->willReturn(["stuff"]);
$this->getFunctionMock("App\\Models", "ldap_parse_result")->expects($this->exactly(2))->willReturnCallback(
function ($ldapconn, $search_results, $errcode , $matcheddn , $errmsg , $referrals, &$controls) {
static $count = 0;
if($count == 0) {
$count++;
$controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] = "cookie";
return ["count" => 1];
} else {
$controls = [];
return ["count" => 1];
}
}
);
$this->getFunctionMock("App\\Models", "ldap_get_entries")->expects($this->exactly(2))->willReturn(["count" => 1]);
$results = Ldap::findLdapUsers();
$this->assertEqualsCanonicalizing(["count" => 2], $results);
}
}