googleauth 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: adef96ddcbbab3bf0cdf0bcb5e2772bbf3de2f04
4
- data.tar.gz: ab58fe380898294110ec76ac571a22ef411efe6b
3
+ metadata.gz: ea1a2e263bd1d7876d89a72efe9c3a7d7059865c
4
+ data.tar.gz: d50f82ec16c2af13c46254705101efa26cc407c0
5
5
  SHA512:
6
- metadata.gz: 5ad82896ca3cac5f605ece8cf51c512bc4625c782045452fee044064ae4d751e33e29101f6c9caef569f08816b1b1066626000a3b541c30dc02033a70cbebe7c
7
- data.tar.gz: cd31e77291d39c9cf44c6ffa39a3e939274a5ca9834d6ee558f2e5ec4d5b575d6c8d29655cbaa4b25dc8e5ca89330a5f87d61dd1eea302398ac388c27f56aa46
6
+ metadata.gz: 56c038dfecebcf7239404231372e79fbd626f18cc833291ab83caaf81f8f3b8579b4ec7b38ceb670a0b44e8b155290333c52569ce664a52dc67bb08637a36fcd
7
+ data.tar.gz: d23147ecd1ba999174ead84aa967be369a6aa7c77f85505c18f600ab47b390fc356668bf495e38e49909beed9958e471bdb6148a2c58b202857d7ea713c06529
@@ -1,15 +1,32 @@
1
- # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2015-05-18 09:38:28 -0700 using RuboCop version 0.31.0.
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2015-10-14 13:50:41 -0700 using RuboCop version 0.34.2.
3
4
  # The point is for the user to remove these configuration records
4
5
  # one by one as the offenses are removed from the code base.
5
6
  # Note that changes in the inspected code, or installation of new
6
7
  # versions of RuboCop, may require this file to be generated again.
7
8
 
8
- # Offense count: 3
9
+ # Offense count: 4
9
10
  Metrics/AbcSize:
10
- Max: 24
11
+ Max: 27
11
12
 
12
- # Offense count: 10
13
+ # Offense count: 1
14
+ # Configuration parameters: CountComments.
15
+ Metrics/ClassLength:
16
+ Max: 109
17
+
18
+ # Offense count: 1
19
+ Metrics/CyclomaticComplexity:
20
+ Max: 7
21
+
22
+ # Offense count: 16
13
23
  # Configuration parameters: CountComments.
14
24
  Metrics/MethodLength:
15
- Max: 13
25
+ Max: 22
26
+
27
+ # Offense count: 2
28
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
29
+ Style/FormatString:
30
+ Exclude:
31
+ - 'lib/googleauth/user_authorizer.rb'
32
+ - 'lib/googleauth/web_user_authorizer.rb'
@@ -7,6 +7,9 @@ rvm:
7
7
  - 1.9.3
8
8
  - rbx-2
9
9
  - jruby
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: rbx-2 # See rubinius/rubinius#3485 - rubocop segfaults
10
13
  script: "bundle exec rake"
11
14
  addons:
12
15
  apt:
@@ -1,8 +1,15 @@
1
+ ## 0.5.0 (12/10/2015)
2
+
3
+ ### Changes
4
+
5
+ * Initial support for user credentials ([@sqrrrl][])
6
+ * Update Signet to 0.7
7
+
1
8
  ## 0.4.2 (05/08/2015)
2
9
 
3
10
  ### Changes
4
11
 
