Completed
Pull Request — master (#32767)
by Sujith
61:42 queued 48:53
created

CreateUser::createUser()   F

Complexity

Conditions 23
Paths 181

Size

Total Lines 92

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
nc 181
nop 4
dl 0
loc 92
rs 3.4916
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 Sujith Haridasan <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2018, ownCloud GmbH
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OC\User\Service;
23
24
use OCP\IGroupManager;
25
use OCP\ILogger;
26
use OCP\IUserManager;
27
use OCP\IUserSession;
28
use OCP\Mail\IMailer;
29
use OCP\Security\ISecureRandom;
30
use OCP\User\Exceptions\CannotCreateUserException;
31
use OCP\User\Exceptions\InvalidEmailException;
32
use OCP\User\Exceptions\UserAlreadyExistsException;
33
use OCP\User\UserPasswordCreateEvent;
34
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
35
use Symfony\Component\EventDispatcher\GenericEvent;
36
37
class CreateUser {
38
	/** @var IUserSession  */
39
	private $userSession;
40
	/** @var IGroupManager  */
41
	private $groupManager;
42
	/** @var IUserManager  */
43
	private $userManager;
44
	/** @var IMailer  */
45
	private $mailer;
46
	/** @var ISecureRandom  */
47
	private $secureRandom;
48
	/** @var EventDispatcherInterface  */
49
	private $eventDispatcher;
50
	/** @var ILogger  */
51
	private $logger;
52
	/** @var UserSendMail  */
53
	private $userSendMail;
54
55
	/**
56
	 * CreateUser constructor.
57
	 *
58
	 * @param IUserSession $userSession
59
	 * @param IGroupManager $groupManager
60
	 * @param IUserManager $userManager
61
	 * @param IMailer $mailer
62
	 * @param ISecureRandom $secureRandom
63
	 * @param EventDispatcherInterface $eventDispatcher
64
	 * @param ILogger $logger
65
	 * @param UserSendMail $userSendMail
66
	 */
67
	public function __construct(IUserSession $userSession,
68
								IGroupManager $groupManager,
69
								IUserManager $userManager,
70
								IMailer $mailer,
71
								ISecureRandom $secureRandom,
72
								EventDispatcherInterface $eventDispatcher,
73
								ILogger $logger, UserSendMail $userSendMail) {
74
		$this->userSession = $userSession;
75
		$this->groupManager = $groupManager;
76
		$this->userManager = $userManager;
77
		$this->mailer = $mailer;
78
		$this->secureRandom = $secureRandom;
79
		$this->eventDispatcher = $eventDispatcher;
80
		$this->logger = $logger;
81
		$this->userSendMail = $userSendMail;
82
	}
83
84
	/**
85
	 * @param $username
86
	 * @param $password
87
	 * @param array $groups
88
	 * @param string $email
89
	 * @return bool|\OCP\IUser
90
	 * @throws CannotCreateUserException
91
	 * @throws InvalidEmailException
92
	 * @throws UserAlreadyExistsException
93
	 */
94
	public function createUser($username, $password, array $groups= [], $email='') {
95
		if ($email !== '' && !$this->mailer->validateMailAddress($email)) {
96
			throw new InvalidEmailException("Invalid mail address");
97
		}
98
99
		$currentUser = $this->userSession->getUser();
100
101
		if (!$this->isAdmin()) {
102
			if (!empty($groups)) {
103
				foreach ($groups as $key => $group) {
104
					$groupObject = $this->groupManager->get($group);
105
					if ($groupObject === null) {
106
						unset($groups[$key]);
107
						continue;
108
					}
109
110
					if (!$this->groupManager->getSubAdmin()->isSubAdminofGroup($currentUser, $groupObject)) {
0 ignored issues
show
Bug introduced by
It seems like $currentUser defined by $this->userSession->getUser() on line 99 can be null; however, OCP\ISubAdminManager::isSubAdminofGroup() 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...
111
						unset($groups[$key]);
112
					}
113
				}
114
			}
115
116
			if (empty($groups)) {
117
				$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($currentUser);
0 ignored issues
show
Bug introduced by
It seems like $currentUser defined by $this->userSession->getUser() on line 99 can be null; however, OCP\ISubAdminManager::getSubAdminsGroups() 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...
118
				// New class returns IGroup[] so convert back
119
				$gids = [];
120
				foreach ($groups as $group) {
121
					$gids[] = $group->getGID();
122
				}
123
				$groups = $gids;
124
			}
125
		}
126
127
		if ($this->userManager->userExists($username)) {
128
			throw new UserAlreadyExistsException('A user with that name already exists.');
129
		}
130
131
		try {
132
			if (($password === '') && ($email !== '')) {
133
				/**
134
				 * Generate a random password as we are going to have this
135
				 * use one time. The new user has to reset it using the link
136
				 * from email.
137
				 */
138
				$passwordCreateEvent = new UserPasswordCreateEvent(UserPasswordCreateEvent::CREATE_PASSWORD, []);
139
				$this->eventDispatcher->dispatch(
140
					UserPasswordCreateEvent::CREATE_PASSWORD, $passwordCreateEvent);
141
				$password = $passwordCreateEvent->getPassword();
142
				if ($password === false) {
143
					$password = $this->secureRandom->generate(20);
144
				}
145
			}
146
			$user = $this->userManager->createUser($username, $password);
147
		} catch (\Exception $exception) {
148
			$message = $exception->getMessage();
149
			if (!$message) {
150
				$message = 'Unable to create user.';
151
			}
152
			throw new CannotCreateUserException($message);
153
		}
154
155
		if ($user === null) {
156
			throw new CannotCreateUserException('Unable to create user.');
157
		}
158
159
		if ($groups !== null) {
160
			foreach ($groups as $groupName) {
161
				if ($groupName !== null) {
162
					$group = $this->groupManager->get($groupName);
163
164
					if (empty($group)) {
165
						$group = $this->groupManager->createGroup($groupName);
166
					}
167
					$group->addUser($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->crea...r($username, $password) on line 146 can also be of type boolean; however, OCP\IGroup::addUser() does only seem to accept object<OCP\IUser>, 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...
168
					$this->logger->info('Added userid ' . $user->getUID() . ' to group ' . $group->getGID());
169
				}
170
			}
171
		}
172
		/**
173
		 * Send new user mail only if a mail is set
174
		 */
175
		if ($email !== '') {
176
			$user->setEMailAddress($email);
177
			try {
178
				$this->userSendMail->generateTokenAndSendMail($username, $email);
179
			} catch (\Exception $e) {
180
				$this->logger->error("Can't send new user mail to $email: " . $e->getMessage(), ['app' => 'settings']);
181
			}
182
		}
183
184
		return $user;
185
	}
186
187
	private function isAdmin() {
188
		// Check if current user (active and not in incognito mode)
189
		// is an admin
190
		$activeUser = $this->userSession->getUser();
191
		if ($activeUser !== null) {
192
			return $this->groupManager->isAdmin($activeUser->getUID());
193
		}
194
		// Check if it is triggered from command line
195
		$cli = \in_array(\php_sapi_name(), ['cli']);
196
		if ($cli === true) {
197
			return true;
198
		}
199
		return false;
200
	}
201
}