Check token on micropub post request, store correct user
authorChristian Weiske <cweiske@cweiske.de>
Sun, 7 Aug 2016 17:23:44 +0000 (19:23 +0200)
committerChristian Weiske <cweiske@cweiske.de>
Sun, 7 Aug 2016 17:23:44 +0000 (19:23 +0200)
src/anoweco/Urls.php
www/auth.php
www/micropub.php
www/token.php

index 35084a5..5096d31 100644 (file)
@@ -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);
+    }
 }
 ?>
index 4872838..22e3de9 100644 (file)
@@ -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'] = '';
             }
         }
     }
index 43c41b1..8d8d34a 100644 (file)
@@ -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'
index bf10e70..be9a264 100644 (file)
@@ -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 = '<h1>"\'' . 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');