diff --git a/apps/user_ldap/composer/composer/autoload_classmap.php b/apps/user_ldap/composer/composer/autoload_classmap.php
index a1743773f49eb..b1a7ed2f376b2 100644
--- a/apps/user_ldap/composer/composer/autoload_classmap.php
+++ b/apps/user_ldap/composer/composer/autoload_classmap.php
@@ -28,6 +28,7 @@
'OCA\\User_LDAP\\Configuration' => $baseDir . '/../lib/Configuration.php',
'OCA\\User_LDAP\\Connection' => $baseDir . '/../lib/Connection.php',
'OCA\\User_LDAP\\ConnectionFactory' => $baseDir . '/../lib/ConnectionFactory.php',
+ 'OCA\\User_LDAP\\Controller\\APIController' => $baseDir . '/../lib/Controller/APIController.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',
@@ -83,6 +84,7 @@
'OCA\\User_LDAP\\PagedResults\\TLinkId' => $baseDir . '/../lib/PagedResults/TLinkId.php',
'OCA\\User_LDAP\\Proxy' => $baseDir . '/../lib/Proxy.php',
'OCA\\User_LDAP\\Service\\BirthdateParserService' => $baseDir . '/../lib/Service/BirthdateParserService.php',
+ 'OCA\\User_LDAP\\Service\\CheckUserService' => $baseDir . '/../lib/Service/CheckUserService.php',
'OCA\\User_LDAP\\Service\\UpdateGroupsService' => $baseDir . '/../lib/Service/UpdateGroupsService.php',
'OCA\\User_LDAP\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
'OCA\\User_LDAP\\Settings\\Section' => $baseDir . '/../lib/Settings/Section.php',
diff --git a/apps/user_ldap/composer/composer/autoload_static.php b/apps/user_ldap/composer/composer/autoload_static.php
index af3ddfbdf020a..ec718ae56517c 100644
--- a/apps/user_ldap/composer/composer/autoload_static.php
+++ b/apps/user_ldap/composer/composer/autoload_static.php
@@ -43,6 +43,7 @@ class ComposerStaticInitUser_LDAP
'OCA\\User_LDAP\\Configuration' => __DIR__ . '/..' . '/../lib/Configuration.php',
'OCA\\User_LDAP\\Connection' => __DIR__ . '/..' . '/../lib/Connection.php',
'OCA\\User_LDAP\\ConnectionFactory' => __DIR__ . '/..' . '/../lib/ConnectionFactory.php',
+ 'OCA\\User_LDAP\\Controller\\APIController' => __DIR__ . '/..' . '/../lib/Controller/APIController.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',
@@ -98,6 +99,7 @@ class ComposerStaticInitUser_LDAP
'OCA\\User_LDAP\\PagedResults\\TLinkId' => __DIR__ . '/..' . '/../lib/PagedResults/TLinkId.php',
'OCA\\User_LDAP\\Proxy' => __DIR__ . '/..' . '/../lib/Proxy.php',
'OCA\\User_LDAP\\Service\\BirthdateParserService' => __DIR__ . '/..' . '/../lib/Service/BirthdateParserService.php',
+ 'OCA\\User_LDAP\\Service\\CheckUserService' => __DIR__ . '/..' . '/../lib/Service/CheckUserService.php',
'OCA\\User_LDAP\\Service\\UpdateGroupsService' => __DIR__ . '/..' . '/../lib/Service/UpdateGroupsService.php',
'OCA\\User_LDAP\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
'OCA\\User_LDAP\\Settings\\Section' => __DIR__ . '/..' . '/../lib/Settings/Section.php',
diff --git a/apps/user_ldap/lib/Command/CheckUser.php b/apps/user_ldap/lib/Command/CheckUser.php
index 363518673e76c..ccb218e987d58 100644
--- a/apps/user_ldap/lib/Command/CheckUser.php
+++ b/apps/user_ldap/lib/Command/CheckUser.php
@@ -8,10 +8,7 @@
namespace OCA\User_LDAP\Command;
-use OCA\User_LDAP\Helper;
-use OCA\User_LDAP\Mapping\UserMapping;
-use OCA\User_LDAP\User\DeletedUsersIndex;
-use OCA\User_LDAP\User_Proxy;
+use OCA\User_LDAP\Service\CheckUserService;
use OCP\IUserManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -21,11 +18,8 @@
class CheckUser extends Command {
public function __construct(
- protected User_Proxy $backend,
- protected Helper $helper,
- protected DeletedUsersIndex $dui,
- protected UserMapping $mapping,
- protected IUserManager $userManager,
+ private readonly IUserManager $userManager,
+ private readonly CheckUserService $checkUserService,
) {
parent::__construct();
}
@@ -77,7 +71,11 @@ protected function configure(): void {
#[\Override]
protected function execute(InputInterface $input, OutputInterface $output): int {
try {
- $this->assertAllowed($input->getOption('force'));
+ if (!$this->checkUserService->assertAllowed($input->getOption('force'))) {
+ throw new \Exception('Cannot check user existence, because '
+ . 'disabled LDAP configurations are present.');
+ }
+
$uid = $input->getArgument('ocName');
if ($uid !== null) {
@@ -109,77 +107,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
private function checkUser(InputInterface $input, OutputInterface $output, string $uid): int {
- if ($this->backend->getLDAPAccess($uid)->stringResemblesDN($uid)) {
- $username = $this->backend->dn2UserName($uid);
- if ($username !== false) {
- $uid = $username;
- }
- }
- $wasMapped = $this->userWasMapped($uid);
- $exists = $this->backend->userExistsOnLDAP($uid, true);
- if ($exists === true) {
+ $result = $this->checkUserService->checkUser($uid, $input->getOption('update'));
+ if ($result['exists']) {
$output->writeln('The user is still available on LDAP.');
- if ($input->getOption('update')) {
- $this->updateUser($uid, $output);
- }
- return self::SUCCESS;
- }
-
- if ($wasMapped) {
- $this->dui->markUser($uid);
+ } elseif ($result['wasMapped']) {
$output->writeln('The user does not exists on LDAP anymore.');
- $output->writeln('Clean up the user\'s remnants by: ./occ user:delete "'
- . $uid . '"');
- return self::SUCCESS;
- }
-
- throw new \Exception('The given user is not a recognized LDAP user.');
- }
-
- /**
- * checks whether a user is actually mapped
- * @param string $ocName the username as used in Nextcloud
- */
- protected function userWasMapped(string $ocName): bool {
- $dn = $this->mapping->getDNByName($ocName);
- return $dn !== false;
- }
-
- /**
- * checks whether the setup allows reliable checking of LDAP user existence
- * @throws \Exception
- */
- protected function assertAllowed(bool $force): void {
- if ($this->helper->haveDisabledConfigurations() && !$force) {
- throw new \Exception('Cannot check user existence, because '
- . 'disabled LDAP configurations are present.');
+ $output->writeln('Clean up the user\'s remnants by: ./occ user:delete "' . $uid . '"');
}
-
- // we don't check ldapUserCleanupInterval from config.php because this
- // action is triggered manually, while the setting only controls the
- // background job.
- }
-
- private function updateUser(string $uid, OutputInterface $output): void {
- try {
- $access = $this->backend->getLDAPAccess($uid);
- $attrs = $access->userManager->getAttributes();
- $user = $access->userManager->get($uid);
- $avatarAttributes = $access->getConnection()->resolveRule('avatar');
- $baseDn = $this->helper->DNasBaseParameter($user->getDN());
- $result = $access->search('objectclass=*', $baseDn, $attrs, 1, 0);
- foreach ($result[0] as $attribute => $valueSet) {
- $output->writeln(' ' . $attribute . ': ');
- foreach ($valueSet as $value) {
- if (in_array($attribute, $avatarAttributes)) {
- $value = '{ImageData}';
- }
- $output->writeln(' ' . $value);
- }
+ if (isset($result['attributes'])) {
+ foreach ($result['attributes'] as $attribute => $value) {
+ $output->writeln(' ' . $attribute . ': ' . $value);
}
- $access->batchApplyUserAttributes($result);
- } catch (\Exception $e) {
- $output->writeln('Error while trying to lookup and update attributes from LDAP');
}
+ return self::SUCCESS;
}
}
diff --git a/apps/user_ldap/lib/Controller/APIController.php b/apps/user_ldap/lib/Controller/APIController.php
new file mode 100644
index 0000000000000..86b0cc3ceb23c
--- /dev/null
+++ b/apps/user_ldap/lib/Controller/APIController.php
@@ -0,0 +1,55 @@
+}, array{}>
+ * @throws OCSException An unexpected error happened
+ * @throws OCSForbiddenException Cannot check user existence
+ *
+ * 200: The user configuration as found in the LDAP server
+ */
+ #[ApiRoute(verb: 'POST', url: '/api/v1/checkUser/{userID}')]
+ #[AuthorizedAdminSetting(settings: Admin::class)]
+ public function checkUser(string $userID): DataResponse {
+ if ($this->checkUserService->assertAllowed(false)) {
+ throw new OCSForbiddenException('Cannot check user existence, because disabled LDAP configurations are present.');
+ }
+
+ $result = $this->checkUserService->checkUser($userID, true);
+ return new DataResponse($result);
+ }
+}
diff --git a/apps/user_ldap/lib/Controller/ConfigAPIController.php b/apps/user_ldap/lib/Controller/ConfigAPIController.php
index e1faacac902f5..e91dec074c619 100644
--- a/apps/user_ldap/lib/Controller/ConfigAPIController.php
+++ b/apps/user_ldap/lib/Controller/ConfigAPIController.php
@@ -314,7 +314,7 @@ public function testConfiguration(string $configID) {
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
#[ApiRoute(verb: 'POST', url: '/api/v1/config/{configID}/copy')]
- public function copyConfiguration(string $configID) {
+ public function copyConfiguration(string $configID): DataResponse {
try {
$this->ensureConfigIDExists($configID);
$configPrefix = $this->ldapHelper->getNextServerConfigurationPrefix();
@@ -337,8 +337,7 @@ public function copyConfiguration(string $configID) {
* @param string $configID
* @throws OCSNotFoundException
*/
- #[AuthorizedAdminSetting(settings: Admin::class)]
- private function ensureConfigIDExists($configID): void {
+ private function ensureConfigIDExists(string $configID): void {
$prefixes = $this->ldapHelper->getServerConfigurationPrefixes();
if (!in_array($configID, $prefixes, true)) {
throw new OCSNotFoundException('Config ID not found');
diff --git a/apps/user_ldap/lib/Service/CheckUserService.php b/apps/user_ldap/lib/Service/CheckUserService.php
new file mode 100644
index 0000000000000..5a4a222e566ad
--- /dev/null
+++ b/apps/user_ldap/lib/Service/CheckUserService.php
@@ -0,0 +1,106 @@
+}
+ * @throws UserNotFoundException
+ * @throws \OCP\PreConditionNotMetException
+ */
+ public function checkUser(string $uid, bool $update): int {
+ if ($this->backend->getLDAPAccess($uid)->stringResemblesDN($uid)) {
+ $username = $this->backend->dn2UserName($uid);
+ if ($username !== false) {
+ $uid = $username;
+ }
+ }
+ $wasMapped = $this->userWasMapped($uid);
+ $exists = $this->backend->userExistsOnLDAP($uid, true);
+ if ($exists === true) {
+ if ($update) {
+ $attributes = $this->updateUser($uid);
+ return ['exists' => true, 'wasMapped' => $wasMapped, 'attributes' => $attributes];
+ }
+ return ['exists' => true, 'wasMapped' => $wasMapped];
+ }
+
+ if (!$wasMapped) {
+ throw new UserNotFoundException('The given user is not a recognized LDAP user.');
+ }
+
+ $this->dui->markUser($uid);
+ return ['exists' => true, 'wasMapped' => $wasMapped];
+ }
+
+ /**
+ * checks whether a user is actually mapped
+ * @param string $ocName the username as used in Nextcloud
+ */
+ protected function userWasMapped(string $ocName): bool {
+ $dn = $this->mapping->getDNByName($ocName);
+ return $dn !== false;
+ }
+
+ /**
+ * Checks whether the setup allows reliable checking of LDAP user existence
+ */
+ public function assertAllowed(bool $force): bool {
+ // we don't check ldapUserCleanupInterval from config.php because this
+ // action is triggered manually, while the setting only controls the
+ // background job.
+ return !$this->helper->haveDisabledConfigurations() || $force;
+ }
+
+ /**
+ * @param string $uid
+ * @return array The attributes
+ */
+ private function updateUser(string $uid): array {
+ try {
+ $access = $this->backend->getLDAPAccess($uid);
+ $attrs = $access->userManager->getAttributes();
+ $user = $access->userManager->get($uid);
+ $avatarAttributes = $access->getConnection()->resolveRule('avatar');
+ $baseDn = $this->helper->DNasBaseParameter($user->getDN());
+ $result = $access->search('objectclass=*', $baseDn, $attrs, 1, 0);
+ $attributes = [];
+ foreach ($result[0] as $attribute => $valueSet) {
+ foreach ($valueSet as $value) {
+ if (in_array($attribute, $avatarAttributes)) {
+ $value = '{ImageData}';
+ }
+ $attributes[$attribute] = $value;
+ }
+ }
+ $access->batchApplyUserAttributes($result);
+ return $attributes;
+ } catch (\Exception $e) {
+ throw new \Exception('Error while trying to lookup and update attributes from LDAP', previous: $e);
+ }
+ }
+}
diff --git a/apps/user_ldap/openapi.json b/apps/user_ldap/openapi.json
index 08e1f34dc0e9e..2d525ee87accb 100644
--- a/apps/user_ldap/openapi.json
+++ b/apps/user_ldap/openapi.json
@@ -47,6 +47,176 @@
}
},
"paths": {
+ "/ocs/v2.php/apps/user_ldap/api/v1/checkUser/{userID}": {
+ "post": {
+ "operationId": "api-check-user",
+ "summary": "Check a user and update its attribute in the LDAP server",
+ "description": "This endpoint requires admin access",
+ "tags": [
+ "api"
+ ],
+ "security": [
+ {
+ "bearer_auth": []
+ },
+ {
+ "basic_auth": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "userID",
+ "in": "path",
+ "description": "ID of the user",
+ "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": "The user configuration as found in the LDAP server",
+ "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": [
+ "exists",
+ "wasMapped"
+ ],
+ "properties": {
+ "exists": {
+ "type": "boolean"
+ },
+ "wasMapped": {
+ "type": "boolean"
+ },
+ "attributes": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Cannot check user existence",
+ "content": {
+ "application/json": {
+ "schema": {
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "ocs"
+ ],
+ "properties": {
+ "ocs": {
+ "type": "object",
+ "required": [
+ "meta",
+ "data"
+ ],
+ "properties": {
+ "meta": {
+ "$ref": "#/components/schemas/OCSMeta"
+ },
+ "data": {}
+ }
+ }
+ }
+ },
+ {
+ "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": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/ocs/v2.php/apps/user_ldap/api/v1/config": {
"post": {
"operationId": "configapi-create",
diff --git a/openapi.json b/openapi.json
index d329d32c3ab62..cd2adf0777a47 100644
--- a/openapi.json
+++ b/openapi.json
@@ -37554,6 +37554,176 @@
}
}
},
+ "/ocs/v2.php/apps/user_ldap/api/v1/checkUser/{userID}": {
+ "post": {
+ "operationId": "user_ldap-api-check-user",
+ "summary": "Check a user and update its attribute in the LDAP server",
+ "description": "This endpoint requires admin access",
+ "tags": [
+ "user_ldap/api"
+ ],
+ "security": [
+ {
+ "bearer_auth": []
+ },
+ {
+ "basic_auth": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "userID",
+ "in": "path",
+ "description": "ID of the user",
+ "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": "The user configuration as found in the LDAP server",
+ "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": [
+ "exists",
+ "wasMapped"
+ ],
+ "properties": {
+ "exists": {
+ "type": "boolean"
+ },
+ "wasMapped": {
+ "type": "boolean"
+ },
+ "attributes": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Cannot check user existence",
+ "content": {
+ "application/json": {
+ "schema": {
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "ocs"
+ ],
+ "properties": {
+ "ocs": {
+ "type": "object",
+ "required": [
+ "meta",
+ "data"
+ ],
+ "properties": {
+ "meta": {
+ "$ref": "#/components/schemas/OCSMeta"
+ },
+ "data": {}
+ }
+ }
+ }
+ },
+ {
+ "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": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/ocs/v2.php/apps/user_ldap/api/v1/config": {
"post": {
"operationId": "user_ldap-configapi-create",