ce7cb702218693d08ea6d83464d2457c9250027c
This commit is contained in:
commit
34c5f8963c
1 changed files with 145336 additions and 0 deletions
93
services/mail-keys/tests/setup.ts
Normal file
93
services/mail-keys/tests/setup.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
|
||||
type Fields = Record<string, unknown>;
|
||||
type Method = [string, Fields, string];
|
||||
type Result = { methodResponses: Method[] };
|
||||
|
||||
export async function bootstrap(runtime: string, store: Fields, fixture: { issuer: string; origins: string[]; management: number }): Promise<void> {
|
||||
const token = (await readFile(`${runtime}/bootstrap.pat`, 'utf8')).trim();
|
||||
async function idp(path: string, body?: Fields, method = 'POST'): Promise<Fields> {
|
||||
const response = await fetch(`${fixture.issuer}${path}`, {
|
||||
method, headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
const value = await response.json() as Fields;
|
||||
if (!response.ok) throw new Error(`${path}: ${JSON.stringify(value)}`);
|
||||
return value;
|
||||
}
|
||||
async function mail(method: string, args: Fields): Promise<Fields> {
|
||||
const response = await fetch(`http://localhost:${fixture.management}/jmap/`, {
|
||||
method: 'POST', headers: { Authorization: `Basic ${Buffer.from('synthetic-admin:synthetic-admin').toString('base64')}`, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ using: ['urn:stalwart:jmap'], methodCalls: [[method, args, 't']] }),
|
||||
});
|
||||
const value = await response.json() as Result;
|
||||
const result = value.methodResponses[0];
|
||||
if (result[0] === 'error' || result[1].notCreated || result[1].notUpdated) throw new Error(`${method}: ${JSON.stringify(value)}`);
|
||||
return result[1];
|
||||
}
|
||||
async function create(name: string, value: Fields): Promise<string> {
|
||||
const result = await mail(`x:${name}/set`, { create: { test: value } });
|
||||
return (result.created as Record<string, { id: string }>).test.id;
|
||||
}
|
||||
async function singleton(name: string, value: Fields): Promise<void> {
|
||||
await mail(`x:${name}/set`, { update: { singleton: value } });
|
||||
}
|
||||
const project = await idp('/management/v1/projects', { name: 'Mail keys', projectRoleAssertion: true, projectRoleCheck: true, hasProjectCheck: true });
|
||||
const projectId = String(project.id);
|
||||
await idp(`/management/v1/projects/${projectId}/roles`, { roleKey: 'mail_user', displayName: 'Mail user', group: 'Mail' });
|
||||
const app = await idp(`/management/v1/projects/${projectId}/apps/oidc`, {
|
||||
name: 'Mail keys', redirectUris: fixture.origins.map(origin => `${origin}/account/oauth/callback`),
|
||||
responseTypes: ['OIDC_RESPONSE_TYPE_CODE'], grantTypes: ['OIDC_GRANT_TYPE_AUTHORIZATION_CODE'],
|
||||
appType: 'OIDC_APP_TYPE_USER_AGENT', authMethodType: 'OIDC_AUTH_METHOD_TYPE_NONE',
|
||||
postLogoutRedirectUris: fixture.origins.map(origin => `${origin}/account/oauth/logout`),
|
||||
version: 'OIDC_VERSION_1_0', devMode: true, accessTokenType: 'OIDC_TOKEN_TYPE_BEARER',
|
||||
idTokenUserinfoAssertion: true,
|
||||
});
|
||||
const users: Record<string, { id: string; password: string }> = {};
|
||||
for (const name of ['alice', 'bob']) {
|
||||
const password = `Synthetic-${name}-42!`;
|
||||
const user = await idp('/management/v1/users/human', {
|
||||
userName: `${name}@example.test`, profile: { firstName: name, lastName: 'Synthetic', preferredLanguage: 'en' },
|
||||
email: { email: `${name}@example.test`, isEmailVerified: true },
|
||||
initialPassword: password,
|
||||
});
|
||||
const id = String(user.userId);
|
||||
await idp(`/management/v1/users/${id}/password`, { password, noChangeRequired: true });
|
||||
await idp(`/management/v1/users/${id}/grants`, { projectId, roleKeys: ['mail_user'] });
|
||||
users[name] = { id, password };
|
||||
}
|
||||
const domain = await create('Domain', { name: 'example.test', certificateManagement: { '@type': 'Manual' }, dkimManagement: { '@type': 'Manual' }, dnsManagement: { '@type': 'Manual' } });
|
||||
const authDomain = await create('Domain', { name: 'auth.example.test', certificateManagement: { '@type': 'Manual' }, dkimManagement: { '@type': 'Manual' }, dnsManagement: { '@type': 'Manual' } });
|
||||
const protocolPerms = ['authenticate', 'emailSend', 'emailReceive', 'imapAuthenticate', 'imapAppend', 'imapCapability', 'imapId',
|
||||
'imapCopy', 'imapMove', 'imapCreate', 'imapDelete', 'imapEnable', 'imapExpunge', 'imapFetch', 'imapIdle',
|
||||
'imapList', 'imapLsub', 'imapNamespace', 'imapRename', 'imapSearch', 'imapSort', 'imapSelect', 'imapExamine',
|
||||
'imapStatus', 'imapStore', 'imapSubscribe', 'imapThread'];
|
||||
const permissions = Object.fromEntries([...protocolPerms, 'authenticateWithAlias', 'sysAppPasswordGet', 'sysAppPasswordCreate', 'sysAppPasswordUpdate', 'sysAppPasswordDestroy', 'sysAppPasswordQuery'].map(name => [name, true]));
|
||||
for (const [name, user] of Object.entries(users)) {
|
||||
await create('Account', { '@type': 'User', name, domainId: domain,
|
||||
aliases: { '0': { name: user.id, domainId: authDomain } }, credentials: {},
|
||||
roles: { '@type': 'User' }, permissions: { '@type': 'Replace', enabledPermissions: permissions, disabledPermissions: {} },
|
||||
memberGroupIds: {}, encryptionAtRest: { '@type': 'Disabled' }, quotas: {},
|
||||
});
|
||||
}
|
||||
const directory = await create('Directory', { '@type': 'Oidc', description: 'Synthetic Zitadel',
|
||||
issuerUrl: fixture.issuer, requireAudience: projectId, requireScopes: {},
|
||||
claimUsername: 'sub', usernameDomain: 'auth.example.test', claimName: 'sub', claimGroups: null,
|
||||
});
|
||||
await singleton('Authentication', { directoryId: directory });
|
||||
await singleton('DataStore', store);
|
||||
await singleton('SearchStore', store);
|
||||
await singleton('BlobStore', { '@type': 'Default' });
|
||||
await singleton('Http', { useXForwarded: true });
|
||||
await singleton('SystemSettings', { defaultHostname: 'mail.example.test', defaultDomainId: domain });
|
||||
await create('Certificate', { certificate: { '@type': 'File', filePath: '/fixture/tls.crt' },
|
||||
privateKey: { '@type': 'File', filePath: '/fixture/tls.key' } });
|
||||
await create('NetworkListener', { name: 'http', bind: { '0.0.0.0:18882': true }, protocol: 'http', useTls: false, tlsImplicit: false });
|
||||
await create('NetworkListener', { name: 'imap', bind: { '0.0.0.0:18143': true }, protocol: 'imap', useTls: true, tlsImplicit: true });
|
||||
await create('NetworkListener', { name: 'submission', bind: { '0.0.0.0:18587': true }, protocol: 'smtp', useTls: true, tlsImplicit: true });
|
||||
await create('NetworkListener', { name: 'account', bind: { '127.0.0.1:8081': true }, protocol: 'http', useTls: false, tlsImplicit: false });
|
||||
await create('Tracer', { '@type': 'Stdout', buffered: false, ansi: false, multiline: false, enable: true, level: 'info', events: {}, eventsPolicy: 'exclude' });
|
||||
await writeFile(`${runtime}/config.json`, JSON.stringify({ issuer: fixture.issuer, clientId: app.clientId, scope: `openid profile email urn:zitadel:iam:org:project:id:${projectId}:aud` }));
|
||||
await writeFile(`${runtime}/users.json`, JSON.stringify(users));
|
||||
console.log('Synthetic Zitadel users and least-privilege Stalwart accounts configured.');
|
||||
}
|
||||
Loading…
Reference in a new issue