Passed
Push — develop ( de7ad2...74fd77 )
by Fabian
06:12 queued 02:00
created

Sasl::createOptionsObject()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 35
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 22
nc 12
nop 1
dl 0
loc 35
ccs 16
cts 16
cp 1
crap 7
rs 8.6346
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Sasl library.
5
 *
6
 * Copyright (c) 2002-2003 Richard Heyes,
7
 *               2014-2023 Fabian Grutschus
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 *
14
 * o Redistributions of source code must retain the above copyright
15
 *   notice, this list of conditions and the following disclaimer.
16
 * o Redistributions in binary form must reproduce the above copyright
17
 *   notice, this list of conditions and the following disclaimer in the
18
 *   documentation and/or other materials provided with the distribution.|
19
 * o The names of the authors may not be used to endorse or promote
20
 *   products derived from this software without specific prior written
21
 *   permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
 *
35
 * @author Richard Heyes <[email protected]>
36
 */
37
38
namespace Fabiang\Sasl;
39
40
use Fabiang\Sasl\Exception\InvalidArgumentException;
41
use Fabiang\Sasl\Options\DowngradeProtectionOptions;
42
43
/**
44
 * Client implementation of various SASL mechanisms
45
 *
46
 * @author Richard Heyes <[email protected]>
47
 */
48
class Sasl
49
{
50
    /**
51
     * Known authentication mechanisms classes.
52
     *
53
     * @var array
54
     */
55
    protected $mechanisms = array(
56
        'anonymous' => 'Fabiang\\Sasl\Authentication\\Anonymous',
57
        'login'     => 'Fabiang\\Sasl\Authentication\\Login',
58
        'plain'     => 'Fabiang\\Sasl\Authentication\\Plain',
59
        'external'  => 'Fabiang\\Sasl\Authentication\\External',
60
        'crammd5'   => 'Fabiang\\Sasl\Authentication\\CramMD5',
61
        'digestmd5' => 'Fabiang\\Sasl\Authentication\\DigestMD5',
62
    );
63
64
    /**
65
     * Factory class. Returns an object of the request
66
     * type.
67
     *
68
     * @param string $type  One of: Anonymous
69
     *                              Plain
70
     *                              CramMD5
71
     *                              DigestMD5
72
     *                              SCRAM-* (any mechanism of the SCRAM family)
73
     *                      Types are not case sensitive
74
     * @param Options|array Options for authentication
75
     * @return \Fabiang\Sasl\Authentication\AuthenticationInterface
76 48
     */
77
    public function factory($type, $options = array())
78 48
    {
79 48
        $className = null;
80 48
        $parameter = null;
81 48
        $matches   = array();
82
        $options   = $this->createOptionsObject($options);
83 46
84
        $formatedType = strtolower(str_replace('-', '', $type));
85 46
86 32
        if (isset($this->mechanisms[$formatedType])) {
87 14
            $className = $this->mechanisms[$formatedType];
88 12
        } elseif (preg_match('/^scram(?<algo>.{1,9})$/i', $formatedType, $matches)) {
89 12
            $className = 'Fabiang\\Sasl\Authentication\\SCRAM';
90
            $parameter = $matches['algo'];
91
        }
92 46
93 2
        if (null === $className) {
94
            throw new InvalidArgumentException("Invalid SASL mechanism type '$type'");
95
        }
96 44
97
        return new $className($options, $parameter);
98
    }
99
100
    /**
101
     * @param Options|array $options
102
     * @return \Fabiang\Sasl\Options
103
     * @throws InvalidArgumentException
104
     */
105 26
    private function createOptionsObject($options)
106
    {
107 26
        $optionsObject = $options;
108
109 26
        if (is_array($options)) {
110 22
            $downgradeProtectOptions = null;
111 22
            if (isset($options['downgrade_protection'])) {
112 22
                $dpo = $options['downgrade_protection'];
113 22
114 22
                $allowedMechanisms = isset($dpo['allowed_mechanisms']) ? $dpo['allowed_mechanisms'] : array();
115 22
                $allowedChannelBindings = isset($dpo['allowed_channel_bindings']) ?
116
                    $dpo['allowed_channel_bindings'] : array();
117
118
                $downgradeProtectOptions = new DowngradeProtectionOptions($allowedMechanisms, $allowedChannelBindings);
119 26
            }
120 2
121
            $optionsObject = new Options(
122 2
                $this->checkEmpty($options, 'authcid'),
0 ignored issues
show
Bug introduced by
It seems like $options can also be of type Fabiang\Sasl\Options; however, parameter $array of Fabiang\Sasl\Sasl::checkEmpty() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
                $this->checkEmpty(/** @scrutinizer ignore-type */ $options, 'authcid'),
Loading history...
123
                $this->checkEmpty($options, 'secret'),
124
                $this->checkEmpty($options, 'authzid'),
125
                $this->checkEmpty($options, 'service'),
126
                $this->checkEmpty($options, 'hostname'),
127 24
                $downgradeProtectOptions
128
            );
129
        }
130
131
        if (!($optionsObject instanceof Options)) {
132
            throw new InvalidArgumentException(
133
                'Invalid options passed. Argument must be either of type "Fabiang\Sasl\Options" or "array", "'
134
                . (is_object($options) ? get_class($options) : gettype($options))
135
                . '" given.'
136 42
            );
137
        }
138 42
139 42
        return $optionsObject;
140
    }
141
142 2
    /**
143
     * @param array  $array
144
     * @param string $key
145
     * @return mixed
146
     */
147
    private function checkEmpty(array $array, $key)
148
    {
149
        if (isset($array[$key])) {
150
            return $array[$key];
151
        }
152
153
        return null;
154
    }
155
}
156