Completed
Pull Request — master (#32767)
by Sujith
17:56 queued 07:59
created

Add::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 40
rs 9.28
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 * @author Laurens Post <[email protected]>
5
 * @author Thomas Müller <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2018, ownCloud GmbH
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OC\Core\Command\User;
25
26
use OC\AppFramework\Http;
27
use OC\Files\Filesystem;
28
use OC\User\Service\CreateUser;
29
use OC\User\User;
30
use OCP\IGroupManager;
31
use OCP\IUser;
32
use OCP\IUserManager;
33
use OCP\Mail\IMailer;
34
use Symfony\Component\Console\Command\Command;
35
use Symfony\Component\Console\Input\InputInterface;
36
use Symfony\Component\Console\Input\InputOption;
37
use Symfony\Component\Console\Output\OutputInterface;
38
use Symfony\Component\Console\Input\InputArgument;
39
use Symfony\Component\Console\Question\Question;
40
41
class Add extends Command {
42
	/** @var \OCP\IUserManager */
43
	protected $userManager;
44
45
	/** @var \OCP\IGroupManager */
46
	protected $groupManager;
47
48
	/** @var IMailer  */
49
	protected $mailer;
50
51
	/** @var CreateUser  */
52
	protected $createUser;
53
54
	/**
55
	 * Add User constructor.
56
	 *
57
	 * @param IUserManager $userManager
58
	 * @param IGroupManager $groupManager
59
	 * @param IMailer $mailer
60
	 * @param CreateUser $signinWithEmail
0 ignored issues
show
Bug introduced by
There is no parameter named $signinWithEmail. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
61
	 */
62
	public function __construct(IUserManager $userManager, IGroupManager $groupManager,
63
								IMailer $mailer, CreateUser $createUser) {
64
		parent::__construct();
65
		$this->userManager = $userManager;
66
		$this->groupManager = $groupManager;
67
		$this->mailer = $mailer;
68
		$this->createUser = $createUser;
69
	}
70
71
	protected function configure() {
72
		$this
73
			->setName('user:add')
74
			->setDescription('adds a user')
75
			->addArgument(
76
				'uid',
77
				InputArgument::REQUIRED,
78
				'User ID used to login (must only contain a-z, A-Z, 0-9, -, _ and @).'
79
			)
80
			->addOption(
81
				'password-from-env',
82
				null,
83
				InputOption::VALUE_NONE,
84
				'Read password from the OC_PASS environment variable.'
85
			)
86
			->addOption(
87
				'password-from-cmdline',
88
				'p',
89
				InputOption::VALUE_NONE,
90
				'Read password from user input'
91
			)
92
			->addOption(
93
				'display-name',
94
				null,
95
				InputOption::VALUE_OPTIONAL,
96
				'User name used in the web UI (can contain any characters).'
97
			)
98
			->addOption(
99
				'email',
100
				null,
101
				InputOption::VALUE_OPTIONAL,
102
				'Email address for the user.'
103
			)
104
			->addOption(
105
				'group',
106
				'g',
107
				InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
108
				'The groups the user should be added to (The group will be created if it does not exist).'
109
			);
110
	}
111
112
	protected function addUserToGroup(InputInterface $input, OutputInterface $output, IUser $user) {
113
		$groups = $input->getOption('group');
114
115
		if (!empty($groups)) {
116
			// Make sure we init the Filesystem for the user, in case we need to
117
			// init some group shares.
118
			Filesystem::init($user->getUID(), '');
119
		}
120
121
		foreach ($groups as $groupName) {
0 ignored issues
show
Bug introduced by
The expression $groups of type string|array<integer,string>|boolean|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
122
			$group = $this->groupManager->get($groupName);
123
			if (!$group) {
124
				$this->groupManager->createGroup($groupName);
125
				$group = $this->groupManager->get($groupName);
126
				$output->writeln('Created group "' . $group->getGID() . '"');
127
			}
128
			$group->addUser($user);
129
			$output->writeln('User "' . $user->getUID() . '" added to group "' . $group->getGID() . '"');
130
		}
131
	}
132
133
	protected function createUser(InputInterface $input, OutputInterface $output, $password, $email) {
134
		$user = $this->userManager->createUser(
135
			$input->getArgument('uid'),
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('uid') targeting Symfony\Component\Consol...nterface::getArgument() can also be of type array<integer,string> or null; however, OCP\IUserManager::createUser() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
136
			$password
137
		);
138
139
		if ($user instanceof IUser) {
140
			$output->writeln('<info>The user "' . $user->getUID() . '" was created successfully</info>');
141
		} else {
142
			$output->writeln('<error>An error occurred while creating the user</error>');
143
			return 1;
144
		}
145
146 View Code Duplication
		if ($input->getOption('display-name')) {
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...
147
			$user->setDisplayName($input->getOption('display-name'));
148
			$output->writeln('Display name set to "' . $user->getDisplayName() . '"');
149
		}
150
151
		// Set email if supplied & valid
152
		if ($email !== null) {
153
			$user->setEMailAddress($email);
154
			$output->writeln('Email address set to "' . $user->getEMailAddress() . '"');
155
		}
156
	}
157
158
	protected function execute(InputInterface $input, OutputInterface $output) {
159
		$uid = $input->getArgument('uid');
160
		if ($this->userManager->userExists($uid)) {
0 ignored issues
show
Bug introduced by
It seems like $uid defined by $input->getArgument('uid') on line 159 can also be of type array<integer,string> or null; however, OCP\IUserManager::userExists() 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...
161
			$output->writeln('<error>The user "' . $uid . '" already exists.</error>');
162
			return 1;
163
		}
164
165
		$readPasswordFromCmdLine = $input->getOption('password-from-cmdline');
166
		// Validate email before we create the user
167
		if ($input->getOption('email')) {
168
			// Validate first
169
			if (!$this->mailer->validateMailAddress($input->getOption('email'))) {
170
				// Invalid! Error
171
				$output->writeln('<error>Invalid email address supplied</error>');
172
				return 1;
173
			} else {
174
				$email = $input->getOption('email');
175
			}
176
		} else {
177
			$email = null;
178
		}
179
180
		if ($input->getOption('password-from-env')) {
181
			$password = \getenv('OC_PASS');
182
			if (!$password) {
183
				$output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
184
				return 1;
185
			}
186
			$this->createUser($input, $output, $password, $email);
187
			$this->addUserToGroup($input, $output, $this->userManager->get($uid));
0 ignored issues
show
Bug introduced by
It seems like $uid defined by $input->getArgument('uid') on line 159 can also be of type array<integer,string> or null; however, OCP\IUserManager::get() 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...
Bug introduced by
It seems like $this->userManager->get($uid) can be null; however, addUserToGroup() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
188
		} elseif ($readPasswordFromCmdLine) {
189
			/** @var $dialog \Symfony\Component\Console\Helper\QuestionHelper */
190
			$dialog = $this->getHelperSet()->get('question');
191
			$q = new Question('<question>Enter password: </question>', false);
192
			$q->setHidden(true);
193
			$password = $dialog->ask($input, $output, $q);
194
			$q = new Question('<question>Confirm password: </question>', false);
195
			$q->setHidden(true);
196
			$confirm = $dialog->ask($input, $output, $q);
197
198
			if ($password !== $confirm) {
199
				$output->writeln("<error>Passwords did not match!</error>");
200
				return 1;
201
			}
202
203
			$this->createUser($input, $output, $password, $email);
204
			$this->addUserToGroup($input, $output, $this->userManager->get($uid));
0 ignored issues
show
Bug introduced by
It seems like $uid defined by $input->getArgument('uid') on line 159 can also be of type array<integer,string> or null; however, OCP\IUserManager::get() 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...
Bug introduced by
It seems like $this->userManager->get($uid) can be null; however, addUserToGroup() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
205
		} elseif ($input->getOption('email') && ($email !== null)) {
206
			$newUser = $this->createUser->createUser($uid, '', $input->getOption('group'), $email);
207
			if ($newUser instanceof User) {
208
				$output->writeln('<info>The user "' . $newUser->getUID() . '" was created successfully</info>');
209
			}
210 View Code Duplication
			if ($input->getOption('display-name')) {
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...
211
				$user = $this->userManager->get($uid);
0 ignored issues
show
Bug introduced by
It seems like $uid defined by $input->getArgument('uid') on line 159 can also be of type array<integer,string> or null; however, OCP\IUserManager::get() 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...
212
				$user->setDisplayName($input->getOption('display-name'));
213
				$output->writeln('Display name set to "' . $user->getDisplayName() . '"');
214
			}
215
			if ($input->getOption('group')) {
216
				if (\is_array($input->getOption('group'))) {
217
					foreach ($input->getOption('group') as $group) {
218
						if ($this->groupManager->isInGroup($uid, $group)) {
0 ignored issues
show
Bug introduced by
It seems like $uid defined by $input->getArgument('uid') on line 159 can also be of type array<integer,string> or null; however, OCP\IGroupManager::isInGroup() 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...
219
							$output->writeln('User "' . $uid . '" added to group "' . $group . '"');
220
						}
221
					}
222
				}
223
			}
224
		} else {
225
			$output->writeln("<error>Interactive input or --password-from-env is needed for entering a password!</error>");
226
			return 1;
227
		}
228
	}
229
}
230