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
|
@@ -1,3 +1,77 @@
|
|
|
1
|
+
{{#if activeOutlook}}
|
|
2
|
+
<div class="card border-left-info shadow mb-4">
|
|
3
|
+
<div class="card-body">
|
|
4
|
+
<div class="row no-gutters align-items-center">
|
|
5
|
+
<div class="col mr-2">
|
|
6
|
+
<strong>Delegated</strong> access means EmailEngine acts on behalf of a signed-in user. Each
|
|
7
|
+
mailbox owner must complete an interactive OAuth2 login. Use this when users manage their own
|
|
8
|
+
accounts or when you have access to the mailbox credentials.
|
|
9
|
+
{{#if providerData.tutorialUrl}}
|
|
10
|
+
Read about setting up {{providerData.comment}} OAuth2 project from <a
|
|
11
|
+
href="{{providerData.tutorialUrl}}" target="_blank" rel="noopener noreferrer"
|
|
12
|
+
referrerpolicy="no-referrer">here</a>.
|
|
13
|
+
{{/if}}
|
|
14
|
+
</div>
|
|
15
|
+
<div class="col-auto">
|
|
16
|
+
<i class="fas fa-info-circle fa-2x text-gray-300"></i>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
{{/if}}
|
|
22
|
+
|
|
23
|
+
{{#if activeGmailService}}
|
|
24
|
+
<div class="card border-left-info shadow mb-4">
|
|
25
|
+
<div class="card-body">
|
|
26
|
+
<div class="row no-gutters align-items-center">
|
|
27
|
+
<div class="col mr-2">
|
|
28
|
+
<strong>Service accounts</strong> allow EmailEngine to access Gmail mailboxes using a Google Cloud
|
|
29
|
+
service key, with no interactive user login required. The service account must have domain-wide
|
|
30
|
+
delegation enabled in Google Workspace. Use this for automated integrations where you cannot
|
|
31
|
+
perform interactive logins.
|
|
32
|
+
Accounts using service access can only be added via the
|
|
33
|
+
<a href="/admin/swagger#/Account/postV1Account">REST API</a>,
|
|
34
|
+
not through the hosted authentication form.
|
|
35
|
+
{{#if providerData.tutorialUrl}}
|
|
36
|
+
Read about setting up {{providerData.comment}} from <a
|
|
37
|
+
href="{{providerData.tutorialUrl}}" target="_blank" rel="noopener noreferrer"
|
|
38
|
+
referrerpolicy="no-referrer">here</a>.
|
|
39
|
+
{{/if}}
|
|
40
|
+
</div>
|
|
41
|
+
<div class="col-auto">
|
|
42
|
+
<i class="fas fa-info-circle fa-2x text-gray-300"></i>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
{{/if}}
|
|
48
|
+
|
|
49
|
+
{{#if activeOutlookService}}
|
|
50
|
+
<div class="card border-left-info shadow mb-4">
|
|
51
|
+
<div class="card-body">
|
|
52
|
+
<div class="row no-gutters align-items-center">
|
|
53
|
+
<div class="col mr-2">
|
|
54
|
+
<strong>Application</strong> access means EmailEngine authenticates as the app itself using
|
|
55
|
+
client credentials, with no interactive user login required. The Entra app registration must have
|
|
56
|
+
<strong>application permissions</strong> (not delegated) with admin consent. Use this for
|
|
57
|
+
service integrations, shared mailboxes, or when you cannot perform interactive logins.
|
|
58
|
+
Accounts using application access can only be added via the
|
|
59
|
+
<a href="/admin/swagger#/Account/postV1Account">REST API</a>,
|
|
60
|
+
not through the hosted authentication form.
|
|
61
|
+
{{#if providerData.tutorialUrl}}
|
|
62
|
+
Read about setting up {{providerData.comment}} from <a
|
|
63
|
+
href="{{providerData.tutorialUrl}}" target="_blank" rel="noopener noreferrer"
|
|
64
|
+
referrerpolicy="no-referrer">here</a>.
|
|
65
|
+
{{/if}}
|
|
66
|
+
</div>
|
|
67
|
+
<div class="col-auto">
|
|
68
|
+
<i class="fas fa-info-circle fa-2x text-gray-300"></i>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
{{/if}}
|
|
74
|
+
|
|
1
75
|
<div class="card mb-4">
|
|
2
76
|
|
|
3
77
|
<div class="card-body">
|
|
@@ -42,7 +116,7 @@
|
|
|
42
116
|
<small class="form-text text-muted">Optional application description or a comment.</small>
|
|
43
117
|
</div>
|
|
44
118
|
|
|
45
|
-
{{#unless activeGmailService}}
|
|
119
|
+
{{#unless activeGmailService}}{{#unless activeOutlookService}}
|
|
46
120
|
<div class="form-group">
|
|
47
121
|
|
|
48
122
|
<label for="title">
|
|
@@ -63,7 +137,7 @@
|
|
|
63
137
|
<small class="form-text text-muted">Optional display title next to the application button on the account
|
|
64
138
|
type selection page.</small>
|
|
65
139
|
</div>
|
|
66
|
-
{{/unless}}
|
|
140
|
+
{{/unless}}{{/unless}}
|
|
67
141
|
|
|
68
142
|
|
|
69
143
|
{{#if activeGmail}}
|
|
@@ -82,7 +156,7 @@
|
|
|
82
156
|
</div>
|
|
83
157
|
{{/if}}
|
|
84
158
|
|
|
85
|
-
{{#unless activeGmailService}}
|
|
159
|
+
{{#unless activeGmailService}}{{#unless activeOutlookService}}
|
|
86
160
|
<div class="form-group form-check">
|
|
87
161
|
|
|
88
162
|
<input type="checkbox" class="form-check-input {{#if errors.enabled}}is-invalid{{/if}}" id="enabled"
|
|
@@ -94,7 +168,7 @@
|
|
|
94
168
|
<small class="form-text text-muted">If enabled, then this OAuth2 app is shown as an
|
|
95
169
|
account type option in the hosted authentication form.</small>
|
|
96
170
|
</div>
|
|
97
|
-
{{/unless}}
|
|
171
|
+
{{/unless}}{{/unless}}
|
|
98
172
|
|
|
99
173
|
</div>
|
|
100
174
|
</div>
|
|
@@ -175,6 +249,66 @@
|
|
|
175
249
|
{{/if}}
|
|
176
250
|
</div>
|
|
177
251
|
|
|
252
|
+
{{else if activeOutlookService}}
|
|
253
|
+
|
|
254
|
+
<div class="form-group">
|
|
255
|
+
|
|
256
|
+
<label for="clientId">
|
|
257
|
+
Azure Application Id
|
|
258
|
+
</label>
|
|
259
|
+
<input type="text" class="form-control {{#if errors.clientId}}is-invalid{{/if}}" id="clientId"
|
|
260
|
+
name="clientId" value="{{values.clientId}}" placeholder="Enter application (client) ID…" required />
|
|
261
|
+
{{#if errors.clientId}}
|
|
262
|
+
<span class="invalid-feedback">{{errors.clientId}}</span>
|
|
263
|
+
{{/if}}
|
|
264
|
+
<small class="form-text text-muted">The Application (client) ID from your Entra app registration.</small>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div class="form-group">
|
|
268
|
+
|
|
269
|
+
<label for="clientSecret">Client Secret</label>
|
|
270
|
+
|
|
271
|
+
<input type="text" class="form-control {{#if errors.clientSecret}}is-invalid{{/if}}" id="clientSecret"
|
|
272
|
+
name="clientSecret" value="{{values.clientSecret}}" {{#if hasClientSecret}}
|
|
273
|
+
placeholder="Client secret is set but not shown…" {{else}} placeholder="Enter client secret…"
|
|
274
|
+
{{/if}} {{#unless hasClientSecret}}required{{/unless}} />
|
|
275
|
+
{{#if errors.clientSecret}}
|
|
276
|
+
<span class="invalid-feedback">{{errors.clientSecret}}</span>
|
|
277
|
+
{{/if}}
|
|
278
|
+
<small class="form-text text-muted">Client secret value from Certificates & secrets.</small>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
<div class="form-group">
|
|
282
|
+
<label for="cloud">Azure cloud environment</label>
|
|
283
|
+
<select class="custom-select custom-select-sm {{#if errors.cloud}}is-invalid{{/if}}" id="cloud" name="cloud"
|
|
284
|
+
required>
|
|
285
|
+
|
|
286
|
+
{{#each azureClouds}}
|
|
287
|
+
<option value="{{id}}" {{#if selected}}selected{{/if}}>{{name}}{{#if
|
|
288
|
+
description}} —
|
|
289
|
+
{{description}}{{/if}}</option>
|
|
290
|
+
{{/each}}
|
|
291
|
+
</select>
|
|
292
|
+
{{#if errors.cloud}}
|
|
293
|
+
<span class="invalid-feedback">{{errors.cloud}}</span>
|
|
294
|
+
{{/if}}
|
|
295
|
+
<small class="form-text text-muted">Accounts hosted in different cloud environments use different OAuth2
|
|
296
|
+
endpoints.</small>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<div class="form-group">
|
|
300
|
+
<label for="authority">Directory (tenant) ID</label>
|
|
301
|
+
|
|
302
|
+
<input type="text" class="form-control {{#if errors.authority}}is-invalid{{/if}}" id="authority"
|
|
303
|
+
name="authority" value="{{values.authority}}"
|
|
304
|
+
placeholder="Enter tenant ID, eg. "f8cdef31-a31e-4b4a-93e4-5f571e91255a"" required />
|
|
305
|
+
{{#if errors.authority}}
|
|
306
|
+
<span class="invalid-feedback">{{errors.authority}}</span>
|
|
307
|
+
{{/if}}
|
|
308
|
+
<small class="form-text text-muted">The Directory (tenant) ID from your Entra app registration. Client
|
|
309
|
+
credentials flow requires a specific tenant ID.</small>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
178
312
|
{{else}}
|
|
179
313
|
|
|
180
314
|
{{#if activeGmail}}
|
|
@@ -702,6 +836,47 @@
|
|
|
702
836
|
|
|
703
837
|
{{/if}}
|
|
704
838
|
|
|
839
|
+
{{#if activeOutlookService}}
|
|
840
|
+
|
|
841
|
+
<div class="card mb-4">
|
|
842
|
+
|
|
843
|
+
<div class="card-header py-3">
|
|
844
|
+
<h6 class="m-0 font-weight-bold text-primary">Connection type</h6>
|
|
845
|
+
</div>
|
|
846
|
+
|
|
847
|
+
<div class="card-body">
|
|
848
|
+
<input type="hidden" name="baseScopes" value="api" />
|
|
849
|
+
<p><strong>MS Graph API</strong> — This app uses Microsoft Graph API with application permissions
|
|
850
|
+
(client credentials). Requires <code>Mail.ReadWrite</code>, <code>Mail.Send</code>, and
|
|
851
|
+
<code>User.Read.All</code> application permissions with admin consent in Entra.</p>
|
|
852
|
+
</div>
|
|
853
|
+
|
|
854
|
+
<div class="card-footer">
|
|
855
|
+
|
|
856
|
+
<p>Microsoft Graph delivers change notifications to EmailEngine at these two endpoints:</p>
|
|
857
|
+
|
|
858
|
+
<pre><code>{{mainServiceUrl}}/oauth/msg/lifecycle
|
|
859
|
+
{{mainServiceUrl}}/oauth/msg/notification</code></pre>
|
|
860
|
+
|
|
861
|
+
<p>
|
|
862
|
+
Both endpoints must be publicly reachable for anonymous HTTPS
|
|
863
|
+
<strong>POST</strong> requests. When a notification arrives, EmailEngine
|
|
864
|
+
immediately forwards the event to your application through its own webhooks.
|
|
865
|
+
</p>
|
|
866
|
+
|
|
867
|
+
<p>
|
|
868
|
+
If your EmailEngine instance cannot be exposed directly, configure a public
|
|
869
|
+
proxy domain in <span class="text-muted code-link"><a href="/admin/swagger#/Settings/postV1Settings"
|
|
870
|
+
target="_blank" rel="noopener noreferrer">notificationBaseUrl</a></span>. Microsoft Graph will then
|
|
871
|
+
post to <code>https://<proxy-domain>/oauth/msg/...</code> instead of
|
|
872
|
+
<code>{{mainServiceUrl}}</code>.
|
|
873
|
+
</p>
|
|
874
|
+
|
|
875
|
+
</div>
|
|
876
|
+
</div>
|
|
877
|
+
|
|
878
|
+
{{/if}}
|
|
879
|
+
|
|
705
880
|
{{#if activeGmail}}
|
|
706
881
|
<div class="card mb-4 {{#unless baseScopesApi}}d-none{{/unless}}" id="account-type-card-gmail">
|
|
707
882
|
<div class="card-header py-3">
|
|
@@ -119,6 +119,16 @@
|
|
|
119
119
|
</div>
|
|
120
120
|
{{/if}}
|
|
121
121
|
|
|
122
|
+
{{#if activeOutlookService}}
|
|
123
|
+
<div>Your Entra app registration <strong>must</strong> have the following <strong>application permissions</strong> (not
|
|
124
|
+
delegated) with admin consent:</div>
|
|
125
|
+
<ul>
|
|
126
|
+
<li><code>"Mail.ReadWrite"</code></li>
|
|
127
|
+
<li><code>"Mail.Send"</code></li>
|
|
128
|
+
<li><code>"User.Read.All"</code></li>
|
|
129
|
+
</ul>
|
|
130
|
+
{{/if}}
|
|
131
|
+
|
|
122
132
|
{{#if activeMailRu}}
|
|
123
133
|
<div>Your OAuth2 project <strong>must</strong> have the following scopes enabled:</div>
|
|
124
134
|
<ul>
|
package/workers/export.js
CHANGED
|
@@ -755,7 +755,7 @@ const exportWorker = new Worker(
|
|
|
755
755
|
throw new Error('Export not found');
|
|
756
756
|
}
|
|
757
757
|
|
|
758
|
-
await Export.
|
|
758
|
+
await Export.startProcessing(account, exportId);
|
|
759
759
|
await indexMessages(job, exportData);
|
|
760
760
|
await Export.update(account, exportId, { phase: 'exporting' });
|
|
761
761
|
|
|
@@ -788,7 +788,11 @@ const exportWorker = new Worker(
|
|
|
788
788
|
await fs.promises.unlink(exportData.filePath).catch(() => {});
|
|
789
789
|
}
|
|
790
790
|
|
|
791
|
-
|
|
791
|
+
if (err.code === 'ExportCancelled') {
|
|
792
|
+
await Export.deleteFully(account, exportId);
|
|
793
|
+
} else {
|
|
794
|
+
await Export.fail(account, exportId, err.message);
|
|
795
|
+
}
|
|
792
796
|
|
|
793
797
|
if (err.code !== 'AccountDeleted' && err.code !== 'AccountNotFound' && err.code !== 'ExportCancelled') {
|
|
794
798
|
await notify(account, EXPORT_FAILED_NOTIFY, {
|
package/workers/imap.js
CHANGED
|
@@ -37,7 +37,7 @@ const { GmailClient } = require('../lib/email-client/gmail-client');
|
|
|
37
37
|
const { OutlookClient } = require('../lib/email-client/outlook-client');
|
|
38
38
|
const { BaseClient } = require('../lib/email-client/base-client');
|
|
39
39
|
const { Account } = require('../lib/account');
|
|
40
|
-
const { oauth2Apps } = require('../lib/oauth2-apps');
|
|
40
|
+
const { oauth2Apps, isApiBasedApp } = require('../lib/oauth2-apps');
|
|
41
41
|
const { redis, notifyQueue, submitQueue, documentsQueue, getFlowProducer } = require('../lib/db');
|
|
42
42
|
const { MessagePortWritable } = require('../lib/message-port-stream');
|
|
43
43
|
const { getESClient } = require('../lib/document-store');
|
|
@@ -209,7 +209,7 @@ class ConnectionHandler {
|
|
|
209
209
|
oauth2App = await oauth2Apps.get(accountData.oauth2.provider);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
if (oauth2App
|
|
212
|
+
if (isApiBasedApp(oauth2App)) {
|
|
213
213
|
// Use API instead of IMAP
|
|
214
214
|
|
|
215
215
|
switch (oauth2App.provider) {
|
|
@@ -232,6 +232,7 @@ class ConnectionHandler {
|
|
|
232
232
|
accountObject.logger = accountObject.connection.logger;
|
|
233
233
|
break;
|
|
234
234
|
|
|
235
|
+
case 'outlookService':
|
|
235
236
|
case 'outlook':
|
|
236
237
|
accountObject.connection = new OutlookClient(account, {
|
|
237
238
|
runIndex,
|