CryptKey::__construct()   B
last analyzed

Complexity

Conditions 11
Paths 16

Size

Total Lines 43
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 16.51

Importance

Changes 5
Bugs 3 Features 0
Metric Value
cc 11
eloc 27
c 5
b 3
f 0
nc 16
nop 3
dl 0
loc 43
ccs 18
cts 28
cp 0.6429
crap 16.51
rs 7.3166

How to fix   Complexity   

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
3
/**
4
 * Cryptography key holder.
5
 *
6
 * @author      Julián Gutiérrez <[email protected]>
7
 * @copyright   Copyright (c) Alex Bilbie
8
 * @license     http://mit-license.org/
9
 *
10
 * @link        https://github.com/thephpleague/oauth2-server
11
 */
12
13
declare(strict_types=1);
14
15
namespace League\OAuth2\Server;
16
17
use LogicException;
18
use OpenSSLAsymmetricKey;
19
20
use function decoct;
21
use function file_get_contents;
22
use function fileperms;
23
use function in_array;
24
use function is_file;
25
use function is_readable;
26
use function openssl_pkey_get_details;
27
use function openssl_pkey_get_private;
28
use function openssl_pkey_get_public;
29
use function sprintf;
30
use function trigger_error;
31
32
class CryptKey implements CryptKeyInterface
33
{
34
    private const FILE_PREFIX = 'file://';
35
36
    /**
37
     * @var string Key contents
38
     */
39
    protected string $keyContents;
40
41
    protected string $keyPath;
42
43 74
    public function __construct(string $keyPath, protected ?string $passPhrase = null, bool $keyPermissionsCheck = true)
44
    {
45 74
        if (str_starts_with($keyPath, self::FILE_PREFIX) === false && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
46 3
            $this->keyContents = $keyPath;
47 3
            $this->keyPath = '';
48
            // There's no file, so no need for permission check.
49 3
            $keyPermissionsCheck = false;
50 71
        } elseif (is_file($keyPath)) {
51 69
            if (str_starts_with($keyPath, self::FILE_PREFIX) === false) {
52 1
                $keyPath = self::FILE_PREFIX . $keyPath;
53
            }
54
55 69
            if (!is_readable($keyPath)) {
56
                throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
57
            }
58
59 69
            $keyContents = file_get_contents($keyPath);
60
61 69
            if ($keyContents === false) {
62
                throw new LogicException('Unable to read key from file ' . $keyPath);
63
            }
64
65 69
            $this->keyContents = $keyContents;
66 69
            $this->keyPath = $keyPath;
67
68 69
            if (!$this->isValidKey($this->keyContents, $this->passPhrase ?? '')) {
69
                throw new LogicException('Unable to read key from file ' . $keyPath);
70
            }
71
        } else {
72 2
            throw new LogicException('Invalid key supplied');
73
        }
74
75 72
        if ($keyPermissionsCheck === true && PHP_OS_FAMILY !== 'Windows') {
76
            // Verify the permissions of the key
77 69
            $keyPathPerms = decoct(fileperms($this->keyPath) & 0777);
78 69
            if (in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
79
                trigger_error(
80
                    sprintf(
81
                        'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
82
                        $this->keyPath,
83
                        $keyPathPerms
84
                    ),
85
                    E_USER_NOTICE
86
                );
87
            }
88
        }
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94 19
    public function getKeyContents(): string
95
    {
96 19
        return $this->keyContents;
97
    }
98
99
    /**
100
     * Validate key contents.
101
     */
102 74
    private function isValidKey(string $contents, string $passPhrase): bool
103
    {
104 74
        $privateKey = openssl_pkey_get_private($contents, $passPhrase);
105
106 74
        $key = $privateKey instanceof OpenSSLAsymmetricKey ? $privateKey : openssl_pkey_get_public($contents);
107
108 74
        if ($key === false) {
0 ignored issues
show
introduced by
The condition $key === false is always false.
Loading history...
109 2
            return false;
110
        }
111
112 73
        $details = openssl_pkey_get_details($key);
113
114 73
        return $details !== false && in_array(
115 73
            $details['type'] ?? -1,
116 73
            [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
117 73
            true
118 73
        );
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 5
    public function getKeyPath(): string
125
    {
126 5
        return $this->keyPath;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132 21
    public function getPassPhrase(): ?string
133
    {
134 21
        return $this->passPhrase;
135
    }
136
}
137