Passed
Push — master ( c84556...38a67b )
by Andrew
02:08
created

CryptKey::__construct()   B

Complexity

Conditions 10
Paths 16

Size

Total Lines 43
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 14.5537

Importance

Changes 6
Bugs 3 Features 0
Metric Value
cc 10
eloc 27
c 6
b 3
f 0
nc 16
nop 3
dl 0
loc 43
ccs 18
cts 28
cp 0.6429
crap 14.5537
rs 7.6666

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