Passed
Push — dependabot/npm_and_yarn/sass-1... ( dd05dd )
by
unknown
05:21
created
includes/Pages/UserAuth/Login/PageOtpLogin.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -13,18 +13,18 @@
 block discarded – undo
13 13
 
14 14
 class PageOtpLogin extends LoginCredentialPageBase
15 15
 {
16
-    protected function providerSpecificSetup()
17
-    {
18
-        $this->setTemplate('login/otp.tpl');
19
-    }
16
+	protected function providerSpecificSetup()
17
+	{
18
+		$this->setTemplate('login/otp.tpl');
19
+	}
20 20
 
21
-    protected function getProviderCredentials()
22
-    {
23
-        $otp = WebRequest::postString("otp");
24
-        if ($otp === null || $otp === "") {
25
-            throw new ApplicationLogicException("No one-time code specified");
26
-        }
21
+	protected function getProviderCredentials()
22
+	{
23
+		$otp = WebRequest::postString("otp");
24
+		if ($otp === null || $otp === "") {
25
+			throw new ApplicationLogicException("No one-time code specified");
26
+		}
27 27
 
28
-        return $otp;
29
-    }
28
+		return $otp;
29
+	}
30 30
 }
31 31
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Pages/UserAuth/PageOAuthCallback.php 2 patches
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -116,8 +116,7 @@
 block discarded – undo
116 116
         $redirectDestination = WebRequest::clearPostLoginRedirect();
117 117
         if ($redirectDestination !== null && !$user->isNewUser()) {
118 118
             $this->redirectUrl($redirectDestination);
119
-        }
120
-        else {
119
+        } else {
121 120
             $this->redirect('preferences', null, null, 'internal.php');
122 121
         }
123 122
     }
Please login to merge, or discard this patch.
Indentation   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -18,93 +18,93 @@
 block discarded – undo
18 18
 
19 19
 class PageOAuthCallback extends InternalPageBase
20 20
 {
21
-    /**
22
-     * @return bool
23
-     */
24
-    protected function isProtectedPage()
25
-    {
26
-        // This page is critical to ensuring OAuth functionality is operational.
27
-        return false;
28
-    }
21
+	/**
22
+	 * @return bool
23
+	 */
24
+	protected function isProtectedPage()
25
+	{
26
+		// This page is critical to ensuring OAuth functionality is operational.
27
+		return false;
28
+	}
29 29
 
30
-    /**
31
-     * Main function for this page, when no specific actions are called.
32
-     * @return void
33
-     */
34
-    protected function main()
35
-    {
36
-        // This should never get hit except by URL manipulation.
37
-        $this->redirect('');
38
-    }
30
+	/**
31
+	 * Main function for this page, when no specific actions are called.
32
+	 * @return void
33
+	 */
34
+	protected function main()
35
+	{
36
+		// This should never get hit except by URL manipulation.
37
+		$this->redirect('');
38
+	}
39 39
 
40
-    /**
41
-     * Registered endpoint for the account creation callback.
42
-     *
43
-     * If this ever gets hit, something is wrong somewhere.
44
-     */
45
-    protected function create()
46
-    {
47
-        throw new Exception('OAuth account creation endpoint triggered.');
48
-    }
40
+	/**
41
+	 * Registered endpoint for the account creation callback.
42
+	 *
43
+	 * If this ever gets hit, something is wrong somewhere.
44
+	 */
45
+	protected function create()
46
+	{
47
+		throw new Exception('OAuth account creation endpoint triggered.');
48
+	}
49 49
 
50
-    /**
51
-     * Callback entry point
52
-     * @throws ApplicationLogicException
53
-     * @throws OptimisticLockFailedException
54
-     */
55
-    protected function authorise()
56
-    {
57
-        $oauthToken = WebRequest::getString('oauth_token');
58
-        $oauthVerifier = WebRequest::getString('oauth_verifier');
50
+	/**
51
+	 * Callback entry point
52
+	 * @throws ApplicationLogicException
53
+	 * @throws OptimisticLockFailedException
54
+	 */
55
+	protected function authorise()
56
+	{
57
+		$oauthToken = WebRequest::getString('oauth_token');
58
+		$oauthVerifier = WebRequest::getString('oauth_verifier');
59 59
 
60
-        $this->doCallbackValidation($oauthToken, $oauthVerifier);
60
+		$this->doCallbackValidation($oauthToken, $oauthVerifier);
61 61
 
62
-        $database = $this->getDatabase();
62
+		$database = $this->getDatabase();
63 63
 
64
-        $user = OAuthUserHelper::findUserByRequestToken($oauthToken, $database);
65
-        $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration());
64
+		$user = OAuthUserHelper::findUserByRequestToken($oauthToken, $database);
65
+		$oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration());
66 66
 
67
-        try {
68
-            $oauth->completeHandshake($oauthVerifier);
69
-        }
70
-        catch (CurlException $ex) {
71
-            throw new ApplicationLogicException($ex->getMessage(), 0, $ex);
72
-        }
67
+		try {
68
+			$oauth->completeHandshake($oauthVerifier);
69
+		}
70
+		catch (CurlException $ex) {
71
+			throw new ApplicationLogicException($ex->getMessage(), 0, $ex);
72
+		}
73 73
 
74
-        // OK, we're the same session that just did a partial login that was redirected to OAuth. Let's upgrade the
75
-        // login to a full login
76
-        if (WebRequest::getOAuthPartialLogin() === $user->getId()) {
77
-            WebRequest::setLoggedInUser($user);
78
-            $this->getDomainAccessManager()->switchToDefaultDomain($user);
79
-        }
74
+		// OK, we're the same session that just did a partial login that was redirected to OAuth. Let's upgrade the
75
+		// login to a full login
76
+		if (WebRequest::getOAuthPartialLogin() === $user->getId()) {
77
+			WebRequest::setLoggedInUser($user);
78
+			$this->getDomainAccessManager()->switchToDefaultDomain($user);
79
+		}
80 80
 
81
-        // My thinking is there are three cases here:
82
-        //   a) new user => redirect to prefs - it's the only thing they can access other than stats
83
-        //   b) existing user hit the connect button in prefs => redirect to prefs since it's where they were
84
-        //   c) existing user logging in => redirect to wherever they came from
85
-        $redirectDestination = WebRequest::clearPostLoginRedirect();
86
-        if ($redirectDestination !== null && !$user->isNewUser()) {
87
-            $this->redirectUrl($redirectDestination);
88
-        }
89
-        else {
90
-            $this->redirect('preferences', null, null, 'internal.php');
91
-        }
92
-    }
81
+		// My thinking is there are three cases here:
82
+		//   a) new user => redirect to prefs - it's the only thing they can access other than stats
83
+		//   b) existing user hit the connect button in prefs => redirect to prefs since it's where they were
84
+		//   c) existing user logging in => redirect to wherever they came from
85
+		$redirectDestination = WebRequest::clearPostLoginRedirect();
86
+		if ($redirectDestination !== null && !$user->isNewUser()) {
87
+			$this->redirectUrl($redirectDestination);
88
+		}
89
+		else {
90
+			$this->redirect('preferences', null, null, 'internal.php');
91
+		}
92
+	}
93 93
 
