gitlab-mail_room 0.0.9 → 0.0.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab/issue_templates/Default.md +9 -0
  3. data/.gitlab/issue_templates/Release.md +1 -0
  4. data/.gitlab-ci.yml +14 -24
  5. data/.rubocop.yml +5 -0
  6. data/.rubocop_todo.yml +494 -0
  7. data/.ruby-version +1 -1
  8. data/.travis.yml +12 -5
  9. data/CHANGELOG.md +4 -0
  10. data/CONTRIBUTING.md +40 -0
  11. data/README.md +125 -14
  12. data/Rakefile +1 -1
  13. data/lib/mail_room/arbitration/redis.rb +1 -1
  14. data/lib/mail_room/connection.rb +6 -1
  15. data/lib/mail_room/crash_handler.rb +2 -1
  16. data/lib/mail_room/delivery/letter_opener.rb +1 -1
  17. data/lib/mail_room/delivery/postback.rb +42 -6
  18. data/lib/mail_room/delivery/que.rb +15 -1
  19. data/lib/mail_room/delivery/sidekiq.rb +4 -3
  20. data/lib/mail_room/jwt.rb +39 -0
  21. data/lib/mail_room/logger/structured.rb +15 -1
  22. data/lib/mail_room/mailbox.rb +56 -17
  23. data/lib/mail_room/mailbox_watcher.rb +7 -1
  24. data/lib/mail_room/microsoft_graph/connection.rb +243 -0
  25. data/lib/mail_room/microsoft_graph.rb +7 -0
  26. data/lib/mail_room/version.rb +1 -1
  27. data/mail_room.gemspec +7 -1
  28. data/spec/fixtures/jwt_secret +1 -0
  29. data/spec/lib/arbitration/redis_spec.rb +6 -5
  30. data/spec/lib/cli_spec.rb +3 -3
  31. data/spec/lib/configuration_spec.rb +1 -1
  32. data/spec/lib/delivery/letter_opener_spec.rb +4 -3
  33. data/spec/lib/delivery/logger_spec.rb +3 -2
  34. data/spec/lib/delivery/postback_spec.rb +62 -14
  35. data/spec/lib/delivery/sidekiq_spec.rb +33 -11
  36. data/spec/lib/jwt_spec.rb +80 -0
  37. data/spec/lib/logger/structured_spec.rb +34 -2
  38. data/spec/lib/mailbox_spec.rb +65 -17
  39. data/spec/lib/mailbox_watcher_spec.rb +54 -38
  40. data/spec/lib/microsoft_graph/connection_spec.rb +252 -0
  41. data/spec/spec_helper.rb +14 -3
  42. metadata +97 -8
data/README.md CHANGED
@@ -20,7 +20,14 @@ The fork is useful as we can post quick fixes to our own fork and release fixes
20
20
 
21
21
  ## README
22
22
 
23
- mail_room is a configuration based process that will idle on IMAP connections and execute a delivery method when a new message is received. Examples of delivery methods include:
23
+ mail_room is a configuration based process that will listen for incoming
24
+ e-mail and execute a delivery method when a new message is
25
+ received. mail_room supports the following methods for receiving e-mail:
26
+
27
+ * IMAP
28
+ * [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/api/resources/mail-api-overview?view=graph-rest-1.0)
29
+
30
+ Examples of delivery methods include:
24
31
 
25
32
  * POST to a delivery URL (Postback)
26
33
  * Queue a job to Sidekiq or Que for later processing (Sidekiq or Que)
@@ -56,8 +63,8 @@ You will also need to install `faraday` or `letter_opener` if you use the `postb
56
63
  ```yaml
57
64
  ---
58
65
  :health_check:
59
- - :address: "127.0.0.1"
60
- :port: 8080
66
+ :address: "127.0.0.1"
67
+ :port: 8080
61
68
  :mailboxes:
62
69
  -
63
70
  :email: "user1@gmail.com"
@@ -117,6 +124,33 @@ You will also need to install `faraday` or `letter_opener` if you use the `postb
117
124
  :host: 127.0.0.1
118
125
  :port: 26379
119
126
  :worker: EmailReceiverWorker
127
+ -
128
+ :email: "user7@outlook365.com"
129
+ :password: "password"
130
+ :name: "inbox"
131
+ :inbox_method: microsoft_graph
132
+ :inbox_options:
133
+ :tenant_id: 12345
134
+ :client_id: ABCDE
135
+ :client_secret: YOUR-SECRET-HERE
136
+ :poll_interval: 60
137
+ :azure_ad_endpoint: https://login.microsoftonline.com
138
+ :graph_endpoint: https://graph.microsoft.com
139
+ :delivery_method: sidekiq
140
+ :delivery_options:
141
+ :redis_url: redis://localhost:6379
142
+ :worker: EmailReceiverWorker
143
+ -
144
+ :email: "user8@gmail.com"
145
+ :password: "password"
146
+ :name: "inbox"
147
+ :delivery_method: postback
148
+ :delivery_options:
149
+ :delivery_url: "http://localhost:3000/inbox"
150
+ :jwt_auth_header: "Mailroom-Api-Request"
151
+ :jwt_issuer: "mailroom"
152
+ :jwt_algorithm: "HS256"
153
+ :jwt_secret_path: "/etc/secrets/mailroom/.mailroom_secret"
120
154
  ```
