Skip to content

Commit c62f13a

Browse files
committed
New occ command to create guest users
Create guest user with optional password without sending an email. Signed-off-by: Vincent Petry <[email protected]>
1 parent cdef1bb commit c62f13a

File tree

3 files changed

+200
-22
lines changed

3 files changed

+200
-22
lines changed

appinfo/info.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Guests users can only access files shared to them and can't create any files out
2626
</dependencies>
2727
<commands>
2828
<command>OCA\Guests\Command\ListCommand</command>
29+
<command>OCA\Guests\Command\AddCommand</command>
2930
</commands>
3031
<settings>
3132
<admin>OCA\Guests\Settings\Admin</admin>

lib/Command/AddCommand.php

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2020 Vincent Petry <[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+
namespace OCA\Guests\Command;
25+
26+
use OC\Core\Command\Base;
27+
use OCA\Guests\GuestManager;
28+
use OCP\IUser;
29+
use OCP\IUserManager;
30+
use OCP\Mail\IMailer;
31+
use Symfony\Component\Console\Input\InputArgument;
32+
use Symfony\Component\Console\Input\InputInterface;
33+
use Symfony\Component\Console\Input\InputOption;
34+
use Symfony\Component\Console\Output\OutputInterface;
35+
use Symfony\Component\Console\Question\Question;
36+
37+
class AddCommand extends Base {
38+
private $guestManager;
39+
private $mailer;
40+
41+
public function __construct(IUserManager $userManager, IMailer $mailer, GuestManager $guestManager) {
42+
parent::__construct();
43+
$this->userManager = $userManager;
44+
$this->guestManager = $guestManager;
45+
$this->mailer = $mailer;
46+
}
47+
48+
protected function configure() {
49+
$this
50+
->setName('guests:add')
51+
->setDescription('Add a new guest account')
52+
->addArgument(
53+
'created-by',
54+
InputArgument::REQUIRED,
55+
'User ID who is set as creator'
56+
)
57+
->addArgument(
58+
'uid',
59+
InputArgument::REQUIRED,
60+
'User ID used to login (must only contain a-z, A-Z, 0-9, -, _ and @)'
61+
)
62+
->addArgument(
63+
'email',
64+
InputArgument::REQUIRED,
65+
'Email address'
66+
)
67+
->addOption(
68+
'generate-password',
69+
null,
70+
InputOption::VALUE_NONE,
71+
'Do not set an initial password'
72+
)
73+
->addOption(
74+
'password-from-env',
75+
null,
76+
InputOption::VALUE_NONE,
77+
'Read password from environment variable OC_PASS'
78+
)
79+
->addOption(
80+
'display-name',
81+
null,
82+
InputOption::VALUE_OPTIONAL,
83+
'User name used in the web UI (can contain any characters)',
84+
''
85+
)
86+
->addOption(
87+
'language',
88+
null,
89+
InputOption::VALUE_OPTIONAL,
90+
'Language',
91+
''
92+
);
93+
parent::configure();
94+
}
95+
96+
protected function execute(InputInterface $input, OutputInterface $output) {
97+
$creatorUser = $this->userManager->get($input->getArgument('created-by'));
98+
if ($creatorUser === null) {
99+
$output->writeln('<error>The user "' . $input->getArgument('created-by') . '" does not exist.</error>');
100+
return 1;
101+
}
102+
103+
$uid = $input->getArgument('uid');
104+
if ($this->userManager->userExists($uid)) {
105+
$output->writeln('<error>The user "' . $uid . '" already exists.</error>');
106+
return 1;
107+
}
108+
109+
$password = null;
110+
if (!$input->getOption('generate-password')) {
111+
if ($input->getOption('password-from-env')) {
112+
$password = getenv('OC_PASS');
113+
if (!$password) {
114+
$output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
115+
return 1;
116+
}
117+
} elseif ($input->isInteractive()) {
118+
/** @var QuestionHelper $helper */
119+
$helper = $this->getHelper('question');
120+
121+
$question = new Question('Enter password: ');
122+
$question->setHidden(true);
123+
$password = $helper->ask($input, $output, $question);
124+
125+
$question = new Question('Confirm password: ');
126+
$question->setHidden(true);
127+
$confirm = $helper->ask($input, $output,$question);
128+
129+
if ($password !== $confirm) {
130+
$output->writeln("<error>Passwords did not match!</error>");
131+
return 1;
132+
}
133+
} else {
134+
$output->writeln("<error>Interactive input or --password-from-env is needed for entering a password!</error>");
135+
return 1;
136+
}
137+
}
138+
139+
if (!$this->mailer->validateMailAddress($input->getArgument('email'))) {
140+
$output->writeln('<error>Invalid email address</error>');
141+
return 1;
142+
}
143+
144+
try {
145+
$user = $this->guestManager->createGuest(
146+
$creatorUser,
147+
$input->getArgument('uid'),
148+
$input->getArgument('email'),
149+
$input->getOption('display-name'),
150+
$input->getOption('language'),
151+
$password
152+
);
153+
} catch (\Exception $e) {
154+
$output->writeln('<error>' . $e->getMessage() . '</error>');
155+
return 1;
156+
}
157+
158+
if ($user instanceof IUser) {
159+
$output->writeln('<info>The guest account user "' . $user->getUID() . '" was created successfully</info>');
160+
} else {
161+
$output->writeln('<error>An error occurred while creating the user</error>');
162+
return 1;
163+
}
164+
}
165+
}

lib/GuestManager.php

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,19 @@ public function isGuest($user = null) {
9797
return false;
9898
}
9999

100-
public function createGuest(IUser $createdBy, $userId, $email, $displayName = '', $language = '') {
101-
$passwordEvent = new GenerateSecurePasswordEvent();
102-
$this->eventDispatcher->dispatchTyped($passwordEvent);
103-
$this->userManager->createUserFromBackend(
100+
public function createGuest(IUser $createdBy, $userId, $email, $displayName = '', $language = '', $initialPassword = null) : IUser {
101+
if ($initialPassword === null) {
102+
$passwordEvent = new GenerateSecurePasswordEvent();
103+
$this->eventDispatcher->dispatchTyped($passwordEvent);
104+
$password = $passwordEvent->getPassword() ?? $this->secureRandom->generate(20);
105+
} else {
106+
$password = $initialPassword;
107+
}
108+
109+
/** @var IUser */
110+
$user = $this->userManager->createUserFromBackend(
104111
$userId,
105-
$passwordEvent->getPassword() ?? $this->secureRandom->generate(20),
112+
$password,
106113
$this->userBackend
107114
);
108115

@@ -117,25 +124,30 @@ public function createGuest(IUser $createdBy, $userId, $email, $displayName = ''
117124
$this->config->setUserValue($userId, 'core', 'lang', $language);
118125
}
119126

120-
$token = $this->secureRandom->generate(
121-
21,
122-
ISecureRandom::CHAR_DIGITS .
123-
ISecureRandom::CHAR_LOWER .
124-
ISecureRandom::CHAR_UPPER);
125-
126-
$endOfTime = PHP_INT_MAX - 50000;
127-
$token = sprintf('%s:%s', $endOfTime, $token);
128-
129-
$encryptedValue = $this->crypto->encrypt($token, $email . $this->config->getSystemValue('secret'));
130-
131-
$this->config->setUserValue(
132-
$userId,
133-
'core',
134-
'lostpassword',
135-
$encryptedValue
136-
);
127+
if ($initialPassword === null) {
128+
// generate token for lost password so that a link can be sent by email
129+
$token = $this->secureRandom->generate(
130+
21,
131+
ISecureRandom::CHAR_DIGITS .
132+
ISecureRandom::CHAR_LOWER .
133+
ISecureRandom::CHAR_UPPER);
134+
135+
$endOfTime = PHP_INT_MAX - 50000;
136+
$token = sprintf('%s:%s', $endOfTime, $token);
137+
138+
$encryptedValue = $this->crypto->encrypt($token, $email . $this->config->getSystemValue('secret'));
139+
140+
$this->config->setUserValue(
141+
$userId,
142+
'core',
143+
'lostpassword',
144+
$encryptedValue
145+
);
146+
}
137147

138148
$this->config->setUserValue($userId, 'files', 'quota', '0 B');
149+
150+
return $user;
139151
}
140152

141153
public function listGuests() {

0 commit comments

Comments
 (0)