gitlab-mail_room 0.0.15 → 0.0.16
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/.ruby-version +1 -1
- data/README.md +11 -0
- data/lib/mail_room/delivery/postback.rb +36 -4
- data/lib/mail_room/jwt.rb +33 -0
- data/lib/mail_room/version.rb +1 -1
- data/mail_room.gemspec +1 -0
- data/spec/fixtures/jwt_secret +1 -0
- data/spec/lib/delivery/postback_spec.rb +51 -3
- data/spec/lib/jwt_spec.rb +79 -0
- metadata +25 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 379c92ce022edc809e94ff13a0789fa78166483d0228df03db4a6cbaf7213be4
|
4
|
+
data.tar.gz: 1490b43ca949d4ea15240367ef6c6162799cf3648868bfd2982f92688d30f4b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c03b12b703e68b58fbe0b91a6a3cfea7dff35b3aca33abc74589fb4d51aadcbfd5976d586b7313ce10100a48d615861f510eeb025b4e7931ae1d30fb05ec8bc
|
7
|
+
data.tar.gz: 5318027b63f4f04b42270b4d54fcfeb3d69bdce7fcda93a2f431532ea72be96c4e9046be664199a4debeae2cca6b2a4d175cccb850d3c3776783219a3c7309b6
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.7.
|
1
|
+
2.7.5
|
data/README.md
CHANGED
@@ -138,6 +138,17 @@ You will also need to install `faraday` or `letter_opener` if you use the `postb
|
|
138
138
|
:delivery_options:
|
139
139
|
:redis_url: redis://localhost:6379
|
140
140
|
:worker: EmailReceiverWorker
|
141
|
+
-
|
142
|
+
:email: "user8@gmail.com"
|
143
|
+
:password: "password"
|
144
|
+
:name: "inbox"
|
145
|
+
:delivery_method: postback
|
146
|
+
:delivery_options:
|
147
|
+
:delivery_url: "http://localhost:3000/inbox"
|
148
|
+
:jwt_auth_header: "Mailroom-Api-Request"
|
149
|
+
:jwt_issuer: "mailroom"
|
150
|
+
:jwt_algorithm: "HS256"
|
151
|
+
:jwt_secret_path: "/etc/secrets/mailroom/.mailroom_secret"
|
141
152
|
```
|
142
153
|
|
143
154
|
**Note:** If using `delete_after_delivery`, you also probably want to use
|
@@ -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,6 +18,8 @@ module MailRoom
|
|
17
18
|
mailbox.delivery_options[:delivery_token] ||
|
18
19
|
mailbox.delivery_options[:token]
|
19
20
|
|
21
|
+
jwt = initialize_jwt(mailbox.delivery_options)
|
22
|
+
|
20
23
|
username = mailbox.delivery_options[:username]
|
21
24
|
password = mailbox.delivery_options[:password]
|
22
25
|
|
@@ -24,16 +27,31 @@ module MailRoom
|
|
24
27
|
|
25
28
|
content_type = mailbox.delivery_options[:content_type]
|
26
29
|
|
27
|
-
super(url, token, username, password, logger, content_type)
|
30
|
+
super(url, token, username, password, logger, content_type, jwt)
|
28
31
|
end
|
29
32
|
|
30
33
|
def token_auth?
|
31
34
|
!self[:token].nil?
|
32
35
|
end
|
33
36
|
|
37
|
+
def jwt_auth?
|
38
|
+
self[:jwt].valid?
|
39
|
+
end
|
40
|
+
|
34
41
|
def basic_auth?
|
35
42
|
!self[:username].nil? && !self[:password].nil?
|
36
43
|
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def initialize_jwt(delivery_options)
|
48
|
+
::MailRoom::JWT.new(
|
49
|
+
header: delivery_options[:jwt_auth_header],
|
50
|
+
secret_path: delivery_options[:jwt_secret_path],
|
51
|
+
algorithm: delivery_options[:jwt_algorithm],
|
52
|
+
issuer: delivery_options[:jwt_issuer]
|
53
|
+
)
|
54
|
+
end
|
37
55
|
end
|
38
56
|
|
39
57
|
# Build a new delivery, hold the delivery options
|
@@ -60,13 +78,27 @@ module MailRoom
|
|
60
78
|
connection.post do |request|
|
61
79
|
request.url @delivery_options.url
|
62
80
|
request.body = message
|
63
|
-
|
64
|
-
request
|
81
|
+
config_request_content_type(request)
|
82
|
+
config_request_jwt_auth(request)
|
65
83
|
end
|
66
84
|
|
67
85
|
@delivery_options.logger.info({ delivery_method: 'Postback', action: 'message pushed', url: @delivery_options.url })
|
68
86
|
true
|
69
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def config_request_content_type(request)
|
92
|
+
return if @delivery_options.content_type.nil?
|
93
|
+
|
94
|
+
request.headers['Content-Type'] = @delivery_options.content_type
|
95
|
+
end
|
96
|
+
|
97
|
+
def config_request_jwt_auth(request)
|
98
|
+
return unless @delivery_options.jwt_auth?
|
99
|
+
|
100
|
+
request.headers[@delivery_options.jwt.header] = @delivery_options.jwt.token
|
101
|
+
end
|
70
102
|
end
|
71
103
|
end
|
72
104
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module MailRoom
|
7
|
+
# Responsible for validating and generating JWT token
|
8
|
+
class JWT
|
9
|
+
DEFAULT_ISSUER = 'mailroom'
|
10
|
+
DEFAULT_ALGORITHM = 'HS256'
|
11
|
+
|
12
|
+
attr_reader :header, :secret_path, :issuer, :algorithm
|
13
|
+
|
14
|
+
def initialize(header:, secret_path:, issuer:, algorithm:)
|
15
|
+
@header = header
|
16
|
+
@secret_path = secret_path
|
17
|
+
@issuer = issuer || DEFAULT_ISSUER
|
18
|
+
@algorithm = algorithm || DEFAULT_ALGORITHM
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
[@header, @secret_path, @issuer, @algorithm].none?(&:nil?)
|
23
|
+
end
|
24
|
+
|
25
|
+
def token
|
26
|
+
return nil unless valid?
|
27
|
+
|
28
|
+
secret = Base64.strict_decode64(File.read(@secret_path).chomp)
|
29
|
+
payload = { nonce: SecureRandom.hex(12), iss: @issuer }
|
30
|
+
::JWT.encode payload, secret, @algorithm
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/mail_room/version.rb
CHANGED
data/mail_room.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_dependency "net-imap", ">= 0.2.1"
|
21
21
|
gem.add_dependency "oauth2", "~> 1.4.4"
|
22
|
+
gem.add_dependency "jwt", ">= 2.0"
|
22
23
|
|
23
24
|
# Pinning io-wait to 0.1.0, which is the last version to support Ruby < 3
|
24
25
|
gem.add_dependency "io-wait", "~> 0.1.0"
|
@@ -0,0 +1 @@
|
|
1
|
+
aGVsbG93b3JsZA==
|
@@ -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
|
@@ -0,0 +1,79 @@
|
|
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_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
|
+
),
|
36
|
+
{ 'alg' => 'HS256' }
|
37
|
+
]
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'generates a different token for each invocation' do
|
42
|
+
expect(jwt.token).not_to eql(jwt.token)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#valid?' do
|
47
|
+
it 'returns true if all essential components are present' do
|
48
|
+
jwt = described_class.new(**standard_config)
|
49
|
+
expect(jwt.valid?).to eql(true)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns true if header and secret path are present' do
|
53
|
+
jwt = described_class.new(
|
54
|
+
secret_path: secret_path,
|
55
|
+
header: 'Mailroom-Api-Request',
|
56
|
+
issuer: nil,
|
57
|
+
algorithm: nil
|
58
|
+
)
|
59
|
+
expect(jwt.valid?).to eql(true)
|
60
|
+
expect(jwt.issuer).to eql(described_class::DEFAULT_ISSUER)
|
61
|
+
expect(jwt.algorithm).to eql(described_class::DEFAULT_ALGORITHM)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns false if either header or secret_path are missing' do
|
65
|
+
expect(described_class.new(
|
66
|
+
secret_path: nil,
|
67
|
+
header: 'Mailroom-Api-Request',
|
68
|
+
issuer: nil,
|
69
|
+
algorithm: nil
|
70
|
+
).valid?).to eql(false)
|
71
|
+
expect(described_class.new(
|
72
|
+
secret_path: secret_path,
|
73
|
+
header: nil,
|
74
|
+
issuer: nil,
|
75
|
+
algorithm: nil
|
76
|
+
).valid?).to eql(false)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-mail_room
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Pitale
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-imap
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.4.4
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: jwt
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: io-wait
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -291,6 +305,7 @@ files:
|
|
291
305
|
- lib/mail_room/imap.rb
|
292
306
|
- lib/mail_room/imap/connection.rb
|
293
307
|
- lib/mail_room/imap/message.rb
|
308
|
+
- lib/mail_room/jwt.rb
|
294
309
|
- lib/mail_room/logger/structured.rb
|
295
310
|
- lib/mail_room/mailbox.rb
|
296
311
|
- lib/mail_room/mailbox_watcher.rb
|
@@ -300,6 +315,7 @@ files:
|
|
300
315
|
- lib/mail_room/version.rb
|
301
316
|
- logfile.log
|
302
317
|
- mail_room.gemspec
|
318
|
+
- spec/fixtures/jwt_secret
|
303
319
|
- spec/fixtures/test_config.yml
|
304
320
|
- spec/lib/arbitration/redis_spec.rb
|
305
321
|
- spec/lib/cli_spec.rb
|
@@ -314,6 +330,7 @@ files:
|
|
314
330
|
- spec/lib/health_check_spec.rb
|
315
331
|
- spec/lib/imap/connection_spec.rb
|
316
332
|
- spec/lib/imap/message_spec.rb
|
333
|
+
- spec/lib/jwt_spec.rb
|
317
334
|
- spec/lib/logger/structured_spec.rb
|
318
335
|
- spec/lib/mailbox_spec.rb
|
319
336
|
- spec/lib/mailbox_watcher_spec.rb
|
@@ -323,7 +340,7 @@ files:
|
|
323
340
|
homepage: http://github.com/tpitale/mail_room
|
324
341
|
licenses: []
|
325
342
|
metadata: {}
|
326
|
-
post_install_message:
|
343
|
+
post_install_message:
|
327
344
|
rdoc_options: []
|
328
345
|
require_paths:
|
329
346
|
- lib
|
@@ -338,12 +355,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
338
355
|
- !ruby/object:Gem::Version
|
339
356
|
version: '0'
|
340
357
|
requirements: []
|
341
|
-
rubygems_version: 3.1.
|
342
|
-
signing_key:
|
358
|
+
rubygems_version: 3.1.6
|
359
|
+
signing_key:
|
343
360
|
specification_version: 4
|
344
361
|
summary: mail_room will proxy email (gmail) from IMAP to a callback URL, logger, or
|
345
362
|
letter_opener
|
346
363
|
test_files:
|
364
|
+
- spec/fixtures/jwt_secret
|
347
365
|
- spec/fixtures/test_config.yml
|
348
366
|
- spec/lib/arbitration/redis_spec.rb
|
349
367
|
- spec/lib/cli_spec.rb
|
@@ -358,6 +376,7 @@ test_files:
|
|
358
376
|
- spec/lib/health_check_spec.rb
|
359
377
|
- spec/lib/imap/connection_spec.rb
|
360
378
|
- spec/lib/imap/message_spec.rb
|
379
|
+
- spec/lib/jwt_spec.rb
|
361
380
|
- spec/lib/logger/structured_spec.rb
|
362
381
|
- spec/lib/mailbox_spec.rb
|
363
382
|
- spec/lib/mailbox_watcher_spec.rb
|