94
-    /**
95
-     * @param string $oauthToken
96
-     * @param string $oauthVerifier
97
-     *
98
-     * @throws ApplicationLogicException
99
-     */
100
-    private function doCallbackValidation($oauthToken, $oauthVerifier)
101
-    {
102
-        if ($oauthToken === null) {
103
-            throw new ApplicationLogicException('No token provided');
104
-        }
94
+	/**
95
+	 * @param string $oauthToken
96
+	 * @param string $oauthVerifier
97
+	 *
98
+	 * @throws ApplicationLogicException
99
+	 */
100
+	private function doCallbackValidation($oauthToken, $oauthVerifier)
101
+	{
102
+		if ($oauthToken === null) {
103
+			throw new ApplicationLogicException('No token provided');
104
+		}
105 105
 
106
-        if ($oauthVerifier === null) {
107
-            throw new ApplicationLogicException('No oauth verifier provided.');
108
-        }
109
-    }
106
+		if ($oauthVerifier === null) {
107
+			throw new ApplicationLogicException('No oauth verifier provided.');
108
+		}
109
+	}
110 110
 }
111 111
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Pages/UserAuth/PageChangePassword.php 2 patches
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -52,8 +52,7 @@
 block discarded – undo
52 52
             SessionAlert::success('Password changed successfully!');
53 53
 
54 54
             $this->redirect('preferences');
