imap-backup 4.0.0.rc1 → 4.0.0.rc2
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/CHANGELOG.md +8 -0
- data/README.md +22 -8
- data/imap-backup.gemspec +0 -2
- data/lib/imap/backup/account/connection.rb +2 -21
- data/lib/imap/backup/cli/local.rb +4 -4
- data/lib/imap/backup/cli.rb +1 -1
- data/lib/imap/backup/configuration/account.rb +1 -11
- data/lib/imap/backup/version.rb +1 -1
- data/lib/imap/backup.rb +0 -1
- data/spec/unit/imap/backup/account/connection_spec.rb +0 -45
- data/spec/unit/imap/backup/configuration/account_spec.rb +0 -43
- metadata +2 -65
- data/docs/01-credentials-screen.png +0 -0
- data/docs/02-new-project.png +0 -0
- data/docs/03-initial-credentials-for-project.png +0 -0
- data/docs/04-credential-type-selection.png +0 -0
- data/docs/05-cant-create-without-consent-setup.png +0 -0
- data/docs/06-user-type-selection.png +0 -0
- data/docs/07-consent-screen-form.png +0 -0
- data/docs/08-app-scopes.png +0 -0
- data/docs/09-scope-selection.png +0 -0
- data/docs/10-updated-app-scopes.png +0 -0
- data/docs/11-test-users.png +0 -0
- data/docs/12-add-users.png +0 -0
- data/docs/13-create-oauth-client.png +0 -0
- data/docs/14-application-details.png +0 -0
- data/docs/16-initial-menu.png +0 -0
- data/docs/17-inputting-the-email-address.png +0 -0
- data/docs/18-choose-password.png +0 -0
- data/docs/19-supply-client-info.png +0 -0
- data/docs/20-choose-gmail-account.png +0 -0
- data/docs/21-accept-warnings.png +0 -0
- data/docs/22-grant-access.png +0 -0
- data/docs/24-confirm-choices.png +0 -0
- data/docs/25-success-code.png +0 -0
- data/docs/26-type-code-into-imap-backup.png +0 -0
- data/docs/27-success.png +0 -0
- data/docs/setting-up-gmail-with-oauth2.md +0 -166
- data/lib/gmail/authenticator.rb +0 -160
- data/lib/google/auth/stores/in_memory_token_store.rb +0 -9
- data/lib/imap/backup/configuration/gmail_oauth2.rb +0 -102
- data/spec/unit/gmail/authenticator_spec.rb +0 -138
- data/spec/unit/google/auth/stores/in_memory_token_store_spec.rb +0 -15
- data/spec/unit/imap/backup/configuration/gmail_oauth2_spec.rb +0 -121
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 307d6f8c3448e730c14e9550fbfcbfee0c946f28953a9cb6a7070f75feda3033
|
4
|
+
data.tar.gz: 553f15b7bf8dee1ad126111915d398b25181cd4f9aa50b252a980ecc5002ef63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11a50621ff9ef4eacdfa5622ab0d2a9e2fa4dd2adf5cc162a1709686e5187db89ed0aa5d91a374d968e38e1caba62c87d0c384f3c1964a8b001338bbb3b3eb13
|
7
|
+
data.tar.gz: 35902dcb5520b7ca17a6adab6833b2d2296e3655e2ff5cffb7843c018d5b7eb25a2779a3955833088730df95eff5bb02fecff2c8911653637cceafb8897ae7f0
|
data/CHANGELOG.md
CHANGED
@@ -4,8 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
6
6
|
|
7
|
+
## [4.0.0.rc2] - 2021-11-18
|
8
|
+
|
9
|
+
### Removed
|
10
|
+
|
11
|
+
* GMail OAuth2 support. Tokens only last a few days, so this authentication
|
12
|
+
method is not usable for automated backups.
|
13
|
+
|
7
14
|
## [4.0.0.rc1] - 2021-11-17
|
8
15
|
|
9
16
|
### Added
|
17
|
+
|
10
18
|
* `local` commands to list accounts, folders and emails and to view single
|
11
19
|
emails.
|
data/README.md
CHANGED
@@ -20,20 +20,24 @@
|
|
20
20
|
|
21
21
|
To use imap-backup with GMail, you will need to enable 'App passwords' on your account.
|
22
22
|
|
23
|
-
|
23
|
+
# Installation
|
24
|
+
|
25
|
+
```shell
|
26
|
+
$ gem install 'imap-backup'
|
27
|
+
```
|
24
28
|
|
25
|
-
|
26
|
-
users to set up an application specific to their account, the feature
|
27
|
-
is disabled by default.
|
29
|
+
# Commands
|
28
30
|
|
29
|
-
|
31
|
+
For a full list, run
|
30
32
|
|
31
|
-
|
33
|
+
```
|
34
|
+
$ imap-backup help
|
35
|
+
```
|
32
36
|
|
33
|
-
|
37
|
+
For more information about a command, run
|
34
38
|
|
35
39
|
```shell
|
36
|
-
$
|
40
|
+
$ imap-backup help COMMAND
|
37
41
|
```
|
38
42
|
|
39
43
|
# Setup
|
@@ -148,6 +152,16 @@ Each folder is saved to an mbox file.
|
|
148
152
|
Alongside each mbox is a file with extension '.imap', which lists the source IMAP
|
149
153
|
UIDs to allow a full restore.
|
150
154
|
|
155
|
+
# Local commands
|
156
|
+
|
157
|
+
There a various commands for viewing local backup status.
|
158
|
+
|
159
|
+
To view the list, use
|
160
|
+
|
161
|
+
```shell
|
162
|
+
$ imap_backup help local
|
163
|
+
```
|
164
|
+
|
151
165
|
# Troubleshooting
|
152
166
|
|
153
167
|
If you have problems:
|
data/imap-backup.gemspec
CHANGED
@@ -16,8 +16,6 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.required_ruby_version = ">= 2.5"
|
17
17
|
gem.version = Imap::Backup::VERSION
|
18
18
|
|
19
|
-
gem.add_runtime_dependency "gmail_xoauth"
|
20
|
-
gem.add_runtime_dependency "googleauth"
|
21
19
|
gem.add_runtime_dependency "highline"
|
22
20
|
gem.add_runtime_dependency "mail"
|
23
21
|
gem.add_runtime_dependency "rake"
|
@@ -1,15 +1,11 @@
|
|
1
1
|
require "net/imap"
|
2
|
-
require "gmail_xoauth"
|
3
2
|
|
4
|
-
require "gmail/authenticator"
|
5
3
|
require "retry_on_error"
|
6
4
|
|
7
5
|
module Imap::Backup
|
8
6
|
module Account; end
|
9
7
|
|
10
8
|
class Account::Connection
|
11
|
-
class InvalidGmailOauth2RefreshToken < StandardError; end
|
12
|
-
|
13
9
|
include RetryOnError
|
14
10
|
|
15
11
|
LOGIN_RETRY_CLASSES = [EOFError, Errno::ECONNRESET, SocketError].freeze
|
@@ -109,17 +105,8 @@ module Imap::Backup
|
|
109
105
|
"Creating IMAP instance: #{server}, options: #{options.inspect}"
|
110
106
|
)
|
111
107
|
imap = Net::IMAP.new(server, options)
|
112
|
-
|
113
|
-
|
114
|
-
credentials = authenticator.credentials
|
115
|
-
raise InvalidGmailOauth2RefreshToken if !credentials
|
116
|
-
|
117
|
-
Imap::Backup.logger.debug "Logging in with OAuth2 token: #{username}"
|
118
|
-
imap.authenticate("XOAUTH2", username, credentials.access_token)
|
119
|
-
else
|
120
|
-
Imap::Backup.logger.debug "Logging in: #{username}/#{masked_password}"
|
121
|
-
imap.login(username, password)
|
122
|
-
end
|
108
|
+
Imap::Backup.logger.debug "Logging in: #{username}/#{masked_password}"
|
109
|
+
imap.login(username, password)
|
123
110
|
Imap::Backup.logger.debug "Login complete"
|
124
111
|
imap
|
125
112
|
end
|
@@ -181,12 +168,6 @@ module Imap::Backup
|
|
181
168
|
password.gsub(/./, "x")
|
182
169
|
end
|
183
170
|
|
184
|
-
def use_gmail_oauth2?
|
185
|
-
# TODO: test use of ENV
|
186
|
-
server == Email::Provider::GMAIL_IMAP_SERVER &&
|
187
|
-
ENV["IMAP_BACKUP_ENABLE_GMAIL_OAUTH2"]
|
188
|
-
end
|
189
|
-
|
190
171
|
def backup_folders
|
191
172
|
@backup_folders ||=
|
192
173
|
begin
|
@@ -3,13 +3,13 @@ module Imap::Backup
|
|
3
3
|
include Thor::Actions
|
4
4
|
include CLI::Helpers
|
5
5
|
|
6
|
-
desc "accounts", "
|
6
|
+
desc "accounts", "List locally backed-up accounts"
|
7
7
|
def accounts
|
8
8
|
connections = Imap::Backup::Configuration::List.new
|
9
9
|
connections.accounts.each { |a| puts a[:username] }
|
10
10
|
end
|
11
11
|
|
12
|
-
desc "folders EMAIL", "
|
12
|
+
desc "folders EMAIL", "List account folders"
|
13
13
|
def folders(email)
|
14
14
|
connections = Imap::Backup::Configuration::List.new
|
15
15
|
account = connections.accounts.find { |a| a[:username] == email }
|
@@ -21,7 +21,7 @@ module Imap::Backup
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
desc "emails EMAIL FOLDER", "
|
24
|
+
desc "emails EMAIL FOLDER", "List emails in a folder"
|
25
25
|
def emails(email, folder_name)
|
26
26
|
connections = Imap::Backup::Configuration::List.new
|
27
27
|
account = connections.accounts.find { |a| a[:username] == email }
|
@@ -49,7 +49,7 @@ module Imap::Backup
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
desc "email EMAIL FOLDER UID", "
|
52
|
+
desc "email EMAIL FOLDER UID", "Show an email"
|
53
53
|
def email(email, folder_name, uid)
|
54
54
|
connections = Imap::Backup::Configuration::List.new
|
55
55
|
account = connections.accounts.find { |a| a[:username] == email }
|
data/lib/imap/backup/cli.rb
CHANGED
@@ -68,12 +68,7 @@ module Imap::Backup
|
|
68
68
|
|
69
69
|
def modify_password(menu)
|
70
70
|
menu.choice("modify password") do
|
71
|
-
password =
|
72
|
-
if use_gmail_oauth2?(account)
|
73
|
-
Configuration::GmailOauth2.new(account).run
|
74
|
-
else
|
75
|
-
Configuration::Asker.password
|
76
|
-
end
|
71
|
+
password = Configuration::Asker.password
|
77
72
|
|
78
73
|
if !password.nil?
|
79
74
|
account[:password] = password
|
@@ -82,11 +77,6 @@ module Imap::Backup
|
|
82
77
|
end
|
83
78
|
end
|
84
79
|
|
85
|
-
def use_gmail_oauth2?(account)
|
86
|
-
account[:server] == Email::Provider::GMAIL_IMAP_SERVER &&
|
87
|
-
ENV["IMAP_BACKUP_ENABLE_GMAIL_OAUTH2"]
|
88
|
-
end
|
89
|
-
|
90
80
|
def modify_server(menu)
|
91
81
|
menu.choice("modify server") do
|
92
82
|
server = highline.ask("server: ")
|
data/lib/imap/backup/version.rb
CHANGED
data/lib/imap/backup.rb
CHANGED
@@ -7,7 +7,6 @@ require "imap/backup/configuration/account"
|
|
7
7
|
require "imap/backup/configuration/asker"
|
8
8
|
require "imap/backup/configuration/connection_tester"
|
9
9
|
require "imap/backup/configuration/folder_chooser"
|
10
|
-
require "imap/backup/configuration/gmail_oauth2"
|
11
10
|
require "imap/backup/configuration/list"
|
12
11
|
require "imap/backup/configuration/setup"
|
13
12
|
require "imap/backup/configuration/store"
|
@@ -90,56 +90,11 @@ describe Imap::Backup::Account::Connection do
|
|
90
90
|
end
|
91
91
|
|
92
92
|
context "with the GMail IMAP server" do
|
93
|
-
ACCESS_TOKEN = "access_token".freeze
|
94
|
-
|
95
93
|
let(:server) { GMAIL_IMAP_SERVER }
|
96
94
|
let(:refresh_token) { true }
|
97
95
|
let(:result) { nil }
|
98
|
-
let(:authenticator) do
|
99
|
-
instance_double(
|
100
|
-
Gmail::Authenticator,
|
101
|
-
credentials: credentials
|
102
|
-
)
|
103
|
-
end
|
104
|
-
let(:credentials) { OpenStruct.new(access_token: ACCESS_TOKEN) }
|
105
|
-
|
106
|
-
before do
|
107
|
-
allow(Gmail::Authenticator).
|
108
|
-
to receive(:refresh_token?) { refresh_token }
|
109
|
-
allow(Gmail::Authenticator).
|
110
|
-
to receive(:new).
|
111
|
-
with(email: USERNAME, token: PASSWORD) { authenticator }
|
112
|
-
end
|
113
|
-
|
114
|
-
context "when the password is our copy of a GMail refresh token and the environment IMAP_BACKUP_ENABLE_GMAIL_OAUTH2 is set" do
|
115
|
-
before do
|
116
|
-
ENV["IMAP_BACKUP_ENABLE_GMAIL_OAUTH2"] = "1"
|
117
|
-
end
|
118
|
-
|
119
|
-
after do
|
120
|
-
ENV.delete("IMAP_BACKUP_ENABLE_GMAIL_OAUTH2")
|
121
|
-
end
|
122
|
-
|
123
|
-
it "uses the OAuth2 access_token to authenticate" do
|
124
|
-
subject.imap
|
125
|
-
|
126
|
-
expect(imap).to have_received(:authenticate).with(
|
127
|
-
"XOAUTH2", USERNAME, ACCESS_TOKEN
|
128
|
-
)
|
129
|
-
end
|
130
|
-
|
131
|
-
context "when the refresh token is invalid" do
|
132
|
-
let(:credentials) { nil }
|
133
|
-
|
134
|
-
it "raises" do
|
135
|
-
expect { subject.imap }.to raise_error(String)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
96
|
|
140
97
|
context "when the password is not our copy of a GMail refresh token" do
|
141
|
-
let(:refresh_token) { false }
|
142
|
-
|
143
98
|
it "uses the password" do
|
144
99
|
subject.imap
|
145
100
|
|
@@ -241,49 +241,6 @@ describe Imap::Backup::Configuration::Account do
|
|
241
241
|
end
|
242
242
|
end
|
243
243
|
|
244
|
-
describe "choosing 'modify password' when the server is for GMail" do
|
245
|
-
let(:new_password) { "new_password" }
|
246
|
-
let(:current_server) { GMAIL_IMAP_SERVER }
|
247
|
-
let(:gmail_oauth2) do
|
248
|
-
instance_double(Imap::Backup::Configuration::GmailOauth2, run: nil)
|
249
|
-
end
|
250
|
-
|
251
|
-
before do
|
252
|
-
allow(Imap::Backup::Configuration::Asker).
|
253
|
-
to receive(:password) { new_password }
|
254
|
-
allow(Imap::Backup::Configuration::GmailOauth2).
|
255
|
-
to receive(:new).
|
256
|
-
with(account) { gmail_oauth2 }
|
257
|
-
end
|
258
|
-
|
259
|
-
context "when the environment IMAP_BACKUP_ENABLE_GMAIL_OAUTH2 is set" do
|
260
|
-
before do
|
261
|
-
ENV["IMAP_BACKUP_ENABLE_GMAIL_OAUTH2"] = "1"
|
262
|
-
subject.run
|
263
|
-
menu.choices["modify password"].call
|
264
|
-
end
|
265
|
-
|
266
|
-
after do
|
267
|
-
ENV.delete("IMAP_BACKUP_ENABLE_GMAIL_OAUTH2")
|
268
|
-
end
|
269
|
-
|
270
|
-
it "sets up GMail OAuth2" do
|
271
|
-
expect(gmail_oauth2).to have_received(:run)
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
context "when the environment IMAP_BACKUP_ENABLE_GMAIL_OAUTH2 is not set" do
|
276
|
-
before do
|
277
|
-
subject.run
|
278
|
-
menu.choices["modify password"].call
|
279
|
-
end
|
280
|
-
|
281
|
-
it "sets up GMail OAuth2" do
|
282
|
-
expect(gmail_oauth2).to_not have_received(:run)
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
244
|
describe "choosing 'modify server'" do
|
288
245
|
let(:server) { "server" }
|
289
246
|
|
metadata
CHANGED
@@ -1,43 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: imap-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.0.
|
4
|
+
version: 4.0.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Yates
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-11-
|
11
|
+
date: 2021-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: gmail_xoauth
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
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: googleauth
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
13
|
- !ruby/object:Gem::Dependency
|
42
14
|
name: highline
|
43
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -199,39 +171,11 @@ files:
|
|
199
171
|
- Rakefile
|
200
172
|
- bin/imap-backup
|
201
173
|
- docker-compose.yml
|
202
|
-
- docs/01-credentials-screen.png
|
203
|
-
- docs/02-new-project.png
|
204
|
-
- docs/03-initial-credentials-for-project.png
|
205
|
-
- docs/04-credential-type-selection.png
|
206
|
-
- docs/05-cant-create-without-consent-setup.png
|
207
|
-
- docs/06-user-type-selection.png
|
208
|
-
- docs/07-consent-screen-form.png
|
209
|
-
- docs/08-app-scopes.png
|
210
|
-
- docs/09-scope-selection.png
|
211
|
-
- docs/10-updated-app-scopes.png
|
212
|
-
- docs/11-test-users.png
|
213
|
-
- docs/12-add-users.png
|
214
|
-
- docs/13-create-oauth-client.png
|
215
|
-
- docs/14-application-details.png
|
216
|
-
- docs/16-initial-menu.png
|
217
|
-
- docs/17-inputting-the-email-address.png
|
218
|
-
- docs/18-choose-password.png
|
219
|
-
- docs/19-supply-client-info.png
|
220
|
-
- docs/20-choose-gmail-account.png
|
221
|
-
- docs/21-accept-warnings.png
|
222
|
-
- docs/22-grant-access.png
|
223
|
-
- docs/24-confirm-choices.png
|
224
|
-
- docs/25-success-code.png
|
225
|
-
- docs/26-type-code-into-imap-backup.png
|
226
|
-
- docs/27-success.png
|
227
174
|
- docs/docker-imap.md
|
228
|
-
- docs/setting-up-gmail-with-oauth2.md
|
229
175
|
- imap-backup
|
230
176
|
- imap-backup.gemspec
|
231
177
|
- lib/email/mboxrd/message.rb
|
232
178
|
- lib/email/provider.rb
|
233
|
-
- lib/gmail/authenticator.rb
|
234
|
-
- lib/google/auth/stores/in_memory_token_store.rb
|
235
179
|
- lib/imap/backup.rb
|
236
180
|
- lib/imap/backup/account/connection.rb
|
237
181
|
- lib/imap/backup/account/folder.rb
|
@@ -247,7 +191,6 @@ files:
|
|
247
191
|
- lib/imap/backup/configuration/asker.rb
|
248
192
|
- lib/imap/backup/configuration/connection_tester.rb
|
249
193
|
- lib/imap/backup/configuration/folder_chooser.rb
|
250
|
-
- lib/imap/backup/configuration/gmail_oauth2.rb
|
251
194
|
- lib/imap/backup/configuration/list.rb
|
252
195
|
- lib/imap/backup/configuration/setup.rb
|
253
196
|
- lib/imap/backup/configuration/store.rb
|
@@ -276,15 +219,12 @@ files:
|
|
276
219
|
- spec/support/silence_logging.rb
|
277
220
|
- spec/unit/email/mboxrd/message_spec.rb
|
278
221
|
- spec/unit/email/provider_spec.rb
|
279
|
-
- spec/unit/gmail/authenticator_spec.rb
|
280
|
-
- spec/unit/google/auth/stores/in_memory_token_store_spec.rb
|
281
222
|
- spec/unit/imap/backup/account/connection_spec.rb
|
282
223
|
- spec/unit/imap/backup/account/folder_spec.rb
|
283
224
|
- spec/unit/imap/backup/configuration/account_spec.rb
|
284
225
|
- spec/unit/imap/backup/configuration/asker_spec.rb
|
285
226
|
- spec/unit/imap/backup/configuration/connection_tester_spec.rb
|
286
227
|
- spec/unit/imap/backup/configuration/folder_chooser_spec.rb
|
287
|
-
- spec/unit/imap/backup/configuration/gmail_oauth2_spec.rb
|
288
228
|
- spec/unit/imap/backup/configuration/list_spec.rb
|
289
229
|
- spec/unit/imap/backup/configuration/setup_spec.rb
|
290
230
|
- spec/unit/imap/backup/configuration/store_spec.rb
|
@@ -335,15 +275,12 @@ test_files:
|
|
335
275
|
- spec/support/silence_logging.rb
|
336
276
|
- spec/unit/email/mboxrd/message_spec.rb
|
337
277
|
- spec/unit/email/provider_spec.rb
|
338
|
-
- spec/unit/gmail/authenticator_spec.rb
|
339
|
-
- spec/unit/google/auth/stores/in_memory_token_store_spec.rb
|
340
278
|
- spec/unit/imap/backup/account/connection_spec.rb
|
341
279
|
- spec/unit/imap/backup/account/folder_spec.rb
|
342
280
|
- spec/unit/imap/backup/configuration/account_spec.rb
|
343
281
|
- spec/unit/imap/backup/configuration/asker_spec.rb
|
344
282
|
- spec/unit/imap/backup/configuration/connection_tester_spec.rb
|
345
283
|
- spec/unit/imap/backup/configuration/folder_chooser_spec.rb
|
346
|
-
- spec/unit/imap/backup/configuration/gmail_oauth2_spec.rb
|
347
284
|
- spec/unit/imap/backup/configuration/list_spec.rb
|
348
285
|
- spec/unit/imap/backup/configuration/setup_spec.rb
|
349
286
|
- spec/unit/imap/backup/configuration/store_spec.rb
|
Binary file
|
data/docs/02-new-project.png
DELETED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/docs/08-app-scopes.png
DELETED
Binary file
|
data/docs/09-scope-selection.png
DELETED
Binary file
|
Binary file
|
data/docs/11-test-users.png
DELETED
Binary file
|
data/docs/12-add-users.png
DELETED
Binary file
|
Binary file
|
Binary file
|
data/docs/16-initial-menu.png
DELETED
Binary file
|
Binary file
|
data/docs/18-choose-password.png
DELETED
Binary file
|
Binary file
|
Binary file
|
data/docs/21-accept-warnings.png
DELETED
Binary file
|
data/docs/22-grant-access.png
DELETED
Binary file
|
data/docs/24-confirm-choices.png
DELETED
Binary file
|
data/docs/25-success-code.png
DELETED
Binary file
|
Binary file
|
data/docs/27-success.png
DELETED
Binary file
|
@@ -1,166 +0,0 @@
|
|
1
|
-
# Setting up GMail Authentication for imap-backup
|
2
|
-
|
3
|
-
# Create a Google project
|
4
|
-
|
5
|
-
Go to https://console.developers.google.com
|
6
|
-
|
7
|
-
Select "Credentials".
|
8
|
-
|
9
|
-

