xref: /plugin/pureldap/vendor/freedsx/ldap/src/FreeDSx/Ldap/Control/Control.php (revision dad993c57a70866aa1db59c43f043769c2eb7ed0)
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