5
- * Updated UserRefreshCredentials hash to use string keys ([@haabator][])
12
+ * Updated UserRefreshCredentials hash to use string keys ([@haabaato][])
6
13
  [#36](https://github.com/google/google-auth-library-ruby/issues/36)
7
14
 
8
15
  * Add support for a system default credentials file. ([@mr-salty][])
data/Gemfile CHANGED
@@ -2,3 +2,23 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in googleauth.gemspec
4
4
  gemspec
5
+
6
+ group :development do
7
+ gem 'bundler', '~> 1.9'
8
+ gem 'simplecov', '~> 0.9'
9
+ gem 'coveralls', '~> 0.7'
10
+ gem 'fakefs', '~> 0.6'
11
+ gem 'rake', '~> 10.0'
12
+ gem 'rubocop', '~> 0.30'
13
+ gem 'rspec', '~> 3.0'
14
+ gem 'redis', '~> 3.2'
15
+ gem 'fakeredis', '~> 0.5'
16
+ gem 'webmock', '~> 1.21'
17
+ gem 'rack-test', '~> 0.6'
18
+ gem 'sinatra'
19
+ end
20
+
21
+ platforms :jruby do
22
+ group :development do
23
+ end
24
+ end
data/README.md CHANGED
@@ -38,7 +38,8 @@ $ gem install googleauth
38
38
  require 'googleauth'
39
39
 
40
40
  # Get the environment configured authorization
41
- scopes = ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/compute']
41
+ scopes = ['https://www.googleapis.com/auth/cloud-platform',
42
+ 'https://www.googleapis.com/auth/compute']
42
43
  authorization = Google::Auth.get_application_default(scopes)
43
44
 
44
45
  # Add the the access token obtained using the authorization to a hash, e.g
@@ -61,6 +62,84 @@ and authorization level for the application independent of the user. This is
61
62
  the recommended approach to authorize calls to Cloud APIs, particularly when
62
63
  you're building an application that uses Google Compute Engine.
63
64
 
65
+ ## User Credentials
66
+
67
+ The library also provides support for requesting and storing user
68
+ credentials (3-Legged OAuth2.) Two implementations are currently available,
69
+ a generic authorizer useful for command line apps or custom integrations as
70
+ well as a web variant tailored toward Rack-based applications.
71
+
72
+ The authorizers are intended for authorization use cases. For sign-on,
73
+ see [Google Idenity Platform](https://developers.google.com/identity/)
74
+
75
+ ### Example (Web)
76
+
77
+ ```ruby
78
+ require 'googleauth'
79
+ require 'googleauth/web_user_authorizer'
80
+ require 'googleauth/stores/redis_token_store'
81
+ require 'redis'
82
+
83
+ client_id = Google::Auth::ClientId.from_file('/path/to/client_secrets.json')
84
+ scope = ['https://www.googleapis.com/auth/drive']
85
+ token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)
86
+ authorizer = Google::Auth::WebUserAuthorizer.new(
87
+ client_id, scope, token_store, '/oauth2callback')
88
+
89
+
90
+ get('/authorize') do
91
+ # NOTE: Assumes the user is already authenticated to the app
92
+ user_id = request.session['user_id']
93
+ credentials = authorizer.get_credentials(user_id, request)
94
+ if credentials.nil?
95
+ redirect authorizer.get_authorization_url(user_id: user_id, request: request)
96
+ end
97
+ # Credentials are valid, can call APIs
98
+ # ...
99
+ end
100
+
101
+ get('/oauth2callback') do
102
+ target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(
103
+ request)
104
+ redirect target_url
105
+ end
106
+ ```
107
+
108
+ ### Example (Command Line)
109
+
110
+ ```ruby
111
+ require 'googleauth'
112
+ require 'googleauth/stores/file_token_store'
113
+
114
+ scope = 'https://www.googleapis.com/auth/drive'
115
+ client_id = Google::Auth::ClientId.from_file('/path/to/client_secrets.json')
116
+ token_store = Google::Auth::Stores::FileTokenStore.new(
117
+ :file => '/path/to/tokens.yaml')
118
+ authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store)
119
+
120
+ credentials = authorizer.get_credentials(user_id)
121
+ if credentials.nil?
122
+ url = authorizer.get_authorization_url(base_url: 'urn:ietf:wg:oauth:2.0:oob')
123
+ puts "Open #{url} in your browser and enter the resulting code:"
124
+ code = gets
125
+ credentials = authorizer.get_and_store_credentials_from_code(
126
+ user_id: user_id, code: code, base_url: OOB_URI)
127
+ end
128
+
129
+ # OK to use credentials
130
+ ```
131
+
132
+ ### Storage
133
+
134
+ Authorizers require a storage instance to manage long term persistence of
135
+ access and refresh tokens. Two storage implementations are included:
136
+
137
+ * Google::Auth::Stores::FileTokenStore
138
+ * Google::Auth::Stores::RedisTokenStore
139
+
140
+ Custom storage implementations can also be used. See
141
+ [token_store.rb](lib/googleauth/token_store.rb) for additional details.
142
+
64
143
  ## What about auth in google-apis-ruby-client?
65
144
 
66
145
  The goal is for all auth done by
@@ -30,13 +30,5 @@ Gem::Specification.new do |s|
30
30
  s.add_dependency 'jwt', '~> 1.4'
31
31
  s.add_dependency 'memoist', '~> 0.12'
32
32
  s.add_dependency 'multi_json', '~> 1.11'
33
- s.add_dependency 'signet', '~> 0.6'
34
-
35
- s.add_development_dependency 'bundler', '~> 1.9'
36
- s.add_development_dependency 'simplecov', '~> 0.9'
37
- s.add_development_dependency 'coveralls', '~> 0.7'
38
- s.add_development_dependency 'fakefs', '~> 0.6'
39
- s.add_development_dependency 'rake', '~> 10.0'
40
- s.add_development_dependency 'rubocop', '~> 0.30'
41
- s.add_development_dependency 'rspec', '~> 3.0'
33
+ s.add_dependency 'signet', '~> 0.7'
42
34
  end
@@ -34,6 +34,9 @@ require 'googleauth/credentials_loader'
34
34
  require 'googleauth/compute_engine'
35
35
  require 'googleauth/service_account'
36
36
  require 'googleauth/user_refresh'
37
+ require 'googleauth/client_id'
38
+ require 'googleauth/user_authorizer'
39
+ require 'googleauth/web_user_authorizer'
37
40
 
38
41
  module Google
39
42
  # Module Auth provides classes that provide Google-specific authorization
@@ -56,11 +59,11 @@ END
56
59
  json_key_io, scope = options.values_at(:json_key_io, :scope)
57
60
  if json_key_io
58
61
  json_key, clz = determine_creds_class(json_key_io)
59
- clz.new(json_key_io: StringIO.new(MultiJson.dump(json_key)),
60
- scope: scope)
62
+ clz.make_creds(json_key_io: StringIO.new(MultiJson.dump(json_key)),
63
+ scope: scope)
61
64
  else
62
65
  clz = read_creds
63
- clz.new(scope: scope)
66
+ clz.make_creds(scope: scope)
64
67
  end
65
68
  end
66
69
 
@@ -0,0 +1,102 @@
1
+ # Copyright 2014, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'multi_json'
31
+
32
+ module Google
33
+ module Auth
34
+ # Representation of an application's identity for user authorization
35
+ # flows.
36
+ class ClientId
37
+ INSTALLED_APP = 'installed'
38
+ WEB_APP = 'web'
39
+ CLIENT_ID = 'client_id'
40
+ CLIENT_SECRET = 'client_secret'
41
+ MISSING_TOP_LEVEL_ELEMENT_ERROR =
42
+ "Expected top level property 'installed' or 'web' to be present."
43
+
44
+ # Text identifier of the client ID
45
+ # @return [String]
46
+ attr_reader :id
47
+
48
+ # Secret associated with the client ID
49
+ # @return [String]
50
+ attr_reader :secret
51
+
52
+ class << self
53
+ attr_accessor :default
54
+ end
55
+
56
+ # Initialize the Client ID
57
+ #
58
+ # @param [String] id
59
+ # Text identifier of the client ID
60
+ # @param [String] secret
61
+ # Secret associated with the client ID
62
+ # @note Direction instantion is discouraged to avoid embedding IDs
63
+ # & secrets in source. See {#from_file} to load from
64
+ # `client_secrets.json` files.
65
+ def initialize(id, secret)
66
+ fail 'Client id can not be nil' if id.nil?
67
+ fail 'Client secret can not be nil' if secret.nil?
68
+ @id = id
69
+ @secret = secret
70
+ end
71
+
72
+ # Constructs a Client ID from a JSON file downloaed from the
73
+ # Google Developers Console.
74
+ #
75
+ # @param [String, File] file
76
+ # Path of file to read from
77
+ # @return [Google::Auth::ClientID]
78
+ def self.from_file(file)
79
+ fail 'File can not be nil.' if file.nil?
80
+ File.open(file.to_s) do |f|
81
+ json = f.read
82
+ config = MultiJson.load(json)
83
+ from_hash(config)
84
+ end
85
+ end
86
+
87
+ # Constructs a Client ID from a previously loaded JSON file. The hash
88
+ # structure should
89
+ # match the expected JSON format.
90
+ #
91
+ # @param [hash] config
92
+ # Parsed contents of the JSON file
93
+ # @return [Google::Auth::ClientID]
94
+ def self.from_hash(config)
95
+ fail 'Hash can not be nil.' if config.nil?
96
+ raw_detail = config[INSTALLED_APP] || config[WEB_APP]
97
+ fail MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil?
98
+ ClientId.new(raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET])
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,61 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'googleauth/signet'
31
+ require 'googleauth/credentials_loader'
32
+ require 'multi_json'
33
+
34
+ module Google
35
+ module Auth
36
+ # Small utility for normalizing scopes into canonical form
37
+ module ScopeUtil
38
+ ALIASES = {
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
+ }
43
+
44
+ def self.normalize(scope)
45
+ list = as_array(scope)
46
+ list.map { |item| ALIASES[item] || item }
47
+ end
48
+
49
+ def self.as_array(scope)
50
+ case scope
51
+ when Array
52
+ scope
53
+ when String
54
+ scope.split(' ')
55
+ else
56
+ fail 'Invalid scope value. Must be string or array'
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -49,33 +49,37 @@ module Google
49
49
  TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
50
50
  extend CredentialsLoader
51
51
 
52
- # Reads the private key and client email fields from the service account
53
- # JSON key.
54
- def self.read_json_key(json_key_io)
55
- json_key = MultiJson.load(json_key_io.read)
56
- fail 'missing client_email' unless json_key.key?('client_email')
57
- fail 'missing private_key' unless json_key.key?('private_key')
58
- [json_key['private_key'], json_key['client_email']]
59
- end
60
-
61
- # Initializes a ServiceAccountCredentials.
52
+ # Creates a ServiceAccountCredentials.
62
53
  #
63
54
  # @param json_key_io [IO] an IO from which the JSON key can be read
64
55
  # @param scope [string|array|nil] the scope(s) to access
65
- def initialize(options = {})
56
+ def self.make_creds(options = {})
66
57
  json_key_io, scope = options.values_at(:json_key_io, :scope)
67
58
  if json_key_io
68
- private_key, client_email = self.class.read_json_key(json_key_io)
59
+ private_key, client_email = read_json_key(json_key_io)
69
60
  else
70
61
  private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
71
62
  client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
72
63
  end
73
64
 
74
- super(token_credential_uri: TOKEN_CRED_URI,
75
- audience: TOKEN_CRED_URI,
76
- scope: scope,
77
- issuer: client_email,
78
- signing_key: OpenSSL::PKey::RSA.new(private_key))
65
+ new(token_credential_uri: TOKEN_CRED_URI,
66
+ audience: TOKEN_CRED_URI,
67
+ scope: scope,
68
+ issuer: client_email,
69
+ signing_key: OpenSSL::PKey::RSA.new(private_key))
70
+ end
71
+
72
+ # Reads the private key and client email fields from the service account
73
+ # JSON key.
74
+ def self.read_json_key(json_key_io)
75
+ json_key = MultiJson.load(json_key_io.read)
76
+ fail 'missing client_email' unless json_key.key?('client_email')
77
+ fail 'missing private_key' unless json_key.key?('private_key')
78
+ [json_key['private_key'], json_key['client_email']]
79
+ end
80
+
81
+ def initialize(options = {})
82
+ super(options)
79
83
  end
80
84
 
81
85
  # Extends the base class.
@@ -97,7 +101,8 @@ module Google
97
101
  client_email: @issuer
98
102
  }
99
103
  alt_clz = ServiceAccountJwtHeaderCredentials
100
- alt = alt_clz.new(json_key_io: StringIO.new(MultiJson.dump(cred_json)))
104
+ key_io = StringIO.new(MultiJson.dump(cred_json))
105
+ alt = alt_clz.make_creds(json_key_io: key_io)
101
106
  alt.apply!(a_hash)
102
107
  end
103
108
  end