Passed
Push — master ( 04451c...8a407e )
by Peter
02:12
created

Security::checkOauth2Secrets()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
c 0
b 0
f 0
nc 5
nop 0
dl 0
loc 16
rs 9.6111
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Framework\Http\Middleware;
6
7
use AbterPhp\Framework\Constant\Env;
8
use AbterPhp\Framework\Security\SecurityException;
9
use Closure;
10
use Opulence\Cache\ICacheBridge;
11
use Opulence\Environments\Environment;
12
use Opulence\Http\Requests\Request;
13
use Opulence\Http\Responses\Response;
14
use Opulence\Routing\Middleware\IMiddleware;
15
16
class Security implements IMiddleware
17
{
18
    const KEY = 'abterphp:security';
19
20
    const TEST_DB_PASSWORD                 = '28T3pqyvKG3tEgsjE8Rj';
21
    const TEST_ENCRYPTION_KEY              = 'b8fbb40c129ad0e426e19b7d28f42e517ce639282e55ba7a98bd2b698fda7daa';
22
    const TEST_CRYPTO_FRONTEND_SALT        = 'R6n9gNH9ND6USc6D';
23
    const TEST_CRYPTO_ENCRYPTION_PEPPER    = 'h9fyyWr36vBnky9G';
24
    const TEST_OAUTH2_PRIVATE_KEY_PATH     = '/website/tests/resources/private.key';
25
    const TEST_OAUTH2_PRIVATE_KEY_PASSWORD = '8a!J2SZ9%WBII#9Z';
26
    const TEST_OAUTH2_PUBLIC_KEY_PATH      = '/website/tests/resources/public.key';
27
    const TEST_OAUTH2_ENCRYPTION_KEY       = 'def00000cea4c75b84279f43b56dd90851609717c5d29c215fd2c67f9b1acb0c3b1c5ff8528dbeecf0c1f368baa33284aa36d00b24994872970933e8881802287553ff7d'; // nolint
28
29
    /** @var ICacheBridge */
30
    protected $cacheBridge;
31
32
    /** @var string */
33
    protected $environment;
34
35
    /** @var string[] */
36
    protected $environmentData;
37
38
    /** @var string[] */
39
    protected $settings;
40
41
    /**
42
     * Security constructor.
43
     *
44
     * @param ICacheBridge $cacheBridge
45
     * @param string       $environment
46
     */
47
    public function __construct(ICacheBridge $cacheBridge, string $environment)
48
    {
49
        $this->cacheBridge = $cacheBridge;
50
        $this->environment = $environment;
51
    }
52
53
    /**
54
     * @param string $key
55
     *
56
     * @return string
57
     */
58
    public function getVar(string $key): string
59
    {
60
        if (!isset($this->environmentData[$key])) {
61
            return (string)Environment::getVar($key);
62
        }
63
64
        return (string)$this->environmentData[$key];
65
    }
66
67
    /**
68
     * @param array $environmentData
69
     *
70
     * @return $this
71
     */
72
    public function setVar(array $environmentData): Security
73
    {
74
        $this->environmentData = $environmentData;
75
76
        return $this;
77
    }
78
79
    /**
80
     * @param string $key
81
     *
82
     * @return string
83
     */
84
    public function getSetting(string $key): string
85
    {
86
        if (!isset($this->settings[$key])) {
87
            return (string)ini_get($key);
88
        }
89
90
        return (string)$this->settings[$key];
91
    }
92
93
    /**
94
     * @param array $settings
95
     *
96
     * @return Security
97
     */
98
    public function setSettings(array $settings): Security
99
    {
100
        $this->settings = $settings;
101
102
        return $this;
103
    }
104
105
    // $next consists of the next middleware in the pipeline
106
    public function handle(Request $request, Closure $next): Response
107
    {
108
        if ($this->environment !== Environment::PRODUCTION) {
109
            return $next($request);
110
        }
111
112
        // phpcs:disable Generic.CodeAnalysis.EmptyStatement
113
        try {
114
            if ($this->cacheBridge->get(static::KEY)) {
115
                return $next($request);
116
            }
117
        } catch (\Exception $e) {
118
            // It's always safe to check potential security risks, only makes the response slightly slower
119
        }
120
        // phpcs:enable Generic.CodeAnalysis.EmptyStatement
121
122
        $this->checkGeneralSecrets();
123
        $this->checkOauth2Secrets();
124
        $this->checkPhpSettings();
125
126
        $this->cacheBridge->set(static::KEY, true, PHP_INT_MAX);
127
128
        return $next($request);
129
    }
130
131
    private function checkGeneralSecrets()
132
    {
133
        if ($this->getVar(Env::DB_PASSWORD) === static::TEST_DB_PASSWORD) {
134
            throw new SecurityException('Invalid DB_PASSWORD environment variable.');
135
        }
136
137
        if ($this->getVar(Env::ENCRYPTION_KEY) === static::TEST_ENCRYPTION_KEY) {
138
            throw new SecurityException('Invalid ENCRYPTION_KEY environment variable.');
139
        }
140
141
        if ($this->getVar(Env::CRYPTO_FRONTEND_SALT) === static::TEST_CRYPTO_FRONTEND_SALT) {
142
            throw new SecurityException('Invalid CRYPTO_FRONTEND_SALT environment variable.');
143
        }
144
145
        if ($this->getVar(Env::CRYPTO_ENCRYPTION_PEPPER) === static::TEST_CRYPTO_ENCRYPTION_PEPPER) {
146
            throw new SecurityException('Invalid CRYPTO_ENCRYPTION_PEPPER environment variable.');
147
        }
148
    }
149
150
    private function checkOauth2Secrets()
151
    {
152
        if ($this->getVar(Env::OAUTH2_PRIVATE_KEY_PATH) === static::TEST_OAUTH2_PRIVATE_KEY_PATH) {
153
            throw new SecurityException('Invalid OAUTH2_PRIVATE_KEY_PATH environment variable.');
154
        }
155
156
        if ($this->getVar(Env::OAUTH2_PRIVATE_KEY_PASSWORD) === static::TEST_OAUTH2_PRIVATE_KEY_PASSWORD) {
157
            throw new SecurityException('Invalid OAUTH2_PRIVATE_KEY_PASSWORD environment variable.');
158
        }
159
160
        if ($this->getVar(Env::OAUTH2_PUBLIC_KEY_PATH) === static::TEST_OAUTH2_PUBLIC_KEY_PATH) {
161
            throw new SecurityException('Invalid OAUTH2_PUBLIC_KEY_PATH environment variable.');
162
        }
163
164
        if ($this->getVar(Env::OAUTH2_ENCRYPTION_KEY) === static::TEST_OAUTH2_ENCRYPTION_KEY) {
165
            throw new SecurityException('Invalid OAUTH2_ENCRYPTION_KEY environment variable.');
166
        }
167
    }
168
169
    private function checkPhpSettings()
170
    {
171
        if ($this->getSetting('display_errors')) {
172
            throw new SecurityException('Unacceptable `display_errors` value for production.');
173
        }
174
    }
175
}
176