]> git.mxchange.org Git - friendica.git/blob - src/Security/OAuth1/OAuthRequest.php
Move library\OAuth1.php to class structure Friendica\Security\OAuth1
[friendica.git] / src / Security / OAuth1 / OAuthRequest.php
1 <?php
2
3 namespace Friendica\Security\OAuth1;
4
5 use Friendica;
6 use Friendica\Security\OAuth1\OAuthUtil;
7
8 class OAuthRequest
9 {
10         private $parameters;
11         private $http_method;
12         private $http_url;
13         // for debug purposes
14         public $base_string;
15         public static $version = '1.0';
16         public static $POST_INPUT = 'php://input';
17
18         function __construct($http_method, $http_url, $parameters = null)
19         {
20                 @$parameters or $parameters = [];
21                 $parameters        = array_merge(OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
22                 $this->parameters  = $parameters;
23                 $this->http_method = $http_method;
24                 $this->http_url    = $http_url;
25         }
26
27
28         /**
29          * attempt to build up a request from what was passed to the server
30          *
31          * @param string|null $http_method
32          * @param string|null $http_url
33          * @param string|null $parameters
34          *
35          * @return OAuthRequest
36          */
37         public static function from_request($http_method = null, $http_url = null, $parameters = null)
38         {
39                 $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
40                         ? 'http'
41                         : 'https';
42                 @$http_url or $http_url = $scheme .
43                                                                   '://' . $_SERVER['HTTP_HOST'] .
44                                                                   ':' .
45                                                                   $_SERVER['SERVER_PORT'] .
46                                                                   $_SERVER['REQUEST_URI'];
47                 @$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
48
49                 // We weren't handed any parameters, so let's find the ones relevant to
50                 // this request.
51                 // If you run XML-RPC or similar you should use this to provide your own
52                 // parsed parameter-list
53                 if (!$parameters) {
54                         // Find request headers
55                         $request_headers = OAuthUtil::get_headers();
56
57                         // Parse the query-string to find GET parameters
58                         $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
59
60                         // It's a POST request of the proper content-type, so parse POST
61                         // parameters and add those overriding any duplicates from GET
62                         if (
63                                 $http_method == "POST"
64                                 && @strstr(
65                                         $request_headers["Content-Type"],
66                                         "application/x-www-form-urlencoded"
67                                 )
68                         ) {
69                                 $post_data  = OAuthUtil::parse_parameters(
70                                         file_get_contents(self::$POST_INPUT)
71                                 );
72                                 $parameters = array_merge($parameters, $post_data);
73                         }
74
75                         // We have a Authorization-header with OAuth data. Parse the header
76                         // and add those overriding any duplicates from GET or POST
77                         if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
78                                 $header_parameters = OAuthUtil::split_header(
79                                         $request_headers['Authorization']
80                                 );
81                                 $parameters        = array_merge($parameters, $header_parameters);
82                         }
83                 }
84                 // fix for friendica redirect system
85
86                 $http_url = substr($http_url, 0, strpos($http_url, $parameters['pagename']) + strlen($parameters['pagename']));
87                 unset($parameters['pagename']);
88
89                 return new OAuthRequest($http_method, $http_url, $parameters);
90         }
91
92         /**
93          * pretty much a helper function to set up the request
94          *
95          * @param \Friendica\Security\OAuth1\OAuthConsumer $consumer
96          * @param \Friendica\Security\OAuth1\OAuthToken    $token
97          * @param string                                   $http_method
98          * @param string                                   $http_url
99          * @param array|null                               $parameters
100          *
101          * @return OAuthRequest
102          */
103         public static function from_consumer_and_token(\Friendica\Security\OAuth1\OAuthConsumer $consumer, $http_method, $http_url, array $parameters = null, \Friendica\Security\OAuth1\OAuthToken $token = null)
104         {
105                 @$parameters or $parameters = [];
106                 $defaults = [
107                         "oauth_version"      => OAuthRequest::$version,
108                         "oauth_nonce"        => OAuthRequest::generate_nonce(),
109                         "oauth_timestamp"    => OAuthRequest::generate_timestamp(),
110                         "oauth_consumer_key" => $consumer->key,
111                 ];
112                 if ($token)
113                         $defaults['oauth_token'] = $token->key;
114
115                 $parameters = array_merge($defaults, $parameters);
116
117                 return new OAuthRequest($http_method, $http_url, $parameters);
118         }
119
120         public function set_parameter($name, $value, $allow_duplicates = true)
121         {
122                 if ($allow_duplicates && isset($this->parameters[$name])) {
123                         // We have already added parameter(s) with this name, so add to the list
124                         if (is_scalar($this->parameters[$name])) {
125                                 // This is the first duplicate, so transform scalar (string)
126                                 // into an array so we can add the duplicates
127                                 $this->parameters[$name] = [$this->parameters[$name]];
128                         }
129
130                         $this->parameters[$name][] = $value;
131                 } else {
132                         $this->parameters[$name] = $value;
133                 }
134         }
135
136         public function get_parameter($name)
137         {
138                 return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
139         }
140
141         public function get_parameters()
142         {
143                 return $this->parameters;
144         }
145
146         public function unset_parameter($name)
147         {
148                 unset($this->parameters[$name]);
149         }
150
151         /**
152          * The request parameters, sorted and concatenated into a normalized string.
153          *
154          * @return string
155          */
156         public function get_signable_parameters()
157         {
158                 // Grab all parameters
159                 $params = $this->parameters;
160
161                 // Remove oauth_signature if present
162                 // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
163                 if (isset($params['oauth_signature'])) {
164                         unset($params['oauth_signature']);
165                 }
166
167                 return OAuthUtil::build_http_query($params);
168         }
169
170         /**
171          * Returns the base string of this request
172          *
173          * The base string defined as the method, the url
174          * and the parameters (normalized), each urlencoded
175          * and the concated with &.
176          */
177         public function get_signature_base_string()
178         {
179                 $parts = [
180                         $this->get_normalized_http_method(),
181                         $this->get_normalized_http_url(),
182                         $this->get_signable_parameters(),
183                 ];
184
185                 $parts = OAuthUtil::urlencode_rfc3986($parts);
186
187                 return implode('&', $parts);
188         }
189
190         /**
191          * just uppercases the http method
192          */
193         public function get_normalized_http_method()
194         {
195                 return strtoupper($this->http_method);
196         }
197
198         /**
199          * parses the url and rebuilds it to be
200          * scheme://host/path
201          */
202         public function get_normalized_http_url()
203         {
204                 $parts = parse_url($this->http_url);
205
206                 $port   = @$parts['port'];
207                 $scheme = $parts['scheme'];
208                 $host   = $parts['host'];
209                 $path   = @$parts['path'];
210
211                 $port or $port = ($scheme == 'https') ? '443' : '80';
212
213                 if (($scheme == 'https' && $port != '443')
214                         || ($scheme == 'http' && $port != '80')
215                 ) {
216                         $host = "$host:$port";
217                 }
218                 return "$scheme://$host$path";
219         }
220
221         /**
222          * builds a url usable for a GET request
223          */
224         public function to_url()
225         {
226                 $post_data = $this->to_postdata();
227                 $out       = $this->get_normalized_http_url();
228                 if ($post_data) {
229                         $out .= '?' . $post_data;
230                 }
231                 return $out;
232         }
233
234         /**
235          * builds the data one would send in a POST request
236          *
237          * @param bool $raw
238          *
239          * @return array|string
240          */
241         public function to_postdata(bool $raw = false)
242         {
243                 if ($raw)
244                         return $this->parameters;
245                 else
246                         return OAuthUtil::build_http_query($this->parameters);
247         }
248
249         /**
250          * builds the Authorization: header
251          *
252          * @param string|null $realm
253          *
254          * @return string
255          * @throws \Friendica\Security\OAuth1\OAuthException
256          */
257         public function to_header($realm = null)
258         {
259                 $first = true;
260                 if ($realm) {
261                         $out   = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
262                         $first = false;
263                 } else
264                         $out = 'Authorization: OAuth';
265
266                 foreach ($this->parameters as $k => $v) {
267                         if (substr($k, 0, 5) != "oauth") continue;
268                         if (is_array($v)) {
269                                 throw new \Friendica\Security\OAuth1\OAuthException('Arrays not supported in headers');
270                         }
271                         $out   .= ($first) ? ' ' : ',';
272                         $out   .= OAuthUtil::urlencode_rfc3986($k) .
273                                           '="' .
274                                           OAuthUtil::urlencode_rfc3986($v) .
275                                           '"';
276                         $first = false;
277                 }
278                 return $out;
279         }
280
281         public function __toString()
282         {
283                 return $this->to_url();
284         }
285
286
287         public function sign_request(\Friendica\Security\OAuth1\OAuthSignatureMethod $signature_method, $consumer, $token)
288         {
289                 $this->set_parameter(
290                         "oauth_signature_method",
291                         $signature_method->get_name(),
292                         false
293                 );
294                 $signature = $this->build_signature($signature_method, $consumer, $token);
295                 $this->set_parameter("oauth_signature", $signature, false);
296         }
297
298         public function build_signature(\Friendica\Security\OAuth1\OAuthSignatureMethod $signature_method, $consumer, $token)
299         {
300                 $signature = $signature_method->build_signature($this, $consumer, $token);
301                 return $signature;
302         }
303
304         /**
305          * util function: current timestamp
306          */
307         private static function generate_timestamp()
308         {
309                 return time();
310         }
311
312         /**
313          * util function: current nonce
314          */
315         private static function generate_nonce()
316         {
317                 return Friendica\Util\Strings::getRandomHex(32);
318         }
319 }