Authentication::determineMechanismClass()   B
last analyzed

Complexity

Conditions 5
Paths 9

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 16
cts 16
cp 1
rs 8.439
c 0
b 0
f 0
cc 5
eloc 15
nc 9
nop 0
crap 5
1
<?php
2
3
/**
4
 * Copyright 2014 Fabian Grutschus. All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without modification,
7
 * are permitted provided that the following conditions are met:
8
 *
9
 * 1. Redistributions of source code must retain the above copyright notice, this
10
 *   list of conditions and the following disclaimer.
11
 *
12
 * 2. Redistributions in binary form must reproduce the above copyright notice,
13
 *   this list of conditions and the following disclaimer in the documentation
14
 *   and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 *
27
 * The views and conclusions contained in the software and documentation are those
28
 * of the authors and should not be interpreted as representing official policies,
29
 * either expressed or implied, of the copyright holders.
30
 *
31
 * @author    Fabian Grutschus <[email protected]>
32
 * @copyright 2014 Fabian Grutschus. All rights reserved.
33
 * @license   BSD
34
 * @link      http://github.com/fabiang/xmpp
35
 */
36
37
namespace Fabiang\Xmpp\EventListener\Stream;
38
39
use Fabiang\Xmpp\Event\XMLEvent;
40
use Fabiang\Xmpp\Exception\RuntimeException;
41
use Fabiang\Xmpp\EventListener\Stream\Authentication\AuthenticationInterface;
42
use Fabiang\Xmpp\Exception\Stream\AuthenticationErrorException;
43
use Fabiang\Xmpp\EventListener\AbstractEventListener;
44
use Fabiang\Xmpp\EventListener\BlockingEventListenerInterface;
45
46
/**
47
 * Listener
48
 *
49
 * @package Xmpp\EventListener
50
 */
51
class Authentication extends AbstractEventListener implements BlockingEventListenerInterface
52
{
53
54
    /**
55
     * Listener is blocking.
56
     *
57
     * @var boolean
58
     */
59
    protected $blocking = false;
60
61
    /**
62
     * Collected mechanisms.
63
     *
64
     * @var array
65
     */
66
    protected $mechanisms = [];
67
68
    /**
69
     * {@inheritDoc}
70
     */
71 3
    public function attachEvents()
72
    {
73 3
        $input = $this->getConnection()->getInputStream()->getEventManager();
74 3
        $input->attach('{urn:ietf:params:xml:ns:xmpp-sasl}mechanisms', [$this, 'authenticate']);
75 3
        $input->attach('{urn:ietf:params:xml:ns:xmpp-sasl}mechanism', [$this, 'collectMechanisms']);
76 3
        $input->attach('{urn:ietf:params:xml:ns:xmpp-sasl}failure', [$this, 'failure']);
77 3
        $input->attach('{urn:ietf:params:xml:ns:xmpp-sasl}success', [$this, 'success']);
78 3
    }
79
80
    /**
81
     * Collect authentication machanisms.
82
     *
83
     * @param XMLEvent $event
84
     * @return void
85
     */
86 3
    public function collectMechanisms(XMLEvent $event)
87
    {
88 3
        if ($this->getConnection()->isReady() && false === $this->isAuthenticated()) {
89
            /* @var $element \DOMElement */
90 3
            list($element) = $event->getParameters();
91 3
            $this->blocking = true;
92 3
            if (false === $event->isStartTag()) {
93 3
                $this->mechanisms[] = strtolower($element->nodeValue);
94 3
            }
95 3
        }
96 3
    }
97
98
    /**
99
     * Authenticate after collecting machanisms.
100
     *
101
     * @param XMLEvent $event
102
     * @return void
103
     */
104 9
    public function authenticate(XMLEvent $event)
105
    {
106 9
        if ($this->getConnection()->isReady() && false === $this->isAuthenticated() && false === $event->isStartTag()) {
107 9
            $this->blocking = true;
108
109 9
            $authentication = $this->determineMechanismClass();
110
111 3
            $authentication->setEventManager($this->getEventManager())
112 3
                ->setOptions($this->getOptions())
113 3
                ->attachEvents();
114
115 3
            $this->getConnection()->addListener($authentication);
116 3
            $authentication->authenticate($this->getOptions()->getUsername(), $this->getOptions()->getPassword());
117 3
        }
118 3
    }
119
120
    /**
121
     * Determine mechanismclass from collected mechanisms.
122
     *
123
     * @return AuthenticationInterface
124
     * @throws RuntimeException
125
     */
126 9
    protected function determineMechanismClass()
127
    {
128 9
        $authenticationClass = null;
129
130 9
        $authenticationClasses = $this->getOptions()->getAuthenticationClasses();
131 9
        foreach ($this->mechanisms as $mechanism) {
132 6
            if (array_key_exists($mechanism, $authenticationClasses)) {
133 6
                $authenticationClass = $authenticationClasses[$mechanism];
134 6
                break;
135
            }
136 9
        }
137
138 9
        if (null === $authenticationClass) {
139 3
            throw new RuntimeException('No supportet authentication machanism found.');
140
        }
141
142 6
        $authentication = new $authenticationClass;
143
144 6
        if (!($authentication instanceof AuthenticationInterface)) {
145 3
            $message = 'Authentication class "' . get_class($authentication)
146 3
                . '" is no instanceof  AuthenticationInterface';
147 3
            throw new RuntimeException($message);
148
        }
149
150 3
        return $authentication;
151
    }
152
153
    /**
154
     * Authentication failed.
155
     *
156
     * @param XMLEvent $event
157
     * @throws StreamErrorException
158
     */
159 3
    public function failure(XMLEvent $event)
160
    {
161 3
        if (false === $event->isStartTag()) {
162 3
            $this->blocking = false;
163 3
            throw AuthenticationErrorException::createFromEvent($event);
164
        }
165
    }
166
167
    /**
168
     * Authentication successful.
169
     *
170
     * @param XMLEvent $event
171
     */
172 3
    public function success(XMLEvent $event)
173
    {
174 3
        if (false === $event->isStartTag()) {
175 3
            $this->blocking = false;
176
177 3
            $connection = $this->getConnection();
178 3
            $connection->resetStreams();
179 3
            $connection->connect();
180
181 3
            $this->getOptions()->setAuthenticated(true);
182 3
        }
183 3
    }
184
185
    /**
186
     * {@inheritDoc}
187
     */
188 3
    public function isBlocking()
189
    {
190 3
        return $this->blocking;
191
    }
192
193
    /**
194
     * Get collected mechanisms.
195
     *
196
     * @return array
197
     */
198 3
    public function getMechanisms()
199
    {
200 3
        return $this->mechanisms;
201
    }
202
203
    /**
204
     *
205
     * @return boolean
206
     */
207 3
    protected function isAuthenticated()
208
    {
209 3
        return $this->getOptions()->isAuthenticated();
210
    }
211
}
212