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,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
|