Skip to content

Commit b557ea6

Browse files
committed
Enable adding E-Mail addresses to new user accounts using the CLI
Signed-off-by: Philip Gatzka <[email protected]>
1 parent 8f11481 commit b557ea6

File tree

3 files changed

+220
-5
lines changed

3 files changed

+220
-5
lines changed

core/Command/User/Add.php

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@
2525
*/
2626
namespace OC\Core\Command\User;
2727

28+
use Egulias\EmailValidator\EmailValidator;
29+
use Egulias\EmailValidator\Validation\RFCValidation;
2830
use OC\Files\Filesystem;
31+
use OCA\Settings\Mailer\NewUserMailHelper;
32+
use OCP\IConfig;
2933
use OCP\IGroup;
3034
use OCP\IGroupManager;
3135
use OCP\IUser;
3236
use OCP\IUserManager;
37+
use OCP\Security\ISecureRandom;
3338
use Symfony\Component\Console\Command\Command;
3439
use Symfony\Component\Console\Helper\QuestionHelper;
3540
use Symfony\Component\Console\Input\InputArgument;
@@ -39,20 +44,56 @@
3944
use Symfony\Component\Console\Question\Question;
4045

4146
class Add extends Command {
42-
/** @var \OCP\IUserManager */
47+
/**
48+
* @var IUserManager
49+
*/
4350
protected $userManager;
4451

45-
/** @var \OCP\IGroupManager */
52+
/**
53+
* @var IGroupManager
54+
*/
4655
protected $groupManager;
4756

57+
/**
58+
* @var EmailValidator
59+
*/
60+
protected $emailValidator;
61+
62+
/**
63+
* @var IConfig
64+
*/
65+
private $config;
66+
67+
/**
68+
* @var NewUserMailHelper
69+
*/
70+
private $mailHelper;
71+
72+
/**
73+
* @var ISecureRandom
74+
*/
75+
private $secureRandom;
76+
4877
/**
4978
* @param IUserManager $userManager
5079
* @param IGroupManager $groupManager
80+
* @param EmailValidator $emailValidator
5181
*/
52-
public function __construct(IUserManager $userManager, IGroupManager $groupManager) {
82+
public function __construct(
83+
IUserManager $userManager,
84+
IGroupManager $groupManager,
85+
EmailValidator $emailValidator,
86+
IConfig $config,
87+
NewUserMailHelper $mailHelper,
88+
ISecureRandom $secureRandom
89+
) {
5390
parent::__construct();
5491
$this->userManager = $userManager;
5592
$this->groupManager = $groupManager;
93+
$this->emailValidator = $emailValidator;
94+
$this->config = $config;
95+
$this->mailHelper = $mailHelper;
96+
$this->secureRandom = $secureRandom;
5697
}
5798

5899
protected function configure() {
@@ -81,11 +122,19 @@ protected function configure() {
81122
'g',
82123
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
83124
'groups the user should be added to (The group will be created if it does not exist)'
125+
)
126+
->addOption(
127+
'email',
128+
null,
129+
InputOption::VALUE_REQUIRED,
130+
'When set, users may register using the default E-Mail verification workflow'
84131
);
85132
}
86133

87134
protected function execute(InputInterface $input, OutputInterface $output): int {
88135
$uid = $input->getArgument('uid');
136+
$emailIsSet = \is_string($input->getOption('email')) && \mb_strlen($input->getOption('email')) > 0;
137+
89138
if ($this->userManager->userExists($uid)) {
90139
$output->writeln('<error>The user "' . $uid . '" already exists.</error>');
91140
return 1;
@@ -97,6 +146,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int
97146
$output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
98147
return 1;
99148
}
149+
} elseif ($emailIsSet) {
150+
// Set a temporary password, an invitation E-Mail will be sent
151+
$password = \implode('', [
152+
$this->secureRandom->generate(10),
153+
$this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER),
154+
$this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER),
155+
$this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS),
156+
$this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS)
157+
]);
100158
} elseif ($input->isInteractive()) {
101159
/** @var QuestionHelper $helper */
102160
$helper = $this->getHelper('question');
@@ -118,6 +176,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int
118176
return 1;
119177
}
120178

179+
$emailIsValid = false;
180+
181+
if ($emailIsSet) {
182+
$emailIsValid = $this->emailValidator->isValid($input->getOption('email'), new RFCValidation());
183+
}
184+
185+
if ($emailIsSet && !$emailIsValid) {
186+
$output->writeln(\sprintf(
187+
'<error>The given E-Mail address "%s" is invalid: %s</error>',
188+
$input->getOption('email'),
189+
$this->emailValidator->getError()->description()
190+
));
191+
192+
return 1;
193+
}
194+
121195
try {
122196
$user = $this->userManager->createUser(
123197
$input->getArgument('uid'),
@@ -128,7 +202,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
128202
return 1;
129203
}
130204

131-
132205
if ($user instanceof IUser) {
133206
$output->writeln('<info>The user "' . $user->getUID() . '" was created successfully</info>');
134207
} else {
@@ -141,6 +214,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int
141214
$output->writeln('Display name set to "' . $user->getDisplayName() . '"');
142215
}
143216

217+
if ($emailIsSet && $emailIsValid) {
218+
$user->setSystemEMailAddress($input->getOption('email'));
219+
$output->writeln('E-Mail set to "' . $user->getSystemEMailAddress() . '"');
220+
221+
if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
222+
try {
223+
$this->mailHelper->sendMail(
224+
$user,
225+
$this->mailHelper->generateTemplate($user, true)
226+
);
227+
} catch (\Exception $e) {
228+
$output->writeln(\sprintf('Unable to send the invitation mail to %s', $user->getEMailAddress()));
229+
}
230+
}
231+
}
232+
144233
$groups = $input->getOption('group');
145234

