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