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