Merge pull request #55518 from nextcloud/fix/ldap-get-rid-of-ajax
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (master, 8.4, main, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, files_reminders) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, routing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Has been cancelled
Psalm static code analysis / static-code-analysis-security (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ocp (push) Has been cancelled
Psalm static code analysis / static-code-analysis-ncu (push) Has been cancelled

fix: Get rid of ajax endpoints in user_ldap
This commit is contained in:
Louis 2025-11-21 18:44:39 +01:00 committed by GitHub
commit e98864bd7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 1709 additions and 541 deletions

View file

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
// Fake ajax endpoint for testing url generator

View file

@ -1,53 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
use OCA\User_LDAP\Mapping\GroupMapping;
use OCA\User_LDAP\Mapping\UserMapping;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IUserManager;
use OCP\Server;
use OCP\User\Events\BeforeUserIdUnassignedEvent;
use OCP\User\Events\UserIdUnassignedEvent;
use OCP\Util;
// Check user and app status
\OC_JSON::checkAdminUser();
\OC_JSON::checkAppEnabled('user_ldap');
\OC_JSON::callCheck();
$subject = (string)$_POST['ldap_clear_mapping'];
$mapping = null;
try {
if ($subject === 'user') {
$mapping = Server::get(UserMapping::class);
/** @var IEventDispatcher $dispatcher */
$dispatcher = Server::get(IEventDispatcher::class);
$result = $mapping->clearCb(
function (string $uid) use ($dispatcher): void {
$dispatcher->dispatchTyped(new BeforeUserIdUnassignedEvent($uid));
/** @psalm-suppress UndefinedInterfaceMethod For now we have to emit, will be removed when all hooks are removed */
Server::get(IUserManager::class)->emit('\OC\User', 'preUnassignedUserId', [$uid]);
},
function (string $uid) use ($dispatcher): void {
$dispatcher->dispatchTyped(new UserIdUnassignedEvent($uid));
/** @psalm-suppress UndefinedInterfaceMethod For now we have to emit, will be removed when all hooks are removed */
Server::get(IUserManager::class)->emit('\OC\User', 'postUnassignedUserId', [$uid]);
}
);
} elseif ($subject === 'group') {
$mapping = Server::get(GroupMapping::class);
$result = $mapping->clear();
}
if ($mapping === null || !$result) {
$l = Util::getL10N('user_ldap');
throw new \Exception($l->t('Failed to clear the mappings.'));
}
\OC_JSON::success();
} catch (\Exception $e) {
\OC_JSON::error(['message' => $e->getMessage()]);
}

View file

@ -1,24 +0,0 @@
<?php
use OCA\User_LDAP\Helper;
use OCP\Server;
use OCP\Util;
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// Check user and app status
\OC_JSON::checkAdminUser();
\OC_JSON::checkAppEnabled('user_ldap');
\OC_JSON::callCheck();
$prefix = (string)$_POST['ldap_serverconfig_chooser'];
$helper = Server::get(Helper::class);
if ($helper->deleteServerConfiguration($prefix)) {
\OC_JSON::success();
} else {
$l = Util::getL10N('user_ldap');
\OC_JSON::error(['message' => $l->t('Failed to delete the server configuration')]);
}

View file

@ -1,23 +0,0 @@
<?php
use OCA\User_LDAP\LDAP;
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// Check user and app status
\OC_JSON::checkAdminUser();
\OC_JSON::checkAppEnabled('user_ldap');
\OC_JSON::callCheck();
$prefix = (string)$_POST['ldap_serverconfig_chooser'];
$ldapWrapper = new LDAP();
$connection = new \OCA\User_LDAP\Connection($ldapWrapper, $prefix);
$configuration = $connection->getConfiguration();
if (isset($configuration['ldap_agent_password']) && $configuration['ldap_agent_password'] !== '') {
// hide password
$configuration['ldap_agent_password'] = '**PASSWORD SET**';
}
\OC_JSON::success(['configuration' => $configuration]);

View file

@ -1,33 +0,0 @@
<?php
use OCA\User_LDAP\Configuration;
use OCA\User_LDAP\Helper;
use OCP\Server;
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// Check user and app status
\OC_JSON::checkAdminUser();
\OC_JSON::checkAppEnabled('user_ldap');
\OC_JSON::callCheck();
$helper = Server::get(Helper::class);
$nk = $helper->getNextServerConfigurationPrefix();
$resultData = ['configPrefix' => $nk];
$newConfig = new Configuration($nk, false);
if (isset($_POST['copyConfig'])) {
$originalConfig = new Configuration($_POST['copyConfig']);
$newConfig->setConfiguration($originalConfig->getConfiguration());
} else {
$configuration = new Configuration($nk, false);
$newConfig->setConfiguration($configuration->getDefaults());
$resultData['defaults'] = $configuration->getDefaults();
}
$newConfig->saveConfiguration();
\OC_JSON::success($resultData);

View file

@ -1,32 +0,0 @@
<?php
use OCA\User_LDAP\LDAP;
/**
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// Check user and app status
\OC_JSON::checkAdminUser();
\OC_JSON::checkAppEnabled('user_ldap');
\OC_JSON::callCheck();
$prefix = (string)$_POST['ldap_serverconfig_chooser'];
// Checkboxes are not submitted, when they are unchecked. Set them manually.
// only legacy checkboxes (Advanced and Expert tab) need to be handled here,
// the Wizard-like tabs handle it on their own
$chkboxes = ['ldap_configuration_active', 'ldap_override_main_server',
'ldap_turn_off_cert_check'];
foreach ($chkboxes as $boxid) {
if (!isset($_POST[$boxid])) {
$_POST[$boxid] = 0;
}
}
$ldapWrapper = new LDAP();
$connection = new \OCA\User_LDAP\Connection($ldapWrapper, $prefix);
$connection->setConfiguration($_POST);
$connection->saveConfiguration();
\OC_JSON::success();

View file

@ -1,76 +0,0 @@
<?php
use OCA\User_LDAP\Exceptions\ConfigurationIssueException;
use OCA\User_LDAP\LDAP;
use OCP\ISession;
use OCP\Server;
use OCP\Util;
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// Check user and app status
\OC_JSON::checkAdminUser();
\OC_JSON::checkAppEnabled('user_ldap');
\OC_JSON::callCheck();
$l = Util::getL10N('user_ldap');
$ldapWrapper = new LDAP();
$connection = new \OCA\User_LDAP\Connection($ldapWrapper, $_POST['ldap_serverconfig_chooser']);
try {
$configurationError = '';
$conf = $connection->getConfiguration();
if ($conf['ldap_configuration_active'] === '0') {
//needs to be true, otherwise it will also fail with an irritating message
$conf['ldap_configuration_active'] = '1';
}
try {
$connection->setConfiguration($conf, throw: true);
} catch (ConfigurationIssueException $e) {
$configurationError = $e->getHint();
}
if ($configurationError === '') {
//Configuration is okay
/*
* Closing the session since it won't be used from this point on. There might be a potential
* race condition if a second request is made: either this request or the other might not
* contact the LDAP backup server the first time when it should, but there shouldn't be any
* problem with that other than the extra connection.
*/
Server::get(ISession::class)->close();
if ($connection->bind()) {
/*
* This shiny if block is an ugly hack to find out whether anonymous
* bind is possible on AD or not. Because AD happily and constantly
* replies with success to any anonymous bind request, we need to
* fire up a broken operation. If AD does not allow anonymous bind,
* it will end up with LDAP error code 1 which is turned into an
* exception by the LDAP wrapper. We catch this. Other cases may
* pass (like e.g. expected syntax error).
*/
try {
$ldapWrapper->read($connection->getConnectionResource(), '', 'objectClass=*', ['dn']);
} catch (\Exception $e) {
if ($e->getCode() === 1) {
\OC_JSON::error(['message' => $l->t('Invalid configuration: Anonymous binding is not allowed.')]);
exit;
}
}
\OC_JSON::success(['message'
=> $l->t('Valid configuration, connection established!')]);
} else {
\OC_JSON::error(['message'
=> $l->t('Valid configuration, but binding failed. Please check the server settings and credentials.')]);
}
} else {
\OC_JSON::error(['message'
=> $l->t('Invalid configuration: %s', $configurationError)]);
}
} catch (\Exception $e) {
\OC_JSON::error(['message' => $e->getMessage()]);
}

