-
Notifications
You must be signed in to change notification settings - Fork 295
fix(provisioning): Do not provision mail account for users without access to mail app #12226
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| namespace OCA\Mail\Service\Provisioning; | ||
|
|
||
| use Horde_Mail_Rfc822_Address; | ||
| use OCA\Mail\AppInfo\Application; | ||
| use OCA\Mail\Db\Alias; | ||
| use OCA\Mail\Db\AliasMapper; | ||
| use OCA\Mail\Db\MailAccount; | ||
|
|
@@ -19,8 +20,10 @@ | |
| use OCA\Mail\Db\TagMapper; | ||
| use OCA\Mail\Exception\ValidationException; | ||
| use OCA\Mail\Service\AccountService; | ||
| use OCP\App\IAppManager; | ||
| use OCP\AppFramework\Db\DoesNotExistException; | ||
| use OCP\AppFramework\Db\MultipleObjectsReturnedException; | ||
| use OCP\DB\Exception; | ||
| use OCP\ICacheFactory; | ||
| use OCP\IUser; | ||
| use OCP\IUserManager; | ||
|
|
@@ -30,6 +33,8 @@ | |
|
|
||
| class Manager { | ||
| public const MAIL_PROVISIONINGS = 'mail_provisionings'; | ||
| /** @var IAppManager */ | ||
| private $appManager; | ||
| /** @var IUserManager */ | ||
| private $userManager; | ||
|
|
||
|
|
@@ -58,6 +63,7 @@ class Manager { | |
| private $cacheFactory; | ||
|
|
||
| public function __construct( | ||
| IAppManager $appManager, | ||
| IUserManager $userManager, | ||
| ProvisioningMapper $provisioningMapper, | ||
| MailAccountMapper $mailAccountMapper, | ||
|
|
@@ -69,6 +75,7 @@ public function __construct( | |
| ICacheFactory $cacheFactory, | ||
| private AccountService $accountService, | ||
| ) { | ||
| $this->appManager = $appManager; | ||
| $this->userManager = $userManager; | ||
| $this->provisioningMapper = $provisioningMapper; | ||
| $this->mailAccountMapper = $mailAccountMapper; | ||
|
|
@@ -125,7 +132,7 @@ public function provision(): int { | |
| * A alias is orphaned if not listed in newAliases anymore | ||
| * (=> the provisioning configuration does contain it anymore) | ||
| * | ||
| * @throws \OCP\DB\Exception | ||
| * @throws Exception | ||
| */ | ||
| private function deleteOrphanedAliases(string $userId, int $accountId, array $newAliases): void { | ||
| $existingAliases = $this->aliasMapper->findAll($accountId, $userId); | ||
|
|
@@ -139,7 +146,7 @@ private function deleteOrphanedAliases(string $userId, int $accountId, array $ne | |
| /** | ||
| * Create new aliases for the given account. | ||
| * | ||
| * @throws \OCP\DB\Exception | ||
| * @throws Exception | ||
| */ | ||
| private function createNewAliases(string $userId, int $accountId, array $newAliases, string $displayName, string $accountEmail): void { | ||
| foreach ($newAliases as $newAlias) { | ||
|
|
@@ -186,6 +193,26 @@ public function ldapAliasesIntegration(Provisioning $provisioning, IUser $user): | |
| * @param Provisioning[] $provisionings | ||
| */ | ||
| public function provisionSingleUser(array $provisionings, IUser $user): bool { | ||
| $unprovisionUser = function (string $userUid) : void { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please make that a class method. |
||
| try { | ||
| /** @noinspection PhpUnhandledExceptionInspection */ | ||
| $this->aliasMapper->deleteProvisionedAliasesByUid($userUid); | ||
| /** @noinspection PhpUnhandledExceptionInspection */ | ||
| $this->mailAccountMapper->deleteProvisionedAccountsByUid($userUid); | ||
| } catch (Exception $e) { | ||
| $this->logger->warning( | ||
| "Error during deletion of mail provisioning profile for user with UID {$userUid}", | ||
| ['exception' => $e] | ||
| ); | ||
| } | ||
| }; | ||
|
|
||
| if (!$this->appManager->isEnabledForUser(Application::APP_ID, $user)) { | ||
| $unprovisionUser($user->getUID()); | ||
|
|
||
| return false; | ||
DerDreschner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| $provisioning = $this->findMatchingConfig($provisionings, $user); | ||
|
|
||
| if ($provisioning === null) { | ||
|
|
@@ -203,8 +230,7 @@ public function provisionSingleUser(array $provisionings, IUser $user): bool { | |
| if ($e instanceof MultipleObjectsReturnedException) { | ||
| // This is unlikely to happen but not impossible. | ||
| // Let's wipe any existing accounts and start fresh | ||
| $this->aliasMapper->deleteProvisionedAliasesByUid($user->getUID()); | ||
| $this->mailAccountMapper->deleteProvisionedAccountsByUid($user->getUID()); | ||
| $unprovisionUser($user->getUID()); | ||
| } | ||
|
|
||
| // Fine, then we create a new one | ||
|
|
@@ -244,7 +270,7 @@ public function provisionSingleUser(array $provisionings, IUser $user): bool { | |
|
|
||
| /** | ||
| * @throws ValidationException | ||
| * @throws \OCP\DB\Exception | ||
| * @throws Exception | ||
| */ | ||
| public function newProvisioning(array $data): Provisioning { | ||
| $provisioning = $this->provisioningMapper->validate($data); | ||
|
|
@@ -258,7 +284,7 @@ public function newProvisioning(array $data): Provisioning { | |
|
|
||
| /** | ||
| * @throws ValidationException | ||
| * @throws \OCP\DB\Exception | ||
| * @throws Exception | ||
| */ | ||
| public function updateProvisioning(array $data): void { | ||
| $provisioning = $this->provisioningMapper->validate($data); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,6 +61,30 @@ public function testProvisionSkipWithoutConfigurations(): void { | |
| $this->assertEquals(0, $count); | ||
| } | ||
|
|
||
| public function testDisabledAppDoesUnprovision() { | ||
| /** @var IUser|MockObject $user */ | ||
| $user = $this->createConfiguredMock(IUser::class, [ | ||
| 'getEmailAddress' => '[email protected]', | ||
| 'getUID' => 'bruce' | ||
| ]); | ||
| $configs = [new Provisioning()]; | ||
| $this->mock->getParameter('appManager') | ||
| ->expects($this->once()) | ||
| ->method('isEnabledForUser') | ||
| ->willReturn(false); | ||
| $this->mock->getParameter('aliasMapper') | ||
| ->expects($this->once()) | ||
| ->method('deleteProvisionedAliasesByUid') | ||
| ->with($user->getUID()); | ||
| $this->mock->getParameter('mailAccountMapper') | ||
| ->expects($this->once()) | ||
| ->method('deleteProvisionedAccountsByUid') | ||
| ->with($user->getUID()); | ||
|
Comment on lines
+79
to
+82
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RE: #12226 (comment) This is sufficient for the unit test. You are asserting that the method is called correctly. Whatever happens inside that method is not subject of this unit test |
||
|
|
||
| $result = $this->manager->provisionSingleUser($configs, $user); | ||
| $this->assertFalse($result); | ||
| } | ||
|
|
||
| public function testUpdateProvisionSingleUser() { | ||
| /** @var IUser|MockObject $user */ | ||
| $user = $this->createConfiguredMock(IUser::class, [ | ||
|
|
@@ -74,6 +98,10 @@ public function testUpdateProvisionSingleUser() { | |
| $configs = [$config]; | ||
| $mailAccount = new MailAccount(); | ||
| $mailAccount->setId(1000); | ||
| $this->mock->getParameter('appManager') | ||
| ->expects($this->once()) | ||
| ->method('isEnabledForUser') | ||
| ->willReturn(true); | ||
| $this->mock->getParameter('mailAccountMapper') | ||
| ->expects($this->once()) | ||
| ->method('findProvisionedAccount') | ||
|
|
@@ -100,6 +128,10 @@ public function testProvisionSingleUser() { | |
| $config->setProvisioningDomain('batman.com'); | ||
| $config->setEmailTemplate('%USER%@batman.com'); | ||
| $configs = [$config]; | ||
| $this->mock->getParameter('appManager') | ||
| ->expects($this->once()) | ||
| ->method('isEnabledForUser') | ||
| ->willReturn(true); | ||
| $this->mock->getParameter('mailAccountMapper') | ||
| ->expects($this->once()) | ||
| ->method('findProvisionedAccount') | ||
|
|
@@ -133,6 +165,10 @@ public function testUpdateProvisionSingleUserWithWildcard() { | |
| $configs = [$config]; | ||
| $mailAccount = new MailAccount(); | ||
| $mailAccount->setId(1000); | ||
| $this->mock->getParameter('appManager') | ||
| ->expects($this->once()) | ||
| ->method('isEnabledForUser') | ||
| ->willReturn(true); | ||
| $this->mock->getParameter('mailAccountMapper') | ||
| ->expects($this->once()) | ||
| ->method('findProvisionedAccount') | ||
|
|
@@ -159,6 +195,10 @@ public function testProvisionSingleUserWithWildcard() { | |
| $config->setProvisioningDomain('*'); | ||
| $config->setEmailTemplate('%USER%@batman.com'); | ||
| $configs = [$config]; | ||
| $this->mock->getParameter('appManager') | ||
| ->expects($this->once()) | ||
| ->method('isEnabledForUser') | ||
| ->willReturn(true); | ||
| $this->mock->getParameter('mailAccountMapper') | ||
| ->expects($this->once()) | ||
| ->method('findProvisionedAccount') | ||
|
|
@@ -186,6 +226,10 @@ public function testProvisionSingleUserNoDomainMatch() { | |
| $config->setProvisioningDomain('arkham-asylum.com'); | ||
| $config->setEmailTemplate('%USER%@batman.com'); | ||
| $configs = [$config]; | ||
| $this->mock->getParameter('appManager') | ||
| ->expects($this->once()) | ||
| ->method('isEnabledForUser') | ||
| ->willReturn(true); | ||
| $this->mock->getParameter('mailAccountMapper') | ||
| ->expects($this->never()) | ||
| ->method('findProvisionedAccount'); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we're far away from NC 20/21, I think it's okay to change that here now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep