PluginProbe ʕ •ᴥ•ʔ
FAPI Member / 2.2.31
FAPI Member v2.2.31
2.2.33 2.2.32 trunk 1.9.47 2.1.18 2.2.24 2.2.25 2.2.26 2.2.28 2.2.29 2.2.30 2.2.31
fapi-member / libs / nette / http / src / Bridges / HttpDI / HttpExtension.php
fapi-member / libs / nette / http / src / Bridges / HttpDI Last commit date
HttpExtension.php 4 weeks ago SessionExtension.php 4 weeks ago
HttpExtension.php
128 lines
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace FapiMember\Library\Nette\Bridges\HttpDI;
9
10 use FapiMember\Library\Nette;
11 use FapiMember\Library\Nette\Schema\Expect;
12 /**
13 * HTTP extension for Nette DI.
14 */
15 class HttpExtension extends Nette\DI\CompilerExtension
16 {
17 /** @var bool */
18 private $cliMode;
19 public function __construct(bool $cliMode = \false)
20 {
21 $this->cliMode = $cliMode;
22 }
23 public function getConfigSchema(): Nette\Schema\Schema
24 {
25 return Expect::structure([
26 'proxy' => Expect::anyOf(Expect::arrayOf('string'), Expect::string()->castTo('array'))->firstIsDefault()->dynamic(),
27 'headers' => Expect::arrayOf('scalar|null')->default(['X-Powered-By' => 'Nette Framework 3', 'Content-Type' => 'text/html; charset=utf-8'])->mergeDefaults(),
28 'frames' => Expect::anyOf(Expect::string(), Expect::bool(), null)->default('SAMEORIGIN'),
29 // X-Frame-Options
30 'csp' => Expect::arrayOf('array|scalar|null'),
31 // Content-Security-Policy
32 'cspReportOnly' => Expect::arrayOf('array|scalar|null'),
33 // Content-Security-Policy-Report-Only
34 'featurePolicy' => Expect::arrayOf('array|scalar|null'),
35 // Feature-Policy
36 'cookiePath' => Expect::string()->dynamic(),
37 'cookieDomain' => Expect::string()->dynamic(),
38 'cookieSecure' => Expect::anyOf('auto', null, \true, \false)->firstIsDefault()->dynamic(),
39 // Whether the cookie is available only through HTTPS
40 'disableNetteCookie' => Expect::bool(\false),
41 ]);
42 }
43 public function loadConfiguration()
44 {
45 $builder = $this->getContainerBuilder();
46 $config = $this->config;
47 $builder->addDefinition($this->prefix('requestFactory'))->setFactory(Nette\Http\RequestFactory::class)->addSetup('setProxy', [$config->proxy]);
48 $request = $builder->addDefinition($this->prefix('request'))->setFactory('@Nette\Http\RequestFactory::fromGlobals');
49 $response = $builder->addDefinition($this->prefix('response'))->setFactory(Nette\Http\Response::class);
50 if ($config->cookiePath !== null) {
51 $response->addSetup('$cookiePath', [$config->cookiePath]);
52 }
53 if ($config->cookieDomain !== null) {
54 $value = ($config->cookieDomain === 'domain') ? $builder::literal('$this->getService(?)->getUrl()->getDomain(2)', [$request->getName()]) : $config->cookieDomain;
55 $response->addSetup('$cookieDomain', [$value]);
56 }
57 if ($config->cookieSecure !== null) {
58 $value = ($config->cookieSecure === 'auto') ? $builder::literal('$this->getService(?)->isSecured()', [$request->getName()]) : $config->cookieSecure;
59 $response->addSetup('$cookieSecure', [$value]);
60 }
61 if ($this->name === 'http') {
62 $builder->addAlias('nette.httpRequestFactory', $this->prefix('requestFactory'));
63 $builder->addAlias('httpRequest', $this->prefix('request'));
64 $builder->addAlias('httpResponse', $this->prefix('response'));
65 }
66 if (!$this->cliMode) {
67 $this->sendHeaders();
68 }
69 }
70 private function sendHeaders()
71 {
72 $config = $this->config;
73 $headers = array_map('strval', $config->headers);
74 if (isset($config->frames) && $config->frames !== \true && !isset($headers['X-Frame-Options'])) {
75 $frames = $config->frames;
76 if ($frames === \false) {
77 $frames = 'DENY';
78 } elseif (preg_match('#^https?:#', $frames)) {
79 $frames = "ALLOW-FROM {$frames}";
80 }
81 $headers['X-Frame-Options'] = $frames;
82 }
83 foreach (['csp', 'cspReportOnly'] as $key) {
84 if (empty($config->{$key})) {
85 continue;
86 }
87 $value = self::buildPolicy($config->{$key});
88 if (strpos($value, "'nonce'")) {
89 $this->initialization->addBody('$cspNonce = base64_encode(random_bytes(16));');
90 $value = Nette\DI\ContainerBuilder::literal('str_replace(?, ? . $cspNonce, ?)', ["'nonce", "'nonce-", $value]);
91 }
92 $headers['Content-Security-Policy' . (($key === 'csp') ? '' : '-Report-Only')] = $value;
93 }
94 if (!empty($config->featurePolicy)) {
95 $headers['Feature-Policy'] = self::buildPolicy($config->featurePolicy);
96 }
97 $this->initialization->addBody('$response = $this->getService(?);', [$this->prefix('response')]);
98 foreach ($headers as $key => $value) {
99 if ($value !== '') {
100 $this->initialization->addBody('$response->setHeader(?, ?);', [$key, $value]);
101 }
102 }
103 if (!$config->disableNetteCookie) {
104 $this->initialization->addBody('Nette\Http\Helpers::initCookie($this->getService(?), $response);', [$this->prefix('request')]);
105 }
106 }
107 private static function buildPolicy(array $config): string
108 {
109 $nonQuoted = ['require-sri-for' => 1, 'sandbox' => 1];
110 $value = '';
111 foreach ($config as $type => $policy) {
112 if ($policy === \false) {
113 continue;
114 }
115 $policy = ($policy === \true) ? [] : (array) $policy;
116 $value .= $type;
117 foreach ($policy as $item) {
118 if (is_array($item)) {
119 $item = key($item) . ':';
120 }
121 $value .= (!isset($nonQuoted[$type]) && preg_match('#^[a-z-]+$#D', $item)) ? " '{$item}'" : " {$item}";
122 }
123 $value .= '; ';
124 }
125 return $value;
126 }
127 }
128