class FirebaseIdToken::Certificates
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: {.request} and {.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 {.ttl}.*
When comes to accessing it, you can either use {.present?} to check if there's any data inside Redis certificates database or {.all} to obtain an `Array` of current certificates.
@example `.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.
@example `.request!` will download always
FirebaseIdToken::Certificates.request # Downloads certificates. FirebaseIdToken::Certificates.request! # Downloads certificates. FirebaseIdToken::Certificates.request! # Downloads certificates.
Constants
- URL
Google's x509 certificates API
URL
.
Attributes
Certificates
saved in the Redis (JSON `String` or `nil`).
A Redis instance.
Public Class Methods
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. @return [Array] @example
FirebaseIdToken::Certificates.request certs = FirebaseIdToken::Certificates.all certs.first #=> {"1d6d01c7[...]" => #<OpenSSL::X509::Certificate[...]}
# 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
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. @param [String] kid Key ID @return [nil, OpenSSL::X509::Certificate] @example
FirebaseIdToken::Certificates.request cert = FirebaseIdToken::Certificates.find "1d6d01f4w7d54c7[...]" #=> <OpenSSL::X509::Certificate: subject=#<OpenSSL [...]
# 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
Returns a `OpenSSL::X509::Certificate` object of the requested Key ID (KID) if there's one.
@raise {Exceptions::CertificateNotFound} if it cannot be found.
@raise {Exceptions::NoCertificatesError} if the Redis certificates database is empty.
@param [String] kid Key ID @return [OpenSSL::X509::Certificate] @example
FirebaseIdToken::Certificates.request cert = FirebaseIdToken::Certificates.find! "1d6d01f4w7d54c7[...]" #=> <OpenSSL::X509::Certificate: subject=#<OpenSSL [...]
# File lib/firebase_id_token/certificates.rb, line 140 def self.find!(kid) find(kid, raise_error: true) end
Sets two instance attributes: `:redis` and `:local_certs`. Those are respectively a Redis instance from {FirebaseIdToken::Configuration} and the certificates in it.
# 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
Returns `true` if there's certificates data on Redis, `false` otherwise. @example
FirebaseIdToken::Certificates.present? #=> false FirebaseIdToken::Certificates.request FirebaseIdToken::Certificates.present? #=> true
# File lib/firebase_id_token/certificates.rb, line 84 def self.present? ! new.local_certs.empty? end
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.
@return [nil, Hash] @see Certificates.request!
# File lib/firebase_id_token/certificates.rb, line 51 def self.request new.request end
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. @return [Hash]
# File lib/firebase_id_token/certificates.rb, line 65 def self.request! new.request! end
@deprecated Use only `request!` in favor of Ruby conventions. It will raise a warning. Kept for compatibility. @see Certificates.request!
# 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
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. @return [Fixnum]
# File lib/firebase_id_token/certificates.rb, line 148 def self.ttl ttl = new.redis.ttl('certificates') ttl < 0 ? 0 : ttl end
Public Instance Methods
@see Certificates.request
# File lib/firebase_id_token/certificates.rb, line 163 def request request! if @local_certs.empty? end
# 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
Private Instance Methods
# File lib/firebase_id_token/certificates.rb, line 180 def read_certificates certs = @redis.get 'certificates' certs ? JSON.parse(certs) : {} end
# File lib/firebase_id_token/certificates.rb, line 185 def save_certificates @redis.setex 'certificates', ttl, @request.body @local_certs = read_certificates end
# File lib/firebase_id_token/certificates.rb, line 190 def ttl cache_control = @request.headers['cache-control'] ttl = cache_control.match(/max-age=([0-9]+)/).captures.first.to_i if ttl > 3600 ttl else raise Exceptions::CertificatesTtlError end end