Passed
Push — master ( e88f89...2c698e )
by Andrew
13:32 queued 54s
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 5
Bugs 3 Features 0
Metric Value
cc 10
eloc 27
c 5
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 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 73
    public function __construct(string $keyPath, protected ?string $passPhrase = null, bool $keyPermissionsCheck = true)
44
    {
45 73
        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 70
        } elseif (is_file($keyPath)) {
51 68
            if (str_starts_with($keyPath, self::FILE_PREFIX) === false) {
52 1
                $keyPath = self::FILE_PREFIX . $keyPath;
53
            }
54
55 68
            if (!is_readable($keyPath)) {
56
                throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
57
            }
58
59 68
            $keyContents = file_get_contents($keyPath);
60
61 68
            if ($keyContents === false) {
62
                throw new LogicException('Unable to read key from file ' . $keyPath);
63
            }
64
65 68
            $this->keyContents = $keyContents;
66 68
            $this->keyPath = $keyPath;
67
68 68
            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 71
        if ($keyPermissionsCheck === true) {
76
            // Verify the permissions of the key
77 68
            $keyPathPerms = decoct(fileperms($this->keyPath) & 0777);
78 68
            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 18
    public function getKeyContents(): string
95
    {
96 18
        return $this->keyContents;
97
    }
98
99
    /**
100
     * Validate key contents.
101
     */
102 73
    private function isValidKey(string $contents, string $passPhrase): bool
103
    {
104 73
        $privateKey = openssl_pkey_get_private($contents, $passPhrase);
105
106 73
        $key = $privateKey instanceof OpenSSLAsymmetricKey ? $privateKey : openssl_pkey_get_public($contents);
107
108 73
        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 72
        $details = openssl_pkey_get_details($key);
113
114 72
        return $details !== false && in_array(
115 72
            $details['type'] ?? -1,
116 72
            [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
117 72
            true
118 72
        );
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 20
    public function getPassPhrase(): ?string
133
    {
134 20
        return $this->passPhrase;
135
    }
136
}
137