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.
@@ -4,6 +4,7 @@ require 'mail_room/delivery/sidekiq'
4
4
  describe MailRoom::Delivery::Sidekiq do
5
5
  subject { described_class.new(options) }
6
6
  let(:redis) { subject.send(:client) }
7
+ let(:raw_client) { redis._client }
7
8
  let(:options) { MailRoom::Delivery::Sidekiq::Options.new(mailbox) }
8
9
 
9
10
  describe '#options' do
@@ -20,7 +21,7 @@ describe MailRoom::Delivery::Sidekiq do
20
21
 
21
22
  context 'with simple redis url' do
22
23
  it 'client has same specified redis_url' do
23
- expect(redis.client.options[:url]).to eq(redis_url)
24
+ expect(raw_client.options[:url]).to eq(redis_url)
24
25
  end
25
26
 
26
27
  it 'client is a instance of RedisNamespace class' do
@@ -39,10 +40,9 @@ describe MailRoom::Delivery::Sidekiq do
39
40
  end
40
41
 
41
42
  it 'client has correct redis_url' do
42
- expect(redis.client.options[:url]).to eq(redis_url)
43
+ expect(raw_client.options[:url]).to eq(redis_url)
43
44
  end
44
45
 
45
-
46
46
  it 'connection has correct values' do
47
47
  expect(redis.connection[:host]).to eq('localhost')
48
48
  expect(redis.connection[:db]).to eq(4)
@@ -87,10 +87,10 @@ describe MailRoom::Delivery::Sidekiq do
87
87
  before { ::Redis::Client::Connector::Sentinel.any_instance.stubs(:resolve).returns(sentinels) }
88
88
 
89
89
  it 'client has same specified sentinel params' do
90
- expect(redis.client.instance_variable_get(:@connector)).to be_a Redis::Client::Connector::Sentinel
91
- expect(redis.client.options[:host]).to eq('sentinel-master')
92
- expect(redis.client.options[:password]).to eq('mypassword')
93
- expect(redis.client.options[:sentinels]).to eq(sentinels)
90
+ expect(raw_client.instance_variable_get(:@connector)).to be_a Redis::Client::Connector::Sentinel
91
+ expect(raw_client.options[:host]).to eq('sentinel-master')
92
+ expect(raw_client.options[:password]).to eq('mypassword')
93
+ expect(raw_client.options[:sentinels]).to eq(sentinels)
94
94
  end
95
95
  end
96
96
 
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ require 'mail_room/jwt'
4
+
5
+ describe MailRoom::JWT do
6
+ let(:secret_path) { File.expand_path('../fixtures/jwt_secret', File.dirname(__FILE__)) }
7
+ let(:secret) { Base64.strict_decode64(File.read(secret_path).chomp) }
8
+
9
+ let(:standard_config) do
10
+ {
11
+ secret_path: secret_path,
12
+ issuer: 'mailroom',
13
+ header: 'Mailroom-Api-Request',
14
+ algorithm: 'HS256'
15
+ }
16
+ end
17
+
18
+ describe '#token' do
19
+ let(:jwt) { described_class.new(**standard_config) }
20
+
21
+ it 'generates a valid jwt token' do
22
+ token = jwt.token
23
+ expect(token).not_to be_empty
24
+
25
+ payload = nil
26
+ expect do
27
+ payload = JWT.decode(token, secret, true, iss: 'mailroom', verify_iat: true, verify_iss: true, algorithm: 'HS256')
28
+ end.not_to raise_error
29
+ expect(payload).to be_an(Array)
30
+ expect(payload).to match(
31
+ [
32
+ a_hash_including(
33
+ 'iss' => 'mailroom',
34
+ 'nonce' => be_a(String),
35
+ 'iat' => be_a(Integer)
36
+ ),
37
+ { 'alg' => 'HS256' }
38
+ ]
39
+ )
40
+ end
41
+
42
+ it 'generates a different token for each invocation' do
43
+ expect(jwt.token).not_to eql(jwt.token)
44
+ end
45
+ end
46
+
47
+ describe '#valid?' do
48
+ it 'returns true if all essential components are present' do
49
+ jwt = described_class.new(**standard_config)
50
+ expect(jwt.valid?).to eql(true)
51
+ end
52
+
53
+ it 'returns true if header and secret path are present' do
54
+ jwt = described_class.new(
55
+ secret_path: secret_path,
56
+ header: 'Mailroom-Api-Request',
57
+ issuer: nil,
58
+ algorithm: nil
59
+ )
60
+ expect(jwt.valid?).to eql(true)
61
+ expect(jwt.issuer).to eql(described_class::DEFAULT_ISSUER)
62
+ expect(jwt.algorithm).to eql(described_class::DEFAULT_ALGORITHM)
63
+ end
64
+
65
+ it 'returns false if either header or secret_path are missing' do
66
+ expect(described_class.new(
67
+ secret_path: nil,
68
+ header: 'Mailroom-Api-Request',
69
+ issuer: nil,
70
+ algorithm: nil
71
+ ).valid?).to eql(false)
72
+ expect(described_class.new(
73
+ secret_path: secret_path,
74
+ header: nil,
75
+ issuer: nil,
76
+ algorithm: nil
77
+ ).valid?).to eql(false)
78
+ end
79
+ end
80
+ end
@@ -14,37 +14,58 @@ describe MailRoom::MicrosoftGraph::Connection do
14
14
  }.merge(REQUIRED_MICROSOFT_GRAPH_DEFAULTS)
