10b3fd2d3SAndreas Gohr<?php 2*dad993c5SAndreas Gohr 30b3fd2d3SAndreas Gohr/** 40b3fd2d3SAndreas Gohr * This file is part of the FreeDSx LDAP package. 50b3fd2d3SAndreas Gohr * 60b3fd2d3SAndreas Gohr * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 70b3fd2d3SAndreas Gohr * 80b3fd2d3SAndreas Gohr * For the full copyright and license information, please view the LICENSE 90b3fd2d3SAndreas Gohr * file that was distributed with this source code. 100b3fd2d3SAndreas Gohr */ 110b3fd2d3SAndreas Gohr 120b3fd2d3SAndreas Gohrnamespace FreeDSx\Ldap\Control; 130b3fd2d3SAndreas Gohr 140b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Asn1; 15*dad993c5SAndreas Gohruse FreeDSx\Asn1\Exception\EncoderException; 16*dad993c5SAndreas Gohruse FreeDSx\Asn1\Exception\PartialPduException; 170b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Type\AbstractType; 180b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Type\BooleanType; 190b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Type\OctetStringType; 200b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Type\SequenceType; 210b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Exception\ProtocolException; 220b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Protocol\LdapEncoder; 230b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Protocol\ProtocolElementInterface; 24*dad993c5SAndreas Gohruse function count; 250b3fd2d3SAndreas Gohr 260b3fd2d3SAndreas Gohr/** 270b3fd2d3SAndreas Gohr * Represents a control. RFC 4511, 4.1.11 280b3fd2d3SAndreas Gohr * 290b3fd2d3SAndreas Gohr * Control ::= SEQUENCE { 300b3fd2d3SAndreas Gohr * controlType LDAPOID, 310b3fd2d3SAndreas Gohr * criticality BOOLEAN DEFAULT FALSE, 320b3fd2d3SAndreas Gohr * controlValue OCTET STRING OPTIONAL } 330b3fd2d3SAndreas Gohr * 340b3fd2d3SAndreas Gohr * @author Chad Sikorra <Chad.Sikorra@gmail.com> 350b3fd2d3SAndreas Gohr */ 360b3fd2d3SAndreas Gohrclass Control implements ProtocolElementInterface 370b3fd2d3SAndreas Gohr{ 380b3fd2d3SAndreas Gohr public const OID_DIR_SYNC = '1.2.840.113556.1.4.841'; 390b3fd2d3SAndreas Gohr 400b3fd2d3SAndreas Gohr public const OID_EXPECTED_ENTRY_COUNT = '1.2.840.113556.1.4.2211'; 410b3fd2d3SAndreas Gohr 420b3fd2d3SAndreas Gohr public const OID_EXTENDED_DN = '1.2.840.113556.1.4.529'; 430b3fd2d3SAndreas Gohr 440b3fd2d3SAndreas Gohr public const OID_PAGING = '1.2.840.113556.1.4.319'; 450b3fd2d3SAndreas Gohr 460b3fd2d3SAndreas Gohr public const OID_POLICY_HINTS = '1.2.840.113556.1.4.2239'; 470b3fd2d3SAndreas Gohr 480b3fd2d3SAndreas Gohr public const OID_PWD_POLICY = '1.3.6.1.4.1.42.2.27.8.5.1'; 490b3fd2d3SAndreas Gohr 500b3fd2d3SAndreas Gohr public const OID_SET_OWNER = '1.2.840.113556.1.4.2255'; 510b3fd2d3SAndreas Gohr 520b3fd2d3SAndreas Gohr public const OID_SD_FLAGS = '1.2.840.113556.1.4.801'; 530b3fd2d3SAndreas Gohr 540b3fd2d3SAndreas Gohr public const OID_SHOW_DELETED = '1.2.840.113556.1.4.417'; 550b3fd2d3SAndreas Gohr 560b3fd2d3SAndreas Gohr public const OID_SHOW_RECYCLED = '1.2.840.113556.1.4.2064'; 570b3fd2d3SAndreas Gohr 580b3fd2d3SAndreas Gohr public const OID_SUBTREE_DELETE = '1.2.840.113556.1.4.805'; 590b3fd2d3SAndreas Gohr 600b3fd2d3SAndreas Gohr public const OID_SORTING = '1.2.840.113556.1.4.473'; 610b3fd2d3SAndreas Gohr 620b3fd2d3SAndreas Gohr public const OID_SORTING_RESPONSE = '1.2.840.113556.1.4.474'; 630b3fd2d3SAndreas Gohr 640b3fd2d3SAndreas Gohr public const OID_VLV = '2.16.840.1.113730.3.4.9'; 650b3fd2d3SAndreas Gohr 660b3fd2d3SAndreas Gohr public const OID_VLV_RESPONSE = '2.16.840.1.113730.3.4.10'; 670b3fd2d3SAndreas Gohr 680b3fd2d3SAndreas Gohr /** 690b3fd2d3SAndreas Gohr * @var string 700b3fd2d3SAndreas Gohr */ 710b3fd2d3SAndreas Gohr protected $controlType; 720b3fd2d3SAndreas Gohr 730b3fd2d3SAndreas Gohr /** 740b3fd2d3SAndreas Gohr * @var bool 750b3fd2d3SAndreas Gohr */ 760b3fd2d3SAndreas Gohr protected $criticality; 770b3fd2d3SAndreas Gohr 780b3fd2d3SAndreas Gohr /** 790b3fd2d3SAndreas Gohr * @var AbstractType|ProtocolElementInterface|string|null 800b3fd2d3SAndreas Gohr */ 810b3fd2d3SAndreas Gohr protected $controlValue; 820b3fd2d3SAndreas Gohr 830b3fd2d3SAndreas Gohr /** 840b3fd2d3SAndreas Gohr * @param string $controlType 850b3fd2d3SAndreas Gohr * @param bool $criticality 860b3fd2d3SAndreas Gohr * @param null|mixed $controlValue 870b3fd2d3SAndreas Gohr */ 880b3fd2d3SAndreas Gohr public function __construct(string $controlType, bool $criticality = false, $controlValue = null) 890b3fd2d3SAndreas Gohr { 900b3fd2d3SAndreas Gohr $this->controlType = $controlType; 910b3fd2d3SAndreas Gohr $this->criticality = $criticality; 920b3fd2d3SAndreas Gohr $this->controlValue = $controlValue; 930b3fd2d3SAndreas Gohr } 940b3fd2d3SAndreas Gohr 950b3fd2d3SAndreas Gohr /** 960b3fd2d3SAndreas Gohr * @param string $oid 970b3fd2d3SAndreas Gohr * @return $this 980b3fd2d3SAndreas Gohr */ 990b3fd2d3SAndreas Gohr public function setTypeOid(string $oid) 1000b3fd2d3SAndreas Gohr { 1010b3fd2d3SAndreas Gohr $this->controlType = $oid; 1020b3fd2d3SAndreas Gohr 1030b3fd2d3SAndreas Gohr return $this; 1040b3fd2d3SAndreas Gohr } 1050b3fd2d3SAndreas Gohr 1060b3fd2d3SAndreas Gohr /** 1070b3fd2d3SAndreas Gohr * @return string 1080b3fd2d3SAndreas Gohr */ 1090b3fd2d3SAndreas Gohr public function getTypeOid(): string 1100b3fd2d3SAndreas Gohr { 1110b3fd2d3SAndreas Gohr return $this->controlType; 1120b3fd2d3SAndreas Gohr } 1130b3fd2d3SAndreas Gohr 1140b3fd2d3SAndreas Gohr /** 1150b3fd2d3SAndreas Gohr * @param bool $criticality 1160b3fd2d3SAndreas Gohr * @return $this 1170b3fd2d3SAndreas Gohr */ 1180b3fd2d3SAndreas Gohr public function setCriticality(bool $criticality) 1190b3fd2d3SAndreas Gohr { 1200b3fd2d3SAndreas Gohr $this->criticality = $criticality; 1210b3fd2d3SAndreas Gohr 1220b3fd2d3SAndreas Gohr return $this; 1230b3fd2d3SAndreas Gohr } 1240b3fd2d3SAndreas Gohr 1250b3fd2d3SAndreas Gohr /** 1260b3fd2d3SAndreas Gohr * @return bool 1270b3fd2d3SAndreas Gohr */ 1280b3fd2d3SAndreas Gohr public function getCriticality(): bool 1290b3fd2d3SAndreas Gohr { 1300b3fd2d3SAndreas Gohr return $this->criticality; 1310b3fd2d3SAndreas Gohr } 1320b3fd2d3SAndreas Gohr 1330b3fd2d3SAndreas Gohr /** 1340b3fd2d3SAndreas Gohr * @param AbstractType|ProtocolElementInterface|string|null $controlValue 1350b3fd2d3SAndreas Gohr * @return $this 1360b3fd2d3SAndreas Gohr */ 1370b3fd2d3SAndreas Gohr public function setValue($controlValue) 1380b3fd2d3SAndreas Gohr { 1390b3fd2d3SAndreas Gohr $this->controlValue = $controlValue; 1400b3fd2d3SAndreas Gohr 1410b3fd2d3SAndreas Gohr return $this; 1420b3fd2d3SAndreas Gohr } 1430b3fd2d3SAndreas Gohr 1440b3fd2d3SAndreas Gohr /** 1450b3fd2d3SAndreas Gohr * @return AbstractType|ProtocolElementInterface|string|null 1460b3fd2d3SAndreas Gohr */ 1470b3fd2d3SAndreas Gohr public function getValue() 1480b3fd2d3SAndreas Gohr { 1490b3fd2d3SAndreas Gohr return $this->controlValue; 1500b3fd2d3SAndreas Gohr } 1510b3fd2d3SAndreas Gohr 1520b3fd2d3SAndreas Gohr /** 153*dad993c5SAndreas Gohr * @return AbstractType 154*dad993c5SAndreas Gohr * @throws EncoderException 1550b3fd2d3SAndreas Gohr */ 1560b3fd2d3SAndreas Gohr public function toAsn1(): AbstractType 1570b3fd2d3SAndreas Gohr { 1580b3fd2d3SAndreas Gohr $asn1 = Asn1::sequence( 1590b3fd2d3SAndreas Gohr Asn1::octetString($this->controlType), 1600b3fd2d3SAndreas Gohr Asn1::boolean($this->criticality) 1610b3fd2d3SAndreas Gohr ); 1620b3fd2d3SAndreas Gohr 1630b3fd2d3SAndreas Gohr if ($this->controlValue !== null) { 1640b3fd2d3SAndreas Gohr $encoder = new LdapEncoder(); 1650b3fd2d3SAndreas Gohr if ($this->controlValue instanceof AbstractType) { 1660b3fd2d3SAndreas Gohr $value = $encoder->encode($this->controlValue); 1670b3fd2d3SAndreas Gohr } elseif ($this->controlValue instanceof ProtocolElementInterface) { 1680b3fd2d3SAndreas Gohr $value = $encoder->encode($this->controlValue->toAsn1()); 1690b3fd2d3SAndreas Gohr } else { 1700b3fd2d3SAndreas Gohr $value = $this->controlValue; 1710b3fd2d3SAndreas Gohr } 1720b3fd2d3SAndreas Gohr $asn1->addChild(Asn1::octetString($value)); 1730b3fd2d3SAndreas Gohr } 1740b3fd2d3SAndreas Gohr 1750b3fd2d3SAndreas Gohr return $asn1; 1760b3fd2d3SAndreas Gohr } 1770b3fd2d3SAndreas Gohr 1780b3fd2d3SAndreas Gohr /** 1790b3fd2d3SAndreas Gohr * @return string 1800b3fd2d3SAndreas Gohr */ 1810b3fd2d3SAndreas Gohr public function __toString() 1820b3fd2d3SAndreas Gohr { 1830b3fd2d3SAndreas Gohr return $this->controlType; 1840b3fd2d3SAndreas Gohr } 1850b3fd2d3SAndreas Gohr 1860b3fd2d3SAndreas Gohr /** 187*dad993c5SAndreas Gohr * {@inheritDoc} 188*dad993c5SAndreas Gohr * @return static|ProtocolElementInterface 1890b3fd2d3SAndreas Gohr */ 1900b3fd2d3SAndreas Gohr public static function fromAsn1(AbstractType $type) 1910b3fd2d3SAndreas Gohr { 1920b3fd2d3SAndreas Gohr if (!$type instanceof SequenceType) { 1930b3fd2d3SAndreas Gohr throw new ProtocolException(sprintf( 1940b3fd2d3SAndreas Gohr 'Protocol encoding issue. Expected a sequence type but received: %s', 1950b3fd2d3SAndreas Gohr get_class($type) 1960b3fd2d3SAndreas Gohr )); 1970b3fd2d3SAndreas Gohr } 1980b3fd2d3SAndreas Gohr 1990b3fd2d3SAndreas Gohr return new static(...self::parseAsn1ControlValues($type)); 2000b3fd2d3SAndreas Gohr } 2010b3fd2d3SAndreas Gohr 2020b3fd2d3SAndreas Gohr /** 2030b3fd2d3SAndreas Gohr * @param Control $control 2040b3fd2d3SAndreas Gohr * @param AbstractType $type 205*dad993c5SAndreas Gohr * @return self 2060b3fd2d3SAndreas Gohr * @throws ProtocolException 2070b3fd2d3SAndreas Gohr */ 2080b3fd2d3SAndreas Gohr protected static function mergeControlData(Control $control, AbstractType $type) 2090b3fd2d3SAndreas Gohr { 210*dad993c5SAndreas Gohr if (!($type instanceof SequenceType && count($type->getChildren()) <= 3)) { 2110b3fd2d3SAndreas Gohr throw new ProtocolException(sprintf( 2120b3fd2d3SAndreas Gohr 'The received control is malformed. Expected at least 3 sequence values. Received %s.', 2130b3fd2d3SAndreas Gohr count($type->getChildren()) 2140b3fd2d3SAndreas Gohr )); 2150b3fd2d3SAndreas Gohr } 2160b3fd2d3SAndreas Gohr [0 => $control->controlType, 1 => $control->criticality, 2 => $control->controlValue] = self::parseAsn1ControlValues($type); 2170b3fd2d3SAndreas Gohr 2180b3fd2d3SAndreas Gohr return $control; 2190b3fd2d3SAndreas Gohr } 2200b3fd2d3SAndreas Gohr 2210b3fd2d3SAndreas Gohr /** 2220b3fd2d3SAndreas Gohr * @param AbstractType $type 2230b3fd2d3SAndreas Gohr * @return AbstractType 2240b3fd2d3SAndreas Gohr * @throws ProtocolException 225*dad993c5SAndreas Gohr * @throws EncoderException 226*dad993c5SAndreas Gohr * @throws PartialPduException 2270b3fd2d3SAndreas Gohr */ 2280b3fd2d3SAndreas Gohr protected static function decodeEncodedValue(AbstractType $type) 2290b3fd2d3SAndreas Gohr { 2300b3fd2d3SAndreas Gohr if (!$type instanceof SequenceType) { 2310b3fd2d3SAndreas Gohr throw new ProtocolException('The received control is malformed. Unable to get the encoded value.'); 2320b3fd2d3SAndreas Gohr } 2330b3fd2d3SAndreas Gohr 2340b3fd2d3SAndreas Gohr [2 => $value] = self::parseAsn1ControlValues($type); 2350b3fd2d3SAndreas Gohr if ($value === null) { 2360b3fd2d3SAndreas Gohr throw new ProtocolException('The received control is malformed. Unable to get the encoded value.'); 2370b3fd2d3SAndreas Gohr } 2380b3fd2d3SAndreas Gohr 2390b3fd2d3SAndreas Gohr return (new LdapEncoder())->decode($value); 2400b3fd2d3SAndreas Gohr } 2410b3fd2d3SAndreas Gohr 2420b3fd2d3SAndreas Gohr /** 2430b3fd2d3SAndreas Gohr * @param SequenceType $type 2440b3fd2d3SAndreas Gohr * @return array 2450b3fd2d3SAndreas Gohr */ 2460b3fd2d3SAndreas Gohr protected static function parseAsn1ControlValues(SequenceType $type) 2470b3fd2d3SAndreas Gohr { 2480b3fd2d3SAndreas Gohr $oid = null; 2490b3fd2d3SAndreas Gohr $criticality = false; 2500b3fd2d3SAndreas Gohr $value = null; 2510b3fd2d3SAndreas Gohr 2520b3fd2d3SAndreas Gohr /* 2530b3fd2d3SAndreas Gohr * RFC 4511, 4.1.1. States responses should not have criticality set, but not that it must not be set. So do not 2540b3fd2d3SAndreas Gohr * assume the position of the octet string value. Accounts for the additional logic of the checks here. 2550b3fd2d3SAndreas Gohr */ 2560b3fd2d3SAndreas Gohr foreach ($type->getChildren() as $i => $child) { 2570b3fd2d3SAndreas Gohr if ($child->getTagClass() !== AbstractType::TAG_CLASS_UNIVERSAL) { 2580b3fd2d3SAndreas Gohr continue; 2590b3fd2d3SAndreas Gohr } 2600b3fd2d3SAndreas Gohr 2610b3fd2d3SAndreas Gohr if ($i === 0) { 2620b3fd2d3SAndreas Gohr $oid = $child->getValue(); 2630b3fd2d3SAndreas Gohr } elseif ($child instanceof BooleanType) { 2640b3fd2d3SAndreas Gohr $criticality = $child->getValue(); 2650b3fd2d3SAndreas Gohr } elseif ($child instanceof OctetStringType) { 2660b3fd2d3SAndreas Gohr $value = $child->getValue(); 2670b3fd2d3SAndreas Gohr } 2680b3fd2d3SAndreas Gohr } 2690b3fd2d3SAndreas Gohr 2700b3fd2d3SAndreas Gohr return [$oid, $criticality, $value]; 2710b3fd2d3SAndreas Gohr } 2720b3fd2d3SAndreas Gohr} 273