]> git.mxchange.org Git - friendica.git/blob - src/Model/User/Cookie.php
We now store the receivers as well
[friendica.git] / src / Model / User / Cookie.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2022, 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 IManageConfigValues $config
56          * @param App\BaseURL         $baseURL
57          * @param array               $SERVER The $_SERVER array
58          * @param array               $COOKIE The $_COOKIE array
59          */
60         public function __construct(IManageConfigValues $config, App\BaseURL $baseURL, array $SERVER = [], array $COOKIE = [])
61         {
62                 $this->sslEnabled     = $baseURL->getSSLPolicy() === App\BaseURL::SSL_POLICY_FULL;
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 = ($SERVER['REMOTE_ADDR'] ?? null) ?: '0.0.0.0';
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          * Clears the Friendica cookie
129          */
130         public function clear(): bool
131         {
132                 $this->data = [];
133                 // make sure cookie is deleted on browser close, as a security measure
134                 return $this->setCookie( '', -3600, $this->sslEnabled);
135         }
136
137         /**
138          * Send the cookie, should be called every time $this->data is changed or to refresh the cookie.
139          *
140          * @return bool
141          */
142         public function send(): bool
143         {
144                 return $this->setCookie(
145                         json_encode(['ip' => $this->remoteAddr] + $this->data),
146                         $this->lifetime + time(),
147                         $this->sslEnabled
148                 );
149         }
150
151         /**
152          * setcookie() wrapper: protected, internal function for test-mocking possibility
153          *
154          * @link  https://php.net/manual/en/function.setcookie.php
155          *
156          * @param string $value  [optional]
157          * @param int    $expire [optional]
158          * @param bool   $secure [optional]
159          *
160          * @return bool If output exists prior to calling this function,
161          *
162          */
163         protected function setCookie(string $value = null, int $expire = null,
164                                      bool $secure = null): bool
165         {
166                 return setcookie(self::NAME, $value, $expire, self::PATH, self::DOMAIN, $secure, self::HTTPONLY);
167         }
168
169         /**
170          * Calculate a hash of a user's private data for storage in the cookie.
171          * Hashed twice, with the user's own private key first, then the node's private key second.
172          *
173          * @param string $privateData User private data
174          * @param string $privateKey  User private key
175          *
176          * @return string Hashed data
177          */
178         public function hashPrivateData(string $privateData, string $privateKey): string
179         {
180                 return hash_hmac(
181                         'sha256',
182                         hash_hmac('sha256', $privateData, $privateKey),
183                         $this->sitePrivateKey
184                 );
185         }
186
187         /**
188          * @param string $hash        Hash from a cookie key value
189          * @param string $privateData User private data
190          * @param string $privateKey  User private key
191          *
192          * @return boolean
193          *
194          */
195         public function comparePrivateDataHash(string $hash, string $privateData, string $privateKey): bool
196         {
197                 return hash_equals(
198                         $this->hashPrivateData($privateData, $privateKey),
199                         $hash
200                 );
201         }
202 }