]> git.mxchange.org Git - friendica.git/blob - src/Model/User/Cookie.php
Merge pull request #13172 from annando/parent-view
[friendica.git] / src / Model / User / Cookie.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\Model\User;
23
24 use Friendica\App;
25 use Friendica\Core\Config\Capability\IManageConfigValues;
26
27 /**
28  * Interacting with the Friendica Cookie of a user
29  */
30 class Cookie
31 {
32         /** @var int Default expire duration in days */
33         const DEFAULT_EXPIRE = 7;
34         /** @var string The name of the Friendica cookie */
35         const NAME = 'Friendica';
36         /** @var string The path of the Friendica cookie */
37         const PATH = '/';
38         /** @var string The domain name of the Friendica cookie */
39         const DOMAIN = '';
40         /** @var bool True, if the cookie should only be accessible through HTTP */
41         const HTTPONLY = true;
42
43         /** @var string The remote address of this node */
44         private $remoteAddr;
45         /** @var bool True, if the connection is ssl enabled */
46         private $sslEnabled;
47         /** @var string The private key of this Friendica node */
48         private $sitePrivateKey;
49         /** @var int The default cookie lifetime */
50         private $lifetime;
51         /** @var array The Friendica cookie data array */
52         private $data;
53
54         /**
55          * @param App\Request         $request The current http request
56          * @param IManageConfigValues $config
57          * @param App\BaseURL         $baseURL
58          * @param array               $COOKIE The $_COOKIE array
59          */
60         public function __construct(App\Request $request, IManageConfigValues $config, App\BaseURL $baseURL, array $COOKIE = [])
61         {
62                 $this->sslEnabled     = $baseURL->getScheme() === 'https';
63                 $this->sitePrivateKey = $config->get('system', 'site_prvkey');
64
65                 $authCookieDays = $config->get('system', 'auth_cookie_lifetime',
66                         self::DEFAULT_EXPIRE);
67                 $this->lifetime = $authCookieDays * 24 * 60 * 60;
68
69                 $this->remoteAddr = $request->getRemoteAddress();
70
71                 $this->data = json_decode($COOKIE[self::NAME] ?? '[]', true) ?: [];
72         }
73
74         /**
75          * Returns the value for a key of the Friendica cookie
76          *
77          * @param string $key
78          * @param mixed  $default
79          * @return mixed|null The value for the provided cookie key
80          */
81         public function get(string $key, $default = null)
82         {
83                 return $this->data[$key] ?? $default;
84         }
85
86         /**
87          * Set a single cookie key value.
88          * Overwrites an existing value with the same key.
89          *
90          * @param $key
91          * @param $value
92          * @return bool
93          */
94         public function set($key, $value): bool
95         {
96                 return $this->setMultiple([$key => $value]);
97         }
98
99         /**
100          * Sets multiple cookie key values.
101          * Overwrites existing values with the same key.
102          *
103          * @param array $values
104          * @return bool
105          */
106         public function setMultiple(array $values): bool
107         {
108                 $this->data = $values + $this->data;
109
110                 return $this->send();
111         }
112
113         /**
114          * Remove a cookie key
115          *
116          * @param string $key
117          */
118         public function unset(string $key)
119         {
120                 if (isset($this->data[$key])) {
121                         unset($this->data[$key]);
122
123                         $this->send();
124                 }
125         }
126
127         /**
128          * Resets the cookie to a given data set
129          *
130          * @param array $data
131          *
132          * @return bool
133          */
134         public function reset(array $data): bool
135         {
136                 return $this->clear() &&
137                            $this->setMultiple($data);
138         }
139
140         /**
141          * Clears the Friendica cookie
142          */
143         public function clear(): bool
144         {
145                 $this->data = [];
146                 // make sure cookie is deleted on browser close, as a security measure
147                 return $this->setCookie('', -3600, $this->sslEnabled);
148         }
149
150         /**
151          * Send the cookie, should be called every time $this->data is changed or to refresh the cookie.
152          *
153          * @return bool
154          */
155         public function send(): bool
156         {
157                 return $this->setCookie(
158                         json_encode(['ip' => $this->remoteAddr] + $this->data),
159                         $this->lifetime + time(),
160                         $this->sslEnabled
161                 );
162         }
163
164         /**
165          * setcookie() wrapper: protected, internal function for test-mocking possibility
166          *
167          * @link  https://php.net/manual/en/function.setcookie.php
168          *
169          * @param string $value  [optional]
170          * @param int    $expire [optional]
171          * @param bool   $secure [optional]
172          *
173          * @return bool If output exists prior to calling this function,
174          *
175          */
176         protected function setCookie(string $value = null, int $expire = null,
177                                                                  bool $secure = null): bool
178         {
179                 return setcookie(self::NAME, $value, $expire, self::PATH, self::DOMAIN, $secure, self::HTTPONLY);
180         }
181
182         /**
183          * Calculate a hash of a user's private data for storage in the cookie.
184          * Hashed twice, with the user's own private key first, then the node's private key second.
185          *
186          * @param string $privateData User private data
187          * @param string $privateKey  User private key
188          *
189          * @return string Hashed data
190          */
191         public function hashPrivateData(string $privateData, string $privateKey): string
192         {
193                 return hash_hmac(
194                         'sha256',
195                         hash_hmac('sha256', $privateData, $privateKey),
196                         $this->sitePrivateKey
197                 );
198         }
199
200         /**
201          * @param string $hash        Hash from a cookie key value
202          * @param string $privateData User private data
203          * @param string $privateKey  User private key
204          *
205          * @return boolean
206          *
207          */
208         public function comparePrivateDataHash(string $hash, string $privateData, string $privateKey): bool
209         {
210                 return hash_equals(
211                         $this->hashPrivateData($privateData, $privateKey),
212                         $hash
213                 );
214         }
215 }