|
10
|
-
|
11
|
-
Select "CREATE PROJECT".
|
12
|
-
|
13
|
-

|
14
|
-
|
15
|
-
Set or accept the "Project name",
|
16
|
-
|
17
|
-
And optionally do the same with the "Project ID",
|
18
|
-
|
19
|
-
Leave "Location" on "No organization",
|
20
|
-
|
21
|
-
Click "CREATE".
|
22
|
-
|
23
|
-

|
24
|
-
|
25
|
-
Click "+ CREATE CREDENTIALS".
|
26
|
-
|
27
|
-

|
28
|
-
|
29
|
-
Select "OAuth client ID".
|
30
|
-
|
31
|
-

|
32
|
-
|
33
|
-
Click "CONFIGURE CONSENT SCREEN".
|
34
|
-
|
35
|
-

|
36
|
-
|
37
|
-
Select "External",
|
38
|
-
|
39
|
-
Click "CREATE".
|
40
|
-
|
41
|
-

|
42
|
-
|
43
|
-
Fill in "App name",
|
44
|
-
|
45
|
-
Select your email as "User support email",
|
46
|
-
|
47
|
-
Type in your email at the bottom under "Developer contact information",
|
48
|
-
|
49
|
-
Click "SAVE AND CONTINUE".
|
50
|
-
|
51
|
-

