Completed
Branch newinternal (cdd491)
by Simon
04:39
created

PageForgotPassword::reset()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 19
c 2
b 0
f 0
nc 4
nop 0
dl 0
loc 31
rs 6.7272
ccs 0
cts 26
cp 0
crap 56
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\Pages;
10
11
use Waca\DataObjects\User;
12
use Waca\Exceptions\ApplicationLogicException;
13
use Waca\PdoDatabase;
14
use Waca\Security\SecurityConfiguration;
15
use Waca\SessionAlert;
16
use Waca\Tasks\InternalPageBase;
17
use Waca\WebRequest;
18
19
class PageForgotPassword extends InternalPageBase
20
{
21
	/**
22
	 * Main function for this page, when no specific actions are called.
23
	 *
24
	 * This is the forgotten password reset form
25
	 * @category Security-Critical
26
	 */
27
	protected function main()
28
	{
29
		if (WebRequest::wasPosted()) {
30
			$this->validateCSRFToken();
31
			$username = WebRequest::postString('username');
32
			$email = WebRequest::postEmail('email');
33
			$database = $this->getDatabase();
34
35 View Code Duplication
			if ($username === null || trim($username) === "" || $email === null || trim($email) === "") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
36
				throw new ApplicationLogicException("Both username and email address must be specified!");
37
			}
38
39
			$user = User::getByUsername($username, $database);
40
			$this->sendResetMail($user, $email);
41
42
			SessionAlert::success('<strong>Your password reset request has been completed.</strong> Please check your e-mail.');
43
44
			$this->redirect('login');
45
		}
46
		else {
47
			$this->assignCSRFToken();
48
			$this->setTemplate('forgot-password/forgotpw.tpl');
49
		}
50
	}
51
52
	/**
53
	 * Sends a reset email if the user is authenticated
54
	 *
55
	 * @param User|boolean $user  The user located from the database, or false. Doesn't really matter, since we do the
56
	 *                            check anyway within this method and silently skip if we don't have a user.
57
	 * @param string       $email The provided email address
58
	 */
59
	private function sendResetMail($user, $email)
60
	{
61
		// If the user isn't found, or the email address is wrong, skip sending the details silently.
62
		if (!$user instanceof User) {
63
			return;
64
		}
65
66
		if (strtolower($user->getEmail()) === strtolower($email)) {
67
			$clientIp = $this->getXffTrustProvider()
68
				->getTrustedClientIp(WebRequest::remoteAddress(), WebRequest::forwardedAddress());
69
70
			$this->assign("user", $user);
71
			$this->assign("hash", $user->getForgottenPasswordHash());
72
			$this->assign("remoteAddress", $clientIp);
73
74
			$emailContent = $this->fetchTemplate('forgot-password/reset-mail.tpl');
75
76
			$this->getEmailHelper()->sendMail($user->getEmail(), "", $emailContent);
77
		}
78
	}
79
80
	/**
81
	 * Entry point for the reset action
82
	 *
83
	 * This is the reset password part of the form.
84
	 * @category Security-Critical
85
	 */
86
	protected function reset()
87
	{
88
		$si = WebRequest::getString('si');
89
		$id = WebRequest::getString('id');
90
91
		if ($si === null || trim($si) === "" || $id === null || trim($id) === "") {
92
			throw new ApplicationLogicException("Link not valid, please ensure it has copied correctly");
93
		}
94
95
		$database = $this->getDatabase();
96
		$user = $this->getResettingUser($id, $database, $si);
97
98
		// Dual mode
99
		if (WebRequest::wasPosted()) {
100
			$this->validateCSRFToken();
101
			try {
102
				$this->doReset($user);
103
			}
104
			catch (ApplicationLogicException $ex) {
105
				SessionAlert::error($ex->getMessage());
106
				$this->redirect('forgotPassword', 'reset', array('si' => $si, 'id' => $id));
107
108
				return;
109
			}
110
		}
111
		else {
112
			$this->assignCSRFToken();
113
			$this->assign('user', $user);
114
			$this->setTemplate('forgot-password/forgotpwreset.tpl');
115
		}
116
	}
117
118
	/**
119
	 * Gets the user resetting their password from the database, or throwing an exception if that is not possible.
120
	 *
121
	 * @param integer     $id       The ID of the user to retrieve
122
	 * @param PdoDatabase $database The database object to use
123
	 * @param string      $si       The reset hash provided
124
	 *
125
	 * @return User
126
	 * @throws ApplicationLogicException
127
	 */
128
	private function getResettingUser($id, $database, $si)
129
	{
130
		$user = User::getById($id, $database);
131
132
		if ($user === false || $user->getForgottenPasswordHash() !== $si || $user->isCommunityUser()) {
133
			throw new ApplicationLogicException("User not found");
134
		}
135
136
		return $user;
137
	}
138
139
	/**
140
	 * Performs the setting of the new password
141
	 *
142
	 * @param User $user The user to set the password for
143
	 *
144
	 * @throws ApplicationLogicException
145
	 */
146
	private function doReset(User $user)
147
	{
148
		$pw = WebRequest::postString('pw');
149
		$pw2 = WebRequest::postString('pw2');
150
151
		if ($pw !== $pw2) {
152
			throw new ApplicationLogicException('Passwords do not match!');
153
		}
154
155
		$user->setPassword($pw);
156
		$user->save();
157
158
		SessionAlert::success('You may now log in!');
159
		$this->redirect('login');
160
	}
161
162
	/**
163
	 * Sets up the security for this page. If certain actions have different permissions, this should be reflected in
164
	 * the return value from this function.
165
	 *
166
	 * If this page even supports actions, you will need to check the route
167
	 *
168
	 * @return SecurityConfiguration
169
	 * @category Security-Critical
170
	 */
171
	protected function getSecurityConfiguration()
172
	{
173
		return $this->getSecurityManager()->configure()->asPublicPage();
174
	}
175
}