Passed
Pull Request — master (#1215)
by
unknown
35:02
created

CryptKey::__construct()   B

Complexity

Conditions 11
Paths 30

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 12.3274

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 24
c 2
b 0
f 0
nc 30
nop 3
dl 0
loc 38
ccs 14
cts 18
cp 0.7778
crap 12.3274
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
 * Cryptography key holder.
4
 *
5
 * @author      Julián Gutiérrez <[email protected]>
6
 * @copyright   Copyright (c) Alex Bilbie
7
 * @license     http://mit-license.org/
8
 *
9
 * @link        https://github.com/thephpleague/oauth2-server
10
 */
11
12
namespace League\OAuth2\Server;
13
14
use LogicException;
15
use RuntimeException;
16
17
class CryptKey
18
{
19
    /** @deprecated left for backward compatibility check */
20
    const RSA_KEY_PATTERN =
21
        '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)\R.*(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\R?$/s';
22
23
    private const FILE_PREFIX = 'file://';
24
25
    /**
26
     * @var string
27
     */
28
    protected $keyPath;
29
30
    /**
31
     * @var null|string
32
     */
33
    protected $passPhrase;
34
35
    /**
36
     * @param string      $keyPath
37 56
     * @param null|string $passPhrase
38
     * @param bool        $keyPermissionsCheck
39 56
     */
40 1
    public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true)
41 55
    {
42 1
        $this->keyPath = $keyPath;
43 1
        $this->passPhrase = $passPhrase;
44
45
        if (\is_file($this->keyPath) && !$this->isFilePath()) {
46
            $this->keyPath = self::FILE_PREFIX . $this->keyPath;
47 55
        }
48 2
49
        if ($this->isFilePath()) {
50
            if (!\file_exists($this->keyPath) || !\is_readable($this->keyPath)) {
51 55
                throw new LogicException(\sprintf('Key path "%s" does not exist or is not readable', $this->keyPath));
52 1
            }
53
54
            $contents = \file_get_contents($this->keyPath);
55 54
        } else {
56
            $contents = $keyPath;
57 54
        }
58 54
59
        if ($this->isValidKey($contents, $this->passPhrase ?? '')) {
60
            if (!$this->isFilePath()) {
61
                $this->keyPath = $this->saveKeyToFile($keyPath);
62
            }
63
        } else {
64
            throw new LogicException('Unable to read key' . ($this->isFilePath() ? " from file $keyPath" : ''));
65
        }
66
67 54
        if ($keyPermissionsCheck === true) {
68 54
            // Verify the permissions of the key
69 54
            $keyPathPerms = \decoct(\fileperms($this->keyPath) & 0777);
70
            if (\in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
71
                \trigger_error(
72
                    \sprintf(
73
                        'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
74
                        $this->keyPath,
75
                        $keyPathPerms
76
                    ),
77
                    E_USER_NOTICE
78 1
                );
79
            }
80 1
        }
81 1
    }
82
83 1
    /**
84
     * @param string $key
85
     *
86
     * @throws RuntimeException
87 1
     *
88
     * @return string
89
     */
90
    private function saveKeyToFile($key)
91
    {
92
        $tmpDir = \sys_get_temp_dir();
93 1
        $keyPath = $tmpDir . '/' . \sha1($key) . '.key';
94
95
        if (\file_exists($keyPath)) {
96
            return self::FILE_PREFIX . $keyPath;
97
        }
98
99 1
        if (\file_put_contents($keyPath, $key) === false) {
100
            // @codeCoverageIgnoreStart
101
            throw new RuntimeException(\sprintf('Unable to write key file to temporary directory "%s"', $tmpDir));
102
            // @codeCoverageIgnoreEnd
103
        }
104
105
        if (\chmod($keyPath, 0600) === false) {
106
            // @codeCoverageIgnoreStart
107 19
            throw new RuntimeException(\sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath));
108
            // @codeCoverageIgnoreEnd
109 19
        }
110
111
        return self::FILE_PREFIX . $keyPath;
112
    }
113
114
    /**
115
     * Validate key contents.
116
     *
117 10
     * @param string $contents
118
     * @param string $passPhrase
119 10
     *
120
     * @return bool
121
     */
122
    private function isValidKey($contents, $passPhrase)
123
    {
124
        $pkey = openssl_pkey_get_private($contents, $passPhrase) ?: openssl_pkey_get_public($contents);
125
        if ($pkey === false) {
0 ignored issues
show
introduced by
The condition $pkey === false is always false.
Loading history...
126
            return false;
127
        }
128
        $details = openssl_pkey_get_details($pkey);
129
130
        return $details !== false && in_array(
131
            $details['type'] ?? -1,
132
            [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
133
            true
134
        );
135
    }
136
137
    /**
138
     * Checks whether the key is a file.
139
     *
140
     * @return bool
141
     */
142
    private function isFilePath()
143
    {
144
        return \strpos($this->keyPath, self::FILE_PREFIX) === 0;
145
    }
146
147
    /**
148
     * Retrieve key path.
149
     *
150
     * @return string
151
     */
152
    public function getKeyPath()
153
    {
154
        return $this->keyPath;
155
    }
156
157
    /**
158
     * Retrieve key pass phrase.
159
     *
160
     * @return null|string
161
     */
162
    public function getPassPhrase()
163
    {
164
        return $this->passPhrase;
165
    }
166
}
167