15
15
  end
16
16
  let(:mailbox) { build_mailbox(options) }
17
- let(:base_url) { 'https://graph.microsoft.com/v1.0/users/user@example.com/mailFolders/inbox/messages' }
18
- let(:message_base_url) { 'https://graph.microsoft.com/v1.0/users/user@example.com/messages' }
17
+ let(:graph_endpoint) { 'https://graph.microsoft.com' }
18
+ let(:azure_ad_endpoint) { 'https://login.microsoftonline.com' }
19
+ let(:base_url) { "#{graph_endpoint}/v1.0/users/user@example.com/mailFolders/inbox/messages" }
20
+ let(:message_base_url) { "#{graph_endpoint}/v1.0/users/user@example.com/messages" }
21
+
22
+ let(:connection) { described_class.new(mailbox) }
23
+ let(:uid) { 1 }
24
+ let(:access_token) { SecureRandom.hex }
25
+ let(:refresh_token) { SecureRandom.hex }
26
+ let(:expires_in) { Time.now + 3600 }
27
+ let(:unread_messages_body) { '' }
28
+ let(:status) { 200 }
29
+ let!(:stub_token) do
30
+ stub_request(:post, "#{azure_ad_endpoint}/#{tenant_id}/oauth2/v2.0/token").to_return(
31
+ body: { 'access_token' => access_token, 'refresh_token' => refresh_token, 'expires_in' => expires_in }.to_json,
32
+ headers: { 'Content-Type' => 'application/json' }
33
+ )
34
+ end
35
+ let!(:stub_unread_messages_request) do
36
+ stub_request(:get, "#{base_url}?$filter=isRead%20eq%20false").to_return(
37
+ status: status,
38
+ body: unread_messages_body.to_json,
39
+ headers: { 'Content-Type' => 'application/json' }
40
+ )
41
+ end
19
42
 
20
43
  before do
21
44
  WebMock.enable!
22
45
  end
23
46
 
24
- context '#wait' do
25
- let(:connection) { described_class.new(mailbox) }
26
- let(:uid) { 1 }
27
- let(:access_token) { SecureRandom.hex }
28
- let(:refresh_token) { SecureRandom.hex }
29
- let(:expires_in) { Time.now + 3600 }
30
- let(:unread_messages_body) { '' }
31
- let(:status) { 200 }
32
- let!(:stub_token) do
33
- stub_request(:post, "https://login.microsoftonline.com/#{tenant_id}/oauth2/v2.0/token").to_return(
34
- body: { 'access_token' => access_token, 'refresh_token' => refresh_token, 'expires_in' => expires_in }.to_json,
35
- headers: { 'Content-Type' => 'application/json' }
36
- )
47
+ context '#quit' do
48
+ it 'returns false' do
49
+ expect(connection.stopped?).to be_falsey
37
50
  end
38
- let!(:stub_unread_messages_request) do
39
- stub_request(:get, "#{base_url}?$filter=isRead%20eq%20false").to_return(
40
- status: status,
41
- body: unread_messages_body.to_json,
42
- headers: { 'Content-Type' => 'application/json' }
43
- )
51
+
52
+ it 'returns true' do
53
+ connection.quit
54
+
55
+ expect(connection.stopped?).to be_truthy
44
56
  end
