mail_room 0.10.1 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +10 -0
- data/.github/workflows/ci.yml +54 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +86 -90
- data/.ruby-version +1 -1
- data/.tool-versions +1 -0
- data/CHANGELOG.md +8 -0
- data/README.md +40 -18
- data/lib/mail_room/arbitration/redis.rb +8 -1
- data/lib/mail_room/connection.rb +8 -1
- data/lib/mail_room/delivery/postback.rb +71 -11
- data/lib/mail_room/delivery/que.rb +15 -1
- data/lib/mail_room/delivery/sidekiq.rb +7 -0
- data/lib/mail_room/jwt.rb +39 -0
- data/lib/mail_room/microsoft_graph/connection.rb +33 -7
- data/lib/mail_room/version.rb +1 -1
- data/mail_room.gemspec +4 -3
- data/spec/fixtures/jwt_secret +1 -0
- data/spec/lib/arbitration/redis_spec.rb +6 -5
- data/spec/lib/delivery/letter_opener_spec.rb +2 -1
- data/spec/lib/delivery/logger_spec.rb +2 -1
- data/spec/lib/delivery/postback_spec.rb +51 -3
- data/spec/lib/delivery/sidekiq_spec.rb +7 -7
- data/spec/lib/jwt_spec.rb +80 -0
- data/spec/lib/microsoft_graph/connection_spec.rb +86 -24
- metadata +37 -10
- data/.travis.yml +0 -17
data/README.md
CHANGED
@@ -111,10 +111,23 @@ You will also need to install `faraday` or `letter_opener` if you use the `postb
|
|
111
111
|
:client_id: ABCDE
|
112
112
|
:client_secret: YOUR-SECRET-HERE
|
113
113
|
:poll_interval: 60
|
114
|
+
:azure_ad_endpoint: https://login.microsoftonline.com
|
115
|
+
:graph_endpoint: https://graph.microsoft.com
|
114
116
|
:delivery_method: sidekiq
|
115
117
|
:delivery_options:
|
116
118
|
:redis_url: redis://localhost:6379
|
117
119
|
:worker: EmailReceiverWorker
|
120
|
+
-
|
121
|
+
:email: "user8@gmail.com"
|
122
|
+
:password: "password"
|
123
|
+
:name: "inbox"
|
124
|
+
:delivery_method: postback
|
125
|
+
:delivery_options:
|
126
|
+
:delivery_url: "http://localhost:3000/inbox"
|
127
|
+
:jwt_auth_header: "Mailroom-Api-Request"
|
128
|
+
:jwt_issuer: "mailroom"
|
129
|
+
:jwt_algorithm: "HS256"
|
130
|
+
:jwt_secret_path: "/etc/secrets/mailroom/.mailroom_secret"
|
118
131
|
```
|
119
132
|
|
120
133
|
**Note:** If using `delete_after_delivery`, you also probably want to use
|
@@ -135,11 +148,11 @@ If you're seeing the error `Please log in via your web browser: https://support.
|
|
135
148
|
### Microsoft Graph configuration
|
136
149
|
|
137
150
|
To use the Microsoft Graph API instead of IMAP to read e-mail, you will
|
138
|
-
need to create an application in
|
139
|
-
[Microsoft instructions](https://
|
151
|
+
need to create an application in Microsoft Entra ID (formerly known as Azure Active Directory). See the
|
152
|
+
[Microsoft instructions](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) for more details:
|
140
153
|
|
141
154
|
1. Sign in to the [Azure portal](https://portal.azure.com).
|
142
|
-
1. Search for and select `
|
155
|
+
1. Search for and select `Microsoft Entra ID`.
|
143
156
|
1. Under `Manage`, select `App registrations` > `New registration`.
|
144
157
|
1. Enter a `Name` for your application, such as `MailRoom`. Users of your app might see this name, and you can change it later.
|
145
158
|
1. If `Supported account types` is listed, select the appropriate option.
|
@@ -186,6 +199,25 @@ Fill in `inbox_options` with these values:
|
|
186
199
|
By default, MailRoom will poll for new messages every 60 seconds. `poll_interval` configures the number of
|
187
200
|
seconds to poll. Setting the value to 0 or under will default to 60 seconds.
|
188
201
|
|
202
|
+
### Alternative Azure cloud deployments
|
203
|
+
|
204
|
+
MailRoom will default to using the standard Azure HTTPS endpoints. To
|
205
|
+
configure MailRoom with Microsoft Cloud for US Government or other
|
206
|
+
[national cloud deployments](https://docs.microsoft.com/en-us/graph/deployments), set
|
207
|
+
the `azure_ad_endpoint` and `graph_endpoint` accordingly. For example,
|
208
|
+
for Microsoft Cloud for US Government:
|
209
|
+
|
210
|
+
```yaml
|
211
|
+
:inbox_method: microsoft_graph
|
212
|
+
:inbox_options:
|
213
|
+
:tenant_id: 12345
|
214
|
+
:client_id: ABCDE
|
215
|
+
:client_secret: YOUR-SECRET-HERE
|
216
|
+
:poll_interval: 60
|
217
|
+
:azure_ad_endpoint: https://login.microsoftonline.us
|
218
|
+
:graph_endpoint: https://graph.microsoft.us
|
219
|
+
```
|
220
|
+
|
189
221
|
## delivery_method ##
|
190
222
|
|
191
223
|
### postback ###
|
@@ -215,7 +247,7 @@ Delivery options:
|
|
215
247
|
Required, defaults to `redis://localhost:6379`.
|
216
248
|
- **sentinels**: A list of sentinels servers used to provide HA to Redis. (see [Sentinel Support](#sentinel-support))
|
217
249
|
Optional.
|
218
|
-
- **namespace**: The Redis namespace Sidekiq works under. Use the same Redis namespace that's used to configure Sidekiq.
|
250
|
+
- **namespace**: [DEPRECATED] The Redis namespace Sidekiq works under. Use the same Redis namespace that's used to configure Sidekiq.
|
219
251
|
Optional.
|
220
252
|
- **queue**: The Sidekiq queue the job is pushed onto. Make sure Sidekiq actually reads off this queue.
|
221
253
|
Required, defaults to `default`.
|
@@ -312,8 +344,8 @@ And finally, configure MailRoom to use the postback configuration with the optio
|
|
312
344
|
:delivery_method: postback
|
313
345
|
:delivery_options:
|
314
346
|
:delivery_url: https://example.com/rails/action_mailbox/relay/inbound_emails
|
315
|
-
:
|
316
|
-
:
|
347
|
+
:username: actionmailbox
|
348
|
+
:password: <INGRESS_PASSWORD>
|
317
349
|
```
|
318
350
|
|
319
351
|
## Receiving `postback` in Rails ##
|
@@ -359,7 +391,7 @@ When running multiple instances of MailRoom against a single mailbox, to try to
|
|
359
391
|
:arbitration_options:
|
360
392
|
# The Redis server to connect with. Defaults to redis://localhost:6379.
|
361
393
|
:redis_url: redis://redis.example.com:6379
|
362
|
-
# The Redis namespace to house the Redis keys under. Optional.
|
394
|
+
# [DEPRECATED] The Redis namespace to house the Redis keys under. Optional.
|
363
395
|
:namespace: mail_room
|
364
396
|
-
|
365
397
|
:email: "user2@gmail.com"
|
@@ -379,7 +411,7 @@ When running multiple instances of MailRoom against a single mailbox, to try to
|
|
379
411
|
-
|
380
412
|
:host: 127.0.0.1
|
381
413
|
:port: 26379
|
382
|
-
# The Redis namespace to house the Redis keys under. Optional.
|
414
|
+
# [DEPRECATED] The Redis namespace to house the Redis keys under. Optional.
|
383
415
|
:namespace: mail_room
|
384
416
|
```
|
385
417
|
|
@@ -422,13 +454,3 @@ respectively and MailRoom will log there.
|
|
422
454
|
4. Push to the branch (`git push origin my-new-feature`)
|
423
455
|
5. Create new Pull Request
|
424
456
|
6. If accepted, ask for commit rights
|
425
|
-
|
426
|
-
## TODO ##
|
427
|
-
|
428
|
-
1. specs, this is just a (working) proof of concept √
|
429
|
-
2. finish code for POSTing to callback with auth √
|
430
|
-
3. accept mailbox configuration for one account directly on the commandline; or ask for it
|
431
|
-
4. add example rails endpoint, with auth examples
|
432
|
-
5. add example configs for upstart/init.d √
|
433
|
-
6. log to stdout √
|
434
|
-
7. add a development mode that opens in letter_opener by ryanb √
|
@@ -9,6 +9,13 @@ module MailRoom
|
|
9
9
|
namespace = mailbox.arbitration_options[:namespace]
|
10
10
|
sentinels = mailbox.arbitration_options[:sentinels]
|
11
11
|
|
12
|
+
if namespace
|
13
|
+
warn <<~MSG
|
14
|
+
Redis namespaces are deprecated. This option will be ignored in future versions.
|
15
|
+
See https://github.com/sidekiq/sidekiq/issues/2586 for more details."
|
16
|
+
MSG
|
17
|
+
end
|
18
|
+
|
12
19
|
super(redis_url, namespace, sentinels)
|
13
20
|
end
|
14
21
|
end
|
@@ -31,7 +38,7 @@ module MailRoom
|
|
31
38
|
# Any subsequent failure in the instance which gets the lock will be dealt
|
32
39
|
# with by the expiration, at which time another instance can pick up the
|
33
40
|
# message and try again.
|
34
|
-
client.set(key, 1,
|
41
|
+
client.set(key, 1, nx: true, ex: expiration)
|
35
42
|
end
|
36
43
|
|
37
44
|
private
|
data/lib/mail_room/connection.rb
CHANGED
@@ -6,16 +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
|
-
def quit
|
24
|
+
def quit
|
25
|
+
@stopped = true
|
26
|
+
end
|
20
27
|
end
|
21
28
|
end
|
@@ -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
|
@@ -49,24 +71,62 @@ module MailRoom
|
|
49
71
|
connection = Faraday.new
|
50
72
|
|
51
73
|
if @delivery_options.token_auth?
|
52
|
-
connection
|
74
|
+
config_token_auth(connection)
|
53
75
|
elsif @delivery_options.basic_auth?
|
54
|
-
connection
|
55
|
-
@delivery_options.username,
|
56
|
-
@delivery_options.password
|
57
|
-
)
|
76
|
+
config_basic_auth(connection)
|
58
77
|
end
|
59
78
|
|
60
79
|
connection.post do |request|
|
61
80
|
request.url @delivery_options.url
|
62
81
|
request.body = message
|
63
|
-
|
64
|
-
request
|
82
|
+
config_request_content_type(request)
|
83
|
+
config_request_jwt_auth(request)
|
65
84
|
end
|
66
85
|
|
67
86
|
@delivery_options.logger.info({ delivery_method: 'Postback', action: 'message pushed', url: @delivery_options.url })
|
68
87
|
true
|
69
88
|
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def config_request_content_type(request)
|
93
|
+
return if @delivery_options.content_type.nil?
|
94
|
+
|
95
|
+
request.headers['Content-Type'] = @delivery_options.content_type
|
96
|
+
end
|
97
|
+
|
98
|
+
def config_request_jwt_auth(request)
|
99
|
+
return unless @delivery_options.jwt_auth?
|
100
|
+
|
101
|
+
request.headers[@delivery_options.jwt.header] = @delivery_options.jwt.token
|
102
|
+
end
|
103
|
+
|
104
|
+
def config_token_auth(connection)
|
105
|
+
# connection.token_auth was removed in Faraday v2 in favor of connection.request(:authorization, 'Token', token)
|
106
|
+
if defined?(connection.token_auth)
|
107
|
+
connection.token_auth @delivery_options.token
|
108
|
+
else
|
109
|
+
connection.request(
|
110
|
+
:authorization, 'Token',
|
111
|
+
@delivery_options.token
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def config_basic_auth(connection)
|
117
|
+
if defined?(connection.basic_auth)
|
118
|
+
connection.basic_auth(
|
119
|
+
@delivery_options.username,
|
120
|
+
@delivery_options.password
|
121
|
+
)
|
122
|
+
else
|
123
|
+
connection.request(
|
124
|
+
:authorization, :basic,
|
125
|
+
@delivery_options.username,
|
126
|
+
@delivery_options.password
|
127
|
+
)
|
128
|
+
end
|
129
|
+
end
|
70
130
|
end
|
71
131
|
end
|
72
132
|
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
|
@@ -18,6 +18,13 @@ module MailRoom
|
|
18
18
|
worker = mailbox.delivery_options[:worker]
|
19
19
|
logger = mailbox.logger
|
20
20
|
|
21
|
+
if namespace
|
22
|
+
warn <<~MSG
|
23
|
+
Redis namespaces are deprecated. This option will be ignored in future versions.
|
24
|
+
See https://github.com/sidekiq/sidekiq/issues/2586 for more details."
|
25
|
+
MSG
|
26
|
+
end
|
27
|
+
|
21
28
|
super(redis_url, namespace, sentinels, queue, worker, logger, redis_db)
|
22
29
|
end
|
23
30
|
end
|
@@ -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
|
@@ -6,7 +6,6 @@ require 'oauth2'
|
|
6
6
|
module MailRoom
|
7
7
|
module MicrosoftGraph
|
8
8
|
class Connection < MailRoom::Connection
|
9
|
-
SCOPE = 'https://graph.microsoft.com/.default'
|
10
9
|
NEXT_PAGE_KEY = '@odata.nextLink'
|
11
10
|
DEFAULT_POLL_INTERVAL_S = 60
|
12
11
|
|
@@ -22,6 +21,8 @@ module MailRoom
|
|
22
21
|
end
|
23
22
|
|
24
23
|
def wait
|
24
|
+
return if stopped?
|
25
|
+
|
25
26
|
process_mailbox
|
26
27
|
|
27
28
|
@throttled_count = 0
|
@@ -42,17 +43,30 @@ module MailRoom
|
|
42
43
|
private
|
43
44
|
|
44
45
|
def wait_for_new_messages
|
45
|
-
|
46
|
+
sleep_while_running(poll_interval)
|
46
47
|
end
|
47
48
|
|
48
49
|
def backoff
|
49
|
-
|
50
|
+
sleep_while_running(backoff_secs)
|
50
51
|
end
|
51
52
|
|
52
53
|
def backoff_secs
|
53
54
|
[60 * 10, 2**throttled_count].min
|
54
55
|
end
|
55
56
|
|
57
|
+
# Unless wake up periodically, we won't notice that the thread was stopped
|
58
|
+
# if we sleep the entire interval.
|
59
|
+
def sleep_while_running(sleep_interval)
|
60
|
+
sleep_interval.times do
|
61
|
+
do_sleep(1)
|
62
|
+
return if stopped?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def do_sleep(interval)
|
67
|
+
sleep(interval)
|
68
|
+
end
|
69
|
+
|
56
70
|
def reset
|
57
71
|
@token = nil
|
58
72
|
@throttled_count = 0
|
@@ -61,12 +75,12 @@ module MailRoom
|
|
61
75
|
def setup
|
62
76
|
@mailbox.logger.info({ context: @mailbox.context, action: 'Retrieving OAuth2 token...' })
|
63
77
|
|
64
|
-
@token = client.client_credentials.get_token({ scope:
|
78
|
+
@token = client.client_credentials.get_token({ scope: scope })
|
65
79
|
end
|
66
80
|
|
67
81
|
def client
|
68
82
|
@client ||= OAuth2::Client.new(client_id, client_secret,
|
69
|
-
site:
|
83
|
+
site: azure_ad_endpoint,
|
70
84
|
authorize_url: "/#{tenant_id}/oauth2/v2.0/authorize",
|
71
85
|
token_url: "/#{tenant_id}/oauth2/v2.0/token",
|
72
86
|
auth_scheme: :basic_auth)
|
@@ -192,7 +206,7 @@ module MailRoom
|
|
192
206
|
end
|
193
207
|
|
194
208
|
def base_url
|
195
|
-
"
|
209
|
+
"#{graph_endpoint}/v1.0/users/#{mailbox.email}/mailFolders/#{mailbox.name}/messages"
|
196
210
|
end
|
197
211
|
|
198
212
|
def unread_messages_url
|
@@ -201,7 +215,7 @@ module MailRoom
|
|
201
215
|
|
202
216
|
def msg_url(id)
|
203
217
|
# Attempting to use the base_url fails with "The OData request is not supported"
|
204
|
-
"
|
218
|
+
"#{graph_endpoint}/v1.0/users/#{mailbox.email}/messages/#{id}"
|
205
219
|
end
|
206
220
|
|
207
221
|
def rfc822_msg_url(id)
|
@@ -212,6 +226,18 @@ module MailRoom
|
|
212
226
|
def log_exception(message, exception)
|
213
227
|
@mailbox.logger.warn({ context: @mailbox.context, message: message, exception: exception.to_s })
|
214
228
|
end
|
229
|
+
|
230
|
+
def scope
|
231
|
+
"#{graph_endpoint}/.default"
|
232
|
+
end
|
233
|
+
|
234
|
+
def graph_endpoint
|
235
|
+
inbox_options[:graph_endpoint] || 'https://graph.microsoft.com'
|
236
|
+
end
|
237
|
+
|
238
|
+
def azure_ad_endpoint
|
239
|
+
inbox_options[:azure_ad_endpoint] || 'https://login.microsoftonline.com'
|
240
|
+
end
|
215
241
|
end
|
216
242
|
end
|
217
243
|
end
|
data/lib/mail_room/version.rb
CHANGED
data/mail_room.gemspec
CHANGED
@@ -18,19 +18,20 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
20
|
gem.add_dependency "net-imap", ">= 0.2.1"
|
21
|
-
gem.add_dependency "oauth2", "
|
21
|
+
gem.add_dependency "oauth2", [">= 1.4.4", "< 3"]
|
22
|
+
gem.add_dependency "jwt", ">= 2.0"
|
22
23
|
|
23
24
|
gem.add_development_dependency "rake"
|
24
25
|
gem.add_development_dependency "rspec", "~> 3.9"
|
25
26
|
gem.add_development_dependency "rubocop", "~> 1.11"
|
26
|
-
gem.add_development_dependency "mocha", "~>
|
27
|
+
gem.add_development_dependency "mocha", "~> 2.0"
|
27
28
|
gem.add_development_dependency "simplecov"
|
28
29
|
|
29
30
|
# for testing delivery methods
|
30
31
|
gem.add_development_dependency "faraday"
|
31
32
|
gem.add_development_dependency "mail"
|
32
33
|
gem.add_development_dependency "letter_opener"
|
33
|
-
gem.add_development_dependency "redis", "~>
|
34
|
+
gem.add_development_dependency "redis", "~> 4"
|
34
35
|
gem.add_development_dependency "redis-namespace"
|
35
36
|
gem.add_development_dependency "pg"
|
36
37
|
gem.add_development_dependency "charlock_holmes"
|
@@ -0,0 +1 @@
|
|
1
|
+
aGVsbG93b3JsZA==
|
@@ -15,6 +15,7 @@ describe MailRoom::Arbitration::Redis do
|
|
15
15
|
|
16
16
|
# Private, but we don't care.
|
17
17
|
let(:redis) { subject.send(:client) }
|
18
|
+
let(:raw_client) { redis._client }
|
18
19
|
|
19
20
|
describe '#deliver?' do
|
20
21
|
context "when called the first time" do
|
@@ -95,7 +96,7 @@ describe MailRoom::Arbitration::Redis do
|
|
95
96
|
it 'client has same specified url' do
|
96
97
|
subject.deliver?(123)
|
97
98
|
|
98
|
-
expect(
|
99
|
+
expect(raw_client.options[:url]).to eq redis_url
|
99
100
|
end
|
100
101
|
|
101
102
|
it 'client is a instance of Redis class' do
|
@@ -137,10 +138,10 @@ describe MailRoom::Arbitration::Redis do
|
|
137
138
|
before { ::Redis::Client::Connector::Sentinel.any_instance.stubs(:resolve).returns(sentinels) }
|
138
139
|
|
139
140
|
it 'client has same specified sentinel params' do
|
140
|
-
expect(
|
141
|
-
expect(
|
142
|
-
expect(
|
143
|
-
expect(
|
141
|
+
expect(raw_client.instance_variable_get(:@connector)).to be_a Redis::Client::Connector::Sentinel
|
142
|
+
expect(raw_client.options[:host]).to eq('sentinel-master')
|
143
|
+
expect(raw_client.options[:password]).to eq('mypassword')
|
144
|
+
expect(raw_client.options[:sentinels]).to eq(sentinels)
|
144
145
|
end
|
145
146
|
end
|
146
147
|
end
|
@@ -4,12 +4,13 @@ require 'mail_room/delivery/letter_opener'
|
|
4
4
|
describe MailRoom::Delivery::LetterOpener do
|
5
5
|
describe '#deliver' do
|
6
6
|
let(:mailbox) {build_mailbox(location: '/tmp/somewhere')}
|
7
|
-
let(:delivery_method) {stub
|
7
|
+
let(:delivery_method) {stub}
|
8
8
|
let(:mail) {stub}
|
9
9
|
|
10
10
|
before :each do
|
11
11
|
Mail.stubs(:read_from_string).returns(mail)
|
12
12
|
::LetterOpener::DeliveryMethod.stubs(:new).returns(delivery_method)
|
13
|
+
delivery_method.stubs(:deliver!)
|
13
14
|
end
|
14
15
|
|
15
16
|
it 'creates a new LetterOpener::DeliveryMethod' do
|
@@ -19,7 +19,8 @@ describe MailRoom::Delivery::Logger do
|
|
19
19
|
let(:mailbox) {build_mailbox(log_path: '/var/log/mail-room.log')}
|
20
20
|
|
21
21
|
it 'creates a new file to append to' do
|
22
|
-
file = stub
|
22
|
+
file = stub
|
23
|
+
file.stubs(:sync=)
|
23
24
|
|
24
25
|
File.expects(:open).with('/var/log/mail-room.log', 'a').returns(file)
|
25
26
|
::Logger.stubs(:new).with(file)
|
@@ -65,11 +65,10 @@ describe MailRoom::Delivery::Postback do
|
|
65
65
|
}
|
66
66
|
})}
|
67
67
|
|
68
|
-
|
69
68
|
let(:delivery_options) {
|
70
69
|
MailRoom::Delivery::Postback::Options.new(mailbox)
|
71
70
|
}
|
72
|
-
|
71
|
+
|
73
72
|
it 'posts the message with faraday' do
|
74
73
|
connection = stub
|
75
74
|
request = stub
|
@@ -82,10 +81,59 @@ describe MailRoom::Delivery::Postback do
|
|
82
81
|
connection.expects(:basic_auth).with('user1', 'password123abc')
|
83
82
|
|
84
83
|
MailRoom::Delivery::Postback.new(delivery_options).deliver('a message')
|
85
|
-
|
84
|
+
|
86
85
|
expect(request.headers['Content-Type']).to eq('text/plain')
|
87
86
|
end
|
88
87
|
end
|
88
|
+
|
89
|
+
context 'with jwt token in the delivery options' do
|
90
|
+
let(:mailbox) {build_mailbox({
|
91
|
+
delivery_options: {
|
92
|
+
url: 'http://localhost/inbox',
|
93
|
+
jwt_auth_header: "Mailroom-Api-Request",
|
94
|
+
jwt_issuer: "mailroom",
|
95
|
+
jwt_algorithm: "HS256",
|
96
|
+
jwt_secret_path: "secret_path"
|
97
|
+
}
|
98
|
+
})}
|
99
|
+
|
100
|
+
let(:delivery_options) {
|
101
|
+
MailRoom::Delivery::Postback::Options.new(mailbox)
|
102
|
+
}
|
103
|
+
|
104
|
+
it 'posts the message with faraday' do
|
105
|
+
connection = stub
|
106
|
+
request = stub
|
107
|
+
Faraday.stubs(:new).returns(connection)
|
108
|
+
|
109
|
+
connection.expects(:post).yields(request).twice
|
110
|
+
request.stubs(:url)
|
111
|
+
request.stubs(:body=)
|
112
|
+
request.stubs(:headers).returns({})
|
113
|
+
|
114
|
+
jwt = stub
|
115
|
+
MailRoom::JWT.expects(:new).with(
|
116
|
+
header: 'Mailroom-Api-Request',
|
117
|
+
issuer: 'mailroom',
|
118
|
+
algorithm: 'HS256',
|
119
|
+
secret_path: 'secret_path'
|
120
|
+
).returns(jwt)
|
121
|
+
jwt.stubs(:valid?).returns(true)
|
122
|
+
jwt.stubs(:header).returns('Mailroom-Api-Request')
|
123
|
+
jwt.stubs(:token).returns('a_jwt_token')
|
124
|
+
|
125
|
+
delivery = MailRoom::Delivery::Postback.new(delivery_options)
|
126
|
+
|
127
|
+
delivery.deliver('a message')
|
128
|
+
expect(request.headers['Mailroom-Api-Request']).to eql('a_jwt_token')
|
129
|
+
|
130
|
+
# A different jwt token for the second time
|
131
|
+
jwt.stubs(:token).returns('another_jwt_token')
|
132
|
+
|
133
|
+
delivery.deliver('another message')
|
134
|
+
expect(request.headers['Mailroom-Api-Request']).to eql('another_jwt_token')
|
135
|
+
end
|
136
|
+
end
|
89
137
|
end
|
90
138
|
end
|
91
139
|
end
|