1<?php 2/** 3 * This file is part of the FreeDSx LDAP package. 4 * 5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace FreeDSx\Ldap\Search\Filter; 12 13use FreeDSx\Asn1\Asn1; 14use FreeDSx\Asn1\Type\AbstractType; 15use FreeDSx\Asn1\Type\BooleanType; 16use FreeDSx\Asn1\Type\IncompleteType; 17use FreeDSx\Asn1\Type\OctetStringType; 18use FreeDSx\Asn1\Type\SequenceType; 19use FreeDSx\Ldap\Entry\Attribute; 20use FreeDSx\Ldap\Exception\ProtocolException; 21use FreeDSx\Ldap\Protocol\LdapEncoder; 22 23/** 24 * Represents an extensible matching rule filter. RFC 4511, 4.5.1.7.7 25 * 26 * MatchingRuleAssertion ::= SEQUENCE { 27 * matchingRule [1] MatchingRuleId OPTIONAL, 28 * type [2] AttributeDescription OPTIONAL, 29 * matchValue [3] AssertionValue, 30 * dnAttributes [4] BOOLEAN DEFAULT FALSE } 31 * 32 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 33 */ 34class MatchingRuleFilter implements FilterInterface 35{ 36 protected const CHOICE_TAG = 9; 37 38 /** 39 * @var null|string 40 */ 41 protected $matchingRule; 42 43 /** 44 * @var null|string 45 */ 46 protected $attribute; 47 48 /** 49 * @var string 50 */ 51 protected $value; 52 53 /** 54 * @var bool 55 */ 56 protected $useDnAttributes; 57 58 /** 59 * @param null|string $matchingRule 60 * @param null|string $attribute 61 * @param string $value 62 * @param bool $useDnAttributes 63 */ 64 public function __construct(?string $matchingRule, ?string $attribute, string $value, bool $useDnAttributes = false) 65 { 66 $this->matchingRule = $matchingRule; 67 $this->attribute = $attribute; 68 $this->value = $value; 69 $this->useDnAttributes = $useDnAttributes; 70 } 71 72 /** 73 * @return null|string 74 */ 75 public function getAttribute(): ?string 76 { 77 return $this->attribute; 78 } 79 80 /** 81 * @param null|string $attribute 82 * @return $this 83 */ 84 public function setAttribute(?string $attribute) 85 { 86 $this->attribute = $attribute; 87 88 return $this; 89 } 90 91 /** 92 * @return null|string 93 */ 94 public function getMatchingRule(): ?string 95 { 96 return $this->matchingRule; 97 } 98 99 /** 100 * @param null|string $matchingRule 101 * @return $this 102 */ 103 public function setMatchingRule(?string $matchingRule) 104 { 105 $this->matchingRule = $matchingRule; 106 107 return $this; 108 } 109 110 /** 111 * @return string 112 */ 113 public function getValue(): string 114 { 115 return $this->value; 116 } 117 118 /** 119 * @param string $value 120 * @return $this 121 */ 122 public function setValue(string $value) 123 { 124 $this->value = $value; 125 126 return $this; 127 } 128 129 /** 130 * @return bool 131 */ 132 public function getUseDnAttributes(): bool 133 { 134 return $this->useDnAttributes; 135 } 136 137 /** 138 * @param bool $useDnAttributes 139 * @return $this 140 */ 141 public function setUseDnAttributes(bool $useDnAttributes) 142 { 143 $this->useDnAttributes = $useDnAttributes; 144 145 return $this; 146 } 147 148 /** 149 * {@inheritdoc} 150 */ 151 public function toAsn1(): AbstractType 152 { 153 /** @var \FreeDSx\Asn1\Type\SequenceType $matchingRule */ 154 $matchingRule = Asn1::context(self::CHOICE_TAG, Asn1::sequence()); 155 156 if ($this->matchingRule !== null) { 157 $matchingRule->addChild(Asn1::context(1, Asn1::octetString($this->matchingRule))); 158 } 159 if ($this->attribute !== null) { 160 $matchingRule->addChild(Asn1::context(2, Asn1::octetString($this->attribute))); 161 } 162 $matchingRule->addChild(Asn1::context(3, Asn1::octetString($this->value))); 163 $matchingRule->addChild(Asn1::context(4, Asn1::boolean($this->useDnAttributes))); 164 165 return $matchingRule; 166 } 167 168 /** 169 * {@inheritdoc} 170 */ 171 public function toString(): string 172 { 173 $filter = ''; 174 if ($this->attribute !== null) { 175 $filter = $this->attribute; 176 } 177 if ($this->matchingRule !== null) { 178 $filter .= ':' . $this->matchingRule; 179 } 180 if ($this->useDnAttributes) { 181 $filter .= ':dn'; 182 } 183 184 return self::PAREN_LEFT . $filter . self::FILTER_EXT . Attribute::escape($this->value) . self::PAREN_RIGHT; 185 } 186 187 /** 188 * @return string 189 */ 190 public function __toString() 191 { 192 return $this->toString(); 193 } 194 195 /** 196 * {@inheritdoc} 197 */ 198 public static function fromAsn1(AbstractType $type) 199 { 200 $type = $type instanceof IncompleteType ? (new LdapEncoder())->complete($type, AbstractType::TAG_TYPE_SEQUENCE) : $type; 201 if (!($type instanceof SequenceType && (count($type) >= 1 && count($type) <= 4))) { 202 throw new ProtocolException('The matching rule filter is malformed'); 203 } 204 $matchingRule = null; 205 $matchingType = null; 206 $matchValue = null; 207 $useDnAttr = null; 208 209 foreach ($type->getChildren() as $child) { 210 if ($child->getTagClass() !== AbstractType::TAG_CLASS_CONTEXT_SPECIFIC) { 211 continue; 212 } 213 if ($child->getTagNumber() === 1) { 214 $matchingRule = $child; 215 } elseif ($child->getTagNumber() === 2) { 216 $matchingType = $child; 217 } elseif ($child->getTagNumber() === 3) { 218 $matchValue = $child; 219 } elseif ($child->getTagNumber() === 4) { 220 $useDnAttr = $child; 221 } 222 } 223 if (!$matchValue instanceof OctetStringType) { 224 throw new ProtocolException('The matching rule filter is malformed.'); 225 } 226 if ($matchingRule !== null && !$matchingRule instanceof OctetStringType) { 227 throw new ProtocolException('The matching rule filter is malformed.'); 228 } 229 if ($matchingType !== null && !$matchingType instanceof OctetStringType) { 230 throw new ProtocolException('The matching rule filter is malformed.'); 231 } 232 if ($useDnAttr !== null && !$useDnAttr instanceof BooleanType) { 233 throw new ProtocolException('The matching rule filter is malformed.'); 234 } 235 $matchingRule = ($matchingRule !== null) ? $matchingRule->getValue() : null; 236 $matchingType = ($matchingType !== null) ? $matchingType->getValue() : null; 237 $useDnAttr = ($useDnAttr !== null) ? $useDnAttr->getValue() : false; 238 239 return new self( 240 $matchingRule, 241 $matchingType, 242 $matchValue->getValue(), 243 $useDnAttr 244 ); 245 } 246} 247