googleauth 0.8.0 → 0.8.1
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/.kokoro/build.sh +2 -34
- data/.kokoro/continuous/common.cfg +5 -0
- data/.kokoro/continuous/linux.cfg +1 -1
- data/.kokoro/osx.sh +2 -33
- data/.kokoro/presubmit/common.cfg +5 -0
- data/.kokoro/presubmit/linux.cfg +1 -1
- data/.kokoro/release.cfg +53 -0
- data/.kokoro/trampoline.sh +3 -23
- data/.kokoro/windows.sh +2 -30
- data/.rubocop.yml +7 -24
- data/CHANGELOG.md +24 -39
- data/Gemfile +14 -14
- data/README.md +21 -1
- data/Rakefile +84 -10
- data/googleauth.gemspec +23 -23
- data/lib/googleauth.rb +6 -6
- data/lib/googleauth/application_default.rb +11 -11
- data/lib/googleauth/client_id.rb +16 -16
- data/lib/googleauth/compute_engine.rb +27 -27
- data/lib/googleauth/credentials.rb +35 -37
- data/lib/googleauth/credentials_loader.rb +64 -67
- data/lib/googleauth/default_credentials.rb +18 -18
- data/lib/googleauth/iam.rb +9 -9
- data/lib/googleauth/json_key_reader.rb +6 -6
- data/lib/googleauth/scope_util.rb +11 -11
- data/lib/googleauth/service_account.rb +42 -42
- data/lib/googleauth/signet.rb +15 -17
- data/lib/googleauth/stores/file_token_store.rb +8 -8
- data/lib/googleauth/stores/redis_token_store.rb +17 -17
- data/lib/googleauth/token_store.rb +6 -6
- data/lib/googleauth/user_authorizer.rb +55 -59
- data/lib/googleauth/user_refresh.rb +27 -27
- data/lib/googleauth/version.rb +1 -1
- data/lib/googleauth/web_user_authorizer.rb +55 -56
- data/spec/googleauth/apply_auth_examples.rb +46 -46
- data/spec/googleauth/client_id_spec.rb +54 -54
- data/spec/googleauth/compute_engine_spec.rb +41 -41
- data/spec/googleauth/credentials_spec.rb +97 -97
- data/spec/googleauth/get_application_default_spec.rb +114 -114
- data/spec/googleauth/iam_spec.rb +25 -25
- data/spec/googleauth/scope_util_spec.rb +24 -24
- data/spec/googleauth/service_account_spec.rb +204 -194
- data/spec/googleauth/signet_spec.rb +37 -38
- data/spec/googleauth/stores/file_token_store_spec.rb +12 -12
- data/spec/googleauth/stores/redis_token_store_spec.rb +11 -11
- data/spec/googleauth/stores/store_examples.rb +16 -16
- data/spec/googleauth/user_authorizer_spec.rb +120 -121
- data/spec/googleauth/user_refresh_spec.rb +151 -146
- data/spec/googleauth/web_user_authorizer_spec.rb +66 -66
- data/spec/spec_helper.rb +19 -19
- metadata +4 -6
- data/.kokoro/common.cfg +0 -22
- data/.travis.yml +0 -40
|
@@ -27,61 +27,60 @@
|
|
|
27
27
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
28
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
|
|
30
|
-
spec_dir = File.expand_path
|
|
31
|
-
$LOAD_PATH.unshift
|
|
30
|
+
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
|
|
31
|
+
$LOAD_PATH.unshift spec_dir
|
|
32
32
|
$LOAD_PATH.uniq!
|
|
33
33
|
|
|
34
|
-
require
|
|
35
|
-
require
|
|
36
|
-
require
|
|
37
|
-
require
|
|
38
|
-
require
|
|
34
|
+
require "apply_auth_examples"
|
|
35
|
+
require "googleauth/signet"
|
|
36
|
+
require "jwt"
|
|
37
|
+
require "openssl"
|
|
38
|
+
require "spec_helper"
|
|
39
39
|
|
|
40
40
|
describe Signet::OAuth2::Client do
|
|
41
|
-
before
|
|
42
|
-
@key = OpenSSL::PKey::RSA.new
|
|
41
|
+
before :example do
|
|
42
|
+
@key = OpenSSL::PKey::RSA.new 2048
|
|
43
43
|
@client = Signet::OAuth2::Client.new(
|
|
44
|
-
token_credential_uri:
|
|
45
|
-
scope:
|
|
46
|
-
issuer:
|
|
47
|
-
audience:
|
|
48
|
-
signing_key:
|
|
44
|
+
token_credential_uri: "https://oauth2.googleapis.com/token",
|
|
45
|
+
scope: "https://www.googleapis.com/auth/userinfo.profile",
|
|
46
|
+
issuer: "app@example.com",
|
|
47
|
+
audience: "https://oauth2.googleapis.com/token",
|
|
48
|
+
signing_key: @key
|
|
49
49
|
)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
def make_auth_stubs
|
|
53
|
-
access_token = opts[:access_token] ||
|
|
54
|
-
body = MultiJson.dump(
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
def make_auth_stubs opts
|
|
53
|
+
access_token = opts[:access_token] || ""
|
|
54
|
+
body = MultiJson.dump("access_token" => access_token,
|
|
55
|
+
"token_type" => "Bearer",
|
|
56
|
+
"expires_in" => 3600)
|
|
57
57
|
blk = proc do |request|
|
|
58
|
-
params = Addressable::URI.form_unencode
|
|
59
|
-
_claim, _header = JWT.decode(params.assoc(
|
|
58
|
+
params = Addressable::URI.form_unencode request.body
|
|
59
|
+
_claim, _header = JWT.decode(params.assoc("assertion").last,
|
|
60
60
|
@key.public_key, true,
|
|
61
|
-
algorithm:
|
|
61
|
+
algorithm: "RS256")
|
|
62
62
|
end
|
|
63
|
-
with_params = {body: hash_including(
|
|
64
|
-
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
stub_request(:post, 'https://oauth2.googleapis.com/token')
|
|
63
|
+
with_params = { body: hash_including(
|
|
64
|
+
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
|
65
|
+
) }
|
|
66
|
+
with_params[:headers] = { "User-Agent" => opts[:user_agent] } if opts[:user_agent]
|
|
67
|
+
stub_request(:post, "https://oauth2.googleapis.com/token")
|
|
69
68
|
.with(with_params, &blk)
|
|
70
|
-
.to_return(body:
|
|
71
|
-
status:
|
|
72
|
-
headers: {
|
|
69
|
+
.to_return(body: body,
|
|
70
|
+
status: 200,
|
|
71
|
+
headers: { "Content-Type" => "application/json" })
|
|
73
72
|
end
|
|
74
73
|
|
|
75
|
-
it_behaves_like
|
|
74
|
+
it_behaves_like "apply/apply! are OK"
|
|
76
75
|
|
|
77
76
|
describe "#configure_connection" do
|
|
78
77
|
it "honors default_connection" do
|
|
79
78
|
token = "1/abcdef1234567890"
|
|
80
79
|
stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/1.0"
|
|
81
|
-
conn = Faraday.new headers: {"User-Agent" => "RubyRocks/1.0"}
|
|
82
|
-
@client.configure_connection
|
|
80
|
+
conn = Faraday.new headers: { "User-Agent" => "RubyRocks/1.0" }
|
|
81
|
+
@client.configure_connection default_connection: conn
|
|
83
82
|
md = { foo: "bar" }
|
|
84
|
-
@client.apply!
|
|
83
|
+
@client.apply! md
|
|
85
84
|
want = { foo: "bar", authorization: "Bearer #{token}" }
|
|
86
85
|
expect(md).to eq(want)
|
|
87
86
|
expect(stub).to have_been_requested
|
|
@@ -91,11 +90,11 @@ describe Signet::OAuth2::Client do
|
|
|
91
90
|
token = "1/abcdef1234567890"
|
|
92
91
|
stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/2.0"
|
|
93
92
|
connection_builder = proc do
|
|
94
|
-
Faraday.new headers: {"User-Agent" => "RubyRocks/2.0"}
|
|
93
|
+
Faraday.new headers: { "User-Agent" => "RubyRocks/2.0" }
|
|
95
94
|
end
|
|
96
|
-
@client.configure_connection
|
|
95
|
+
@client.configure_connection connection_builder: connection_builder
|
|
97
96
|
md = { foo: "bar" }
|
|
98
|
-
@client.apply!
|
|
97
|
+
@client.apply! md
|
|
99
98
|
want = { foo: "bar", authorization: "Bearer #{token}" }
|
|
100
99
|
expect(md).to eq(want)
|
|
101
100
|
expect(stub).to have_been_requested
|
|
@@ -27,31 +27,31 @@
|
|
|
27
27
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
28
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
|
|
30
|
-
spec_dir = File.expand_path
|
|
31
|
-
$LOAD_PATH.unshift
|
|
30
|
+
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
|
|
31
|
+
$LOAD_PATH.unshift spec_dir
|
|
32
32
|
$LOAD_PATH.uniq!
|
|
33
33
|
|
|
34
|
-
require
|
|
35
|
-
require
|
|
36
|
-
require
|
|
37
|
-
require
|
|
38
|
-
require
|
|
39
|
-
require
|
|
34
|
+
require "googleauth"
|
|
35
|
+
require "googleauth/stores/file_token_store"
|
|
36
|
+
require "spec_helper"
|
|
37
|
+
require "fakefs/safe"
|
|
38
|
+
require "fakefs/spec_helpers"
|
|
39
|
+
require "googleauth/stores/store_examples"
|
|
40
40
|
|
|
41
41
|
module FakeFS
|
|
42
42
|
class File
|
|
43
43
|
# FakeFS doesn't implement. And since we don't need to actually lock,
|
|
44
44
|
# just stub out...
|
|
45
|
-
def flock
|
|
45
|
+
def flock *; end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
describe Google::Auth::Stores::FileTokenStore do
|
|
50
50
|
include FakeFS::SpecHelpers
|
|
51
51
|
|
|
52
|
-
let
|
|
53
|
-
Google::Auth::Stores::FileTokenStore.new
|
|
52
|
+
let :store do
|
|
53
|
+
Google::Auth::Stores::FileTokenStore.new file: "/tokens.yaml"
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
it_behaves_like
|
|
56
|
+
it_behaves_like "token store"
|
|
57
57
|
end
|
|
@@ -27,24 +27,24 @@
|
|
|
27
27
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
28
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
|
|
30
|
-
spec_dir = File.expand_path
|
|
31
|
-
$LOAD_PATH.unshift
|
|
30
|
+
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
|
|
31
|
+
$LOAD_PATH.unshift spec_dir
|
|
32
32
|
$LOAD_PATH.uniq!
|
|
33
33
|
|
|
34
|
-
require
|
|
35
|
-
require
|
|
36
|
-
require
|
|
37
|
-
require
|
|
38
|
-
require
|
|
34
|
+
require "googleauth"
|
|
35
|
+
require "googleauth/stores/redis_token_store"
|
|
36
|
+
require "spec_helper"
|
|
37
|
+
require "fakeredis/rspec"
|
|
38
|
+
require "googleauth/stores/store_examples"
|
|
39
39
|
|
|
40
40
|
describe Google::Auth::Stores::RedisTokenStore do
|
|
41
|
-
let
|
|
41
|
+
let :redis do
|
|
42
42
|
Redis.new
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
let
|
|
46
|
-
Google::Auth::Stores::RedisTokenStore.new
|
|
45
|
+
let :store do
|
|
46
|
+
Google::Auth::Stores::RedisTokenStore.new redis: redis
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
it_behaves_like
|
|
49
|
+
it_behaves_like "token store"
|
|
50
50
|
end
|
|
@@ -27,32 +27,32 @@
|
|
|
27
27
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
28
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
|
|
30
|
-
spec_dir = File.expand_path
|
|
31
|
-
$LOAD_PATH.unshift
|
|
30
|
+
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
|
|
31
|
+
$LOAD_PATH.unshift spec_dir
|
|
32
32
|
$LOAD_PATH.uniq!
|
|
33
33
|
|
|
34
|
-
require
|
|
34
|
+
require "spec_helper"
|
|
35
35
|
|
|
36
|
-
shared_examples
|
|
37
|
-
before
|
|
38
|
-
store.store
|
|
36
|
+
shared_examples "token store" do
|
|
37
|
+
before :each do
|
|
38
|
+
store.store "default", "test"
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
it
|
|
42
|
-
expect(store.load(
|
|
41
|
+
it "should return a stored value" do
|
|
42
|
+
expect(store.load("default")).to eq "test"
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
it
|
|
46
|
-
expect(store.load(
|
|
45
|
+
it "should return nil for missing tokens" do
|
|
46
|
+
expect(store.load("notavalidkey")).to be_nil
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
it
|
|
50
|
-
store.delete
|
|
51
|
-
expect(store.load(
|
|
49
|
+
it "should return nil for deleted tokens" do
|
|
50
|
+
store.delete "default"
|
|
51
|
+
expect(store.load("default")).to be_nil
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
it
|
|
55
|
-
store.store
|
|
56
|
-
expect(store.load(
|
|
54
|
+
it "should save overwrite values on store" do
|
|
55
|
+
store.store "default", "test2"
|
|
56
|
+
expect(store.load("default")).to eq "test2"
|
|
57
57
|
end
|
|
58
58
|
end
|
|
@@ -27,295 +27,294 @@
|
|
|
27
27
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
28
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
|
|
30
|
-
spec_dir = File.expand_path
|
|
31
|
-
$LOAD_PATH.unshift
|
|
30
|
+
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
|
|
31
|
+
$LOAD_PATH.unshift spec_dir
|
|
32
32
|
$LOAD_PATH.uniq!
|
|
33
33
|
|
|
34
|
-
require
|
|
35
|
-
require
|
|
36
|
-
require
|
|
37
|
-
require
|
|
38
|
-
require
|
|
34
|
+
require "googleauth"
|
|
35
|
+
require "googleauth/user_authorizer"
|
|
36
|
+
require "uri"
|
|
37
|
+
require "multi_json"
|
|
38
|
+
require "spec_helper"
|
|
39
39
|
|
|
40
40
|
describe Google::Auth::UserAuthorizer do
|
|
41
41
|
include TestHelpers
|
|
42
42
|
|
|
43
|
-
let(:client_id) { Google::Auth::ClientId.new
|
|
44
|
-
let(:scope) { %w
|
|
43
|
+
let(:client_id) { Google::Auth::ClientId.new "testclient", "notasecret" }
|
|
44
|
+
let(:scope) { %w[email profile] }
|
|
45
45
|
let(:token_store) { DummyTokenStore.new }
|
|
46
|
-
let(:callback_uri) {
|
|
47
|
-
let
|
|
46
|
+
let(:callback_uri) { "https://www.example.com/oauth/callback" }
|
|
47
|
+
let :authorizer do
|
|
48
48
|
Google::Auth::UserAuthorizer.new(client_id,
|
|
49
49
|
scope,
|
|
50
50
|
token_store,
|
|
51
51
|
callback_uri)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
shared_examples
|
|
55
|
-
it
|
|
54
|
+
shared_examples "valid authorization url" do
|
|
55
|
+
it "should have a valid base URI" do
|
|
56
56
|
expect(uri).to match %r{https://accounts.google.com/o/oauth2/auth}
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
it
|
|
59
|
+
it "should request offline access" do
|
|
60
60
|
expect(URI(uri).query).to match(/access_type=offline/)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
it
|
|
63
|
+
it "should request response type code" do
|
|
64
64
|
expect(URI(uri).query).to match(/response_type=code/)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
it
|
|
67
|
+
it "should force approval" do
|
|
68
68
|
expect(URI(uri).query).to match(/approval_prompt=force/)
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
it
|
|
71
|
+
it "should include granted scopes" do
|
|
72
72
|
expect(URI(uri).query).to match(/include_granted_scopes=true/)
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
it
|
|
75
|
+
it "should include the correct client id" do
|
|
76
76
|
expect(URI(uri).query).to match(/client_id=testclient/)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
it
|
|
79
|
+
it "should not include a client secret" do
|
|
80
80
|
expect(URI(uri).query).to_not match(/client_secret/)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
it
|
|
83
|
+
it "should include the callback uri" do
|
|
84
84
|
expect(URI(uri).query).to match(
|
|
85
85
|
%r{redirect_uri=https://www.example.com/oauth/callback}
|
|
86
86
|
)
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
it
|
|
89
|
+
it "should include the scope" do
|
|
90
90
|
expect(URI(uri).query).to match(/scope=email%20profile/)
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
context
|
|
95
|
-
let
|
|
96
|
-
authorizer.get_authorization_url
|
|
94
|
+
context "when generating authorization URLs with user ID & state" do
|
|
95
|
+
let :uri do
|
|
96
|
+
authorizer.get_authorization_url login_hint: "user1", state: "mystate"
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
it_behaves_like
|
|
99
|
+
it_behaves_like "valid authorization url"
|
|
100
100
|
|
|
101
|
-
it
|
|
101
|
+
it "includes a login hint" do
|
|
102
102
|
expect(URI(uri).query).to match(/login_hint=user1/)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
it
|
|
105
|
+
it "includes the app state" do
|
|
106
106
|
expect(URI(uri).query).to match(/state=mystate/)
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
context
|
|
111
|
-
let(:uri) { authorizer.get_authorization_url
|
|
110
|
+
context "when generating authorization URLs with user ID and no state" do
|
|
111
|
+
let(:uri) { authorizer.get_authorization_url login_hint: "user1" }
|
|
112
112
|
|
|
113
|
-
it_behaves_like
|
|
113
|
+
it_behaves_like "valid authorization url"
|
|
114
114
|
|
|
115
|
-
it
|
|
115
|
+
it "includes a login hint" do
|
|
116
116
|
expect(URI(uri).query).to match(/login_hint=user1/)
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
it
|
|
119
|
+
it "does not include the state parameter" do
|
|
120
120
|
expect(URI(uri).query).to_not match(/state/)
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
context
|
|
124
|
+
context "when generating authorization URLs with no user ID and no state" do
|
|
125
125
|
let(:uri) { authorizer.get_authorization_url }
|
|
126
126
|
|
|
127
|
-
it_behaves_like
|
|
127
|
+
it_behaves_like "valid authorization url"
|
|
128
128
|
|
|
129
|
-
it
|
|
129
|
+
it "does not include the login hint parameter" do
|
|
130
130
|
expect(URI(uri).query).to_not match(/login_hint/)
|
|
131
131
|
end
|
|
132
132
|
|
|
133
|
-
it
|
|
133
|
+
it "does not include the state parameter" do
|
|
134
134
|
expect(URI(uri).query).to_not match(/state/)
|
|
135
135
|
end
|
|
136
136
|
end
|
|
137
137
|
|
|
138
|
-
context
|
|
139
|
-
let
|
|
138
|
+
context "when retrieving tokens" do
|
|
139
|
+
let :token_json do
|
|
140
140
|
MultiJson.dump(
|
|
141
|
-
access_token:
|
|
142
|
-
refresh_token:
|
|
141
|
+
access_token: "accesstoken",
|
|
142
|
+
refresh_token: "refreshtoken",
|
|
143
143
|
expiration_time_millis: 1_441_234_742_000
|
|
144
144
|
)
|
|
145
145
|
end
|
|
146
146
|
|
|
147
|
-
context
|
|
148
|
-
let
|
|
149
|
-
token_store.store
|
|
150
|
-
authorizer.get_credentials
|
|
147
|
+
context "with a valid user id" do
|
|
148
|
+
let :credentials do
|
|
149
|
+
token_store.store "user1", token_json
|
|
150
|
+
authorizer.get_credentials "user1"
|
|
151
151
|
end
|
|
152
152
|
|
|
153
|
-
it
|
|
153
|
+
it "should return an instance of UserRefreshCredentials" do
|
|
154
154
|
expect(credentials).to be_instance_of(
|
|
155
155
|
Google::Auth::UserRefreshCredentials
|
|
156
156
|
)
|
|
157
157
|
end
|
|
158
158
|
|
|
159
|
-
it
|
|
160
|
-
expect(credentials.refresh_token).to eq
|
|
159
|
+
it "should return credentials with a valid refresh token" do
|
|
160
|
+
expect(credentials.refresh_token).to eq "refreshtoken"
|
|
161
161
|
end
|
|
162
162
|
|
|
163
|
-
it
|
|
164
|
-
expect(credentials.access_token).to eq
|
|
163
|
+
it "should return credentials with a valid access token" do
|
|
164
|
+
expect(credentials.access_token).to eq "accesstoken"
|
|
165
165
|
end
|
|
166
166
|
|
|
167
|
-
it
|
|
168
|
-
expect(credentials.client_id).to eq
|
|
167
|
+
it "should return credentials with a valid client ID" do
|
|
168
|
+
expect(credentials.client_id).to eq "testclient"
|
|
169
169
|
end
|
|
170
170
|
|
|
171
|
-
it
|
|
172
|
-
expect(credentials.client_secret).to eq
|
|
171
|
+
it "should return credentials with a valid client secret" do
|
|
172
|
+
expect(credentials.client_secret).to eq "notasecret"
|
|
173
173
|
end
|
|
174
174
|
|
|
175
|
-
it
|
|
176
|
-
expect(credentials.scope).to eq %w
|
|
175
|
+
it "should return credentials with a valid scope" do
|
|
176
|
+
expect(credentials.scope).to eq %w[email profile]
|
|
177
177
|
end
|
|
178
178
|
|
|
179
|
-
it
|
|
179
|
+
it "should return credentials with a valid expiration time" do
|
|
180
180
|
expect(credentials.expires_at).to eq Time.at(1_441_234_742)
|
|
181
181
|
end
|
|
182
182
|
end
|
|
183
183
|
|
|
184
|
-
context
|
|
185
|
-
it
|
|
186
|
-
expect(authorizer.get_credentials(
|
|
184
|
+
context "with an invalid user id" do
|
|
185
|
+
it "should return nil" do
|
|
186
|
+
expect(authorizer.get_credentials("notauser")).to be_nil
|
|
187
187
|
end
|
|
188
188
|
end
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
-
context
|
|
191
|
+
context "when saving tokens" do
|
|
192
192
|
let(:expiry) { Time.now.to_i }
|
|
193
|
-
let
|
|
193
|
+
let :credentials do
|
|
194
194
|
Google::Auth::UserRefreshCredentials.new(
|
|
195
|
-
client_id:
|
|
195
|
+
client_id: client_id.id,
|
|
196
196
|
client_secret: client_id.secret,
|
|
197
|
-
scope:
|
|
198
|
-
refresh_token:
|
|
199
|
-
access_token:
|
|
200
|
-
expires_at:
|
|
197
|
+
scope: scope,
|
|
198
|
+
refresh_token: "refreshtoken",
|
|
199
|
+
access_token: "accesstoken",
|
|
200
|
+
expires_at: expiry
|
|
201
201
|
)
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
-
let
|
|
205
|
-
authorizer.store_credentials
|
|
206
|
-
token_store.load
|
|
204
|
+
let :token_json do
|
|
205
|
+
authorizer.store_credentials "user1", credentials
|
|
206
|
+
token_store.load "user1"
|
|
207
207
|
end
|
|
208
208
|
|
|
209
|
-
it
|
|
209
|
+
it "should persist in the token store" do
|
|
210
210
|
expect(token_json).to_not be_nil
|
|
211
211
|
end
|
|
212
212
|
|
|
213
|
-
it
|
|
214
|
-
expect(MultiJson.load(token_json)[
|
|
213
|
+
it "should persist the refresh token" do
|
|
214
|
+
expect(MultiJson.load(token_json)["refresh_token"]).to eq "refreshtoken"
|
|
215
215
|
end
|
|
216
216
|
|
|
217
|
-
it
|
|
218
|
-
expect(MultiJson.load(token_json)[
|
|
217
|
+
it "should persist the access token" do
|
|
218
|
+
expect(MultiJson.load(token_json)["access_token"]).to eq "accesstoken"
|
|
219
219
|
end
|
|
220
220
|
|
|
221
|
-
it
|
|
222
|
-
expect(MultiJson.load(token_json)[
|
|
221
|
+
it "should persist the client id" do
|
|
222
|
+
expect(MultiJson.load(token_json)["client_id"]).to eq "testclient"
|
|
223
223
|
end
|
|
224
224
|
|
|
225
|
-
it
|
|
226
|
-
expect(MultiJson.load(token_json)[
|
|
225
|
+
it "should persist the scope" do
|
|
226
|
+
expect(MultiJson.load(token_json)["scope"]).to include("email", "profile")
|
|
227
227
|
end
|
|
228
228
|
|
|
229
|
-
it
|
|
229
|
+
it "should persist the expiry as milliseconds" do
|
|
230
230
|
expected_expiry = expiry * 1000
|
|
231
|
-
expect(MultiJson.load(token_json)[
|
|
231
|
+
expect(MultiJson.load(token_json)["expiration_time_millis"]).to eql(
|
|
232
232
|
expected_expiry
|
|
233
233
|
)
|
|
234
234
|
end
|
|
235
235
|
end
|
|
236
236
|
|
|
237
|
-
context
|
|
238
|
-
let
|
|
239
|
-
MultiJson.dump(
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
context "with valid authorization code" do
|
|
238
|
+
let :token_json do
|
|
239
|
+
MultiJson.dump("access_token" => "1/abc123",
|
|
240
|
+
"token_type" => "Bearer",
|
|
241
|
+
"expires_in" => 3600)
|
|
242
242
|
end
|
|
243
243
|
|
|
244
|
-
before
|
|
245
|
-
stub_request(:post,
|
|
244
|
+
before :example do
|
|
245
|
+
stub_request(:post, "https://oauth2.googleapis.com/token")
|
|
246
246
|
.to_return(body: token_json, status: 200, headers: {
|
|
247
|
-
|
|
247
|
+
"Content-Type" => "application/json"
|
|
248
248
|
})
|
|
249
249
|
end
|
|
250
250
|
|
|
251
|
-
it
|
|
251
|
+
it "should exchange a code for credentials" do
|
|
252
252
|
credentials = authorizer.get_credentials_from_code(
|
|
253
|
-
user_id:
|
|
253
|
+
user_id: "user1", code: "code"
|
|
254
254
|
)
|
|
255
|
-
expect(credentials.access_token).to eq
|
|
255
|
+
expect(credentials.access_token).to eq "1/abc123"
|
|
256
256
|
end
|
|
257
257
|
|
|
258
|
-
it
|
|
259
|
-
authorizer.get_credentials_from_code
|
|
260
|
-
expect(token_store.load(
|
|
258
|
+
it "should not store credentials when get only requested" do
|
|
259
|
+
authorizer.get_credentials_from_code user_id: "user1", code: "code"
|
|
260
|
+
expect(token_store.load("user1")).to be_nil
|
|
261
261
|
end
|
|
262
262
|
|
|
263
|
-
it
|
|
263
|
+
it "should store credentials when requested" do
|
|
264
264
|
authorizer.get_and_store_credentials_from_code(
|
|
265
|
-
user_id:
|
|
265
|
+
user_id: "user1", code: "code"
|
|
266
266
|
)
|
|
267
|
-
expect(token_store.load(
|
|
267
|
+
expect(token_store.load("user1")).to_not be_nil
|
|
268
268
|
end
|
|
269
269
|
end
|
|
270
270
|
|
|
271
|
-
context
|
|
272
|
-
before
|
|
273
|
-
stub_request(:post,
|
|
271
|
+
context "with invalid authorization code" do
|
|
272
|
+
before :example do
|
|
273
|
+
stub_request(:post, "https://oauth2.googleapis.com/token")
|
|
274
274
|
.to_return(status: 400)
|
|
275
275
|
end
|
|
276
276
|
|
|
277
|
-
it
|
|
277
|
+
it "should raise an authorization error" do
|
|
278
278
|
expect do
|
|
279
|
-
authorizer.get_credentials_from_code
|
|
279
|
+
authorizer.get_credentials_from_code user_id: "user1", code: "badcode"
|
|
280
280
|
end.to raise_error Signet::AuthorizationError
|
|
281
281
|
end
|
|
282
282
|
|
|
283
|
-
it
|
|
283
|
+
it "should not store credentials when exchange fails" do
|
|
284
284
|
expect do
|
|
285
|
-
authorizer.get_credentials_from_code
|
|
285
|
+
authorizer.get_credentials_from_code user_id: "user1", code: "badcode"
|
|
286
286
|
end.to raise_error Signet::AuthorizationError
|
|
287
|
-
expect(token_store.load(
|
|
287
|
+
expect(token_store.load("user1")).to be_nil
|
|
288
288
|
end
|
|
289
289
|
end
|
|
290
290
|
|
|
291
|
-
context
|
|
292
|
-
let
|
|
291
|
+
context "when reovking authorization" do
|
|
292
|
+
let :token_json do
|
|
293
293
|
MultiJson.dump(
|
|
294
|
-
access_token:
|
|
295
|
-
refresh_token:
|
|
294
|
+
access_token: "accesstoken",
|
|
295
|
+
refresh_token: "refreshtoken",
|
|
296
296
|
expiration_time_millis: 1_441_234_742_000
|
|
297
297
|
)
|
|
298
298
|
end
|
|
299
299
|
|
|
300
|
-
before
|
|
301
|
-
token_store.store
|
|
302
|
-
stub_request(:post,
|
|
303
|
-
.with(body: hash_including(
|
|
300
|
+
before :example do
|
|
301
|
+
token_store.store "user1", token_json
|
|
302
|
+
stub_request(:post, "https://oauth2.googleapis.com/revoke")
|
|
303
|
+
.with(body: hash_including("token" => "refreshtoken"))
|
|
304
304
|
.to_return(status: 200)
|
|
305
305
|
end
|
|
306
306
|
|
|
307
|
-
it
|
|
308
|
-
authorizer.revoke_authorization
|
|
307
|
+
it "should revoke the grant" do
|
|
308
|
+
authorizer.revoke_authorization "user1"
|
|
309
309
|
expect(a_request(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
)
|
|
310
|
+
:post, "https://oauth2.googleapis.com/revoke"
|
|
311
|
+
).with(body: hash_including("token" => "refreshtoken")))
|
|
313
312
|
.to have_been_made
|
|
314
313
|
end
|
|
315
314
|
|
|
316
|
-
it
|
|
317
|
-
authorizer.revoke_authorization
|
|
318
|
-
expect(token_store.load(
|
|
315
|
+
it "should remove the token from storage" do
|
|
316
|
+
authorizer.revoke_authorization "user1"
|
|
317
|
+
expect(token_store.load("user1")).to be_nil
|
|
319
318
|
end
|
|
320
319
|
end
|
|
321
320
|
|