Class: FirebaseIdToken::Certificates

Inherits:
Object
  • Object
show all
Defined in:
lib/firebase_id_token/certificates.rb

Overview

Manage download and access of Google's x509 certificates. Keeps certificates on a Redis namespace database.

Download & Access Certificates

It describes two ways to download it: Certificates.request and Certificates.request!. The first will only do something when Redis certificates database is empty, the second one will always request a new download to Google's API and override the database with the response.

It's important to note that when saving a set of certificates, it will also set a Redis expiration time to match Google's API header expires. After this time went out, Redis will automatically delete those certificates.

To know how many seconds left until the expiration you can use Certificates.ttl.

When comes to accessing it, you can either use Certificates.present? to check if there's any data inside Redis certificates database or Certificates.all to obtain an Array of current certificates.

Examples:

.request will only download once

FirebaseIdToken::Certificates.request # Downloads certificates.
FirebaseIdToken::Certificates.request # Won't do anything.
FirebaseIdToken::Certificates.request # Won't do anything either.

.request! will download always

FirebaseIdToken::Certificates.request # Downloads certificates.
FirebaseIdToken::Certificates.request! # Downloads certificates.
FirebaseIdToken::Certificates.request! # Downloads certificates.

Constant Summary collapse

URL =

Google's x509 certificates API URL.

'https://www.googleapis.com/robot/v1/metadata/x509/'\
'[email protected]'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCertificates

Sets two instance attributes: :redis and :local_certs. Those are respectively a Redis instance from FirebaseIdToken::Configuration and the certificates in it.



156
157
158
159
160
# File 'lib/firebase_id_token/certificates.rb', line 156

def initialize
  @redis = Redis::Namespace.new('firebase_id_token',
    redis: FirebaseIdToken.configuration.redis)
  @local_certs = read_certificates
end

Instance Attribute Details

#local_certsObject (readonly)

Certificates saved in the Redis (JSON String or nil).



36
37
38
# File 'lib/firebase_id_token/certificates.rb', line 36

def local_certs
  @local_certs
end

#redisObject (readonly)

A Redis instance.



34
35
36
# File 'lib/firebase_id_token/certificates.rb', line 34

def redis
  @redis
end

Class Method Details

.allArray

Returns an array of hashes, each hash is a single {key => value} pair containing the certificate KID String as key and a OpenSSL::X509::Certificate object of the respective certificate as value. Returns a empty Array when there's no certificates data on Redis.

Examples:

FirebaseIdToken::Certificates.request
certs = FirebaseIdToken::Certificates.all
certs.first #=> {"1d6d01c7[...]" => #<OpenSSL::X509::Certificate[...]}

Returns:

  • (Array)


98
99
100
101
# File 'lib/firebase_id_token/certificates.rb', line 98

def self.all
  new.local_certs.map { |kid, cert|
    { kid => OpenSSL::X509::Certificate.new(cert) } }
end

.find(kid, raise_error: false) ⇒ nil, OpenSSL::X509::Certificate

Returns a OpenSSL::X509::Certificate object of the requested Key ID (KID) if there's one. Returns nil otherwise.

It will raise a Exceptions::NoCertificatesError if the Redis certificates database is empty.

Examples:

FirebaseIdToken::Certificates.request
cert = FirebaseIdToken::Certificates.find "1d6d01f4w7d54c7[...]"
#=> <OpenSSL::X509::Certificate: subject=#<OpenSSL [...]

Parameters:

  • kid (String)

    Key ID

Returns:

  • (nil, OpenSSL::X509::Certificate)

Raises:



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/firebase_id_token/certificates.rb', line 114

def self.find(kid, raise_error: false)
  certs = new.local_certs
  raise Exceptions::NoCertificatesError if certs.empty?

  return OpenSSL::X509::Certificate.new certs[kid] if certs[kid]

  return unless raise_error

  raise Exceptions::CertificateNotFound,
    "Unable to find a certificate with `#{kid}`."
end

.find!(kid) ⇒ OpenSSL::X509::Certificate

Returns a OpenSSL::X509::Certificate object of the requested Key ID (KID) if there's one.

database is empty.

Examples:

FirebaseIdToken::Certificates.request
cert = FirebaseIdToken::Certificates.find! "1d6d01f4w7d54c7[...]"
#=> <OpenSSL::X509::Certificate: subject=#<OpenSSL [...]

Parameters:

  • kid (String)

    Key ID

Returns:

  • (OpenSSL::X509::Certificate)

Raises:



140
141
142
# File 'lib/firebase_id_token/certificates.rb', line 140

def self.find!(kid)
  find(kid, raise_error: true)
end

.present?Boolean

Returns true if there's certificates data on Redis, false otherwise.

Returns:

  • (Boolean)


84
85
86
# File 'lib/firebase_id_token/certificates.rb', line 84

def self.present?
  ! new.local_certs.empty?
end

.requestnil, Hash

Calls request! only if there are no certificates on Redis. It will return nil otherwise.

It will raise Exceptions::CertificatesRequestError if the request fails or Exceptions::CertificatesTtlError when Google responds with a low TTL, check out request! for more info.

Returns:

  • (nil, Hash)

See Also:



51
52
53
# File 'lib/firebase_id_token/certificates.rb', line 51

def self.request
  new.request
end

.request!Hash

Triggers a HTTPS request to Google's x509 certificates API. If it responds with a status 200 OK, saves the request body into Redis and returns it as a Hash.

Otherwise it will raise a Exceptions::CertificatesRequestError.

This is really rare to happen, but Google may respond with a low TTL certificate. This is a SecurityError and will raise a Exceptions::CertificatesTtlError. You are mostly like to never face it.

Returns:

  • (Hash)


65
66
67
# File 'lib/firebase_id_token/certificates.rb', line 65

def self.request!
  new.request!
end

.request_anywayObject

Deprecated.

Use only request! in favor of Ruby conventions.

It will raise a warning. Kept for compatibility.

See Also:



72
73
74
75
76
77
# File 'lib/firebase_id_token/certificates.rb', line 72

def self.request_anyway
  warn 'WARNING: FirebaseIdToken::Certificates.request_anyway is '\
    'deprecated. Use FirebaseIdToken::Certificates.request! instead.'

  new.request!
end

.ttlFixnum

Returns the current certificates TTL (Time-To-Live) in seconds. Zero meaning no certificates. It's the same as the certificates expiration time, use it to know when to request again.

Returns:

  • (Fixnum)


148
149
150
151
# File 'lib/firebase_id_token/certificates.rb', line 148

def self.ttl
  ttl = new.redis.ttl('certificates')
  ttl < 0 ? 0 : ttl
end

Instance Method Details

#requestObject

See Also:



163
164
165
# File 'lib/firebase_id_token/certificates.rb', line 163

def request
  request! if @local_certs.empty?
end

#request!Object

See Also:



168
169
170
171
172
173
174
175
176
# File 'lib/firebase_id_token/certificates.rb', line 168

def request!
  @request = HTTParty.get URL
  code = @request.code
  if code == 200
    save_certificates
  else
    raise Exceptions::CertificatesRequestError.new(code)
  end
end