]> git.mxchange.org Git - friendica.git/blob - src/Security/OAuth.php
Issue 13221: Diaspora posts are now stored correctly
[friendica.git] / src / Security / OAuth.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Security;
23
24 use Friendica\Core\Logger;
25 use Friendica\Core\Worker;
26 use Friendica\Database\Database;
27 use Friendica\Database\DBA;
28 use Friendica\Model\Contact;
29 use Friendica\Model\User;
30 use Friendica\Module\BaseApi;
31 use Friendica\Util\DateTimeFormat;
32
33 /**
34  * OAuth Server
35  */
36 class OAuth
37 {
38         /**
39          * @var bool|int
40          */
41         protected static $current_user_id = 0;
42         /**
43          * @var array
44          */
45         protected static $current_token = [];
46
47         /**
48          * Get current user id, returns 0 if not logged in
49          *
50          * @return int User ID
51          */
52         public static function getCurrentUserID()
53         {
54                 if (empty(self::$current_user_id)) {
55                         $token = self::getCurrentApplicationToken();
56                         if (!empty($token['uid'])) {
57                                 self::$current_user_id = $token['uid'];
58                         } else {
59                                 self::$current_user_id = 0;
60                         }
61                 }
62
63                 return (int)self::$current_user_id;
64         }
65
66         /**
67          * Get current application token
68          *
69          * @return array token
70          */
71         public static function getCurrentApplicationToken()
72         {
73                 if (empty(self::$current_token)) {
74                         self::$current_token = self::getTokenByBearer();
75                 }
76
77                 return self::$current_token;
78         }
79
80         /**
81          * Get the user token via the Bearer token
82          *
83          * @return array User Token
84          */
85         private static function getTokenByBearer()
86         {
87                 $authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
88
89                 if (empty($authorization)) {
90                         // workaround for HTTP-auth in CGI mode
91                         $authorization = $_SERVER['REDIRECT_REMOTE_USER'] ?? '';
92                 }
93
94                 if (substr($authorization, 0, 7) != 'Bearer ') {
95                         return [];
96                 }
97
98                 $condition = ['access_token' => trim(substr($authorization, 7))];
99
100                 $token = DBA::selectFirst('application-view', ['uid', 'id', 'name', 'website', 'created_at', 'read', 'write', 'follow', 'push'], $condition);
101                 if (!DBA::isResult($token)) {
102                         Logger::notice('Token not found', $condition);
103                         return [];
104                 }
105                 Logger::debug('Token found', $token);
106
107                 User::updateLastActivity($token['uid']);
108
109                 // Regularly update suggestions
110                 if (Contact\Relation::areSuggestionsOutdated($token['uid'])) {
111                         Worker::add(Worker::PRIORITY_MEDIUM, 'UpdateSuggestions', $token['uid']);
112                 }
113
114                 return $token;
115         }
116
117         /**
118          * Get the application record via the provided request header fields
119          *
120          * @param string $client_id
121          * @param string $client_secret
122          * @param string $redirect_uri
123          * @return array application record
124          */
125         public static function getApplication(string $client_id, string $client_secret, string $redirect_uri)
126         {
127                 $condition = ['client_id' => $client_id];
128                 if (!empty($client_secret)) {
129                         $condition['client_secret'] = $client_secret;
130                 }
131                 if (!empty($redirect_uri)) {
132                         $condition['redirect_uri'] = $redirect_uri;
133                 }
134
135                 $application = DBA::selectFirst('application', [], $condition);
136                 if (!DBA::isResult($application)) {
137                         Logger::warning('Application not found', $condition);
138                         return [];
139                 }
140                 return $application;
141         }
142
143         /**
144          * Check if an token for the application and user exists
145          *
146          * @param array $application
147          * @param integer $uid
148          * @return boolean
149          */
150         public static function existsTokenForUser(array $application, int $uid)
151         {
152                 return DBA::exists('application-token', ['application-id' => $application['id'], 'uid' => $uid]);
153         }
154
155         /**
156          * Fetch the token for the given application and user
157          *
158          * @param array $application
159          * @param integer $uid
160          * @return array application record
161          */
162         public static function getTokenForUser(array $application, int $uid)
163         {
164                 return DBA::selectFirst('application-token', [], ['application-id' => $application['id'], 'uid' => $uid]);
165         }
166
167         /**
168          * Create and fetch an token for the application and user
169          *
170          * @param array   $application
171          * @param integer $uid
172          * @param string  $scope
173          * @return array application record
174          */
175         public static function createTokenForUser(array $application, int $uid, string $scope)
176         {
177                 $code         = bin2hex(random_bytes(32));
178                 $access_token = bin2hex(random_bytes(32));
179
180                 $fields = [
181                         'application-id' => $application['id'],
182                         'uid'            => $uid,
183                         'code'           => $code,
184                         'access_token'   => $access_token,
185                         'scopes'         => $scope,
186                         'read'           => (stripos($scope, BaseApi::SCOPE_READ) !== false),
187                         'write'          => (stripos($scope, BaseApi::SCOPE_WRITE) !== false),
188                         'follow'         => (stripos($scope, BaseApi::SCOPE_FOLLOW) !== false),
189                         'push'           => (stripos($scope, BaseApi::SCOPE_PUSH) !== false),
190                         'created_at'     => DateTimeFormat::utcNow()];
191
192                 foreach ([BaseApi::SCOPE_READ, BaseApi::SCOPE_WRITE, BaseApi::SCOPE_WRITE, BaseApi::SCOPE_PUSH] as $scope) {
193                         if ($fields[$scope] && !$application[$scope]) {
194                                 Logger::warning('Requested token scope is not allowed for the application', ['token' => $fields, 'application' => $application]);
195                         }
196                 }
197
198                 if (!DBA::insert('application-token', $fields, Database::INSERT_UPDATE)) {
199                         return [];
200                 }
201
202                 return DBA::selectFirst('application-token', [], ['application-id' => $application['id'], 'uid' => $uid]);
203         }
204 }