55
-        }
56
-        else {
55
+        } else {
57 56
             $this->assignCSRFToken();
58 57
             $this->setTemplate('preferences/changePassword.tpl');
59 58
             $this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js");
Please login to merge, or discard this patch.
Indentation   +57 added lines, -57 removed lines patch added patch discarded remove patch
@@ -17,71 +17,71 @@
 block discarded – undo
17 17
 
18 18
 class PageChangePassword extends InternalPageBase
19 19
 {
20
-    /**
21
-     * Main function for this page, when no specific actions are called.
22
-     * @return void
23
-     */
24
-    protected function main()
25
-    {
26
-        $this->setHtmlTitle('Change Password');
20
+	/**
21
+	 * Main function for this page, when no specific actions are called.
22
+	 * @return void
23
+	 */
24
+	protected function main()
25
+	{
26
+		$this->setHtmlTitle('Change Password');
27 27
 
28
-        if (WebRequest::wasPosted()) {
29
-            $this->validateCSRFToken();
30
-            try {
31
-                $oldPassword = WebRequest::postString('password');
32
-                $newPassword = WebRequest::postString('newpassword');
33
-                $newPasswordConfirmation = WebRequest::postString('newpasswordconfirm');
28
+		if (WebRequest::wasPosted()) {
29
+			$this->validateCSRFToken();
30
+			try {
31
+				$oldPassword = WebRequest::postString('password');
32
+				$newPassword = WebRequest::postString('newpassword');
33
+				$newPasswordConfirmation = WebRequest::postString('newpasswordconfirm');
34 34
 
35
-                $user = User::getCurrent($this->getDatabase());
36
-                if (!$user instanceof User) {
37
-                    throw new ApplicationLogicException('User not found');
38
-                }
35
+				$user = User::getCurrent($this->getDatabase());
36
+				if (!$user instanceof User) {
37
+					throw new ApplicationLogicException('User not found');
38
+				}
39 39
 
40
-                $this->validateNewPassword($oldPassword, $newPassword, $newPasswordConfirmation, $user);
40
+				$this->validateNewPassword($oldPassword, $newPassword, $newPasswordConfirmation, $user);
41 41
 
42
-                $passwordProvider = new PasswordCredentialProvider($this->getDatabase(), $this->getSiteConfiguration());
43
-                $passwordProvider->setCredential($user, 1, $newPassword);
44
-            }
45
-            catch (ApplicationLogicException $ex) {
46
-                SessionAlert::error($ex->getMessage());
47
-                $this->redirect('changePassword');
42
+				$passwordProvider = new PasswordCredentialProvider($this->getDatabase(), $this->getSiteConfiguration());
43
+				$passwordProvider->setCredential($user, 1, $newPassword);
44
+			}
45
+			catch (ApplicationLogicException $ex) {
46
+				SessionAlert::error($ex->getMessage());
47
+				$this->redirect('changePassword');
48 48
 
49
-                return;
50
-            }
49
+				return;
50
+			}
51 51
 
52
-            SessionAlert::success('Password changed successfully!');
52
+			SessionAlert::success('Password changed successfully!');
53 53
 
54
-            $this->redirect('preferences');
55
-        }
56
-        else {
57
-            $this->assignCSRFToken();
58
-            $this->setTemplate('preferences/changePassword.tpl');
59
-            $this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js");
60
-        }
61
-    }
54
+			$this->redirect('preferences');
55
+		}
56
+		else {
57
+			$this->assignCSRFToken();
58
+			$this->setTemplate('preferences/changePassword.tpl');
59
+			$this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js");
60
+		}
61
+	}
62 62
 
63
-    /**
64
-     * @param string $oldPassword
65
-     * @param string $newPassword
66
-     * @param string $newPasswordConfirmation
67
-     * @param User   $user
68
-     *
69
-     * @throws ApplicationLogicException
70
-     */
71
-    protected function validateNewPassword($oldPassword, $newPassword, $newPasswordConfirmation, User $user)
72
-    {
73
-        if ($oldPassword === null || $newPassword === null || $newPasswordConfirmation === null) {
74
-            throw new ApplicationLogicException('All three fields must be completed to change your password');
75
-        }
63
+	/**
64
+	 * @param string $oldPassword
65
+	 * @param string $newPassword
66
+	 * @param string $newPasswordConfirmation
67
+	 * @param User   $user
68
+	 *
69
+	 * @throws ApplicationLogicException
70
+	 */
71
+	protected function validateNewPassword($oldPassword, $newPassword, $newPasswordConfirmation, User $user)
72
+	{
73
+		if ($oldPassword === null || $newPassword === null || $newPasswordConfirmation === null) {
74
+			throw new ApplicationLogicException('All three fields must be completed to change your password');
75
+		}
76 76
 
77
-        if ($newPassword !== $newPasswordConfirmation) {
78
-            throw new ApplicationLogicException('Your new passwords did not match!');
79
-        }
77
+		if ($newPassword !== $newPasswordConfirmation) {
78
+			throw new ApplicationLogicException('Your new passwords did not match!');
79
+		}
80 80
 
81
-        // TODO: adapt for MFA support
82
-        $passwordProvider = new PasswordCredentialProvider($this->getDatabase(), $this->getSiteConfiguration());
83
-        if (!$passwordProvider->authenticate($user, $oldPassword)) {
84
-            throw new ApplicationLogicException('The password you entered was incorrect.');
85
-        }
86
-    }
81
+		// TODO: adapt for MFA support
82
+		$passwordProvider = new PasswordCredentialProvider($this->getDatabase(), $this->getSiteConfiguration());
83
+		if (!$passwordProvider->authenticate($user, $oldPassword)) {
84
+			throw new ApplicationLogicException('The password you entered was incorrect.');
85
+		}
86
+	}
87 87
 }
Please login to merge, or discard this patch.
includes/Pages/UserAuth/PageForgotPassword.php 2 patches
Braces   +2 added lines, -4 removed lines patch added patch discarded remove patch
@@ -46,8 +46,7 @@  discard block
 block discarded – undo
46 46
             SessionAlert::success('<strong>Your password reset request has been completed.</strong> If the details you have provided match our records, you should receive an email shortly.');
47 47
 
48 48
             $this->redirect('login');
49
-        }
50
-        else {
49
+        } else {
51 50
             $this->assignCSRFToken();
52 51
             $this->setTemplate('forgot-password/forgotpw.tpl');
53 52
         }
@@ -130,8 +129,7 @@  discard block
 block discarded – undo
130 129
 
131 130
                 return;
132 131
             }