View file

@ -1,120 +0,0 @@
<?php
use OCA\User_LDAP\AccessFactory;
use OCA\User_LDAP\Configuration;
use OCA\User_LDAP\LDAP;
use OCA\User_LDAP\Wizard;
use OCP\Server;
use OCP\Util;
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// Check user and app status
\OC_JSON::checkAdminUser();
\OC_JSON::checkAppEnabled('user_ldap');
\OC_JSON::callCheck();
$l = Util::getL10N('user_ldap');
if (!isset($_POST['action'])) {
\OC_JSON::error(['message' => $l->t('No action specified')]);
}
$action = (string)$_POST['action'];
if (!isset($_POST['ldap_serverconfig_chooser'])) {
\OC_JSON::error(['message' => $l->t('No configuration specified')]);
}
$prefix = (string)$_POST['ldap_serverconfig_chooser'];
$ldapWrapper = new LDAP();
$configuration = new Configuration($prefix);
$con = new \OCA\User_LDAP\Connection($ldapWrapper, $prefix, null);
$con->setConfiguration($configuration->getConfiguration());
$con->ldapConfigurationActive = (string)true;
$con->setIgnoreValidation(true);
$factory = Server::get(AccessFactory::class);
$access = $factory->get($con);
$wizard = new Wizard($configuration, $ldapWrapper, $access);
switch ($action) {
case 'guessPortAndTLS':
case 'guessBaseDN':
case 'detectEmailAttribute':
case 'detectUserDisplayNameAttribute':
case 'determineGroupMemberAssoc':
case 'determineUserObjectClasses':
case 'determineGroupObjectClasses':
case 'determineGroupsForUsers':
case 'determineGroupsForGroups':
case 'determineAttributes':
case 'getUserListFilter':
case 'getUserLoginFilter':
case 'getGroupFilter':
case 'countUsers':
case 'countGroups':
case 'countInBaseDN':
try {
$result = $wizard->$action();
if ($result !== false) {
\OC_JSON::success($result->getResultArray());
exit;
}
} catch (\Exception $e) {
\OC_JSON::error(['message' => $e->getMessage(), 'code' => $e->getCode()]);
exit;
}
\OC_JSON::error();
exit;
break;
case 'testLoginName': {
try {
$loginName = $_POST['ldap_test_loginname'];
$result = $wizard->$action($loginName);
if ($result !== false) {
\OC_JSON::success($result->getResultArray());
exit;
}
} catch (\Exception $e) {
\OC_JSON::error(['message' => $e->getMessage()]);
exit;
}
\OC_JSON::error();
exit;
break;
}
case 'save':
$key = $_POST['cfgkey'] ?? false;
$val = $_POST['cfgval'] ?? null;
if ($key === false || is_null($val)) {
\OC_JSON::error(['message' => $l->t('No data specified')]);
exit;
}
if (is_array($key)) {
\OC_JSON::error(['message' => $l->t('Invalid data specified')]);
exit;
}
$cfg = [$key => $val];
$setParameters = [];
$configuration->setConfiguration($cfg, $setParameters);
if (!in_array($key, $setParameters)) {
\OC_JSON::error(['message' => $l->t('Could not set configuration %1$s to %2$s', [$key, $setParameters[0]])]);
exit;
}
$configuration->saveConfiguration();
//clear the cache on save
$connection = new \OCA\User_LDAP\Connection($ldapWrapper, $prefix);
$connection->clearCache();
\OC_JSON::success();
break;
default:
\OC_JSON::error(['message' => $l->t('Action does not exist')]);
break;
}

