fix(server): test & schema
This commit is contained in:
@@ -469,6 +469,13 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The account id for the cloudflare r2 storage provider."
|
"description": "The account id for the cloudflare r2 storage provider."
|
||||||
},
|
},
|
||||||
|
"jurisdiction": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"eu"
|
||||||
|
],
|
||||||
|
"description": "Optional jurisdiction for the cloudflare r2 endpoint. Set to \"eu\" for EU buckets."
|
||||||
|
},
|
||||||
"usePresignedURL": {
|
"usePresignedURL": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The presigned url config for the cloudflare r2 storage provider.",
|
"description": "The presigned url config for the cloudflare r2 storage provider.",
|
||||||
@@ -486,13 +493,6 @@
|
|||||||
"description": "The presigned key for the cloudflare r2 storage provider."
|
"description": "The presigned key for the cloudflare r2 storage provider."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"jurisdiction": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"eu"
|
|
||||||
],
|
|
||||||
"description": "Optional jurisdiction for the cloudflare r2 endpoint. Set to \"eu\" for EU buckets."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -667,6 +667,13 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The account id for the cloudflare r2 storage provider."
|
"description": "The account id for the cloudflare r2 storage provider."
|
||||||
},
|
},
|
||||||
|
"jurisdiction": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"eu"
|
||||||
|
],
|
||||||
|
"description": "Optional jurisdiction for the cloudflare r2 endpoint. Set to \"eu\" for EU buckets."
|
||||||
|
},
|
||||||
"usePresignedURL": {
|
"usePresignedURL": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The presigned url config for the cloudflare r2 storage provider.",
|
"description": "The presigned url config for the cloudflare r2 storage provider.",
|
||||||
@@ -684,13 +691,6 @@
|
|||||||
"description": "The presigned key for the cloudflare r2 storage provider."
|
"description": "The presigned key for the cloudflare r2 storage provider."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"jurisdiction": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"eu"
|
|
||||||
],
|
|
||||||
"description": "Optional jurisdiction for the cloudflare r2 endpoint. Set to \"eu\" for EU buckets."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -861,11 +861,14 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"google": {
|
"google": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Google Calendar integration config\n@default {\"enabled\":false,\"clientId\":\"\",\"clientSecret\":\"\",\"externalWebhookUrl\":\"\",\"webhookVerificationToken\":\"\",\"requestTimeoutMs\":10000}\n@link https://developers.google.com/calendar/api/guides/push",
|
"description": "Google Calendar integration config\n@default {\"enabled\":false,\"allowNewAccounts\":true,\"clientId\":\"\",\"clientSecret\":\"\",\"externalWebhookUrl\":\"\",\"webhookVerificationToken\":\"\",\"requestTimeoutMs\":10000}\n@link https://developers.google.com/calendar/api/guides/push",
|
||||||
"properties": {
|
"properties": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"allowNewAccounts": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"clientId": {
|
"clientId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -884,6 +887,7 @@
|
|||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
"allowNewAccounts": true,
|
||||||
"clientId": "",
|
"clientId": "",
|
||||||
"clientSecret": "",
|
"clientSecret": "",
|
||||||
"externalWebhookUrl": "",
|
"externalWebhookUrl": "",
|
||||||
@@ -1296,6 +1300,13 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The account id for the cloudflare r2 storage provider."
|
"description": "The account id for the cloudflare r2 storage provider."
|
||||||
},
|
},
|
||||||
|
"jurisdiction": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"eu"
|
||||||
|
],
|
||||||
|
"description": "Optional jurisdiction for the cloudflare r2 endpoint. Set to \"eu\" for EU buckets."
|
||||||
|
},
|
||||||
"usePresignedURL": {
|
"usePresignedURL": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The presigned url config for the cloudflare r2 storage provider.",
|
"description": "The presigned url config for the cloudflare r2 storage provider.",
|
||||||
@@ -1313,13 +1324,6 @@
|
|||||||
"description": "The presigned key for the cloudflare r2 storage provider."
|
"description": "The presigned key for the cloudflare r2 storage provider."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"jurisdiction": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"eu"
|
|
||||||
],
|
|
||||||
"description": "Optional jurisdiction for the cloudflare r2 endpoint. Set to \"eu\" for EU buckets."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,47 +209,77 @@ test('listAccounts includes calendars count', async t => {
|
|||||||
t.is(counts.get(accountB.id), 1);
|
t.is(counts.get(accountB.id), 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('assertCanLinkProvider blocks new google calendar accounts when disabled', async t => {
|
test.serial(
|
||||||
config.calendar.google.allowNewAccounts = false;
|
'assertCanLinkProvider blocks new google calendar accounts when disabled',
|
||||||
const user = await module.create(Mockers.User);
|
async t => {
|
||||||
|
config.calendar.google.allowNewAccounts = false;
|
||||||
|
const user = await module.create(Mockers.User);
|
||||||
|
|
||||||
const error = await t.throwsAsync(
|
const error = await t.throwsAsync(
|
||||||
calendarService.assertCanLinkProvider(user.id, CalendarProviderName.Google)
|
calendarService.assertCanLinkProvider(
|
||||||
);
|
user.id,
|
||||||
t.true(error instanceof GraphqlBadRequest);
|
CalendarProviderName.Google
|
||||||
t.is(
|
)
|
||||||
(error as GraphqlBadRequest).data?.code,
|
);
|
||||||
'calendar_provider_link_disabled'
|
t.true(error instanceof GraphqlBadRequest);
|
||||||
);
|
t.is(
|
||||||
});
|
(error as GraphqlBadRequest).data?.code,
|
||||||
|
'calendar_provider_link_disabled'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
test('assertCanLinkProvider allows users with an existing google calendar account', async t => {
|
test.serial(
|
||||||
config.calendar.google.allowNewAccounts = false;
|
'assertCanLinkProvider allows users with an existing google calendar account',
|
||||||
const user = await module.create(Mockers.User);
|
async t => {
|
||||||
await createAccount(user.id);
|
config.calendar.google.allowNewAccounts = false;
|
||||||
|
const user = await module.create(Mockers.User);
|
||||||
|
await createAccount(user.id);
|
||||||
|
|
||||||
await t.notThrowsAsync(
|
await t.notThrowsAsync(
|
||||||
calendarService.assertCanLinkProvider(user.id, CalendarProviderName.Google)
|
calendarService.assertCanLinkProvider(
|
||||||
);
|
user.id,
|
||||||
});
|
CalendarProviderName.Google
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
test('handleOAuthCallback does not persist new google account when linking is disabled', async t => {
|
test.serial(
|
||||||
config.calendar.google.allowNewAccounts = false;
|
'handleOAuthCallback does not persist new google account when linking is disabled',
|
||||||
const provider = new MockCalendarProvider();
|
async t => {
|
||||||
providerFactory.register(provider);
|
config.calendar.google.allowNewAccounts = false;
|
||||||
const user = await module.create(Mockers.User);
|
const provider = new MockCalendarProvider();
|
||||||
|
mock.method(providerFactory, 'get', () => provider);
|
||||||
|
const user = await module.create(Mockers.User);
|
||||||
|
|
||||||
const error = await t.throwsAsync(
|
const error = await t.throwsAsync(
|
||||||
calendarService.handleOAuthCallback({
|
calendarService.handleOAuthCallback({
|
||||||
provider: CalendarProviderName.Google,
|
provider: CalendarProviderName.Google,
|
||||||
code: 'code',
|
code: 'code',
|
||||||
redirectUri: 'https://example.com/callback',
|
redirectUri: 'https://example.com/callback',
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
t.true(error instanceof GraphqlBadRequest);
|
t.true(error instanceof GraphqlBadRequest);
|
||||||
t.is((await models.calendarAccount.listByUser(user.id)).length, 0);
|
t.is((await models.calendarAccount.listByUser(user.id)).length, 0);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.serial(
|
||||||
|
'canLinkProvider returns false for new google calendar accounts when disabled',
|
||||||
|
async t => {
|
||||||
|
config.calendar.google.allowNewAccounts = false;
|
||||||
|
const user = await module.create(Mockers.User);
|
||||||
|
|
||||||
|
t.false(
|
||||||
|
await calendarService.canLinkProvider(
|
||||||
|
user.id,
|
||||||
|
CalendarProviderName.Google
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
test('syncSubscription resets invalid sync token and maps events', async t => {
|
test('syncSubscription resets invalid sync token and maps events', async t => {
|
||||||
const user = await module.create(Mockers.User);
|
const user = await module.create(Mockers.User);
|
||||||
|
|||||||
@@ -33,17 +33,20 @@ import {
|
|||||||
export class CalendarServerConfigResolver {
|
export class CalendarServerConfigResolver {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly providerFactory: CalendarProviderFactory,
|
private readonly providerFactory: CalendarProviderFactory,
|
||||||
private readonly config: Config
|
private readonly config: Config,
|
||||||
|
private readonly calendar: CalendarService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ResolveField(() => [CalendarProviderName])
|
@ResolveField(() => [CalendarProviderName])
|
||||||
calendarProviders() {
|
async calendarProviders(@CurrentUser() user?: CurrentUser) {
|
||||||
return this.providerFactory.providers.filter(provider => {
|
const providers = [];
|
||||||
return (
|
for (const provider of this.providerFactory.providers) {
|
||||||
provider !== CalendarProviderName.Google ||
|
if (!(await this.calendar.canLinkProvider(user?.id, provider))) {
|
||||||
this.config.calendar.google.allowNewAccounts !== false
|
continue;
|
||||||
);
|
}
|
||||||
});
|
providers.push(provider);
|
||||||
|
}
|
||||||
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResolveField(() => [CalendarCalDAVProviderPresetObjectType])
|
@ResolveField(() => [CalendarCalDAVProviderPresetObjectType])
|
||||||
|
|||||||
@@ -571,18 +571,28 @@ export class CalendarService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async assertCanLinkProvider(userId: string, provider: CalendarProviderName) {
|
async assertCanLinkProvider(userId: string, provider: CalendarProviderName) {
|
||||||
if (this.canCreateNewAccounts(provider)) {
|
if (await this.canLinkProvider(userId, provider)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const accounts = await this.models.calendarAccount.listByUser(userId);
|
|
||||||
if (accounts.some(account => account.provider === provider)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw this.providerLinkDisabledError(provider);
|
throw this.providerLinkDisabledError(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async canLinkProvider(
|
||||||
|
userId: string | null | undefined,
|
||||||
|
provider: CalendarProviderName
|
||||||
|
) {
|
||||||
|
if (this.canCreateNewAccounts(provider)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!userId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accounts = await this.models.calendarAccount.listByUser(userId);
|
||||||
|
return accounts.some(account => account.provider === provider);
|
||||||
|
}
|
||||||
|
|
||||||
getAuthUrl(
|
getAuthUrl(
|
||||||
provider: CalendarProviderName,
|
provider: CalendarProviderName,
|
||||||
state: string,
|
state: string,
|
||||||
|
|||||||
Reference in New Issue
Block a user