Class: Autosign::Token
- Inherits:
-
Object
- Object
- Autosign::Token
- Defined in:
- lib/autosign/token.rb
Overview
Class modeling JSON Web Tokens as credentials for certificate auto signing. See jwt.io for more information about JSON web tokens in general.
Instance Attribute Summary collapse
-
#certname ⇒ String
readonly
Common name or regex of common names for which this token is valid.
-
#requester ⇒ String
readonly
Arbitrary string identifying the person or machine that generated the token initially.
-
#reusable ⇒ True, False
readonly
True if the token can be used multiple times, false if the token is intended as a one-time credential.
-
#secret ⇒ String
readonly
Shared HMAC secret used to sign or validate tokens.
-
#uuid ⇒ String
RFC4122 v4 UUID functioning as unique identifier for the token.
-
#validfor ⇒ Integer, String
readonly
Seconds that the token will be valid for after being issued.
-
#validto ⇒ Integer, String
POSIX seconds since epoch that the token is valid until.
Class Method Summary collapse
-
.from_token(token, hmac_secret) ⇒ Autosign::Token
Create an Autosign::Token object from a serialized token after validating the signature and expiration time validity.
-
.token_validto(token, hmac_secret) ⇒ Integer
Extract the expiration time, in seconds since epoch, from a signed token.
-
.validate(requested_certname, token, hmac_secret) ⇒ True, False
Validate an existing JSON Web Token.
Instance Method Summary collapse
-
#initialize(certname, reusable = false, validfor = 7200, requester, secret) ⇒ Autosign::Config
constructor
Create a token instance to model an individual JWT token.
-
#reusable? ⇒ True, False
check if the token is reusable or a one-time use token.
-
#sign ⇒ Object
Sign the token with HMAC using a SHA-512 hash.
-
#to_hash ⇒ Hash{String => String}
convert the token to a hash.
Constructor Details
#initialize(certname, reusable = false, validfor = 7200, requester, secret) ⇒ Autosign::Config
Create a token instance to model an individual JWT token
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/autosign/token.rb', line 34 def initialize(certname, reusable=false, validfor=7200, requester, secret) # set up logging @log = Logging.logger[self.class] @log.debug "initializing #{self.class.name}" @validfor = validfor @certname = certname @reusable = reusable @requester = requester @secret = secret @uuid = SecureRandom.uuid # UUID is needed to allow token regeneration with the same settings @validto = Time.now.to_i + self.validfor end |
Instance Attribute Details
#certname ⇒ String
Returns common name or regex of common names for which this token is valid.
14 15 16 |
# File 'lib/autosign/token.rb', line 14 def certname @certname end |
#requester ⇒ String
Returns arbitrary string identifying the person or machine that generated the token initially.
18 19 20 |
# File 'lib/autosign/token.rb', line 18 def requester @requester end |
#reusable ⇒ True, False
Returns true if the token can be used multiple times, false if the token is intended as a one-time credential.
16 17 18 |
# File 'lib/autosign/token.rb', line 16 def reusable @reusable end |
#secret ⇒ String
Returns shared HMAC secret used to sign or validate tokens.
20 21 22 |
# File 'lib/autosign/token.rb', line 20 def secret @secret end |
#uuid ⇒ String
Returns RFC4122 v4 UUID functioning as unique identifier for the token.
24 25 26 |
# File 'lib/autosign/token.rb', line 24 def uuid @uuid end |
#validfor ⇒ Integer, String (readonly)
Returns seconds that the token will be valid for after being issued.
12 13 14 |
# File 'lib/autosign/token.rb', line 12 def validfor @validfor end |
#validto ⇒ Integer, String
Returns POSIX seconds since epoch that the token is valid until.
22 23 24 |
# File 'lib/autosign/token.rb', line 22 def validto @validto end |
Class Method Details
.from_token(token, hmac_secret) ⇒ Autosign::Token
Create an Autosign::Token object from a serialized token after validating the signature and expiration time validity.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/autosign/token.rb', line 137 def self.from_token(token, hmac_secret) begin decoded = JWT.decode(token, hmac_secret)[0] rescue JWT::ExpiredSignature raise Autosign::Token::ExpiredToken rescue raise Autosign::Token::Invalid end cert_data = MultiJson.load(decoded["data"]) new_token = self.new(cert_data["certname"], cert_data["reusable"], cert_data["validfor"], cert_data["requester"], hmac_secret) new_token.validto = self.token_validto(token, hmac_secret) new_token.uuid = cert_data["uuid"] new_token end |
.token_validto(token, hmac_secret) ⇒ Integer
Extract the expiration time, in seconds since epoch, from a signed token. Uses HMAC secret to validate the expiration time.
159 160 161 162 163 164 165 166 167 168 |
# File 'lib/autosign/token.rb', line 159 def self.token_validto(token, hmac_secret) begin decoded = JWT.decode(token, hmac_secret)[0] rescue JWT::ExpiredSignature raise Autosign::Token::ExpiredToken rescue raise Autosign::Token::Invalid end return decoded['exp'].to_i end |
.validate(requested_certname, token, hmac_secret) ⇒ True, False
Validate an existing JSON Web Token.
-
Use the HMAC secret to validate the data in the token
-
Compare the expiration time in the token to the current time to determine if it’s valid
-
compare the certname or regex of certnames in the token to the requested common name from the certificate signing request
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 |
# File 'lib/autosign/token.rb', line 58 def self.validate(requested_certname, token, hmac_secret) @log = Logging.logger[self.class] @log.debug "attempting to validate token" @log.info "attempting to validate token for: #{requested_certname.to_s}" errors = [] begin @log.debug "Decoding and parsing token" data = MultiJson.load(JWT.decode(token, hmac_secret)[0]["data"]) rescue JWT::ExpiredSignature @log.warn "Token has an expired signature" errors << "Expired Signature" rescue @log.warn "Unable to validate token successfully" errors << "Invalid Token" end @log.warn "validation failed with: #{errors.join(', ')}" unless errors.count == 0 if data.nil? @log.error "token is nil; this probably means the token failed to validate" return false end certname_is_regex = (data["certname"] =~ /\/[^\/].*\//) ? true : false if certname_is_regex @log.debug "validating certname as regular expression" regexp = Regexp.new(/\/([^\/].*)\//.match(data["certname"])[1]) unless regexp.match(requested_certname) errors << "certname: '#{requested_certname}' does not match validation regex: '#{regexp.to_s}'" end else unless data["certname"] == requested_certname errors << "certname: '#{requested_certname}' does not match certname '#{data["certname"]}' in token" end end unless errors.count == 0 @log.warn "validation failed with: #{errors.join(', ')}" return false else @log.info "validated token successfully" return true end # we should never get here, but if we do we should break instead of returning anything @log.error "unexpectedly reached end of validation method" raise Autosign::Token::ValidationError end |
Instance Method Details
#reusable? ⇒ True, False
check if the token is reusable or a one-time use token
109 110 111 |
# File 'lib/autosign/token.rb', line 109 def reusable? !!@reusable end |
#sign ⇒ Object
Sign the token with HMAC using a SHA-512 hash
126 127 128 129 |
# File 'lib/autosign/token.rb', line 126 def sign() exp_payload = { :data => to_json, :exp => validto.to_s} JWT.encode exp_payload, secret, 'HS512' end |
#to_hash ⇒ Hash{String => String}
convert the token to a hash
115 116 117 118 119 120 121 122 123 |
# File 'lib/autosign/token.rb', line 115 def to_hash { "certname" => certname, "requester" => requester, "reusable" => reusable?, "validfor" => validfor, "uuid" => uuid } end |