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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec4ee38465229ec567497afa371c9001ba6b55f3f7b427815d2aeeaaa3916569
4
- data.tar.gz: f98d83bd07c436f6d833c8ed9710a7cfe33d43a0f56891495eb495c75001ac6f
3
+ metadata.gz: 379c92ce022edc809e94ff13a0789fa78166483d0228df03db4a6cbaf7213be4
4
+ data.tar.gz: 1490b43ca949d4ea15240367ef6c6162799cf3648868bfd2982f92688d30f4b0
5
5
  SHA512:
6
- metadata.gz: e7146b00f46e4ba842fad2234caf4f9145263f25aa4b21daaf16b17d5308d9cb7fc028c4cdaeede5e542bc05476bbbf95dff3f9fc5aae35fefca458004cfcd6e
7
- data.tar.gz: c89588a3d43583ce20f1513b8488293f8b2f56b13bd15331276288f4331f2bef59abef77d790836cdcfe298d8af3fc9c4475586c44bb174fb485a6e1f4ac0834
6
+ metadata.gz: 4c03b12b703e68b58fbe0b91a6a3cfea7dff35b3aca33abc74589fb4d51aadcbfd5976d586b7313ce10100a48d615861f510eeb025b4e7931ae1d30fb05ec8bc
7
+ data.tar.gz: 5318027b63f4f04b42270b4d54fcfeb3d69bdce7fcda93a2f431532ea72be96c4e9046be664199a4debeae2cca6b2a4d175cccb850d3c3776783219a3c7309b6
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.2
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
- # request.options[:timeout] = 3
64
- request.headers['Content-Type'] = @delivery_options.content_type unless @delivery_options.content_type.nil?
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
@@ -1,4 +1,4 @@
1
1
  module MailRoom
2
2
  # Current version of gitlab-mail_room gem
3
- VERSION = "0.0.15"
3
+ VERSION = "0.0.16"
4
4
  end
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.15
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-14 00:00:00.000000000 Z
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.4
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