133
-        }
134
-        else {
132
+        } else {
135 133
             $this->assignCSRFToken();
136 134
             $this->assign('user', $user);
137 135
             $this->setTemplate('forgot-password/forgotpwreset.tpl');
Please login to merge, or discard this patch.
Indentation   +209 added lines, -209 removed lines patch added patch discarded remove patch
@@ -23,213 +23,213 @@
 block discarded – undo
23 23
 
24 24
 class PageForgotPassword extends InternalPageBase
25 25
 {
26
-    /**
27
-     * Main function for this page, when no specific actions are called.
28
-     *
29
-     * This is the forgotten password reset form
30
-     * @category Security-Critical
31
-     */
32
-    protected function main()
33
-    {
34
-        if (WebRequest::wasPosted()) {
35
-            $this->validateCSRFToken();
36
-            $username = WebRequest::postString('username');
37
-            $email = WebRequest::postEmail('email');
38
-            $database = $this->getDatabase();
39
-
40
-            if ($username === null || trim($username) === "" || $email === null || trim($email) === "") {
41
-                throw new ApplicationLogicException("Both username and email address must be specified!");
42
-            }
43
-
44
-            $user = User::getByUsername($username, $database);
45
-            $this->sendResetMail($user, $email);
46
-
47
-            SessionAlert::success('<strong>Your password reset request has been completed.</strong> If the details you have provided match our records, you should receive an email shortly.');
48
-
49
-            $this->redirect('login');
50
-        }
51
-        else {
52
-            $this->assignCSRFToken();
53
-            $this->setTemplate('forgot-password/forgotpw.tpl');
54
-        }
55
-    }
56
-
57
-    /**
58
-     * Sends a reset email if the user is authenticated
59
-     *
60
-     * @param User|boolean $user  The user located from the database, or false. Doesn't really matter, since we do the
61
-     *                            check anyway within this method and silently skip if we don't have a user.
62
-     * @param string       $email The provided email address
63
-     */
64
-    private function sendResetMail($user, $email)
65
-    {
66
-        // If the user isn't found, or the email address is wrong, skip sending the details silently.
67
-        if (!$user instanceof User) {
68
-            return;
69
-        }
70
-
71
-        if (strtolower($user->getEmail()) === strtolower($email)) {
72
-            $clientIp = $this->getXffTrustProvider()
73
-                ->getTrustedClientIp(WebRequest::remoteAddress(), WebRequest::forwardedAddress());
74
-
75
-            $this->cleanExistingTokens($user);
76
-
77
-            $hash = Base32::encodeUpper(openssl_random_pseudo_bytes(30));
78
-
79
-            $encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
80
-
81
-            $cred = new Credential();
82
-            $cred->setDatabase($this->getDatabase());
83
-            $cred->setFactor(-1);
84
-            $cred->setUserId($user->getId());
85
-            $cred->setType('reset');
86
-            $cred->setData($encryptionHelper->encryptData($hash));
87
-            $cred->setVersion(0);
88
-            $cred->setDisabled(0);
89
-            $cred->setTimeout(new DateTimeImmutable('+ 1 hour'));
90
-            $cred->setPriority(9);
91
-            $cred->save();
92
-
93
-            $this->assign("user", $user);
94
-            $this->assign("hash", $hash);
95
-            $this->assign("remoteAddress", $clientIp);
96
-
97
-            $emailContent = $this->fetchTemplate('forgot-password/reset-mail.tpl');
98
-
99
-            // FIXME: domains!
100
-            /** @var Domain $domain */
101
-            $domain = Domain::getById(1, $this->getDatabase());
102
-            $this->getEmailHelper()->sendMail(
103
-                null, $user->getEmail(), "WP:ACC password reset", $emailContent);
104
-        }
105
-    }
106
-
107
-    /**
108
-     * Entry point for the reset action
109
-     *
110
-     * This is the reset password part of the form.
111
-     * @category Security-Critical
112
-     */
113
-    protected function reset()
114
-    {
115
-        $si = WebRequest::getString('si');
116
-        $id = WebRequest::getString('id');
117
-
118
-        if ($si === null || trim($si) === "" || $id === null || trim($id) === "") {
119
-            throw new ApplicationLogicException("Link not valid, please ensure it has copied correctly");
120
-        }
121
-
122
-        $database = $this->getDatabase();
123
-        $user = $this->getResettingUser($id, $database, $si);
124
-
125
-        // Dual mode
126
-        if (WebRequest::wasPosted()) {
127
-            $this->validateCSRFToken();
128
-            try {
129
-                $this->doReset($user);
130
-                $this->cleanExistingTokens($user);
131
-            }
132
-            catch (ApplicationLogicException $ex) {
133
-                SessionAlert::error($ex->getMessage());
134
-                $this->redirect('forgotPassword', 'reset', array('si' => $si, 'id' => $id));
135
-
136
-                return;
137
-            }
138
-        }
139
-        else {
140
-            $this->assignCSRFToken();
141
-            $this->assign('user', $user);
142
-            $this->setTemplate('forgot-password/forgotpwreset.tpl');
143
-            $this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js");
144
-        }
145
-    }
146
-
147
-    /**
148
-     * Gets the user resetting their password from the database, or throwing an exception if that is not possible.
149
-     *
150
-     * @param integer     $id       The ID of the user to retrieve
151
-     * @param PdoDatabase $database The database object to use
152
-     * @param string      $si       The reset hash provided
153
-     *
154
-     * @return User
155
-     * @throws ApplicationLogicException
156
-     */
157
-    private function getResettingUser($id, $database, $si)
158
-    {
159
-        $user = User::getById($id, $database);
160
-
161
-        if ($user === false || $user->isCommunityUser()) {
162
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
163
-        }
164
-
165
-        $statement = $database->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
166
-        $statement->execute([':user' => $user->getId()]);
167
-
168
-        /** @var Credential $credential */
169
-        $credential = $statement->fetchObject(Credential::class);
170
-
171
-        $statement->closeCursor();
172
-
173
-        if ($credential === false) {
174
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
175
-        }
176
-
177
-        $credential->setDatabase($database);
178
-
179
-        $encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
180
-        if ($encryptionHelper->decryptData($credential->getData()) != $si) {
181
-            throw new ApplicationLogicException("Password reset failed. Please try again.");
182
-        }
183
-
184
-        if ($credential->getTimeout() < new DateTimeImmutable()) {
185
-            $credential->delete();
186
-            throw new ApplicationLogicException("Password reset token expired. Please try again.");
187
-        }
188
-
189
-        return $user;
190
-    }
191
-
192
-    /**
193
-     * Performs the setting of the new password
194
-     *
195
-     * @param User $user The user to set the password for
196
-     *
197
-     * @throws ApplicationLogicException
198
-     */
199
-    private function doReset(User $user)
200
-    {
201
-        $pw = WebRequest::postString('newpassword');
202
-        $pw2 = WebRequest::postString('newpasswordconfirm');
203
-
204
-        if ($pw !== $pw2) {
205
-            throw new ApplicationLogicException('Passwords do not match!');
206
-        }
207
-
208
-        $passwordCredentialProvider = new PasswordCredentialProvider($user->getDatabase(), $this->getSiteConfiguration());
209
-        $passwordCredentialProvider->setCredential($user, 1, $pw);
210
-
211
-        SessionAlert::success('You may now log in!');
212
-        $this->redirect('login');
213
-    }
214
-
215
-    protected function isProtectedPage()
216
-    {
217
-        return false;
218
-    }
219
-
220
-    /**
221
-     * @param $user
222
-     */
223
-    private function cleanExistingTokens($user): void
224
-    {
225
-        // clean out existing reset tokens
226
-        $statement = $this->getDatabase()->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
227
-        $statement->execute([':user' => $user->getId()]);
228
-        $existing = $statement->fetchAll(PdoDatabase::FETCH_CLASS, Credential::class);
229
-
230
-        foreach ($existing as $c) {
231
-            $c->setDatabase($this->getDatabase());
232
-            $c->delete();
233
-        }
234
-    }
26
+	/**
27
+	 * Main function for this page, when no specific actions are called.
28
+	 *
29
+	 * This is the forgotten password reset form
30
+	 * @category Security-Critical
31
+	 */
32
+	protected function main()
33
+	{
34
+		if (WebRequest::wasPosted()) {
35
+			$this->validateCSRFToken();
36
+			$username = WebRequest::postString('username');
37
+			$email = WebRequest::postEmail('email');
38
+			$database = $this->getDatabase();
39
+
40
+			if ($username === null || trim($username) === "" || $email === null || trim($email) === "") {
41
+				throw new ApplicationLogicException("Both username and email address must be specified!");
42
+			}
43
+
44
+			$user = User::getByUsername($username, $database);
45
+			$this->sendResetMail($user, $email);
46
+
47
+			SessionAlert::success('<strong>Your password reset request has been completed.</strong> If the details you have provided match our records, you should receive an email shortly.');
48
+
49
+			$this->redirect('login');
50
+		}
51
+		else {
52
+			$this->assignCSRFToken();
53
+			$this->setTemplate('forgot-password/forgotpw.tpl');
54
+		}
55
+	}
56
+
57
+	/**
58
+	 * Sends a reset email if the user is authenticated
59
+	 *
60
+	 * @param User|boolean $user  The user located from the database, or false. Doesn't really matter, since we do the
61
+	 *                            check anyway within this method and silently skip if we don't have a user.
62
+	 * @param string       $email The provided email address
63
+	 */
64
+	private function sendResetMail($user, $email)
65
+	{
66
+		// If the user isn't found, or the email address is wrong, skip sending the details silently.
67
+		if (!$user instanceof User) {
68
+			return;
69
+		}
70
+
71
+		if (strtolower($user->getEmail()) === strtolower($email)) {
72
+			$clientIp = $this->getXffTrustProvider()
73
+				->getTrustedClientIp(WebRequest::remoteAddress(), WebRequest::forwardedAddress());
74
+
75
+			$this->cleanExistingTokens($user);
76
+
77
+			$hash = Base32::encodeUpper(openssl_random_pseudo_bytes(30));
78
+
79
+			$encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
80
+
81
+			$cred = new Credential();
82
+			$cred->setDatabase($this->getDatabase());
83
+			$cred->setFactor(-1);
84
+			$cred->setUserId($user->getId());
85
+			$cred->setType('reset');
86
+			$cred->setData($encryptionHelper->encryptData($hash));
87
+			$cred->setVersion(0);
88
+			$cred->setDisabled(0);
89
+			$cred->setTimeout(new DateTimeImmutable('+ 1 hour'));
90
+			$cred->setPriority(9);
91
+			$cred->save();
92
+
93
+			$this->assign("user", $user);
94
+			$this->assign("hash", $hash);
95
+			$this->assign("remoteAddress", $clientIp);
96
+
97
+			$emailContent = $this->fetchTemplate('forgot-password/reset-mail.tpl');
98
+
99
+			// FIXME: domains!
100
+			/** @var Domain $domain */
101
+			$domain = Domain::getById(1, $this->getDatabase());
102
+			$this->getEmailHelper()->sendMail(
103
+				null, $user->getEmail(), "WP:ACC password reset", $emailContent);
104
+		}
105
+	}
106
+
107
+	/**
108
+	 * Entry point for the reset action
109
+	 *
110
+	 * This is the reset password part of the form.
111
+	 * @category Security-Critical
112
+	 */
113
+	protected function reset()
114
+	{
115
+		$si = WebRequest::getString('si');
116
+		$id = WebRequest::getString('id');
117
+
118
+		if ($si === null || trim($si) === "" || $id === null || trim($id) === "") {
119
+			throw new ApplicationLogicException("Link not valid, please ensure it has copied correctly");
120
+		}
121
+
122
+		$database = $this->getDatabase();
123
+		$user = $this->getResettingUser($id, $database, $si);
124
+
125
+		// Dual mode
126
+		if (WebRequest::wasPosted()) {
127
+			$this->validateCSRFToken();
128
+			try {
129
+				$this->doReset($user);
130
+				$this->cleanExistingTokens($user);
131
+			}
132
+			catch (ApplicationLogicException $ex) {
133
+				SessionAlert::error($ex->getMessage());
134
+				$this->redirect('forgotPassword', 'reset', array('si' => $si, 'id' => $id));
135
+
136
+				return;
137
+			}
138
+		}
139
+		else {
140
+			$this->assignCSRFToken();
141
+			$this->assign('user', $user);
142
+			$this->setTemplate('forgot-password/forgotpwreset.tpl');
143
+			$this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js");
144
+		}
145
+	}
146
+
147
+	/**
148
+	 * Gets the user resetting their password from the database, or throwing an exception if that is not possible.
149
+	 *
150
+	 * @param integer     $id       The ID of the user to retrieve
151
+	 * @param PdoDatabase $database The database object to use
152
+	 * @param string      $si       The reset hash provided
153
+	 *
154
+	 * @return User
155
+	 * @throws ApplicationLogicException
156
+	 */
157
+	private function getResettingUser($id, $database, $si)
158
+	{
159
+		$user = User::getById($id, $database);
160
+
161
+		if ($user === false || $user->isCommunityUser()) {
162
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
163
+		}
164
+
165
+		$statement = $database->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
166
+		$statement->execute([':user' => $user->getId()]);
167
+
168
+		/** @var Credential $credential */
169
+		$credential = $statement->fetchObject(Credential::class);
170
+
171
+		$statement->closeCursor();
172
+
173
+		if ($credential === false) {
174
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
175
+		}
176
+
177
+		$credential->setDatabase($database);
178
+
179
+		$encryptionHelper = new EncryptionHelper($this->getSiteConfiguration());
180
+		if ($encryptionHelper->decryptData($credential->getData()) != $si) {
181
+			throw new ApplicationLogicException("Password reset failed. Please try again.");
182
+		}
183
+
184
+		if ($credential->getTimeout() < new DateTimeImmutable()) {
185
+			$credential->delete();
186
+			throw new ApplicationLogicException("Password reset token expired. Please try again.");
187
+		}
188
+
189
+		return $user;
190
+	}
191
+
192
+	/**
193
+	 * Performs the setting of the new password
194
+	 *
195
+	 * @param User $user The user to set the password for
196
+	 *
197
+	 * @throws ApplicationLogicException
198
+	 */
199
+	private function doReset(User $user)
200
+	{
201
+		$pw = WebRequest::postString('newpassword');
202
+		$pw2 = WebRequest::postString('newpasswordconfirm');
203
+
204
+		if ($pw !== $pw2) {
205
+			throw new ApplicationLogicException('Passwords do not match!');
206
+		}
207
+
208
+		$passwordCredentialProvider = new PasswordCredentialProvider($user->getDatabase(), $this->getSiteConfiguration());
209
+		$passwordCredentialProvider->setCredential($user, 1, $pw);
210
+
211
+		SessionAlert::success('You may now log in!');
212
+		$this->redirect('login');
213
+	}
214
+
215
+	protected function isProtectedPage()
216
+	{
217
+		return false;
218
+	}
219
+
220
+	/**
221
+	 * @param $user
222
+	 */
223
+	private function cleanExistingTokens($user): void
224
+	{
225
+		// clean out existing reset tokens
226
+		$statement = $this->getDatabase()->prepare("SELECT * FROM credential WHERE type = 'reset' AND user = :user;");
227
+		$statement->execute([':user' => $user->getId()]);
228
+		$existing = $statement->fetchAll(PdoDatabase::FETCH_CLASS, Credential::class);
229
+
230
+		foreach ($existing as $c) {
231
+			$c->setDatabase($this->getDatabase());
232
+			$c->delete();
233
+		}
234
+	}
235 235
 }
Please login to merge, or discard this patch.
includes/API/IXmlApiAction.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -16,12 +16,12 @@
 block discarded – undo
16 16
  */
17 17
 interface IXmlApiAction extends IRoutedTask, IApiAction
18 18
 {
19
-    /**
20
-     * Method that runs API action
21
-     *
22
-     * @param DOMElement $apiDocument
23
-     *
24
-     * @return DOMElement The modified API document
25
-     */
26
-    public function executeApiAction(DOMElement $apiDocument);
19
+	/**
20
+	 * Method that runs API action
21
+	 *
22
+	 * @param DOMElement $apiDocument
23
+	 *
24
+	 * @return DOMElement The modified API document
25
+	 */
26
+	public function executeApiAction(DOMElement $apiDocument);
27 27
 }
Please login to merge, or discard this patch.
includes/API/IJsonApiAction.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -15,10 +15,10 @@
 block discarded – undo
15 15
  */
16 16
 interface IJsonApiAction extends IRoutedTask, IApiAction
17 17
 {
18
-    /**
19
-     * Method that runs API action
20
-     *
21
-     * @return object|array The modified API document
22
-     */
23
-    public function executeApiAction();
18
+	/**
19
+	 * Method that runs API action
20
+	 *
21
+	 * @return object|array The modified API document
22
+	 */
23
+	public function executeApiAction();
24 24
 }
Please login to merge, or discard this patch.
includes/API/Actions/UnknownAction.php 1 patch
Indentation   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -16,15 +16,15 @@
 block discarded – undo
16 16
  */
17 17
 class UnknownAction extends HelpAction implements IXmlApiAction
18 18
 {
19
-    public function executeApiAction(DOMElement $apiDocument)
20
-    {
21
-        $errorText = "Unknown API action specified.";
22
-        $errorNode = $this->document->createElement("error", $errorText);
23
-        $apiDocument->appendChild($errorNode);
19
+	public function executeApiAction(DOMElement $apiDocument)
20
+	{
21
+		$errorText = "Unknown API action specified.";
22
+		$errorNode = $this->document->createElement("error", $errorText);
23
+		$apiDocument->appendChild($errorNode);
24 24
 
25
-        $helpElement = $this->getHelpElement();
26
-        $apiDocument->appendChild($helpElement);
25
+		$helpElement = $this->getHelpElement();
26
+		$apiDocument->appendChild($helpElement);
27 27
 
28
-        return $apiDocument;
29
-    }
28
+		return $apiDocument;
29
+	}
30 30
 }
Please login to merge, or discard this patch.
includes/API/Actions/HelpAction.php 1 patch
Indentation   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -18,34 +18,34 @@
 block discarded – undo
18 18
  */
19 19
 class HelpAction extends XmlApiPageBase implements IXmlApiAction
20 20
 {
21
-    public function executeApiAction(DOMElement $apiDocument)
22
-    {
23
-        $helpElement = $this->getHelpElement();
24
-        $apiDocument->appendChild($helpElement);
25
-
26
-        return $apiDocument;
27
-    }
28
-
29
-    /**
30
-     * Gets the help information
31
-     * @return DOMElement
32
-     */
33
-    protected function getHelpElement()
34
-    {
35
-        $helpInfo = "API help can be found at https://github.com/enwikipedia-acc/waca/wiki/API";
36
-
37
-        $help = $this->document->createElement("help");
38
-        $helptext = $this->document->createElement("info", $helpInfo);
39
-        $helpactions = $this->document->createElement("actions");
40
-
41
-        foreach (ApiRequestRouter::getActionList() as $action) {
42
-            $actionElement = $this->document->createElement("action", $action);
43
-            $helpactions->appendChild($actionElement);
44
-        }
45
-
46
-        $help->appendChild($helptext);
47
-        $help->appendChild($helpactions);
48
-
49
-        return $help;
50
-    }
21
+	public function executeApiAction(DOMElement $apiDocument)
22
+	{
23
+		$helpElement = $this->getHelpElement();
24
+		$apiDocument->appendChild($helpElement);
25
+
26
+		return $apiDocument;
27
+	}
28
+
29
+	/**
30
+	 * Gets the help information
31
+	 * @return DOMElement
32
+	 */
33
+	protected function getHelpElement()
34
+	{
35
+		$helpInfo = "API help can be found at https://github.com/enwikipedia-acc/waca/wiki/API";
36
+
37
+		$help = $this->document->createElement("help");
38
+		$helptext = $this->document->createElement("info", $helpInfo);
39
+		$helpactions = $this->document->createElement("actions");
40
+
41
+		foreach (ApiRequestRouter::getActionList() as $action) {
42
+			$actionElement = $this->document->createElement("action", $action);
43
+			$helpactions->appendChild($actionElement);
44
+		}
45
+
46
+		$help->appendChild($helptext);
47
+		$help->appendChild($helpactions);
48
+
49
+		return $help;
50
+	}
51 51
 }
Please login to merge, or discard this patch.
includes/API/Actions/MonitorAction.php 1 patch
Indentation   +50 added lines, -50 removed lines patch added patch discarded remove patch
@@ -23,65 +23,65 @@
 block discarded – undo
23 23
  */
24 24
 class MonitorAction extends XmlApiPageBase implements IXmlApiAction
25 25
 {
26
-    /**
27
-     * @param DOMElement $apiDocument
28
-     *
29
-     * @return DOMElement
30
-     */
31
-    public function executeApiAction(DOMElement $apiDocument)
32
-    {
33
-        $now = new DateTime();
26
+	/**
27
+	 * @param DOMElement $apiDocument
28
+	 *
29
+	 * @return DOMElement
30
+	 */
31
+	public function executeApiAction(DOMElement $apiDocument)
32
+	{
33
+		$now = new DateTime();
34 34
 
35
-        $old = $this->getOldest();
36
-        $oldest = new DateTime($old);
35
+		$old = $this->getOldest();
36
+		$oldest = new DateTime($old);
37 37
 
38
-        $new = $this->getNewest();
39
-        $newest = new DateTime($new);
38
+		$new = $this->getNewest();
39
+		$newest = new DateTime($new);
40 40
 
41
-        $monitoringElement = $this->document->createElement("data");
42
-        $monitoringElement->setAttribute("date", $now->format('c'));
43
-        $monitoringElement->setAttribute("oldest", $old === null ? null : $oldest->format('c'));
44
-        $monitoringElement->setAttribute("newest", $new === null ? null : $newest->format('c'));
45
-        $apiDocument->appendChild($monitoringElement);
41
+		$monitoringElement = $this->document->createElement("data");
42
+		$monitoringElement->setAttribute("date", $now->format('c'));
43
+		$monitoringElement->setAttribute("oldest", $old === null ? null : $oldest->format('c'));
44
+		$monitoringElement->setAttribute("newest", $new === null ? null : $newest->format('c'));
45
+		$apiDocument->appendChild($monitoringElement);
46 46
 
47
-        return $apiDocument;
48
-    }
47
+		return $apiDocument;
48
+	}
49 49
 
50
-    /**
51
-     * @return string|null
52
-     */
53
-    private function getOldest()
54
-    {
55
-        $statement = $this->getDatabase()
56
-            ->prepare("SELECT min(date) FROM request WHERE email != :email AND ip != :ip;");
57
-        $successful = $statement->execute(array(
58
-            ':email' => $this->getSiteConfiguration()->getDataClearEmail(),
59
-            ':ip'    => $this->getSiteConfiguration()->getDataClearIp(),
60
-        ));
50
+	/**
51
+	 * @return string|null
52
+	 */
53
+	private function getOldest()
54
+	{
55
+		$statement = $this->getDatabase()
56
+			->prepare("SELECT min(date) FROM request WHERE email != :email AND ip != :ip;");
57
+		$successful = $statement->execute(array(
58
+			':email' => $this->getSiteConfiguration()->getDataClearEmail(),
59
+			':ip'    => $this->getSiteConfiguration()->getDataClearIp(),
60
+		));
61 61
 
62
-        if (!$successful) {
63
-            return null;
64
-        }
62
+		if (!$successful) {
63
+			return null;
64
+		}
65 65
 
66
-        $result = $statement->fetchColumn();
66
+		$result = $statement->fetchColumn();
67 67
 
68
-        return $result;
69
-    }
68
+		return $result;
69
+	}
70 70
 
71
-    /**
72
-     * @return string
73
-     */
74
-    private function getNewest()
75
-    {
76
-        $statement = $this->getDatabase()
77
-            ->prepare("SELECT max(date) FROM request WHERE email != :email AND ip != :ip;");
78
-        $statement->execute(array(
79
-            ':email' => $this->getSiteConfiguration()->getDataClearEmail(),
80
-            ':ip'    => $this->getSiteConfiguration()->getDataClearIp(),
81
-        ));
71
+	/**
72
+	 * @return string
73
+	 */
74
+	private function getNewest()
75
+	{
76
+		$statement = $this->getDatabase()
77
+			->prepare("SELECT max(date) FROM request WHERE email != :email AND ip != :ip;");
78
+		$statement->execute(array(
79
+			':email' => $this->getSiteConfiguration()->getDataClearEmail(),
80
+			':ip'    => $this->getSiteConfiguration()->getDataClearIp(),
81
+		));
82 82
 
83
-        $result = $statement->fetchColumn(0);
83
+		$result = $statement->fetchColumn(0);
84 84
 
85
-        return $result;
86
-    }
85
+		return $result;
86
+	}
87 87
 }
Please login to merge, or discard this patch.