googleauth 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,9 +27,9 @@
|
|
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
|
-
require
|
31
|
-
require
|
32
|
-
require
|
30
|
+
require "memoist"
|
31
|
+
require "os"
|
32
|
+
require "rbconfig"
|
33
33
|
|
34
34
|
module Google
|
35
35
|
# Module Auth provides classes that provide Google-specific authorization
|
@@ -39,47 +39,45 @@ module Google
|
|
39
39
|
# credentials files on the file system.
|
40
40
|
module CredentialsLoader
|
41
41
|
extend Memoist
|
42
|
-
ENV_VAR =
|
43
|
-
PRIVATE_KEY_VAR =
|
44
|
-
CLIENT_EMAIL_VAR =
|
45
|
-
CLIENT_ID_VAR =
|
46
|
-
CLIENT_SECRET_VAR =
|
47
|
-
REFRESH_TOKEN_VAR =
|
48
|
-
ACCOUNT_TYPE_VAR =
|
49
|
-
PROJECT_ID_VAR =
|
50
|
-
GCLOUD_POSIX_COMMAND =
|
51
|
-
GCLOUD_WINDOWS_COMMAND =
|
52
|
-
GCLOUD_CONFIG_COMMAND =
|
53
|
-
|
54
|
-
CREDENTIALS_FILE_NAME =
|
42
|
+
ENV_VAR = "GOOGLE_APPLICATION_CREDENTIALS".freeze
|
43
|
+
PRIVATE_KEY_VAR = "GOOGLE_PRIVATE_KEY".freeze
|
44
|
+
CLIENT_EMAIL_VAR = "GOOGLE_CLIENT_EMAIL".freeze
|
45
|
+
CLIENT_ID_VAR = "GOOGLE_CLIENT_ID".freeze
|
46
|
+
CLIENT_SECRET_VAR = "GOOGLE_CLIENT_SECRET".freeze
|
47
|
+
REFRESH_TOKEN_VAR = "GOOGLE_REFRESH_TOKEN".freeze
|
48
|
+
ACCOUNT_TYPE_VAR = "GOOGLE_ACCOUNT_TYPE".freeze
|
49
|
+
PROJECT_ID_VAR = "GOOGLE_PROJECT_ID".freeze
|
50
|
+
GCLOUD_POSIX_COMMAND = "gcloud".freeze
|
51
|
+
GCLOUD_WINDOWS_COMMAND = "gcloud.cmd".freeze
|
52
|
+
GCLOUD_CONFIG_COMMAND = "config config-helper --format json".freeze
|
53
|
+
|
54
|
+
CREDENTIALS_FILE_NAME = "application_default_credentials.json".freeze
|
55
55
|
NOT_FOUND_ERROR =
|
56
56
|
"Unable to read the credential file specified by #{ENV_VAR}".freeze
|
57
57
|
WELL_KNOWN_PATH = "gcloud/#{CREDENTIALS_FILE_NAME}".freeze
|
58
|
-
WELL_KNOWN_ERROR =
|
58
|
+
WELL_KNOWN_ERROR = "Unable to read the default credential file".freeze
|
59
59
|
|
60
60
|
SYSTEM_DEFAULT_ERROR =
|
61
|
-
|
61
|
+
"Unable to read the system default credential file".freeze
|
62
62
|
|
63
|
-
CLOUD_SDK_CLIENT_ID =
|
64
|
-
|
63
|
+
CLOUD_SDK_CLIENT_ID = "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.app"\
|
64
|
+
"s.googleusercontent.com".freeze
|
65
65
|
|
66
|
-
CLOUD_SDK_CREDENTIALS_WARNING =
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
CLOUD_SDK_CREDENTIALS_WARNING = "Your application has authenticated "\
|
67
|
+
"using end user credentials from Google Cloud SDK. We recommend that "\
|
68
|
+
"most server applications use service accounts instead. If your "\
|
69
|
+
"application continues to use end user credentials from Cloud SDK, "\
|
70
70
|
'you might receive a "quota exceeded" or "API not enabled" error. For'\
|
71
|
-
|
72
|
-
|
71
|
+
" more information about service accounts, see "\
|
72
|
+
"https://cloud.google.com/docs/authentication/.".freeze
|
73
73
|
|
74
74
|
# make_creds proxies the construction of a credentials instance
|
75
75
|
#
|
76
76
|
# By default, it calls #new on the current class, but this behaviour can
|
77
77
|
# be modified, allowing different instances to be created.
|
78
|
-
def make_creds
|
78
|
+
def make_creds *args
|
79
79
|
creds = new(*args)
|
80
|
-
if creds.respond_to?(:configure_connection) && args.size == 1
|
81
|
-
creds = creds.configure_connection(args[0])
|
82
|
-
end
|
80
|
+
creds = creds.configure_connection args[0] if creds.respond_to?(:configure_connection) && args.size == 1
|
83
81
|
creds
|
84
82
|
end
|
85
83
|
|
@@ -95,16 +93,16 @@ module Google
|
|
95
93
|
# The following keys are recognized:
|
96
94
|
# * `:default_connection` The connection object to use.
|
97
95
|
# * `:connection_builder` A `Proc` that returns a connection.
|
98
|
-
def from_env
|
96
|
+
def from_env scope = nil, options = {}
|
99
97
|
options = interpret_options scope, options
|
100
|
-
if ENV.key?(ENV_VAR)
|
98
|
+
if ENV.key?(ENV_VAR) && !ENV[ENV_VAR].empty?
|
101
99
|
path = ENV[ENV_VAR]
|
102
|
-
raise "file #{path} does not exist" unless File.exist?
|
103
|
-
File.open
|
104
|
-
return make_creds
|
100
|
+
raise "file #{path} does not exist" unless File.exist? path
|
101
|
+
File.open path do |f|
|
102
|
+
return make_creds options.merge(json_key_io: f)
|
105
103
|
end
|
106
104
|
elsif service_account_env_vars? || authorized_user_env_vars?
|
107
|
-
return make_creds
|
105
|
+
return make_creds options
|
108
106
|
end
|
109
107
|
rescue StandardError => e
|
110
108
|
raise "#{NOT_FOUND_ERROR}: #{e}"
|
@@ -121,16 +119,16 @@ module Google
|
|
121
119
|
# The following keys are recognized:
|
122
120
|
# * `:default_connection` The connection object to use.
|
123
121
|
# * `:connection_builder` A `Proc` that returns a connection.
|
124
|
-
def from_well_known_path
|
122
|
+
def from_well_known_path scope = nil, options = {}
|
125
123
|
options = interpret_options scope, options
|
126
|
-
home_var = OS.windows? ?
|
124
|
+
home_var = OS.windows? ? "APPDATA" : "HOME"
|
127
125
|
base = WELL_KNOWN_PATH
|
128
|
-
root = ENV[home_var].nil? ?
|
129
|
-
base = File.join
|
130
|
-
path = File.join
|
131
|
-
return nil unless File.exist?
|
132
|
-
File.open
|
133
|
-
return make_creds
|
126
|
+
root = ENV[home_var].nil? ? "" : ENV[home_var]
|
127
|
+
base = File.join ".config", base unless OS.windows?
|
128
|
+
path = File.join root, base
|
129
|
+
return nil unless File.exist? path
|
130
|
+
File.open path do |f|
|
131
|
+
return make_creds options.merge(json_key_io: f)
|
134
132
|
end
|
135
133
|
rescue StandardError => e
|
136
134
|
raise "#{WELL_KNOWN_ERROR}: #{e}"
|
@@ -147,61 +145,60 @@ module Google
|
|
147
145
|
# The following keys are recognized:
|
148
146
|
# * `:default_connection` The connection object to use.
|
149
147
|
# * `:connection_builder` A `Proc` that returns a connection.
|
150
|
-
def from_system_default_path
|
148
|
+
def from_system_default_path scope = nil, options = {}
|
151
149
|
options = interpret_options scope, options
|
152
150
|
if OS.windows?
|
153
|
-
return nil unless ENV[
|
154
|
-
prefix = File.join
|
151
|
+
return nil unless ENV["ProgramData"]
|
152
|
+
prefix = File.join ENV["ProgramData"], "Google/Auth"
|
155
153
|
else
|
156
|
-
prefix =
|
154
|
+
prefix = "/etc/google/auth/"
|
157
155
|
end
|
158
|
-
path = File.join
|
159
|
-
return nil unless File.exist?
|
160
|
-
File.open
|
161
|
-
return make_creds
|
156
|
+
path = File.join prefix, CREDENTIALS_FILE_NAME
|
157
|
+
return nil unless File.exist? path
|
158
|
+
File.open path do |f|
|
159
|
+
return make_creds options.merge(json_key_io: f)
|
162
160
|
end
|
163
161
|
rescue StandardError => e
|
164
162
|
raise "#{SYSTEM_DEFAULT_ERROR}: #{e}"
|
165
163
|
end
|
166
164
|
|
165
|
+
module_function
|
166
|
+
|
167
167
|
# Issues warning if cloud sdk client id is used
|
168
|
-
def warn_if_cloud_sdk_credentials
|
168
|
+
def warn_if_cloud_sdk_credentials client_id
|
169
169
|
warn CLOUD_SDK_CREDENTIALS_WARNING if client_id == CLOUD_SDK_CLIENT_ID
|
170
170
|
end
|
171
|
-
module_function :warn_if_cloud_sdk_credentials
|
172
171
|
|
173
172
|
# Finds project_id from gcloud CLI configuration
|
174
173
|
def load_gcloud_project_id
|
175
174
|
gcloud = GCLOUD_WINDOWS_COMMAND if OS.windows?
|
176
175
|
gcloud = GCLOUD_POSIX_COMMAND unless OS.windows?
|
177
|
-
|
178
|
-
config
|
179
|
-
|
176
|
+
gcloud_json = IO.popen("#{gcloud} #{GCLOUD_CONFIG_COMMAND}", &:read)
|
177
|
+
config = MultiJson.load gcloud_json
|
178
|
+
config["configuration"]["properties"]["core"]["project"]
|
179
|
+
rescue StandardError
|
180
180
|
nil
|
181
181
|
end
|
182
|
-
module_function :load_gcloud_project_id
|
183
182
|
|
184
183
|
private
|
185
184
|
|
186
|
-
def interpret_options
|
185
|
+
def interpret_options scope, options
|
187
186
|
if scope.is_a? Hash
|
188
187
|
options = scope
|
189
188
|
scope = nil
|
190
189
|
end
|
191
|
-
if scope && !options[:scope]
|
192
|
-
|
193
|
-
else
|
194
|
-
options
|
195
|
-
end
|
190
|
+
return options.merge scope: scope if scope && !options[:scope]
|
191
|
+
options
|
196
192
|
end
|
197
193
|
|
198
194
|
def service_account_env_vars?
|
199
|
-
([PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR] - ENV.keys).empty?
|
195
|
+
([PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR] - ENV.keys).empty? &&
|
196
|
+
!ENV.to_h.fetch_values(PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR).join(" ").empty?
|
200
197
|
end
|
201
198
|
|
202
199
|
def authorized_user_env_vars?
|
203
|
-
([CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR] -
|
204
|
-
ENV.
|
200
|
+
([CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR] - ENV.keys).empty? &&
|
201
|
+
!ENV.to_h.fetch_values(CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR).join(" ").empty?
|
205
202
|
end
|
206
203
|
end
|
207
204
|
end
|
@@ -27,12 +27,12 @@
|
|
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
|
-
require
|
31
|
-
require
|
30
|
+
require "multi_json"
|
31
|
+
require "stringio"
|
32
32
|
|
33
|
-
require
|
34
|
-
require
|
35
|
-
require
|
33
|
+
require "googleauth/credentials_loader"
|
34
|
+
require "googleauth/service_account"
|
35
|
+
require "googleauth/user_refresh"
|
36
36
|
|
37
37
|
module Google
|
38
38
|
# Module Auth provides classes that provide Google-specific authorization
|
@@ -45,17 +45,17 @@ module Google
|
|
45
45
|
|
46
46
|
# override CredentialsLoader#make_creds to use the class determined by
|
47
47
|
# loading the json.
|
48
|
-
def self.make_creds
|
48
|
+
def self.make_creds options = {}
|
49
49
|
json_key_io = options[:json_key_io]
|
50
50
|
if json_key_io
|
51
|
-
json_key, clz = determine_creds_class
|
52
|
-
warn_if_cloud_sdk_credentials json_key[
|
53
|
-
io = StringIO.new
|
54
|
-
clz.make_creds
|
51
|
+
json_key, clz = determine_creds_class json_key_io
|
52
|
+
warn_if_cloud_sdk_credentials json_key["client_id"]
|
53
|
+
io = StringIO.new MultiJson.dump(json_key)
|
54
|
+
clz.make_creds options.merge(json_key_io: io)
|
55
55
|
else
|
56
56
|
warn_if_cloud_sdk_credentials ENV[CredentialsLoader::CLIENT_ID_VAR]
|
57
57
|
clz = read_creds
|
58
|
-
clz.make_creds
|
58
|
+
clz.make_creds options
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -64,9 +64,9 @@ module Google
|
|
64
64
|
type = ENV[env_var]
|
65
65
|
raise "#{env_var} is undefined in env" unless type
|
66
66
|
case type
|
67
|
-
when
|
67
|
+
when "service_account"
|
68
68
|
ServiceAccountCredentials
|
69
|
-
when
|
69
|
+
when "authorized_user"
|
70
70
|
UserRefreshCredentials
|
71
71
|
else
|
72
72
|
raise "credentials type '#{type}' is not supported"
|
@@ -74,15 +74,15 @@ module Google
|
|
74
74
|
end
|
75
75
|
|
76
76
|
# Reads the input json and determines which creds class to use.
|
77
|
-
def self.determine_creds_class
|
77
|
+
def self.determine_creds_class json_key_io
|
78
78
|
json_key = MultiJson.load json_key_io.read
|
79
|
-
key =
|
80
|
-
raise "the json is missing the '#{key}' field" unless json_key.key?
|
79
|
+
key = "type"
|
80
|
+
raise "the json is missing the '#{key}' field" unless json_key.key? key
|
81
81
|
type = json_key[key]
|
82
82
|
case type
|
83
|
-
when
|
83
|
+
when "service_account"
|
84
84
|
[json_key, ServiceAccountCredentials]
|
85
|
-
when
|
85
|
+
when "authorized_user"
|
86
86
|
[json_key, UserRefreshCredentials]
|
87
87
|
else
|
88
88
|
raise "credentials type '#{type}' is not supported"
|
data/lib/googleauth/iam.rb
CHANGED
@@ -27,9 +27,9 @@
|
|
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
|
-
require
|
31
|
-
require
|
32
|
-
require
|
30
|
+
require "googleauth/signet"
|
31
|
+
require "googleauth/credentials_loader"
|
32
|
+
require "multi_json"
|
33
33
|
|
34
34
|
module Google
|
35
35
|
# Module Auth provides classes that provide Google-specific authorization
|
@@ -37,14 +37,14 @@ module Google
|
|
37
37
|
module Auth
|
38
38
|
# Authenticates requests using IAM credentials.
|
39
39
|
class IAMCredentials
|
40
|
-
SELECTOR_KEY =
|
41
|
-
TOKEN_KEY =
|
40
|
+
SELECTOR_KEY = "x-goog-iam-authority-selector".freeze
|
41
|
+
TOKEN_KEY = "x-goog-iam-authorization-token".freeze
|
42
42
|
|
43
43
|
# Initializes an IAMCredentials.
|
44
44
|
#
|
45
45
|
# @param selector the IAM selector.
|
46
46
|
# @param token the IAM token.
|
47
|
-
def initialize
|
47
|
+
def initialize selector, token
|
48
48
|
raise TypeError unless selector.is_a? String
|
49
49
|
raise TypeError unless token.is_a? String
|
50
50
|
@selector = selector
|
@@ -52,16 +52,16 @@ module Google
|
|
52
52
|
end
|
53
53
|
|
54
54
|
# Adds the credential fields to the hash.
|
55
|
-
def apply!
|
55
|
+
def apply! a_hash
|
56
56
|
a_hash[SELECTOR_KEY] = @selector
|
57
57
|
a_hash[TOKEN_KEY] = @token
|
58
58
|
a_hash
|
59
59
|
end
|
60
60
|
|
61
61
|
# Returns a clone of a_hash updated with the authoriation header
|
62
|
-
def apply
|
62
|
+
def apply a_hash
|
63
63
|
a_copy = a_hash.clone
|
64
|
-
apply!
|
64
|
+
apply! a_copy
|
65
65
|
a_copy
|
66
66
|
end
|
67
67
|
|
@@ -34,12 +34,12 @@ module Google
|
|
34
34
|
# JsonKeyReader contains the behaviour used to read private key and
|
35
35
|
# client email fields from the service account
|
36
36
|
module JsonKeyReader
|
37
|
-
def read_json_key
|
38
|
-
json_key = MultiJson.load
|
39
|
-
raise
|
40
|
-
raise
|
41
|
-
project_id = json_key[
|
42
|
-
[json_key[
|
37
|
+
def read_json_key json_key_io
|
38
|
+
json_key = MultiJson.load json_key_io.read
|
39
|
+
raise "missing client_email" unless json_key.key? "client_email"
|
40
|
+
raise "missing private_key" unless json_key.key? "private_key"
|
41
|
+
project_id = json_key["project_id"]
|
42
|
+
[json_key["private_key"], json_key["client_email"], project_id]
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -27,33 +27,33 @@
|
|
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
|
-
require
|
31
|
-
require
|
32
|
-
require
|
30
|
+
require "googleauth/signet"
|
31
|
+
require "googleauth/credentials_loader"
|
32
|
+
require "multi_json"
|
33
33
|
|
34
34
|
module Google
|
35
35
|
module Auth
|
36
36
|
# Small utility for normalizing scopes into canonical form
|
37
37
|
module ScopeUtil
|
38
38
|
ALIASES = {
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
"email" => "https://www.googleapis.com/auth/userinfo.email",
|
40
|
+
"profile" => "https://www.googleapis.com/auth/userinfo.profile",
|
41
|
+
"openid" => "https://www.googleapis.com/auth/plus.me"
|
42
42
|
}.freeze
|
43
43
|
|
44
|
-
def self.normalize
|
45
|
-
list = as_array
|
44
|
+
def self.normalize scope
|
45
|
+
list = as_array scope
|
46
46
|
list.map { |item| ALIASES[item] || item }
|
47
47
|
end
|
48
48
|
|
49
|
-
def self.as_array
|
49
|
+
def self.as_array scope
|
50
50
|
case scope
|
51
51
|
when Array
|
52
52
|
scope
|
53
53
|
when String
|
54
|
-
scope.split
|
54
|
+
scope.split " "
|
55
55
|
else
|
56
|
-
raise
|
56
|
+
raise "Invalid scope value. Must be string or array"
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -27,12 +27,12 @@
|
|
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
|
-
require
|
31
|
-
require
|
32
|
-
require
|
33
|
-
require
|
34
|
-
require
|
35
|
-
require
|
30
|
+
require "googleauth/signet"
|
31
|
+
require "googleauth/credentials_loader"
|
32
|
+
require "googleauth/json_key_reader"
|
33
|
+
require "jwt"
|
34
|
+
require "multi_json"
|
35
|
+
require "stringio"
|
36
36
|
|
37
37
|
module Google
|
38
38
|
# Module Auth provides classes that provide Google-specific authorization
|
@@ -47,7 +47,7 @@ module Google
|
|
47
47
|
#
|
48
48
|
# cf [Application Default Credentials](http://goo.gl/mkAHpZ)
|
49
49
|
class ServiceAccountCredentials < Signet::OAuth2::Client
|
50
|
-
TOKEN_CRED_URI =
|
50
|
+
TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
|
51
51
|
extend CredentialsLoader
|
52
52
|
extend JsonKeyReader
|
53
53
|
attr_reader :project_id
|
@@ -56,10 +56,10 @@ module Google
|
|
56
56
|
#
|
57
57
|
# @param json_key_io [IO] an IO from which the JSON key can be read
|
58
58
|
# @param scope [string|array|nil] the scope(s) to access
|
59
|
-
def self.make_creds
|
60
|
-
json_key_io, scope = options.values_at
|
59
|
+
def self.make_creds options = {}
|
60
|
+
json_key_io, scope = options.values_at :json_key_io, :scope
|
61
61
|
if json_key_io
|
62
|
-
private_key, client_email, project_id = read_json_key
|
62
|
+
private_key, client_email, project_id = read_json_key json_key_io
|
63
63
|
else
|
64
64
|
private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
65
65
|
client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
@@ -68,26 +68,26 @@ module Google
|
|
68
68
|
project_id ||= CredentialsLoader.load_gcloud_project_id
|
69
69
|
|
70
70
|
new(token_credential_uri: TOKEN_CRED_URI,
|
71
|
-
audience:
|
72
|
-
scope:
|
73
|
-
issuer:
|
74
|
-
signing_key:
|
75
|
-
project_id:
|
71
|
+
audience: TOKEN_CRED_URI,
|
72
|
+
scope: scope,
|
73
|
+
issuer: client_email,
|
74
|
+
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
75
|
+
project_id: project_id)
|
76
76
|
.configure_connection(options)
|
77
77
|
end
|
78
78
|
|
79
79
|
# Handles certain escape sequences that sometimes appear in input.
|
80
80
|
# Specifically, interprets the "\n" sequence for newline, and removes
|
81
81
|
# enclosing quotes.
|
82
|
-
def self.unescape
|
82
|
+
def self.unescape str
|
83
83
|
str = str.gsub '\n', "\n"
|
84
84
|
str = str[1..-2] if str.start_with?('"') && str.end_with?('"')
|
85
85
|
str
|
86
86
|
end
|
87
87
|
|
88
|
-
def initialize
|
88
|
+
def initialize options = {}
|
89
89
|
@project_id = options[:project_id]
|
90
|
-
super
|
90
|
+
super options
|
91
91
|
end
|
92
92
|
|
93
93
|
# Extends the base class.
|
@@ -95,7 +95,7 @@ module Google
|
|
95
95
|
# If scope(s) is not set, it creates a transient
|
96
96
|
# ServiceAccountJwtHeaderCredentials instance and uses that to
|
97
97
|
# authenticate instead.
|
98
|
-
def apply!
|
98
|
+
def apply! a_hash, opts = {}
|
99
99
|
# Use the base implementation if scopes are set
|
100
100
|
unless scope.nil?
|
101
101
|
super
|
@@ -105,13 +105,13 @@ module Google
|
|
105
105
|
# Use the ServiceAccountJwtHeaderCredentials using the same cred values
|
106
106
|
# if no scopes are set.
|
107
107
|
cred_json = {
|
108
|
-
private_key:
|
108
|
+
private_key: @signing_key.to_s,
|
109
109
|
client_email: @issuer
|
110
110
|
}
|
111
111
|
alt_clz = ServiceAccountJwtHeaderCredentials
|
112
|
-
key_io = StringIO.new
|
113
|
-
alt = alt_clz.make_creds
|
114
|
-
alt.apply!
|
112
|
+
key_io = StringIO.new MultiJson.dump(cred_json)
|
113
|
+
alt = alt_clz.make_creds json_key_io: key_io
|
114
|
+
alt.apply! a_hash
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
@@ -127,8 +127,8 @@ module Google
|
|
127
127
|
class ServiceAccountJwtHeaderCredentials
|
128
128
|
JWT_AUD_URI_KEY = :jwt_aud_uri
|
129
129
|
AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY
|
130
|
-
TOKEN_CRED_URI =
|
131
|
-
SIGNING_ALGORITHM =
|
130
|
+
TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
|
131
|
+
SIGNING_ALGORITHM = "RS256".freeze
|
132
132
|
EXPIRY = 60
|
133
133
|
extend CredentialsLoader
|
134
134
|
extend JsonKeyReader
|
@@ -141,43 +141,43 @@ module Google
|
|
141
141
|
# By default, it calls #new with 2 args, the second one being an
|
142
142
|
# optional scope. Here's the constructor only has one param, so
|
143
143
|
# we modify make_creds to reflect this.
|
144
|
-
def self.make_creds
|
145
|
-
new
|
144
|
+
def self.make_creds *args
|
145
|
+
new json_key_io: args[0][:json_key_io]
|
146
146
|
end
|
147
147
|
|
148
148
|
# Initializes a ServiceAccountJwtHeaderCredentials.
|
149
149
|
#
|
150
150
|
# @param json_key_io [IO] an IO from which the JSON key can be read
|
151
|
-
def initialize
|
151
|
+
def initialize options = {}
|
152
152
|
json_key_io = options[:json_key_io]
|
153
153
|
if json_key_io
|
154
154
|
@private_key, @issuer, @project_id =
|
155
|
-
self.class.read_json_key
|
155
|
+
self.class.read_json_key json_key_io
|
156
156
|
else
|
157
157
|
@private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
158
158
|
@issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
159
159
|
@project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
|
160
160
|
end
|
161
161
|
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
162
|
-
@signing_key = OpenSSL::PKey::RSA.new
|
162
|
+
@signing_key = OpenSSL::PKey::RSA.new @private_key
|
163
163
|
end
|
164
164
|
|
165
165
|
# Construct a jwt token if the JWT_AUD_URI key is present in the input
|
166
166
|
# hash.
|
167
167
|
#
|
168
168
|
# The jwt token is used as the value of a 'Bearer '.
|
169
|
-
def apply!
|
170
|
-
jwt_aud_uri = a_hash.delete
|
169
|
+
def apply! a_hash, opts = {}
|
170
|
+
jwt_aud_uri = a_hash.delete JWT_AUD_URI_KEY
|
171
171
|
return a_hash if jwt_aud_uri.nil?
|
172
|
-
jwt_token = new_jwt_token
|
172
|
+
jwt_token = new_jwt_token jwt_aud_uri, opts
|
173
173
|
a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
|
174
174
|
a_hash
|
175
175
|
end
|
176
176
|
|
177
177
|
# Returns a clone of a_hash updated with the authoriation header
|
178
|
-
def apply
|
178
|
+
def apply a_hash, opts = {}
|
179
179
|
a_copy = a_hash.clone
|
180
|
-
apply!
|
180
|
+
apply! a_copy, opts
|
181
181
|
a_copy
|
182
182
|
end
|
183
183
|
|
@@ -190,17 +190,17 @@ module Google
|
|
190
190
|
protected
|
191
191
|
|
192
192
|
# Creates a jwt uri token.
|
193
|
-
def new_jwt_token
|
193
|
+
def new_jwt_token jwt_aud_uri, options = {}
|
194
194
|
now = Time.new
|
195
195
|
skew = options[:skew] || 60
|
196
196
|
assertion = {
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
197
|
+
"iss" => @issuer,
|
198
|
+
"sub" => @issuer,
|
199
|
+
"aud" => jwt_aud_uri,
|
200
|
+
"exp" => (now + EXPIRY).to_i,
|
201
|
+
"iat" => (now - skew).to_i
|
202
202
|
}
|
203
|
-
JWT.encode
|
203
|
+
JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
|
204
204
|
end
|
205
205
|
end
|
206
206
|
end
|