146235
if (!empty($groups)) {

core/register_command.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* along with this program. If not, see <http://www.gnu.org/licenses/>
4949
*
5050
*/
51+
5152
use Psr\Log\LoggerInterface;
5253

5354
$application->add(new \Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand());
@@ -177,7 +178,7 @@
177178
$application->add(\OC::$server->query(\OC\Core\Command\Preview\Repair::class));
178179
$application->add(\OC::$server->query(\OC\Core\Command\Preview\ResetRenderedTexts::class));
179180

180-
$application->add(new OC\Core\Command\User\Add(\OC::$server->getUserManager(), \OC::$server->getGroupManager()));
181+
$application->add(new OC\Core\Command\User\Add(\OC::$server->getUserManager(), \OC::$server->getGroupManager(), \OC::$server->get(Egulias\EmailValidator\EmailValidator::class), \OC::$server->get(\OC\AllConfig::class), \OC::$server->get(\OCA\Settings\Mailer\NewUserMailHelper::class), \OC::$server->get(\OCP\Security\ISecureRandom::class)));
181182
$application->add(new OC\Core\Command\User\Delete(\OC::$server->getUserManager()));
182183
$application->add(new OC\Core\Command\User\Disable(\OC::$server->getUserManager()));
183184
$application->add(new OC\Core\Command\User\Enable(\OC::$server->getUserManager()));
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2021, Philip Gatzka ([email protected])
4+
*
5+
* @author Philip Gatzka <[email protected]>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as
11+
* published by the Free Software Foundation, either version 3 of the
12+
* License, or (at your option) any later version.
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
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
24+
declare(strict_types=1);
25+
26+
namespace Core\Command\User;
27+
28+
use Egulias\EmailValidator\EmailValidator;
29+
use OC\Core\Command\User\Add;
30+
use OCA\Settings\Mailer\NewUserMailHelper;
31+
use OCP\IConfig;
32+
use OCP\IGroupManager;
33+
use OCP\IUser;
34+
use OCP\IUserManager;
35+
use OCP\Mail\IEMailTemplate;
36+
use OCP\Security\ISecureRandom;
37+
use Symfony\Component\Console\Input\InputInterface;
38+
use Symfony\Component\Console\Output\OutputInterface;
39+
use Test\TestCase;
40+
41+
class AddTest extends TestCase {
42+
43+
/**
44+
* @dataProvider addEmailDataProvider
45+
*/
46+
public function testAddEmail(?string $email, bool $isValid, bool $shouldSendMail): void {
47+
$userManager = static::createMock(IUserManager::class);
48+
$groupManager = static::createMock(IGroupManager::class);
49+
$user = static::createMock(IUser::class);
50+
$config = static::createMock(IConfig::class);
51+
$mailHelper = static::createMock(NewUserMailHelper::class);
52+
$secureRandom = static::createMock(ISecureRandom::class);
53+
54+
$consoleInput = static::createMock(InputInterface::class);
55+
$consoleOutput = static::createMock(OutputInterface::class);
56+
57+
$user->expects($isValid ? static::once() : static::never())
58+
->method('setSystemEMailAddress')
59+
->with(static::equalTo($email));
60+
61+
$userManager->method('createUser')
62+
->willReturn($user);
63+
64+
$config->method('getAppValue')
65+
->willReturn($shouldSendMail ? 'yes' : 'no');
66+
67+
$mailHelper->method('generateTemplate')
68+
->willReturn(static::createMock(IEMailTemplate::class));
69+
70+
$mailHelper->expects($isValid && $shouldSendMail ? static::once() : static::never())
71+
->method('sendMail');
72+
73+
$consoleInput->method('getOption')
74+
->will(static::returnValueMap([
75+
['password-from-env', '1'],
76+
['email', $email],
77+
['group', []],
78+
]));
79+
80+
$addCommand = new Add(
81+
$userManager,
82+
$groupManager,
83+
new EmailValidator(),
84+
$config,
85+
$mailHelper,
86+
$secureRandom
87+
);
88+
89+
\putenv('OC_PASS=dummy-for-testing');
90+
91+
$this->invokePrivate($addCommand, 'execute', [
92+
$consoleInput,
93+
$consoleOutput
94+
]);
95+
}
96+
97+
/**
98+
* @return \Generator<string, array>
99+
*/
100+
public function addEmailDataProvider(): \Generator {
101+
yield 'Valid E-Mail' => [
102+
103+
true,
104+
true,
105+
];
106+
107+
yield 'Invalid E-Mail' => [
108+
'info@@example.com',
109+
false,
110+
true,
111+
];
112+
113+
yield 'No E-Mail' => [
114+
'',
115+
false,
116+
true,
117+
];
118+
119+
yield 'Valid E-Mail, but no mail should be sent' => [
120+
121+
true,
122+
false,
123+
];
124+
}
125+
}

0 commit comments

Comments
 (0)