mail_xoauth2 1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 27a0ef9770c2172e574541dbb950bc4f7b074163a42903acfeadf2570c95609c
4
+ data.tar.gz: 499e917ef4edf4e6f4f3a3f8f51f5305f60c1d67bd6706ce94f8644ef331a2ff
5
+ SHA512:
6
+ metadata.gz: b0dcd0f7f2c8fbd565b680866e7a29e72e9c8acb3891be7a36e521913dd53ff392a9490d5c3ef94354324f127efe0e78c881ddc9294ecd65cadf56529c7c0ba0
7
+ data.tar.gz: 9a4f954d7350be1d603de16aa1cd813c2046d1516ffe99cb7b4ee13b5e4bece972a0fcd9b778777f166e39a44fc71a5049f755bd1acaecf1a201b6db66518d14
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Mailbutler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,30 @@
1
+ # mail_xoauth2
2
+ Get access to IMAP and STMP via OAuth2, using the standard Ruby Net libraries.
3
+
4
+ This gem is based on and inspired by [gmail_xoauth](https://github.com/nfo/gmail_xoauth).
5
+
6
+ ## Usage
7
+
8
+ ### IMAP OAuth 2.0
9
+
10
+ ```ruby
11
+ require 'mail_xoauth2'
12
+ imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
13
+ imap.authenticate('XOAUTH2', 'myemail@gmail.com', my_oauth2_token)
14
+ messages_count = imap.status('INBOX', ['MESSAGES'])['MESSAGES']
15
+ puts "Seeing #{messages_count} messages in INBOX"
16
+ ```
17
+
18
+ ### SMTP OAuth 2.0
19
+
20
+ ```ruby
21
+ require 'mail_xoauth2'
22
+ smtp = Net::SMTP.new('smtp.gmail.com', 587)
23
+ smtp.enable_starttls_auto
24
+ smtp.start('gmail.com', 'myemail@gmail.com', my_oauth2_token, :xoauth2)
25
+ smtp.finish
26
+ ```
27
+
28
+ ## License
29
+
30
+ See LICENSE for details.
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/imap'
4
+
5
+ module MailXoauth2
6
+ class ImapXoauth2Authenticator
7
+ def process(_data)
8
+ build_oauth2_string(@user, @oauth2_token, true)
9
+ end
10
+
11
+ private
12
+
13
+ # +user+ is an email address: roger@gmail.com
14
+ # +oauth2_token+ is the OAuth2 token
15
+ def initialize(user, oauth2_token)
16
+ @user = user
17
+ @oauth2_token = oauth2_token
18
+ end
19
+
20
+ include Oauth2String
21
+ end
22
+ end
23
+
24
+ Net::IMAP.add_authenticator('XOAUTH2', MailXoauth2::ImapXoauth2Authenticator)
@@ -0,0 +1,17 @@
1
+ module MailXoauth2
2
+ module Oauth2String
3
+ private
4
+
5
+ #
6
+ # Builds the "oauth2 protocol authentication string". See https://developers.google.com/google-apps/gmail/xoauth2_protocol
7
+ #
8
+ # +user+ is an email address: roger@gmail.com
9
+ # +oauth2_token+ is the oauth2 token
10
+ def build_oauth2_string(user, oauth2_token, encode_base64 = false)
11
+ oauth2_string = format("user=%<user>s\1auth=Bearer %<token>s\1\1".encode('us-ascii'), user: user, token: oauth2_token)
12
+ oauth2_string = Base64.strict_encode64(oauth2_string) if encode_base64
13
+
14
+ oauth2_string
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ require 'net/smtp'
2
+ require 'base64'
3
+
4
+ module MailXoauth2
5
+ module SmtpXoauth2Authenticator
6
+ def send_xoauth2(auth_token)
7
+ critical do
8
+ get_response("AUTH XOAUTH2 #{auth_token}")
9
+ end
10
+ end
11
+ private :send_xoauth2
12
+
13
+ def get_final_status
14
+ critical do
15
+ get_response('')
16
+ end
17
+ end
18
+ private :get_final_status
19
+
20
+ def auth_xoauth2(user, oauth2_token)
21
+ check_auth_args user, oauth2_token
22
+
23
+ auth_string = build_oauth2_string(user, oauth2_token, true)
24
+ res = send_xoauth2(auth_string)
25
+
26
+ # See note about SMTP protocol exchange in https://developers.google.com/gmail/xoauth2_protocol
27
+ res = get_final_status if res.continue?
28
+
29
+ check_auth_response res
30
+ res
31
+ end
32
+
33
+ include Oauth2String
34
+ end
35
+ end
36
+
37
+ # Not pretty, right ?
38
+ Net::SMTP.__send__('include', MailXoauth2::SmtpXoauth2Authenticator)
@@ -0,0 +1,3 @@
1
+ module MailXoauth2
2
+ VERSION = '1.0'.freeze
3
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mail_xoauth2/oauth2_string'
4
+ require 'mail_xoauth2/imap_xoauth2_authenticator'
5
+ require 'mail_xoauth2/smtp_xoauth2_authenticator'
data/test/helper.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'yaml'
2
+
3
+ require 'rubygems'
4
+ require 'test/unit'
5
+ require 'mocha/test_unit'
6
+
7
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
9
+ require 'mail_xoauth2'
10
+
11
+ # Wanna debug ? Activate the IMAP debug mode, it will show the client/server conversation
12
+ # Net::IMAP.debug = true
13
+
14
+ # SMTP debugging can only be enabled on Net::SMTP instances
15
+ # Net::SMTP.class_eval do
16
+ # def initialize_with_debug(*args)
17
+ # initialize_without_debug(*args)
18
+ # @debug_output = STDERR
19
+ # end
20
+ # alias_method :initialize_without_debug, :initialize
21
+ # alias_method :initialize, :initialize_with_debug
22
+ # end
23
+
24
+ VALID_CREDENTIALS = begin
25
+ YAML.load_file(File.join(File.dirname(__FILE__), 'valid_credentials.yml'))
26
+ rescue Errno::ENOENT
27
+ STDERR.puts '
28
+ Warning: some tests are disabled because they require valid credentials.
29
+ To enable them, create a file "test/valid_credentials.yml".
30
+ It should also ontain a valid OAuth2 access token.
31
+ Of course, this file is .gitignored. Template:
32
+
33
+ ---
34
+ :email: someuser@gmail.com
35
+ :consumer_key: anonymous # "anonymous" is a valid value for testing
36
+ :consumer_secret: anonymous # "anonymous" is a valid value for testing
37
+ :token: 1/nE2xBCDOU0429bTeJySE11kRE95qzKQNlfTaaBcDeFg
38
+ :token_secret: 123Z/bMsi9fFhN6qHFWOabcd
39
+ :oauth2_token: ya29.AHES6ZTIpsLuSyMwnh-3C40WWcuiOe4N7he0a8xnkvDk_6Q_6yUg7E
40
+
41
+ '
42
+ false
43
+ end
44
+
45
+ class Test::Unit::TestCase
46
+ end
@@ -0,0 +1,30 @@
1
+ require 'helper'
2
+
3
+ class TestImapXoauth2Authenticator < Test::Unit::TestCase
4
+ def setup; end
5
+
6
+ def test_xoauth2_authenticator_is_enabled
7
+ authenticators = Net::IMAP.__send__('class_variable_get', '@@authenticators')
8
+ assert_not_nil authenticators['XOAUTH2']
9
+ assert_equal authenticators['XOAUTH2'], GmailXoauth::ImapXoauth2Authenticator
10
+ end
11
+
12
+ def test_authenticate_with_invalid_credentials
13
+ imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
14
+ assert_raise(Net::IMAP::NoResponseError) do
15
+ imap.authenticate('XOAUTH2', 'roger@moore.com', 'a')
16
+ end
17
+ end
18
+
19
+ def test_authenticate_with_valid_credentials
20
+ return unless VALID_CREDENTIALS
21
+
22
+ imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
23
+ imap.authenticate('XOAUTH2', VALID_CREDENTIALS[:email], VALID_CREDENTIALS[:oauth2_token])
24
+ mailboxes = imap.list('', '*')
25
+ assert_instance_of Array, mailboxes
26
+ assert_instance_of Net::IMAP::MailboxList, mailboxes.first
27
+ ensure
28
+ imap.disconnect if imap
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'helper'
4
+
5
+ class TestOauth2String < Test::Unit::TestCase
6
+ def setup; end
7
+
8
+ def test_build_oauth2_string
9
+ user = 'somebody@gmail.com'
10
+ oauth2_access_token = SecureRandom.urlsafe_base64
11
+
12
+ oauth2_string = SomeNetClass.new.__send__('build_oauth2_string', user, oauth2_access_token)
13
+
14
+ assert_equal(
15
+ "user=#{user}\1auth=Bearer #{oauth2_access_token}\1\1",
16
+ oauth2_string
17
+ )
18
+ end
19
+
20
+ def test_build_encoded_oauth2_string
21
+ user = 'somebody@gmail.com'
22
+ oauth2_access_token = SecureRandom.urlsafe_base64
23
+
24
+ oauth2_string = SomeNetClass.new.__send__('build_oauth2_string', user, oauth2_access_token, true)
25
+
26
+ assert_equal(
27
+ Base64.strict_encode64("user=#{user}\1auth=Bearer #{oauth2_access_token}\1\1"),
28
+ oauth2_string
29
+ )
30
+ end
31
+ end
32
+
33
+ class SomeNetClass
34
+ include MailXoauth2::Oauth2String
35
+ end
@@ -0,0 +1,56 @@
1
+ require 'helper'
2
+
3
+ class TestSmtpXoauth2Authenticator < Test::Unit::TestCase
4
+ def setup; end
5
+
6
+ def test_smtp_authenticator_is_enabled
7
+ assert Net::SMTP.new(nil).respond_to?(:auth_xoauth2), 'The Net::SMTP class should define the method :auth_xoauth2'
8
+ end
9
+
10
+ def test_authenticate_with_invalid_credentials
11
+ smtp = Net::SMTP.new('smtp.gmail.com', 587)
12
+ smtp.enable_starttls_auto
13
+ assert_raise(Net::SMTPAuthenticationError) do
14
+ smtp.start('gmail.com', 'roger@moore.com', 'a', :xoauth2)
15
+ end
16
+ end
17
+
18
+ def test_authenticate_with_valid_credentials
19
+ return unless VALID_CREDENTIALS
20
+
21
+ smtp = Net::SMTP.new('smtp.gmail.com', 587)
22
+ smtp.enable_starttls_auto
23
+
24
+ assert_nothing_raised do
25
+ smtp.start('gmail.com', VALID_CREDENTIALS[:email], VALID_CREDENTIALS[:oauth2_token], :xoauth2)
26
+ end
27
+ ensure
28
+ smtp.finish if smtp&.started?
29
+ end
30
+
31
+ # Test the handling of 3xx response to an "AUTH XOAUTH2" request... client
32
+ # should send a "\r\n" to get the actual error that occurred.
33
+ #
34
+ # See note about SMTP protocol exchange in https://developers.google.com/
35
+ # gmail/xoauth2_protocol
36
+ #
37
+ def test_authenticate_with_continuation
38
+ smtp = Net::SMTP.new('smtp.gmail.com', 587)
39
+
40
+ # Stub return from initial "AUTH XOAUTH2" request as well as the empty line sent to get final error
41
+ smtp.stubs(:send_xoauth2).returns(Net::SMTP::Response.parse('334 eyJzdGF0dXMiOiI0MDAiLCJzY2hlbWVzIjoiQmVhcmVyIiwic2NvcGUiOiJodHRwczovL21haWwuZ29vZ2xlLmNvbS8ifQ=='))
42
+ smtp.stubs(:get_final_status).returns(Net::SMTP::Response.parse('454 4.7.0 Too many login attempts, please try again later. j63sm3521185itj.19 - gsmtp'))
43
+
44
+ smtp.enable_starttls_auto
45
+
46
+ # Validate an error is still raised
47
+ ex = assert_raise(Net::SMTPAuthenticationError) do
48
+ smtp.start('gmail.com', 'roger@moore.com', 'a', :xoauth2)
49
+ end
50
+
51
+ # ...and that the 334 is not passed back to the caller.
52
+ assert_equal('454 4.7.0 Too many login attempts, please try again later. j63sm3521185itj.19 - gsmtp', ex.message)
53
+ ensure
54
+ smtp.finish if smtp&.started?
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mail_xoauth2
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Fabian Jäger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-06-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mocha
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: shoulda
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Get access to IMAP and STMP via OAuth2, using the standard Ruby Net libraries
42
+ email:
43
+ - fabian@mailbutler.io
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE
49
+ - README.markdown
50
+ - lib/mail_xoauth2.rb
51
+ - lib/mail_xoauth2/imap_xoauth2_authenticator.rb
52
+ - lib/mail_xoauth2/oauth2_string.rb
53
+ - lib/mail_xoauth2/smtp_xoauth2_authenticator.rb
54
+ - lib/mail_xoauth2/version.rb
55
+ - test/helper.rb
56
+ - test/test_imap_xoauth2_authenticator.rb
57
+ - test/test_oauth2_string.rb
58
+ - test/test_smtp_xoauth2_authenticator.rb
59
+ homepage: https://github.com/Mailbutler/mail_xoauth2
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options:
65
+ - "--charset=UTF-8"
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 1.3.6
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.7.6
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Get access to IMAP and STMP via OAuth2, using the standard Ruby Net libraries
84
+ test_files: []