121
155
 
122
156
  **Note:** If using `delete_after_delivery`, you also probably want to use
@@ -134,6 +168,91 @@ the server is running. Otherwise, it returns a 500 status code.
134
168
 
135
169
  This feature is not included in upstream `mail_room` and is specific to GitLab.
136
170
 
171
+ ## inbox_method
172
+
173
+ By default, IMAP mode is assumed for reading a mailbox.
174
+
175
+ ### IMAP Server Configuration ##
176
+
177
+ You can set per-mailbox configuration for the IMAP server's `host` (default: 'imap.gmail.com'), `port` (default: 993), `ssl` (default: true), and `start_tls` (default: false).
178
+
179
+ If you want to set additional options for IMAP SSL you can pass a YAML hash to match [SSLContext#set_params](http://docs.ruby-lang.org/en/2.2.0/OpenSSL/SSL/SSLContext.html#method-i-set_params). If you set `verify_mode` to `:none` it'll replace with the appropriate constant.
180
+
181
+ If you're seeing the error `Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)`, you need to configure your Gmail account to allow less secure apps to access it: https://support.google.com/accounts/answer/6010255.
182
+
183
+ ### Microsoft Graph configuration
184
+
185
+ To use the Microsoft Graph API instead of IMAP to read e-mail, you will
186
+ need to create an application in the Azure Active Directory. See the
187
+ [Microsoft instructions](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) for more details:
188
+
189
+ 1. Sign in to the [Azure portal](https://portal.azure.com).
190
+ 1. Search for and select `Azure Active Directory`.
191
+ 1. Under `Manage`, select `App registrations` > `New registration`.
192
+ 1. Enter a `Name` for your application, such as `MailRoom`. Users of your app might see this name, and you can change it later.
193
+ 1. If `Supported account types` is listed, select the appropriate option.
194
+ 1. Leave `Redirect URI` blank. This is not needed.
195
+ 1. Select `Register`.
196
+ 1. Under `Manage`, select `Certificates & secrets`.
197
+ 1. Under `Client secrets`, select `New client secret`, and enter a name.
198
+ 1. Under `Expires`, select `Never`, unless you plan on updating the credentials every time it expires.
199
+ 1. Select `Add`. Record the secret value in a safe location for use in a later step.
200
+ 1. Under `Manage`, select `API Permissions` > `Add a permission`. Select `Microsoft Graph`.
201
+ 1. Select `Application permissions`.
202
+ 1. Under the `Mail` node, select `Mail.ReadWrite`, and then select Add permissions.
203
+ 1. If `User.Read` is listed in the permission list, you can delete this.
204
+ 1. Click `Grant admin consent` for these permissions.
205
+
206
+ #### Restrict mailbox access
207
+
208
+ Note that for MailRoom to work as a service account, this application
209
+ must have the `Mail.ReadWrite` to read/write mail in *all*
210
+ mailboxes. However, while this appears to be security risk,
211
+ we can configure an application access policy to limit the
212
+ mailbox access for this account. [Follow these instructions](https://docs.microsoft.com/en-us/graph/auth-limit-mailbox-access)
213
+ to setup PowerShell and configure this policy.
214
+
215
+ #### MailRoom config for Microsoft Graph
216
+
217
+ In the MailRoom configuration, set `inbox_method` to `microsoft_graph`.
218
+ You will also need:
219
+
220
+ * The client and tenant ID from the `Overview` section in the Azure app page
221
+ * The client secret created earlier
222
+
223
+ Fill in `inbox_options` with these values:
224
+
225
+ ```yaml
226
+ :inbox_method: microsoft_graph
227
+ :inbox_options:
228
+ :tenant_id: 12345
229
+ :client_id: ABCDE
230
+ :client_secret: YOUR-SECRET-HERE
231
+ :poll_interval: 60
232
+ ```
233
+
234
+ By default, MailRoom will poll for new messages every 60 seconds. `poll_interval` configures the number of
235
+ seconds to poll. Setting the value to 0 or under will default to 60 seconds.
236
+
237
+ ### Alternative Azure cloud deployments
238
+
239
+ MailRoom will default to using the standard Azure HTTPS endpoints. To
240
+ configure MailRoom with Microsoft Cloud for US Government or other
241
+ [national cloud deployments](https://docs.microsoft.com/en-us/graph/deployments), set
242
+ the `azure_ad_endpoint` and `graph_endpoint` accordingly. For example,
243
+ for Microsoft Cloud for US Government:
244
+
245
+ ```yaml
246
+ :inbox_method: microsoft_graph
247
+ :inbox_options:
248
+ :tenant_id: 12345
249
+ :client_id: ABCDE
250
+ :client_secret: YOUR-SECRET-HERE
251
+ :poll_interval: 60
252
+ :azure_ad_endpoint: https://login.microsoftonline.us
253
+ :graph_endpoint: https://graph.microsoft.us
254
+ ```
255
+
137
256
  ## delivery_method ##
138
257
 
139
258
  ### postback ###
@@ -260,8 +379,8 @@ And finally, configure MailRoom to use the postback configuration with the optio
260
379
  :delivery_method: postback
261
380
  :delivery_options:
262
381
  :delivery_url: https://example.com/rails/action_mailbox/relay/inbound_emails
263
- :delivery_username: actionmailbox
264
- :delivery_password: <INGRESS_PASSWORD>
382
+ :username: actionmailbox
383
+ :password: <INGRESS_PASSWORD>
265
384
  ```
266
385
 
267
386
  ## Receiving `postback` in Rails ##
@@ -278,20 +397,12 @@ it's probably because the content-type is set to Faraday's default, which is `H
278
397
  ## idle_timeout ##
279
398
 
280
399
  By default, the IDLE command will wait for 29 minutes (in order to keep the server connection happy).
281
- If you'd prefer not to wait that long, you can pass `imap_timeout` in seconds for your mailbox configuration.
400
+ If you'd prefer not to wait that long, you can pass `idle_timeout` in seconds for your mailbox configuration.
282
401
 
283
402
  ## Search Command ##
284
403
 
285
404
  This setting allows configuration of the IMAP search command sent to the server. This still defaults 'UNSEEN'. You may find that 'NEW' works better for you.
286
405
 
287
- ## IMAP Server Configuration ##
288
-
289
- You can set per-mailbox configuration for the IMAP server's `host` (default: 'imap.gmail.com'), `port` (default: 993), `ssl` (default: true), and `start_tls` (default: false).
290
-
291
- If you want to set additional options for IMAP SSL you can pass a YAML hash to match [SSLContext#set_params](http://docs.ruby-lang.org/en/2.2.0/OpenSSL/SSL/SSLContext.html#method-i-set_params). If you set `verify_mode` to `:none` it'll replace with the appropriate constant.
292
-
293
- If you're seeing the error `Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)`, you need to configure your Gmail account to allow less secure apps to access it: https://support.google.com/accounts/answer/6010255.
294
-
295
406
  ## Running in Production ##
296
407
 
297
408
  I suggest running with either upstart or init.d. Check out this wiki page for some example scripts for both: https://github.com/tpitale/mail_room/wiki/Init-Scripts-for-Running-mail_room
data/Rakefile CHANGED
@@ -3,4 +3,4 @@ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
@@ -31,7 +31,7 @@ module MailRoom
31
31
  # Any subsequent failure in the instance which gets the lock will be dealt
32
32
  # with by the expiration, at which time another instance can pick up the
33
33
  # message and try again.
34
- client.set(key, 1, {:nx => true, :ex => expiration})
34
+ client.set(key, 1, nx: true, ex: expiration)
35
35
  end
36
36
 
37
37
  private
@@ -6,18 +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
24
  def quit
20
- raise NotImplementedError
25
+ @stopped = true
21
26
  end
22
27
  end
23
28
  end
@@ -1,3 +1,4 @@
1
+ require 'date'
1
2
 
2
3
  module MailRoom
3
4
  class CrashHandler
@@ -19,7 +20,7 @@ module MailRoom
19
20
  private
20
21
 
21
22
  def json(error)
22
- { time: Time.now, severity: :fatal, message: error.message, backtrace: error.backtrace }.to_json
23
+ { time: DateTime.now.iso8601(3), severity: :fatal, message: error.message, backtrace: error.backtrace }.to_json
23
24
  end
24
25
  end
25
26
  end
@@ -24,7 +24,7 @@ module MailRoom
24
24
  # Trigger `LetterOpener` to deliver our message
25
25
  # @param message [String] the email message as a string, RFC822 format
26
26
  def deliver(message)
27
- method = ::LetterOpener::DeliveryMethod.new(:location => @delivery_options.location)
27
+ method = ::LetterOpener::DeliveryMethod.new(location: @delivery_options.location)
28
28
  method.deliver!(Mail.read_from_string(message))
29
29
 
30
30
  true
@@ -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
- username = mailbox.delivery_options[:username]
21
- password = mailbox.delivery_options[:password]
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
@@ -60,13 +82,27 @@ module MailRoom
60
82
  connection.post do |request|
61
83
  request.url @delivery_options.url
62
84
  request.body = message
63
- # request.options[:timeout] = 3
64
- request.headers['Content-Type'] = @delivery_options.content_type unless @delivery_options.content_type.nil?
85
+ config_request_content_type(request)
86
+ config_request_jwt_auth(request)
65
87
  end
66
88
 
67
89
  @delivery_options.logger.info({ delivery_method: 'Postback', action: 'message pushed', url: @delivery_options.url })
68
90
  true
69
91
  end
92
+
93
+ private
94
+
95
+ def config_request_content_type(request)
96
+ return if @delivery_options.content_type.nil?
97
+
98
+ request.headers['Content-Type'] = @delivery_options.content_type
99
+ end
100
+
101
+ def config_request_jwt_auth(request)
102
+ return unless @delivery_options.jwt_auth?
103
+
104
+ request.headers[@delivery_options.jwt.header] = @delivery_options.jwt.token
105
+ end
70
106
  end
71
107
  end
72
108
  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
@@ -8,16 +8,17 @@ module MailRoom
8
8
  # Sidekiq Delivery method
9
9
  # @author Douwe Maan
10
10
  class Sidekiq
11
- Options = Struct.new(:redis_url, :namespace, :sentinels, :queue, :worker, :logger) do
11
+ Options = Struct.new(:redis_url, :namespace, :sentinels, :queue, :worker, :logger, :redis_db) do
12
12
  def initialize(mailbox)
13
13
  redis_url = mailbox.delivery_options[:redis_url] || "redis://localhost:6379"
14
+ redis_db = mailbox.delivery_options[:redis_db] || 0
14
15
  namespace = mailbox.delivery_options[:namespace]
15
16
  sentinels = mailbox.delivery_options[:sentinels]
16
17
  queue = mailbox.delivery_options[:queue] || "default"
17
18
  worker = mailbox.delivery_options[:worker]
18
19
  logger = mailbox.logger
19
20
 
20
- super(redis_url, namespace, sentinels, queue, worker, logger)
21
+ super(redis_url, namespace, sentinels, queue, worker, logger, redis_db)
21
22
  end
22
23
  end
23
24
 
@@ -45,7 +46,7 @@ module MailRoom
45
46
  def client
46
47
  @client ||= begin
47
48
  sentinels = options.sentinels
48
- redis_options = { url: options.redis_url }
49
+ redis_options = { url: options.redis_url, db: options.redis_db }
49
50
  redis_options[:sentinels] = sentinels if sentinels
50
51
 
51
52
  redis = ::Redis.new(redis_options)
@@ -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
@@ -1,3 +1,4 @@
1
+ require 'date'
1
2
  require 'logger'
2
3
  require 'json'
3
4
 
@@ -10,12 +11,25 @@ module MailRoom
10
11
 
11
12
  data = {}
12
13
  data[:severity] = severity
13
- data[:time] = timestamp || Time.now.to_s
14
+ data[:time] = format_timestamp(timestamp || Time.now)
14
15
  # only accept a Hash
15
16
  data.merge!(message)
16
17
 
17
18
  data.to_json + "\n"
18
19
  end
20
+
21
+ private
22
+
23
+ def format_timestamp(timestamp)
24
+ case timestamp
25
+ when Time
26
+ timestamp.to_datetime.iso8601(3).to_s
27
+ when DateTime
28
+ timestamp.iso8601(3).to_s
29
+ else
30
+ timestamp
31
+ end
32
+ end
19
33
  end
20
34
  end
21
35
  end
@@ -1,11 +1,14 @@
1
1
  require "mail_room/delivery"
2
2
  require "mail_room/arbitration"
3
3
  require "mail_room/imap"
4
+ require "mail_room/microsoft_graph"
4
5
 
5
6
  module MailRoom
6
7
  # Mailbox Configuration fields
7
8
  MAILBOX_FIELDS = [
8
9
  :email,
10
+ :inbox_method,
11
+ :inbox_options,
9
12
  :password,
10
13
  :host,
11
14
  :port,
@@ -42,24 +45,26 @@ module MailRoom
42
45
  # 29 minutes, as suggested by the spec: https://tools.ietf.org/html/rfc2177
43
46
  IMAP_IDLE_TIMEOUT = 29 * 60 # 29 minutes in in seconds
44
47
 
45
- REQUIRED_CONFIGURATION = [:name, :email, :password, :host, :port]
48
+ IMAP_CONFIGURATION = [:name, :email, :password, :host, :port].freeze
49
+ MICROSOFT_GRAPH_CONFIGURATION = [:name, :email].freeze
50
+ MICROSOFT_GRAPH_INBOX_OPTIONS = [:tenant_id, :client_id, :client_secret].freeze
46
51
 
47
52
  # Default attributes for the mailbox configuration
48
53
  DEFAULTS = {
49
- :search_command => 'UNSEEN',
50
- :delivery_method => 'postback',
51
- :host => 'imap.gmail.com',
52
- :port => 993,
53
- :ssl => true,
54
- :start_tls => false,
55
- :limit_max_unread => 0,
56
- :idle_timeout => IMAP_IDLE_TIMEOUT,
57
- :delete_after_delivery => false,
58
- :expunge_deleted => false,
59
- :delivery_options => {},
60
- :arbitration_method => 'noop',
61
- :arbitration_options => {},
62
- :logger => {}
54
+ search_command: 'UNSEEN',
55
+ delivery_method: 'postback',
56
+ host: 'imap.gmail.com',
57
+ port: 993,
58
+ ssl: true,
59
+ start_tls: false,
60
+ limit_max_unread: 0,
61
+ idle_timeout: IMAP_IDLE_TIMEOUT,
62
+ delete_after_delivery: false,
63
+ expunge_deleted: false,
64
+ delivery_options: {},
65
+ arbitration_method: 'noop',
66
+ arbitration_options: {},
67
+ logger: {}
63
68
  }
64
69
 
65
70
  # Store the configuration and require the appropriate delivery method
@@ -122,14 +127,32 @@ module MailRoom
122
127
  { email: self.email, name: self.name }
123
128
  end
124
129
 
130
+ def imap?
131
+ !microsoft_graph?
132
+ end
133
+
134
+ def microsoft_graph?
135
+ self[:inbox_method].to_s == 'microsoft_graph'
136
+ end
137
+
125
138
  def validate!
139
+ if microsoft_graph?
140
+ validate_microsoft_graph!
141
+ else
142
+ validate_imap!
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ def validate_imap!
126
149
  if self[:idle_timeout] > IMAP_IDLE_TIMEOUT
127
150
  raise IdleTimeoutTooLarge,
128
151
  "Please use an idle timeout smaller than #{29*60} to prevent " \
129
152
  "IMAP server disconnects"
130
153
  end
131
154
 
132
- REQUIRED_CONFIGURATION.each do |k|
155
+ IMAP_CONFIGURATION.each do |k|
133
156
  if self[k].nil?
134
157
  raise ConfigurationError,
135
158
  "Field :#{k} is required in Mailbox: #{inspect}"
@@ -137,7 +160,23 @@ module MailRoom
137
160
  end
138
161
  end
139
162
 
140
- private
163
+ def validate_microsoft_graph!
164
+ raise ConfigurationError, "Missing inbox_options in Mailbox: #{inspect}" unless self.inbox_options.is_a?(Hash)
165
+
166
+ MICROSOFT_GRAPH_CONFIGURATION.each do |k|
167
+ if self[k].nil?
168
+ raise ConfigurationError,
169
+ "Field :#{k} is required in Mailbox: #{inspect}"
170
+ end
171
+ end
172
+
173
+ MICROSOFT_GRAPH_INBOX_OPTIONS.each do |k|
174
+ if self[:inbox_options][k].nil?
175
+ raise ConfigurationError,
176
+ "inbox_options field :#{k} is required in Mailbox: #{inspect}"
177
+ end
178
+ end
179
+ end
141
180
 
142
181
  def parsed_arbitration_options
143
182
  arbitration_klass::Options.new(self)
@@ -62,8 +62,14 @@ module MailRoom
62
62
  end
63
63
 
64
64
  private
65
+
65
66
  def connection
66
- @connection ||= ::MailRoom::IMAP::Connection.new(@mailbox)
67
+ @connection ||=
68
+ if @mailbox.microsoft_graph?
69
+ ::MailRoom::MicrosoftGraph::Connection.new(@mailbox)
70
+ else
71
+ ::MailRoom::IMAP::Connection.new(@mailbox)
72
+ end
67
73
  end
68
74
  end
69
75
  end