Skip to content
Open
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
2 changes: 1 addition & 1 deletion lib/GroupBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ public function createGroup(string $name, ?string $samlGid = null): ?string {
'app' => 'user_saml',
'exception' => $e,
]);
return null;
}
return null;
}

// Add to cache
Expand Down
85 changes: 85 additions & 0 deletions tests/unit/GroupBackendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,91 @@ public function testSearchInGroupMatchesDisplayNameAndEmail(): void {
$this->assertArrayHasKey($this->users[0]['uid'], $byUid, 'UID search should still work');
}

public function testGetBackendName(): void {
$this->assertSame('user_saml', $this->groupBackend->getBackendName());
}

public function testGetDisplayName(): void {
$group = $this->groups[0];
$this->assertSame($group['saml_gid'], $this->groupBackend->getDisplayName($group['gid']));

// falls back to the gid itself when the group is unknown
$this->assertSame('unknown_gid', $this->groupBackend->getDisplayName('unknown_gid'));
}

public function testSetDisplayName(): void {
$group = $this->groups[2];
$this->assertTrue($this->groupBackend->setDisplayName($group['gid'], 'New Display Name'));
$this->assertSame('New Display Name', $this->groupBackend->getDisplayName($group['gid']));

$this->assertFalse($this->groupBackend->setDisplayName('unknown_gid', 'New Display Name'));
}

public function testGroupsExists(): void {
$gids = array_column($this->groups, 'gid');
$result = $this->groupBackend->groupsExists([...$gids, 'unknown_gid']);

$this->assertCount(count($gids), $result);
foreach ($gids as $gid) {
$this->assertContains($gid, $result);
}
}

public function testGetGroupDetails(): void {
$group = $this->groups[0];
$this->assertSame(['displayName' => $group['saml_gid']], $this->groupBackend->getGroupDetails($group['gid']));
$this->assertSame([], $this->groupBackend->getGroupDetails('unknown_gid'));
}

public function testGetGroupsDetails(): void {
$group = $this->groups[0];
$result = $this->groupBackend->getGroupsDetails([$group['gid'], 'unknown_gid']);

$this->assertSame(['displayName' => $group['saml_gid']], $result[$group['gid']]);
$this->assertArrayNotHasKey('unknown_gid', $result);
}

public function testAddToGroupIsIdempotent(): void {
$group = $this->groups[2];
$uid = $this->users[0]['uid'];
$this->assertFalse($this->groupBackend->inGroup($uid, $group['gid']));

$this->assertTrue($this->groupBackend->addToGroup($uid, $group['gid']));
$this->assertTrue($this->groupBackend->inGroup($uid, $group['gid']));

// adding an already-member user is a no-op that still reports success
$this->assertTrue($this->groupBackend->addToGroup($uid, $group['gid']));

$this->groupBackend->removeFromGroup($uid, $group['gid']);
}

public function testRemoveFromGroupReturnsFalseWhenNotMember(): void {
$group = $this->groups[2];
$uid = $this->users[0]['uid'];

$this->assertFalse($this->groupBackend->removeFromGroup($uid, $group['gid']));

$this->groupBackend->addToGroup($uid, $group['gid']);
$this->assertTrue($this->groupBackend->removeFromGroup($uid, $group['gid']));
}

public function testCreateGroupReturnsNullOnDuplicate(): void {
$existingGroup = $this->groups[0];
$this->assertNull($this->groupBackend->createGroup($existingGroup['gid']));
}

public function testDeleteGroup(): void {
$gid = $this->groupBackend->createGroup('user_saml_integration_test_throwaway_group');
$this->assertNotNull($gid);
$this->assertTrue($this->groupBackend->groupExists($gid));

$this->assertTrue($this->groupBackend->deleteGroup($gid));
$this->assertFalse($this->groupBackend->groupExists($gid));

// deleting an already-deleted group still reports success
$this->assertTrue($this->groupBackend->deleteGroup($gid));
}

