emailengine-app 2.63.4 → 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.
Files changed (59) hide show
  1. package/.github/workflows/test.yml +4 -0
  2. package/CHANGELOG.md +70 -0
  3. package/copy-static-files.sh +1 -1
  4. package/data/google-crawlers.json +1 -1
  5. package/eslint.config.js +2 -0
  6. package/lib/account.js +13 -9
  7. package/lib/api-routes/account-routes.js +7 -1
  8. package/lib/consts.js +17 -1
  9. package/lib/email-client/gmail/gmail-api.js +1 -12
  10. package/lib/email-client/imap-client.js +5 -3
  11. package/lib/email-client/outlook/graph-api.js +9 -15
  12. package/lib/email-client/outlook-client.js +406 -177
  13. package/lib/export.js +17 -0
  14. package/lib/imapproxy/imap-server.js +3 -2
  15. package/lib/oauth/gmail.js +12 -1
  16. package/lib/oauth/outlook.js +99 -1
  17. package/lib/oauth/pubsub/google.js +253 -85
  18. package/lib/oauth2-apps.js +620 -389
  19. package/lib/outbox.js +1 -1
  20. package/lib/routes-ui.js +193 -238
  21. package/lib/schemas.js +189 -12
  22. package/lib/ui-routes/account-routes.js +7 -2
  23. package/lib/ui-routes/admin-entities-routes.js +3 -3
  24. package/lib/ui-routes/oauth-routes.js +27 -175
  25. package/package.json +21 -21
  26. package/sbom.json +1 -1
  27. package/server.js +54 -22
  28. package/static/licenses.html +30 -90
  29. package/translations/de.mo +0 -0
  30. package/translations/de.po +54 -42
  31. package/translations/en.mo +0 -0
  32. package/translations/en.po +55 -43
  33. package/translations/et.mo +0 -0
  34. package/translations/et.po +54 -42
  35. package/translations/fr.mo +0 -0
  36. package/translations/fr.po +54 -42
  37. package/translations/ja.mo +0 -0
  38. package/translations/ja.po +54 -42
  39. package/translations/messages.pot +93 -71
  40. package/translations/nl.mo +0 -0
  41. package/translations/nl.po +54 -42
  42. package/translations/pl.mo +0 -0
  43. package/translations/pl.po +54 -42
  44. package/views/config/oauth/app.hbs +12 -0
  45. package/views/config/oauth/edit.hbs +2 -0
  46. package/views/config/oauth/index.hbs +4 -1
  47. package/views/config/oauth/new.hbs +2 -0
  48. package/views/config/oauth/subscriptions.hbs +175 -0
  49. package/views/error.hbs +4 -4
  50. package/views/partials/oauth_form.hbs +179 -4
  51. package/views/partials/oauth_tabs.hbs +8 -0
  52. package/views/partials/scope_info.hbs +10 -0
  53. package/workers/api.js +174 -96
  54. package/workers/documents.js +1 -0
  55. package/workers/export.js +6 -2
  56. package/workers/imap.js +33 -49
  57. package/workers/smtp.js +1 -0
  58. package/workers/submit.js +1 -0
  59. package/workers/webhooks.js +42 -30
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
@@ -450,6 +444,14 @@ const settingsSchema = {
450
444
  queueKeep: Joi.number().integer().empty('').min(0).description('Number of completed and failed queue entries to retain for debugging'),
451
445
  deliveryAttempts: Joi.number().integer().empty('').min(0).description('Maximum number of delivery attempts before marking a message as permanently failed'),
452
446
 
447
+ gmailSubscriptionTtl: Joi.number()
448
+ .integer()
449
+ .empty('')
450
+ .min(0)
451
+ .max(365)
452
+ .description('Gmail Pub/Sub subscription inactivity expiration in days. Empty for Google default (31 days), 0 for no expiration.')
453
+ .label('GmailSubscriptionTTL'),
454
+
453
455
  /* ────────────── Templates ────────────── */
454
456
 
455
457
  templateHeader: Joi.string()
@@ -1159,6 +1161,13 @@ const lastErrorSchema = Joi.object({
1159
1161
  .label('OAuthTokenRequestError')
1160
1162
  }).label('AccountErrorEntry');
1161
1163
 
1164
+ const pubSubErrorSchema = Joi.object({
1165
+ message: Joi.string().example('Failed to process subscription loop').description('Error message'),
1166
+ description: Joi.string().allow(null).example('Subscription not found').description('Error details')
1167
+ })
1168
+ .description('Pub/Sub subscription error, if any')
1169
+ .label('PubSubError');
1170
+
1162
1171
  const templateSchemas = {
1163
1172
  subject: Joi.string()
1164
1173
  .allow('')
@@ -1350,6 +1359,7 @@ const googleProjectIdSchema = Joi.string()
1350
1359
  .trim()
1351
1360
  .allow('', false, null)
1352
1361
  .max(256)
1362
+ .pattern(/^[a-z][a-z0-9-]{4,28}[a-z0-9]$/)
1353
1363
  .example('project-name-425411')
1354
1364
  .description('Google Cloud Project ID')
1355
1365
  .label('GoogleProjectId');
@@ -1513,8 +1523,18 @@ const oauthCreateSchema = {
1513
1523
  .empty('')
1514
1524
  .max(1024)
1515
1525
  .when('provider', {
1516
- is: 'outlook',
1517
- then: Joi.required(),
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
+ ],
1518
1538
  otherwise: Joi.optional().valid(false, null)
1519
1539
  })
1520
1540
  .example('common')
@@ -1525,7 +1545,7 @@ const oauthCreateSchema = {
1525
1545
  .trim()
1526
1546
  .empty('')
1527
1547
  .when('provider', {
1528
- is: 'outlook',
1548
+ is: Joi.valid('outlook', 'outlookService'),
1529
1549
  then: Joi.valid('global', 'gcc-high', 'dod', 'china').default('global'),
1530
1550
  otherwise: Joi.optional().valid(false, null)
1531
1551
  })
@@ -1549,7 +1569,7 @@ const oauthCreateSchema = {
1549
1569
  .allow('')
1550
1570
  .uri({ scheme: ['http', 'https'], allowRelative: false })
1551
1571
  .when('provider', {
1552
- not: 'gmailService',
1572
+ not: Joi.valid('gmailService', 'outlookService'),
1553
1573
  then: Joi.required(),
1554
1574
  otherwise: Joi.optional().valid(false, null)
1555
1575
  })
@@ -1558,6 +1578,160 @@ const oauthCreateSchema = {
1558
1578
  .label('OAuth2AppRedirectUrl')
1559
1579
  };
1560
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
+
1561
1735
  const tokenRestrictionsSchema = Joi.object({
1562
1736
  referrers: Joi.array()
1563
1737
  .items(Joi.string())
@@ -1806,6 +1980,7 @@ const exportStatusSchema = Joi.object({
1806
1980
  progress: exportProgressSchema,
1807
1981
  created: Joi.date().iso().example('2024-01-15T10:30:00Z').description('When export was created'),
1808
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'),
1809
1984
  error: Joi.string().allow(null).description('Error message if export failed')
1810
1985
  }).label('ExportStatus');
1811
1986
 
@@ -1860,6 +2035,7 @@ module.exports = {
1860
2035
  searchSchema,
1861
2036
  messageUpdateSchema,
1862
2037
  oauthCreateSchema,
2038
+ oauthUpdateSchema,
1863
2039
  tokenRestrictionsSchema,
1864
2040
  accountIdSchema,
1865
2041
  ipSchema,
@@ -1880,7 +2056,8 @@ module.exports = {
1880
2056
  exportStatusSchema,
1881
2057
  exportListSchema,
1882
2058
  exportProgressSchema,
1883
- exportIdSchema
2059
+ exportIdSchema,
2060
+ pubSubErrorSchema
1884
2061
  };
1885
2062
 
1886
2063
  /*
@@ -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);
@@ -1352,7 +1357,7 @@ function init(args) {
1352
1357
 
1353
1358
  async failAction(request, h, err) {
1354
1359
  await request.flash({ type: 'danger', message: `Couldn't delete account. Try again.` });
1355
- request.logger.error({ msg: 'Failed to delete delete the account', err });
1360
+ request.logger.error({ msg: 'Failed to delete the account', err });
1356
1361
 
1357
1362
  return h.redirect('/admin/accounts').takeover();
1358
1363
  },
@@ -766,7 +766,7 @@ return payload;`)
766
766
 
767
767
  async failAction(request, h, err) {
768
768
  await request.flash({ type: 'danger', message: `Couldn't delete webhook. Try again.` });
769
- request.logger.error({ msg: 'Failed to delete delete Webhook Route', err });
769
+ request.logger.error({ msg: 'Failed to delete Webhook Route', err });
770
770
 
771
771
  return h.redirect('/admin/webhooks').takeover();
772
772
  },
@@ -1415,7 +1415,7 @@ return payload;`)
1415
1415
 
1416
1416
  async failAction(request, h, err) {
1417
1417
  await request.flash({ type: 'danger', message: `Couldn't delete account. Try again.` });
1418
- request.logger.error({ msg: 'Failed to delete delete the account', err });
1418
+ request.logger.error({ msg: 'Failed to delete the account', err });
1419
1419
 
1420
1420
  return h.redirect('/admin/templates').takeover();
1421
1421
  },
@@ -2122,7 +2122,7 @@ return payload;`)
2122
2122
 
2123
2123
  async failAction(request, h, err) {
2124
2124
  await request.flash({ type: 'danger', message: `Couldn't delete gateway. Try again.` });
2125
- request.logger.error({ msg: 'Failed to delete delete the gateway', err });
2125
+ request.logger.error({ msg: 'Failed to delete the gateway', err });
2126
2126
 
2127
2127
  return h.redirect('/admin/gateways').takeover();
2128
2128
  },
@@ -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, LEGACY_KEYS, OAUTH_PROVIDERS, oauth2ProviderData } = require('../oauth2-apps');
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, googleProjectIdSchema, googleWorkspaceAccountsSchema } = require('../schemas');
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({
@@ -402,6 +266,12 @@ function init({ server, call }) {
402
266
  try {
403
267
  await oauth2Apps.del(request.payload.app);
404
268
 
269
+ try {
270
+ await call({ cmd: 'googlePubSubRemove', app: request.payload.app });
271
+ } catch (err) {
272
+ request.logger.error({ msg: 'Failed to notify workers about OAuth2 app deletion', err, app: request.payload.app });
273
+ }
274
+
405
275
  await request.flash({ type: 'info', message: `OAuth2 app deleted` });
406
276
 
407
277
  return h.redirect('/admin/config/oauth');
@@ -421,7 +291,7 @@ function init({ server, call }) {
421
291
 
422
292
  async failAction(request, h, err) {
423
293
  await request.flash({ type: 'danger', message: `Couldn't delete OAuth2 app. Try again.` });
424
- request.logger.error({ msg: 'Failed to delete delete the OAuth2 application', err });
294
+ request.logger.error({ msg: 'Failed to delete the OAuth2 application', err });
425
295
 
426
296
  return h.redirect('/admin/config/oauth').takeover();
427
297
  },
@@ -442,10 +312,7 @@ function init({ server, call }) {
442
312
  let providerData = oauth2ProviderData(provider);
443
313
 
444
314
  let serviceUrl = await settings.get('serviceUrl');
445
- let defaultRedirectUrl = `${serviceUrl}/oauth`;
446
- if (provider === 'outlook') {
447
- defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
448
- }
315
+ let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, provider);
449
316
 
450
317
  let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
451
318
 
@@ -477,10 +344,10 @@ function init({ server, call }) {
477
344
 
478
345
  values: {
479
346
  provider,
480
- redirectUrl: defaultRedirectUrl
347
+ ...(provider !== 'outlookService' ? { redirectUrl: defaultRedirectUrl } : {})
481
348
  },
482
349
 
483
- authorityCommon: true
350
+ authorityCommon: provider !== 'outlookService'
484
351
  },
485
352
  {
486
353
  layout: 'app'
@@ -538,7 +405,7 @@ function init({ server, call }) {
538
405
  throw new Error('Unexpected result');
539
406
  }
540
407
 
541
- if (oauth2App && oauth2App.pubsubUpdates && oauth2App.pubsubUpdates.pubSubSubscription) {
408
+ if (oauth2App && oauth2App.pubsubUpdates && Object.keys(oauth2App.pubsubUpdates).length > 0) {
542
409
  await call({ cmd: 'googlePubSub', app: oauth2App.id });
543
410
  }
544
411
 
@@ -556,10 +423,7 @@ function init({ server, call }) {
556
423
  let providerData = oauth2ProviderData(provider);
557
424
 
558
425
  let serviceUrl = await settings.get('serviceUrl');
559
- let defaultRedirectUrl = `${serviceUrl}/oauth`;
560
- if (provider === 'outlook') {
561
- defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
562
- }
426
+ let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, provider);
563
427
 
564
428
  let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
565
429
 
@@ -636,10 +500,7 @@ function init({ server, call }) {
636
500
  let providerData = oauth2ProviderData(provider);
637
501
 
638
502
  let serviceUrl = await settings.get('serviceUrl');
639
- let defaultRedirectUrl = `${serviceUrl}/oauth`;
640
- if (provider === 'outlook') {
641
- defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
642
- }
503
+ let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, provider);
643
504
 
644
505
  let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
645
506
 
@@ -708,10 +569,7 @@ function init({ server, call }) {
708
569
 
709
570
  let providerData = oauth2ProviderData(appData.provider, appData.cloud);
710
571
  let serviceUrl = await settings.get('serviceUrl');
711
- let defaultRedirectUrl = `${serviceUrl}/oauth`;
712
- if (providerData.provider === 'outlook') {
713
- defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
714
- }
572
+ let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, providerData.provider);
715
573
 
716
574
  let values = Object.assign({}, appData, {
717
575
  clientSecret: '',
@@ -824,7 +682,7 @@ function init({ server, call }) {
824
682
  throw new Error('Unexpected result');
825
683
  }
826
684
 
827
- if (oauth2App && oauth2App.pubsubUpdates && oauth2App.pubsubUpdates.pubSubSubscription) {
685
+ if (oauth2App && oauth2App.pubsubUpdates && Object.keys(oauth2App.pubsubUpdates).length > 0) {
828
686
  await call({ cmd: 'googlePubSub', app: oauth2App.id });
829
687
  }
830
688
 
@@ -837,10 +695,7 @@ function init({ server, call }) {
837
695
  let providerData = oauth2ProviderData(appData.provider, appData.cloud);
838
696
 
839
697
  let serviceUrl = await settings.get('serviceUrl');
840
- let defaultRedirectUrl = `${serviceUrl}/oauth`;
841
- if (appData.provider === 'outlook') {
842
- defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
843
- }
698
+ let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, appData.provider);
844
699
 
845
700
  let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
846
701
 
@@ -926,10 +781,7 @@ function init({ server, call }) {
926
781
  let providerData = oauth2ProviderData(provider);
927
782
 
928
783
  let serviceUrl = await settings.get('serviceUrl');
929
- let defaultRedirectUrl = `${serviceUrl}/oauth`;
930
- if (provider === 'outlook') {
931
- defaultRedirectUrl = defaultRedirectUrl.replace(/^http:\/\/127\.0\.0\.1\b/i, 'http://localhost');
932
- }
784
+ let defaultRedirectUrl = normalizeOutlookRedirectUrl(`${serviceUrl}/oauth`, provider);
933
785
 
934
786
  let pubSubApps = await oauth2Apps.list(0, 1000, { pubsub: true });
935
787