Merge pull request #9320 from tobiasd/20200929-adminHotfixVersions
[friendica.git/.git] / src / Network / FKOAuthDataStore.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
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\Network;
23
24 use Friendica\Core\Logger;
25 use Friendica\Database\DBA;
26 use Friendica\DI;
27 use Friendica\Util\Strings;
28 use OAuthConsumer;
29 use OAuthDataStore;
30 use OAuthToken;
31
32 define('REQUEST_TOKEN_DURATION', 300);
33 define('ACCESS_TOKEN_DURATION', 31536000);
34
35 /**
36  * OAuthDataStore class
37  */
38 class FKOAuthDataStore extends OAuthDataStore
39 {
40         /**
41          * @return string
42          * @throws \Exception
43          */
44         private static function genToken()
45         {
46                 return Strings::getRandomHex(32);
47         }
48
49         /**
50          * @param string $consumer_key key
51          * @return OAuthConsumer|null
52          * @throws \Exception
53          */
54         public function lookup_consumer($consumer_key)
55         {
56                 Logger::log(__function__ . ":" . $consumer_key);
57
58                 $s = DBA::select('clients', ['client_id', 'pw', 'redirect_uri'], ['client_id' => $consumer_key]);
59                 $r = DBA::toArray($s);
60
61                 if (DBA::isResult($r)) {
62                         return new OAuthConsumer($r[0]['client_id'], $r[0]['pw'], $r[0]['redirect_uri']);
63                 }
64
65                 return null;
66         }
67
68         /**
69          * @param OAuthConsumer $consumer
70          * @param string        $token_type
71          * @param string        $token_id
72          * @return OAuthToken|null
73          * @throws \Exception
74          */
75         public function lookup_token(OAuthConsumer $consumer, $token_type, $token_id)
76         {
77                 Logger::log(__function__ . ":" . $consumer . ", " . $token_type . ", " . $token_id);
78
79                 $s = DBA::select('tokens', ['id', 'secret', 'scope', 'expires', 'uid'], ['client_id' => $consumer->key, 'scope' => $token_type, 'id' => $token_id]);
80                 $r = DBA::toArray($s);
81
82                 if (DBA::isResult($r)) {
83                         $ot = new OAuthToken($r[0]['id'], $r[0]['secret']);
84                         $ot->scope = $r[0]['scope'];
85                         $ot->expires = $r[0]['expires'];
86                         $ot->uid = $r[0]['uid'];
87                         return $ot;
88                 }
89
90                 return null;
91         }
92
93         /**
94          * @param OAuthConsumer $consumer
95          * @param OAuthToken    $token
96          * @param string        $nonce
97          * @param int           $timestamp
98          * @return mixed
99          * @throws \Exception
100          */
101         public function lookup_nonce(OAuthConsumer $consumer, OAuthToken $token, $nonce, int $timestamp)
102         {
103                 $token = DBA::selectFirst('tokens', ['id', 'secret'], ['client_id' => $consumer->key, 'id' => $nonce, 'expires' => $timestamp]);
104                 if (DBA::isResult($token)) {
105                         return new OAuthToken($token['id'], $token['secret']);
106                 }
107
108                 return null;
109         }
110
111         /**
112          * @param OAuthConsumer $consumer
113          * @param string        $callback
114          * @return OAuthToken|null
115          * @throws \Exception
116          */
117         public function new_request_token(OAuthConsumer $consumer, $callback = null)
118         {
119                 Logger::log(__function__ . ":" . $consumer . ", " . $callback);
120                 $key = self::genToken();
121                 $sec = self::genToken();
122
123                 if ($consumer->key) {
124                         $k = $consumer->key;
125                 } else {
126                         $k = $consumer;
127                 }
128
129                 $r = DBA::insert(
130                         'tokens',
131                         [
132                                 'id' => $key,
133                                 'secret' => $sec,
134                                 'client_id' => $k,
135                                 'scope' => 'request',
136                                 'expires' => time() + REQUEST_TOKEN_DURATION
137                         ]
138                 );
139
140                 if (!$r) {
141                         return null;
142                 }
143
144                 return new OAuthToken($key, $sec);
145         }
146
147         /**
148          * @param OAuthToken    $token    token
149          * @param OAuthConsumer $consumer consumer
150          * @param string        $verifier optional, defult null
151          * @return OAuthToken
152          * @throws \Exception
153          */
154         public function new_access_token(OAuthToken $token, OAuthConsumer $consumer, $verifier = null)
155         {
156                 Logger::log(__function__ . ":" . $token . ", " . $consumer . ", " . $verifier);
157
158                 // return a new access token attached to this consumer
159                 // for the user associated with this token if the request token
160                 // is authorized
161                 // should also invalidate the request token
162
163                 $ret = null;
164
165                 // get user for this verifier
166                 $uverifier = DI::config()->get("oauth", $verifier);
167                 Logger::log(__function__ . ":" . $verifier . "," . $uverifier);
168
169                 if (is_null($verifier) || ($uverifier !== false)) {
170                         $key = self::genToken();
171                         $sec = self::genToken();
172                         $r = DBA::insert(
173                                 'tokens',
174                                 [
175                                         'id' => $key,
176                                         'secret' => $sec,
177                                         'client_id' => $consumer->key,
178                                         'scope' => 'access',
179                                         'expires' => time() + ACCESS_TOKEN_DURATION,
180                                         'uid' => $uverifier
181                                 ]
182                         );
183
184                         if ($r) {
185                                 $ret = new OAuthToken($key, $sec);
186                         }
187                 }
188
189                 DBA::delete('tokens', ['id' => $token->key]);
190
191                 if (!is_null($ret) && !is_null($uverifier)) {
192                         DI::config()->delete("oauth", $verifier);
193                 }
194
195                 return $ret;
196         }
197 }