From e48de2ef638ae4d0d7d64e6671cb4c0080b658e7 Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 27 Jun 2026 12:59:00 -0400 Subject: [PATCH 1/2] Refactor to use AuthzId in all relevant spots. --- .../Control/ProxyAuthorizationControl.php | 52 ++++++++++++++---- src/FreeDSx/Ldap/Controls.php | 5 +- .../Ldap/Protocol/Authorization/AuthzId.php | 5 ++ .../ProxiedAuthorizationResolver.php | 8 ++- .../ServerWhoAmIHandler.php | 10 +--- .../Backend/Auth/PasswordAuthenticator.php | 5 +- .../Token/AuthenticatedTokenInterface.php | 6 +++ src/FreeDSx/Ldap/Server/Token/BindToken.php | 51 +++++++++++++----- .../LdapProxiedAuthorizationControlTest.php | 7 +-- .../Control/ProxyAuthorizationControlTest.php | 54 +++++++++++++++---- .../Authorization/AuthzIdResolverTest.php | 3 +- .../DispatchAuthorizationTest.php | 4 +- .../Authorization/DispatchAuthorizerTest.php | 9 ++-- .../ProxiedAuthorizationResolverTest.php | 37 +++++++------ tests/unit/Protocol/Bind/SimpleBindTest.php | 3 +- .../ServerWhoAmIHandlerTest.php | 6 +-- tests/unit/Server/Logging/EventLoggerTest.php | 6 +-- .../Server/Logging/OperationAuditorTest.php | 3 +- .../OperationAuditMiddlewareTest.php | 7 +-- .../SearchLimit/SearchLimitResolverTest.php | 4 +- 20 files changed, 185 insertions(+), 100 deletions(-) diff --git a/src/FreeDSx/Ldap/Control/ProxyAuthorizationControl.php b/src/FreeDSx/Ldap/Control/ProxyAuthorizationControl.php index 2481eaf7..427bb3a1 100644 --- a/src/FreeDSx/Ldap/Control/ProxyAuthorizationControl.php +++ b/src/FreeDSx/Ldap/Control/ProxyAuthorizationControl.php @@ -15,7 +15,9 @@ use FreeDSx\Asn1\Type\AbstractType; use FreeDSx\Asn1\Type\SequenceType; +use FreeDSx\Ldap\Exception\InvalidArgumentException; use FreeDSx\Ldap\Exception\ProtocolException; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; /** * Represents a proxied authorization control. RFC 4370. @@ -26,10 +28,16 @@ */ final class ProxyAuthorizationControl extends Control { + private string $rawAuthzId; + + private ?AuthzId $authzId; + public function __construct( - private string $authzId = '', + AuthzId $authzId, bool $criticality = true, ) { + $this->setAuthzId($authzId); + parent::__construct( self::OID_PROXY_AUTHORIZATION, $criticality, @@ -37,23 +45,34 @@ public function __construct( } /** - * The authorization identity to assume: an authzId ("dn:..." / "u:..."), or empty for anonymous. + * The authorization identity to assume; lazily parsed from the raw value when decoded from a request. + * + * @throws InvalidArgumentException when the raw wire value is not a valid authzId form (e.g. a malformed request) */ - public function getAuthzId(): string + public function getAuthzId(): AuthzId { - return $this->authzId; + return $this->authzId ??= AuthzId::fromString($this->rawAuthzId); } - public function setAuthzId(string $authzId): self + /** + * The raw authzId wire value, which may be an invalid form when decoded from a request. + */ + public function getRawAuthzId(): string + { + return $this->rawAuthzId; + } + + public function setAuthzId(AuthzId $authzId): self { $this->authzId = $authzId; + $this->rawAuthzId = $authzId->toString(); return $this; } public function toAsn1(): SequenceType { - $this->controlValue = $this->authzId; + $this->controlValue = $this->rawAuthzId; return parent::toAsn1(); } @@ -73,10 +92,23 @@ public static function fromAsn1(AbstractType $type): static } [1 => $criticality, 2 => $authzId] = self::parseAsn1ControlValues($type); + $rawAuthzId = $authzId ?? ''; - return new static( - $authzId ?? '', - $criticality, - ); + try { + return new static( + AuthzId::fromString($rawAuthzId), + $criticality, + ); + } catch (InvalidArgumentException) { + // Preserve a malformed value so it surfaces as an authorization denial rather than a decode failure. + $control = new static( + AuthzId::anonymous(), + $criticality, + ); + $control->rawAuthzId = $rawAuthzId; + $control->authzId = null; + + return $control; + } } } diff --git a/src/FreeDSx/Ldap/Controls.php b/src/FreeDSx/Ldap/Controls.php index f4c13fd5..446d871a 100644 --- a/src/FreeDSx/Ldap/Controls.php +++ b/src/FreeDSx/Ldap/Controls.php @@ -31,6 +31,7 @@ use FreeDSx\Ldap\Control\SubentriesControl; use FreeDSx\Ldap\Control\Sync\SyncRequestControl; use FreeDSx\Ldap\Control\Vlv\VlvControl; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; use FreeDSx\Ldap\Protocol\ProtocolElementInterface; use FreeDSx\Ldap\Search\Filter\FilterInterface; use FreeDSx\Ldap\Search\Filter\GreaterThanOrEqualFilter; @@ -184,9 +185,9 @@ public static function relaxRules(bool $criticality = true): Control } /** - * Create a Proxied Authorization control to run the operation as the given authzId ("dn:..." / "u:...", or empty for anonymous). + * Create a Proxied Authorization control to run the operation as the given authzId (use AuthzId::anonymous() for anonymous). */ - public static function proxyAuthorization(string $authzId = ''): ProxyAuthorizationControl + public static function proxyAuthorization(AuthzId $authzId): ProxyAuthorizationControl { return new ProxyAuthorizationControl($authzId); } diff --git a/src/FreeDSx/Ldap/Protocol/Authorization/AuthzId.php b/src/FreeDSx/Ldap/Protocol/Authorization/AuthzId.php index 034d17b2..34a9f1dc 100644 --- a/src/FreeDSx/Ldap/Protocol/Authorization/AuthzId.php +++ b/src/FreeDSx/Ldap/Protocol/Authorization/AuthzId.php @@ -62,6 +62,11 @@ public static function fromString(string $authzId): self }; } + public static function anonymous(): self + { + return new self(AuthzIdType::Anonymous); + } + public static function fromDn(Dn $dn): self { return new self( diff --git a/src/FreeDSx/Ldap/Protocol/Authorization/ProxiedAuthorizationResolver.php b/src/FreeDSx/Ldap/Protocol/Authorization/ProxiedAuthorizationResolver.php index d5d8dca0..3c3492ff 100644 --- a/src/FreeDSx/Ldap/Protocol/Authorization/ProxiedAuthorizationResolver.php +++ b/src/FreeDSx/Ldap/Protocol/Authorization/ProxiedAuthorizationResolver.php @@ -89,21 +89,19 @@ public function resolve( ); } - $rawAuthzId = $control->getAuthzId(); - if (!$token instanceof AuthenticatedTokenInterface) { $this->authzIdResolver->deny( $token, - $rawAuthzId, + $control->getRawAuthzId(), ); } try { - $authzId = AuthzId::fromString($rawAuthzId); + $authzId = $control->getAuthzId(); } catch (InvalidArgumentException) { $this->authzIdResolver->deny( $token, - $rawAuthzId, + $control->getRawAuthzId(), ); } diff --git a/src/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerWhoAmIHandler.php b/src/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerWhoAmIHandler.php index 63d55046..864023c3 100644 --- a/src/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerWhoAmIHandler.php +++ b/src/FreeDSx/Ldap/Protocol/ServerProtocolHandler/ServerWhoAmIHandler.php @@ -13,7 +13,6 @@ namespace FreeDSx\Ldap\Protocol\ServerProtocolHandler; -use Exception; use FreeDSx\Asn1\Exception\EncoderException; use FreeDSx\Ldap\Operation\LdapResult; use FreeDSx\Ldap\Operation\Response\ExtendedResponse; @@ -46,14 +45,7 @@ public function handleRequest( $userId = null; if ($token instanceof AuthenticatedTokenInterface) { - $resolvedDn = $token->getResolvedDn(); - - try { - $resolvedDn->toArray(); - $userId = 'dn:' . $resolvedDn->toString(); - } catch (Exception) { - $userId = 'u:' . $token->getUsername(); - } + $userId = $token->getAuthzId()->toString(); } $this->queue->sendMessage(new LdapMessageResponse( diff --git a/src/FreeDSx/Ldap/Server/Backend/Auth/PasswordAuthenticator.php b/src/FreeDSx/Ldap/Server/Backend/Auth/PasswordAuthenticator.php index ba94674e..df926fb2 100644 --- a/src/FreeDSx/Ldap/Server/Backend/Auth/PasswordAuthenticator.php +++ b/src/FreeDSx/Ldap/Server/Backend/Auth/PasswordAuthenticator.php @@ -15,6 +15,7 @@ use FreeDSx\Ldap\Exception\OperationException; use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; use FreeDSx\Ldap\Server\Backend\Auth\NameResolver\BindNameResolverInterface; use FreeDSx\Ldap\Server\Backend\LdapBackendInterface; use FreeDSx\Ldap\Server\Token\AuthenticatedTokenInterface; @@ -58,9 +59,9 @@ public function authenticate( foreach ($attr->getValues() as $stored) { if ($this->hashService->verify($password, $stored)) { return new BindToken( - $name, + AuthzId::fromDn($entry->getDn()), $password, - $entry->getDn(), + $name, ); } } diff --git a/src/FreeDSx/Ldap/Server/Token/AuthenticatedTokenInterface.php b/src/FreeDSx/Ldap/Server/Token/AuthenticatedTokenInterface.php index 5b2ed90c..1a8468fd 100644 --- a/src/FreeDSx/Ldap/Server/Token/AuthenticatedTokenInterface.php +++ b/src/FreeDSx/Ldap/Server/Token/AuthenticatedTokenInterface.php @@ -14,6 +14,7 @@ namespace FreeDSx\Ldap\Server\Token; use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; /** * Implemented by tokens that represent a successfully authenticated identity with a resolved DN. @@ -24,6 +25,11 @@ interface AuthenticatedTokenInterface extends TokenInterface { public function getResolvedDn(): Dn; + /** + * The bound identity as an authorization identity: the resolved DN, or the username when it is not a DN. + */ + public function getAuthzId(): AuthzId; + /** * Whether the bound identity must change its password before any other operation is permitted (pwdReset). */ diff --git a/src/FreeDSx/Ldap/Server/Token/BindToken.php b/src/FreeDSx/Ldap/Server/Token/BindToken.php index 60313caf..f9e4e1f4 100644 --- a/src/FreeDSx/Ldap/Server/Token/BindToken.php +++ b/src/FreeDSx/Ldap/Server/Token/BindToken.php @@ -14,11 +14,12 @@ namespace FreeDSx\Ldap\Server\Token; use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; use FreeDSx\Ldap\Server\Utility\Uuid; use SensitiveParameter; /** - * Represents a username/password token that is bound and authorized. + * Represents a bound and authorized identity, identified by its authorization identity (authzId). * * @api * @@ -28,10 +29,10 @@ class BindToken implements AuthenticatedTokenInterface { private string $id; - private string $username; - private readonly Dn $resolvedDn; + private string $authcId; + private string $password; private int $version; @@ -40,17 +41,21 @@ class BindToken implements AuthenticatedTokenInterface private readonly ?Dn $authorizingDn; + /** + * @param AuthzId $authzId the resolved authorization identity (its DN, or the username when it is not a DN) + * @param string|null $authcId the authentication identity as presented, for auditing; defaults to the authzId value + */ public function __construct( - string $username, + private readonly AuthzId $authzId, #[SensitiveParameter] - string $password, - Dn $resolvedDn, + string $password = '', + ?string $authcId = null, int $version = 3, ?Dn $authorizingDn = null, ) { $this->id = Uuid::v4(); - $this->username = $username; - $this->resolvedDn = $resolvedDn; + $this->resolvedDn = new Dn($authzId->getValue()); + $this->authcId = $authcId ?? $authzId->getValue(); $this->password = $password; $this->version = $version; $this->authorizingDn = $authorizingDn; @@ -64,9 +69,9 @@ public static function fromDn( ?Dn $authorizingDn = null, ): self { return new self( - $dn, + AuthzId::fromDn(new Dn($dn)), $password, - new Dn($dn), + $dn, $version, $authorizingDn, ); @@ -82,9 +87,26 @@ public static function fromSasl( ?Dn $authorizingDn = null, ): self { return new self( + AuthzId::fromDn($resolvedDn), + '', $username, + $version, + $authorizingDn, + ); + } + + /** + * Creates a token for an identity that resolved to a bare username rather than a DN. + */ + public static function fromUsername( + string $username, + int $version = 3, + ?Dn $authorizingDn = null, + ): self { + return new self( + AuthzId::fromUsername($username), '', - $resolvedDn, + $username, $version, $authorizingDn, ); @@ -95,9 +117,14 @@ public function getId(): string return $this->id; } + public function getAuthzId(): AuthzId + { + return $this->authzId; + } + public function getUsername(): ?string { - return $this->username; + return $this->authcId; } public function getPassword(): ?string diff --git a/tests/integration/Security/LdapProxiedAuthorizationControlTest.php b/tests/integration/Security/LdapProxiedAuthorizationControlTest.php index 264f6255..020ff05b 100644 --- a/tests/integration/Security/LdapProxiedAuthorizationControlTest.php +++ b/tests/integration/Security/LdapProxiedAuthorizationControlTest.php @@ -15,6 +15,7 @@ use FreeDSx\Ldap\Controls; use FreeDSx\Ldap\Exception\OperationException; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; use FreeDSx\Ldap\Operation\Response\ExtendedResponse; use FreeDSx\Ldap\Operation\ResultCode; use FreeDSx\Ldap\Operations; @@ -62,7 +63,7 @@ public function test_whoami_reports_the_proxied_identity(): void $response = $this->ldapClient()->send( Operations::whoami(), - Controls::proxyAuthorization('dn:' . self::PROXIED_DN), + Controls::proxyAuthorization(AuthzId::fromString('dn:' . self::PROXIED_DN)), ); self::assertNotNull($response); @@ -95,7 +96,7 @@ public function test_a_proxied_search_runs_under_the_proxied_identity(): void Operations::search(Filters::equal('cn', 'alice')) ->base('dc=foo,dc=bar') ->useSubtreeScope(), - Controls::proxyAuthorization('dn:' . self::PROXIED_DN), + Controls::proxyAuthorization(AuthzId::fromString('dn:' . self::PROXIED_DN)), ); self::assertCount(1, $entries); @@ -116,7 +117,7 @@ public function test_proxy_is_denied_for_a_target_outside_the_permitted_subtree( Operations::search(Filters::present('objectClass')) ->base('dc=foo,dc=bar') ->useSubtreeScope(), - Controls::proxyAuthorization('dn:cn=user,dc=foo,dc=bar'), + Controls::proxyAuthorization(AuthzId::fromString('dn:cn=user,dc=foo,dc=bar')), ); } } diff --git a/tests/unit/Control/ProxyAuthorizationControlTest.php b/tests/unit/Control/ProxyAuthorizationControlTest.php index f7d574e8..1bfffe11 100644 --- a/tests/unit/Control/ProxyAuthorizationControlTest.php +++ b/tests/unit/Control/ProxyAuthorizationControlTest.php @@ -7,23 +7,33 @@ use FreeDSx\Asn1\Asn1; use FreeDSx\Ldap\Control\Control; use FreeDSx\Ldap\Control\ProxyAuthorizationControl; +use FreeDSx\Ldap\Entry\Dn; +use FreeDSx\Ldap\Exception\InvalidArgumentException; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; +use FreeDSx\Ldap\Protocol\Authorization\AuthzIdType; use PHPUnit\Framework\TestCase; final class ProxyAuthorizationControlTest extends TestCase { public function test_it_defaults_to_an_empty_anonymous_authz_id(): void { - self::assertSame( - '', - (new ProxyAuthorizationControl())->getAuthzId(), - ); + $control = new ProxyAuthorizationControl(AuthzId::anonymous()); + + self::assertSame('', $control->getRawAuthzId()); + self::assertTrue($control->getAuthzId()->isType(AuthzIdType::Anonymous)); } public function test_it_exposes_the_authz_id(): void { + $control = new ProxyAuthorizationControl(AuthzId::fromDn(new Dn('cn=foo,dc=example,dc=com'))); + + self::assertSame( + 'dn:cn=foo,dc=example,dc=com', + $control->getRawAuthzId(), + ); self::assertSame( 'dn:cn=foo,dc=example,dc=com', - (new ProxyAuthorizationControl('dn:cn=foo,dc=example,dc=com'))->getAuthzId(), + $control->getAuthzId()->toString(), ); } @@ -31,13 +41,13 @@ public function test_it_has_the_proxy_authorization_oid(): void { self::assertSame( Control::OID_PROXY_AUTHORIZATION, - (new ProxyAuthorizationControl())->getTypeOid(), + (new ProxyAuthorizationControl(AuthzId::anonymous()))->getTypeOid(), ); } public function test_it_is_critical_by_default(): void { - self::assertTrue((new ProxyAuthorizationControl('u:bob'))->getCriticality()); + self::assertTrue((new ProxyAuthorizationControl(AuthzId::fromUsername('bob')))->getCriticality()); } public function test_it_preserves_a_non_critical_flag_from_asn1(): void @@ -59,7 +69,7 @@ public function test_it_generates_asn1_with_the_raw_authz_id_value(): void Asn1::boolean(true), Asn1::octetString('dn:cn=foo,dc=example,dc=com'), ), - (new ProxyAuthorizationControl('dn:cn=foo,dc=example,dc=com'))->toAsn1(), + (new ProxyAuthorizationControl(AuthzId::fromDn(new Dn('cn=foo,dc=example,dc=com'))))->toAsn1(), ); } @@ -71,7 +81,7 @@ public function test_it_generates_asn1_with_an_empty_value_for_anonymous(): void Asn1::boolean(true), Asn1::octetString(''), ), - (new ProxyAuthorizationControl())->toAsn1(), + (new ProxyAuthorizationControl(AuthzId::anonymous()))->toAsn1(), ); } @@ -85,7 +95,11 @@ public function test_it_is_constructed_from_asn1_reading_the_raw_value(): void self::assertSame( 'u:bob', - $control->getAuthzId(), + $control->getRawAuthzId(), + ); + self::assertSame( + 'u:bob', + $control->getAuthzId()->toString(), ); self::assertTrue($control->getCriticality()); self::assertSame( @@ -104,7 +118,25 @@ public function test_it_is_constructed_from_asn1_with_an_empty_value(): void self::assertSame( '', - $control->getAuthzId(), + $control->getRawAuthzId(), ); } + + public function test_it_defers_a_malformed_authz_id_until_it_is_read(): void + { + $control = ProxyAuthorizationControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_PROXY_AUTHORIZATION), + Asn1::boolean(true), + Asn1::octetString('mail:bob@example.com'), + )); + + self::assertSame( + 'mail:bob@example.com', + $control->getRawAuthzId(), + ); + + $this->expectException(InvalidArgumentException::class); + + $control->getAuthzId(); + } } diff --git a/tests/unit/Protocol/Authorization/AuthzIdResolverTest.php b/tests/unit/Protocol/Authorization/AuthzIdResolverTest.php index 6ce60b71..b28b3732 100644 --- a/tests/unit/Protocol/Authorization/AuthzIdResolverTest.php +++ b/tests/unit/Protocol/Authorization/AuthzIdResolverTest.php @@ -61,10 +61,9 @@ protected function setUp(): void EventLogPolicy::all(), ), ); - $this->boundToken = new BindToken( + $this->boundToken = BindToken::fromDn( self::ADMIN_DN, 'secret', - new Dn(self::ADMIN_DN), ); } diff --git a/tests/unit/Protocol/Authorization/DispatchAuthorizationTest.php b/tests/unit/Protocol/Authorization/DispatchAuthorizationTest.php index 531fa3c4..667b79c9 100644 --- a/tests/unit/Protocol/Authorization/DispatchAuthorizationTest.php +++ b/tests/unit/Protocol/Authorization/DispatchAuthorizationTest.php @@ -13,7 +13,6 @@ namespace Tests\Unit\FreeDSx\Ldap\Protocol\Authorization; -use FreeDSx\Ldap\Entry\Dn; use FreeDSx\Ldap\Exception\RuntimeException; use FreeDSx\Ldap\Protocol\Authorization\DispatchAuthorization; use FreeDSx\Ldap\Server\Token\BindToken; @@ -23,10 +22,9 @@ final class DispatchAuthorizationTest extends TestCase { public function test_proceed_carries_the_effective_token(): void { - $token = new BindToken( + $token = BindToken::fromDn( 'cn=alice,dc=example,dc=com', 'secret', - new Dn('cn=alice,dc=example,dc=com'), ); $authorization = DispatchAuthorization::proceed($token); diff --git a/tests/unit/Protocol/Authorization/DispatchAuthorizerTest.php b/tests/unit/Protocol/Authorization/DispatchAuthorizerTest.php index 76b9ae71..3b54151c 100644 --- a/tests/unit/Protocol/Authorization/DispatchAuthorizerTest.php +++ b/tests/unit/Protocol/Authorization/DispatchAuthorizerTest.php @@ -15,12 +15,12 @@ use FreeDSx\Ldap\Control\Control; use FreeDSx\Ldap\Control\ProxyAuthorizationControl; -use FreeDSx\Ldap\Entry\Dn; use FreeDSx\Ldap\Entry\Entry; use FreeDSx\Ldap\Exception\OperationException; use FreeDSx\Ldap\Operation\Request\DeleteRequest; use FreeDSx\Ldap\Operation\Request\ExtendedRequest; use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; use FreeDSx\Ldap\Protocol\Authorization\DispatchAuthorizer; use FreeDSx\Ldap\Protocol\Authorization\AuthzIdResolver; use FreeDSx\Ldap\Protocol\Authorization\ProxiedAuthorizationResolver; @@ -156,7 +156,7 @@ public function test_it_proceeds_under_the_proxied_identity_when_a_proxy_control ->willReturn(new Entry(self::PROXIED_DN)); $authorization = $this->subject->authorize($this->message( - new ProxyAuthorizationControl('dn:' . self::PROXIED_DN), + new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN)), )); $effective = $authorization->token(); @@ -192,16 +192,15 @@ public function test_it_propagates_a_proxy_authorization_denial(): void $this->expectExceptionCode(ResultCode::AUTHORIZATION_DENIED); $this->subject->authorize($this->message( - new ProxyAuthorizationControl('dn:' . self::PROXIED_DN), + new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN)), )); } private function boundToken(): BindToken { - return new BindToken( + return BindToken::fromDn( self::BOUND_DN, 'secret', - new Dn(self::BOUND_DN), ); } diff --git a/tests/unit/Protocol/Authorization/ProxiedAuthorizationResolverTest.php b/tests/unit/Protocol/Authorization/ProxiedAuthorizationResolverTest.php index b0381484..1411d452 100644 --- a/tests/unit/Protocol/Authorization/ProxiedAuthorizationResolverTest.php +++ b/tests/unit/Protocol/Authorization/ProxiedAuthorizationResolverTest.php @@ -13,10 +13,10 @@ namespace Tests\Unit\FreeDSx\Ldap\Protocol\Authorization; +use FreeDSx\Asn1\Asn1; use FreeDSx\Ldap\Control\Control; use FreeDSx\Ldap\Control\ControlBag; use FreeDSx\Ldap\Control\ProxyAuthorizationControl; -use FreeDSx\Ldap\Entry\Dn; use FreeDSx\Ldap\Entry\Entry; use FreeDSx\Ldap\Exception\InvalidArgumentException; use FreeDSx\Ldap\Exception\OperationException; @@ -24,6 +24,7 @@ use FreeDSx\Ldap\Operation\Request\ExtendedRequest; use FreeDSx\Ldap\Operation\Request\RequestInterface; use FreeDSx\Ldap\Operation\ResultCode; +use FreeDSx\Ldap\Protocol\Authorization\AuthzId; use FreeDSx\Ldap\Protocol\Authorization\AuthzIdResolver; use FreeDSx\Ldap\Protocol\Authorization\ProxiedAuthorizationResolver; use FreeDSx\Ldap\Server\AccessControl\AccessControlInterface; @@ -75,10 +76,9 @@ protected function setUp(): void ), ), ); - $this->boundToken = new BindToken( + $this->boundToken = BindToken::fromDn( self::ADMIN_DN, 'secret', - new Dn(self::ADMIN_DN), ); } @@ -105,7 +105,7 @@ public function test_it_resolves_a_dn_authz_id_to_the_proxied_identity(): void $result = $this->subject->resolve( $this->eligibleRequest(), - new ControlBag(new ProxyAuthorizationControl('dn:' . self::PROXIED_DN)), + new ControlBag(new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN))), $this->boundToken, ); @@ -132,7 +132,7 @@ public function test_it_resolves_a_userid_authz_id_via_the_identity_resolver(): $result = $this->subject->resolve( $this->eligibleRequest(), - new ControlBag(new ProxyAuthorizationControl('u:alice')), + new ControlBag(new ProxyAuthorizationControl(AuthzId::fromString('u:alice'))), $this->boundToken, ); @@ -152,7 +152,7 @@ public function test_it_returns_an_anonymous_identity_for_an_empty_authz_id(): v $result = $this->subject->resolve( $this->eligibleRequest(), - new ControlBag(new ProxyAuthorizationControl('')), + new ControlBag(new ProxyAuthorizationControl(AuthzId::anonymous())), $this->boundToken, ); @@ -179,7 +179,7 @@ public function test_it_denies_without_resolving_when_the_subject_has_no_proxy_c ->method('resolve'); $this->assertDeniesWith( - new ProxyAuthorizationControl('dn:' . self::PROXIED_DN), + new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN)), 'dn:' . self::PROXIED_DN, ); } @@ -198,7 +198,7 @@ public function test_it_denies_when_the_bound_identity_lacks_proxy_permission_fo )); $this->assertDeniesWith( - new ProxyAuthorizationControl('dn:' . self::PROXIED_DN), + new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN)), 'dn:' . self::PROXIED_DN, ); } @@ -211,7 +211,7 @@ public function test_it_denies_when_the_authz_id_cannot_be_resolved(): void ->willReturn(null); $this->assertDeniesWith( - new ProxyAuthorizationControl('dn:' . self::PROXIED_DN), + new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN)), 'dn:' . self::PROXIED_DN, ); } @@ -224,7 +224,7 @@ public function test_it_denies_a_malformed_dn_authz_id_without_disconnecting(): ->willThrowException(new InvalidArgumentException('The DN value is not valid.')); $this->assertDeniesWith( - new ProxyAuthorizationControl('dn:not a dn'), + new ProxyAuthorizationControl(AuthzId::fromString('dn:not a dn')), 'dn:not a dn', ); } @@ -233,8 +233,13 @@ public function test_it_denies_an_unknown_authz_id_form(): void { $this->grantProxyCapability(); + // A malformed authzId form can only originate from the wire, so build it via fromAsn1. $this->assertDeniesWith( - new ProxyAuthorizationControl('mail:alice@example.com'), + ProxyAuthorizationControl::fromAsn1(Asn1::sequence( + Asn1::octetString(Control::OID_PROXY_AUTHORIZATION), + Asn1::boolean(true), + Asn1::octetString('mail:alice@example.com'), + )), 'mail:alice@example.com', ); } @@ -244,7 +249,7 @@ public function test_it_denies_when_the_bound_identity_is_not_authenticated(): v try { $this->subject->resolve( $this->eligibleRequest(), - new ControlBag(new ProxyAuthorizationControl('dn:' . self::PROXIED_DN)), + new ControlBag(new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN))), new AnonToken(), ); self::fail('Expected an OperationException.'); @@ -264,7 +269,7 @@ public function test_it_rejects_the_control_on_an_ineligible_operation(string $e try { $this->subject->resolve( new ExtendedRequest($extendedOid), - new ControlBag(new ProxyAuthorizationControl('dn:' . self::PROXIED_DN)), + new ControlBag(new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN))), $this->boundToken, ); self::fail('Expected an OperationException.'); @@ -298,7 +303,7 @@ public function test_it_rejects_a_non_critical_proxy_control_as_a_protocol_error $this->subject->resolve( $this->eligibleRequest(), new ControlBag(new ProxyAuthorizationControl( - 'dn:' . self::PROXIED_DN, + AuthzId::fromString('dn:' . self::PROXIED_DN), false, )), $this->boundToken, @@ -323,8 +328,8 @@ public function test_it_rejects_more_than_one_proxy_control_as_a_protocol_error( $this->subject->resolve( $this->eligibleRequest(), new ControlBag( - new ProxyAuthorizationControl('dn:' . self::PROXIED_DN), - new ProxyAuthorizationControl('u:alice'), + new ProxyAuthorizationControl(AuthzId::fromString('dn:' . self::PROXIED_DN)), + new ProxyAuthorizationControl(AuthzId::fromString('u:alice')), ), $this->boundToken, ); diff --git a/tests/unit/Protocol/Bind/SimpleBindTest.php b/tests/unit/Protocol/Bind/SimpleBindTest.php index a393f837..367a6905 100644 --- a/tests/unit/Protocol/Bind/SimpleBindTest.php +++ b/tests/unit/Protocol/Bind/SimpleBindTest.php @@ -50,9 +50,8 @@ protected function setUp(): void public function test_it_should_return_a_token_on_success(): void { - $expectedToken = new BindToken( + $expectedToken = BindToken::fromSasl( 'foo@bar', - 'bar', new Dn('cn=foo,dc=bar'), ); diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php index ca64b431..16c3ff77 100644 --- a/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php @@ -81,9 +81,8 @@ public function test_it_should_use_resolved_dn_when_it_differs_from_username(): $this->subject->handleRequest( $request, - new BindToken( + BindToken::fromSasl( 'uid=alice', - '12345', new Dn('cn=Alice,dc=example,dc=com'), ), ); @@ -103,9 +102,8 @@ public function test_it_should_handle_a_who_am_i_when_there_is_a_token_with_a_no $this->subject->handleRequest( $request, - BindToken::fromDn( + BindToken::fromUsername( 'foo@bar.local', - '12345', ), ); } diff --git a/tests/unit/Server/Logging/EventLoggerTest.php b/tests/unit/Server/Logging/EventLoggerTest.php index d9ff4847..00a10b1e 100644 --- a/tests/unit/Server/Logging/EventLoggerTest.php +++ b/tests/unit/Server/Logging/EventLoggerTest.php @@ -93,10 +93,9 @@ public function test_record_emits_enabled_event_with_level_template_and_merged_c public function test_record_includes_authorized_by_for_a_proxied_subject(): void { - $token = new BindToken( + $token = BindToken::fromDn( 'cn=alice,dc=example,dc=com', '', - new Dn('cn=alice,dc=example,dc=com'), 3, new Dn('cn=admin,dc=example,dc=com'), ); @@ -131,10 +130,9 @@ public function test_record_includes_authorized_by_for_a_proxied_subject(): void public function test_record_omits_authorized_by_for_a_non_proxied_subject(): void { - $token = new BindToken( + $token = BindToken::fromDn( 'cn=alice,dc=example,dc=com', 'secret', - new Dn('cn=alice,dc=example,dc=com'), ); $this->mockLogger diff --git a/tests/unit/Server/Logging/OperationAuditorTest.php b/tests/unit/Server/Logging/OperationAuditorTest.php index 87c1de60..788b6261 100644 --- a/tests/unit/Server/Logging/OperationAuditorTest.php +++ b/tests/unit/Server/Logging/OperationAuditorTest.php @@ -63,10 +63,9 @@ protected function setUp(): void $this->recordingLogger, EventLogPolicy::all(), )); - $this->token = new BindToken( + $this->token = BindToken::fromDn( self::ACTOR_DN, 'secret', - new Dn(self::ACTOR_DN), ); } diff --git a/tests/unit/Server/Middleware/OperationAuditMiddlewareTest.php b/tests/unit/Server/Middleware/OperationAuditMiddlewareTest.php index c187db2c..93ecb1ce 100644 --- a/tests/unit/Server/Middleware/OperationAuditMiddlewareTest.php +++ b/tests/unit/Server/Middleware/OperationAuditMiddlewareTest.php @@ -13,7 +13,6 @@ namespace Tests\Unit\FreeDSx\Ldap\Server\Middleware; -use FreeDSx\Ldap\Entry\Dn; use FreeDSx\Ldap\Entry\Entry; use FreeDSx\Ldap\Exception\OperationException; use FreeDSx\Ldap\Exception\SchemaRuleException; @@ -55,10 +54,9 @@ protected function setUp(): void ))); $this->context = new ServerRequestContext( new LdapMessageRequest(1, new AddRequest(Entry::create('cn=foo,dc=bar'))), - new BindToken( + BindToken::fromDn( 'cn=alice,dc=bar', 'secret', - new Dn('cn=alice,dc=bar'), ), ); } @@ -206,10 +204,9 @@ private function contextFor(RequestInterface $request): ServerRequestContext { return new ServerRequestContext( new LdapMessageRequest(1, $request), - new BindToken( + BindToken::fromDn( 'cn=alice,dc=bar', 'secret', - new Dn('cn=alice,dc=bar'), ), ); } diff --git a/tests/unit/Server/SearchLimit/SearchLimitResolverTest.php b/tests/unit/Server/SearchLimit/SearchLimitResolverTest.php index d96ca2f8..37de1568 100644 --- a/tests/unit/Server/SearchLimit/SearchLimitResolverTest.php +++ b/tests/unit/Server/SearchLimit/SearchLimitResolverTest.php @@ -13,7 +13,6 @@ namespace Tests\Unit\FreeDSx\Ldap\Server\SearchLimit; -use FreeDSx\Ldap\Entry\Dn; use FreeDSx\Ldap\Server\AccessControl\Subject\Subject; use FreeDSx\Ldap\Server\SearchLimit\SearchLimitResolver; use FreeDSx\Ldap\Server\SearchLimit\SearchLimitRule; @@ -96,10 +95,9 @@ public function test_first_matching_rule_wins(): void private function authenticatedToken(): BindToken { - return new BindToken( + return BindToken::fromDn( 'cn=user,dc=foo,dc=bar', '12345', - new Dn('cn=user,dc=foo,dc=bar'), ); } } From 960af6a056b607e4bd18baf8b76a932067a643a2 Mon Sep 17 00:00:00 2001 From: Chad Sikorra Date: Sat, 27 Jun 2026 13:15:03 -0400 Subject: [PATCH 2/2] Remove the password from the token. It was previously used in the old server proxy setup. It is no longer necessary and is not good to store in memory. --- .../Backend/Auth/PasswordAuthenticator.php | 1 - .../Ldap/Server/Proxy/ProxyAuthenticator.php | 5 +---- src/FreeDSx/Ldap/Server/Token/AnonToken.php | 5 ----- src/FreeDSx/Ldap/Server/Token/BindToken.php | 18 +----------------- src/FreeDSx/Ldap/Server/Token/SystemToken.php | 5 ----- .../Ldap/Server/Token/TokenInterface.php | 2 -- .../PasswordPolicyChangeEnforcementTest.php | 6 +----- ...asswordPolicyPlainModifyEnforcementTest.php | 1 - tests/unit/Protocol/AuthenticatorTest.php | 1 - .../Authorization/AuthzIdResolverTest.php | 1 - .../DispatchAuthorizationTest.php | 1 - .../Authorization/DispatchAuthorizerTest.php | 1 - .../ProxiedAuthorizationResolverTest.php | 1 - .../ServerPasswordModifyHandlerTest.php | 1 - .../ServerWhoAmIHandlerTest.php | 1 - .../RuleBasedAccessControlTest.php | 1 - .../AccessControl/SimpleAccessControlTest.php | 4 ---- .../Subject/AnonymousSubjectMatcherTest.php | 1 - .../AuthenticatedSubjectMatcherTest.php | 1 - .../Subject/CallbackSubjectMatcherTest.php | 4 ---- .../Subject/DnSubjectMatcherTest.php | 3 --- .../Subject/DnSubtreeSubjectMatcherTest.php | 3 --- .../Subject/GroupSubjectMatcherTest.php | 18 +++--------------- .../Subject/SelfSubjectMatcherTest.php | 3 --- .../PasswordPolicyAwareAuthenticatorTest.php | 1 - .../OperationalAttributeGeneratorTest.php | 5 +---- .../Write/PasswordPolicyWriteHandlerTest.php | 1 - tests/unit/Server/Logging/EventLoggerTest.php | 2 -- .../Server/Logging/OperationAuditorTest.php | 1 - .../AuthorizationResolutionMiddlewareTest.php | 4 ++-- .../Server/Middleware/BindMiddlewareTest.php | 4 ++-- .../OperationAuditMiddlewareTest.php | 2 -- .../Pipeline/ServerRequestContextTest.php | 2 +- .../PasswordModifyServiceTest.php | 4 ---- .../PasswordPolicy/PasswordResetGateTest.php | 1 - .../SearchLimit/SearchLimitResolverTest.php | 1 - tests/unit/Server/Token/AnonTokenTest.php | 6 ------ tests/unit/Server/Token/BindTokenTest.php | 11 ----------- tests/unit/Server/Token/SystemTokenTest.php | 5 ----- 39 files changed, 12 insertions(+), 126 deletions(-) diff --git a/src/FreeDSx/Ldap/Server/Backend/Auth/PasswordAuthenticator.php b/src/FreeDSx/Ldap/Server/Backend/Auth/PasswordAuthenticator.php index df926fb2..c6911928 100644 --- a/src/FreeDSx/Ldap/Server/Backend/Auth/PasswordAuthenticator.php +++ b/src/FreeDSx/Ldap/Server/Backend/Auth/PasswordAuthenticator.php @@ -60,7 +60,6 @@ public function authenticate( if ($this->hashService->verify($password, $stored)) { return new BindToken( AuthzId::fromDn($entry->getDn()), - $password, $name, ); } diff --git a/src/FreeDSx/Ldap/Server/Proxy/ProxyAuthenticator.php b/src/FreeDSx/Ldap/Server/Proxy/ProxyAuthenticator.php index 947475f9..df90aa26 100644 --- a/src/FreeDSx/Ldap/Server/Proxy/ProxyAuthenticator.php +++ b/src/FreeDSx/Ldap/Server/Proxy/ProxyAuthenticator.php @@ -56,10 +56,7 @@ public function authenticate( ); } - return BindToken::fromDn( - $name, - $password, - ); + return BindToken::fromDn($name); } /** diff --git a/src/FreeDSx/Ldap/Server/Token/AnonToken.php b/src/FreeDSx/Ldap/Server/Token/AnonToken.php index e984704c..9a0e0983 100644 --- a/src/FreeDSx/Ldap/Server/Token/AnonToken.php +++ b/src/FreeDSx/Ldap/Server/Token/AnonToken.php @@ -54,11 +54,6 @@ public function getUsername(): ?string return $this->username; } - public function getPassword(): ?string - { - return null; - } - public function getVersion(): int { return $this->version; diff --git a/src/FreeDSx/Ldap/Server/Token/BindToken.php b/src/FreeDSx/Ldap/Server/Token/BindToken.php index f9e4e1f4..b2a1cc92 100644 --- a/src/FreeDSx/Ldap/Server/Token/BindToken.php +++ b/src/FreeDSx/Ldap/Server/Token/BindToken.php @@ -16,7 +16,6 @@ use FreeDSx\Ldap\Entry\Dn; use FreeDSx\Ldap\Protocol\Authorization\AuthzId; use FreeDSx\Ldap\Server\Utility\Uuid; -use SensitiveParameter; /** * Represents a bound and authorized identity, identified by its authorization identity (authzId). @@ -33,8 +32,6 @@ class BindToken implements AuthenticatedTokenInterface private string $authcId; - private string $password; - private int $version; private bool $mustChangePassword = false; @@ -47,8 +44,6 @@ class BindToken implements AuthenticatedTokenInterface */ public function __construct( private readonly AuthzId $authzId, - #[SensitiveParameter] - string $password = '', ?string $authcId = null, int $version = 3, ?Dn $authorizingDn = null, @@ -56,21 +51,17 @@ public function __construct( $this->id = Uuid::v4(); $this->resolvedDn = new Dn($authzId->getValue()); $this->authcId = $authcId ?? $authzId->getValue(); - $this->password = $password; $this->version = $version; $this->authorizingDn = $authorizingDn; } public static function fromDn( string $dn, - #[SensitiveParameter] - string $password, int $version = 3, ?Dn $authorizingDn = null, ): self { return new self( AuthzId::fromDn(new Dn($dn)), - $password, $dn, $version, $authorizingDn, @@ -78,7 +69,7 @@ public static function fromDn( } /** - * Creates a token for a SASL-authenticated identity; no plaintext password is carried. + * Creates a token for a SASL-authenticated identity. */ public static function fromSasl( string $username, @@ -88,7 +79,6 @@ public static function fromSasl( ): self { return new self( AuthzId::fromDn($resolvedDn), - '', $username, $version, $authorizingDn, @@ -105,7 +95,6 @@ public static function fromUsername( ): self { return new self( AuthzId::fromUsername($username), - '', $username, $version, $authorizingDn, @@ -127,11 +116,6 @@ public function getUsername(): ?string return $this->authcId; } - public function getPassword(): ?string - { - return $this->password; - } - public function getResolvedDn(): Dn { return $this->resolvedDn; diff --git a/src/FreeDSx/Ldap/Server/Token/SystemToken.php b/src/FreeDSx/Ldap/Server/Token/SystemToken.php index c9b56315..d3386c5e 100644 --- a/src/FreeDSx/Ldap/Server/Token/SystemToken.php +++ b/src/FreeDSx/Ldap/Server/Token/SystemToken.php @@ -42,11 +42,6 @@ public function getUsername(): string return self::IDENTITY; } - public function getPassword(): ?string - { - return null; - } - public function getVersion(): int { return $this->version; diff --git a/src/FreeDSx/Ldap/Server/Token/TokenInterface.php b/src/FreeDSx/Ldap/Server/Token/TokenInterface.php index 0b97e578..27253206 100644 --- a/src/FreeDSx/Ldap/Server/Token/TokenInterface.php +++ b/src/FreeDSx/Ldap/Server/Token/TokenInterface.php @@ -28,8 +28,6 @@ public function getId(): string; public function getUsername(): ?string; - public function getPassword(): ?string; - public function getVersion(): int; /** diff --git a/tests/integration/Security/PasswordPolicyChangeEnforcementTest.php b/tests/integration/Security/PasswordPolicyChangeEnforcementTest.php index 15dde712..d18f1eec 100644 --- a/tests/integration/Security/PasswordPolicyChangeEnforcementTest.php +++ b/tests/integration/Security/PasswordPolicyChangeEnforcementTest.php @@ -353,17 +353,13 @@ private function request( private function selfToken(): BindToken { - return BindToken::fromDn( - self::USER_DN, - self::OLD_PASSWORD, - ); + return BindToken::fromDn(self::USER_DN); } private function adminToken(): BindToken { return BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'admin-pass', ); } diff --git a/tests/integration/Security/PasswordPolicyPlainModifyEnforcementTest.php b/tests/integration/Security/PasswordPolicyPlainModifyEnforcementTest.php index b12d6539..bb467c72 100644 --- a/tests/integration/Security/PasswordPolicyPlainModifyEnforcementTest.php +++ b/tests/integration/Security/PasswordPolicyPlainModifyEnforcementTest.php @@ -366,7 +366,6 @@ private function token(): BindToken { return BindToken::fromDn( self::USER_DN, - 'original-pass', ); } diff --git a/tests/unit/Protocol/AuthenticatorTest.php b/tests/unit/Protocol/AuthenticatorTest.php index 93497f75..ef3bdfa3 100644 --- a/tests/unit/Protocol/AuthenticatorTest.php +++ b/tests/unit/Protocol/AuthenticatorTest.php @@ -72,7 +72,6 @@ public function test_it_should_return_the_token(): void $token = BindToken::fromDn( 'foo', - 'bar', ); $this->authOne diff --git a/tests/unit/Protocol/Authorization/AuthzIdResolverTest.php b/tests/unit/Protocol/Authorization/AuthzIdResolverTest.php index b28b3732..b80abe62 100644 --- a/tests/unit/Protocol/Authorization/AuthzIdResolverTest.php +++ b/tests/unit/Protocol/Authorization/AuthzIdResolverTest.php @@ -63,7 +63,6 @@ protected function setUp(): void ); $this->boundToken = BindToken::fromDn( self::ADMIN_DN, - 'secret', ); } diff --git a/tests/unit/Protocol/Authorization/DispatchAuthorizationTest.php b/tests/unit/Protocol/Authorization/DispatchAuthorizationTest.php index 667b79c9..43bc3e10 100644 --- a/tests/unit/Protocol/Authorization/DispatchAuthorizationTest.php +++ b/tests/unit/Protocol/Authorization/DispatchAuthorizationTest.php @@ -24,7 +24,6 @@ public function test_proceed_carries_the_effective_token(): void { $token = BindToken::fromDn( 'cn=alice,dc=example,dc=com', - 'secret', ); $authorization = DispatchAuthorization::proceed($token); diff --git a/tests/unit/Protocol/Authorization/DispatchAuthorizerTest.php b/tests/unit/Protocol/Authorization/DispatchAuthorizerTest.php index 3b54151c..741998bc 100644 --- a/tests/unit/Protocol/Authorization/DispatchAuthorizerTest.php +++ b/tests/unit/Protocol/Authorization/DispatchAuthorizerTest.php @@ -200,7 +200,6 @@ private function boundToken(): BindToken { return BindToken::fromDn( self::BOUND_DN, - 'secret', ); } diff --git a/tests/unit/Protocol/Authorization/ProxiedAuthorizationResolverTest.php b/tests/unit/Protocol/Authorization/ProxiedAuthorizationResolverTest.php index 1411d452..b11f3db3 100644 --- a/tests/unit/Protocol/Authorization/ProxiedAuthorizationResolverTest.php +++ b/tests/unit/Protocol/Authorization/ProxiedAuthorizationResolverTest.php @@ -78,7 +78,6 @@ protected function setUp(): void ); $this->boundToken = BindToken::fromDn( self::ADMIN_DN, - 'secret', ); } diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerPasswordModifyHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerPasswordModifyHandlerTest.php index 2241159e..e65bf3e0 100644 --- a/tests/unit/Protocol/ServerProtocolHandler/ServerPasswordModifyHandlerTest.php +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerPasswordModifyHandlerTest.php @@ -69,7 +69,6 @@ protected function setUp(): void ); $this->userToken = BindToken::fromDn( 'cn=user,dc=foo,dc=bar', - '12345', ); $this->subject = new ServerPasswordModifyHandler( diff --git a/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php b/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php index 16c3ff77..6f3115d4 100644 --- a/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php +++ b/tests/unit/Protocol/ServerProtocolHandler/ServerWhoAmIHandlerTest.php @@ -55,7 +55,6 @@ public function test_it_should_handle_a_who_am_i_when_there_is_a_token_with_a_DN $request, BindToken::fromDn( 'cn=foo,dc=foo,dc=bar', - '12345', ), ); } diff --git a/tests/unit/Server/AccessControl/RuleBasedAccessControlTest.php b/tests/unit/Server/AccessControl/RuleBasedAccessControlTest.php index c6e5f40f..1016d826 100644 --- a/tests/unit/Server/AccessControl/RuleBasedAccessControlTest.php +++ b/tests/unit/Server/AccessControl/RuleBasedAccessControlTest.php @@ -47,7 +47,6 @@ protected function setUp(): void $this->dn = new Dn('dc=foo,dc=bar'); $this->bindToken = BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ); } diff --git a/tests/unit/Server/AccessControl/SimpleAccessControlTest.php b/tests/unit/Server/AccessControl/SimpleAccessControlTest.php index d799599a..b0741ab8 100644 --- a/tests/unit/Server/AccessControl/SimpleAccessControlTest.php +++ b/tests/unit/Server/AccessControl/SimpleAccessControlTest.php @@ -115,7 +115,6 @@ public function test_it_should_allow_authenticated_search(): void OperationType::Search, BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->dn, ); @@ -129,7 +128,6 @@ public function test_it_should_allow_authenticated_add(): void OperationType::Add, BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->dn, ); @@ -142,7 +140,6 @@ public function test_it_should_return_entry_unchanged_from_filter_entry(): void $result = $this->subject->filterEntry( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $entry, ); @@ -175,7 +172,6 @@ public function test_authorize_attribute_access_is_a_no_op(): void $this->subject->authorizeAttribute( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->dn, 'userPassword', diff --git a/tests/unit/Server/AccessControl/Subject/AnonymousSubjectMatcherTest.php b/tests/unit/Server/AccessControl/Subject/AnonymousSubjectMatcherTest.php index a3b757e3..d951641d 100644 --- a/tests/unit/Server/AccessControl/Subject/AnonymousSubjectMatcherTest.php +++ b/tests/unit/Server/AccessControl/Subject/AnonymousSubjectMatcherTest.php @@ -44,7 +44,6 @@ public function test_it_should_not_match_bind_token(): void self::assertFalse($this->subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->dn, )); diff --git a/tests/unit/Server/AccessControl/Subject/AuthenticatedSubjectMatcherTest.php b/tests/unit/Server/AccessControl/Subject/AuthenticatedSubjectMatcherTest.php index 66e7af2c..7b016286 100644 --- a/tests/unit/Server/AccessControl/Subject/AuthenticatedSubjectMatcherTest.php +++ b/tests/unit/Server/AccessControl/Subject/AuthenticatedSubjectMatcherTest.php @@ -37,7 +37,6 @@ public function test_it_should_match_bind_token(): void self::assertTrue($this->subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->dn, )); diff --git a/tests/unit/Server/AccessControl/Subject/CallbackSubjectMatcherTest.php b/tests/unit/Server/AccessControl/Subject/CallbackSubjectMatcherTest.php index 41181cf2..5da78cd2 100644 --- a/tests/unit/Server/AccessControl/Subject/CallbackSubjectMatcherTest.php +++ b/tests/unit/Server/AccessControl/Subject/CallbackSubjectMatcherTest.php @@ -31,7 +31,6 @@ public function test_it_should_delegate_to_callback_returning_true(): void self::assertTrue($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), new Dn('dc=foo,dc=bar'), )); @@ -46,7 +45,6 @@ public function test_it_should_delegate_to_callback_returning_false(): void self::assertFalse($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), new Dn('dc=foo,dc=bar'), )); @@ -59,7 +57,6 @@ public function test_it_should_pass_token_and_dn_to_callback(): void $token = BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ); $dn = new Dn('dc=foo,dc=bar'); @@ -96,7 +93,6 @@ public function test_it_should_return_false_when_callback_throws(): void self::assertFalse($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), new Dn('dc=foo,dc=bar'), )); diff --git a/tests/unit/Server/AccessControl/Subject/DnSubjectMatcherTest.php b/tests/unit/Server/AccessControl/Subject/DnSubjectMatcherTest.php index a9f44f9d..c7ba36f5 100644 --- a/tests/unit/Server/AccessControl/Subject/DnSubjectMatcherTest.php +++ b/tests/unit/Server/AccessControl/Subject/DnSubjectMatcherTest.php @@ -35,7 +35,6 @@ public function test_it_should_match_when_username_equals_configured_dn(): void self::assertTrue($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -48,7 +47,6 @@ public function test_it_should_match_case_insensitively(): void self::assertTrue($subject->matches( BindToken::fromDn( 'CN=Admin,DC=foo,DC=bar', - 'secret', ), $this->targetDn, )); @@ -61,7 +59,6 @@ public function test_it_should_not_match_when_username_differs(): void self::assertFalse($subject->matches( BindToken::fromDn( 'cn=other,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); diff --git a/tests/unit/Server/AccessControl/Subject/DnSubtreeSubjectMatcherTest.php b/tests/unit/Server/AccessControl/Subject/DnSubtreeSubjectMatcherTest.php index 6ded0176..0c22839a 100644 --- a/tests/unit/Server/AccessControl/Subject/DnSubtreeSubjectMatcherTest.php +++ b/tests/unit/Server/AccessControl/Subject/DnSubtreeSubjectMatcherTest.php @@ -35,7 +35,6 @@ public function test_it_should_match_when_bound_dn_is_within_subtree(): void self::assertTrue($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -48,7 +47,6 @@ public function test_it_should_not_match_when_bound_dn_is_in_sibling_subtree(): self::assertFalse($subject->matches( BindToken::fromDn( 'cn=admin,dc=other,dc=bar', - 'secret', ), $this->targetDn, )); @@ -81,7 +79,6 @@ public function test_it_should_not_match_and_not_throw_when_resolved_dn_is_not_v self::assertFalse($subject->matches( BindToken::fromDn( 'jdoe', - 'secret', ), $this->targetDn, )); diff --git a/tests/unit/Server/AccessControl/Subject/GroupSubjectMatcherTest.php b/tests/unit/Server/AccessControl/Subject/GroupSubjectMatcherTest.php index ebcc5ce6..d52528bc 100644 --- a/tests/unit/Server/AccessControl/Subject/GroupSubjectMatcherTest.php +++ b/tests/unit/Server/AccessControl/Subject/GroupSubjectMatcherTest.php @@ -51,7 +51,6 @@ public function test_it_should_match_when_bound_dn_is_a_group_member(): void self::assertTrue($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -74,7 +73,6 @@ public function test_it_should_match_case_insensitively(): void self::assertTrue($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -97,7 +95,6 @@ public function test_it_should_not_match_when_bound_dn_is_not_a_member(): void self::assertFalse($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -115,7 +112,6 @@ public function test_it_should_not_match_when_group_entry_is_not_found(): void self::assertFalse($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -128,7 +124,6 @@ public function test_it_should_not_match_when_backend_is_not_set(): void self::assertFalse($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -184,7 +179,6 @@ public function test_it_should_use_custom_member_attribute(): void self::assertTrue($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -207,7 +201,6 @@ public function test_it_should_fetch_group_entry_only_once_across_multiple_calls $token = BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ); $subject->matches( @@ -257,14 +250,12 @@ public function test_it_should_fetch_group_entry_separately_per_token_id(): void $subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, ); $subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, ); @@ -291,7 +282,6 @@ public function test_it_should_bypass_cache_when_max_cache_size_is_zero(): void $token = BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ); $subject->matches($token, $this->targetDn); @@ -316,7 +306,6 @@ public function test_it_should_match_when_member_dn_has_surrounding_whitespace() self::assertTrue($subject->matches( BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'secret', ), $this->targetDn, )); @@ -339,7 +328,6 @@ public function test_it_should_not_match_when_dn_is_different_after_normalizatio self::assertFalse($subject->matches( BindToken::fromDn( 'cn=admin, dc=foo, dc=bar', - 'secret', ), $this->targetDn, )); @@ -364,9 +352,9 @@ public function test_it_should_evict_oldest_entry_when_cache_is_full(): void ); $subject->setBackend($this->mockBackend); - $token1 = BindToken::fromDn('cn=admin,dc=foo,dc=bar', 'secret'); - $token2 = BindToken::fromDn('cn=admin,dc=foo,dc=bar', 'secret'); - $token3 = BindToken::fromDn('cn=admin,dc=foo,dc=bar', 'secret'); + $token1 = BindToken::fromDn('cn=admin,dc=foo,dc=bar'); + $token2 = BindToken::fromDn('cn=admin,dc=foo,dc=bar'); + $token3 = BindToken::fromDn('cn=admin,dc=foo,dc=bar'); $subject->matches($token1, $this->targetDn); $subject->matches($token2, $this->targetDn); diff --git a/tests/unit/Server/AccessControl/Subject/SelfSubjectMatcherTest.php b/tests/unit/Server/AccessControl/Subject/SelfSubjectMatcherTest.php index 70ce1dd5..d2d4174e 100644 --- a/tests/unit/Server/AccessControl/Subject/SelfSubjectMatcherTest.php +++ b/tests/unit/Server/AccessControl/Subject/SelfSubjectMatcherTest.php @@ -33,7 +33,6 @@ public function test_it_should_match_when_username_equals_target_dn(): void self::assertTrue($this->subject->matches( BindToken::fromDn( 'cn=foo,dc=bar', - 'secret', ), new Dn('cn=foo,dc=bar'), )); @@ -44,7 +43,6 @@ public function test_it_should_match_case_insensitively(): void self::assertTrue($this->subject->matches( BindToken::fromDn( 'CN=Foo,DC=bar', - 'secret', ), new Dn('cn=foo,dc=bar'), )); @@ -55,7 +53,6 @@ public function test_it_should_not_match_when_username_differs_from_target_dn(): self::assertFalse($this->subject->matches( BindToken::fromDn( 'cn=admin,dc=bar', - 'secret', ), new Dn('cn=foo,dc=bar'), )); diff --git a/tests/unit/Server/Backend/Auth/PasswordPolicyAwareAuthenticatorTest.php b/tests/unit/Server/Backend/Auth/PasswordPolicyAwareAuthenticatorTest.php index 5f6c5a85..20e44b1f 100644 --- a/tests/unit/Server/Backend/Auth/PasswordPolicyAwareAuthenticatorTest.php +++ b/tests/unit/Server/Backend/Auth/PasswordPolicyAwareAuthenticatorTest.php @@ -227,7 +227,6 @@ private function expectInnerReturnsToken(): BindToken { $token = BindToken::fromDn( self::DN, - 'secret', ); $this->inner ->method('authenticate') diff --git a/tests/unit/Server/Backend/Storage/OperationalAttributeGeneratorTest.php b/tests/unit/Server/Backend/Storage/OperationalAttributeGeneratorTest.php index 9cab24d4..9edc7bfe 100644 --- a/tests/unit/Server/Backend/Storage/OperationalAttributeGeneratorTest.php +++ b/tests/unit/Server/Backend/Storage/OperationalAttributeGeneratorTest.php @@ -438,10 +438,7 @@ private function anonymousContext(): WriteContext private function boundContext(string $dn): WriteContext { return new WriteContext( - BindToken::fromDn( - $dn, - '', - ), + BindToken::fromDn($dn), new ControlBag(), ); } diff --git a/tests/unit/Server/Backend/Write/PasswordPolicyWriteHandlerTest.php b/tests/unit/Server/Backend/Write/PasswordPolicyWriteHandlerTest.php index 694c9a3a..d64867d9 100644 --- a/tests/unit/Server/Backend/Write/PasswordPolicyWriteHandlerTest.php +++ b/tests/unit/Server/Backend/Write/PasswordPolicyWriteHandlerTest.php @@ -348,7 +348,6 @@ private function writeContext(): WriteContext return new WriteContext( BindToken::fromDn( self::USER_DN, - 'original-pass', ), new ControlBag(), ); diff --git a/tests/unit/Server/Logging/EventLoggerTest.php b/tests/unit/Server/Logging/EventLoggerTest.php index 00a10b1e..82ad6776 100644 --- a/tests/unit/Server/Logging/EventLoggerTest.php +++ b/tests/unit/Server/Logging/EventLoggerTest.php @@ -95,7 +95,6 @@ public function test_record_includes_authorized_by_for_a_proxied_subject(): void { $token = BindToken::fromDn( 'cn=alice,dc=example,dc=com', - '', 3, new Dn('cn=admin,dc=example,dc=com'), ); @@ -132,7 +131,6 @@ public function test_record_omits_authorized_by_for_a_non_proxied_subject(): voi { $token = BindToken::fromDn( 'cn=alice,dc=example,dc=com', - 'secret', ); $this->mockLogger diff --git a/tests/unit/Server/Logging/OperationAuditorTest.php b/tests/unit/Server/Logging/OperationAuditorTest.php index 788b6261..8b4f3e2d 100644 --- a/tests/unit/Server/Logging/OperationAuditorTest.php +++ b/tests/unit/Server/Logging/OperationAuditorTest.php @@ -65,7 +65,6 @@ protected function setUp(): void )); $this->token = BindToken::fromDn( self::ACTOR_DN, - 'secret', ); } diff --git a/tests/unit/Server/Middleware/AuthorizationResolutionMiddlewareTest.php b/tests/unit/Server/Middleware/AuthorizationResolutionMiddlewareTest.php index f5a6561f..8b3cabcc 100644 --- a/tests/unit/Server/Middleware/AuthorizationResolutionMiddlewareTest.php +++ b/tests/unit/Server/Middleware/AuthorizationResolutionMiddlewareTest.php @@ -64,7 +64,7 @@ public function test_a_request_requiring_authentication_is_rejected(): void public function test_a_request_requiring_a_password_change_is_rejected_and_stashes_the_control(): void { - $token = BindToken::fromDn('cn=user,dc=foo,dc=bar', 'secret'); + $token = BindToken::fromDn('cn=user,dc=foo,dc=bar'); $token->markMustChangePassword(); $this->authorization->setToken($token); @@ -90,7 +90,7 @@ public function test_a_request_requiring_a_password_change_is_rejected_and_stash public function test_an_authorized_request_is_delegated_with_the_resolved_token(): void { - $token = BindToken::fromDn('cn=user,dc=foo,dc=bar', 'secret'); + $token = BindToken::fromDn('cn=user,dc=foo,dc=bar'); $this->authorization->setToken($token); $this->subject()->process( diff --git a/tests/unit/Server/Middleware/BindMiddlewareTest.php b/tests/unit/Server/Middleware/BindMiddlewareTest.php index 0f0806a6..8fff69cb 100644 --- a/tests/unit/Server/Middleware/BindMiddlewareTest.php +++ b/tests/unit/Server/Middleware/BindMiddlewareTest.php @@ -66,7 +66,7 @@ public function test_a_non_bind_request_is_delegated(): void public function test_a_successful_bind_stores_the_token_and_short_circuits(): void { - $token = BindToken::fromDn('cn=user,dc=foo,dc=bar', 'secret'); + $token = BindToken::fromDn('cn=user,dc=foo,dc=bar'); $this->authenticator ->method('bind') ->willReturn($token); @@ -119,7 +119,7 @@ public function test_an_unsupported_authentication_type_is_rejected(): void public function test_a_failed_rebind_resets_an_authenticated_session_to_anonymous(): void { - $this->authorization->setToken(BindToken::fromDn('cn=admin,dc=foo,dc=bar', 'secret')); + $this->authorization->setToken(BindToken::fromDn('cn=admin,dc=foo,dc=bar')); $this->authenticator ->method('bind') ->willThrowException(new OperationException( diff --git a/tests/unit/Server/Middleware/OperationAuditMiddlewareTest.php b/tests/unit/Server/Middleware/OperationAuditMiddlewareTest.php index 93ecb1ce..cda2498a 100644 --- a/tests/unit/Server/Middleware/OperationAuditMiddlewareTest.php +++ b/tests/unit/Server/Middleware/OperationAuditMiddlewareTest.php @@ -56,7 +56,6 @@ protected function setUp(): void new LdapMessageRequest(1, new AddRequest(Entry::create('cn=foo,dc=bar'))), BindToken::fromDn( 'cn=alice,dc=bar', - 'secret', ), ); } @@ -206,7 +205,6 @@ private function contextFor(RequestInterface $request): ServerRequestContext new LdapMessageRequest(1, $request), BindToken::fromDn( 'cn=alice,dc=bar', - 'secret', ), ); } diff --git a/tests/unit/Server/Middleware/Pipeline/ServerRequestContextTest.php b/tests/unit/Server/Middleware/Pipeline/ServerRequestContextTest.php index e586677b..760796ba 100644 --- a/tests/unit/Server/Middleware/Pipeline/ServerRequestContextTest.php +++ b/tests/unit/Server/Middleware/Pipeline/ServerRequestContextTest.php @@ -46,7 +46,7 @@ public function test_token_or_fail_throws_when_no_token_is_resolved(): void public function test_with_token_returns_a_new_context_carrying_the_token(): void { - $token = BindToken::fromDn('cn=user,dc=foo,dc=bar', 'secret'); + $token = BindToken::fromDn('cn=user,dc=foo,dc=bar'); $withToken = $this->subject->withToken($token); diff --git a/tests/unit/Server/PasswordModify/PasswordModifyServiceTest.php b/tests/unit/Server/PasswordModify/PasswordModifyServiceTest.php index 45392570..0224d2fe 100644 --- a/tests/unit/Server/PasswordModify/PasswordModifyServiceTest.php +++ b/tests/unit/Server/PasswordModify/PasswordModifyServiceTest.php @@ -67,7 +67,6 @@ protected function setUp(): void ); $this->userToken = BindToken::fromDn( self::USER_DN, - '12345', ); $this->subject = new PasswordModifyService( @@ -124,7 +123,6 @@ public function test_named_identity_is_resolved_via_the_identity_resolver(): voi ), BindToken::fromDn( 'cn=admin,dc=foo,dc=bar', - 'adminpass', ), new ControlBag(), ); @@ -277,7 +275,6 @@ public function test_must_change_identity_cannot_target_another_entry(): void $token = BindToken::fromDn( 'cn=other,dc=foo,dc=bar', - 'secret', ); $token->markMustChangePassword(); @@ -313,7 +310,6 @@ public function test_must_change_self_change_clears_the_session_flag(): void $token = BindToken::fromDn( self::USER_DN, - '12345', ); $token->markMustChangePassword(); diff --git a/tests/unit/Server/PasswordPolicy/PasswordResetGateTest.php b/tests/unit/Server/PasswordPolicy/PasswordResetGateTest.php index ba1c63f0..fad7838d 100644 --- a/tests/unit/Server/PasswordPolicy/PasswordResetGateTest.php +++ b/tests/unit/Server/PasswordPolicy/PasswordResetGateTest.php @@ -145,7 +145,6 @@ private function token(): BindToken { return BindToken::fromDn( self::USER_DN, - 'secret', ); } } diff --git a/tests/unit/Server/SearchLimit/SearchLimitResolverTest.php b/tests/unit/Server/SearchLimit/SearchLimitResolverTest.php index 37de1568..6c35cb7e 100644 --- a/tests/unit/Server/SearchLimit/SearchLimitResolverTest.php +++ b/tests/unit/Server/SearchLimit/SearchLimitResolverTest.php @@ -97,7 +97,6 @@ private function authenticatedToken(): BindToken { return BindToken::fromDn( 'cn=user,dc=foo,dc=bar', - '12345', ); } } diff --git a/tests/unit/Server/Token/AnonTokenTest.php b/tests/unit/Server/Token/AnonTokenTest.php index f69b761a..6feeb8a6 100644 --- a/tests/unit/Server/Token/AnonTokenTest.php +++ b/tests/unit/Server/Token/AnonTokenTest.php @@ -33,12 +33,6 @@ public function test_it_should_get_the_username(): void ); } - public function test_it_should_get_a_null_password(): void - { - self::assertNull($this->subject->getPassword()); - ; - } - public function test_it_should_get_the_version(): void { self::assertSame( diff --git a/tests/unit/Server/Token/BindTokenTest.php b/tests/unit/Server/Token/BindTokenTest.php index 3f6f449f..9f94cee7 100644 --- a/tests/unit/Server/Token/BindTokenTest.php +++ b/tests/unit/Server/Token/BindTokenTest.php @@ -24,7 +24,6 @@ protected function setUp(): void { $this->subject = BindToken::fromDn( 'foo', - 'bar', ); } @@ -36,14 +35,6 @@ public function test_it_should_get_the_username(): void ); } - public function test_it_should_get_the_password(): void - { - self::assertSame( - 'bar', - $this->subject->getPassword(), - ); - } - public function test_it_should_get_the_version(): void { self::assertSame( @@ -64,7 +55,6 @@ public function test_it_should_return_a_unique_id_per_instance(): void { $other = BindToken::fromDn( 'foo', - 'bar', ); self::assertNotSame( @@ -77,7 +67,6 @@ public function test_it_should_return_the_resolved_dn(): void { $token = BindToken::fromDn( 'cn=foo,dc=bar', - 'secret', ); self::assertSame( diff --git a/tests/unit/Server/Token/SystemTokenTest.php b/tests/unit/Server/Token/SystemTokenTest.php index 74a85aa5..46d424ae 100644 --- a/tests/unit/Server/Token/SystemTokenTest.php +++ b/tests/unit/Server/Token/SystemTokenTest.php @@ -32,11 +32,6 @@ public function test_it_reports_the_system_identity(): void ); } - public function test_it_carries_no_password(): void - { - self::assertNull((new SystemToken())->getPassword()); - } - public function test_it_defaults_to_ldap_v3(): void { self::assertSame(