Completed
Push — master ( a51466...8daf75 )
by Raffael
02:47
created

LdapPreauth::preauth()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 54
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 54
rs 7.8331
c 0
b 0
f 0
cc 7
eloc 30
nc 13
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * Micro
6
 *
7
 * @copyright   Copryright (c) 2012-2017 gyselroth GmbH (https://gyselroth.com)
8
 * @license     GPLv3 https://opensource.org/licenses/GPL-3.0
9
 */
10
11
namespace Micro\Auth\Adapter;
12
13
use \Micro\Auth\Exception;
14
use \Micro\Auth\Adapter\Basic\Ldap;
15
use \Micro\Ldap as LdapServer;
16
17
class LdapPreauth extends Ldap
18
{
19
    /**
20
     * Source networks
21
     *
22
     * @var array
23
     */
24
    protected $source = [];
25
    
26
    
27
    /**
28
     * Key
29
     *
30
     * @var string
31
     */
32
    protected $key;
33
34
35
    /**
36
     * Set options
37
     *
38
     * @param   Iterable $config
39
     * @return  LdapServer
40
     */
41
    public function setOptions(?Iterable $config=null): LdapServer
42
    {
43
        if ($config === null) {
44
            return $this;
45
        }
46
47
        foreach ($config as $option => $value) {
48
            switch ($option) {
49
                case 'source':
50
                    $this->source = $value;
51
                break;
52
                
53
                case 'key':
54
                    if (empty($value)) {
55
                        throw new Exception('empty preauth key is not allowed');
56
                    }
57
58
                    $this->key = $value;
59
                break;
60
            }
61
        }
62
63
        parent::setOptions($config);
64
        return $this;
65
    }
66
67
68
    /**
69
     * Authenticate
70
     *
71
     * @return bool
72
     */
73
    public function authenticate(): bool
74
    {
75
        if (!isset($_SERVER['HTTP_X_PREAUTH'])) {
76
            $this->logger->debug('skip auth adapter ['.get_class($this).'], no http x-preauth header found', [
0 ignored issues
show
Bug introduced by
The property logger does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
77
                'category' => get_class($this)
78
            ]);
79
        
80
            return false;
81
        }
82
83
        $header = $_SERVER['HTTP_X_PREAUTH'];
84
        
85
        if (!empty($header)) {
86
            $this->logger->debug('found http x-preauth header', [
87
                'category' => get_class($this)
88
            ]);
89
            
90
            return $this->preauth($header);
91
        } else {
92
            $this->logger->warning('http x-preauth header contains an empty empt value', [
93
                'category' => get_class($this)
94
            ]);
95
        
96
            return false;
97
        }
98
    }
99
100
101
    /**
102
     * Preauth header
103
     *
104
     * @param   string $value
105
     * @return  bool
106
     */
107
    protected function preauth(string $value): bool
108
    {
109
        $parts   = explode('|', $value);
110
111
        if (count($parts) !== 2) {
112
            $this->logger->warning('invalid header x-preauth value, parts != 2', [
113
                'category' => get_class($this)
114
            ]);
115
116
            return false;
117
        }
118
119
        $found = false;
120
        foreach ($this->source as $ip) {
121
            if ($this->ipInRange($_SERVER['REMOTE_ADDR'], $ip)) {
122
                $this->logger->debug('x-preauth authentication request from known network ['.$_SERVER['REMOTE_ADDR'].']', [
123
                    'category' => get_class($this)
124
                ]);
125
126
                $found = true;
127
                break;
128
            }
129
        }
130
131
        if ($found === false) {
132
            $this->logger->warning('x-preauth authentication request from unknown network ['.$_SERVER['REMOTE_ADDR'].']', [
133
                'category' => get_class($this)
134
            ]);
135
136
            return false;
137
        }
138
139
        $key     = $parts[1];
140
        $account = $parts[0];
141
142
        if (!$this->checkLdapUser($account)) {
143
            return false;
144
        }
145
146
        if ($key !== $this->key) {
147
            $this->logger->warning('invalid x-preauth key value, wrong key?', [
148
                'category' => get_class($this)
149
            ]);
150
        
151
            return false;
152
        } else {
153
            $this->logger->info('valid x-preauth key value found', [
154
                'category' => get_class($this)
155
            ]);
156
        
157
            $this->identity = $account;
0 ignored issues
show
Bug introduced by
The property identity does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
158
            return true;
159
        }
160
    }
161
162
163
    /**
164
     * Check if ldap user does exis
165
     *
166
     * @param   string $username
167
     * @return  bool
168
     */
169
    public function checkLdapUser(string $username): bool
170
    {
171
        $this->connect();
0 ignored issues
show
Bug introduced by
The method connect() does not seem to exist on object<Micro\Auth\Adapter\LdapPreauth>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
172
        $esc_username = ldap_escape($username);
173
        $filter       = sprintf($this->account_filter, $esc_username);
174
        $result       = ldap_search($this->connection, $this->basedn, $filter, ['dn']);
0 ignored issues
show
Bug introduced by
The property connection does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property basedn does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
175
        $entries      = ldap_get_entries($this->connection, $result);
176
177
        if ($entries['count'] === 0) {
178
            $this->logger->warning("user not found with ldap filter [{$filter}]", [
179
                'category' => get_class($this)
180
            ]);
181
182
            return false;
183
        } elseif ($entries['count'] > 1) {
184
            $this->logger->warning("more than one user found with ldap filter [{$filter}]", [
185
                'category' => get_class($this)
186
            ]);
187
    
188
            return false;
189
        }
190
191
        $dn = $entries[0]['dn'];
192
        $this->logger->info("found ldap user [{$dn}] with filter [{$filter}]", [
193
            'category' => get_class($this)
194
        ]);
195
196
        $this->ldap_dn   = $dn;
197
198
        return true;
199
    }
200
201
202
    /**
203
     * Check if a given ip is in a network
204
     *
205
     * @param   string $ip IP to check in IPV4 format eg. 127.0.0.1
206
     * @param   string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
207
     * @return  bool true if the ip is in this range / false if not.
208
     */
209
    protected function ipInRange(string $ip, string $range) : bool
210
    {
211
        if (strpos($range, '/') == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($range, '/') of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
212
            $range .= '/32';
213
        }
214
215
        // $range is in IP/CIDR format eg 127.0.0.1/24
216
        list($range, $netmask) = explode('/', $range, 2);
217
        $range_decimal = ip2long($range);
218
        $ip_decimal = ip2long($ip);
219
        $wildcard_decimal = pow(2, (32 - $netmask)) - 1;
220
        $netmask_decimal = ~ $wildcard_decimal;
221
        return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
222
    }
223
}
224