1<?php
2/**
3 * This file is part of the FreeDSx LDAP package.
4 *
5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace FreeDSx\Ldap\Operation\Request;
12
13use FreeDSx\Asn1\Asn1;
14use FreeDSx\Asn1\Type\AbstractType;
15use FreeDSx\Asn1\Type\IntegerType;
16use FreeDSx\Asn1\Type\OctetStringType;
17use FreeDSx\Asn1\Type\SequenceType;
18use FreeDSx\Ldap\Exception\ProtocolException;
19
20/**
21 * Represents a base bind request. RFC 4511, 4.2
22 *
23 * BindRequest ::= [APPLICATION 0] SEQUENCE {
24 *     version                 INTEGER (1 ..  127),
25 *     name                    LDAPDN,
26 *     authentication          AuthenticationChoice }
27 *
28 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
29 */
30abstract class BindRequest implements RequestInterface
31{
32    protected const APP_TAG = 0;
33
34    /**
35     * @var int
36     */
37    protected $version = 3;
38
39    /**
40     * @var string
41     */
42    protected $username;
43
44    /**
45     * @param int $version
46     * @return $this
47     */
48    public function setVersion(int $version)
49    {
50        $this->version = $version;
51
52        return $this;
53    }
54
55    /**
56     * @return int
57     */
58    public function getVersion(): int
59    {
60        return $this->version;
61    }
62
63    /**
64     * @param string $username
65     * @return $this
66     */
67    public function setUsername(string $username)
68    {
69        $this->username = $username;
70
71        return $this;
72    }
73
74    /**
75     * @return string
76     */
77    public function getUsername(): string
78    {
79        return $this->username;
80    }
81
82    /**
83     * {@inheritdoc}
84     */
85    public function toAsn1(): AbstractType
86    {
87        $this->validate();
88
89        return Asn1::application(self::APP_TAG, Asn1::sequence(
90            Asn1::integer($this->version),
91            Asn1::octetString($this->username),
92            $this->getAsn1AuthChoice()
93        ));
94    }
95
96    /**
97     * {@inheritdoc}
98     */
99    public static function fromAsn1(AbstractType $type)
100    {
101        if (!$type instanceof SequenceType) {
102            throw new ProtocolException('The bind request in malformed');
103        }
104        $version = $type->getChild(0);
105        $name = $type->getChild(1);
106        $auth = $type->getChild(2);
107
108        if ($version === null || $name === null || $auth === null) {
109            throw new ProtocolException('The bind request in malformed');
110        }
111        if (!($version instanceof IntegerType && $name instanceof OctetStringType)) {
112            throw new ProtocolException('The bind request in malformed');
113        }
114        $version = $version->getValue();
115        $name = $name->getValue();
116
117        if ($auth->getTagNumber() !== 0) {
118            throw new ProtocolException(sprintf(
119                'Only a simple bind is currently supported, but got: %s',
120                $auth->getTagNumber()
121            ));
122        }
123        $auth = (string) $auth->getValue();
124
125        if ($auth === '') {
126            return new AnonBindRequest($name, $version);
127        } else {
128            return new SimpleBindRequest($name, $auth, $version);
129        }
130    }
131
132    /**
133     * Get the ASN1 AuthenticationChoice for the bind request.
134     *
135     * @return AbstractType
136     */
137    abstract protected function getAsn1AuthChoice(): AbstractType;
138
139    /**
140     * This is called as the request is transformed to ASN1 to be encoded. If the request parameters are not valid
141     * then the method should throw an exception.
142     */
143    abstract protected function validate(): void;
144}
145