From 1074fe9f773f579e6b6f8e1950c7479fd40ce879 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 7 Aug 2016 19:23:44 +0200 Subject: [PATCH] Check token on micropub post request, store correct user --- src/anoweco/Urls.php | 14 ++++++ www/auth.php | 24 +++++----- www/micropub.php | 105 ++++++++++++++++++++++++++++++++++++------- www/token.php | 31 +++++++++---- 4 files changed, 136 insertions(+), 38 deletions(-) diff --git a/src/anoweco/Urls.php b/src/anoweco/Urls.php index 35084a5..5096d31 100644 --- a/src/anoweco/Urls.php +++ b/src/anoweco/Urls.php @@ -30,5 +30,19 @@ class Urls . $_SERVER['HTTP_HOST'] . $str; } + + public static function userId($url) + { + $userbaseurl = Urls::full('/user/'); + if (substr($url, 0, strlen($userbaseurl)) != $userbaseurl) { + return null; + } + //actual user URL - loads his data + $userId = substr($url, strrpos($url, '/') + 1, -4); + if (intval($userId) != $userId) { + return null; + } + return intval($userId); + } } ?> diff --git a/www/auth.php b/www/auth.php index 4872838..22e3de9 100644 --- a/www/auth.php +++ b/www/auth.php @@ -60,20 +60,16 @@ if ($_SERVER['REQUEST_METHOD'] == 'GET') { 'name' => '', 'imageurl' => '', ); - $userbaseurl = Urls::full('/user/'); - if (substr($me, 0, strlen($userbaseurl)) == $userbaseurl) { - //actual user URL - loads his data - $userid = substr($me, strrpos($me, '/') + 1, -4); - if (intval($userid) == $userid) { - $storage = new Storage(); - $rowUser = $storage->getUser($userid); - if ($rowUser !== null) { - $id['mode'] = 'data'; - $id['name'] = $rowUser->user_name; - $id['imageurl'] = $rowUser->user_imageurl; - if ($id['imageurl'] == Urls::userImg()) { - $id['imageurl'] = ''; - } + $userId = Urls::userId($me); + if ($userId !== null) { + $storage = new Storage(); + $rowUser = $storage->getUser($userId); + if ($rowUser !== null) { + $id['mode'] = 'data'; + $id['name'] = $rowUser->user_name; + $id['imageurl'] = $rowUser->user_imageurl; + if ($id['imageurl'] == Urls::userImg()) { + $id['imageurl'] = ''; } } } diff --git a/www/micropub.php b/www/micropub.php index 43c41b1..8d8d34a 100644 --- a/www/micropub.php +++ b/www/micropub.php @@ -9,7 +9,7 @@ header('HTTP/1.0 500 Internal Server Error'); require 'www-header.php'; /** - * Send out an error + * Send out a micropub error * * @param string $status HTTP status code line * @param string $code One of the allowed status types: @@ -19,7 +19,7 @@ require 'www-header.php'; * - not_found * @param string $description */ -function error($status, $code, $description) +function mpError($status, $code, $description) { header($status); header('Content-Type: application/json'); @@ -29,20 +29,69 @@ function error($status, $code, $description) exit(1); } -function handleCreate($json) +function validateToken($token) { + $ctx = stream_context_create( + array( + 'http' => array( + 'header' => array( + 'Authorization: Bearer ' . $token + ), + ), + ) + ); + //FIXME: make hard-coded token server URL configurable + $res = @file_get_contents(Urls::full('/token.php'), false, $ctx); + list($dummy, $code, $msg) = explode(' ', $http_response_header[0]); + if ($code != 200) { + mpError( + 'HTTP/1.0 403 Forbidden', + 'forbidden', + 'Error verifying bearer token' + ); + } + + parse_str($res, $data); + //FIXME: they spit out non-micropub json error responess + verifyUrlParameter($data, 'me'); + verifyUrlParameter($data, 'client_id'); + verifyParameter($data, 'scope'); + + return [$data['me'], $data['client_id'], $data['scope']]; +} + +function handleCreate($json, $token) +{ + list($me, $client_id, $scope) = validateToken($token); + $userId = Urls::userId($me); + if ($userId === null) { + mpError( + 'HTTP/1.0 403 Forbidden', + 'forbidden', + 'Invalid user URL' + ); + } + $storage = new Storage(); + $rowUser = $storage->getUser($userId); + if ($rowUser === null) { + mpError( + 'HTTP/1.0 403 Forbidden', + 'forbidden', + 'User not found: ' . $userId + ); + } + if (!isset($json->properties->{'in-reply-to'})) { - error( + mpError( 'HTTP/1.0 400 Bad Request', 'invalid_request', 'Only replies accepted' ); } - //FIXME: read bearer token - //FIXME: get user ID + $storage = new Storage(); try { - $id = $storage->addComment($json, 0); + $id = $storage->addComment($json, $userId); header('HTTP/1.0 201 Created'); header('Location: ' . Urls::full(Urls::comment($id))); @@ -54,9 +103,28 @@ function handleCreate($json) } } +function getTokenFromHeader() +{ + if (!isset($_SERVER['HTTP_AUTHORIZATION'])) { + mpError( + 'HTTP/1.0 403 Forbidden', 'forbidden', + 'Authorization HTTP header missing' + ); + } + list($bearer, $token) = explode(' ', $_SERVER['HTTP_AUTHORIZATION'], 2); + if ($bearer !== 'Bearer') { + mpError( + 'HTTP/1.0 403 Forbidden', 'forbidden', + 'Authorization header must start with "Bearer"' + ); + } + return trim($token); +} + + if ($_SERVER['REQUEST_METHOD'] == 'GET') { if (!isset($_GET['q'])) { - error( + mpError( 'HTTP/1.1 400 Bad Request', 'invalid_request', 'Parameter "q" missing.' @@ -80,7 +148,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'GET') { } } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { if (!isset($_SERVER['CONTENT_TYPE'])) { - error( + mpError( 'HTTP/1.1 400 Bad Request', 'invalid_request', 'Content-Type header missing.' @@ -106,8 +174,14 @@ if ($_SERVER['REQUEST_METHOD'] == 'GET') { $base->type = ['h-' . $data['h']]; unset($data['h']); } + if (isset($data['access_token'])) { + $token = $data['access_token']; + unset($data['access_token']); + } else { + $token = getTokenFromHeader(); + } //reserved properties - foreach (['access_token', 'q', 'url', 'action'] as $key) { + foreach (['q', 'url', 'action'] as $key) { if (isset($data[$key])) { $base->$key = $data[$key]; unset($data[$key]); @@ -125,27 +199,28 @@ if ($_SERVER['REQUEST_METHOD'] == 'GET') { } $json = $base; $json->properties = (object) $data; - handleCreate($json); + handleCreate($json, $token); } else if ($ctype == 'application/javascript') { $input = file_get_contents('php://stdin'); $json = json_decode($input); if ($json === null) { - error( + mpError( 'HTTP/1.1 400 Bad Request', 'invalid_request', 'Invalid JSON' ); } - handleCreate($json); + $token = getTokenFromHeader(); + handleCreate($json, $token); } else { - error( + mpError( 'HTTP/1.1 400 Bad Request', 'invalid_request', 'Unsupported POST content type' ); } } else { - error( + mpError( 'HTTP/1.0 400 Bad Request', 'invalid_request', 'Unsupported HTTP request method' diff --git a/www/token.php b/www/token.php index bf10e70..be9a264 100644 --- a/www/token.php +++ b/www/token.php @@ -48,15 +48,24 @@ if ($_SERVER['REQUEST_METHOD'] == 'GET') { } //FIXME: use real decryption - $data = json_decode($token); - if ($data === null) { - error('Invalid token'); + $encData = base64_decode($token); + if ($encData === false) { + error('Invalid token data'); } - $data = (array) $data; + parse_str($encData, $data); + $emoji = verifyParameter($data, 'emoji'); + $signature = verifyParameter($data, 'signature'); $me = verifyUrlParameter($data, 'me'); $client_id = verifyUrlParameter($data, 'client_id'); $scope = verifyParameter($data, 'scope'); + if ($emoji != '\360\237\222\251') { + error('Dog poo missing'); + } + if ($signature != 'FIXME') { + error('Invalid signature'); + } + header('HTTP/1.0 200 OK'); header('Content-type: application/x-www-form-urlencoded'); echo http_build_query( @@ -80,11 +89,15 @@ if ($_SERVER['REQUEST_METHOD'] == 'GET') { $scope = 'post'; //FIXME: use real encryption - $access_token = '

"\'' . json_encode( - array( - 'me' => $me, - 'client_id' => $client_id, - 'scope' => $scope + $access_token = base64_encode( + http_build_query( + array( + 'emoji' => '\360\237\222\251', + 'me' => $me, + 'client_id' => $client_id, + 'scope' => $scope, + 'signature' => 'FIXME', + ) ) ); header('HTTP/1.0 200 OK'); -- 2.30.2