Passed
Push — develop ( 046a3b...2fdc71 )
by Fabian
02:29
created

Sasl::factory()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 14
nc 6
nop 2
dl 0
loc 22
ccs 15
cts 15
cp 1
crap 4
rs 9.7998
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * Sasl library.
5
 *
6
 * Copyright (c) 2002-2003 Richard Heyes,
7
 *               2014-2021 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
42
/**
43
 * Client implementation of various SASL mechanisms
44
 *
45
 * @author Richard Heyes <[email protected]>
46
 */
47
class Sasl
48
{
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 Authentication\AuthenticationInterface
76
     */
77 48
    public function factory($type, $options = array())
78
    {
79 48
        $className = null;
80 48
        $parameter = null;
81 48
        $matches   = array();
82 48
        $options   = $this->createOptionsObject($options);
83
84 46
        $formatedType = strtolower(str_replace('-', '', $type));
85
86 46
        if (isset($this->mechanisms[$formatedType])) {
87 32
            $className = $this->mechanisms[$formatedType];
88 14
        } elseif (preg_match('/^scram(?<algo>.{1,9})$/i', $formatedType, $matches)) {
89 12
            $className = 'Fabiang\\Sasl\Authentication\\SCRAM';
90 12
            $parameter = $matches['algo'];
91
        }
92
93 46
        if (null === $className) {
94 2
            throw new InvalidArgumentException("Invalid SASL mechanism type '$type'");
95
        }
96
97 44
        $object = new $className($options, $parameter);
98 44
        return $object;
99
    }
100
101
    /**
102
     *
103
     * @param Options|array $options
104
     * @return \Fabiang\Sasl\Options
105
     * @throws InvalidArgumentException
106
     */
107 26
    private function createOptionsObject($options)
108
    {
109 26
        $optionsObject = $options;
110
111 26
        if (is_array($options)) {
112 22
            $optionsObject = new Options(
113 22
                $this->checkEmpty($options, 'authcid'),
114 22
                $this->checkEmpty($options, 'secret'),
115 22
                $this->checkEmpty($options, 'authzid'),
116 22
                $this->checkEmpty($options, 'service'),
117 22
                $this->checkEmpty($options, 'hostname')
118
            );
119
        }
120
121 26
        if (!($optionsObject instanceof Options)) {
122 2
            throw new InvalidArgumentException(
123
                'Invalid options passed. Argument must be either of type "Fabiang\Sasl\Options" or "array", "'
124 2
                . (is_object($options) ? get_class($options) : gettype($options))
125
                . '" given.'
126
            );
127
        }
128
129 24
        return $optionsObject;
130
    }
131
132
    /**
133
     *
134
     * @param array  $array
135
     * @param string $key
136
     * @return mixed
137
     */
138 42
    private function checkEmpty(array $array, $key)
139
    {
140 42
        if (isset($array[$key])) {
141 42
            return $array[$key];
142
        }
143
144 2
        return null;
145
    }
146
}
147