]> git.mxchange.org Git - friendica.git/blob - src/Module/Login.php
Move Temporal::convert() to DateTimeFormat::convert()
[friendica.git] / src / Module / Login.php
1 <?php
2 /**
3  * @file src/Module/Login.php
4  */
5 namespace Friendica\Module;
6
7 use Friendica\BaseModule;
8 use Friendica\Core\Addon;
9 use Friendica\Core\Config;
10 use Friendica\Core\L10n;
11 use Friendica\Database\DBM;
12 use Friendica\Model\User;
13 use Friendica\Util\DateTimeFormat;
14 use Friendica\Util\Network;
15 use dba;
16 use Exception;
17 use LightOpenID;
18
19 require_once 'boot.php';
20 require_once 'include/datetime.php';
21 require_once 'include/security.php';
22 require_once 'include/text.php';
23
24 /**
25  * Login module
26  *
27  * @author Hypolite Petovan mrpetovan@gmail.com
28  */
29 class Login extends BaseModule
30 {
31         public static function content()
32         {
33                 $a = self::getApp();
34
35                 if (x($_SESSION, 'theme')) {
36                         unset($_SESSION['theme']);
37                 }
38
39                 if (x($_SESSION, 'mobile-theme')) {
40                         unset($_SESSION['mobile-theme']);
41                 }
42
43                 if (local_user()) {
44                         goaway(self::getApp()->get_baseurl());
45                 }
46
47                 return self::form(self::getApp()->get_baseurl(), $a->config['register_policy'] != REGISTER_CLOSED);
48         }
49
50         public static function post()
51         {
52                 session_unset();
53                 // OpenId Login
54                 if (
55                         !x($_POST, 'password')
56                         && (
57                                 x($_POST, 'openid_url')
58                                 || x($_POST, 'username')
59                         )
60                 ) {
61                         $noid = Config::get('system', 'no_openid');
62
63                         $openid_url = trim($_POST['openid_url'] ? : $_POST['username']);
64
65                         // if it's an email address or doesn't resolve to a URL, fail.
66                         if ($noid || strpos($openid_url, '@') || !Network::isUrlValid($openid_url)) {
67                                 notice(L10n::t('Login failed.') . EOL);
68                                 goaway(self::getApp()->get_baseurl());
69                                 // NOTREACHED
70                         }
71
72                         // Otherwise it's probably an openid.
73                         try {
74                                 $openid = new LightOpenID;
75                                 $openid->identity = $openid_url;
76                                 $_SESSION['openid'] = $openid_url;
77                                 $_SESSION['remember'] = $_POST['remember'];
78                                 $openid->returnUrl = self::getApp()->get_baseurl(true) . '/openid';
79                                 goaway($openid->authUrl());
80                         } catch (Exception $e) {
81                                 notice(L10n::t('We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID.') . '<br /><br >' . L10n::t('The error message was:') . ' ' . $e->getMessage());
82                         }
83                         // NOTREACHED
84                 }
85
86                 if (x($_POST, 'auth-params') && $_POST['auth-params'] === 'login') {
87                         $record = null;
88
89                         $addon_auth = [
90                                 'username' => trim($_POST['username']),
91                                 'password' => trim($_POST['password']),
92                                 'authenticated' => 0,
93                                 'user_record' => null
94                         ];
95
96                         /*
97                          * An addon indicates successful login by setting 'authenticated' to non-zero value and returning a user record
98                          * Addons should never set 'authenticated' except to indicate success - as hooks may be chained
99                          * and later addons should not interfere with an earlier one that succeeded.
100                          */
101                         Addon::callHooks('authenticate', $addon_auth);
102
103                         if ($addon_auth['authenticated'] && count($addon_auth['user_record'])) {
104                                 $record = $addon_auth['user_record'];
105                         } else {
106                                 $user_id = User::authenticate(trim($_POST['username']), trim($_POST['password']));
107                                 if ($user_id) {
108                                         $record = dba::selectFirst('user', [], ['uid' => $user_id]);
109                                 }
110                         }
111
112                         if (!$record || !count($record)) {
113                                 logger('authenticate: failed login attempt: ' . notags(trim($_POST['username'])) . ' from IP ' . $_SERVER['REMOTE_ADDR']);
114                                 notice(L10n::t('Login failed.') . EOL);
115                                 goaway(self::getApp()->get_baseurl());
116                         }
117
118                         if (!$_POST['remember']) {
119                                 new_cookie(0); // 0 means delete on browser exit
120                         }
121
122                         // if we haven't failed up this point, log them in.
123                         $_SESSION['remember'] = $_POST['remember'];
124                         $_SESSION['last_login_date'] = DateTimeFormat::utcNow();
125                         authenticate_success($record, true, true);
126
127                         if (x($_SESSION, 'return_url')) {
128                                 $return_url = $_SESSION['return_url'];
129                                 unset($_SESSION['return_url']);
130                         } else {
131                                 $return_url = '';
132                         }
133
134                         goaway($return_url);
135                 }
136         }
137
138         /**
139          * @brief Tries to auth the user from the cookie or session
140          *
141          * @todo Should be moved to Friendica\Core\Session when it's created
142          */
143         public static function sessionAuth()
144         {
145                 // When the "Friendica" cookie is set, take the value to authenticate and renew the cookie.
146                 if (isset($_COOKIE["Friendica"])) {
147                         $data = json_decode($_COOKIE["Friendica"]);
148                         if (isset($data->uid)) {
149
150                                 $user = dba::selectFirst('user', [],
151                                         [
152                                                 'uid'             => $data->uid,
153                                                 'blocked'         => false,
154                                                 'account_expired' => false,
155                                                 'account_removed' => false,
156                                                 'verified'        => true,
157                                         ]
158                                 );
159                                 if (DBM::is_result($user)) {
160                                         if ($data->hash != cookie_hash($user)) {
161                                                 logger("Hash for user " . $data->uid . " doesn't fit.");
162                                                 nuke_session();
163                                                 goaway(self::getApp()->get_baseurl());
164                                         }
165
166                                         // Renew the cookie
167                                         // Expires after 7 days by default,
168                                         // can be set via system.auth_cookie_lifetime
169                                         $authcookiedays = Config::get('system', 'auth_cookie_lifetime', 7);
170                                         new_cookie($authcookiedays * 24 * 60 * 60, $user);
171
172                                         // Do the authentification if not done by now
173                                         if (!isset($_SESSION) || !isset($_SESSION['authenticated'])) {
174                                                 authenticate_success($user);
175
176                                                 if (Config::get('system', 'paranoia')) {
177                                                         $_SESSION['addr'] = $data->ip;
178                                                 }
179                                         }
180                                 }
181                         }
182                 }
183
184                 if (isset($_SESSION) && x($_SESSION, 'authenticated')) {
185                         if (x($_SESSION, 'visitor_id') && !x($_SESSION, 'uid')) {
186                                 $r = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
187                                         intval($_SESSION['visitor_id'])
188                                 );
189                                 if (DBM::is_result($r)) {
190                                         self::getApp()->contact = $r[0];
191                                 }
192                         }
193
194                         if (x($_SESSION, 'uid')) {
195                                 // already logged in user returning
196                                 $check = Config::get('system', 'paranoia');
197                                 // extra paranoia - if the IP changed, log them out
198                                 if ($check && ($_SESSION['addr'] != $_SERVER['REMOTE_ADDR'])) {
199                                         logger('Session address changed. Paranoid setting in effect, blocking session. ' .
200                                                 $_SESSION['addr'] . ' != ' . $_SERVER['REMOTE_ADDR']);
201                                         nuke_session();
202                                         goaway(self::getApp()->get_baseurl());
203                                 }
204
205                                 $user = dba::selectFirst('user', [],
206                                         [
207                                                 'uid'             => $_SESSION['uid'],
208                                                 'blocked'         => false,
209                                                 'account_expired' => false,
210                                                 'account_removed' => false,
211                                                 'verified'        => true,
212                                         ]
213                                 );
214                                 if (!DBM::is_result($user)) {
215                                         nuke_session();
216                                         goaway(self::getApp()->get_baseurl());
217                                 }
218
219                                 // Make sure to refresh the last login time for the user if the user
220                                 // stays logged in for a long time, e.g. with "Remember Me"
221                                 $login_refresh = false;
222                                 if (!x($_SESSION['last_login_date'])) {
223                                         $_SESSION['last_login_date'] = DateTimeFormat::utcNow();
224                                 }
225                                 if (strcmp(DateTimeFormat::utc('now - 12 hours'), $_SESSION['last_login_date']) > 0) {
226                                         $_SESSION['last_login_date'] = DateTimeFormat::utcNow();
227                                         $login_refresh = true;
228                                 }
229                                 authenticate_success($user, false, false, $login_refresh);
230                         }
231                 }
232         }
233
234         /**
235          * @brief Wrapper for adding a login box.
236          *
237          * @param string $return_url The url relative to the base the user should be sent
238          *                                                       back to after login completes
239          * @param bool $register If $register == true provide a registration link.
240          *                                               This will most always depend on the value of $a->config['register_policy'].
241          * @param array $hiddens  optional
242          *
243          * @return string Returns the complete html for inserting into the page
244          *
245          * @hooks 'login_hook' string $o
246          */
247         public static function form($return_url = null, $register = false, $hiddens = [])
248         {
249                 $a = self::getApp();
250                 $o = '';
251                 $reg = false;
252                 if ($register) {
253                         $reg = [
254                                 'title' => L10n::t('Create a New Account'),
255                                 'desc' => L10n::t('Register')
256                         ];
257                 }
258
259                 $noid = Config::get('system', 'no_openid');
260
261                 if (is_null($return_url)) {
262                         $return_url = $a->query_string;
263                 }
264
265                 if (local_user()) {
266                         $tpl = get_markup_template('logout.tpl');
267                 } else {
268                         $a->page['htmlhead'] .= replace_macros(
269                                 get_markup_template('login_head.tpl'),
270                                 [
271                                         '$baseurl' => $a->get_baseurl(true)
272                                 ]
273                         );
274
275                         $tpl = get_markup_template('login.tpl');
276                         $_SESSION['return_url'] = $return_url;
277                 }
278
279                 $o .= replace_macros(
280                         $tpl,
281                         [
282                                 '$dest_url'     => self::getApp()->get_baseurl(true) . '/login',
283                                 '$logout'       => L10n::t('Logout'),
284                                 '$login'        => L10n::t('Login'),
285
286                                 '$lname'        => ['username', L10n::t('Nickname or Email: ') , '', ''],
287                                 '$lpassword'    => ['password', L10n::t('Password: '), '', ''],
288                                 '$lremember'    => ['remember', L10n::t('Remember me'), 0,  ''],
289
290                                 '$openid'       => !$noid,
291                                 '$lopenid'      => ['openid_url', L10n::t('Or login using OpenID: '),'',''],
292
293                                 '$hiddens'      => $hiddens,
294
295                                 '$register'     => $reg,
296
297                                 '$lostpass'     => L10n::t('Forgot your password?'),
298                                 '$lostlink'     => L10n::t('Password Reset'),
299
300                                 '$tostitle'     => L10n::t('Website Terms of Service'),
301                                 '$toslink'      => L10n::t('terms of service'),
302
303                                 '$privacytitle' => L10n::t('Website Privacy Policy'),
304                                 '$privacylink'  => L10n::t('privacy policy'),
305                         ]
306                 );
307
308                 Addon::callHooks('login_hook', $o);
309
310                 return $o;
311         }
312 }