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\Protocol\ServerProtocolHandler;
13
14use FreeDSx\Asn1\Exception\EncoderException;
15use FreeDSx\Ldap\Control\Control;
16use FreeDSx\Ldap\Entry\Entry;
17use FreeDSx\Ldap\Operation\Request\ExtendedRequest;
18use FreeDSx\Ldap\Operation\Request\SearchRequest;
19use FreeDSx\Ldap\Operation\Response\SearchResultDone;
20use FreeDSx\Ldap\Operation\Response\SearchResultEntry;
21use FreeDSx\Ldap\Operation\ResultCode;
22use FreeDSx\Ldap\Protocol\LdapMessageRequest;
23use FreeDSx\Ldap\Protocol\LdapMessageResponse;
24use FreeDSx\Ldap\Protocol\Queue\ServerQueue;
25use FreeDSx\Ldap\Server\RequestContext;
26use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface;
27use FreeDSx\Ldap\Server\RequestHandler\RootDseHandlerInterface;
28use FreeDSx\Ldap\Server\Token\TokenInterface;
29use function count;
30
31/**
32 * Handles RootDSE based search requests.
33 *
34 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
35 */
36class ServerRootDseHandler implements ServerProtocolHandlerInterface
37{
38    /**
39     * @var RootDseHandlerInterface|null
40     */
41    private $rootDseHandler;
42
43    public function __construct(?RootDseHandlerInterface $rootDseHandler = null)
44    {
45        $this->rootDseHandler = $rootDseHandler;
46    }
47
48    /**
49     * @throws EncoderException
50     */
51    public function handleRequest(
52        LdapMessageRequest $message,
53        TokenInterface $token,
54        RequestHandlerInterface $dispatcher,
55        ServerQueue $queue,
56        array $options
57    ): void {
58        $entry = Entry::fromArray('', [
59            'namingContexts' => $options['dse_naming_contexts'] ?? '',
60            'supportedExtension' => [
61                ExtendedRequest::OID_WHOAMI,
62            ],
63            'supportedLDAPVersion' => ['3'],
64            'vendorName' => $options['dse_vendor_name'] ?? '',
65        ]);
66        if (isset($options['ssl_cert'])) {
67            $entry->add(
68                'supportedExtension',
69                ExtendedRequest::OID_START_TLS
70            );
71        }
72        if (isset($options['paging_handler'])) {
73            $entry->add(
74                'supportedControl',
75                Control::OID_PAGING
76            );
77        }
78        if (isset($options['vendor_version'])) {
79            $entry->set('vendorVersion', $options['vendor_version']);
80        }
81        if (isset($options['alt_server'])) {
82            $entry->set('altServer', $options['alt_server']);
83        }
84
85        /** @var SearchRequest $request */
86        $request = $message->getRequest();
87        $this->filterEntryAttributes($request, $entry);
88
89        if ($this->rootDseHandler) {
90            $entry = $this->rootDseHandler->rootDse(
91                new RequestContext($message->controls(), $token),
92                $request,
93                $entry
94            );
95        }
96
97        $queue->sendMessage(
98            new LdapMessageResponse(
99                $message->getMessageId(),
100                new SearchResultEntry($entry)
101            ),
102            new LdapMessageResponse(
103                $message->getMessageId(),
104                new SearchResultDone(ResultCode::SUCCESS)
105            )
106        );
107    }
108
109    /**
110     * Filters attributes from an entry to return only what was requested.
111     */
112    protected function filterEntryAttributes(SearchRequest $request, Entry $entry): void
113    {
114        if (count($request->getAttributes()) !== 0) {
115            foreach ($entry->getAttributes() as $dseAttr) {
116                $found = false;
117                foreach ($request->getAttributes() as $attribute) {
118                    if ($attribute->equals($dseAttr)) {
119                        $found = true;
120                        break;
121                    }
122                }
123                if ($found === true && $request->getAttributesOnly()) {
124                    $dseAttr->reset();
125                }
126                if ($found === false) {
127                    $entry->reset($dseAttr);
128                    $entry->changes()->reset();
129                }
130            }
131        }
132        if ($request->getAttributesOnly()) {
133            foreach ($entry->getAttributes() as $attribute) {
134                $attribute->reset();
135            }
136        }
137    }
138}
139