xref: /plugin/pureldap/vendor/freedsx/ldap/src/FreeDSx/Ldap/Operation/LdapResult.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\Operation;
130b3fd2d3SAndreas Gohr
140b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Asn1;
15*dad993c5SAndreas Gohruse FreeDSx\Asn1\Exception\EncoderException;
160b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Type\AbstractType;
170b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Type\IncompleteType;
180b3fd2d3SAndreas Gohruse FreeDSx\Asn1\Type\SequenceType;
190b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Entry\Dn;
200b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Exception\ProtocolException;
210b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Exception\UrlParseException;
220b3fd2d3SAndreas Gohruse FreeDSx\Ldap\LdapUrl;
230b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Operation\Response\ResponseInterface;
240b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Protocol\LdapEncoder;
25*dad993c5SAndreas Gohruse function array_map;
26*dad993c5SAndreas Gohruse function count;
270b3fd2d3SAndreas Gohr
280b3fd2d3SAndreas Gohr/**
290b3fd2d3SAndreas Gohr * Represents the result of an operation request. RFC 4511, 4.1.9
300b3fd2d3SAndreas Gohr *
310b3fd2d3SAndreas Gohr * LDAPResult ::= SEQUENCE {
320b3fd2d3SAndreas Gohr *     resultCode         ENUMERATED {
330b3fd2d3SAndreas Gohr *         success                      (0),
340b3fd2d3SAndreas Gohr *         operationsError              (1),
350b3fd2d3SAndreas Gohr *         protocolError                (2),
360b3fd2d3SAndreas Gohr *         timeLimitExceeded            (3),
370b3fd2d3SAndreas Gohr *         sizeLimitExceeded            (4),
380b3fd2d3SAndreas Gohr *         compareFalse                 (5),
390b3fd2d3SAndreas Gohr *         compareTrue                  (6),
400b3fd2d3SAndreas Gohr *         authMethodNotSupported       (7),
410b3fd2d3SAndreas Gohr *         strongerAuthRequired         (8),
420b3fd2d3SAndreas Gohr *         -- 9 reserved --
430b3fd2d3SAndreas Gohr *         referral                     (10),
440b3fd2d3SAndreas Gohr *         adminLimitExceeded           (11),
450b3fd2d3SAndreas Gohr *         unavailableCriticalExtension (12),
460b3fd2d3SAndreas Gohr *         confidentialityRequired      (13),
470b3fd2d3SAndreas Gohr *         saslBindInProgress           (14),
480b3fd2d3SAndreas Gohr *         noSuchAttribute              (16),
490b3fd2d3SAndreas Gohr *         undefinedAttributeType       (17),
500b3fd2d3SAndreas Gohr *         inappropriateMatching        (18),
510b3fd2d3SAndreas Gohr *         constraintViolation          (19),
520b3fd2d3SAndreas Gohr *         attributeOrValueExists       (20),
530b3fd2d3SAndreas Gohr *         invalidAttributeSyntax       (21),
540b3fd2d3SAndreas Gohr *         -- 22-31 unused --
550b3fd2d3SAndreas Gohr *         noSuchObject                 (32),
560b3fd2d3SAndreas Gohr *         aliasProblem                 (33),
570b3fd2d3SAndreas Gohr *         invalidDNSyntax              (34),
580b3fd2d3SAndreas Gohr *         -- 35 reserved for undefined isLeaf --
590b3fd2d3SAndreas Gohr *         aliasDereferencingProblem    (36),
600b3fd2d3SAndreas Gohr *         -- 37-47 unused --
610b3fd2d3SAndreas Gohr *         inappropriateAuthentication  (48),
620b3fd2d3SAndreas Gohr *         invalidCredentials           (49),
630b3fd2d3SAndreas Gohr *         insufficientAccessRights     (50),
640b3fd2d3SAndreas Gohr *         busy                         (51),
650b3fd2d3SAndreas Gohr *         unavailable                  (52),
660b3fd2d3SAndreas Gohr *         unwillingToPerform           (53),
670b3fd2d3SAndreas Gohr *         loopDetect                   (54),
680b3fd2d3SAndreas Gohr *         -- 55-63 unused --
690b3fd2d3SAndreas Gohr *         namingViolation              (64),
700b3fd2d3SAndreas Gohr *         objectClassViolation         (65),
710b3fd2d3SAndreas Gohr *         notAllowedOnNonLeaf          (66),
720b3fd2d3SAndreas Gohr *         notAllowedOnRDN              (67),
730b3fd2d3SAndreas Gohr *         entryAlreadyExists           (68),
740b3fd2d3SAndreas Gohr *         objectClassModsProhibited    (69),
750b3fd2d3SAndreas Gohr *         -- 70 reserved for CLDAP --
760b3fd2d3SAndreas Gohr *         affectsMultipleDSAs          (71),
770b3fd2d3SAndreas Gohr *         -- 72-79 unused --
780b3fd2d3SAndreas Gohr *         other                        (80),
790b3fd2d3SAndreas Gohr *         ...  },
800b3fd2d3SAndreas Gohr *     matchedDN          LDAPDN,
810b3fd2d3SAndreas Gohr *     diagnosticMessage  LDAPString,
820b3fd2d3SAndreas Gohr *     referral           [3] Referral OPTIONAL }
830b3fd2d3SAndreas Gohr *
840b3fd2d3SAndreas Gohr * @author Chad Sikorra <Chad.Sikorra@gmail.com>
850b3fd2d3SAndreas Gohr */
860b3fd2d3SAndreas Gohrclass LdapResult implements ResponseInterface
870b3fd2d3SAndreas Gohr{
880b3fd2d3SAndreas Gohr    /**
890b3fd2d3SAndreas Gohr     * @var int
900b3fd2d3SAndreas Gohr     */
910b3fd2d3SAndreas Gohr    protected $tagNumber;
920b3fd2d3SAndreas Gohr
930b3fd2d3SAndreas Gohr    /**
940b3fd2d3SAndreas Gohr     * @var int
950b3fd2d3SAndreas Gohr     */
960b3fd2d3SAndreas Gohr    protected $resultCode;
970b3fd2d3SAndreas Gohr
980b3fd2d3SAndreas Gohr    /**
990b3fd2d3SAndreas Gohr     * @var Dn
1000b3fd2d3SAndreas Gohr     */
1010b3fd2d3SAndreas Gohr    protected $dn;
1020b3fd2d3SAndreas Gohr
1030b3fd2d3SAndreas Gohr    /**
1040b3fd2d3SAndreas Gohr     * @var string
1050b3fd2d3SAndreas Gohr     */
1060b3fd2d3SAndreas Gohr    protected $diagnosticMessage;
1070b3fd2d3SAndreas Gohr
1080b3fd2d3SAndreas Gohr    /**
1090b3fd2d3SAndreas Gohr     * @var LdapUrl[]
1100b3fd2d3SAndreas Gohr     */
1110b3fd2d3SAndreas Gohr    protected $referrals = [];
1120b3fd2d3SAndreas Gohr
1130b3fd2d3SAndreas Gohr    public function __construct(int $resultCode, string $dn = '', string $diagnosticMessage = '', LdapUrl ...$referrals)
1140b3fd2d3SAndreas Gohr    {
1150b3fd2d3SAndreas Gohr        $this->resultCode = $resultCode;
1160b3fd2d3SAndreas Gohr        $this->dn = new Dn($dn);
1170b3fd2d3SAndreas Gohr        $this->diagnosticMessage = $diagnosticMessage;
1180b3fd2d3SAndreas Gohr        $this->referrals = $referrals;
1190b3fd2d3SAndreas Gohr    }
1200b3fd2d3SAndreas Gohr
1210b3fd2d3SAndreas Gohr    /**
1220b3fd2d3SAndreas Gohr     * @return string
1230b3fd2d3SAndreas Gohr     */
1240b3fd2d3SAndreas Gohr    public function getDiagnosticMessage(): string
1250b3fd2d3SAndreas Gohr    {
1260b3fd2d3SAndreas Gohr        return $this->diagnosticMessage;
1270b3fd2d3SAndreas Gohr    }
1280b3fd2d3SAndreas Gohr
1290b3fd2d3SAndreas Gohr    /**
1300b3fd2d3SAndreas Gohr     * @return Dn
1310b3fd2d3SAndreas Gohr     */
1320b3fd2d3SAndreas Gohr    public function getDn(): Dn
1330b3fd2d3SAndreas Gohr    {
1340b3fd2d3SAndreas Gohr        return $this->dn;
1350b3fd2d3SAndreas Gohr    }
1360b3fd2d3SAndreas Gohr
1370b3fd2d3SAndreas Gohr    /**
1380b3fd2d3SAndreas Gohr     * @return LdapUrl[]
1390b3fd2d3SAndreas Gohr     */
1400b3fd2d3SAndreas Gohr    public function getReferrals(): array
1410b3fd2d3SAndreas Gohr    {
1420b3fd2d3SAndreas Gohr        return $this->referrals;
1430b3fd2d3SAndreas Gohr    }
1440b3fd2d3SAndreas Gohr
1450b3fd2d3SAndreas Gohr    /**
1460b3fd2d3SAndreas Gohr     * {@inheritdoc}
1470b3fd2d3SAndreas Gohr     */
1480b3fd2d3SAndreas Gohr    public function getResultCode(): int
1490b3fd2d3SAndreas Gohr    {
1500b3fd2d3SAndreas Gohr        return $this->resultCode;
1510b3fd2d3SAndreas Gohr    }
1520b3fd2d3SAndreas Gohr
1530b3fd2d3SAndreas Gohr    /**
154*dad993c5SAndreas Gohr     * @return AbstractType
155*dad993c5SAndreas Gohr     * @throws ProtocolException
1560b3fd2d3SAndreas Gohr     */
1570b3fd2d3SAndreas Gohr    public function toAsn1(): AbstractType
1580b3fd2d3SAndreas Gohr    {
1590b3fd2d3SAndreas Gohr        $result = Asn1::sequence(
1600b3fd2d3SAndreas Gohr            Asn1::enumerated($this->resultCode),
1610b3fd2d3SAndreas Gohr            Asn1::octetString($this->dn),
1620b3fd2d3SAndreas Gohr            Asn1::octetString($this->diagnosticMessage)
1630b3fd2d3SAndreas Gohr        );
164*dad993c5SAndreas Gohr        if (count($this->referrals) !== 0) {
165*dad993c5SAndreas Gohr            $result->addChild(Asn1::context(3, Asn1::sequence(...array_map(function ($v) {
1660b3fd2d3SAndreas Gohr                return Asn1::octetString($v);
1670b3fd2d3SAndreas Gohr            }, $this->referrals))));
1680b3fd2d3SAndreas Gohr        }
1690b3fd2d3SAndreas Gohr        if ($this->tagNumber === null) {
1700b3fd2d3SAndreas Gohr            throw new ProtocolException(sprintf('You must define the tag number property on %s', get_parent_class()));
1710b3fd2d3SAndreas Gohr        }
1720b3fd2d3SAndreas Gohr
1730b3fd2d3SAndreas Gohr        return Asn1::application($this->tagNumber, $result);
1740b3fd2d3SAndreas Gohr    }
1750b3fd2d3SAndreas Gohr
1760b3fd2d3SAndreas Gohr    /**
177*dad993c5SAndreas Gohr     * {@inheritDoc}
178*dad993c5SAndreas Gohr     * @return self
179*dad993c5SAndreas Gohr     * @throws EncoderException
1800b3fd2d3SAndreas Gohr     */
1810b3fd2d3SAndreas Gohr    public static function fromAsn1(AbstractType $type)
1820b3fd2d3SAndreas Gohr    {
1830b3fd2d3SAndreas Gohr        [$resultCode, $dn, $diagnosticMessage, $referrals] = self::parseResultData($type);
1840b3fd2d3SAndreas Gohr
1850b3fd2d3SAndreas Gohr        return new static($resultCode, $dn, $diagnosticMessage, ...$referrals);
1860b3fd2d3SAndreas Gohr    }
1870b3fd2d3SAndreas Gohr
1880b3fd2d3SAndreas Gohr    /**
1890b3fd2d3SAndreas Gohr     * @param AbstractType $type
1900b3fd2d3SAndreas Gohr     * @return array
191*dad993c5SAndreas Gohr     * @psalm-return array{0: mixed, 1: mixed, 2: mixed, 3: list<LdapUrl>}
1920b3fd2d3SAndreas Gohr     * @throws ProtocolException
193*dad993c5SAndreas Gohr     * @throws EncoderException
1940b3fd2d3SAndreas Gohr     */
1950b3fd2d3SAndreas Gohr    protected static function parseResultData(AbstractType $type)
1960b3fd2d3SAndreas Gohr    {
1970b3fd2d3SAndreas Gohr        if (!$type instanceof SequenceType) {
1980b3fd2d3SAndreas Gohr            throw new ProtocolException('The LDAP result is malformed.');
1990b3fd2d3SAndreas Gohr        }
2000b3fd2d3SAndreas Gohr        $referrals = [];
2010b3fd2d3SAndreas Gohr
2020b3fd2d3SAndreas Gohr        # Somewhat ugly minor optimization. Though it's probably less likely for most setups to get referrals.
2030b3fd2d3SAndreas Gohr        # So only try to iterate them if we possibly have them.
204*dad993c5SAndreas Gohr        $count = count($type->getChildren());
2050b3fd2d3SAndreas Gohr        if ($count > 3) {
2060b3fd2d3SAndreas Gohr            for ($i = 3; $i < $count; $i++) {
2070b3fd2d3SAndreas Gohr                $child = $type->getChild($i);
2080b3fd2d3SAndreas Gohr                if ($child !== null && $child->getTagClass() === AbstractType::TAG_CLASS_CONTEXT_SPECIFIC && $child->getTagNumber() === 3) {
2090b3fd2d3SAndreas Gohr                    if (!$child instanceof IncompleteType) {
2100b3fd2d3SAndreas Gohr                        throw new ProtocolException('The ASN1 structure for the referrals is malformed.');
2110b3fd2d3SAndreas Gohr                    }
2120b3fd2d3SAndreas Gohr                    $child = (new LdapEncoder())->complete($child, AbstractType::TAG_TYPE_SEQUENCE);
2130b3fd2d3SAndreas Gohr                    foreach ($child->getChildren() as $ldapUrl) {
2140b3fd2d3SAndreas Gohr                        try {
2150b3fd2d3SAndreas Gohr                            $referrals[] = LdapUrl::parse($ldapUrl->getValue());
2160b3fd2d3SAndreas Gohr                        } catch (UrlParseException $e) {
2170b3fd2d3SAndreas Gohr                            throw new ProtocolException($e->getMessage());
2180b3fd2d3SAndreas Gohr                        }
2190b3fd2d3SAndreas Gohr                    }
2200b3fd2d3SAndreas Gohr                }
2210b3fd2d3SAndreas Gohr            }
2220b3fd2d3SAndreas Gohr        }
2230b3fd2d3SAndreas Gohr
2240b3fd2d3SAndreas Gohr        $result = $type->getChild(0);
2250b3fd2d3SAndreas Gohr        $dn = $type->getChild(1);
2260b3fd2d3SAndreas Gohr        $diagnostic = $type->getChild(2);
2270b3fd2d3SAndreas Gohr        if ($result === null || $dn === null || $diagnostic === null) {
2280b3fd2d3SAndreas Gohr            throw new ProtocolException('The LDAP result is malformed.');
2290b3fd2d3SAndreas Gohr        }
2300b3fd2d3SAndreas Gohr
2310b3fd2d3SAndreas Gohr        return [
2320b3fd2d3SAndreas Gohr            $result->getValue(),
2330b3fd2d3SAndreas Gohr            $dn->getValue(),
2340b3fd2d3SAndreas Gohr            $diagnostic->getValue(),
2350b3fd2d3SAndreas Gohr            $referrals
2360b3fd2d3SAndreas Gohr        ];
2370b3fd2d3SAndreas Gohr    }
2380b3fd2d3SAndreas Gohr}
239