View file

@ -7,20 +7,6 @@ declare(strict_types=1);
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
$this->create('user_ldap_ajax_clearMappings', 'apps/user_ldap/ajax/clearMappings.php')
->actionInclude('user_ldap/ajax/clearMappings.php');
$this->create('user_ldap_ajax_deleteConfiguration', 'apps/user_ldap/ajax/deleteConfiguration.php')
->actionInclude('user_ldap/ajax/deleteConfiguration.php');
$this->create('user_ldap_ajax_getConfiguration', 'apps/user_ldap/ajax/getConfiguration.php')
->actionInclude('user_ldap/ajax/getConfiguration.php');
$this->create('user_ldap_ajax_getNewServerConfigPrefix', 'apps/user_ldap/ajax/getNewServerConfigPrefix.php')
->actionInclude('user_ldap/ajax/getNewServerConfigPrefix.php');
$this->create('user_ldap_ajax_setConfiguration', 'apps/user_ldap/ajax/setConfiguration.php')
->actionInclude('user_ldap/ajax/setConfiguration.php');
$this->create('user_ldap_ajax_testConfiguration', 'apps/user_ldap/ajax/testConfiguration.php')
->actionInclude('user_ldap/ajax/testConfiguration.php');
$this->create('user_ldap_ajax_wizard', 'apps/user_ldap/ajax/wizard.php')
->actionInclude('user_ldap/ajax/wizard.php');
return [
'routes' => [

View file

@ -30,6 +30,7 @@ return array(
'OCA\\User_LDAP\\ConnectionFactory' => $baseDir . '/../lib/ConnectionFactory.php',
'OCA\\User_LDAP\\Controller\\ConfigAPIController' => $baseDir . '/../lib/Controller/ConfigAPIController.php',
'OCA\\User_LDAP\\Controller\\RenewPasswordController' => $baseDir . '/../lib/Controller/RenewPasswordController.php',
'OCA\\User_LDAP\\Controller\\WizardController' => $baseDir . '/../lib/Controller/WizardController.php',
'OCA\\User_LDAP\\DataCollector\\LdapDataCollector' => $baseDir . '/../lib/DataCollector/LdapDataCollector.php',
'OCA\\User_LDAP\\Db\\GroupMembership' => $baseDir . '/../lib/Db/GroupMembership.php',
'OCA\\User_LDAP\\Db\\GroupMembershipMapper' => $baseDir . '/../lib/Db/GroupMembershipMapper.php',
@ -95,5 +96,6 @@ return array(
'OCA\\User_LDAP\\User_LDAP' => $baseDir . '/../lib/User_LDAP.php',
'OCA\\User_LDAP\\User_Proxy' => $baseDir . '/../lib/User_Proxy.php',
'OCA\\User_LDAP\\Wizard' => $baseDir . '/../lib/Wizard.php',
'OCA\\User_LDAP\\WizardFactory' => $baseDir . '/../lib/WizardFactory.php',
'OCA\\User_LDAP\\WizardResult' => $baseDir . '/../lib/WizardResult.php',
);

View file

@ -45,6 +45,7 @@ class ComposerStaticInitUser_LDAP
'OCA\\User_LDAP\\ConnectionFactory' => __DIR__ . '/..' . '/../lib/ConnectionFactory.php',
'OCA\\User_LDAP\\Controller\\ConfigAPIController' => __DIR__ . '/..' . '/../lib/Controller/ConfigAPIController.php',
'OCA\\User_LDAP\\Controller\\RenewPasswordController' => __DIR__ . '/..' . '/../lib/Controller/RenewPasswordController.php',
'OCA\\User_LDAP\\Controller\\WizardController' => __DIR__ . '/..' . '/../lib/Controller/WizardController.php',
'OCA\\User_LDAP\\DataCollector\\LdapDataCollector' => __DIR__ . '/..' . '/../lib/DataCollector/LdapDataCollector.php',
'OCA\\User_LDAP\\Db\\GroupMembership' => __DIR__ . '/..' . '/../lib/Db/GroupMembership.php',
'OCA\\User_LDAP\\Db\\GroupMembershipMapper' => __DIR__ . '/..' . '/../lib/Db/GroupMembershipMapper.php',
@ -110,6 +111,7 @@ class ComposerStaticInitUser_LDAP
'OCA\\User_LDAP\\User_LDAP' => __DIR__ . '/..' . '/../lib/User_LDAP.php',
'OCA\\User_LDAP\\User_Proxy' => __DIR__ . '/..' . '/../lib/User_Proxy.php',
'OCA\\User_LDAP\\Wizard' => __DIR__ . '/..' . '/../lib/Wizard.php',
'OCA\\User_LDAP\\WizardFactory' => __DIR__ . '/..' . '/../lib/WizardFactory.php',
'OCA\\User_LDAP\\WizardResult' => __DIR__ . '/..' . '/../lib/WizardResult.php',
);

View file

@ -8,7 +8,10 @@ namespace OCA\User_LDAP\Controller;
use OCA\User_LDAP\Configuration;
use OCA\User_LDAP\ConnectionFactory;
use OCA\User_LDAP\Exceptions\ConfigurationIssueException;
use OCA\User_LDAP\Helper;
use OCA\User_LDAP\ILDAPWrapper;
use OCA\User_LDAP\LDAP;
use OCA\User_LDAP\Settings\Admin;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
@ -18,7 +21,9 @@ use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\OCSController;
use OCP\IL10N;
use OCP\IRequest;
use OCP\Server;
use Psr\Log\LoggerInterface;
class ConfigAPIController extends OCSController {
@ -28,6 +33,7 @@ class ConfigAPIController extends OCSController {
private Helper $ldapHelper,
private LoggerInterface $logger,
private ConnectionFactory $connectionFactory,
private IL10N $l,
) {
parent::__construct($appName, $request);
}
@ -227,6 +233,103 @@ class ConfigAPIController extends OCSController {
return new DataResponse($data);
}
/**
* Test a configuration
*
* @param string $configID ID of the LDAP config
* @return DataResponse<Http::STATUS_OK, array{success:bool,message:string}, array{}>
* @throws OCSException An unexpected error happened
* @throws OCSNotFoundException Config not found
*
* 200: Test was run and results are returned
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
#[ApiRoute(verb: 'POST', url: '/api/v1/config/{configID}/test')]
public function testConfiguration(string $configID) {
try {
$this->ensureConfigIDExists($configID);
$connection = $this->connectionFactory->get($configID);
$conf = $connection->getConfiguration();
if ($conf['ldap_configuration_active'] === '0') {
//needs to be true, otherwise it will also fail with an irritating message
$conf['ldap_configuration_active'] = '1';
}
try {
$connection->setConfiguration($conf, throw: true);
} catch (ConfigurationIssueException $e) {
return new DataResponse([
'success' => false,
'message' => $this->l->t('Invalid configuration: %s', $e->getHint()),
]);
}
// Configuration is okay
if (!$connection->bind()) {
return new DataResponse([
'success' => false,
'message' => $this->l->t('Valid configuration, but binding failed. Please check the server settings and credentials.'),
]);
}
/*
* This shiny if block is an ugly hack to find out whether anonymous
* bind is possible on AD or not. Because AD happily and constantly
* replies with success to any anonymous bind request, we need to
* fire up a broken operation. If AD does not allow anonymous bind,
* it will end up with LDAP error code 1 which is turned into an
* exception by the LDAP wrapper. We catch this. Other cases may
* pass (like e.g. expected syntax error).
*/
try {
$ldapWrapper = Server::get(ILDAPWrapper::class);
$ldapWrapper->read($connection->getConnectionResource(), '', 'objectClass=*', ['dn']);
} catch (\Exception $e) {
if ($e->getCode() === 1) {
return new DataResponse([
'success' => false,
'message' => $this->l->t('Invalid configuration: Anonymous binding is not allowed.'),
]);
}
}
return new DataResponse([
'success' => true,
'message' => $this->l->t('Valid configuration, connection established!'),
]);
} catch (OCSException $e) {
throw $e;
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new OCSException('An issue occurred when testing the config.');
}
}
/**
* Copy a configuration
*
* @param string $configID ID of the LDAP config
* @return DataResponse<Http::STATUS_OK, array{configID:string}, array{}>
* @throws OCSException An unexpected error happened
* @throws OCSNotFoundException Config not found
*
* 200: Config was copied, new configID was returned
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
#[ApiRoute(verb: 'POST', url: '/api/v1/config/{configID}/copy')]
public function copyConfiguration(string $configID) {
try {
$this->ensureConfigIDExists($configID);
$configPrefix = $this->ldapHelper->getNextServerConfigurationPrefix();
$newConfig = new Configuration($configPrefix, false);
$originalConfig = new Configuration($configID);
$newConfig->setConfiguration($originalConfig->getConfiguration());
$newConfig->saveConfiguration();
return new DataResponse(['configID' => $configPrefix]);
} catch (OCSException $e) {
throw $e;
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new OCSException('An issue occurred when creating the new config.');
}
}
/**
* If the given config ID is not available, an exception is thrown
*

View file

@ -0,0 +1,164 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\User_LDAP\Controller;
use OCA\User_LDAP\Configuration;
use OCA\User_LDAP\ConnectionFactory;
use OCA\User_LDAP\Mapping\GroupMapping;
use OCA\User_LDAP\Mapping\UserMapping;
use OCA\User_LDAP\Settings\Admin;
use OCA\User_LDAP\WizardFactory;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCSController;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\Server;
use OCP\User\Events\BeforeUserIdUnassignedEvent;
use OCP\User\Events\UserIdUnassignedEvent;
use Psr\Log\LoggerInterface;
class WizardController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
private LoggerInterface $logger,
private ConnectionFactory $connectionFactory,
private IL10N $l,
private WizardFactory $wizardFactory,
private IEventDispatcher $eventDispatcher,
) {
parent::__construct($appName, $request);
}
/**
* Run a wizard action and returns the result
*
* @param string $configID ID of the LDAP configuration
* @param string $wizardAction Wizard action to run
* @param ?string $loginName Login name to test for testLoginName action
* @return DataResponse<Http::STATUS_OK, array{changes:array<string,int|string|list<int>|list<string>>,options?:array<string,list<string>>}, array{}>
* @throws OCSException
*
* 200: Wizard action result
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
#[ApiRoute(verb: 'POST', url: '/api/v1/wizard/{configID}/{wizardAction}')]
public function action(
string $configID, string $wizardAction,
?string $loginName = null,
) {
try {
$wizard = $this->wizardFactory->get($configID);
switch ($wizardAction) {
case 'guessPortAndTLS':
case 'guessBaseDN':
case 'detectEmailAttribute':
case 'detectUserDisplayNameAttribute':
case 'determineGroupMemberAssoc':
case 'determineUserObjectClasses':
case 'determineGroupObjectClasses':
case 'determineGroupsForUsers':
case 'determineGroupsForGroups':
case 'determineAttributes':
case 'getUserListFilter':
case 'getUserLoginFilter':
case 'getGroupFilter':
case 'countUsers':
case 'countGroups':
case 'countInBaseDN':
try {
$result = $wizard->$wizardAction();
if ($result !== false) {
return new DataResponse($result->getResultArray());
}
} catch (\Exception $e) {
throw new OCSException($e->getMessage());
}
throw new OCSException();
case 'testLoginName':
try {
if ($loginName === null || $loginName === '') {
throw new OCSException('No login name passed');
}
$result = $wizard->$wizardAction($loginName);
if ($result !== false) {
return new DataResponse($result->getResultArray());
}
} catch (\Exception $e) {
throw new OCSException($e->getMessage());
}
throw new OCSException();
default:
throw new OCSException($this->l->t('Action does not exist'));
break;
}
} catch (OCSException $e) {
throw $e;
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new OCSException('An issue occurred.');
}
}
/**
* Clear user or group mappings
*
* @param 'user'|'group' $subject Whether to clear group or user mappings
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
* @throws OCSException
*
* 200: Clearing was done successfuly
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
#[ApiRoute(verb: 'POST', url: '/api/v1/wizard/clearMappings')]
public function clearMappings(
string $subject,
) {
$mapping = null;
try {
if ($subject === 'user') {
$mapping = Server::get(UserMapping::class);
$result = $mapping->clearCb(
function (string $uid): void {
$this->eventDispatcher->dispatchTyped(new BeforeUserIdUnassignedEvent($uid));
/** @psalm-suppress UndefinedInterfaceMethod For now we have to emit, will be removed when all hooks are removed */
Server::get(IUserManager::class)->emit('\OC\User', 'preUnassignedUserId', [$uid]);
},
function (string $uid): void {
$this->eventDispatcher->dispatchTyped(new UserIdUnassignedEvent($uid));
/** @psalm-suppress UndefinedInterfaceMethod For now we have to emit, will be removed when all hooks are removed */
Server::get(IUserManager::class)->emit('\OC\User', 'postUnassignedUserId', [$uid]);
}
);
} elseif ($subject === 'group') {
$mapping = Server::get(GroupMapping::class);
$result = $mapping->clear();
} else {
throw new OCSException($this->l->t('Unsupported subject ' . $subject));
}
if (!$result) {
throw new OCSException($this->l->t('Failed to clear the mappings.'));
}
return new DataResponse();
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new OCSException('An issue occurred.');
}
}
}

