Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ While theoretically any other authentication provider implementing either one of
<command>OCA\User_SAML\Command\ConfigSet</command>
<command>OCA\User_SAML\Command\GetMetadata</command>
<command>OCA\User_SAML\Command\GroupMigrationCopyIncomplete</command>
<command>OCA\User_SAML\Command\UserAdd</command>
</commands>
<settings>
<admin>OCA\User_SAML\Settings\Admin</admin>
Expand Down
6 changes: 3 additions & 3 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ public function boot(IBootContext $context): void {

// All requests that are not authenticated and match against the "/login" route are
// redirected to the SAML login endpoint
if (!$isCLI &&
!$userSession->isLoggedIn() &&
($request->getPathInfo() === '/login')) {
if (!$isCLI
&& !$userSession->isLoggedIn()
&& ($request->getPathInfo() === '/login')) {
try {
$params = $request->getParams();
} catch (\LogicException) {
Expand Down
88 changes: 88 additions & 0 deletions lib/Command/UserAdd.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\User_SAML\Command;

use OC\Core\Command\Base;
use OCA\User_SAML\UserBackend;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class UserAdd extends Base {
public function __construct(
protected IUserManager $userManager,
protected UserBackend $backend,
private LoggerInterface $logger,
) {
parent::__construct();
}
protected function configure(): void {
$this
->setName('saml:user:add')
->setDescription('Add a SAML account')
->addArgument(
'uid',
InputArgument::REQUIRED,
'Account ID as provided by the IdP (must only contain a-z, A-Z, 0-9, -, _ and @)'
)
->addOption(
'display-name',
null,
InputOption::VALUE_REQUIRED,
'Name as presented in the web interface (can contain any characters)'
)
->addOption(
'email',
null,
InputOption::VALUE_OPTIONAL,
'Set user default email in user profile'
);
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$uid = $input->getArgument('uid');

if ($this->userManager->userExists($uid)) {
$output->writeln('<error>The account "' . $uid . '" already exists.</error>');
return 1;
}

if (!$output->isQuiet()) {
$output->writeln('<info>The account "' . $uid . '" is to be added to the SAML backend.</info>');
}

try {
$this->backend->createUserIfNotExists($uid);
} catch (\Exception $e) {
$output->writeln('<error>SAML create user ' . $e->getMessage() . '</error>');
return 1;
}

try {
$this->backend->setDisplayName($uid, $input->getOption('display-name'));
$email = $input->getOption('email');
if (!empty($email)) {
$user = $this->userManager->get($uid);
$user->setSystemEMailAddress($email);
}
} catch (\Exception $e) {
$output->writeln('<error>SAML create user Email and DisplayName ' . $e->getMessage() . '</error>');
return 1;
}

if (!$output->isQuiet()) {
$output->writeln('<info>SAML user "' . $uid . '" added.</info>');
}

return 0;
}

}
4 changes: 2 additions & 2 deletions lib/Controller/SAMLController.php
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,8 @@ public function assertionConsumerService(): Http\RedirectResponse {
* @throws Error
*/
public function singleLogoutService(): Http\RedirectResponse {
$isFromGS = ($this->config->getSystemValueBool('gs.enabled', false) &&
$this->config->getSystemValueString('gss.mode', '') === 'master');
$isFromGS = ($this->config->getSystemValueBool('gs.enabled', false)
&& $this->config->getSystemValueString('gss.mode', '') === 'master');

// Some IDPs send the SLO request via POST, but OneLogin php-saml only handles GET.
// To hack around this issue we copy the request from _POST to _GET.
Expand Down
4 changes: 2 additions & 2 deletions lib/DavPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public function initialize(Server $server) {

public function beforeMethod(RequestInterface $request, ResponseInterface $response) {
if (
$this->config->getAppValue('user_saml', 'type') === 'environment-variable' &&
!$this->session->exists('user_saml.samlUserData')
$this->config->getAppValue('user_saml', 'type') === 'environment-variable'
&& !$this->session->exists('user_saml.samlUserData')
) {
$uidMapping = $this->samlSettings->get(1)['general-uid_mapping'];
if (isset($this->auth[$uidMapping])) {
Expand Down
4 changes: 2 additions & 2 deletions lib/GroupManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ protected function hasGroupForeignMembers(IGroup $group): bool {
* allowed only for groups owned by the SAML backend.
*/
protected function mayModifyGroup(?IGroup $group): bool {
$isInTransition =
$group !== null
$isInTransition
= $group !== null
&& $group->getGID() !== 'admin'
&& in_array('Database', $group->getBackendNames())
&& $this->isGroupInTransitionList($group->getGID());
Expand Down
12 changes: 6 additions & 6 deletions tests/unit/UserBackendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ public function testUpdateAttributesWithoutAttributes() {
$user = $this->createMock(IUser::class);

$this->config->method('getAppValue')
->willReturnCallback(fn (string $appId, string $key, string $default) =>
->willReturnCallback(fn (string $appId, string $key, string $default)
// Unused parameters are intentionally kept for clarity
$default);
=> $default);

$this->userManager
->expects($this->once())
Expand Down Expand Up @@ -138,9 +138,9 @@ public function testUpdateAttributesWithoutValidUser() {
$this->getMockedBuilder();

$this->config->method('getAppValue')
->willReturnCallback(fn (string $appId, string $key, string $default) =>
->willReturnCallback(fn (string $appId, string $key, string $default)
// Unused parameters are intentionally kept for clarity
$default);
=> $default);

$this->userManager
->expects($this->once())
Expand Down Expand Up @@ -227,9 +227,9 @@ public function testUpdateAttributesQuotaDefaultFallback() {
$attributes = ['email' => 'new@example.com', 'displayname' => 'New Displayname', 'quota' => ''];

$this->config->method('getAppValue')
->willReturnCallback(fn (string $appId, string $key, string $default) =>
->willReturnCallback(fn (string $appId, string $key, string $default)
// Unused $appId parameter is intentionally kept for clarity
match ($key) {
=> match ($key) {
'saml-attribute-mapping-email_mapping' => 'email',
'saml-attribute-mapping-displayName_mapping' => 'displayname',
'saml-attribute-mapping-quota_mapping' => 'quota',
Expand Down
Loading