mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-11-11 02:41:33 +00:00
[Web] API: complete the OpenAPI docs, add route tests for json_api.php
This commit is contained in:
47 files changed
+18848
-7491
No files matched your search
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mailcow\Tests\Api\Modules;
|
||||
|
||||
use Mailcow\Tests\Api\ApiTestCase;
|
||||
|
||||
/** Routes identity-provider, identity-provider-test, identity-provider-sync and identity-link */
|
||||
class IdentityRoutesTest extends ApiTestCase
|
||||
{
|
||||
private const API = 'Mailcow\Identity\IdentityModule->api->';
|
||||
/** The Identity Provider settings that prerequisites.inc.php loads into $iam_settings */
|
||||
private const IAM_SETTINGS = ['iam_settings' => [
|
||||
'authsource' => 'keycloak',
|
||||
'server_url' => 'https://auth.example.org',
|
||||
'realm' => 'mailcow',
|
||||
'client_id' => 'mailcow',
|
||||
'client_secret' => 'client-secret-value',
|
||||
'bindpass' => 'bind-password',
|
||||
'access_token' => 'cached-token',
|
||||
'redirect_url_extra' => ['https://webmail.example.org'],
|
||||
'login_provisioning' => true,
|
||||
]];
|
||||
private const SETTINGS = [
|
||||
'authsource' => 'keycloak',
|
||||
'server_url' => 'https://auth.example.org',
|
||||
'realm' => 'mailcow',
|
||||
'client_id' => 'mailcow',
|
||||
'client_secret' => 'client-secret-value',
|
||||
'bindpass' => 'bind-password',
|
||||
'access_token' => 'cached-token',
|
||||
'redirect_url_extra' => ['https://webmail.example.org'],
|
||||
'login_provisioning' => true,
|
||||
];
|
||||
|
||||
public function testSettingsForReadWriteKeys(): void
|
||||
{
|
||||
$result = $this->get('get/identity-provider', ['globals' => self::IAM_SETTINGS]);
|
||||
|
||||
$this->assertSame(self::SETTINGS, $result->json());
|
||||
$this->assertNotCalled($result, self::API . 'getStatus');
|
||||
}
|
||||
|
||||
public function testReadOnlyKeysGetNoSecrets(): void
|
||||
{
|
||||
$result = $this->get('get/identity-provider', ['globals' => self::IAM_SETTINGS, 'session' => self::API_RO]);
|
||||
|
||||
$this->assertSame(array_replace(self::SETTINGS, ['client_secret' => '*', 'bindpass' => '*', 'access_token' => '*']), $result->json());
|
||||
}
|
||||
|
||||
public function testSettingsOnlyForAdmins(): void
|
||||
{
|
||||
foreach ([self::DOMAIN_ADMIN, self::USER] as $session) {
|
||||
$this->assertSame('{}', $this->get('get/identity-provider', ['globals' => self::IAM_SETTINGS, 'session' => $session])->body);
|
||||
}
|
||||
}
|
||||
|
||||
public function testOtherObjectsAnswerTheSettingsToo(): void
|
||||
{
|
||||
$result = $this->get('get/identity-provider/anything', ['globals' => self::IAM_SETTINGS]);
|
||||
|
||||
$this->assertSame(self::SETTINGS, $result->json());
|
||||
}
|
||||
|
||||
public function testSyncStatus(): void
|
||||
{
|
||||
$status = ['provider' => 'keycloak', 'can_sync' => true, 'sync_enabled' => false, 'counts' => ['accounts' => 2], 'runs' => []];
|
||||
|
||||
$result = $this->get('get/identity-provider/sync-status', ['session' => self::API_RO, 'script' => [self::answer(self::API . 'getStatus', [], $status)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'getStatus', []);
|
||||
$this->assertSame($status, $result->json());
|
||||
}
|
||||
|
||||
public function testLink(): void
|
||||
{
|
||||
$link = ['provider' => 'keycloak', 'external_id' => '8d5da28d-7541-428b-8356-a9704f73e7f4', 'idp_disabled' => 0, 'missing_since' => null];
|
||||
|
||||
$result = $this->get('get/identity-link/alice@example.org', ['script' => [self::answer(self::API . 'getLink', ['alice@example.org'], $link)]]);
|
||||
$this->assertCalled($result, self::API . 'getLink', ['alice@example.org']);
|
||||
$this->assertSame($link, $result->json());
|
||||
|
||||
$none = $this->get('get/identity-link');
|
||||
$this->assertCalled($none, self::API . 'getLink', ['']);
|
||||
$this->assertSame('{}', $none->body);
|
||||
}
|
||||
|
||||
public function testEditSettings(): void
|
||||
{
|
||||
$attr = ['authsource' => 'ldap', 'host' => 'ldap.example.org', 'basedn' => 'dc=example,dc=org', 'binddn' => 'cn=mailcow', 'bindpass' => 'secret'];
|
||||
$messages = [self::message('success', ['object_modified', ''], ['identity_provider', 'edit', ['bindpass' => '*']])];
|
||||
|
||||
$result = $this->post('edit/identity-provider', ['items' => ['identity-provider'], 'attr' => $attr],
|
||||
['script' => [self::answer('identity_provider', ['edit'], true, $messages)]]);
|
||||
|
||||
$this->assertCalled($result, 'identity_provider', ['edit', $attr]);
|
||||
$this->assertSame($messages, $result->json());
|
||||
}
|
||||
|
||||
public function testConnectionTestAnswersWithoutDetails(): void
|
||||
{
|
||||
$attr = ['authsource' => 'keycloak', 'server_url' => 'https://auth.example.org', 'realm' => 'mailcow', 'client_id' => 'mailcow', 'client_secret' => 'secret'];
|
||||
|
||||
$ok = $this->post('edit/identity-provider-test', ['attr' => $attr], ['script' => [self::answer('identity_provider', ['test'], true)]]);
|
||||
$this->assertCalled($ok, 'identity_provider', ['test', $attr]);
|
||||
$this->assertSame(['type' => 'success', 'msg' => 'Task completed'], $ok->json());
|
||||
|
||||
$failed = $this->post('edit/identity-provider-test', ['attr' => $attr], ['script' => [self::answer('identity_provider', ['test'], false)]]);
|
||||
$this->assertSame(['type' => 'error', 'msg' => 'Cannot edit item'], $failed->json());
|
||||
}
|
||||
|
||||
public function testRequestSync(): void
|
||||
{
|
||||
$result = $this->post('edit/identity-provider-sync', ['items' => ['sync'], 'attr' => ['mode' => 'preview']]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'requestRun', [['mode' => 'preview']]);
|
||||
}
|
||||
|
||||
public function testDeleteSettingsIgnoresTheBody(): void
|
||||
{
|
||||
$result = $this->post('delete/identity-provider', ['anything']);
|
||||
|
||||
$this->assertSame([['delete']], $result->callsTo('identity_provider'));
|
||||
}
|
||||
|
||||
public function testDeleteLinks(): void
|
||||
{
|
||||
$messages = [self::message('success', ['iam_link_removed', 'alice@example.org'], ['identity_provider', 'delete', 'link', ['alice@example.org']])];
|
||||
|
||||
$result = $this->post('delete/identity-link', ['alice@example.org'], ['script' => [self::answer(self::API . 'unlink', [], true, $messages)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'unlink', [['alice@example.org']]);
|
||||
$this->assertSame($messages, $result->json());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mailcow\Tests\Api\Modules;
|
||||
|
||||
use Mailcow\Tests\Api\ApiTestCase;
|
||||
|
||||
/** Routes mailbox-group: json_api.php hands them to the MailboxGroup module and passes its answers through */
|
||||
class MailboxGroupRoutesTest extends ApiTestCase
|
||||
{
|
||||
private const API = 'Mailcow\MailboxGroup\MailboxGroupModule->api->';
|
||||
|
||||
public function testAdd(): void
|
||||
{
|
||||
$attr = ['domain' => 'example.org', 'name' => 'Sales', 'members' => ['alice@example.org'], 'sender_allowed' => 1];
|
||||
$messages = [
|
||||
self::message('success', ['group_added', 'Sales'], ['mailbox_group', 'add', 'group', $attr]),
|
||||
self::message('warning', ['group_imap_grant_failed', 'shared@example.org'], ['mailbox_group', 'add', 'group', $attr]),
|
||||
];
|
||||
|
||||
$result = $this->post('add/mailbox-group', $attr, ['script' => [self::answer(self::API . 'addGroup', [], true, $messages)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'addGroup', [$attr]);
|
||||
$this->assertSame($messages, $result->json());
|
||||
}
|
||||
|
||||
public function testGetAll(): void
|
||||
{
|
||||
$groups = [['id' => 7, 'domain' => 'example.org', 'name' => 'Sales', 'can_edit' => true]];
|
||||
|
||||
$result = $this->get('get/mailbox-group/all', ['script' => [self::answer(self::API . 'getGroups', [], $groups)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'getGroups', [null]);
|
||||
$this->assertSame($groups, $result->json());
|
||||
}
|
||||
|
||||
public function testGetAllOfDomains(): void
|
||||
{
|
||||
$result = $this->get('get/mailbox-group/all/example.org,example.com');
|
||||
|
||||
$this->assertCalled($result, self::API . 'getGroups', ['example.org,example.com']);
|
||||
}
|
||||
|
||||
public function testNoGroupsAnswerAnEmptyObject(): void
|
||||
{
|
||||
$result = $this->get('get/mailbox-group/all', ['script' => [self::answer(self::API . 'getGroups', [], [])]]);
|
||||
|
||||
$this->assertSame('{}', $result->body);
|
||||
}
|
||||
|
||||
public function testGetMemberships(): void
|
||||
{
|
||||
$memberships = ['groups' => [['id' => 7, 'name' => 'Sales', 'source' => 'manual']], 'template' => 'Staff', 'send_as' => ['sales@example.org']];
|
||||
|
||||
$result = $this->get('get/mailbox-group/member/alice@example.org', ['script' => [self::answer(self::API . 'getMemberships', [], $memberships)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'getMemberships', ['alice@example.org']);
|
||||
$this->assertSame($memberships, $result->json());
|
||||
$this->assertNotCalled($result, self::API . 'getGroup');
|
||||
}
|
||||
|
||||
public function testGetMembershipsWithoutMailbox(): void
|
||||
{
|
||||
$result = $this->get('get/mailbox-group/member');
|
||||
|
||||
$this->assertCalled($result, self::API . 'getMemberships', ['']);
|
||||
$this->assertSame('{}', $result->body);
|
||||
}
|
||||
|
||||
public function testGetOne(): void
|
||||
{
|
||||
$group = ['id' => 7, 'domain' => 'example.org', 'name' => 'Sales'];
|
||||
|
||||
$result = $this->get('get/mailbox-group/7', ['script' => [self::answer(self::API . 'getGroup', ['7'], $group)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'getGroup', ['7']);
|
||||
$this->assertSame($group, $result->json());
|
||||
}
|
||||
|
||||
public function testEdit(): void
|
||||
{
|
||||
$attr = ['add_members' => ['carol@example.org'], 'remove_members' => 'bob@example.org'];
|
||||
|
||||
$result = $this->post('edit/mailbox-group', ['items' => [7, '8'], 'attr' => $attr]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'editGroups', [[7, '8'], $attr]);
|
||||
}
|
||||
|
||||
public function testEditWithASingleIdOrWithoutItems(): void
|
||||
{
|
||||
$single = $this->post('edit/mailbox-group', ['items' => 7, 'attr' => ['active' => 0]]);
|
||||
$this->assertCalled($single, self::API . 'editGroups', [[7], ['active' => 0]]);
|
||||
|
||||
$none = $this->post('edit/mailbox-group', ['attr' => ['id' => [9], 'active' => 0]]);
|
||||
$this->assertCalled($none, self::API . 'editGroups', [[], ['id' => [9], 'active' => 0]]);
|
||||
}
|
||||
|
||||
public function testDelete(): void
|
||||
{
|
||||
$messages = [
|
||||
self::message('success', ['group_removed', 'Sales'], ['mailbox_group', 'delete', 'group', ['id' => [7, 'x']]]),
|
||||
self::message('danger', 'access_denied', ['mailbox_group', 'delete', 'group', ['id' => [7, 'x']]]),
|
||||
];
|
||||
|
||||
$result = $this->post('delete/mailbox-group', [7, 'x'], ['script' => [self::answer(self::API . 'deleteGroups', [], true, $messages)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'deleteGroups', [[7, 'x']]);
|
||||
$this->assertSame($messages, $result->json());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mailcow\Tests\Api\Modules;
|
||||
|
||||
use Mailcow\Tests\Api\ApiTestCase;
|
||||
|
||||
/** Routes syncjob, syncjobs, syncjob_source, syncjob_options and imapsync_settings: handed to the Imapsync module */
|
||||
class SyncJobRoutesTest extends ApiTestCase
|
||||
{
|
||||
private const API = 'Mailcow\Imapsync\ImapsyncModule->api->';
|
||||
|
||||
public function testAddJob(): void
|
||||
{
|
||||
$attr = ['username' => 'alice@example.org', 'source_id' => 3, 'user1' => 'alice@example.com', 'password1' => 'secret', 'mins_interval' => 20];
|
||||
$messages = [self::message('success', ['mailbox_modified', 'alice@example.org'], ['syncjob', 'add', 'job', ['password1' => '*']])];
|
||||
|
||||
$result = $this->post('add/syncjob', $attr, ['script' => [self::answer(self::API . 'addJob', [], true, $messages)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'addJob', [$attr]);
|
||||
$this->assertSame($messages, $result->json());
|
||||
}
|
||||
|
||||
public function testAddSource(): void
|
||||
{
|
||||
$attr = ['name' => 'Old provider', 'host' => 'imap.example.com', 'port' => 993, 'enc' => 'SSL', 'scope' => 'domain', 'domains' => ['example.org']];
|
||||
|
||||
$result = $this->post('add/syncjob_source', $attr);
|
||||
|
||||
$this->assertCalled($result, self::API . 'addSource', [$attr]);
|
||||
}
|
||||
|
||||
public function testAllJobsOfAnApiKey(): void
|
||||
{
|
||||
$jobs = [['id' => 12, 'user2' => 'alice@example.org', 'log' => 'done']];
|
||||
|
||||
$result = $this->get('get/syncjobs/all', ['script' => [self::answer(self::API . 'getAllJobs', [], $jobs)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'getAllJobs', [null, []]);
|
||||
$this->assertNotCalled($result, 'mailbox');
|
||||
$this->assertSame($jobs, $result->json());
|
||||
}
|
||||
|
||||
public function testAllJobsWithoutLog(): void
|
||||
{
|
||||
$result = $this->get('get/syncjobs/all/no_log');
|
||||
|
||||
$this->assertCalled($result, self::API . 'getAllJobs', ['no_log', []]);
|
||||
$this->assertSame('{}', $result->body);
|
||||
}
|
||||
|
||||
public function testAllJobsOfADomainAdminAreTheJobsOfHisMailboxes(): void
|
||||
{
|
||||
$result = $this->get('get/syncjobs/all/no_log', ['session' => self::DOMAIN_ADMIN, 'script' => [
|
||||
self::answer('mailbox', ['get', 'domains'], ['example.org', 'example.com']),
|
||||
self::answer('mailbox', ['get', 'mailboxes', 'example.org'], ['alice@example.org', 'bob@example.org']),
|
||||
self::answer('mailbox', ['get', 'mailboxes', 'example.com'], false),
|
||||
]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'getAllJobs', ['no_log', ['alice@example.org', 'bob@example.org']]);
|
||||
}
|
||||
|
||||
public function testAllJobsOfAUser(): void
|
||||
{
|
||||
$result = $this->get('get/syncjobs/all', ['session' => self::USER, 'script' => [self::answer('mailbox', ['get', 'domains'], false)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'getAllJobs', [null, []]);
|
||||
}
|
||||
|
||||
public function testJobsOfAMailbox(): void
|
||||
{
|
||||
$withLog = $this->get('get/syncjobs/alice@example.org');
|
||||
$this->assertCalled($withLog, self::API . 'getJobs', ['alice@example.org', null]);
|
||||
$this->assertNotCalled($withLog, self::API . 'getAllJobs');
|
||||
|
||||
$withoutLog = $this->get('get/syncjobs/alice@example.org/no_log');
|
||||
$this->assertCalled($withoutLog, self::API . 'getJobs', ['alice@example.org', 'no_log']);
|
||||
|
||||
$own = $this->get('get/syncjobs');
|
||||
$this->assertCalled($own, self::API . 'getJobs', [null, null]);
|
||||
}
|
||||
|
||||
public function testSources(): void
|
||||
{
|
||||
$sources = [['id' => 3, 'name' => 'Old provider', 'has_client_secret' => false]];
|
||||
|
||||
$all = $this->get('get/syncjob_source/all', ['script' => [self::answer(self::API . 'getSources', [], $sources)]]);
|
||||
$this->assertCalled($all, self::API . 'getSources', []);
|
||||
$this->assertSame($sources, $all->json());
|
||||
|
||||
$one = $this->get('get/syncjob_source/3', ['script' => [self::answer(self::API . 'getSource', ['3'], $sources[0])]]);
|
||||
$this->assertCalled($one, self::API . 'getSource', ['3']);
|
||||
$this->assertSame($sources[0], $one->json());
|
||||
|
||||
$this->assertSame('{}', $this->get('get/syncjob_source/99')->body);
|
||||
}
|
||||
|
||||
public function testOptions(): void
|
||||
{
|
||||
$options = ['folder' => true, 'syncinternaldates' => false, 'sslargs1' => '^SSL_verify_mode=[01]$'];
|
||||
|
||||
$result = $this->get('get/syncjob_options', ['session' => self::USER, 'script' => [self::answer(self::API . 'getOptions', [], $options)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'getOptions', []);
|
||||
$this->assertSame($options, $result->json());
|
||||
}
|
||||
|
||||
public function testSettings(): void
|
||||
{
|
||||
$settings = ['max_parallel' => 3, 'max_bytes_per_second' => 2048000, 'max_kb_per_second' => 2000];
|
||||
|
||||
$result = $this->get('get/imapsync_settings', ['script' => [self::answer(self::API . 'getSettings', [], $settings)]]);
|
||||
$this->assertCalled($result, self::API . 'getSettings', []);
|
||||
$this->assertSame($settings, $result->json());
|
||||
|
||||
$this->assertSame('{}', $this->get('get/imapsync_settings', ['session' => self::DOMAIN_ADMIN, 'script' => [self::answer(self::API . 'getSettings', [], [])]])->body);
|
||||
}
|
||||
|
||||
public function testEditJobs(): void
|
||||
{
|
||||
$result = $this->post('edit/syncjob', ['items' => [12, '13'], 'attr' => ['active' => 0, 'mins_interval' => 60]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'editJobs', [[12, '13'], ['active' => 0, 'mins_interval' => 60]]);
|
||||
$this->assertNotCalled($result, self::API . 'runNext');
|
||||
|
||||
$other = $this->post('edit/syncjob/anything', ['items' => [12], 'attr' => ['active' => 1]]);
|
||||
$this->assertCalled($other, self::API . 'editJobs', [[12], ['active' => 1]]);
|
||||
}
|
||||
|
||||
public function testRunNext(): void
|
||||
{
|
||||
$messages = [
|
||||
self::message('danger', ['imapsync_job_running', 13], ['syncjob', 'edit', 'run_next', ['id' => [12, 13]]]),
|
||||
self::message('success', ['imapsync_run_next_done', 1], ['syncjob', 'edit', 'run_next', ['id' => [12, 13]]]),
|
||||
];
|
||||
|
||||
$result = $this->post('edit/syncjob/run_next', ['items' => [12, 13], 'attr' => ['ignored' => 1]],
|
||||
['script' => [self::answer(self::API . 'runNext', [], true, $messages)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'runNext', [[12, 13]]);
|
||||
$this->assertNotCalled($result, self::API . 'editJobs');
|
||||
$this->assertSame($messages, $result->json());
|
||||
}
|
||||
|
||||
public function testRunNextWithoutMovedJobs(): void
|
||||
{
|
||||
$result = $this->post('edit/syncjob/run_next', ['items' => [99]], ['script' => [self::answer(self::API . 'runNext', [], false)]]);
|
||||
|
||||
$this->assertSame(['type' => 'error', 'msg' => 'Cannot edit item'], $result->json());
|
||||
}
|
||||
|
||||
public function testEditSettings(): void
|
||||
{
|
||||
$result = $this->post('edit/imapsync_settings', ['items' => ['ignored'], 'attr' => ['max_parallel' => 4, 'max_kb_per_second' => 2000]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'editSettings', [['max_parallel' => 4, 'max_kb_per_second' => 2000]]);
|
||||
}
|
||||
|
||||
public function testEditSources(): void
|
||||
{
|
||||
$attr = ['description' => 'Migration finished', 'scope' => 'user', 'users' => ['alice@example.org']];
|
||||
|
||||
$result = $this->post('edit/syncjob_source', ['items' => [3], 'attr' => $attr]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'editSources', [[3], $attr]);
|
||||
$this->assertNotCalled($result, self::API . 'refreshSourceToken');
|
||||
}
|
||||
|
||||
public function testRefreshToken(): void
|
||||
{
|
||||
$result = $this->post('edit/syncjob_source/refresh_token', ['items' => [5, 6], 'attr' => []]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'refreshSourceToken', [[5, 6]]);
|
||||
$this->assertNotCalled($result, self::API . 'editSources');
|
||||
}
|
||||
|
||||
public function testDeleteJobs(): void
|
||||
{
|
||||
$result = $this->post('delete/syncjob', [12, '13']);
|
||||
|
||||
$this->assertCalled($result, self::API . 'deleteJobs', [[12, '13']]);
|
||||
}
|
||||
|
||||
public function testDeleteSources(): void
|
||||
{
|
||||
$messages = [self::message('danger', ['imapsync_source_in_use', 'Old provider'], ['syncjob', 'delete', 'source', ['id' => [3]]])];
|
||||
|
||||
$result = $this->post('delete/syncjob_source', [3], ['script' => [self::answer(self::API . 'deleteSources', [], true, $messages)]]);
|
||||
|
||||
$this->assertCalled($result, self::API . 'deleteSources', [[3]]);
|
||||
$this->assertSame($messages, $result->json());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user