emailengine-app 2.64.0 → 2.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/test.yml +4 -0
- package/CHANGELOG.md +14 -0
- package/copy-static-files.sh +1 -1
- package/data/google-crawlers.json +1 -1
- package/lib/account.js +7 -7
- package/lib/api-routes/account-routes.js +7 -1
- package/lib/email-client/outlook/graph-api.js +2 -2
- package/lib/email-client/outlook-client.js +43 -10
- package/lib/export.js +17 -0
- package/lib/imapproxy/imap-server.js +2 -2
- package/lib/oauth/outlook.js +99 -1
- package/lib/oauth2-apps.js +66 -12
- package/lib/outbox.js +1 -1
- package/lib/routes-ui.js +7 -147
- package/lib/schemas.js +171 -11
- package/lib/ui-routes/account-routes.js +6 -1
- package/lib/ui-routes/oauth-routes.js +18 -172
- package/package.json +19 -19
- package/sbom.json +1 -1
- package/static/licenses.html +23 -83
- package/translations/messages.pot +71 -71
- package/views/config/oauth/edit.hbs +2 -0
- package/views/config/oauth/index.hbs +2 -1
- package/views/config/oauth/new.hbs +2 -0
- package/views/partials/oauth_form.hbs +179 -4
- package/views/partials/scope_info.hbs +10 -0
- package/workers/export.js +6 -2
- package/workers/imap.js +3 -2
package/lib/routes-ui.js
CHANGED
|
@@ -37,7 +37,7 @@ const { Account } = require('./account');
|
|
|
37
37
|
const { Gateway } = require('./gateway');
|
|
38
38
|
const { redis, submitQueue, notifyQueue, documentsQueue } = require('./db');
|
|
39
39
|
const psl = require('psl');
|
|
40
|
-
const { oauth2Apps,
|
|
40
|
+
const { oauth2Apps, OAUTH_PROVIDERS, oauth2ProviderData, SERVICE_ACCOUNT_PROVIDERS } = require('./oauth2-apps');
|
|
41
41
|
const { autodetectImapSettings } = require('./autodetect-imap-settings');
|
|
42
42
|
const getSecret = require('./get-secret');
|
|
43
43
|
const os = require('os');
|
|
@@ -45,10 +45,9 @@ const {
|
|
|
45
45
|
ADDRESS_STRATEGIES,
|
|
46
46
|
settingsSchema,
|
|
47
47
|
oauthCreateSchema,
|
|
48
|
+
oauthUpdateSchema,
|
|
48
49
|
accountIdSchema,
|
|
49
50
|
defaultAccountTypeSchema,
|
|
50
|
-
googleProjectIdSchema,
|
|
51
|
-
googleWorkspaceAccountsSchema,
|
|
52
51
|
exportIdSchema
|
|
53
52
|
} = require('./schemas');
|
|
54
53
|
const fs = require('fs');
|
|
@@ -357,150 +356,6 @@ const OKTA_OAUTH2_CLIENT_ID = readEnvValue('OKTA_OAUTH2_CLIENT_ID');
|
|
|
357
356
|
const OKTA_OAUTH2_CLIENT_SECRET = readEnvValue('OKTA_OAUTH2_CLIENT_SECRET');
|
|
358
357
|
const USE_OKTA_AUTH = !!(OKTA_OAUTH2_ISSUER && OKTA_OAUTH2_CLIENT_ID && OKTA_OAUTH2_CLIENT_SECRET);
|
|
359
358
|
|
|
360
|
-
const oauthUpdateSchema = {
|
|
361
|
-
app: Joi.string().empty('').max(255).example('gmail').label('Provider').required(),
|
|
362
|
-
|
|
363
|
-
provider: Joi.string()
|
|
364
|
-
.trim()
|
|
365
|
-
.empty('')
|
|
366
|
-
.max(256)
|
|
367
|
-
.valid(...Object.keys(OAUTH_PROVIDERS))
|
|
368
|
-
.example('gmail')
|
|
369
|
-
.required()
|
|
370
|
-
.description('OAuth2 provider'),
|
|
371
|
-
|
|
372
|
-
name: Joi.string()
|
|
373
|
-
.trim()
|
|
374
|
-
.empty('')
|
|
375
|
-
.max(256)
|
|
376
|
-
.example('My Gmail App')
|
|
377
|
-
.description('Application name')
|
|
378
|
-
.when('app', {
|
|
379
|
-
not: Joi.string().valid(...LEGACY_KEYS),
|
|
380
|
-
then: Joi.required(),
|
|
381
|
-
otherwise: Joi.optional().valid(false, null)
|
|
382
|
-
}),
|
|
383
|
-
description: Joi.string().trim().allow('').max(1024).example('My cool app').description('Application description'),
|
|
384
|
-
|
|
385
|
-
title: Joi.string().allow('').trim().max(256).example('App title').description('Title for the application button'),
|
|
386
|
-
|
|
387
|
-
enabled: Joi.boolean().truthy('Y', 'true', '1', 'on').falsy('N', 'false', 0, '').default(false).description('Enable this app'),
|
|
388
|
-
|
|
389
|
-
clientId: Joi.string()
|
|
390
|
-
.trim()
|
|
391
|
-
.allow('')
|
|
392
|
-
.max(256)
|
|
393
|
-
.when('provider', {
|
|
394
|
-
not: 'gmailService',
|
|
395
|
-
then: Joi.required(),
|
|
396
|
-
otherwise: Joi.optional().valid(false, null)
|
|
397
|
-
})
|
|
398
|
-
.description('OAuth2 Client ID'),
|
|
399
|
-
|
|
400
|
-
clientSecret: Joi.string()
|
|
401
|
-
.trim()
|
|
402
|
-
.empty('', false, null)
|
|
403
|
-
.max(256)
|
|
404
|
-
.when('provider', {
|
|
405
|
-
not: 'gmailService',
|
|
406
|
-
then: Joi.optional(),
|
|
407
|
-
otherwise: Joi.forbidden()
|
|
408
|
-
})
|
|
409
|
-
.description('OAuth2 Client Secret'),
|
|
410
|
-
|
|
411
|
-
pubSubApp: Joi.string()
|
|
412
|
-
.empty('')
|
|
413
|
-
.base64({ paddingRequired: false, urlSafe: true })
|
|
414
|
-
.max(512)
|
|
415
|
-
.example('AAAAAQAACnA')
|
|
416
|
-
.description('Cloud Pub/Sub app for Gmail API webhooks'),
|
|
417
|
-
|
|
418
|
-
extraScopes: Joi.string()
|
|
419
|
-
.allow('')
|
|
420
|
-
.trim()
|
|
421
|
-
.max(10 * 1024)
|
|
422
|
-
.description('OAuth2 Extra Scopes'),
|
|
423
|
-
|
|
424
|
-
skipScopes: Joi.string()
|
|
425
|
-
.allow('')
|
|
426
|
-
.trim()
|
|
427
|
-
.max(10 * 1024)
|
|
428
|
-
.description('OAuth2 scopes to skip from the base set'),
|
|
429
|
-
|
|
430
|
-
serviceClient: Joi.string()
|
|
431
|
-
.trim()
|
|
432
|
-
.allow('')
|
|
433
|
-
.max(256)
|
|
434
|
-
.when('provider', {
|
|
435
|
-
is: 'gmailService',
|
|
436
|
-
then: Joi.required(),
|
|
437
|
-
otherwise: Joi.optional().valid(false, null)
|
|
438
|
-
})
|
|
439
|
-
.description('OAuth2 Service Client ID'),
|
|
440
|
-
|
|
441
|
-
googleProjectId: googleProjectIdSchema,
|
|
442
|
-
|
|
443
|
-
googleWorkspaceAccounts: googleWorkspaceAccountsSchema.when('provider', {
|
|
444
|
-
is: 'gmail',
|
|
445
|
-
then: Joi.optional().default(false)
|
|
446
|
-
}),
|
|
447
|
-
|
|
448
|
-
serviceClientEmail: Joi.string()
|
|
449
|
-
.trim()
|
|
450
|
-
.allow('')
|
|
451
|
-
.email()
|
|
452
|
-
.when('provider', {
|
|
453
|
-
is: 'gmailService',
|
|
454
|
-
then: Joi.required(),
|
|
455
|
-
otherwise: Joi.optional().valid(false, null)
|
|
456
|
-
})
|
|
457
|
-
.example('name@project-123.iam.gserviceaccount.com')
|
|
458
|
-
.description('Service Client Email for 2-legged OAuth2 applications'),
|
|
459
|
-
|
|
460
|
-
serviceKey: Joi.string()
|
|
461
|
-
.trim()
|
|
462
|
-
.empty('', false, null)
|
|
463
|
-
.max(100 * 1024)
|
|
464
|
-
.when('provider', {
|
|
465
|
-
is: 'gmailService',
|
|
466
|
-
then: Joi.optional(),
|
|
467
|
-
otherwise: Joi.forbidden()
|
|
468
|
-
})
|
|
469
|
-
.description('OAuth2 Secret Service Key'),
|
|
470
|
-
|
|
471
|
-
authority: Joi.string()
|
|
472
|
-
.trim()
|
|
473
|
-
.empty('')
|
|
474
|
-
.max(1024)
|
|
475
|
-
.when('provider', {
|
|
476
|
-
is: 'outlook',
|
|
477
|
-
then: Joi.required(),
|
|
478
|
-
otherwise: Joi.optional().valid(false, null)
|
|
479
|
-
})
|
|
480
|
-
.example(false)
|
|
481
|
-
.label('SupportedAccountTypes'),
|
|
482
|
-
|
|
483
|
-
cloud: Joi.string()
|
|
484
|
-
.trim()
|
|
485
|
-
.empty('')
|
|
486
|
-
.valid('global', 'gcc-high', 'dod', 'china')
|
|
487
|
-
.example('global')
|
|
488
|
-
.description('Azure cloud type for Outlook OAuth2 applications')
|
|
489
|
-
.label('AzureCloud'),
|
|
490
|
-
|
|
491
|
-
tenant: Joi.string().trim().empty('').max(1024).example('f8cdef31-a31e-4b4a-93e4-5f571e91255a').label('Directorytenant'),
|
|
492
|
-
|
|
493
|
-
redirectUrl: Joi.string()
|
|
494
|
-
.allow('')
|
|
495
|
-
.uri({ scheme: ['http', 'https'], allowRelative: false })
|
|
496
|
-
.when('provider', {
|
|
497
|
-
not: 'gmailService',
|
|
498
|
-
then: Joi.required(),
|
|
499
|
-
otherwise: Joi.optional().valid(false, null)
|
|
500
|
-
})
|
|
501
|
-
.description('OAuth2 Callback URL')
|
|
502
|
-
};
|
|
503
|
-
|
|
504
359
|
function formatAccountData(account, gt) {
|
|
505
360
|
account.type = {};
|
|
506
361
|
|
|
@@ -4761,6 +4616,11 @@ ${Buffer.from(data.content, 'base64url').toString('base64')}
|
|
|
4761
4616
|
requestPayload.email = accountData.email;
|
|
4762
4617
|
}
|
|
4763
4618
|
|
|
4619
|
+
// Service providers use client_credentials - no interactive authorization
|
|
4620
|
+
if (SERVICE_ACCOUNT_PROVIDERS.has(oAuth2Client.provider)) {
|
|
4621
|
+
throw Boom.badRequest('Application-only OAuth providers do not support interactive authorization');
|
|
4622
|
+
}
|
|
4623
|
+
|
|
4764
4624
|
let authorizeUrl = oAuth2Client.generateAuthUrl(requestPayload);
|
|
4765
4625
|
|
|
4766
4626
|
return h.redirect(authorizeUrl);
|
package/lib/schemas.js
CHANGED
|
@@ -4,6 +4,7 @@ const Joi = require('joi');
|
|
|
4
4
|
const config = require('@zone-eu/wild-config');
|
|
5
5
|
const { getByteSize } = require('./tools');
|
|
6
6
|
const { locales } = require('./translations');
|
|
7
|
+
const { LEGACY_KEYS, OAUTH_PROVIDERS } = require('./oauth2-apps');
|
|
7
8
|
|
|
8
9
|
const RESYNC_DELAY = 15 * 60;
|
|
9
10
|
|
|
@@ -22,13 +23,6 @@ const ADDRESS_STRATEGIES = [
|
|
|
22
23
|
{ key: 'random', title: 'Random' }
|
|
23
24
|
];
|
|
24
25
|
|
|
25
|
-
const OAUTH_PROVIDERS = {
|
|
26
|
-
gmail: 'Gmail',
|
|
27
|
-
gmailService: 'Gmail Service Accounts',
|
|
28
|
-
outlook: 'Outlook',
|
|
29
|
-
mailRu: 'Mail.ru'
|
|
30
|
-
};
|
|
31
|
-
|
|
32
26
|
const accountIdSchema = Joi.string().empty('').trim().max(256).example('user123').description('Unique identifier for the email account');
|
|
33
27
|
|
|
34
28
|
// Allowed configuration keys
|
|
@@ -1529,8 +1523,18 @@ const oauthCreateSchema = {
|
|
|
1529
1523
|
.empty('')
|
|
1530
1524
|
.max(1024)
|
|
1531
1525
|
.when('provider', {
|
|
1532
|
-
|
|
1533
|
-
|
|
1526
|
+
switch: [
|
|
1527
|
+
{
|
|
1528
|
+
is: 'outlookService',
|
|
1529
|
+
then: Joi.string().required().invalid('common', 'organizations', 'consumers').messages({
|
|
1530
|
+
'any.invalid': 'Client credentials flow requires a specific tenant ID; "{{#value}}" is not allowed'
|
|
1531
|
+
})
|
|
1532
|
+
},
|
|
1533
|
+
{
|
|
1534
|
+
is: 'outlook',
|
|
1535
|
+
then: Joi.required()
|
|
1536
|
+
}
|
|
1537
|
+
],
|
|
1534
1538
|
otherwise: Joi.optional().valid(false, null)
|
|
1535
1539
|
})
|
|
1536
1540
|
.example('common')
|
|
@@ -1541,7 +1545,7 @@ const oauthCreateSchema = {
|
|
|
1541
1545
|
.trim()
|
|
1542
1546
|
.empty('')
|
|
1543
1547
|
.when('provider', {
|
|
1544
|
-
is: 'outlook',
|
|
1548
|
+
is: Joi.valid('outlook', 'outlookService'),
|
|
1545
1549
|
then: Joi.valid('global', 'gcc-high', 'dod', 'china').default('global'),
|
|
1546
1550
|
otherwise: Joi.optional().valid(false, null)
|
|
1547
1551
|
})
|
|
@@ -1565,7 +1569,7 @@ const oauthCreateSchema = {
|
|
|
1565
1569
|
.allow('')
|
|
1566
1570
|
.uri({ scheme: ['http', 'https'], allowRelative: false })
|
|
1567
1571
|
.when('provider', {
|
|
1568
|
-
not: 'gmailService',
|
|
1572
|
+
not: Joi.valid('gmailService', 'outlookService'),
|
|
1569
1573
|
then: Joi.required(),
|
|
1570
1574
|
otherwise: Joi.optional().valid(false, null)
|
|
1571
1575
|
})
|
|
@@ -1574,6 +1578,160 @@ const oauthCreateSchema = {
|
|
|
1574
1578
|
.label('OAuth2AppRedirectUrl')
|
|
1575
1579
|
};
|
|
1576
1580
|
|
|
1581
|
+
const oauthUpdateSchema = {
|
|
1582
|
+
app: Joi.string().empty('').max(255).example('gmail').label('Provider').required(),
|
|
1583
|
+
|
|
1584
|
+
provider: Joi.string()
|
|
1585
|
+
.trim()
|
|
1586
|
+
.empty('')
|
|
1587
|
+
.max(256)
|
|
1588
|
+
.valid(...Object.keys(OAUTH_PROVIDERS))
|
|
1589
|
+
.example('gmail')
|
|
1590
|
+
.required()
|
|
1591
|
+
.description('OAuth2 provider'),
|
|
1592
|
+
|
|
1593
|
+
name: Joi.string()
|
|
1594
|
+
.trim()
|
|
1595
|
+
.empty('')
|
|
1596
|
+
.max(256)
|
|
1597
|
+
.example('My Gmail App')
|
|
1598
|
+
.description('Application name')
|
|
1599
|
+
.when('app', {
|
|
1600
|
+
not: Joi.string().valid(...LEGACY_KEYS),
|
|
1601
|
+
then: Joi.required(),
|
|
1602
|
+
otherwise: Joi.optional().valid(false, null)
|
|
1603
|
+
}),
|
|
1604
|
+
description: Joi.string().trim().allow('').max(1024).example('My cool app').description('Application description'),
|
|
1605
|
+
|
|
1606
|
+
title: Joi.string().allow('').trim().max(256).example('App title').description('Title for the application button'),
|
|
1607
|
+
|
|
1608
|
+
enabled: Joi.boolean().truthy('Y', 'true', '1', 'on').falsy('N', 'false', 0, '').default(false).description('Enable this app'),
|
|
1609
|
+
|
|
1610
|
+
clientId: Joi.string()
|
|
1611
|
+
.trim()
|
|
1612
|
+
.allow('')
|
|
1613
|
+
.max(256)
|
|
1614
|
+
.when('provider', {
|
|
1615
|
+
not: 'gmailService',
|
|
1616
|
+
then: Joi.required(),
|
|
1617
|
+
otherwise: Joi.optional().valid(false, null)
|
|
1618
|
+
})
|
|
1619
|
+
.description('OAuth2 Client ID'),
|
|
1620
|
+
|
|
1621
|
+
clientSecret: Joi.string()
|
|
1622
|
+
.trim()
|
|
1623
|
+
.empty('', false, null)
|
|
1624
|
+
.max(256)
|
|
1625
|
+
.when('provider', {
|
|
1626
|
+
not: 'gmailService',
|
|
1627
|
+
then: Joi.optional(),
|
|
1628
|
+
otherwise: Joi.forbidden()
|
|
1629
|
+
})
|
|
1630
|
+
.description('OAuth2 Client Secret'),
|
|
1631
|
+
|
|
1632
|
+
pubSubApp: Joi.string()
|
|
1633
|
+
.empty('')
|
|
1634
|
+
.base64({ paddingRequired: false, urlSafe: true })
|
|
1635
|
+
.max(512)
|
|
1636
|
+
.example('AAAAAQAACnA')
|
|
1637
|
+
.description('Cloud Pub/Sub app for Gmail API webhooks'),
|
|
1638
|
+
|
|
1639
|
+
extraScopes: Joi.string()
|
|
1640
|
+
.allow('')
|
|
1641
|
+
.trim()
|
|
1642
|
+
.max(10 * 1024)
|
|
1643
|
+
.description('OAuth2 Extra Scopes'),
|
|
1644
|
+
|
|
1645
|
+
skipScopes: Joi.string()
|
|
1646
|
+
.allow('')
|
|
1647
|
+
.trim()
|
|
1648
|
+
.max(10 * 1024)
|
|
1649
|
+
.description('OAuth2 scopes to skip from the base set'),
|
|
1650
|
+
|
|
1651
|
+
serviceClient: Joi.string()
|
|
1652
|
+
.trim()
|
|
1653
|
+
.allow('')
|
|
1654
|
+
.max(256)
|
|
1655
|
+
.when('provider', {
|
|
1656
|
+
is: 'gmailService',
|
|
1657
|
+
then: Joi.required(),
|
|
1658
|
+
otherwise: Joi.optional().valid(false, null)
|
|
1659
|
+
})
|
|
1660
|
+
.description('OAuth2 Service Client ID'),
|
|
1661
|
+
|
|
1662
|
+
googleProjectId: googleProjectIdSchema,
|
|
1663
|
+
|
|
1664
|
+
googleWorkspaceAccounts: googleWorkspaceAccountsSchema.when('provider', {
|
|
1665
|
+
is: 'gmail',
|
|
1666
|
+
then: Joi.optional().default(false)
|
|
1667
|
+
}),
|
|
1668
|
+
|
|
1669
|
+
serviceClientEmail: Joi.string()
|
|
1670
|
+
.trim()
|
|
1671
|
+
.allow('')
|
|
1672
|
+
.email()
|
|
1673
|
+
.when('provider', {
|
|
1674
|
+
is: 'gmailService',
|
|
1675
|
+
then: Joi.required(),
|
|
1676
|
+
otherwise: Joi.optional().valid(false, null)
|
|
1677
|
+
})
|
|
1678
|
+
.example('name@project-123.iam.gserviceaccount.com')
|
|
1679
|
+
.description('Service Client Email for 2-legged OAuth2 applications'),
|
|
1680
|
+
|
|
1681
|
+
serviceKey: Joi.string()
|
|
1682
|
+
.trim()
|
|
1683
|
+
.empty('', false, null)
|
|
1684
|
+
.max(100 * 1024)
|
|
1685
|
+
.when('provider', {
|
|
1686
|
+
is: 'gmailService',
|
|
1687
|
+
then: Joi.optional(),
|
|
1688
|
+
otherwise: Joi.forbidden()
|
|
1689
|
+
})
|
|
1690
|
+
.description('OAuth2 Secret Service Key'),
|
|
1691
|
+
|
|
1692
|
+
authority: Joi.string()
|
|
1693
|
+
.trim()
|
|
1694
|
+
.empty('')
|
|
1695
|
+
.max(1024)
|
|
1696
|
+
.when('provider', {
|
|
1697
|
+
switch: [
|
|
1698
|
+
{
|
|
1699
|
+
is: 'outlookService',
|
|
1700
|
+
then: Joi.string().required().invalid('common', 'organizations', 'consumers').messages({
|
|
1701
|
+
'any.invalid': 'Client credentials flow requires a specific tenant ID; "{{#value}}" is not allowed'
|
|
1702
|
+
})
|
|
1703
|
+
},
|
|
1704
|
+
{
|
|
1705
|
+
is: 'outlook',
|
|
1706
|
+
then: Joi.required()
|
|
1707
|
+
}
|
|
1708
|
+
],
|
|
1709
|
+
otherwise: Joi.optional().valid(false, null)
|
|
1710
|
+
})
|
|
1711
|
+
.example(false)
|
|
1712
|
+
.label('SupportedAccountTypes'),
|
|
1713
|
+
|
|
1714
|
+
cloud: Joi.string()
|
|
1715
|
+
.trim()
|
|
1716
|
+
.empty('')
|
|
1717
|
+
.valid('global', 'gcc-high', 'dod', 'china')
|
|
1718
|
+
.example('global')
|
|
1719
|
+
.description('Azure cloud type for Outlook OAuth2 applications')
|
|
1720
|
+
.label('AzureCloud'),
|
|
1721
|
+
|
|
1722
|
+
tenant: Joi.string().trim().empty('').max(1024).example('f8cdef31-a31e-4b4a-93e4-5f571e91255a').label('Directorytenant'),
|
|
1723
|
+
|
|
1724
|
+
redirectUrl: Joi.string()
|
|
1725
|
+
.allow('')
|
|
1726
|
+
.uri({ scheme: ['http', 'https'], allowRelative: false })
|
|
1727
|
+
.when('provider', {
|
|
1728
|
+
not: Joi.valid('gmailService', 'outlookService'),
|
|
1729
|
+
then: Joi.required(),
|
|
1730
|
+
otherwise: Joi.optional().valid(false, null)
|
|
1731
|
+
})
|
|
1732
|
+
.description('OAuth2 Callback URL')
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1577
1735
|
const tokenRestrictionsSchema = Joi.object({
|
|
1578
1736
|
referrers: Joi.array()
|
|
1579
1737
|
.items(Joi.string())
|
|
@@ -1822,6 +1980,7 @@ const exportStatusSchema = Joi.object({
|
|
|
1822
1980
|
progress: exportProgressSchema,
|
|
1823
1981
|
created: Joi.date().iso().example('2024-01-15T10:30:00Z').description('When export was created'),
|
|
1824
1982
|
expiresAt: Joi.date().iso().example('2024-01-16T10:30:00Z').description('When export file expires'),
|
|
1983
|
+
truncated: Joi.boolean().example(true).description('Whether the export was truncated due to message count or size limits'),
|
|
1825
1984
|
error: Joi.string().allow(null).description('Error message if export failed')
|
|
1826
1985
|
}).label('ExportStatus');
|
|
1827
1986
|
|
|
@@ -1876,6 +2035,7 @@ module.exports = {
|
|
|
1876
2035
|
searchSchema,
|
|
1877
2036
|
messageUpdateSchema,
|
|
1878
2037
|
oauthCreateSchema,
|
|
2038
|
+
oauthUpdateSchema,
|
|
1879
2039
|
tokenRestrictionsSchema,
|
|
1880
2040
|
accountIdSchema,
|
|
1881
2041
|
ipSchema,
|
|
@@ -22,7 +22,7 @@ const {
|
|
|
22
22
|
} = require('../tools');
|
|
23
23
|
const { Account } = require('../account');
|
|
24
24
|
const { Gateway } = require('../gateway');
|
|
25
|
-
const { oauth2Apps, oauth2ProviderData } = require('../oauth2-apps');
|
|
25
|
+
const { oauth2Apps, oauth2ProviderData, SERVICE_ACCOUNT_PROVIDERS } = require('../oauth2-apps');
|
|
26
26
|
const { autodetectImapSettings } = require('../autodetect-imap-settings');
|
|
27
27
|
const getSecret = require('../get-secret');
|
|
28
28
|
const capa = require('../capa');
|
|
@@ -538,6 +538,11 @@ function init(args) {
|
|
|
538
538
|
requestPayload.email = accountData.email;
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
+
// Service providers use client_credentials - no interactive authorization
|
|
542
|
+
if (SERVICE_ACCOUNT_PROVIDERS.has(oAuth2Client.provider)) {
|
|
543
|
+
throw Boom.badRequest('Application-only OAuth providers do not support interactive authorization');
|
|
544
|
+
}
|
|
545
|
+
|
|
541
546
|
let authorizeUrl = oAuth2Client.generateAuthUrl(requestPayload);
|
|
542
547
|
|
|
543
548
|
return h.redirect(authorizeUrl);
|
|
@@ -5,14 +5,22 @@ const Joi = require('joi');
|
|
|
5
5
|
|
|
6
6
|
const settings = require('../settings');
|
|
7
7
|
const { redis } = require('../db');
|
|
8
|
-
const { oauth2Apps,
|
|
8
|
+
const { oauth2Apps, OAUTH_PROVIDERS, oauth2ProviderData } = require('../oauth2-apps');
|
|
9
9
|
const getSecret = require('../get-secret');
|
|
10
10
|
const { Account } = require('../account');
|
|
11
|
-
const { oauthCreateSchema,
|
|
11
|
+
const { oauthCreateSchema, oauthUpdateSchema } = require('../schemas');
|
|
12
12
|
const consts = require('../consts');
|
|
13
13
|
|
|
14
14
|
const { DEFAULT_PAGE_SIZE } = consts;
|
|
15
15
|
|
|
16
|
+
// Microsoft Entra requires 'localhost' rather than '127.0.0.1' in redirect URIs
|
|
17
|
+
function normalizeOutlookRedirectUrl(redirectUrl, provider) {
|
|
18
|
+
if (provider === 'outlook') {
|
|
19
|
+
return redirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
20
|
+
}
|
|
21
|
+
return redirectUrl;
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
const AZURE_CLOUDS = [
|
|
17
25
|
{
|
|
18
26
|
id: 'global',
|
|
@@ -39,150 +47,6 @@ const AZURE_CLOUDS = [
|
|
|
39
47
|
}
|
|
40
48
|
];
|
|
41
49
|
|
|
42
|
-
const oauthUpdateSchema = {
|
|
43
|
-
app: Joi.string().empty('').max(255).example('gmail').label('Provider').required(),
|
|
44
|
-
|
|
45
|
-
provider: Joi.string()
|
|
46
|
-
.trim()
|
|
47
|
-
.empty('')
|
|
48
|
-
.max(256)
|
|
49
|
-
.valid(...Object.keys(OAUTH_PROVIDERS))
|
|
50
|
-
.example('gmail')
|
|
51
|
-
.required()
|
|
52
|
-
.description('OAuth2 provider'),
|
|
53
|
-
|
|
54
|
-
name: Joi.string()
|
|
55
|
-
.trim()
|
|
56
|
-
.empty('')
|
|
57
|
-
.max(256)
|
|
58
|
-
.example('My Gmail App')
|
|
59
|
-
.description('Application name')
|
|
60
|
-
.when('app', {
|
|
61
|
-
not: Joi.string().valid(...LEGACY_KEYS),
|
|
62
|
-
then: Joi.required(),
|
|
63
|
-
otherwise: Joi.optional().valid(false, null)
|
|
64
|
-
}),
|
|
65
|
-
description: Joi.string().trim().allow('').max(1024).example('My cool app').description('Application description'),
|
|
66
|
-
|
|
67
|
-
title: Joi.string().allow('').trim().max(256).example('App title').description('Title for the application button'),
|
|
68
|
-
|
|
69
|
-
enabled: Joi.boolean().truthy('Y', 'true', '1', 'on').falsy('N', 'false', 0, '').default(false).description('Enable this app'),
|
|
70
|
-
|
|
71
|
-
clientId: Joi.string()
|
|
72
|
-
.trim()
|
|
73
|
-
.allow('')
|
|
74
|
-
.max(256)
|
|
75
|
-
.when('provider', {
|
|
76
|
-
not: 'gmailService',
|
|
77
|
-
then: Joi.required(),
|
|
78
|
-
otherwise: Joi.optional().valid(false, null)
|
|
79
|
-
})
|
|
80
|
-
.description('OAuth2 Client ID'),
|
|
81
|
-
|
|
82
|
-
clientSecret: Joi.string()
|
|
83
|
-
.trim()
|
|
84
|
-
.empty('', false, null)
|
|
85
|
-
.max(256)
|
|
86
|
-
.when('provider', {
|
|
87
|
-
not: 'gmailService',
|
|
88
|
-
then: Joi.optional(),
|
|
89
|
-
otherwise: Joi.forbidden()
|
|
90
|
-
})
|
|
91
|
-
.description('OAuth2 Client Secret'),
|
|
92
|
-
|
|
93
|
-
pubSubApp: Joi.string()
|
|
94
|
-
.empty('')
|
|
95
|
-
.base64({ paddingRequired: false, urlSafe: true })
|
|
96
|
-
.max(512)
|
|
97
|
-
.example('AAAAAQAACnA')
|
|
98
|
-
.description('Cloud Pub/Sub app for Gmail API webhooks'),
|
|
99
|
-
|
|
100
|
-
extraScopes: Joi.string()
|
|
101
|
-
.allow('')
|
|
102
|
-
.trim()
|
|
103
|
-
.max(10 * 1024)
|
|
104
|
-
.description('OAuth2 Extra Scopes'),
|
|
105
|
-
|
|
106
|
-
skipScopes: Joi.string()
|
|
107
|
-
.allow('')
|
|
108
|
-
.trim()
|
|
109
|
-
.max(10 * 1024)
|
|
110
|
-
.description('OAuth2 scopes to skip from the base set'),
|
|
111
|
-
|
|
112
|
-
serviceClient: Joi.string()
|
|
113
|
-
.trim()
|
|
114
|
-
.allow('')
|
|
115
|
-
.max(256)
|
|
116
|
-
.when('provider', {
|
|
117
|
-
is: 'gmailService',
|
|
118
|
-
then: Joi.required(),
|
|
119
|
-
otherwise: Joi.optional().valid(false, null)
|
|
120
|
-
})
|
|
121
|
-
.description('OAuth2 Service Client ID'),
|
|
122
|
-
|
|
123
|
-
googleProjectId: googleProjectIdSchema,
|
|
124
|
-
|
|
125
|
-
googleWorkspaceAccounts: googleWorkspaceAccountsSchema.when('provider', {
|
|
126
|
-
is: 'gmail',
|
|
127
|
-
then: Joi.optional().default(false)
|
|
128
|
-
}),
|
|
129
|
-
|
|
130
|
-
serviceClientEmail: Joi.string()
|
|
131
|
-
.trim()
|
|
132
|
-
.allow('')
|
|
133
|
-
.email()
|
|
134
|
-
.when('provider', {
|
|
135
|
-
is: 'gmailService',
|
|
136
|
-
then: Joi.required(),
|
|
137
|
-
otherwise: Joi.optional().valid(false, null)
|
|
138
|
-
})
|
|
139
|
-
.example('name@project-123.iam.gserviceaccount.com')
|
|
140
|
-
.description('Service Client Email for 2-legged OAuth2 applications'),
|
|
141
|
-
|
|
142
|
-
serviceKey: Joi.string()
|
|
143
|
-
.trim()
|
|
144
|
-
.empty('', false, null)
|
|
145
|
-
.max(100 * 1024)
|
|
146
|
-
.when('provider', {
|
|
147
|
-
is: 'gmailService',
|
|
148
|
-
then: Joi.optional(),
|
|
149
|
-
otherwise: Joi.forbidden()
|
|
150
|
-
})
|
|
151
|
-
.description('OAuth2 Secret Service Key'),
|
|
152
|
-
|
|
153
|
-
authority: Joi.string()
|
|
154
|
-
.trim()
|
|
155
|
-
.empty('')
|
|
156
|
-
.max(1024)
|
|
157
|
-
.when('provider', {
|
|
158
|
-
is: 'outlook',
|
|
159
|
-
then: Joi.required(),
|
|
160
|
-
otherwise: Joi.optional().valid(false, null)
|
|
161
|
-
})
|
|
162
|
-
.example(false)
|
|
163
|
-
.label('SupportedAccountTypes'),
|
|
164
|
-
|
|
165
|
-
cloud: Joi.string()
|
|
166
|
-
.trim()
|
|
167
|
-
.empty('')
|
|
168
|
-
.valid('global', 'gcc-high', 'dod', 'china')
|
|
169
|
-
.example('global')
|
|
170
|
-
.description('Azure cloud type for Outlook OAuth2 applications')
|
|
171
|
-
.label('AzureCloud'),
|
|
172
|
-
|
|
173
|
-
tenant: Joi.string().trim().empty('').max(1024).example('f8cdef31-a31e-4b4a-93e4-5f571e91255a').label('Directorytenant'),
|
|
174
|
-
|
|
175
|
-
redirectUrl: Joi.string()
|
|
176
|
-
.allow('')
|
|
177
|
-
.uri({ scheme: ['http', 'https'], allowRelative: false })
|
|
178
|
-
.when('provider', {
|
|
179
|
-
not: 'gmailService',
|
|
180
|
-
then: Joi.required(),
|
|
181
|
-
otherwise: Joi.optional().valid(false, null)
|
|
182
|
-
})
|
|
183
|
-
.description('OAuth2 Callback URL')
|
|
184
|
-
};
|
|
185
|
-
|
|
186
50
|
function init({ server, call }) {
|
|
187
51
|
// GET /admin/config/oauth - OAuth applications list
|
|
188
52
|
server.route({
|
|
@@ -448,10 +312,7 @@ function init({ server, call }) {
|
|
|
448
312
|
let providerData = oauth2ProviderData(provider);
|
|
449
313
|
|
|
450
314
|
let serviceUrl = await settings.get('serviceUrl');
|
|
451
|
-
let defaultRedirectUrl = `${serviceUrl}/oauth
|
|
452
|
-
if (provider === 'outlook') {
|
|
453
|
-
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
454
|
-
}
|
|
315
|
+
let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, provider);
|
|
455
316
|
|
|
456
317
|
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
457
318
|
|
|
@@ -483,10 +344,10 @@ function init({ server, call }) {
|
|
|
483
344
|
|
|
484
345
|
values: {
|
|
485
346
|
provider,
|
|
486
|
-
redirectUrl: defaultRedirectUrl
|
|
347
|
+
...(provider !== 'outlookService' ? { redirectUrl: defaultRedirectUrl } : {})
|
|
487
348
|
},
|
|
488
349
|
|
|
489
|
-
authorityCommon:
|
|
350
|
+
authorityCommon: provider !== 'outlookService'
|
|
490
351
|
},
|
|
491
352
|
{
|
|
492
353
|
layout: 'app'
|
|
@@ -562,10 +423,7 @@ function init({ server, call }) {
|
|
|
562
423
|
let providerData = oauth2ProviderData(provider);
|
|
563
424
|
|
|
564
425
|
let serviceUrl = await settings.get('serviceUrl');
|
|
565
|
-
let defaultRedirectUrl = `${serviceUrl}/oauth
|
|
566
|
-
if (provider === 'outlook') {
|
|
567
|
-
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
568
|
-
}
|
|
426
|
+
let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, provider);
|
|
569
427
|
|
|
570
428
|
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
571
429
|
|
|
@@ -642,10 +500,7 @@ function init({ server, call }) {
|
|
|
642
500
|
let providerData = oauth2ProviderData(provider);
|
|
643
501
|
|
|
644
502
|
let serviceUrl = await settings.get('serviceUrl');
|
|
645
|
-
let defaultRedirectUrl = `${serviceUrl}/oauth
|
|
646
|
-
if (provider === 'outlook') {
|
|
647
|
-
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
648
|
-
}
|
|
503
|
+
let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, provider);
|
|
649
504
|
|
|
650
505
|
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
651
506
|
|
|
@@ -714,10 +569,7 @@ function init({ server, call }) {
|
|
|
714
569
|
|
|
715
570
|
let providerData = oauth2ProviderData(appData.provider, appData.cloud);
|
|
716
571
|
let serviceUrl = await settings.get('serviceUrl');
|
|
717
|
-
let defaultRedirectUrl = `${serviceUrl}/oauth
|
|
718
|
-
if (providerData.provider === 'outlook') {
|
|
719
|
-
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
720
|
-
}
|
|
572
|
+
let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, providerData.provider);
|
|
721
573
|
|
|
722
574
|
let values = Object.assign({}, appData, {
|
|
723
575
|
clientSecret: '',
|
|
@@ -843,10 +695,7 @@ function init({ server, call }) {
|
|
|
843
695
|
let providerData = oauth2ProviderData(appData.provider, appData.cloud);
|
|
844
696
|
|
|
845
697
|
let serviceUrl = await settings.get('serviceUrl');
|
|
846
|
-
let defaultRedirectUrl = `${serviceUrl}/oauth
|
|
847
|
-
if (appData.provider === 'outlook') {
|
|
848
|
-
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
849
|
-
}
|
|
698
|
+
let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, appData.provider);
|
|
850
699
|
|
|
851
700
|
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
852
701
|
|
|
@@ -932,10 +781,7 @@ function init({ server, call }) {
|
|
|
932
781
|
let providerData = oauth2ProviderData(provider);
|
|
933
782
|
|
|
934
783
|
let serviceUrl = await settings.get('serviceUrl');
|
|
935
|
-
let defaultRedirectUrl = `${serviceUrl}/oauth
|
|
936
|
-
if (provider === 'outlook') {
|
|
937
|
-
defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
|
|
938
|
-
}
|
|
784
|
+
let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, provider);
|
|
939
785
|
|
|
940
786
|
let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
|
|
941
787
|
|