ADconsole v1.0.0
First public release. - docs: document the audit log, user offboarding, CSV export and the "require a reason for destructive actions" setting across README, docs/ and CLAUDE/AGENTS.md; note retention for panel_audit - docs: fill the publishing placeholders (repo URL, security contact, issue-template links) and add a semantic-versioning policy - move agent/contributor context into CLAUDE/ (AGENTS.md + a README); root CLAUDE.md is now a thin @-import pointer - add a favicon (shield-lock, matching the login screen) to the login page and the app layout - CHANGELOG: promote [Unreleased] to [1.0.0]; keep the earlier internal build as [0.1.0] Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UfuvfwexztdS7LDpGuKsuK
This commit is contained in:
commit
1126d96271
174 files changed
+27113
No files matched your search
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\OuTreeService;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\TestCase;
|
||||
|
||||
class OuTreeServiceTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Drzewo jest cache'owane w pliku - czyścimy przed każdym testem,
|
||||
// żeby mock LDAP faktycznie był odpytywany.
|
||||
OuTreeService::flush();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_limits_tree_to_default_user_and_computer_ous(): void
|
||||
{
|
||||
config([
|
||||
'ldap.defaults.user_ou' => 'OU=oddzial,DC=example,DC=com',
|
||||
'ldap.defaults.computer_ou' => 'OU=Komputery,DC=example,DC=com',
|
||||
]);
|
||||
|
||||
$userOu = new class('OU=oddzial,DC=example,DC=com', 'oddzial')
|
||||
{
|
||||
public function __construct(public string $dn, public string $name) {}
|
||||
|
||||
public function getDn(): string
|
||||
{
|
||||
return $this->dn;
|
||||
}
|
||||
|
||||
public function getFirstAttribute(string $attribute): ?string
|
||||
{
|
||||
return $attribute === 'ou' ? $this->name : null;
|
||||
}
|
||||
};
|
||||
|
||||
$nestedUserOu = new class('OU=Users,OU=oddzial,DC=example,DC=com', 'Users')
|
||||
{
|
||||
public function __construct(public string $dn, public string $name) {}
|
||||
|
||||
public function getDn(): string
|
||||
{
|
||||
return $this->dn;
|
||||
}
|
||||
|
||||
public function getFirstAttribute(string $attribute): ?string
|
||||
{
|
||||
return $attribute === 'ou' ? $this->name : null;
|
||||
}
|
||||
};
|
||||
|
||||
$computerOu = new class('OU=Komputery,DC=example,DC=com', 'Komputery')
|
||||
{
|
||||
public function __construct(public string $dn, public string $name) {}
|
||||
|
||||
public function getDn(): string
|
||||
{
|
||||
return $this->dn;
|
||||
}
|
||||
|
||||
public function getFirstAttribute(string $attribute): ?string
|
||||
{
|
||||
return $attribute === 'ou' ? $this->name : null;
|
||||
}
|
||||
};
|
||||
|
||||
$computerChild = new class('OU=Workstations,OU=Komputery,DC=example,DC=com', 'Workstations')
|
||||
{
|
||||
public function __construct(public string $dn, public string $name) {}
|
||||
|
||||
public function getDn(): string
|
||||
{
|
||||
return $this->dn;
|
||||
}
|
||||
|
||||
public function getFirstAttribute(string $attribute): ?string
|
||||
{
|
||||
return $attribute === 'ou' ? $this->name : null;
|
||||
}
|
||||
};
|
||||
|
||||
$externalOu = new class('OU=External,DC=example,DC=com', 'External')
|
||||
{
|
||||
public function __construct(public string $dn, public string $name) {}
|
||||
|
||||
public function getDn(): string
|
||||
{
|
||||
return $this->dn;
|
||||
}
|
||||
|
||||
public function getFirstAttribute(string $attribute): ?string
|
||||
{
|
||||
return $attribute === 'ou' ? $this->name : null;
|
||||
}
|
||||
};
|
||||
|
||||
$query = Mockery::mock();
|
||||
$query->shouldReceive('select')->with(['ou', 'distinguishedname'])->andReturnSelf();
|
||||
$query->shouldReceive('get')->twice()->andReturn([
|
||||
$userOu,
|
||||
$nestedUserOu,
|
||||
$computerOu,
|
||||
$computerChild,
|
||||
$externalOu,
|
||||
]);
|
||||
|
||||
Mockery::mock('alias:\LdapRecord\Models\ActiveDirectory\OrganizationalUnit')
|
||||
->shouldReceive('query')->twice()->andReturn($query);
|
||||
|
||||
$service = new OuTreeService;
|
||||
|
||||
$userTree = $service->tree('users');
|
||||
$computerTree = $service->tree('computers');
|
||||
|
||||
$this->assertCount(1, $userTree);
|
||||
$this->assertSame('oddzial', $userTree[0]['name']);
|
||||
$this->assertCount(1, $userTree[0]['children']);
|
||||
$this->assertSame('Users', $userTree[0]['children'][0]['name']);
|
||||
|
||||
$this->assertCount(1, $computerTree);
|
||||
$this->assertSame('Komputery', $computerTree[0]['name']);
|
||||
$this->assertCount(1, $computerTree[0]['children']);
|
||||
$this->assertSame('Workstations', $computerTree[0]['children'][0]['name']);
|
||||
|
||||
Mockery::close();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_counts_actual_users_inside_each_ou_subtree(): void
|
||||
{
|
||||
config([
|
||||
'ldap.defaults.user_ou' => 'OU=Users,DC=example,DC=com',
|
||||
'ldap.defaults.computer_ou' => 'OU=Komputery,DC=example,DC=com',
|
||||
'ldap.defaults.groups_ou' => 'OU=Grupy,DC=example,DC=com',
|
||||
]);
|
||||
|
||||
$rootOu = new class('OU=Users,DC=example,DC=com', 'Users')
|
||||
{
|
||||
public function __construct(public string $dn, public string $name) {}
|
||||
|
||||
public function getDn(): string
|
||||
{
|
||||
return $this->dn;
|
||||
}
|
||||
|
||||
public function getFirstAttribute(string $attribute): ?string
|
||||
{
|
||||
return $attribute === 'ou' ? $this->name : null;
|
||||
}
|
||||
};
|
||||
|
||||
$childOu = new class('OU=Developers,OU=Users,DC=example,DC=com', 'Developers')
|
||||
{
|
||||
public function __construct(public string $dn, public string $name) {}
|
||||
|
||||
public function getDn(): string
|
||||
{
|
||||
return $this->dn;
|
||||
}
|
||||
|
||||
public function getFirstAttribute(string $attribute): ?string
|
||||
{
|
||||
return $attribute === 'ou' ? $this->name : null;
|
||||
}
|
||||
};
|
||||
|
||||
$ouQuery = Mockery::mock();
|
||||
$ouQuery->shouldReceive('select')->with(['ou', 'distinguishedname'])->andReturnSelf();
|
||||
$ouQuery->shouldReceive('get')->once()->andReturn([$rootOu, $childOu]);
|
||||
|
||||
Mockery::mock('alias:\LdapRecord\Models\ActiveDirectory\OrganizationalUnit')
|
||||
->shouldReceive('query')->once()->andReturn($ouQuery);
|
||||
|
||||
// countObjectsInOu() zwraca liczbę obiektów w CAŁYM poddrzewie danego OU
|
||||
// (zapytanie rekursywne). Liczba przy OU nadrzędnym NIE może być sumą
|
||||
// z dziećmi - inaczej te same obiekty są liczone wielokrotnie
|
||||
// (błąd: root pokazywał 313 zamiast 121).
|
||||
$service = Mockery::mock(OuTreeService::class)->makePartial()->shouldAllowMockingProtectedMethods();
|
||||
$service->shouldReceive('countObjectsInOu')->with('OU=Users,DC=example,DC=com', 'users')->once()->andReturn(5);
|
||||
$service->shouldReceive('countObjectsInOu')->with('OU=Developers,OU=Users,DC=example,DC=com', 'users')->once()->andReturn(2);
|
||||
|
||||
$tree = $service->tree('users');
|
||||
|
||||
$this->assertCount(1, $tree);
|
||||
$this->assertSame(5, $tree[0]['count'], 'Licznik OU nadrzędnego = liczba w całym poddrzewie, bez podwójnego sumowania.');
|
||||
$this->assertSame(2, $tree[0]['children'][0]['count']);
|
||||
|
||||
Mockery::close();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user