/
SecuredLinksPresenterTrait.php
127 lines (101 loc) · 3.35 KB
/
SecuredLinksPresenterTrait.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php
/**
* This file is part of the Nextras community extensions of Nette Framework
*
* @license MIT
* @link https://github.com/nextras
* @author Jan Skrasek
*/
namespace Nextras\Application\UI;
use Nette;
use Nette\Application\UI\PresenterComponent;
trait SecuredLinksPresenterTrait
{
use SecuredLinksControlTrait;
/**
* @param PresenterComponent $component
* @param string $link created URL
* @param string $destination
* @return string
* @throws Nette\Application\UI\InvalidLinkException
*/
public function createSecuredLink(PresenterComponent $component, $link, $destination)
{
/** @var $lastRequest Nette\Application\Request */
$lastRequest = $this->getLastCreatedRequest();
do {
if ($lastRequest === NULL) {
break;
}
$params = $lastRequest->getParameters();
if (!isset($params[Nette\Application\UI\Presenter::SIGNAL_KEY])) {
break;
}
if (($pos = strpos($destination, '#')) !== FALSE) {
$destination = substr($destination, 0, $pos);
}
$a = strpos($destination, '//');
if ($a !== FALSE) {
$destination = substr($destination, $a + 2);
}
$signal = strtr(rtrim($destination, '!'), ':', '-');
$a = strrpos($signal, '-');
if ($a !== FALSE) {
if ($component instanceof Nette\Application\UI\Presenter && substr($destination, -1) !== '!') {
break;
}
$component = $component->getComponent(substr($signal, 0, $a));
$signal = (string) substr($signal, $a + 1);
}
if ($signal == NULL) { // intentionally ==
throw new Nette\Application\UI\InvalidLinkException('Signal must be non-empty string.');
}
// only PresenterComponent
if (!$component instanceof PresenterComponent) {
break;
}
$reflection = $component->getReflection();
$method = $component->formatSignalMethod($signal);
$signalReflection = $reflection->getMethod($method);
if (!$signalReflection->hasAnnotation('secured')) {
break;
}
$origParams = $lastRequest->getParameters();
$protectedParams = array($component->getUniqueId());
foreach ($signalReflection->getParameters() as $param) {
if ($param->isOptional()) {
continue;
}
if (isset($origParams[$component->getParameterId($param->name)])) {
$protectedParams[$param->name] = $origParams[$component->getParameterId($param->name)];
}
}
$protectedParam = $this->getCsrfToken(get_class($component), $method, $protectedParams);
if (($pos = strpos($link, '#')) === FALSE) {
$fragment = '';
} else {
$fragment = substr($link, $pos);
$link = substr($link, 0, $pos);
}
$link .= (strpos($link, '?') !== FALSE ? '&' : '?') . $component->getParameterId('_sec') . '=' . $protectedParam . $fragment;
} while (FALSE);
return $link;
}
/**
* Returns unique token for method and params
* @param string $control
* @param string $method
* @param array $params
* @return string
*/
public function getCsrfToken($control, $method, $params)
{
$session = $this->getSession('Nextras.Application.UI.SecuredLinksPresenterTrait');
if (!isset($session->token)) {
$session->token = Nette\Utils\Random::generate();
}
$params = Nette\Utils\Arrays::flatten($params);
$params = implode('|', array_keys($params)) . '|' . implode('|', array_values($params));
return substr(md5($control . $method . $params . $session->token . $this->getSession()->getId()), 0, 8);
}
}