|
52
|
-
|
53
|
-
Click "ADD OR REMOVE SCOPES".
|
54
|
-
|
55
|
-

|
56
|
-
|
57
|
-
Under "Manually add scopes", type "https://mail.google.com/",
|
58
|
-
|
59
|
-
Click "ADD TO TABLE",
|
60
|
-
|
61
|
-
Click "UPDATE".
|
62
|
-
|
63
|
-

|
64
|
-
|
65
|
-
Click "SAVE AND CONTINUE".
|
66
|
-
|
67
|
-

|
68
|
-
|
69
|
-
Click "+ ADD USERS".
|
70
|
-
|
71
|
-

|
72
|
-
|
73
|
-
Type in your email,
|
74
|
-
|
75
|
-
Click "SAVE AND CONTINUE",
|
76
|
-
|
77
|
-
Click "BACK TO DASHBOARD",
|
78
|
-
|
79
|
-
Click "Credentials" in the menu
|
80
|
-
|
81
|
-
And then click "+ CREATE CREDENTIALS" again,
|
82
|
-
|
83
|
-
And select "OAuth client ID" again.
|
84
|
-
|
85
|
-

|
86
|
-
|
87
|
-
This time you will be able to proceed.
|
88
|
-
|
89
|
-

|
90
|
-
|
91
|
-
Select "TVs and limited input devices",
|
92
|
-
|
93
|
-
Click "CREATE",
|
94
|
-
|
95
|
-
Copy both "Your Client ID"
|
96
|
-
|
97
|
-
And "Your Client Secret".
|
98
|
-
|
99
|
-
# Set up imap-backup
|
100
|
-
|
101
|
-
Run `imap-backup setup`.
|
102
|
-
|
103
|
-