45
57
 
58
+ it 'does not attempt to process the mailbox' do
59
+ connection.quit
60
+
61
+ connection.expects(:process_mailbox).times(0)
62
+ connection.wait
63
+ end
64
+ end
65
+
66
+ context '#wait' do
46
67
  before do
47
- connection.stubs(:wait_for_new_messages)
68
+ connection.stubs(:do_sleep)
48
69
  end
49
70
 
50
71
  describe 'poll interval' do
@@ -52,6 +73,12 @@ describe MailRoom::MicrosoftGraph::Connection do
52
73
  expect(connection.send(:poll_interval)).to eq(60)
53
74
  end
54
75
 
76
+ it 'calls do_sleep 60 times' do
77
+ connection.expects(:do_sleep).with(1).times(60)
78
+
79
+ connection.wait
80
+ end
81
+
55
82
  context 'interval set to 10' do
56
83
  let(:options) do
57
84
  {
@@ -68,10 +95,16 @@ describe MailRoom::MicrosoftGraph::Connection do
68
95
  it 'sets the poll interval to 10' do
69
96
  expect(connection.send(:poll_interval)).to eq(10)
70
97
  end
98
+
99
+ it 'calls do_sleep 10 times' do
100
+ connection.expects(:do_sleep).with(1).times(10)
101
+
102
+ connection.wait
103
+ end
71
104
  end
72
105
  end
73
106
 
74
- context 'with a single message' do
107
+ shared_examples 'with a single message' do
75
108
  let(:message_id) { SecureRandom.hex }
76
109
  let(:unread_messages_body) { { value: ['id' => message_id] } }
77
110
  let(:message_url) { "#{message_base_url}/#{message_id}" }
@@ -103,9 +136,38 @@ describe MailRoom::MicrosoftGraph::Connection do
103
136
  end
104
137
  end
105
138
 
139
+ context 'with default Azure settings' do
140
+ before do
141
+ puts options
142
+ end
143
+ it_behaves_like 'with a single message'
144
+ end
145
+
146
+ # https://docs.microsoft.com/en-us/graph/deployments
147
+ context 'with an alternative Azure deployment' do
148
+ let(:graph_endpoint) { 'https://graph.microsoft.us' }
149
+ let(:azure_ad_endpoint) { 'https://login.microsoftonline.us' }
150
+ let(:options) do
151
+ {
152
+ inbox_method: :microsoft_graph,
153
+ delete_after_delivery: true,
154
+ expunge_deleted: true,
155
+ inbox_options: {
156
+ tenant_id: '98776',
157
+ client_id: '12345',
158
+ client_secret: 'MY-SECRET',
159
+ graph_endpoint: 'https://graph.microsoft.us',
160
+ azure_ad_endpoint: 'https://login.microsoftonline.us'
161
+ }
162
+ }
163
+ end
164
+
165
+ it_behaves_like 'with a single message'
166
+ end
167
+
106
168
  context 'with multiple pages of messages' do
107
169
  let(:message_ids) { [SecureRandom.hex, SecureRandom.hex] }
108
- let(:next_page_url) { 'https://graph.microsoft.com/v1.0/nextPage' }
170
+ let(:next_page_url) { "#{graph_endpoint}/v1.0/nextPage" }
109
171
  let(:unread_messages_body) { { value: ['id' => message_ids.first], '@odata.nextLink' => next_page_url } }
110
172
  let(:message_body) { 'hello world' }
111
173
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_room
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Pitale
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-13 00:00:00.000000000 Z
11
+ date: 2024-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-imap
@@ -28,16 +28,36 @@ dependencies:
28
28
  name: oauth2
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.4.4
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3'
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: 1.4.4
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3'
47
+ - !ruby/object:Gem::Dependency
48
+ name: jwt
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '2.0'
41
61
  - !ruby/object:Gem::Dependency
42
62
  name: rake
43
63
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +106,14 @@ dependencies:
86
106
  requirements:
87
107
  - - "~>"
88
108
  - !ruby/object:Gem::Version
89
- version: '1.11'
109
+ version: '2.0'
90
110
  type: :development
91
111
  prerelease: false
92
112
  version_requirements: !ruby/object:Gem::Requirement
93
113
  requirements:
94
114
  - - "~>"
95
115
  - !ruby/object:Gem::Version
96
- version: '1.11'
116
+ version: '2.0'
97
117
  - !ruby/object:Gem::Dependency
98
118
  name: simplecov
99
119
  requirement: !ruby/object:Gem::Requirement
@@ -156,14 +176,14 @@ dependencies:
156
176
  requirements:
157
177
  - - "~>"
158
178
  - !ruby/object:Gem::Version
159
- version: 3.3.1
179
+ version: '4'
160
180
  type: :development
161
181
  prerelease: false
162
182
  version_requirements: !ruby/object:Gem::Requirement
163
183
  requirements:
164
184
  - - "~>"
165
185
  - !ruby/object:Gem::Version
166
- version: 3.3.1
186
+ version: '4'
167
187
  - !ruby/object:Gem::Dependency
168
188
  name: redis-namespace
169
189
  requirement: !ruby/object:Gem::Requirement
@@ -228,12 +248,14 @@ executables:
228
248
  extensions: []
229
249
  extra_rdoc_files: []
230
250
  files:
251
+ - ".github/dependabot.yml"
252
+ - ".github/workflows/ci.yml"
231
253
  - ".gitignore"
232
254
  - ".gitlab-ci.yml"
233
255
  - ".rubocop.yml"
234
256
  - ".rubocop_todo.yml"
235
257
  - ".ruby-version"
236
- - ".travis.yml"
258
+ - ".tool-versions"
237
259
  - CHANGELOG.md
238
260
  - CODE_OF_CONDUCT.md
239
261
  - Gemfile
@@ -260,6 +282,7 @@ files:
260
282
  - lib/mail_room/imap.rb
261
283
  - lib/mail_room/imap/connection.rb
262
284
  - lib/mail_room/imap/message.rb
285
+ - lib/mail_room/jwt.rb
263
286
  - lib/mail_room/logger/structured.rb
264
287
  - lib/mail_room/mailbox.rb
265
288
  - lib/mail_room/mailbox_watcher.rb
@@ -269,6 +292,7 @@ files:
269
292
  - lib/mail_room/version.rb
270
293
  - logfile.log
271
294
  - mail_room.gemspec
295
+ - spec/fixtures/jwt_secret
272
296
  - spec/fixtures/test_config.yml
273
297
  - spec/lib/arbitration/redis_spec.rb
274
298
  - spec/lib/cli_spec.rb
@@ -282,6 +306,7 @@ files:
282
306
  - spec/lib/delivery/sidekiq_spec.rb
283
307
  - spec/lib/imap/connection_spec.rb
284
308
  - spec/lib/imap/message_spec.rb
309
+ - spec/lib/jwt_spec.rb
285
310
  - spec/lib/logger/structured_spec.rb
286
311
  - spec/lib/mailbox_spec.rb
287
312
  - spec/lib/mailbox_watcher_spec.rb
@@ -306,12 +331,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
306
331
  - !ruby/object:Gem::Version
307
332
  version: '0'
308
333
  requirements: []
309
- rubygems_version: 3.0.3
334
+ rubygems_version: 3.5.5
310
335
  signing_key:
311
336
  specification_version: 4
312
337
  summary: mail_room will proxy email (gmail) from IMAP to a callback URL, logger, or
313
338
  letter_opener
314
339
  test_files:
340
+ - spec/fixtures/jwt_secret
315
341
  - spec/fixtures/test_config.yml
316
342
  - spec/lib/arbitration/redis_spec.rb
317
343
  - spec/lib/cli_spec.rb
@@ -325,6 +351,7 @@ test_files:
325
351
  - spec/lib/delivery/sidekiq_spec.rb
326
352
  - spec/lib/imap/connection_spec.rb
327
353
  - spec/lib/imap/message_spec.rb
354
+ - spec/lib/jwt_spec.rb
328
355
  - spec/lib/logger/structured_spec.rb
329
356
  - spec/lib/mailbox_spec.rb
330
357
  - spec/lib/mailbox_watcher_spec.rb
data/.travis.yml DELETED
@@ -1,17 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.5
4
- - 2.6
5
- - 2.7
6
- - 3.0
7
- - truffleruby
8
- services:
9
- - redis
10
- script:
11
- - bundle exec rspec spec
12
-
13
- jobs:
14
- - language: ruby
15
- rvm: 2.7
16
- script:
17
- - bundle exec rubocop