View file

@ -199,7 +199,7 @@ class Wizard extends LDAPUtility {
$count = (int)$this->countUsersWithAttribute($attr, true);
if ($count > 0) {
//no change, but we sent it back to make sure the user interface
//is still correct, even if the ajax call was cancelled meanwhile
//is still correct, even if the call was cancelled meanwhile
$this->result->addChange('ldap_display_name', $attr);
return $this->result;
}

View file

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\User_LDAP;
class WizardFactory {
public function __construct(
private ILDAPWrapper $ldap,
private AccessFactory $accessFactory,
) {
}
public function get(string $configID): Wizard {
$configuration = new Configuration($configID);
$connection = new Connection($this->ldap, $configID, null);
$connection->setConfiguration($configuration->getConfiguration());
$connection->ldapConfigurationActive = (string)true;
$connection->setIgnoreValidation(true);
$access = $this->accessFactory->get($connection);
return new Wizard($configuration, $this->ldap, $access);
}
}

View file

@ -1,52 +1,56 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\User_LDAP;
class WizardResult {
protected $changes = [];
protected $options = [];
protected $markedChange = false;
/**
* @var array<string,int|string|int[]|string[]>
*/
protected array $changes = [];
/**
* @var array<string,string[]>
*/
protected array $options = [];
protected bool $markedChange = false;
/**
* @param string $key
* @param mixed $value
* @param int|string|int[]|string[] $value
*/
public function addChange($key, $value) {
public function addChange(string $key, int|string|array $value): void {
$this->changes[$key] = $value;
}
public function markChange() {
public function markChange(): void {
$this->markedChange = true;
}
/**
* @param string $key
* @param array|string $values
* @param string|string[] $values
*/
public function addOptions($key, $values) {
public function addOptions(string $key, string|array $values): void {
if (!is_array($values)) {
$values = [$values];
}
$this->options[$key] = $values;
}
/**
* @return bool
*/
public function hasChanges() {
public function hasChanges(): bool {
return (count($this->changes) > 0 || $this->markedChange);
}
/**
* @return array
* @return array{changes:array<string,int|string|int[]|string[]>,options?:array<string,string[]>}
*/
public function getResultArray() {
public function getResultArray(): array {
$result = [];
$result['changes'] = $this->changes;
if (count($this->options) > 0) {

View file

@ -696,6 +696,670 @@
}
}
}
},
"/ocs/v2.php/apps/user_ldap/api/v1/config/{configID}/test": {
"post": {
"operationId": "configapi-test-configuration",
"summary": "Test a configuration",
"description": "This endpoint requires admin access",
"tags": [
"configapi"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "configID",
"in": "path",
"description": "ID of the LDAP config",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Test was run and results are returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"success",
"message"
],
"properties": {
"success": {
"type": "boolean"
},
"message": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "Config not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Logged in account must be an admin",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_ldap/api/v1/config/{configID}/copy": {
"post": {
"operationId": "configapi-copy-configuration",
"summary": "Copy a configuration",
"description": "This endpoint requires admin access",
"tags": [
"configapi"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "configID",
"in": "path",
"description": "ID of the LDAP config",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Config was copied, new configID was returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"configID"
],
"properties": {
"configID": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "Config not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Logged in account must be an admin",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_ldap/api/v1/wizard/{configID}/{wizardAction}": {
"post": {
"operationId": "wizard-action",
"summary": "Run a wizard action and returns the result",
"description": "This endpoint requires admin access",
"tags": [
"wizard"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"loginName": {
"type": "string",
"nullable": true,
"default": null,
"description": "Login name to test for testLoginName action"
}
}
}
}
}
},
"parameters": [
{
"name": "configID",
"in": "path",
"description": "ID of the LDAP configuration",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "wizardAction",
"in": "path",
"description": "Wizard action to run",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Wizard action result",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"changes"
],
"properties": {
"changes": {
"type": "object",
"additionalProperties": {
"anyOf": [
{
"type": "integer",
"format": "int64"
},
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "integer",
"format": "int64"
}
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"options": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Logged in account must be an admin",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_ldap/api/v1/wizard/clearMappings": {
"post": {
"operationId": "wizard-clear-mappings",
"summary": "Clear user or group mappings",
"description": "This endpoint requires admin access",
"tags": [
"wizard"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"subject"
],
"properties": {
"subject": {
"type": "string",
"enum": [
"user",
"group"
],
"description": "Whether to clear group or user mappings"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Clearing was done successfuly",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Logged in account must be an admin",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
}
},
"tags": []

View file

@ -111,7 +111,7 @@ async function getUserLoginFilter() {
*/
async function verifyLoginName() {
try {
const response = await callWizard('testLoginName', props.configId, { ldap_test_loginname: testUsername.value })
const response = await callWizard('testLoginName', props.configId, { loginName: testUsername.value })
const testLoginName = response.changes!.ldap_test_loginname as number
const testEffectiveFilter = response.changes!.ldap_test_effective_filter as string

View file

@ -83,7 +83,7 @@
<NcButton :disabled="loadingGuessBaseDN" @click="guessBaseDN">
{{ t('user_ldap', 'Detect Base DN') }}
</NcButton>
<NcButton :disabled="loadingCountInBaseDN" @click="countInBaseDN">
<NcButton :disabled="loadingCountInBaseDN || ldapConfigProxy.ldapBase === ''" @click="countInBaseDN">
{{ t('user_ldap', 'Test Base DN') }}
</NcButton>
</div>
@ -146,8 +146,7 @@ async function guessBaseDN() {
try {
loadingGuessBaseDN.value = true
const { changes } = await callWizard('guessBaseDN', props.configId)
// Not using ldapConfigProxy to avoid triggering the save logic.
ldapConfigs.value[props.configId].ldapBase = (changes!.ldap_base as string) ?? ''
ldapConfigProxy.value.ldapBase = (changes!.ldap_base as string) ?? ''
} finally {
loadingGuessBaseDN.value = false
}

View file

@ -48,8 +48,8 @@ const ldapConfigsStore = useLDAPConfigsStore()
const { updatingConfig } = storeToRefs(ldapConfigsStore)
const loading = ref(false)
const result = ref<{ message: string, status: 'error' | 'success' } | null>(null)
const isValide = computed(() => result.value?.status === 'success')
const result = ref<{ success: boolean, message: string } | null>(null)
const isValide = computed(() => result.value?.success)
watch(updatingConfig, () => {
result.value = null

View file

@ -8,15 +8,12 @@ import type { AxiosResponse } from '@nextcloud/axios'
import type { OCSResponse } from '@nextcloud/typings/ocs'
import type { LDAPConfig } from '../models/index.ts'
import axios from '@nextcloud/axios'
import axios, { isAxiosError } from '@nextcloud/axios'
import { getDialogBuilder, showError, showSuccess } from '@nextcloud/dialogs'
import { t } from '@nextcloud/l10n'
import { generateOcsUrl, generateUrl } from '@nextcloud/router'
import path from 'path'
import { generateOcsUrl } from '@nextcloud/router'
import logger from './logger.ts'
const AJAX_ENDPOINT = generateUrl('apps/user_ldap/ajax')
export type WizardAction
= 'guessPortAndTLS'
| 'guessBaseDN'
@ -35,7 +32,6 @@ export type WizardAction
| 'countGroups'
| 'countInBaseDN'
| 'testLoginName'
| 'save'
/**
*
@ -55,12 +51,12 @@ export async function copyConfig(configId: string) {
params.set('copyConfig', configId)
const response = await axios.post(
path.join(AJAX_ENDPOINT, 'getNewServerConfigPrefix.php'),
generateOcsUrl('apps/user_ldap/api/v1/config/{configId}/copy', { configId }),
params,
) as AxiosResponse<{ status: 'error' | 'success', configPrefix: string }>
) as AxiosResponse<OCSResponse<{ configID: string }>>
logger.debug('Created configuration', { configId: response.data.configPrefix })
return response.data.configPrefix
logger.debug('Created configuration', { configId: response.data.ocs.data.configID })
return response.data.ocs.data.configID
}
/**
@ -119,16 +115,12 @@ export async function deleteConfig(configId: string): Promise<boolean> {
*/
export async function testConfiguration(configId: string) {
const params = new FormData()
params.set('ldap_serverconfig_chooser', configId)
const response = await axios.post(
path.join(AJAX_ENDPOINT, 'testConfiguration.php'),
params,
) as AxiosResponse<{ message: string, status: 'error' | 'success' }>
const response = await axios.post(generateOcsUrl('apps/user_ldap/api/v1/config/{configId}/test', { configId })) as AxiosResponse<OCSResponse<{ success: boolean, message: string }>>
logger.debug(`Configuration is ${response.data.status === 'success' ? 'valide' : 'invalide'}`, { configId, params, response })
logger.debug(`Configuration is ${response.data.ocs.data.success ? 'valide' : 'invalide'}`, { configId, params, response })
return response.data
return response.data.ocs.data
}
/**
@ -144,19 +136,18 @@ export async function clearMapping(subject: 'user' | 'group') {
return false
}
const params = new FormData()
params.set('ldap_clear_mapping', subject)
try {
const response = await axios.post(
generateOcsUrl('apps/user_ldap/api/v1/wizard/clearMappings'),
{ subject },
) as AxiosResponse<OCSResponse>
const response = await axios.post(
path.join(AJAX_ENDPOINT, 'clearMappings.php'),
params,
)
if (response.data.status === 'success') {
logger.debug('Cleared mapping', { subject, params, response })
showSuccess(t('user_ldap', 'Mapping cleared'))
} else {
showError(t('user_ldap', 'Failed to clear mapping'))
return true
} catch (error) {
const errorResponse = (error as AxiosError<OCSResponse>).response
showError(errorResponse?.data.ocs.meta.message || t('user_ldap', 'Failed to clear mapping'))
}
}
@ -168,27 +159,28 @@ export async function clearMapping(subject: 'user' | 'group') {
*/
export async function callWizard(action: WizardAction, configId: string, extraParams: Record<string, string> = {}) {
const params = new FormData()
params.set('action', action)
params.set('ldap_serverconfig_chooser', configId)
Object.entries(extraParams).forEach(([key, value]) => {
params.set(key, value)
})
const response = await axios.post(
path.join(AJAX_ENDPOINT, 'wizard.php'),
params,
) as AxiosResponse<{ status: 'error', message?: string } | { status: 'success', changes?: Record<string, unknown>, options?: Record<string, []> }>
try {
const response = await axios.post(
generateOcsUrl('apps/user_ldap/api/v1/wizard/{configId}/{action}', { configId, action }),
params,
) as AxiosResponse<OCSResponse<{ changes?: Record<string, unknown>, options?: Record<string, []> }>>
logger.debug(`Called wizard action: ${action}`, { configId, params, response })
logger.debug(`Called wizard action: ${action}`, { configId, params, response })
if (response.data.status === 'error') {
const message = response.data.message ?? t('user_ldap', 'An error occurred')
showError(message)
throw new Error(message)
return response.data.ocs.data
} catch (error) {
if (isAxiosError(error) && error.response?.data.ocs.meta.status === 'failure') {
const message = error.response.data.ocs.meta.message ?? t('user_ldap', 'An error occurred')
showError(message)
}
throw error
}
return response.data
}
/**

View file

@ -8,7 +8,7 @@ import type { LDAPConfig } from '../models/index.ts'
import { loadState } from '@nextcloud/initial-state'
import { defineStore } from 'pinia'
import Vue, { computed, ref } from 'vue'
import { callWizard, copyConfig, createConfig, deleteConfig, getConfig } from '../services/ldapConfigService.ts'
import { copyConfig, createConfig, deleteConfig, getConfig, updateConfig } from '../services/ldapConfigService.ts'
export const useLDAPConfigsStore = defineStore('ldap-configs', () => {
const ldapConfigs = ref(loadState('user_ldap', 'ldapConfigs') as Record<string, LDAPConfig>)
@ -31,7 +31,7 @@ export const useLDAPConfigsStore = defineStore('ldap-configs', () => {
;(async () => {
updatingConfig.value++
await callWizard('save', configId, { cfgkey: property, cfgval: newValue })
await updateConfig(configId, { [property]: newValue })
updatingConfig.value--
if (postSetHooks[property] !== undefined) {

View file

@ -2625,88 +2625,6 @@
<code><![CDATA[getName]]></code>
</DeprecatedMethod>
</file>
<file src="apps/user_ldap/ajax/clearMappings.php">
<DeprecatedMethod>
<code><![CDATA[\OC_JSON::callCheck()]]></code>
<code><![CDATA[\OC_JSON::checkAdminUser()]]></code>
<code><![CDATA[\OC_JSON::checkAppEnabled('user_ldap')]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $e->getMessage()])]]></code>
<code><![CDATA[\OC_JSON::success()]]></code>
</DeprecatedMethod>
</file>
<file src="apps/user_ldap/ajax/deleteConfiguration.php">
<DeprecatedMethod>
<code><![CDATA[\OC_JSON::callCheck()]]></code>
<code><![CDATA[\OC_JSON::checkAdminUser()]]></code>
<code><![CDATA[\OC_JSON::checkAppEnabled('user_ldap')]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $l->t('Failed to delete the server configuration')])]]></code>
<code><![CDATA[\OC_JSON::success()]]></code>
</DeprecatedMethod>
</file>
<file src="apps/user_ldap/ajax/getConfiguration.php">
<DeprecatedMethod>
<code><![CDATA[\OC_JSON::callCheck()]]></code>
<code><![CDATA[\OC_JSON::checkAdminUser()]]></code>
<code><![CDATA[\OC_JSON::checkAppEnabled('user_ldap')]]></code>
<code><![CDATA[\OC_JSON::success(['configuration' => $configuration])]]></code>
</DeprecatedMethod>
</file>
<file src="apps/user_ldap/ajax/getNewServerConfigPrefix.php">
<DeprecatedMethod>
<code><![CDATA[\OC_JSON::callCheck()]]></code>
<code><![CDATA[\OC_JSON::checkAdminUser()]]></code>
<code><![CDATA[\OC_JSON::checkAppEnabled('user_ldap')]]></code>
<code><![CDATA[\OC_JSON::success($resultData)]]></code>
</DeprecatedMethod>
</file>
<file src="apps/user_ldap/ajax/setConfiguration.php">
<DeprecatedMethod>
<code><![CDATA[\OC_JSON::callCheck()]]></code>
<code><![CDATA[\OC_JSON::checkAdminUser()]]></code>
<code><![CDATA[\OC_JSON::checkAppEnabled('user_ldap')]]></code>
<code><![CDATA[\OC_JSON::success()]]></code>
</DeprecatedMethod>
</file>
<file src="apps/user_ldap/ajax/testConfiguration.php">
<DeprecatedMethod>
<code><![CDATA[\OC_JSON::callCheck()]]></code>
<code><![CDATA[\OC_JSON::checkAdminUser()]]></code>
<code><![CDATA[\OC_JSON::checkAppEnabled('user_ldap')]]></code>
<code><![CDATA[\OC_JSON::error(['message'
=> $l->t('Valid configuration, but binding failed. Please check the server settings and credentials.')])]]></code>
<code><![CDATA[\OC_JSON::error(['message'
=> $l->t('Invalid configuration: %s', $configurationError)])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $e->getMessage()])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $l->t('Invalid configuration: Anonymous binding is not allowed.')])]]></code>
<code><![CDATA[\OC_JSON::success(['message'
=> $l->t('Valid configuration, connection established!')])]]></code>
</DeprecatedMethod>
</file>
<file src="apps/user_ldap/ajax/wizard.php">
<DeprecatedMethod>
<code><![CDATA[\OC_JSON::callCheck()]]></code>
<code><![CDATA[\OC_JSON::checkAdminUser()]]></code>
<code><![CDATA[\OC_JSON::checkAppEnabled('user_ldap')]]></code>
<code><![CDATA[\OC_JSON::error()]]></code>
<code><![CDATA[\OC_JSON::error()]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $e->getMessage(), 'code' => $e->getCode()])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $e->getMessage()])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $l->t('Action does not exist')])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $l->t('Could not set configuration %1$s to %2$s', [$key, $setParameters[0]])])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $l->t('Invalid data specified')])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $l->t('No action specified')])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $l->t('No configuration specified')])]]></code>
<code><![CDATA[\OC_JSON::error(['message' => $l->t('No data specified')])]]></code>
<code><![CDATA[\OC_JSON::success($result->getResultArray())]]></code>
<code><![CDATA[\OC_JSON::success($result->getResultArray())]]></code>
<code><![CDATA[\OC_JSON::success()]]></code>
</DeprecatedMethod>
</file>
<file src="apps/user_ldap/appinfo/routes.php">
<InvalidScope>
<code><![CDATA[$this]]></code>
</InvalidScope>
</file>
<file src="apps/user_ldap/lib/Access.php">
<DeprecatedMethod>
<code><![CDATA[emit]]></code>

View file

@ -11,7 +11,7 @@ import _ from 'underscore'
* @class OC.Files.FileInfo
* @classdesc File information
*
* @param {Object} data file data, see attributes for details
* @param {object} data file data, see attributes for details
*
* @since 8.2
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -51,7 +51,6 @@ SPDX-FileCopyrightText: Mark <mark@remarkablemark.org>
SPDX-FileCopyrightText: Mapbox
SPDX-FileCopyrightText: Kirill Fomichev <fanatid@ya.ru> (https://github.com/fanatid)
SPDX-FileCopyrightText: Julian Gruber
SPDX-FileCopyrightText: Joyent
SPDX-FileCopyrightText: Jordan Humphreys <jordan@zurb.com>
SPDX-FileCopyrightText: Jordan Harband <ljharb@gmail.com>
SPDX-FileCopyrightText: Jordan Harband
@ -590,15 +589,6 @@ This file is generated from multiple sources. Included packages:
- parse-asn1
- version: 5.1.9
- license: ISC
- inherits
- version: 2.0.3
- license: ISC
- util
- version: 0.10.4
- license: MIT
- path
- version: 0.12.7
- license: MIT
- pbkdf2
- version: 3.1.5
- license: MIT

File diff suppressed because one or more lines are too long

View file

@ -35382,6 +35382,670 @@
}
}
},
"/ocs/v2.php/apps/user_ldap/api/v1/config/{configID}/test": {
"post": {
"operationId": "user_ldap-configapi-test-configuration",
"summary": "Test a configuration",
"description": "This endpoint requires admin access",
"tags": [
"user_ldap/configapi"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "configID",
"in": "path",
"description": "ID of the LDAP config",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Test was run and results are returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"success",
"message"
],
"properties": {
"success": {
"type": "boolean"
},
"message": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "Config not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Logged in account must be an admin",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_ldap/api/v1/config/{configID}/copy": {
"post": {
"operationId": "user_ldap-configapi-copy-configuration",
"summary": "Copy a configuration",
"description": "This endpoint requires admin access",
"tags": [
"user_ldap/configapi"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "configID",
"in": "path",
"description": "ID of the LDAP config",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Config was copied, new configID was returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"configID"
],
"properties": {
"configID": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "Config not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Logged in account must be an admin",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_ldap/api/v1/wizard/{configID}/{wizardAction}": {
"post": {
"operationId": "user_ldap-wizard-action",
"summary": "Run a wizard action and returns the result",
"description": "This endpoint requires admin access",
"tags": [
"user_ldap/wizard"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"loginName": {
"type": "string",
"nullable": true,
"default": null,
"description": "Login name to test for testLoginName action"
}
}
}
}
}
},
"parameters": [
{
"name": "configID",
"in": "path",
"description": "ID of the LDAP configuration",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "wizardAction",
"in": "path",
"description": "Wizard action to run",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Wizard action result",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"changes"
],
"properties": {
"changes": {
"type": "object",
"additionalProperties": {
"anyOf": [
{
"type": "integer",
"format": "int64"
},
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "integer",
"format": "int64"
}
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"options": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Logged in account must be an admin",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_ldap/api/v1/wizard/clearMappings": {
"post": {
"operationId": "user_ldap-wizard-clear-mappings",
"summary": "Clear user or group mappings",
"description": "This endpoint requires admin access",
"tags": [
"user_ldap/wizard"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"subject"
],
"properties": {
"subject": {
"type": "string",
"enum": [
"user",
"group"
],
"description": "Whether to clear group or user mappings"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Clearing was done successfuly",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"401": {
"description": "Current user is not logged in",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Logged in account must be an admin",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/user_status/api/v1/heartbeat": {
"put": {
"operationId": "user_status-heartbeat-heartbeat",

View file

@ -114,16 +114,16 @@ class UrlGeneratorTest extends \Test\TestCase {
public static function provideDocRootAppUrlParts(): array {
return [
['user_ldap', 'ajax/wizard.php', [], '/index.php/apps/user_ldap/ajax/wizard.php'],
['user_ldap', 'ajax/wizard.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php/apps/user_ldap/ajax/wizard.php?trut=trat&dut=dat'],
['testing', 'ajax/endpoint.php', [], '/index.php/apps/testing/ajax/endpoint.php'],
['testing', 'ajax/endpoint.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php/apps/testing/ajax/endpoint.php?trut=trat&dut=dat'],
['', 'index.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php?trut=trat&dut=dat'],
];
}
public static function provideSubDirAppUrlParts(): array {
return [
['user_ldap', 'ajax/wizard.php', [], '/nextcloud/index.php/apps/user_ldap/ajax/wizard.php'],
['user_ldap', 'ajax/wizard.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php/apps/user_ldap/ajax/wizard.php?trut=trat&dut=dat'],
['testing', 'ajax/endpoint.php', [], '/nextcloud/index.php/apps/testing/ajax/endpoint.php'],
['testing', 'ajax/endpoint.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php/apps/testing/ajax/endpoint.php?trut=trat&dut=dat'],
['', 'index.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php?trut=trat&dut=dat'],
];
}