|
104
|
-
|
105
|
-
Choose 'add account'.
|
106
|
-
|
107
|
-

|
108
|
-
|
109
|
-
Type in your GMail address.
|
110
|
-
|
111
|
-
Note: if you have a custom domain (GSuite) address,
|
112
|
-
e.g. "me@mycompany.com", you now need to
|
113
|
-
choose 'server' and
|
114
|
-
type in 'imap.gmail.com'.
|
115
|
-
|
116
|
-

|
117
|
-
|
118
|
-
Choose `password`.
|
119
|
-
|
120
|
-

|
121
|
-
|
122
|
-
Type your "Client ID" and "Client Secret",
|
123
|
-
|
124
|
-
Next you will be shown a URL to open in your browser.
|
125
|
-
|
126
|
-

|
127
|
-
|
128
|
-
If you have more than one GMail account you will need to choose which
|
129
|
-
you are configuring.
|
130
|
-
|
131
|
-

|
132
|
-
|
133
|
-
As the project "app" is in test mode,
|
134
|
-
you'll need to accept to ignore this warning.
|
135
|
-
|
136
|
-
Click "Advanced",
|
137
|
-
|
138
|
-
Then click "Go to XXXYYYZZZ (unsafe)"
|
139
|
-
|
140
|
-

|
141
|
-
|
142
|
-
Choose "Allow".
|
143
|
-
|
144
|
-

