gitlab-mail_room 0.0.15 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
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