Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/FreeDSx/Ldap/Schema/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,28 @@ public function isIntegerOrdered(string $nameOrOid): ?bool
return $attributeType->syntaxOid === SyntaxOid::OID_INTEGER;
}

/**
* The effective SUBSTR rule OID, walking the SUP chain when the type declares none directly.
*/
public function getSubstringRuleOid(string $nameOrOid): ?string
{
$attributeType = $this->getAttributeType($nameOrOid);
$seen = [];

while ($attributeType !== null && !isset($seen[$attributeType->oid])) {
if ($attributeType->substringOid !== null) {
return $attributeType->substringOid;
}

$seen[$attributeType->oid] = true;
$attributeType = $attributeType->superTypeOid !== null
? $this->getAttributeType($attributeType->superTypeOid)
: null;
}

return null;
}

/**
* Whether one attribute type is the other, or descends from it through the SUP chain.
*
Expand Down
35 changes: 4 additions & 31 deletions src/FreeDSx/Ldap/Schema/Validation/SchemaValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@
use FreeDSx\Ldap\Entry\Entry;
use FreeDSx\Ldap\Exception\OperationException;
use FreeDSx\Ldap\Operation\ResultCode;
use FreeDSx\Ldap\Schema\Definition\AttributeType;
use FreeDSx\Ldap\Schema\Definition\AttributeUsage;
use FreeDSx\Ldap\Schema\Definition\ObjectClass;
use FreeDSx\Ldap\Schema\Definition\ObjectClassType;
use FreeDSx\Ldap\Schema\Schema;
use FreeDSx\Ldap\Schema\SchemaValidationMode;
use FreeDSx\Ldap\Schema\Validation\Syntax\AttributeSyntaxResolver;
use FreeDSx\Ldap\Schema\Validation\Syntax\SyntaxValidatorInterface;
use FreeDSx\Ldap\Schema\Validation\Syntax\SyntaxValidatorRegistry;
use FreeDSx\Ldap\Server\Backend\Write\Command\UpdateCommand;

/**
Expand All @@ -37,14 +36,13 @@ final class SchemaValidator
{
private const EXTENSIBLE_OBJECT = 'extensibleObject';

private readonly SyntaxValidatorRegistry $syntaxValidators;
private readonly AttributeSyntaxResolver $syntaxResolver;

public function __construct(
private readonly Schema $schema,
private readonly SchemaValidationMode $mode,
?SyntaxValidatorRegistry $syntaxValidators = null,
) {
$this->syntaxValidators = $syntaxValidators ?? SyntaxValidatorRegistry::default();
$this->syntaxResolver = new AttributeSyntaxResolver($schema);
}

public function mode(): SchemaValidationMode
Expand Down Expand Up @@ -334,10 +332,7 @@ private function checkAttributeSyntaxes(Entry $entry): void
continue;
}

$syntaxOid = $this->resolveSyntaxOid($attrType);
$validator = $syntaxOid === null
? null
: $this->syntaxValidators->get($syntaxOid);
$validator = $this->syntaxResolver->validatorFor($attrType);
if ($validator === null) {
continue;
}
Expand Down Expand Up @@ -371,28 +366,6 @@ private function checkValuesConform(
}
}

/**
* Resolves the effective syntax OID, walking the SUP chain when not set directly.
*/
private function resolveSyntaxOid(AttributeType $type): ?string
{
$visited = [];
$current = $type;

while ($current !== null && !isset($visited[$current->oid])) {
if ($current->syntaxOid !== null) {
return $current->syntaxOid;
}

$visited[$current->oid] = true;
$current = $current->superTypeOid !== null
? $this->schema->getAttributeType($current->superTypeOid)
: null;
}

return null;
}

