Completed
Pull Request — master (#32345)
by Sujith
11:02
created

ResetPassword::execute()   D

Complexity

Conditions 15
Paths 23

Size

Total Lines 95

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
nc 23
nop 2
dl 0
loc 95
rs 4.8423
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author Andreas Fischer <[email protected]>
4
 * @author Christopher Schäpers <[email protected]>
5
 * @author Clark Tomlinson <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Laurens Post <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Thomas Müller <[email protected]>
10
 * @author Sujith Haridasan <[email protected]>
11
 *
12
 * @copyright Copyright (c) 2018, ownCloud GmbH
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License, version 3,
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
29
namespace OC\Core\Command\User;
30
31
use OC\Helper\EnvironmentHelper;
32
use OCP\AppFramework\Http\TemplateResponse;
33
use OCP\AppFramework\Utility\ITimeFactory;
34
use OCP\IConfig;
35
use OCP\IL10N;
36
use OCP\IURLGenerator;
37
use OCP\IUserManager;
38
use OCP\Mail\IMailer;
39
use OCP\Security\ISecureRandom;
40
use OCP\Util;
41
use Symfony\Component\Console\Command\Command;
42
use Symfony\Component\Console\Input\InputInterface;
43
use Symfony\Component\Console\Input\InputArgument;
44
use Symfony\Component\Console\Input\InputOption;
45
use Symfony\Component\Console\Output\OutputInterface;
46
use Symfony\Component\Console\Question\Question;
47
48
class ResetPassword extends Command {
49
50
	/** @var IUserManager */
51
	protected $userManager;
52
	/** @var \OC_Defaults  */
53
	private $defaults;
54
	/** @var IURLGenerator  */
55
	private $urlgenerator;
56
	/** @var IConfig  */
57
	private $config;
58
	/** @var IMailer  */
59
	private $mailer;
60
	/** @var ISecureRandom  */
61
	private $secureRandom;
62
	/** @var ITimeFactory  */
63
	private $timeFactory;
64
	/** @var IL10N  */
65
	private $l10n;
66
	/** @var EnvironmentHelper  */
67
	private $environmentHelper;
68
69
	public function __construct(IUserManager $userManager,
70
						\OC_Defaults $defaults,
71
						IURLGenerator $urlgenerator,
72
						IConfig $config,
73
						IMailer $mailer,
74
						ISecureRandom $secureRandom,
75
						ITimeFactory $timeFactory,
76
						IL10N $l10n,
77
						EnvironmentHelper $environmentHelper) {
78
		$this->userManager = $userManager;
79
		$this->defaults = $defaults;
80
		$this->urlgenerator = $urlgenerator;
81
		$this->config = $config;
82
		$this->mailer = $mailer;
83
		$this->secureRandom = $secureRandom;
84
		$this->timeFactory = $timeFactory;
85
		$this->l10n = $l10n;
86
		$this->environmentHelper = $environmentHelper;
87
		parent::__construct();
88
	}
89
90
	protected function configure() {
91
		$this
92
			->setName('user:resetpassword')
93
			->setDescription('Resets the password of the named user.')
94
			->addArgument(
95
				'user',
96
				InputArgument::REQUIRED,
97
				'The user\'s name.'
98
			)
99
			->addOption(
100
				'password-from-env',
101
				null,
102
				InputOption::VALUE_NONE,
103
				'Read the password from the OC_PASS environment variable.'
104
			)
105
			->addOption(
106
				'send-email',
107
				null,
108
				InputOption::VALUE_NONE,
109
				'The email-id set while creating the user, will be used to send link for password reset. This option will also display the link sent to user.'
110
			)
111
			->addOption(
112
				'output-link',
113
				null,
114
				InputOption::VALUE_NONE,
115
				'The link to reset the password will be displayed.'
116
			)
117
		;
118
	}
119
120
	protected function execute(InputInterface $input, OutputInterface $output) {
121
		$username = $input->getArgument('user');
122
		$emailLink = $input->getOption('send-email');
123
		$displayLink = $input->getOption('output-link');
124
125
		/** @var $user \OCP\IUser */
126
		$user = $this->userManager->get($username);
127
		if ($user === null) {
128
			$output->writeln('<error>User does not exist</error>');
129
			return 1;
130
		}
131
132
		if ($input->getOption('password-from-env')) {
133
			$password = $this->environmentHelper->getEnvVar('OC_PASS');
134
			if (!$password) {
135
				$output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
136
				return 1;
137
			}
138
		} elseif ($emailLink | $displayLink) {
139
			$userId = $user->getUID();
140
			$token = $this->secureRandom->generate(21,
141
				ISecureRandom::CHAR_DIGITS,
142
				ISecureRandom::CHAR_LOWER, ISecureRandom::CHAR_UPPER);
0 ignored issues
show
Unused Code introduced by
The call to ISecureRandom::generate() has too many arguments starting with \OCP\Security\ISecureRandom::CHAR_LOWER.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
143
			$this->config->setUserValue($user->getUID(), 'owncloud',
144
				'lostpassword', $this->timeFactory->getTime() . ':' . $token);
145
			$mailData = [
146
				'link' => $this->urlgenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $userId, 'token' => $token])
147
			];
148
149
			if ($emailLink && $user->getEMailAddress() !== null) {
150
				$mail = new TemplateResponse('core', 'lostpassword/email', $mailData, 'blank');
151
				$mailContent = $mail->render();
152
153
				$subject = $this->l10n->t('Your password is reset');
154
				try {
155
					$message = $this->mailer->createMessage();
156
					$message->setTo([$user->getEMailAddress() => $userId]);
157
					$message->setSubject($subject);
158
					$message->setHtmlBody($mailContent);
0 ignored issues
show
Bug introduced by
It seems like $mailContent defined by $mail->render() on line 151 can also be of type boolean; however, OC\Mail\Message::setHtmlBody() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
159
					$message->setFrom([Util::getDefaultEmailAddress('no-reply') => $this->defaults->getName()]);
160
					$this->mailer->send($message);
161
				} catch (\Exception $e) {
162
					$output->writeln('<error>Can\'t send new user mail to ' . $user->getEMailAddress() . ': ' . $e->getMessage());
163
					return 1;
164
				}
165
			} else {
166
				if ($emailLink) {
167
					$output->writeln('<error>Email address is not set for the user ' . $user->getUID() . '</error>');
168
					return 1;
169
				}
170
			}
171
			$output->writeln('The password reset link is: ' . $mailData['link']);
172
			return 0;
173
		} elseif ($input->isInteractive()) {
174
			/** @var $dialog \Symfony\Component\Console\Helper\QuestionHelper */
175
			$dialog = $this->getHelperSet()->get('question');
176
177
			if (\OCP\App::isEnabled('encryption')) {
178
				$output->writeln(
179
					'<error>Warning: Resetting the password when using encryption will result in data loss!</error>'
180
				);
181
				if (!$dialog->ask($input, $output, new Question('<question>Do you want to continue?</question>', true))) {
182
					return 1;
183
				}
184
			}
185
186
			$q = new Question('<question>Enter a new password: </question>', false);
187
			$q->setHidden(true);
188
			$password = $dialog->ask($input, $output, $q);
189
			if ($password === false) {
190
				// When user presses RETURN key or no password characters are entered,
191
				// $password gets a boolean value false.
192
				$output->writeln("<error>Password cannot be empty!</error>");
193
				return 1;
194
			}
195
			$q = new Question('<question>Confirm the new password: </question>', false);
196
			$q->setHidden(true);
197
			$confirm = $dialog->ask($input, $output, $q);
198
			if ($password !== $confirm) {
199
				$output->writeln("<error>Passwords did not match!</error>");
200
				return 1;
201
			}
202
		} else {
203
			$output->writeln("<error>Interactive input or --password-from-env is needed for entering a new password!</error>");
204
			return 1;
205
		}
206
207
		$success = $user->setPassword($password);
208
		if ($success) {
209
			$output->writeln("<info>Successfully reset password for " . $username . "</info>");
210
		} else {
211
			$output->writeln("<error>Error while resetting password!</error>");
212
			return 1;
213
		}
214
	}
215
}
216