File: credentials.rb

package info (click to toggle)
ruby-google-cloud-core 1.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 128 kB
  • sloc: ruby: 509; makefile: 3
file content (135 lines) | stat: -rw-r--r-- 4,675 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# Copyright 2014 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This file is now considered DEPRECATED.
# Libraries that depend on google-cloud-core ~> 1.1 should not use this file.
# Keep the implementation in place to remain compatible with so older gems.


require "json"
require "signet/oauth_2/client"
require "forwardable"
require "googleauth"

module Google
  module Cloud
    ##
    # @private
    # Represents the OAuth 2.0 signing logic.
    # This class is intended to be inherited by API-specific classes
    # which overrides the SCOPE constant.
    class Credentials
      TOKEN_CREDENTIAL_URI = "https://accounts.google.com/o/oauth2/token"
      AUDIENCE = "https://accounts.google.com/o/oauth2/token"
      SCOPE = []
      PATH_ENV_VARS = %w[GOOGLE_CLOUD_KEYFILE GCLOUD_KEYFILE]
      JSON_ENV_VARS = %w[GOOGLE_CLOUD_KEYFILE_JSON GCLOUD_KEYFILE_JSON]
      DEFAULT_PATHS = ["~/.config/gcloud/application_default_credentials.json"]

      attr_accessor :client

      ##
      # Delegate client methods to the client object.
      extend Forwardable
      def_delegators :@client,
                     :token_credential_uri, :audience,
                     :scope, :issuer, :signing_key

      def initialize keyfile, scope: nil
        verify_keyfile_provided! keyfile
        if keyfile.is_a? Signet::OAuth2::Client
          @client = keyfile
        elsif keyfile.is_a? Hash
          hash = stringify_hash_keys keyfile
          hash["scope"] ||= scope
          @client = init_client hash
        else
          verify_keyfile_exists! keyfile
          json = JSON.parse ::File.read(keyfile)
          json["scope"] ||= scope
          @client = init_client json
        end
        @client.fetch_access_token!
      end

      ##
      # Returns the default credentials.
      #
      def self.default scope: nil
        env  = ->(v) { ENV[v] }
        json = ->(v) { JSON.parse ENV[v] rescue nil unless ENV[v].nil? }
        path = ->(p) { ::File.file? p }

        # First try to find keyfile file from environment variables.
        self::PATH_ENV_VARS.map(&env).compact.select(&path).each do |file|
          return new file, scope: scope
        end
        # Second try to find keyfile json from environment variables.
        self::JSON_ENV_VARS.map(&json).compact.each do |hash|
          return new hash, scope: scope
        end
        # Third try to find keyfile file from known file paths.
        self::DEFAULT_PATHS.select(&path).each do |file|
          return new file, scope: scope
        end
        # Finally get instantiated client from Google::Auth.
        scope ||= self::SCOPE
        client = Google::Auth.get_application_default scope
        new client
      end

      protected

      ##
      # Verify that the keyfile argument is provided.
      def verify_keyfile_provided! keyfile
        raise "You must provide a keyfile to connect with." if keyfile.nil?
      end

      ##
      # Verify that the keyfile argument is a file.
      def verify_keyfile_exists! keyfile
        exists = ::File.file? keyfile
        raise "The keyfile '#{keyfile}' is not a valid file." unless exists
      end

      ##
      # Initializes the Signet client.
      def init_client keyfile
        client_opts = client_options keyfile
        Signet::OAuth2::Client.new client_opts
      end

      ##
      # returns a new Hash with string keys instead of symbol keys.
      def stringify_hash_keys hash
        Hash[hash.map { |k, v| [k.to_s, v] }]
      end

      def client_options options
        # Keyfile options have higher priority over constructor defaults
        options["token_credential_uri"] ||= self.class::TOKEN_CREDENTIAL_URI
        options["audience"]             ||= self.class::AUDIENCE
        options["scope"]                ||= self.class::SCOPE

        # client options for initializing signet client
        { token_credential_uri: options["token_credential_uri"],
          audience: options["audience"],
          scope: Array(options["scope"]),
          issuer: options["client_email"],
          signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
      end
    end
  end
end