gitlab-mail_room 0.0.9 → 0.0.23
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.
- checksums.yaml +4 -4
- data/.gitlab/issue_templates/Default.md +9 -0
- data/.gitlab/issue_templates/Release.md +1 -0
- data/.gitlab-ci.yml +14 -24
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +494 -0
- data/.ruby-version +1 -1
- data/.travis.yml +12 -5
- data/CHANGELOG.md +4 -0
- data/CONTRIBUTING.md +40 -0
- data/README.md +125 -14
- data/Rakefile +1 -1
- data/lib/mail_room/arbitration/redis.rb +1 -1
- data/lib/mail_room/connection.rb +6 -1
- data/lib/mail_room/crash_handler.rb +2 -1
- data/lib/mail_room/delivery/letter_opener.rb +1 -1
- data/lib/mail_room/delivery/postback.rb +42 -6
- data/lib/mail_room/delivery/que.rb +15 -1
- data/lib/mail_room/delivery/sidekiq.rb +4 -3
- data/lib/mail_room/jwt.rb +39 -0
- data/lib/mail_room/logger/structured.rb +15 -1
- data/lib/mail_room/mailbox.rb +56 -17
- data/lib/mail_room/mailbox_watcher.rb +7 -1
- data/lib/mail_room/microsoft_graph/connection.rb +243 -0
- data/lib/mail_room/microsoft_graph.rb +7 -0
- data/lib/mail_room/version.rb +1 -1
- data/mail_room.gemspec +7 -1
- data/spec/fixtures/jwt_secret +1 -0
- data/spec/lib/arbitration/redis_spec.rb +6 -5
- data/spec/lib/cli_spec.rb +3 -3
- data/spec/lib/configuration_spec.rb +1 -1
- data/spec/lib/delivery/letter_opener_spec.rb +4 -3
- data/spec/lib/delivery/logger_spec.rb +3 -2
- data/spec/lib/delivery/postback_spec.rb +62 -14
- data/spec/lib/delivery/sidekiq_spec.rb +33 -11
- data/spec/lib/jwt_spec.rb +80 -0
- data/spec/lib/logger/structured_spec.rb +34 -2
- data/spec/lib/mailbox_spec.rb +65 -17
- data/spec/lib/mailbox_watcher_spec.rb +54 -38
- data/spec/lib/microsoft_graph/connection_spec.rb +252 -0
- data/spec/spec_helper.rb +14 -3
- metadata +97 -8
data/README.md
CHANGED
@@ -20,7 +20,14 @@ The fork is useful as we can post quick fixes to our own fork and release fixes
|
|
20
20
|
|
21
21
|
## README
|
22
22
|
|
23
|
-
mail_room is a configuration based process that will
|
23
|
+
mail_room is a configuration based process that will listen for incoming
|
24
|
+
e-mail and execute a delivery method when a new message is
|
25
|
+
received. mail_room supports the following methods for receiving e-mail:
|
26
|
+
|
27
|
+
* IMAP
|
28
|
+
* [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/api/resources/mail-api-overview?view=graph-rest-1.0)
|
29
|
+
|
30
|
+
Examples of delivery methods include:
|
24
31
|
|
25
32
|
* POST to a delivery URL (Postback)
|
26
33
|
* Queue a job to Sidekiq or Que for later processing (Sidekiq or Que)
|
@@ -56,8 +63,8 @@ You will also need to install `faraday` or `letter_opener` if you use the `postb
|
|
56
63
|
```yaml
|
57
64
|
---
|
58
65
|
:health_check:
|
59
|
-
|
60
|
-
|
66
|
+
:address: "127.0.0.1"
|
67
|
+
:port: 8080
|
61
68
|
:mailboxes:
|
62
69
|
-
|
63
70
|
:email: "user1@gmail.com"
|
@@ -117,6 +124,33 @@ You will also need to install `faraday` or `letter_opener` if you use the `postb
|
|
117
124
|
:host: 127.0.0.1
|
118
125
|
:port: 26379
|
119
126
|
:worker: EmailReceiverWorker
|
127
|
+
-
|
128
|
+
:email: "user7@outlook365.com"
|
129
|
+
:password: "password"
|
130
|
+
:name: "inbox"
|
131
|
+
:inbox_method: microsoft_graph
|
132
|
+
:inbox_options:
|
133
|
+
:tenant_id: 12345
|
134
|
+
:client_id: ABCDE
|
135
|
+
:client_secret: YOUR-SECRET-HERE
|
136
|
+
:poll_interval: 60
|
137
|
+
:azure_ad_endpoint: https://login.microsoftonline.com
|
138
|
+
:graph_endpoint: https://graph.microsoft.com
|
139
|
+
:delivery_method: sidekiq
|
140
|
+
:delivery_options:
|
141
|
+
:redis_url: redis://localhost:6379
|
142
|
+
:worker: EmailReceiverWorker
|
143
|
+
-
|
144
|
+
:email: "user8@gmail.com"
|
145
|
+
:password: "password"
|
146
|
+
:name: "inbox"
|
147
|
+
:delivery_method: postback
|
148
|
+
:delivery_options:
|
149
|
+
:delivery_url: "http://localhost:3000/inbox"
|
150
|
+
:jwt_auth_header: "Mailroom-Api-Request"
|
151
|
+
:jwt_issuer: "mailroom"
|
152
|
+
:jwt_algorithm: "HS256"
|
153
|
+
:jwt_secret_path: "/etc/secrets/mailroom/.mailroom_secret"
|
120
154
|
```
|
121
155
|
|
122
156
|
**Note:** If using `delete_after_delivery`, you also probably want to use
|
@@ -134,6 +168,91 @@ the server is running. Otherwise, it returns a 500 status code.
|
|
134
168
|
|
135
169
|
This feature is not included in upstream `mail_room` and is specific to GitLab.
|
136
170
|
|
171
|
+
## inbox_method
|
172
|
+
|
173
|
+
By default, IMAP mode is assumed for reading a mailbox.
|
174
|
+
|
175
|
+
### IMAP Server Configuration ##
|
176
|
+
|
177
|
+
You can set per-mailbox configuration for the IMAP server's `host` (default: 'imap.gmail.com'), `port` (default: 993), `ssl` (default: true), and `start_tls` (default: false).
|
178
|
+
|
179
|
+
If you want to set additional options for IMAP SSL you can pass a YAML hash to match [SSLContext#set_params](http://docs.ruby-lang.org/en/2.2.0/OpenSSL/SSL/SSLContext.html#method-i-set_params). If you set `verify_mode` to `:none` it'll replace with the appropriate constant.
|
180
|
+
|
181
|
+
If you're seeing the error `Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)`, you need to configure your Gmail account to allow less secure apps to access it: https://support.google.com/accounts/answer/6010255.
|
182
|
+
|
183
|
+
### Microsoft Graph configuration
|
184
|
+
|
185
|
+
To use the Microsoft Graph API instead of IMAP to read e-mail, you will
|
186
|
+
need to create an application in the Azure Active Directory. See the
|
187
|
+
[Microsoft instructions](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) for more details:
|
188
|
+
|
189
|
+
1. Sign in to the [Azure portal](https://portal.azure.com).
|
190
|
+
1. Search for and select `Azure Active Directory`.
|
191
|
+
1. Under `Manage`, select `App registrations` > `New registration`.
|
192
|
+
1. Enter a `Name` for your application, such as `MailRoom`. Users of your app might see this name, and you can change it later.
|
193
|
+
1. If `Supported account types` is listed, select the appropriate option.
|
194
|
+
1. Leave `Redirect URI` blank. This is not needed.
|
195
|
+
1. Select `Register`.
|
196
|
+
1. Under `Manage`, select `Certificates & secrets`.
|
197
|
+
1. Under `Client secrets`, select `New client secret`, and enter a name.
|
198
|
+
1. Under `Expires`, select `Never`, unless you plan on updating the credentials every time it expires.
|
199
|
+
1. Select `Add`. Record the secret value in a safe location for use in a later step.
|
200
|
+
1. Under `Manage`, select `API Permissions` > `Add a permission`. Select `Microsoft Graph`.
|
201
|
+
1. Select `Application permissions`.
|
202
|
+
1. Under the `Mail` node, select `Mail.ReadWrite`, and then select Add permissions.
|
203
|
+
1. If `User.Read` is listed in the permission list, you can delete this.
|
204
|
+
1. Click `Grant admin consent` for these permissions.
|
205
|
+
|
206
|
+
#### Restrict mailbox access
|
207
|
+
|
208
|
+
Note that for MailRoom to work as a service account, this application
|
209
|
+
must have the `Mail.ReadWrite` to read/write mail in *all*
|
210
|
+
mailboxes. However, while this appears to be security risk,
|
211
|
+
we can configure an application access policy to limit the
|
212
|
+
mailbox access for this account. [Follow these instructions](https://docs.microsoft.com/en-us/graph/auth-limit-mailbox-access)
|
213
|
+
to setup PowerShell and configure this policy.
|
214
|
+
|
215
|
+
#### MailRoom config for Microsoft Graph
|
216
|
+
|
217
|
+
In the MailRoom configuration, set `inbox_method` to `microsoft_graph`.
|
218
|
+
You will also need:
|
219
|
+
|
220
|
+
* The client and tenant ID from the `Overview` section in the Azure app page
|
221
|
+
* The client secret created earlier
|
222
|
+
|
223
|
+
Fill in `inbox_options` with these values:
|
224
|
+
|
225
|
+
```yaml
|
226
|
+
:inbox_method: microsoft_graph
|
227
|
+
:inbox_options:
|
228
|
+
:tenant_id: 12345
|
229
|
+
:client_id: ABCDE
|
230
|
+
:client_secret: YOUR-SECRET-HERE
|
231
|
+
:poll_interval: 60
|
232
|
+
```
|
233
|
+
|
234
|
+
By default, MailRoom will poll for new messages every 60 seconds. `poll_interval` configures the number of
|
235
|
+
seconds to poll. Setting the value to 0 or under will default to 60 seconds.
|
236
|
+
|
237
|
+
### Alternative Azure cloud deployments
|
238
|
+
|
239
|
+
MailRoom will default to using the standard Azure HTTPS endpoints. To
|
240
|
+
configure MailRoom with Microsoft Cloud for US Government or other
|
241
|
+
[national cloud deployments](https://docs.microsoft.com/en-us/graph/deployments), set
|
242
|
+
the `azure_ad_endpoint` and `graph_endpoint` accordingly. For example,
|
243
|
+
for Microsoft Cloud for US Government:
|
244
|
+
|
245
|
+
```yaml
|
246
|
+
:inbox_method: microsoft_graph
|
247
|
+
:inbox_options:
|
248
|
+
:tenant_id: 12345
|
249
|
+
:client_id: ABCDE
|
250
|
+
:client_secret: YOUR-SECRET-HERE
|
251
|
+
:poll_interval: 60
|
252
|
+
:azure_ad_endpoint: https://login.microsoftonline.us
|
253
|
+
:graph_endpoint: https://graph.microsoft.us
|
254
|
+
```
|
255
|
+
|
137
256
|
## delivery_method ##
|
138
257
|
|
139
258
|
### postback ###
|
@@ -260,8 +379,8 @@ And finally, configure MailRoom to use the postback configuration with the optio
|
|
260
379
|
:delivery_method: postback
|
261
380
|
:delivery_options:
|
262
381
|
:delivery_url: https://example.com/rails/action_mailbox/relay/inbound_emails
|
263
|
-
:
|
264
|
-
:
|
382
|
+
:username: actionmailbox
|
383
|
+
:password: <INGRESS_PASSWORD>
|
265
384
|
```
|
266
385
|
|
267
386
|
## Receiving `postback` in Rails ##
|
@@ -278,20 +397,12 @@ it's probably because the content-type is set to Faraday's default, which is `H
|
|
278
397
|
## idle_timeout ##
|
279
398
|
|
280
399
|
By default, the IDLE command will wait for 29 minutes (in order to keep the server connection happy).
|
281
|
-
If you'd prefer not to wait that long, you can pass `
|
400
|
+
If you'd prefer not to wait that long, you can pass `idle_timeout` in seconds for your mailbox configuration.
|
282
401
|
|
283
402
|
## Search Command ##
|
284
403
|
|
285
404
|
This setting allows configuration of the IMAP search command sent to the server. This still defaults 'UNSEEN'. You may find that 'NEW' works better for you.
|
286
405
|
|
287
|
-
## IMAP Server Configuration ##
|
288
|
-
|
289
|
-
You can set per-mailbox configuration for the IMAP server's `host` (default: 'imap.gmail.com'), `port` (default: 993), `ssl` (default: true), and `start_tls` (default: false).
|
290
|
-
|
291
|
-
If you want to set additional options for IMAP SSL you can pass a YAML hash to match [SSLContext#set_params](http://docs.ruby-lang.org/en/2.2.0/OpenSSL/SSL/SSLContext.html#method-i-set_params). If you set `verify_mode` to `:none` it'll replace with the appropriate constant.
|
292
|
-
|
293
|
-
If you're seeing the error `Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)`, you need to configure your Gmail account to allow less secure apps to access it: https://support.google.com/accounts/answer/6010255.
|
294
|
-
|
295
406
|
## Running in Production ##
|
296
407
|
|
297
408
|
I suggest running with either upstart or init.d. Check out this wiki page for some example scripts for both: https://github.com/tpitale/mail_room/wiki/Init-Scripts-for-Running-mail_room
|
data/Rakefile
CHANGED
@@ -31,7 +31,7 @@ module MailRoom
|
|
31
31
|
# Any subsequent failure in the instance which gets the lock will be dealt
|
32
32
|
# with by the expiration, at which time another instance can pick up the
|
33
33
|
# message and try again.
|
34
|
-
client.set(key, 1,
|
34
|
+
client.set(key, 1, nx: true, ex: expiration)
|
35
35
|
end
|
36
36
|
|
37
37
|
private
|
data/lib/mail_room/connection.rb
CHANGED
@@ -6,18 +6,23 @@ module MailRoom
|
|
6
6
|
|
7
7
|
def initialize(mailbox)
|
8
8
|
@mailbox = mailbox
|
9
|
+
@stopped = false
|
9
10
|
end
|
10
11
|
|
11
12
|
def on_new_message(&block)
|
12
13
|
@new_message_handler = block
|
13
14
|
end
|
14
15
|
|
16
|
+
def stopped?
|
17
|
+
@stopped
|
18
|
+
end
|
19
|
+
|
15
20
|
def wait
|
16
21
|
raise NotImplementedError
|
17
22
|
end
|
18
23
|
|
19
24
|
def quit
|
20
|
-
|
25
|
+
@stopped = true
|
21
26
|
end
|
22
27
|
end
|
23
28
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'date'
|
1
2
|
|
2
3
|
module MailRoom
|
3
4
|
class CrashHandler
|
@@ -19,7 +20,7 @@ module MailRoom
|
|
19
20
|
private
|
20
21
|
|
21
22
|
def json(error)
|
22
|
-
{ time:
|
23
|
+
{ time: DateTime.now.iso8601(3), severity: :fatal, message: error.message, backtrace: error.backtrace }.to_json
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
@@ -24,7 +24,7 @@ module MailRoom
|
|
24
24
|
# Trigger `LetterOpener` to deliver our message
|
25
25
|
# @param message [String] the email message as a string, RFC822 format
|
26
26
|
def deliver(message)
|
27
|
-
method = ::LetterOpener::DeliveryMethod.new(:
|
27
|
+
method = ::LetterOpener::DeliveryMethod.new(location: @delivery_options.location)
|
28
28
|
method.deliver!(Mail.read_from_string(message))
|
29
29
|
|
30
30
|
true
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'faraday'
|
2
|
+
require "mail_room/jwt"
|
2
3
|
|
3
4
|
module MailRoom
|
4
5
|
module Delivery
|
5
6
|
# Postback Delivery method
|
6
7
|
# @author Tony Pitale
|
7
8
|
class Postback
|
8
|
-
Options = Struct.new(:url, :token, :username, :password, :logger, :content_type) do
|
9
|
+
Options = Struct.new(:url, :token, :username, :password, :logger, :content_type, :jwt) do
|
9
10
|
def initialize(mailbox)
|
10
11
|
url =
|
11
12
|
mailbox.delivery_url ||
|
@@ -17,23 +18,44 @@ module MailRoom
|
|
17
18
|
mailbox.delivery_options[:delivery_token] ||
|
18
19
|
mailbox.delivery_options[:token]
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
jwt = initialize_jwt(mailbox.delivery_options)
|
22
|
+
|
23
|
+
username =
|
24
|
+
mailbox.delivery_options[:username] ||
|
25
|
+
mailbox.delivery_options[:delivery_username]
|
26
|
+
password =
|
27
|
+
mailbox.delivery_options[:password] ||
|
28
|
+
mailbox.delivery_options[:delivery_password]
|
22
29
|
|
23
30
|
logger = mailbox.logger
|
24
31
|
|
25
32
|
content_type = mailbox.delivery_options[:content_type]
|
26
33
|
|
27
|
-
super(url, token, username, password, logger, content_type)
|
34
|
+
super(url, token, username, password, logger, content_type, jwt)
|
28
35
|
end
|
29
36
|
|
30
37
|
def token_auth?
|
31
38
|
!self[:token].nil?
|
32
39
|
end
|
33
40
|
|
41
|
+
def jwt_auth?
|
42
|
+
self[:jwt].valid?
|
43
|
+
end
|
44
|
+
|
34
45
|
def basic_auth?
|
35
46
|
!self[:username].nil? && !self[:password].nil?
|
36
47
|
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def initialize_jwt(delivery_options)
|
52
|
+
::MailRoom::JWT.new(
|
53
|
+
header: delivery_options[:jwt_auth_header],
|
54
|
+
secret_path: delivery_options[:jwt_secret_path],
|
55
|
+
algorithm: delivery_options[:jwt_algorithm],
|
56
|
+
issuer: delivery_options[:jwt_issuer]
|
57
|
+
)
|
58
|
+
end
|
37
59
|
end
|
38
60
|
|
39
61
|
# Build a new delivery, hold the delivery options
|
@@ -60,13 +82,27 @@ module MailRoom
|
|
60
82
|
connection.post do |request|
|
61
83
|
request.url @delivery_options.url
|
62
84
|
request.body = message
|
63
|
-
|
64
|
-
request
|
85
|
+
config_request_content_type(request)
|
86
|
+
config_request_jwt_auth(request)
|
65
87
|
end
|
66
88
|
|
67
89
|
@delivery_options.logger.info({ delivery_method: 'Postback', action: 'message pushed', url: @delivery_options.url })
|
68
90
|
true
|
69
91
|
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def config_request_content_type(request)
|
96
|
+
return if @delivery_options.content_type.nil?
|
97
|
+
|
98
|
+
request.headers['Content-Type'] = @delivery_options.content_type
|
99
|
+
end
|
100
|
+
|
101
|
+
def config_request_jwt_auth(request)
|
102
|
+
return unless @delivery_options.jwt_auth?
|
103
|
+
|
104
|
+
request.headers[@delivery_options.jwt.header] = @delivery_options.jwt.token
|
105
|
+
end
|
70
106
|
end
|
71
107
|
end
|
72
108
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'pg'
|
2
2
|
require 'json'
|
3
|
+
require 'charlock_holmes'
|
3
4
|
|
4
5
|
module MailRoom
|
5
6
|
module Delivery
|
@@ -34,7 +35,7 @@ module MailRoom
|
|
34
35
|
# deliver the message by pushing it onto the configured Sidekiq queue
|
35
36
|
# @param message [String] the email message as a string, RFC822 format
|
36
37
|
def deliver(message)
|
37
|
-
queue_job(message)
|
38
|
+
queue_job(utf8_encode_message(message))
|
38
39
|
@options.logger.info({ delivery_method: 'Que', action: 'message pushed' })
|
39
40
|
end
|
40
41
|
|
@@ -58,6 +59,19 @@ module MailRoom
|
|
58
59
|
|
59
60
|
connection.exec(sql, [options.priority, options.job_class, options.queue, JSON.dump(args)])
|
60
61
|
end
|
62
|
+
|
63
|
+
def utf8_encode_message(message)
|
64
|
+
message = message.dup
|
65
|
+
|
66
|
+
message.force_encoding("UTF-8")
|
67
|
+
return message if message.valid_encoding?
|
68
|
+
|
69
|
+
detection = CharlockHolmes::EncodingDetector.detect(message)
|
70
|
+
return message unless detection && detection[:encoding]
|
71
|
+
|
72
|
+
# Convert non-UTF-8 body UTF-8 so it can be dumped as JSON.
|
73
|
+
CharlockHolmes::Converter.convert(message, detection[:encoding], 'UTF-8')
|
74
|
+
end
|
61
75
|
end
|
62
76
|
end
|
63
77
|
end
|
@@ -8,16 +8,17 @@ module MailRoom
|
|
8
8
|
# Sidekiq Delivery method
|
9
9
|
# @author Douwe Maan
|
10
10
|
class Sidekiq
|
11
|
-
Options = Struct.new(:redis_url, :namespace, :sentinels, :queue, :worker, :logger) do
|
11
|
+
Options = Struct.new(:redis_url, :namespace, :sentinels, :queue, :worker, :logger, :redis_db) do
|
12
12
|
def initialize(mailbox)
|
13
13
|
redis_url = mailbox.delivery_options[:redis_url] || "redis://localhost:6379"
|
14
|
+
redis_db = mailbox.delivery_options[:redis_db] || 0
|
14
15
|
namespace = mailbox.delivery_options[:namespace]
|
15
16
|
sentinels = mailbox.delivery_options[:sentinels]
|
16
17
|
queue = mailbox.delivery_options[:queue] || "default"
|
17
18
|
worker = mailbox.delivery_options[:worker]
|
18
19
|
logger = mailbox.logger
|
19
20
|
|
20
|
-
super(redis_url, namespace, sentinels, queue, worker, logger)
|
21
|
+
super(redis_url, namespace, sentinels, queue, worker, logger, redis_db)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -45,7 +46,7 @@ module MailRoom
|
|
45
46
|
def client
|
46
47
|
@client ||= begin
|
47
48
|
sentinels = options.sentinels
|
48
|
-
redis_options = { url: options.redis_url }
|
49
|
+
redis_options = { url: options.redis_url, db: options.redis_db }
|
49
50
|
redis_options[:sentinels] = sentinels if sentinels
|
50
51
|
|
51
52
|
redis = ::Redis.new(redis_options)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'jwt'
|
6
|
+
require 'base64'
|
7
|
+
|
8
|
+
module MailRoom
|
9
|
+
# Responsible for validating and generating JWT token
|
10
|
+
class JWT
|
11
|
+
DEFAULT_ISSUER = 'mailroom'
|
12
|
+
DEFAULT_ALGORITHM = 'HS256'
|
13
|
+
|
14
|
+
attr_reader :header, :secret_path, :issuer, :algorithm
|
15
|
+
|
16
|
+
def initialize(header:, secret_path:, issuer:, algorithm:)
|
17
|
+
@header = header
|
18
|
+
@secret_path = secret_path
|
19
|
+
@issuer = issuer || DEFAULT_ISSUER
|
20
|
+
@algorithm = algorithm || DEFAULT_ALGORITHM
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
[@header, @secret_path, @issuer, @algorithm].none?(&:nil?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def token
|
28
|
+
return nil unless valid?
|
29
|
+
|
30
|
+
secret = Base64.strict_decode64(File.read(@secret_path).chomp)
|
31
|
+
payload = {
|
32
|
+
nonce: SecureRandom.hex(12),
|
33
|
+
iat: Time.now.to_i, # https://github.com/jwt/ruby-jwt#issued-at-claim
|
34
|
+
iss: @issuer
|
35
|
+
}
|
36
|
+
::JWT.encode payload, secret, @algorithm
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'date'
|
1
2
|
require 'logger'
|
2
3
|
require 'json'
|
3
4
|
|
@@ -10,12 +11,25 @@ module MailRoom
|
|
10
11
|
|
11
12
|
data = {}
|
12
13
|
data[:severity] = severity
|
13
|
-
data[:time] = timestamp || Time.now
|
14
|
+
data[:time] = format_timestamp(timestamp || Time.now)
|
14
15
|
# only accept a Hash
|
15
16
|
data.merge!(message)
|
16
17
|
|
17
18
|
data.to_json + "\n"
|
18
19
|
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def format_timestamp(timestamp)
|
24
|
+
case timestamp
|
25
|
+
when Time
|
26
|
+
timestamp.to_datetime.iso8601(3).to_s
|
27
|
+
when DateTime
|
28
|
+
timestamp.iso8601(3).to_s
|
29
|
+
else
|
30
|
+
timestamp
|
31
|
+
end
|
32
|
+
end
|
19
33
|
end
|
20
34
|
end
|
21
35
|
end
|
data/lib/mail_room/mailbox.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
require "mail_room/delivery"
|
2
2
|
require "mail_room/arbitration"
|
3
3
|
require "mail_room/imap"
|
4
|
+
require "mail_room/microsoft_graph"
|
4
5
|
|
5
6
|
module MailRoom
|
6
7
|
# Mailbox Configuration fields
|
7
8
|
MAILBOX_FIELDS = [
|
8
9
|
:email,
|
10
|
+
:inbox_method,
|
11
|
+
:inbox_options,
|
9
12
|
:password,
|
10
13
|
:host,
|
11
14
|
:port,
|
@@ -42,24 +45,26 @@ module MailRoom
|
|
42
45
|
# 29 minutes, as suggested by the spec: https://tools.ietf.org/html/rfc2177
|
43
46
|
IMAP_IDLE_TIMEOUT = 29 * 60 # 29 minutes in in seconds
|
44
47
|
|
45
|
-
|
48
|
+
IMAP_CONFIGURATION = [:name, :email, :password, :host, :port].freeze
|
49
|
+
MICROSOFT_GRAPH_CONFIGURATION = [:name, :email].freeze
|
50
|
+
MICROSOFT_GRAPH_INBOX_OPTIONS = [:tenant_id, :client_id, :client_secret].freeze
|
46
51
|
|
47
52
|
# Default attributes for the mailbox configuration
|
48
53
|
DEFAULTS = {
|
49
|
-
:
|
50
|
-
:
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
59
|
-
:
|
60
|
-
:
|
61
|
-
:
|
62
|
-
:
|
54
|
+
search_command: 'UNSEEN',
|
55
|
+
delivery_method: 'postback',
|
56
|
+
host: 'imap.gmail.com',
|
57
|
+
port: 993,
|
58
|
+
ssl: true,
|
59
|
+
start_tls: false,
|
60
|
+
limit_max_unread: 0,
|
61
|
+
idle_timeout: IMAP_IDLE_TIMEOUT,
|
62
|
+
delete_after_delivery: false,
|
63
|
+
expunge_deleted: false,
|
64
|
+
delivery_options: {},
|
65
|
+
arbitration_method: 'noop',
|
66
|
+
arbitration_options: {},
|
67
|
+
logger: {}
|
63
68
|
}
|
64
69
|
|
65
70
|
# Store the configuration and require the appropriate delivery method
|
@@ -122,14 +127,32 @@ module MailRoom
|
|
122
127
|
{ email: self.email, name: self.name }
|
123
128
|
end
|
124
129
|
|
130
|
+
def imap?
|
131
|
+
!microsoft_graph?
|
132
|
+
end
|
133
|
+
|
134
|
+
def microsoft_graph?
|
135
|
+
self[:inbox_method].to_s == 'microsoft_graph'
|
136
|
+
end
|
137
|
+
|
125
138
|
def validate!
|
139
|
+
if microsoft_graph?
|
140
|
+
validate_microsoft_graph!
|
141
|
+
else
|
142
|
+
validate_imap!
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def validate_imap!
|
126
149
|
if self[:idle_timeout] > IMAP_IDLE_TIMEOUT
|
127
150
|
raise IdleTimeoutTooLarge,
|
128
151
|
"Please use an idle timeout smaller than #{29*60} to prevent " \
|
129
152
|
"IMAP server disconnects"
|
130
153
|
end
|
131
154
|
|
132
|
-
|
155
|
+
IMAP_CONFIGURATION.each do |k|
|
133
156
|
if self[k].nil?
|
134
157
|
raise ConfigurationError,
|
135
158
|
"Field :#{k} is required in Mailbox: #{inspect}"
|
@@ -137,7 +160,23 @@ module MailRoom
|
|
137
160
|
end
|
138
161
|
end
|
139
162
|
|
140
|
-
|
163
|
+
def validate_microsoft_graph!
|
164
|
+
raise ConfigurationError, "Missing inbox_options in Mailbox: #{inspect}" unless self.inbox_options.is_a?(Hash)
|
165
|
+
|
166
|
+
MICROSOFT_GRAPH_CONFIGURATION.each do |k|
|
167
|
+
if self[k].nil?
|
168
|
+
raise ConfigurationError,
|
169
|
+
"Field :#{k} is required in Mailbox: #{inspect}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
MICROSOFT_GRAPH_INBOX_OPTIONS.each do |k|
|
174
|
+
if self[:inbox_options][k].nil?
|
175
|
+
raise ConfigurationError,
|
176
|
+
"inbox_options field :#{k} is required in Mailbox: #{inspect}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
141
180
|
|
142
181
|
def parsed_arbitration_options
|
143
182
|
arbitration_klass::Options.new(self)
|
@@ -62,8 +62,14 @@ module MailRoom
|
|
62
62
|
end
|
63
63
|
|
64
64
|
private
|
65
|
+
|
65
66
|
def connection
|
66
|
-
@connection ||=
|
67
|
+
@connection ||=
|
68
|
+
if @mailbox.microsoft_graph?
|
69
|
+
::MailRoom::MicrosoftGraph::Connection.new(@mailbox)
|
70
|
+
else
|
71
|
+
::MailRoom::IMAP::Connection.new(@mailbox)
|
72
|
+
end
|
67
73
|
end
|
68
74
|
end
|
69
75
|
end
|