/**
* @throws OperationException
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

/**
* This file is part of the FreeDSx LDAP package.
*
* (c) Chad Sikorra <Chad.Sikorra@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FreeDSx\Ldap\Schema\Validation\Syntax;

use FreeDSx\Ldap\Entry\Attribute;
use FreeDSx\Ldap\Schema\Definition\AttributeType;
use FreeDSx\Ldap\Schema\Schema;

/**
* Answers whether a value conforms to the syntax an attribute type carries, following the SUP chain to find it.
*
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
*/
final readonly class AttributeSyntaxResolver
{
private SyntaxValidatorRegistry $registry;

public function __construct(private Schema $schema)
{
$this->registry = SyntaxValidatorRegistry::default();
}

/**
* Whether a value conforms; true when the type is unknown or its syntax carries no validator.
*/
public function conforms(
string $attributeDescription,
string $value,
): bool {
$type = $this->schema->getAttributeType(Attribute::normalizeName($attributeDescription));

if ($type === null) {
return true;
}

return $this->validatorFor($type)?->isValid($value) ?? true;
}

/**
* The validator enforcing the type's effective syntax, or null when it is unconstrained.
*/
public function validatorFor(AttributeType $type): ?SyntaxValidatorInterface
{
$syntaxOid = $this->resolveSyntaxOid($type);

return $syntaxOid === null
? null
: $this->registry->get($syntaxOid);
}

/**
* Resolves the effective syntax OID, walking the SUP chain when not set directly.
*/
private function resolveSyntaxOid(AttributeType $type): ?string
{
$visited = [];
$current = $type;

while ($current !== null && !isset($visited[$current->oid])) {
if ($current->syntaxOid !== null) {
return $current->syntaxOid;
}

$visited[$current->oid] = true;
$current = $current->superTypeOid !== null
? $this->schema->getAttributeType($current->superTypeOid)
: null;
}

return null;
}
}
2 changes: 1 addition & 1 deletion src/FreeDSx/Ldap/Search/Filter/ApproximateFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
*/
class ApproximateFilter implements FilterInterface, FilterAttributeInterface, Stringable
class ApproximateFilter implements FilterInterface, AttributeValueAssertionInterface, Stringable
{
use AttributeValueAssertionTrait;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

/**
* This file is part of the FreeDSx LDAP package.
*
* (c) Chad Sikorra <Chad.Sikorra@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FreeDSx\Ldap\Search\Filter;

/**
* A filter asserting one whole value against an attribute type.
*/
interface AttributeValueAssertionInterface extends FilterAttributeInterface
{
/**
* The attribute type asserted against, which is always named for these filters.
*/
public function getAttribute(): string;

/**
* The value being asserted.
*/
public function getValue(): string;
}
2 changes: 1 addition & 1 deletion src/FreeDSx/Ldap/Search/Filter/EqualityFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
*/
class EqualityFilter implements FilterInterface, FilterAttributeInterface, Stringable
class EqualityFilter implements FilterInterface, AttributeValueAssertionInterface, Stringable
{
use AttributeValueAssertionTrait;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
*/
class GreaterThanOrEqualFilter implements FilterInterface, FilterAttributeInterface, Stringable
class GreaterThanOrEqualFilter implements FilterInterface, AttributeValueAssertionInterface, Stringable
{
use AttributeValueAssertionTrait;

Expand Down
2 changes: 1 addition & 1 deletion src/FreeDSx/Ldap/Search/Filter/LessThanOrEqualFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
*/
class LessThanOrEqualFilter implements FilterInterface, FilterAttributeInterface, Stringable
class LessThanOrEqualFilter implements FilterInterface, AttributeValueAssertionInterface, Stringable
{
use AttributeValueAssertionTrait;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use FreeDSx\Ldap\Server\Backend\Storage\Exception\InvalidAttributeException;
use FreeDSx\Ldap\Search\Filter\AndFilter;
use FreeDSx\Ldap\Search\Filter\ApproximateFilter;
use FreeDSx\Ldap\Search\Filter\AttributeValueAssertionInterface;
use FreeDSx\Ldap\Search\Filter\EqualityFilter;
use FreeDSx\Ldap\Search\Filter\FilterAttributeInterface;
use FreeDSx\Ldap\Search\Filter\FilterInterface;
Expand Down Expand Up @@ -62,14 +63,48 @@ private function filterSupport(string $attribute): AttributeFilterSupport
?? AttributeFilterSupport::Exact;
}

private function dispatch(FilterInterface $filter): ?SqlFilterResult
/**
* An item the type cannot answer is Undefined for every entry, whatever the attribute otherwise supports.
*/
private function supportFor(FilterInterface $filter): AttributeFilterSupport
{
if ($filter instanceof AttributeValueAssertionInterface && !$this->assertionValueConforms($filter)) {
return AttributeFilterSupport::NeverMatches;
}

// A substring item applies the type's SUBSTR rule, so a type declaring none cannot answer it.
if ($filter instanceof SubstringFilter && !$this->hasSubstringRule($filter->getAttribute())) {
return AttributeFilterSupport::NeverMatches;
}

$attribute = $filter instanceof FilterAttributeInterface
? $filter->getAttribute()
: null;
$support = $attribute !== null
? $this->filterSupport($attribute)
: AttributeFilterSupport::Exact;

return $attribute === null
? AttributeFilterSupport::Exact
: $this->filterSupport($attribute);
}

private function assertionValueConforms(AttributeValueAssertionInterface $filter): bool
{
return $this->attributeContext?->assertionValueConforms(
$filter->getAttribute(),
$filter->getValue(),
) ?? true;
}

/**
* Whether the attribute defines a SUBSTR rule; true when the caller had no schema.
*/
private function hasSubstringRule(string $attribute): bool
{
return $this->attributeContext?->hasSubstringRule($attribute) ?? true;
}

private function dispatch(FilterInterface $filter): ?SqlFilterResult
{
$support = $this->supportFor($filter);

// Rows under this name alone are not the whole answer, so leave the item to the evaluator.
if ($support === AttributeFilterSupport::NeedsEvaluator) {
Expand Down
38 changes: 38 additions & 0 deletions src/FreeDSx/Ldap/Server/Backend/Storage/EntryDnAttributeTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

/**
* This file is part of the FreeDSx LDAP package.
*
* (c) Chad Sikorra <Chad.Sikorra@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FreeDSx\Ldap\Server\Backend\Storage;

use FreeDSx\Ldap\Entry\Attribute;
use FreeDSx\Ldap\Schema\Definition\AttributeTypeOid;

use function strcasecmp;

/**
* Recognizes the entryDN description, whose value comes from the entry itself rather than from stored values.
*
* @author Chad Sikorra <Chad.Sikorra@gmail.com>
*/
trait EntryDnAttributeTrait
{
/**
* Whether a description names entryDN, by either its name or its OID.
*/
private static function isEntryDnAttribute(string $attributeDescription): bool
{
$type = Attribute::normalizeName($attributeDescription);

return strcasecmp($type, AttributeTypeOid::NAME_ENTRY_DN) === 0
|| $type === AttributeTypeOid::OID_ENTRY_DN;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,17 @@ public function isCaseInsensitive(string $attribute): ?bool;
* How faithfully SQL alone can answer an assertion on the attribute.
*/
public function filterSupport(string $attribute): AttributeFilterSupport;

/**
* Whether a value conforms to the attribute's syntax (false makes the item Undefined per RFC 4511 4.5.1.7).
*/
public function assertionValueConforms(
string $attribute,
string $value,
): bool;

/**
* Whether the attribute defines a SUBSTR rule (false makes a substring item Undefined per RFC 4511 4.5.1.7).
*/
public function hasSubstringRule(string $attribute): bool;
}
Loading
Loading