]> git.mxchange.org Git - friendica.git/blob - src/Model/User/Cookie.php
Merge pull request #8265 from nupplaphil/task/add_license
[friendica.git] / src / Model / User / Cookie.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\Model\User;
23
24 use Friendica\App;
25 use Friendica\Core\Config\IConfig;
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 = '0.0.0.0';
45         /** @var bool True, if the connection is ssl enabled */
46         private $sslEnabled = false;
47         /** @var string The private key of this Friendica node */
48         private $sitePrivateKey;
49         /** @var int The default cookie lifetime */
50         private $lifetime = self::DEFAULT_EXPIRE * 24 * 60 * 60;
51         /** @var array The $_COOKIE array */
52         private $cookie;
53
54         public function __construct(IConfig $config, App\BaseURL $baseURL, array $server = [], array $cookie = [])
55         {
56                 if (!empty($server['REMOTE_ADDR'])) {
57                         $this->remoteAddr = $server['REMOTE_ADDR'];
58                 }
59
60                 $this->sslEnabled     = $baseURL->getSSLPolicy() === App\BaseURL::SSL_POLICY_FULL;
61                 $this->sitePrivateKey = $config->get('system', 'site_prvkey');
62
63                 $authCookieDays = $config->get('system', 'auth_cookie_lifetime',
64                         self::DEFAULT_EXPIRE);
65                 $this->lifetime = $authCookieDays * 24 * 60 * 60;
66                 $this->cookie   = $cookie;
67         }
68
69         /**
70          * Checks if the Friendica cookie is set for a user
71          *
72          * @param string $hash       The cookie hash
73          * @param string $password   The user password
74          * @param string $privateKey The private Key of the user
75          *
76          * @return boolean True, if the cookie is set
77          *
78          */
79         public function check(string $hash, string $password, string $privateKey)
80         {
81                 return hash_equals(
82                         $this->getHash($password, $privateKey),
83                         $hash
84                 );
85         }
86
87         /**
88          * Set the Friendica cookie for a user
89          *
90          * @param int      $uid        The user id
91          * @param string   $password   The user password
92          * @param string   $privateKey The user private key
93          * @param int|null $seconds    optional the seconds
94          *
95          * @return bool
96          */
97         public function set(int $uid, string $password, string $privateKey, int $seconds = null)
98         {
99                 if (!isset($seconds)) {
100                         $seconds = $this->lifetime + time();
101                 } elseif (isset($seconds) && $seconds != 0) {
102                         $seconds = $seconds + time();
103                 }
104
105                 $value = json_encode([
106                         'uid'  => $uid,
107                         'hash' => $this->getHash($password, $privateKey),
108                         'ip'   => $this->remoteAddr,
109                 ]);
110
111                 return $this->setCookie(self::NAME, $value, $seconds, $this->sslEnabled);
112         }
113
114         /**
115          * Returns the data of the Friendicas user cookie
116          *
117          * @return mixed|null The JSON data, null if not set
118          */
119         public function getData()
120         {
121                 // When the "Friendica" cookie is set, take the value to authenticate and renew the cookie.
122                 if (isset($this->cookie[self::NAME])) {
123                         $data = json_decode($this->cookie[self::NAME]);
124                         if (!empty($data)) {
125                                 return $data;
126                         }
127                 }
128
129                 return null;
130         }
131
132         /**
133          * Clears the Friendica cookie of this user after leaving the page
134          */
135         public function clear()
136         {
137                 // make sure cookie is deleted on browser close, as a security measure
138                 return $this->setCookie(self::NAME, '', -3600, $this->sslEnabled);
139         }
140
141         /**
142          * Calculate the hash that is needed for the Friendica cookie
143          *
144          * @param string $password   The user password
145          * @param string $privateKey The private key of the user
146          *
147          * @return string Hashed data
148          */
149         private function getHash(string $password, string $privateKey)
150         {
151                 return hash_hmac(
152                         'sha256',
153                         hash_hmac('sha256', $password, $privateKey),
154                         $this->sitePrivateKey
155                 );
156         }
157
158         /**
159          * Send a cookie - protected, internal function for test-mocking possibility
160          *
161          * @link  https://php.net/manual/en/function.setcookie.php
162          *
163          * @param string $name
164          * @param string $value  [optional]
165          * @param int    $expire [optional]
166          * @param bool   $secure [optional]
167          *
168          * @return bool If output exists prior to calling this function,
169          *
170          */
171         protected function setCookie(string $name, string $value = null, int $expire = null,
172                                      bool $secure = null)
173         {
174                 return setcookie($name, $value, $expire, self::PATH, self::DOMAIN, $secure, self::HTTPONLY);
175         }
176 }