Passed
Push — developer ( 1b3c7a...748bda )
by Mariusz
30:45
created

Headers::setHeader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
/**
3
 * Headers controller file.
4
 *
5
 * @package Controller
6
 *
7
 * @copyright YetiForce Sp. z o.o
8
 * @license   YetiForce Public License 3.0 (licenses/LicenseEN.txt or yetiforce.com)
9
 * @author    Mariusz Krzaczkowski <[email protected]>
10
 */
11
12
namespace App\Controller;
13
14
/**
15
 * Headers controller class.
16
 */
17
class Headers
18
{
19
	/**
20
	 * Default header values.
21
	 *
22
	 * @var string[]
23
	 */
24
	protected $headers = [
25
		'Access-Control-Allow-Methods' => 'GET, POST',
26
		'Access-Control-Allow-Origin' => '*',
27
		'Expires' => '-',
28
		'Last-Modified' => '-',
29
		'Pragma' => 'no-cache',
30
		'Cache-Control' => 'private, no-cache, no-store, must-revalidate, post-check=0, pre-check=0',
31
		'Content-Type' => 'text/html; charset=UTF-8',
32
		'Referrer-Policy' => 'no-referrer',
33
		'Permissions-Policy' => 'fullscreen=(self),	geolocation=(), camera=()',
34
		'Cross-Origin-Embedder-Policy' => 'require-corp',
35
		'Cross-Origin-Opener-Policy: ' => 'same-origin',
36
		'Cross-Origin-Resource-Policy: ' => 'same-origin',
37
		'Expect-Ct' => 'enforce; max-age=3600',
38
		'X-Frame-Options' => 'sameorigin',
39
		'X-Xss-Protection' => '1; mode=block',
40
		'X-Content-Type-Options' => 'nosniff',
41
		'X-Robots-Tag' => 'none',
42
		'X-Permitted-Cross-Domain-Policies' => 'none',
43
	];
44
	/**
45
	 * Default CSP header values.
46
	 *
47
	 * @var string[]
48
	 */
49
	public $csp = [
50
		'default-src' => '\'self\' blob:',
51
		'img-src' => '\'self\' data:',
52
		'script-src' => '\'self\' \'unsafe-inline\' blob:',
53
		'form-action' => '\'self\'',
54
		'frame-ancestors' => '\'self\'',
55
		'frame-src' => '\'self\' mailto: tel:',
56
		'style-src' => '\'self\' \'unsafe-inline\'',
57
		'connect-src' => '\'self\'',
58
	];
59
	/**
60
	 * Headers to delete.
61
	 *
62
	 * @var string[]
63
	 */
64
	protected $headersToDelete = ['x-powered-by', 'server'];
65
66
	/**
67
	 * Headers instance..
68
	 *
69
	 * @var self
70
	 */
71
	public static $instance;
72
73
	/**
74
	 * Get headers instance.
75
	 *
76
	 * @return \self
0 ignored issues
show
Documentation introduced by
Should the return type not be Headers?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
77
	 */
78
	public static function getInstance()
79
	{
80
		if (isset(self::$instance)) {
81
			return self::$instance;
82
		}
83
		return self::$instance = new self();
84
	}
85
86
	/**
87
	 * Construct, loads default headers depending on the browser and environment.
88
	 */
89
	public function __construct()
90
	{
91
		$browser = \App\RequestUtil::getBrowserInfo();
92
		$this->headers['expires'] = gmdate('D, d M Y H:i:s') . ' GMT';
93
		$this->headers['last-modified'] = gmdate('D, d M Y H:i:s') . ' GMT';
94
		if ($browser->ie) {
95
			$this->headers['x-ua-compatible'] = 'IE=11,edge';
96
			if ($browser->https) {
97
				$this->headers['pragma'] = 'private';
98
				$this->headers['cache-control'] = 'private, must-revalidate';
99
			}
100
		}
101
		if ($browser->https) {
102
			$this->headers['strict-transport-security'] = 'max-age=31536000; includeSubDomains; preload';
103
		}
104
		if (\App\Config::$cspHeaderActive ?? false) {
105
			$this->loadCsp();
106
		}
107
		if ($keys = (\App\Config::$hpkpKeysHeader ?? [])) {
108
			$this->headers['public-key-pins'] = 'pin-sha256="' . implode('"; pin-sha256="', $keys) . '"; max-age=10000;';
109
		}
110
		$this->headers['access-control-allow-origin'] = \App\Config::$portalUrl;
111
	}
112
113
	/**
114
	 * Set header.
115
	 *
116
	 * @param string $key
117
	 * @param string $value
118
	 */
119
	public function setHeader(string $key, string $value)
120
	{
121
		$this->headers[$key] = $value;
122
	}
123
124
	/**
125
	 * Send headers.
126
	 *
127
	 * @return void
128
	 */
129
	public function send()
130
	{
131
		if (headers_sent()) {
132
			return;
133
		}
134
		foreach ($this->getHeaders() as $value) {
135
			header($value);
136
		}
137
		foreach ($this->headersToDelete as $name) {
138
			header_remove($name);
139
		}
140
	}
141
142
	/**
143
	 * Get headers string.
144
	 *
145
	 * @return string[]
146
	 */
147
	public function getHeaders(): array
148
	{
149
		if (\App\Config::$cspHeaderActive ?? false) {
150
			$this->headers['Content-Security-Policy'] = $this->getCspHeader();
151
		}
152
		$return = [];
153
		foreach ($this->headers as $name => $value) {
154
			$return[] = "$name: $value";
155
		}
156
		return $return;
157
	}
158
159
	/**
160
	 * Load CSP directive.
161
	 *
162
	 * @return void
163
	 */
164
	public function loadCsp()
165
	{
166
	}
167
168
	/**
169
	 * Get CSP headers string.
170
	 *
171
	 * @return string
172
	 */
173
	public function getCspHeader(): string
174
	{
175
		$scp = '';
176
		foreach ($this->csp as $key => $value) {
177
			$scp .= "$key $value; ";
178
		}
179
		return $scp;
180
	}
181
}
182