1<?php
2
3/**
4 * This file is part of the FreeDSx SASL 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\Sasl\Challenge;
13
14use FreeDSx\Sasl\Encoder\PlainEncoder;
15use FreeDSx\Sasl\Exception\SaslException;
16use FreeDSx\Sasl\Message;
17use FreeDSx\Sasl\SaslContext;
18
19/**
20 * The PLAIN challenge / response class.
21 *
22 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
23 */
24class PlainChallenge implements ChallengeInterface
25{
26    /**
27     * @var PlainEncoder
28     */
29    protected $encoder;
30
31    /**
32     * @var SaslContext
33     */
34    protected $context;
35
36    public function __construct(bool $isServerMode = false)
37    {
38        $this->encoder = new PlainEncoder();
39        $this->context = new SaslContext();
40        $this->context->setIsServerMode($isServerMode);
41    }
42
43    /**
44     * {@inheritDoc}
45     */
46    public function challenge(?string $received = null, array $options = []): SaslContext
47    {
48        $received = $received === null ? null : $this->encoder->decode($received, $this->context);
49
50        if ($this->context->isServerMode()) {
51            return $this->serverProcess($received, $options);
52        } else {
53            return $this->clientProcess($options);
54        }
55    }
56
57    protected function serverProcess(?Message $message, array $options): SaslContext
58    {
59        if ($message === null) {
60            return $this->context;
61        }
62        if (!(isset($options['validate']) && is_callable($options['validate']))) {
63            throw new SaslException('You must pass a callable validate option to the plain mechanism in server mode.');
64        }
65        $authzId = $message->get('authzid');
66        $authcId = $message->get('authcid');
67        $password = $message->get('password');
68
69        $this->context->setIsComplete(true);
70        $this->context->setIsAuthenticated((bool) $options['validate']($authzId, $authcId, $password));
71
72        return $this->context;
73    }
74
75    protected function clientProcess(array $options): SaslContext
76    {
77        if (!isset($options['username'])) {
78            throw new SaslException('You must supply a username for the PLAIN mechanism.');
79        }
80        if (!isset($options['password'])) {
81            throw new SaslException('You must supply a password for the PLAIN mechanism.');
82        }
83        $message = new Message([
84            'authzid' => $options['username'],
85            'authcid' => $options['username'],
86            'password' => $options['password'],
87        ]);
88        $this->context->setResponse($this->encoder->encode($message, $this->context));
89        $this->context->setIsComplete(true);
90
91        return $this->context;
92    }
93}
94