private function resetAccountData(): void {
foreach ($this->users as $user) {
$qb = $this->connection->getQueryBuilder();
Expand Down
162 changes: 162 additions & 0 deletions tests/unit/UserBackendIntegrationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\User_SAML\Tests;

use OCA\User_SAML\GroupManager;
use OCA\User_SAML\SAMLSettings;
use OCA\User_SAML\UserBackend;
use OCA\User_SAML\UserData;
use OCP\AppFramework\Services\IAppConfig;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
use Test\TestCase;

/**
* @group DB
*/
class UserBackendIntegrationTest extends TestCase {
private IDBConnection $db;
private UserBackend $userBackend;

private array $users = [
[
'uid' => 'user_saml_integration_test_uid1',
'displayname' => 'SAML Integration User One',
'home' => '/tmp/user_saml_integration_test_uid1',
],
[
'uid' => 'user_saml_integration_test_uid2',
'displayname' => 'SAML Integration User Two',
'home' => '/tmp/user_saml_integration_test_uid2',
],
];

#[\Override]
protected function setUp(): void {
parent::setUp();

$this->db = \OCP\Server::get(IDBConnection::class);
$this->cleanupUsers();
$this->cleanupKnownUsers();

foreach ($this->users as $user) {
$qb = $this->db->getQueryBuilder();
$qb->insert('user_saml_users')
->setValue('uid', $qb->createNamedParameter($user['uid']))
->setValue('displayname', $qb->createNamedParameter($user['displayname']))
->setValue('home', $qb->createNamedParameter($user['home']))
->executeStatement();
}

$this->userBackend = new UserBackend(
$this->createMock(IConfig::class),
$this->createMock(IAppConfig::class),
$this->createMock(IURLGenerator::class),
$this->createMock(ISession::class),
$this->db,
$this->createMock(IUserManager::class),
$this->createMock(GroupManager::class),
$this->getMockBuilder(SAMLSettings::class)->disableOriginalConstructor()->getMock(),
$this->createMock(LoggerInterface::class),
$this->createMock(UserData::class),
$this->createMock(IEventDispatcher::class),
'serverRoot',
);
}

#[\Override]
protected function tearDown(): void {
parent::tearDown();
$this->cleanupUsers();
$this->cleanupKnownUsers();
}

private function cleanupUsers(): void {
foreach ($this->users as $user) {
$qb = $this->db->getQueryBuilder();
$qb->delete('user_saml_users')
->where($qb->expr()->eq('uid', $qb->createNamedParameter($user['uid'])))
->executeStatement();
}
}

private function cleanupKnownUsers(): void {
$qb = $this->db->getQueryBuilder();
$qb->delete('known_users')
->where($qb->expr()->eq('known_to', $qb->createNamedParameter('user_saml_integration_test_searcher')))
->executeStatement();
}

public function testUserExists(): void {
$this->assertTrue($this->userBackend->userExists('user_saml_integration_test_uid1'));
$this->assertFalse($this->userBackend->userExists('user_saml_integration_test_nonexistent'));
}

public function testGetHome(): void {
$this->assertSame('/tmp/user_saml_integration_test_uid1', $this->userBackend->getHome('user_saml_integration_test_uid1'));
$this->assertFalse($this->userBackend->getHome('user_saml_integration_test_nonexistent'));
}

public function testGetDisplayName(): void {
$this->assertSame('SAML Integration User One', $this->userBackend->getDisplayName('user_saml_integration_test_uid1'));
// falls back to the uid itself when no row exists
$this->assertSame('user_saml_integration_test_nonexistent', $this->userBackend->getDisplayName('user_saml_integration_test_nonexistent'));
}

public function testGetDisplayNames(): void {
$displayNames = $this->userBackend->getDisplayNames('SAML Integration User');
$this->assertSame('SAML Integration User One', $displayNames['user_saml_integration_test_uid1']);
$this->assertSame('SAML Integration User Two', $displayNames['user_saml_integration_test_uid2']);
}

public function testGetUsers(): void {
$users = $this->userBackend->getUsers('user_saml_integration_test');
$this->assertContains('user_saml_integration_test_uid1', $users);
$this->assertContains('user_saml_integration_test_uid2', $users);
}

public function testSetDisplayName(): void {
$this->assertTrue($this->userBackend->setDisplayName('user_saml_integration_test_uid1', 'Renamed User'));
$this->assertSame('Renamed User', $this->userBackend->getDisplayName('user_saml_integration_test_uid1'));

$this->assertFalse($this->userBackend->setDisplayName('user_saml_integration_test_nonexistent', 'Nope'));
}

public function testCountUsers(): void {
$this->assertSame(count($this->users), $this->userBackend->countUsers());
}

public function testDeleteUser(): void {
$this->assertTrue($this->userBackend->deleteUser('user_saml_integration_test_uid1'));
$this->assertFalse($this->userBackend->userExists('user_saml_integration_test_uid1'));

// deleting an already-deleted (or never existing) user reports no rows affected
$this->assertFalse($this->userBackend->deleteUser('user_saml_integration_test_uid1'));
}

public function testSearchKnownUsersByDisplayName(): void {
$qb = $this->db->getQueryBuilder();
$qb->insert('known_users')
->setValue('known_to', $qb->createNamedParameter('user_saml_integration_test_searcher'))
->setValue('known_user', $qb->createNamedParameter('user_saml_integration_test_uid1'))
->executeStatement();

$result = $this->userBackend->searchKnownUsersByDisplayName('user_saml_integration_test_searcher', 'User One');
$this->assertSame(['user_saml_integration_test_uid1' => 'SAML Integration User One'], $result);

$result = $this->userBackend->searchKnownUsersByDisplayName('user_saml_integration_test_searcher', 'User Two');
$this->assertSame([], $result);
}
}
Loading
Loading