|
145
|
-
|
146
|
-
Choose "Allow".
|
147
|
-
|
148
|
-

|
149
|
-
|
150
|
-
Click on the copy logo to copy the success code.
|
151
|
-
|
152
|
-

|
153
|
-
|
154
|
-
Paste the success code into imap-backup.
|
155
|
-
|
156
|
-
Finally, choose 'test connection'.
|
157
|
-
|
158
|
-
If all has gone well you should see this:
|
159
|
-
|
160
|
-

|
161
|
-
|
162
|
-
Now choose 'return to main menu',
|
163
|
-
|
164
|
-
Then 'save and exit'.
|
165
|
-
|
166
|
-
Your imap-backup is now configured to back up your GMail.
|
data/lib/gmail/authenticator.rb
DELETED
@@ -1,160 +0,0 @@
|
|
1
|
-
require "googleauth"
|
2
|
-
require "google/auth/stores/in_memory_token_store"
|
3
|
-
|
4
|
-
module Gmail; end
|
5
|
-
|
6
|
-
class Gmail::Authenticator
|
7
|
-
class MalformedImapBackupToken < StandardError; end
|
8
|
-
|
9
|
-
class ImapBackupToken
|
10
|
-
attr_reader :token
|
11
|
-
|
12
|
-
def self.from(
|
13
|
-
access_token:,
|
14
|
-
client_id:,
|
15
|
-
client_secret:,
|
16
|
-
expiration_time_millis:,
|
17
|
-
refresh_token:
|
18
|
-
)
|
19
|
-
{
|
20
|
-
access_token: access_token,
|
21
|
-
client_id: client_id,
|
22
|
-
client_secret: client_secret,
|
23
|
-
expiration_time_millis: expiration_time_millis,
|
24
|
-
refresh_token: refresh_token
|
25
|
-
}.to_json
|
26
|
-
end
|
27
|
-
|
28
|
-
def initialize(token)
|
29
|
-
@token = token
|
30
|
-
end
|
31
|
-
|
32
|
-
def valid?
|
33
|
-
return false if !body
|
34
|
-
return false if !access_token
|
35
|
-
return false if !client_id
|
36
|
-
return false if !client_secret
|
37
|
-
return false if !expiration_time_millis
|
38
|
-
return false if !refresh_token
|
39
|
-
|
40
|
-
true
|
41
|
-
end
|
42
|
-
|
43
|
-
def access_token
|
44
|
-
body["access_token"]
|
45
|
-
end
|
46
|
-
|
47
|
-
def client_id
|
48
|
-
body["client_id"]
|
49
|
-
end
|
50
|
-
|
51
|
-
def client_secret
|
52
|
-
body["client_secret"]
|
53
|
-
end
|
54
|
-
|
55
|
-
def expiration_time_millis
|
56
|
-
body["expiration_time_millis"]
|
57
|
-
end
|
58
|
-
|
59
|
-
def refresh_token
|
60
|
-
body["refresh_token"]
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def body
|
66
|
-
@body ||= JSON.parse(token)
|
67
|
-
rescue JSON::ParserError
|
68
|
-
nil
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
GMAIL_READ_SCOPE = "https://mail.google.com/".freeze
|
73
|
-
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
|
74
|
-
|
75
|
-
attr_reader :email
|
76
|
-
attr_reader :token
|
77
|
-
|
78
|
-
def self.refresh_token?(text)
|
79
|
-
ImapBackupToken.new(text).valid?
|
80
|
-
end
|
81
|
-
|
82
|
-
def initialize(email:, token:)
|
83
|
-
@email = email
|
84
|
-
@token = token
|
85
|
-
end
|
86
|
-
|
87
|
-
def authorization_url
|
88
|
-
authorizer.get_authorization_url(base_url: OOB_URI)
|
89
|
-
end
|
90
|
-
|
91
|
-
def credentials
|
92
|
-
authorizer.get_credentials(email).tap do |c|
|
93
|
-
c.refresh! if c.expired?
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def credentials_from_code(code)
|
98
|
-
authorizer.get_credentials_from_code(
|
99
|
-
user_id: email,
|
100
|
-
code: code,
|
101
|
-
base_url: OOB_URI
|
102
|
-
)
|
103
|
-
end
|
104
|
-
|
105
|
-
private
|
106
|
-
|
107
|
-
def auth_client_id
|
108
|
-
@auth_client_id = Google::Auth::ClientId.new(client_id, client_secret)
|
109
|
-
end
|
110
|
-
|
111
|
-
def authorizer
|
112
|
-
@authorizer ||= Google::Auth::UserAuthorizer.new(
|
113
|
-
auth_client_id, GMAIL_READ_SCOPE, token_store
|
114
|
-
)
|
115
|
-
end
|
116
|
-
|
117
|
-
def access_token
|
118
|
-
imap_backup_token.access_token
|
119
|
-
end
|
120
|
-
|
121
|
-
def client_id
|
122
|
-
imap_backup_token.client_id
|
123
|
-
end
|
124
|
-
|
125
|
-
def client_secret
|
126
|
-
imap_backup_token.client_secret
|
127
|
-
end
|
128
|
-
|
129
|
-
def expiration_time_millis
|
130
|
-
imap_backup_token.expiration_time_millis
|
131
|
-
end
|
132
|
-
|
133
|
-
def refresh_token
|
134
|
-
imap_backup_token.refresh_token
|
135
|
-
end
|
136
|
-
|
137
|
-
def imap_backup_token
|
138
|
-
@imap_backup_token ||=
|
139
|
-
ImapBackupToken.new(token).tap do |t|
|
140
|
-
raise MalformedImapBackupToken if !t.valid?
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def store_token
|
145
|
-
{
|
146
|
-
"client_id" => client_id,
|
147
|
-
"access_token" => access_token,
|
148
|
-
"refresh_token" => refresh_token,
|
149
|
-
"scope": [GMAIL_READ_SCOPE],
|
150
|
-
"expiration_time_millis": expiration_time_millis
|
151
|
-
}.to_json
|
152
|
-
end
|
153
|
-
|
154
|
-
def token_store
|
155
|
-
@token_store ||=
|
156
|
-
Google::Auth::Stores::InMemoryTokenStore.new.tap do |t|
|
157
|
-
t.store(email, store_token)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
@@ -1,102 +0,0 @@
|
|
1
|
-
module Imap::Backup
|
2
|
-
module Configuration; end
|
3
|
-
|
4
|
-
class Configuration::GmailOauth2
|
5
|
-
BANNER = <<~BANNER.freeze
|
6
|
-
GMail OAuth2 Setup
|
7
|
-
|
8
|
-
You need to authorize imap_backup to get access to your email.
|
9
|
-
To do so, please follow the instructions here:
|
10
|
-
|
11
|
-
https://github.com/joeyates/imap-backup/blob/main/docs/setting-up-gmail-with-oauth2.md
|
12
|
-
|
13
|
-
BANNER
|
14
|
-
|
15
|
-
GMAIL_READ_SCOPE = "https://mail.google.com/".freeze
|
16
|
-
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
|
17
|
-
|
18
|
-
attr_reader :account
|
19
|
-
attr_reader :client_id
|
20
|
-
attr_reader :client_secret
|
21
|
-
|
22
|
-
def initialize(account)
|
23
|
-
@account = account
|
24
|
-
end
|
25
|
-
|
26
|
-
def run
|
27
|
-
Kernel.system("clear")
|
28
|
-
Kernel.puts BANNER
|
29
|
-
|
30
|
-
keep = if token.valid?
|
31
|
-
highline.agree("Use existing client info?")
|
32
|
-
else
|
33
|
-
false
|
34
|
-
end
|
35
|
-
|
36
|
-
if keep
|
37
|
-
@client_id = token.client_id
|
38
|
-
@client_secret = token.client_secret
|
39
|
-
else
|
40
|
-
@client_id = highline.ask("client_id: ")
|
41
|
-
@client_secret = highline.ask("client_secret: ")
|
42
|
-
end
|
43
|
-
|
44
|
-
Kernel.puts <<~MESSAGE
|
45
|
-
|
46
|
-
Open the following URL in your browser
|
47
|
-
|
48
|
-
#{authorization_url}
|
49
|
-
|
50
|
-
Then copy the success code
|
51
|
-
|
52
|
-
MESSAGE
|
53
|
-
|
54
|
-
@code = highline.ask("success code: ")
|
55
|
-
@credentials = authorizer.get_and_store_credentials_from_code(
|
56
|
-
user_id: email, code: @code, base_url: OOB_URI
|
57
|
-
)
|
58
|
-
|
59
|
-
raise "Failed" if !@credentials
|
60
|
-
|
61
|
-
new_token = JSON.parse(token_store.load(email))
|
62
|
-
new_token["client_secret"] = client_secret
|
63
|
-
new_token.to_json
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def email
|
69
|
-
account[:username]
|
70
|
-
end
|
71
|
-
|
72
|
-
def password
|
73
|
-
account[:password]
|
74
|
-
end
|
75
|
-
|
76
|
-
def token
|
77
|
-
@token ||= Gmail::Authenticator::ImapBackupToken.new(password)
|
78
|
-
end
|
79
|
-
|
80
|
-
def highline
|
81
|
-
Configuration::Setup.highline
|
82
|
-
end
|
83
|
-
|
84
|
-
def auth_client_id
|
85
|
-
@auth_client_id = Google::Auth::ClientId.new(client_id, client_secret)
|
86
|
-
end
|
87
|
-
|
88
|
-
def authorizer
|
89
|
-
@authorizer ||= Google::Auth::UserAuthorizer.new(
|
90
|
-
auth_client_id, GMAIL_READ_SCOPE, token_store
|
91
|
-
)
|
92
|
-
end
|
93
|
-
|
94
|
-
def token_store
|
95
|
-
@token_store ||= Google::Auth::Stores::InMemoryTokenStore.new
|
96
|
-
end
|
97
|
-
|
98
|
-
def authorization_url
|
99
|
-
authorizer.get_authorization_url(base_url: OOB_URI)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,138 +0,0 @@
|
|
1
|
-
require "gmail/authenticator"
|
2
|
-
require "googleauth"
|
3
|
-
|
4
|
-
describe Gmail::Authenticator do
|
5
|
-
ACCESS_TOKEN = "access_token".freeze
|
6
|
-
AUTHORIZATION_URL = "authorization_url".freeze
|
7
|
-
CLIENT_ID = "client_id".freeze
|
8
|
-
CLIENT_SECRET = "client_secret".freeze
|
9
|
-
CODE = "code".freeze
|
10
|
-
CREDENTIALS = "credentials".freeze
|
11
|
-
EMAIL = "email".freeze
|
12
|
-
EXPIRATION_TIME_MILLIS = "expiration_time_millis".freeze
|
13
|
-
GMAIL_READ_SCOPE = "https://mail.google.com/".freeze
|
14
|
-
IMAP_BACKUP_TOKEN = "imap_backup_token".freeze
|
15
|
-
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
|
16
|
-
REFRESH_TOKEN = "refresh_token".freeze
|
17
|
-
|
18
|
-
subject { described_class.new(**params) }
|
19
|
-
|
20
|
-
let(:params) do
|
21
|
-
{
|
22
|
-
email: EMAIL,
|
23
|
-
token: IMAP_BACKUP_TOKEN
|
24
|
-
}
|
25
|
-
end
|
26
|
-
|
27
|
-
let(:authorizer) do
|
28
|
-
instance_double(Google::Auth::UserAuthorizer)
|
29
|
-
end
|
30
|
-
|
31
|
-
let(:imap_backup_token) do
|
32
|
-
instance_double(
|
33
|
-
Gmail::Authenticator::ImapBackupToken,
|
34
|
-
access_token: ACCESS_TOKEN,
|
35
|
-
client_id: CLIENT_ID,
|
36
|
-
client_secret: CLIENT_SECRET,
|
37
|
-
expiration_time_millis: EXPIRATION_TIME_MILLIS,
|
38
|
-
refresh_token: REFRESH_TOKEN,
|
39
|
-
valid?: true
|
40
|
-
)
|
41
|
-
end
|
42
|
-
|
43
|
-
let(:token_store) do
|
44
|
-
instance_double(Google::Auth::Stores::InMemoryTokenStore)
|
45
|
-
end
|
46
|
-
|
47
|
-
let(:credentials) do
|
48
|
-
instance_double(Google::Auth::UserRefreshCredentials, refresh!: true)
|
49
|
-
end
|
50
|
-
|
51
|
-
let(:expired) { false }
|
52
|
-
|
53
|
-
before do
|
54
|
-
allow(Google::Auth::UserAuthorizer).
|
55
|
-
to receive(:new).
|
56
|
-
with(
|
57
|
-
instance_of(Google::Auth::ClientId),
|
58
|
-
GMAIL_READ_SCOPE,
|
59
|
-
token_store
|
60
|
-
) { authorizer }
|
61
|
-
allow(authorizer).to receive(:get_authorization_url).
|
62
|
-
with(base_url: OOB_URI) { AUTHORIZATION_URL }
|
63
|
-
allow(authorizer).to receive(:get_credentials).
|
64
|
-
with(EMAIL) { credentials }
|
65
|
-
allow(authorizer).to receive(:get_credentials_from_code).
|
66
|
-
with(user_id: EMAIL, code: CODE, base_url: OOB_URI) { CREDENTIALS }
|
67
|
-
|
68
|
-
allow(Google::Auth::UserRefreshCredentials).
|
69
|
-
to receive(:new) { credentials }
|
70
|
-
allow(credentials).to receive(:expired?) { expired }
|
71
|
-
|
72
|
-
allow(Google::Auth::Stores::InMemoryTokenStore).
|
73
|
-
to receive(:new) { token_store }
|
74
|
-
allow(token_store).to receive(:store).
|
75
|
-
with(EMAIL, anything) # TODO: use a JSON matcher
|
76
|
-
allow(Gmail::Authenticator::ImapBackupToken).
|
77
|
-
to receive(:new).
|
78
|
-
with(IMAP_BACKUP_TOKEN) { imap_backup_token }
|
79
|
-
end
|
80
|
-
|
81
|
-
describe "#initialize" do
|
82
|
-
[:email, :token].each do |param|
|
83
|
-
context "parameter #{param}" do
|
84
|
-
let(:params) { super().dup.reject { |k| k == param } }
|
85
|
-
|
86
|
-
it "is expected" do
|
87
|
-
expect { subject }.to raise_error(
|
88
|
-
ArgumentError, /missing keyword: :?#{param}/
|
89
|
-
)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
describe "#credentials" do
|
96
|
-
let!(:result) { subject.credentials }
|
97
|
-
|
98
|
-
it "attempts to get credentials" do
|
99
|
-
expect(authorizer).to have_received(:get_credentials)
|
100
|
-
end
|
101
|
-
|
102
|
-
it "returns the result" do
|
103
|
-
expect(result).to eq(credentials)
|
104
|
-
end
|
105
|
-
|
106
|
-
context "when the access_token has expired" do
|
107
|
-
let(:expired) { true }
|
108
|
-
|
109
|
-
it "refreshes it" do
|
110
|
-
expect(credentials).to have_received(:refresh!)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
describe "#authorization_url" do
|
116
|
-
let!(:result) { subject.authorization_url }
|
117
|
-
|
118
|
-
it "requests an authorization URL" do
|
119
|
-
expect(authorizer).to have_received(:get_authorization_url)
|
120
|
-
end
|
121
|
-
|
122
|
-
it "returns the result" do
|
123
|
-
expect(result).to eq(AUTHORIZATION_URL)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
describe "#credentials_from_code" do
|
128
|
-
let!(:result) { subject.credentials_from_code(CODE) }
|
129
|
-
|
130
|
-
it "requests credentials" do
|
131
|
-
expect(authorizer).to have_received(:get_credentials_from_code)
|
132
|
-
end
|
133
|
-
|
134
|
-
it "returns credentials" do
|
135
|
-
expect(result).to eq(CREDENTIALS)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
require "google/auth/stores/in_memory_token_store"
|
2
|
-
|
3
|
-
describe Google::Auth::Stores::InMemoryTokenStore do
|
4
|
-
KEY = "key".freeze
|
5
|
-
VALUE = "value".freeze
|
6
|
-
|
7
|
-
subject { described_class.new }
|
8
|
-
|
9
|
-
describe "#load" do
|
10
|
-
it "returns an item's value" do
|
11
|
-
subject[KEY] = VALUE
|
12
|
-
expect(subject.load(KEY)).to eq(VALUE)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,121 +0,0 @@
|
|
1
|
-
describe Imap::Backup::Configuration::GmailOauth2 do
|
2
|
-
include HighLineTestHelpers
|
3
|
-
|
4
|
-
CLIENT_ID = "my_client_id".freeze
|
5
|
-
CLIENT_SECRET = "my_client_secret".freeze
|
6
|
-
|
7
|
-
subject { described_class.new(account) }
|
8
|
-
|
9
|
-
let(:authorization_url) { "some long authorization_url" }
|
10
|
-
let(:credentials) { "credentials" }
|
11
|
-
let(:json_token) { '{"sentinel":"foo"}' }
|
12
|
-
let!(:highline_streams) { prepare_highline }
|
13
|
-
let(:highline) { Imap::Backup::Configuration::Setup.highline }
|
14
|
-
let(:input) { highline_streams[0] }
|
15
|
-
let(:output) { highline_streams[1] }
|
16
|
-
let(:account) { {} }
|
17
|
-
let(:user_input) { %W(my_client_id\n my_secret\n my_code\n) }
|
18
|
-
|
19
|
-
let(:authorizer) do
|
20
|
-
instance_double(
|
21
|
-
Google::Auth::UserAuthorizer,
|
22
|
-
get_authorization_url: authorization_url,
|
23
|
-
get_and_store_credentials_from_code: credentials
|
24
|
-
)
|
25
|
-
end
|
26
|
-
let(:token_store) do
|
27
|
-
instance_double(
|
28
|
-
Google::Auth::Stores::InMemoryTokenStore,
|
29
|
-
load: json_token
|
30
|
-
)
|
31
|
-
end
|
32
|
-
let(:token) do
|
33
|
-
instance_double(
|
34
|
-
Gmail::Authenticator::ImapBackupToken,
|
35
|
-
valid?: valid,
|
36
|
-
client_id: CLIENT_ID,
|
37
|
-
client_secret: CLIENT_SECRET
|
38
|
-
)
|
39
|
-
end
|
40
|
-
let(:valid) { false }
|
41
|
-
|
42
|
-
before do
|
43
|
-
allow(Google::Auth::UserAuthorizer).
|
44
|
-
to receive(:new) { authorizer }
|
45
|
-
allow(Google::Auth::Stores::InMemoryTokenStore).
|
46
|
-
to receive(:new) { token_store }
|
47
|
-
allow(Gmail::Authenticator::ImapBackupToken).
|
48
|
-
to receive(:new) { token }
|
49
|
-
|
50
|
-
allow(highline).to receive(:ask).and_call_original
|
51
|
-
allow(highline).to receive(:agree).and_call_original
|
52
|
-
|
53
|
-
allow(Kernel).to receive(:system)
|
54
|
-
allow(Kernel).to receive(:puts)
|
55
|
-
|
56
|
-
allow(input).to receive(:gets).and_return(*user_input)
|
57
|
-
end
|
58
|
-
|
59
|
-
describe "#run" do
|
60
|
-
let!(:result) { subject.run }
|
61
|
-
|
62
|
-
it "clears the screen" do
|
63
|
-
expect(Kernel).to have_received(:system).with("clear")
|
64
|
-
end
|
65
|
-
|
66
|
-
it "requests client_id" do
|
67
|
-
expect(highline).to have_received(:ask).with("client_id: ")
|
68
|
-
end
|
69
|
-
|
70
|
-
it "requests client_secret" do
|
71
|
-
expect(highline).to have_received(:ask).with("client_secret: ")
|
72
|
-
end
|
73
|
-
|
74
|
-
it "displays the authorization URL" do
|
75
|
-
expect(Kernel).
|
76
|
-
to have_received(:puts).
|
77
|
-
with(/#{authorization_url}/)
|
78
|
-
end
|
79
|
-
|
80
|
-
it "requests the success code" do
|
81
|
-
expect(highline).to have_received(:ask).with("success code: ")
|
82
|
-
end
|
83
|
-
|
84
|
-
it "requests an access_token via the code" do
|
85
|
-
expect(authorizer).to have_received(:get_and_store_credentials_from_code)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "returns the credentials" do
|
89
|
-
expect(result).to match('"sentinel":"foo"')
|
90
|
-
end
|
91
|
-
|
92
|
-
it "includes the client_secret in the credentials" do
|
93
|
-
expect(result).to match('"client_secret":"my_secret"')
|
94
|
-
end
|
95
|
-
|
96
|
-
context "when the account already has client info" do
|
97
|
-
let(:valid) { true }
|
98
|
-
let(:user_input) { %W(yes\n) }
|
99
|
-
|
100
|
-
it "requests confirmation of client info" do
|
101
|
-
expect(highline).to have_received(:agree).with("Use existing client info?")
|
102
|
-
end
|
103
|
-
|
104
|
-
context "when yhe user says 'no'" do
|
105
|
-
let(:user_input) { %W(no\n) }
|
106
|
-
|
107
|
-
it "requests client_id" do
|
108
|
-
expect(highline).to have_received(:ask).with("client_id: ")
|
109
|
-
end
|
110
|
-
|
111
|
-
it "requests client_secret" do
|
112
|
-
expect(highline).to have_received(:ask).with("client_secret: ")
|
113
|
-
end
|
114
|
-
|
115
|
-
it "requests the success code" do
|
116
|
-
expect(highline).to have_received(:ask).with("success code: ")
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|