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_anyway. 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 a 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_anyway will download always

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

Constant Summary

URL =

Google's x509 certificates API URL.

'https://www.googleapis.com/robot/v1/metadata/x509/'\
'securetoken@system.gserviceaccount.com'

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.



125
126
127
128
129
# File 'lib/firebase_id_token/certificates.rb', line 125

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 a 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)


88
89
90
91
# File 'lib/firebase_id_token/certificates.rb', line 88

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

.find(kid) ⇒ 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:



104
105
106
107
108
109
110
111
# File 'lib/firebase_id_token/certificates.rb', line 104

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

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

.present?Boolean

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

Returns:

  • (Boolean)


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

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

.requestnil, Hash

Calls request_anyway only if there's 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_anyway 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_anywayHash

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_anyway
  new.request_anyway
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)


117
118
119
120
# File 'lib/firebase_id_token/certificates.rb', line 117

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

Instance Method Details

#requestObject

See Also:



132
133
134
# File 'lib/firebase_id_token/certificates.rb', line 132

def request
  request_anyway if @local_certs.empty?
end

#request_anywayObject

See Also:



137
138
139
140
141
142
143
144
145
# File 'lib/firebase_id_token/certificates.rb', line 137

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