]> git.mxchange.org Git - friendica.git/commitdiff
Login prototype
authorMichael <heluecht@pirati.ca>
Tue, 11 May 2021 06:30:20 +0000 (06:30 +0000)
committerMichael <heluecht@pirati.ca>
Tue, 11 May 2021 06:30:20 +0000 (06:30 +0000)
src/Factory/Api/Mastodon/Attachment.php
src/Module/Api/Mastodon/Apps.php
src/Module/BaseApi.php
src/Module/OAuth/Authorize.php [new file with mode: 0644]
src/Module/OAuth/Revoke.php [new file with mode: 0644]
src/Module/OAuth/Token.php [new file with mode: 0644]
src/Module/Security/Login.php
src/Object/Api/Mastodon/Status.php
src/Security/Authentication.php
static/dbstructure.config.php
static/routes.config.php

index 7ac45f354e65e4a206aac5bfb34f820e08829f29..1345da9e854a5a671351426cb8ea65f6ed33425e 100644 (file)
@@ -59,7 +59,7 @@ class Attachment extends BaseFactory
        public function createFromUriId(int $uriId)
        {
                $attachments = [];
-               foreach (Post\Media::getByURIId($uriId) as $attachment) {
+               foreach (Post\Media::getByURIId($uriId, [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE]) as $attachment) {
 
                        $filetype = !empty($attachment['mimetype']) ? strtolower(substr($attachment['mimetype'], 0, strpos($attachment['mimetype'], '/'))) : '';
 
index a86d5cc9938974e86aa5175bbfdfe9159a0d9005..b5d98d455f2f3ff3963fb78233a04b6ba90d34e3 100644 (file)
@@ -46,8 +46,8 @@ class Apps extends BaseApi
                        DI::mstdnError()->RecordNotFound();
                }
 
-               $client_id     = base64_encode(openssl_random_pseudo_bytes(32));
-               $client_secret = bin2hex(random_bytes(32));
+               $client_id     = bin2hex(openssl_random_pseudo_bytes(32));
+               $client_secret = bin2hex(openssl_random_pseudo_bytes(32));
 
                $fields = ['client_id' => $client_id, 'client_secret' => $client_secret, 'name' => $name, 'redirect_uri' => $redirect];
 
index 248e655109720e18220be15637654040d5691f14..87d0838c581f4c973bb736dba40ca5aa6954c8ed 100644 (file)
@@ -24,6 +24,8 @@ namespace Friendica\Module;
 use Friendica\BaseModule;
 use Friendica\Core\Logger;
 use Friendica\Core\System;
+use Friendica\Database\Database;
+use Friendica\Database\DBA;
 use Friendica\DI;
 use Friendica\Network\HTTPException;
 
@@ -110,7 +112,7 @@ class BaseApi extends BaseModule
        public static function unsupported(string $method = 'all')
        {
                $path = DI::args()->getQueryString();
-               Logger::info('Unimplemented API call', ['method' => $method, 'path' => $path, 'agent' => $_SERVER['HTTP_USER_AGENT'] ?? '']);
+               Logger::info('Unimplemented API call', ['method' => $method, 'path' => $path, 'agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'request' => $_REQUEST ?? []]);
                $error = DI::l10n()->t('API endpoint %s %s is not implemented', strtoupper($method), $path);
                $error_description = DI::l10n()->t('The API endpoint is currently not implemented but might be in the future.');;
                $errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
@@ -135,6 +137,14 @@ class BaseApi extends BaseModule
         */
        protected static function login()
        {
+               $authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
+               $authorization = $_SERVER['AUTHORIZATION'] ?? $authorization;
+
+               if (self::checkBearer($authorization)) {
+                       self::$current_user_id = self::getUserByBearer($authorization);
+                       return (bool)self::$current_user_id;
+               }
+
                api_login(DI::app());
 
                self::$current_user_id = api_user();
@@ -149,6 +159,14 @@ class BaseApi extends BaseModule
         */
        protected static function getCurrentUserID()
        {
+               $authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
+               $authorization = $_SERVER['AUTHORIZATION'] ?? $authorization;
+
+               if (self::checkBearer($authorization)) {
+                       self::$current_user_id = self::getUserByBearer($authorization);
+                       return (int)self::$current_user_id;
+               }
+
                if (is_null(self::$current_user_id)) {
                        api_login(DI::app(), false);
 
@@ -158,6 +176,55 @@ class BaseApi extends BaseModule
                return (int)self::$current_user_id;
        }
 
+       private static function checkBearer(string $authorization)
+       {
+               return(strpos($authorization, 'Bearer ') !== false);
+       }
+
+       private static function getUserByBearer(string $authorization)
+       {
+               $bearer = trim(substr($authorization, 6));
+               $condition = ['access_token' => $bearer];
+               $token = DBA::selectFirst('application-token', ['uid'], $condition);
+               if (!DBA::isResult($token)) {
+                       Logger::warning('Token not found', $condition);
+                       return 0;
+               }
+               Logger::info('Token found', $token);
+               return $token['uid'];
+       }
+
+       public static function getApplication()
+       {
+               $redirect_uri = !isset($_REQUEST['redirect_uri']) ? '' : $_REQUEST['redirect_uri'];
+               $client_id    = !isset($_REQUEST['client_id']) ? '' : $_REQUEST['client_id'];
+
+               if (empty($redirect_uri) || empty($client_id)) {
+                       Logger::warning('Incomplete request');
+                       return [];
+               }
+
+               $condition = ['redirect_uri' => $redirect_uri, 'client_id' => $client_id];
+               $application = DBA::selectFirst('application', [], $condition);
+               if (!DBA::isResult($application)) {
+                       Logger::warning('Application not found', $condition);
+                       return [];
+               }
+               return $application;
+       }
+
+       public static function getTokenForUser(array $application, int $uid)
+       {
+               $code         = bin2hex(openssl_random_pseudo_bytes(32));
+               $access_token = bin2hex(openssl_random_pseudo_bytes(32));
+
+               $fields = ['application-id' => $application['id'], 'uid' => $uid, 'code' => $code, 'access_token' => $access_token];
+               if (!DBA::insert('application-token', $fields, Database::INSERT_UPDATE)) {
+                       return [];
+               }
+
+               return DBA::selectFirst('application-token', [], ['application-id' => $application['id'], 'uid' => $uid]);
+       }
        /**
         * Get user info array.
         *
@@ -207,7 +274,7 @@ class BaseApi extends BaseModule
                                $return = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $return;
                                break;
                }
-               
+
                return $return;
        }
 
diff --git a/src/Module/OAuth/Authorize.php b/src/Module/OAuth/Authorize.php
new file mode 100644 (file)
index 0000000..46a5ee4
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\OAuth;
+
+use Friendica\Core\Logger;
+use Friendica\Core\Session;
+use Friendica\Database\Database;
+use Friendica\Database\DBA;
+use Friendica\DI;
+use Friendica\Module\BaseApi;
+
+/**
+ * Dummy class for all currently unimplemented endpoints
+ */
+class Authorize extends BaseApi
+{
+       /**
+        * @param array $parameters
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        */
+       public static function rawContent(array $parameters = [])
+       {
+               //return;
+
+               $response_type = !isset($_REQUEST['response_type']) ? '' : $_REQUEST['response_type'];
+               if ($response_type != 'code') {
+                       Logger::warning('Wrong or missing response type', ['response_type' => $response_type]);
+                       DI::mstdnError()->RecordNotFound();
+               }
+
+               $application = self::getApplication();
+               if (empty($application)) {
+                       DI::mstdnError()->RecordNotFound();
+               }
+
+               $uid = local_user();
+               if (empty($uid)) {
+                       Logger::info('Redirect to login');
+                       DI::app()->redirect('login?return_path=/oauth/authorize');
+               } else {
+                       Logger::info('Already logged in user', ['uid' => $uid]);
+               }
+
+               $token = self::getTokenForUser($application, $uid);
+               if (!$token) {
+                       DI::mstdnError()->RecordNotFound();
+               }
+
+               DI::app()->redirect($application['redirect_uri'] . '?code=' . $token['code']);
+       }
+}
diff --git a/src/Module/OAuth/Revoke.php b/src/Module/OAuth/Revoke.php
new file mode 100644 (file)
index 0000000..f0457e0
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\OAuth;
+
+use Friendica\Core\Logger;
+use Friendica\Module\BaseApi;
+
+/**
+ * Dummy class for all currently unimplemented endpoints
+ */
+class Revoke extends BaseApi
+{
+       public static function post(array $parameters = [])
+       {
+               self::unsupported('post');
+       }
+}
diff --git a/src/Module/OAuth/Token.php b/src/Module/OAuth/Token.php
new file mode 100644 (file)
index 0000000..a690bd0
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\OAuth;
+
+use Friendica\Core\Logger;
+use Friendica\Core\System;
+use Friendica\Database\DBA;
+use Friendica\DI;
+use Friendica\Module\BaseApi;
+
+/**
+ * Dummy class for all currently unimplemented endpoints
+ */
+class Token extends BaseApi
+{
+       public static function post(array $parameters = [])
+       {
+               $client_secret = !isset($_REQUEST['client_secret']) ? '' : $_REQUEST['client_secret'];
+               $code          = !isset($_REQUEST['code']) ? '' : $_REQUEST['code'];
+               $grant_type    = !isset($_REQUEST['grant_type']) ? '' : $_REQUEST['grant_type'];
+
+               if ($grant_type != 'authorization_code') {
+                       Logger::warning('Wrong or missing grant type', ['grant_type' => $grant_type]);
+                       DI::mstdnError()->RecordNotFound();
+               }
+
+               $application = self::getApplication();
+               if (empty($application)) {
+                       DI::mstdnError()->RecordNotFound();
+               }
+
+               if ($application['client_secret'] != $client_secret) {
+                       Logger::warning('Wrong client secret', $client_secret);
+                       DI::mstdnError()->RecordNotFound();
+               }
+
+               $condition = ['application-id' => $application['id'], 'code' => $code];
+               $token = DBA::selectFirst('application-token', ['access_token'], $condition);
+               if (!DBA::isResult($token)) {
+                       Logger::warning('Token not found', $condition);
+                       DI::mstdnError()->RecordNotFound();
+               }
+
+               System::jsonExit(['access_token' => $token['access_token'], 'token_type' => 'Bearer', 'scope' => $application['scopes']]);
+       }
+}
index 9f5095452e5dbdd83342e4b769f0215c3d562fc9..80a9662204fed8ee6fce6bf83c22560a568cd13e 100644 (file)
@@ -36,8 +36,12 @@ class Login extends BaseModule
 {
        public static function content(array $parameters = [])
        {
+               $return_path = !isset($_REQUEST['return_path']) ? '' : $_REQUEST['return_path'];
+
                if (local_user()) {
-                       DI::baseUrl()->redirect();
+                       DI::baseUrl()->redirect($return_path);
+               } elseif (!empty($return_path)) {
+                       Session::set('return_path', $return_path);
                }
 
                return self::form(Session::get('return_path'), intval(DI::config()->get('config', 'register_policy')) !== \Friendica\Module\Register::CLOSED);
index c7c9247b350f6500fd2a155102e28f7e65894956..26c0705bd16a6570ea407e208e8a94eb19a378a8 100644 (file)
@@ -126,7 +126,7 @@ class Status extends BaseDataTransferObject
                $this->muted = $userAttributes->muted;
                $this->bookmarked = $userAttributes->bookmarked;
                $this->pinned = $userAttributes->pinned;
-               $this->content = BBCode::convert($item['raw-body'] ?? $item['body'], false);
+               $this->content = BBCode::convert($item['raw-body'] ?? $item['body'], false, BBCode::API);
                $this->reblog = $reblog;
                $this->application = $application->toArray();
                $this->account = $account->toArray();
@@ -134,7 +134,7 @@ class Status extends BaseDataTransferObject
                $this->mentions = $mentions;
                $this->tags = $tags;
                $this->emojis = [];
-               $this->card = $card->toArray();
+               //$this->card = $card;
                $this->poll = null;
        }
 
index eab75ba5d0cea145f3e68dbba1d50d9e5ff0578d..acbb4bfd4f45b93526742e01b780837ebada32a7 100644 (file)
@@ -39,6 +39,7 @@ use Friendica\Util\Network;
 use Friendica\Util\Strings;
 use LightOpenID;
 use Friendica\Core\L10n;
+use Friendica\Core\Logger;
 use Psr\Log\LoggerInterface;
 
 /**
index 052f73b9cf94e38d5d50026947f6dc4a771a3c33..41515681e4a17a9050d624f13001a885f18e82a7 100644 (file)
@@ -55,7 +55,7 @@
 use Friendica\Database\DBA;
 
 if (!defined('DB_UPDATE_VERSION')) {
-       define('DB_UPDATE_VERSION', 1415);
+       define('DB_UPDATE_VERSION', 1416);
 }
 
 return [
@@ -442,6 +442,20 @@ return [
                        "client_id" => ["UNIQUE", "client_id"]
                ]
        ],
+       "application-token" => [
+               "comment" => "OAuth user token",
+               "fields" => [
+                       "application-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["application" => "id"], "comment" => ""],
+                       "uid" => ["type" => "mediumint unsigned", "not null" => "1", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "Owner User id"],
+                       "code" => ["type" => "varchar(64)", "not null" => "1", "comment" => ""],
+                       "access_token" => ["type" => "varchar(64)", "not null" => "1", "comment" => ""],
+                       "created_at" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "creation time"],
+               ],
+               "indexes" => [
+                       "PRIMARY" => ["application-id", "uid"],
+                       "uid_id" => ["uid", "application-id"],
+               ]
+       ],
        "attach" => [
                "comment" => "file attachments",
                "fields" => [
index 5fbcffbd7cdbd5770adef37da16fde7a4901c2e4..f666feeff55bd07f1e38e248207dc1db47fea487 100644 (file)
@@ -331,9 +331,9 @@ return [
                '/mark/all' => [Module\Notifications\Notification::class, [R::GET]],
                '/{id:\d+}' => [Module\Notifications\Notification::class, [R::GET, R::POST]],
        ],
-       '/oauth/authorize'             => [Module\Api\Mastodon\Unimplemented::class, [R::GET]],
-       '/oauth/revoke'                => [Module\Api\Mastodon\Unimplemented::class, [R::POST]],
-       '/oauth/token'                 => [Module\Api\Mastodon\Unimplemented::class, [R::POST]],
+       '/oauth/authorize'             => [Module\OAuth\Authorize::class, [R::GET]],
+       '/oauth/revoke'                => [Module\OAuth\Revoke::class, [R::POST]],
+       '/oauth/token'                 => [Module\OAuth\Token::class, [R::POST]],
        '/objects/{guid}[/{activity}]' => [Module\Objects::class, [R::GET]],
 
        '/oembed'         => [