1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of tenside/core. |
5
|
|
|
* |
6
|
|
|
* (c) Christian Schiffler <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
* |
11
|
|
|
* This project is provided in good faith and hope to be usable by anyone. |
12
|
|
|
* |
13
|
|
|
* @package tenside/core |
14
|
|
|
* @author Christian Schiffler <[email protected]> |
15
|
|
|
* @copyright 2015 Christian Schiffler <[email protected]> |
16
|
|
|
* @license https://github.com/tenside/core/blob/master/LICENSE MIT |
17
|
|
|
* @link https://github.com/tenside/core |
18
|
|
|
* @filesource |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace Tenside\CoreBundle\Util; |
22
|
|
|
|
23
|
|
|
use Tenside\Core\Config\TensideJsonConfig; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Signs URIs. |
27
|
|
|
* |
28
|
|
|
* This service behaves exactly like the one in the symfony framework but is ready for phar use. |
29
|
|
|
* As the kernel.secret is not available during phar compile time, we need it lazy loaded. |
30
|
|
|
* |
31
|
|
|
* @author Fabien Potencier <[email protected]> |
32
|
|
|
*/ |
33
|
|
|
class UriSigner |
34
|
|
|
{ |
35
|
|
|
/** |
36
|
|
|
* The resolved secret. |
37
|
|
|
* |
38
|
|
|
* @var string |
39
|
|
|
*/ |
40
|
|
|
private $secret; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* The config to obtain the secret parameter from. |
44
|
|
|
* |
45
|
|
|
* @var TensideJsonConfig |
46
|
|
|
*/ |
47
|
|
|
private $tensideConfig; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Constructor. |
51
|
|
|
* |
52
|
|
|
* @param TensideJsonConfig $tensideConfig The tenside config. |
53
|
|
|
*/ |
54
|
|
|
public function __construct(TensideJsonConfig $tensideConfig) |
55
|
|
|
{ |
56
|
|
|
$this->tensideConfig = $tensideConfig; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Signs a URI. |
61
|
|
|
* |
62
|
|
|
* The given URI is signed by adding a _hash query string parameter |
63
|
|
|
* which value depends on the URI and the secret. |
64
|
|
|
* |
65
|
|
|
* @param string $uri A URI to sign. |
66
|
|
|
* |
67
|
|
|
* @return string The signed URI |
68
|
|
|
*/ |
69
|
|
|
public function sign($uri) |
70
|
|
|
{ |
71
|
|
|
$url = parse_url($uri); |
72
|
|
|
if (isset($url['query'])) { |
73
|
|
|
parse_str($url['query'], $params); |
74
|
|
|
} else { |
75
|
|
|
$params = array(); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
$uri = $this->buildUrl($url, $params); |
|
|
|
|
79
|
|
|
|
80
|
|
|
return $uri.(false === (strpos($uri, '?')) ? '?' : '&').'_hash='.$this->computeHash($uri); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Checks that a URI contains the correct hash. |
85
|
|
|
* |
86
|
|
|
* The _hash query string parameter must be the last one |
87
|
|
|
* (as it is generated that way by the sign() method, it should |
88
|
|
|
* never be a problem). |
89
|
|
|
* |
90
|
|
|
* @param string $uri A signed URI. |
91
|
|
|
* |
92
|
|
|
* @return bool True if the URI is signed correctly, false otherwise |
93
|
|
|
*/ |
94
|
|
|
public function check($uri) |
95
|
|
|
{ |
96
|
|
|
$url = parse_url($uri); |
97
|
|
|
if (isset($url['query'])) { |
98
|
|
|
parse_str($url['query'], $params); |
99
|
|
|
} else { |
100
|
|
|
$params = array(); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
if (empty($params['_hash'])) { |
104
|
|
|
return false; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
$hash = urlencode($params['_hash']); |
108
|
|
|
unset($params['_hash']); |
109
|
|
|
|
110
|
|
|
return $this->computeHash($this->buildUrl($url, $params)) === $hash; |
|
|
|
|
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Calculate the hash. |
115
|
|
|
* |
116
|
|
|
* @param string $uri The uri to calculate the hash for. |
117
|
|
|
* |
118
|
|
|
* @return string |
119
|
|
|
*/ |
120
|
|
|
private function computeHash($uri) |
121
|
|
|
{ |
122
|
|
|
if (!isset($this->secret)) { |
123
|
|
|
$this->secret = $this->tensideConfig->getSecret(); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
return urlencode(base64_encode(hash_hmac('sha256', $uri, $this->secret, true))); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Build the url. |
131
|
|
|
* |
132
|
|
|
* @param array $url The url values. |
133
|
|
|
* |
134
|
|
|
* @param array $params The url parameters. |
135
|
|
|
* |
136
|
|
|
* @return string |
137
|
|
|
* |
138
|
|
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) |
139
|
|
|
* @SuppressWarnings(PHPMD.NPathComplexity) |
140
|
|
|
*/ |
141
|
|
|
private function buildUrl(array $url, array $params = array()) |
142
|
|
|
{ |
143
|
|
|
ksort($params); |
144
|
|
|
$url['query'] = http_build_query($params, '', '&'); |
145
|
|
|
|
146
|
|
|
$scheme = isset($url['scheme']) ? $url['scheme'].'://' : ''; |
147
|
|
|
$host = isset($url['host']) ? $url['host'] : ''; |
148
|
|
|
$port = isset($url['port']) ? ':'.$url['port'] : ''; |
149
|
|
|
$user = isset($url['user']) ? $url['user'] : ''; |
150
|
|
|
$pass = isset($url['pass']) ? ':'.$url['pass'] : ''; |
151
|
|
|
$pass = ($user || $pass) ? $pass . '@' : ''; |
152
|
|
|
$path = isset($url['path']) ? $url['path'] : ''; |
153
|
|
|
$query = isset($url['query']) && $url['query'] ? '?'.$url['query'] : ''; |
154
|
|
|
$fragment = isset($url['fragment']) ? '#'.$url['fragment'] : ''; |
155
|
|
|
|
156
|
|
|
return $scheme.$user.$pass.$host.$port.$path.$query.$fragment; |
157
|
|
|
} |
158
|
|
|
} |
159
|
|
|
|
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.