class Ribbon::Intercom::Service::Channel
Attributes
name[R]
secret_hash_crt[R]
secret_hash_prv[R]
signing_keys[R]
store[R]
token[R]
Public Class Methods
new(store, params={})
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 18 def initialize(store, params={}) @store = store @name = params[:name] or raise Errors::ChannelNameMissingError @token = params[:token] refresh(params) end
Public Instance Methods
==(other)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 110 def ==(other) other.is_a?(Channel) && name == other.name && store == other.store && token == other.token && secret_crt.to_s == other.secret_crt.to_s && secret_prv.to_s == other.secret_prv.to_s && permissions == other.permissions end
close()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 106 def close store.delete(self) end
may(*args)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 60 def may(*args) (@_allowed_to ||= Hash.new(false)).merge!( Hash[ args.map { |perm| [perm.to_s, true] } ] ).keys.to_set end
may!(*args)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 68 def may!(*args) may(*args).tap { save } end
may?(*perms)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 72 def may?(*perms) perms.all? { |perm| _may?(perm) } end
permissions()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 76 def permissions @_allowed_to.keys.to_set end
refresh(params={})
click to toggle source
Refreshes the channel. To be called by the store after obtaining a lock.
# File lib/ribbon/intercom/service/channel.rb, line 27 def refresh(params={}) @secret_hash_crt = params[:secret_hash_crt] @secret_hash_prv = params[:secret_hash_prv] @signing_keys = params[:signing_keys] || {} @_allowed_to = nil may(*params.fetch(:may, [])) end
rotate_secret()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 36 def rotate_secret SecureRandom.hex(16).tap { |secret| @secret_hash_prv = secret_hash_crt @secret_hash_crt = Password.create(secret) } end
rotate_secret!()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 43 def rotate_secret! rotate_secret.tap { save } end
save()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 96 def save # Loop until unique unless token loop { break unless store.token_exists?(@token = SecureRandom.hex(4)) } end _run_validations store.persist(self) end
secret_crt()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 84 def secret_crt @__secret_crt ||= _to_bcrypt_pw(secret_hash_crt) end
secret_prv()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 88 def secret_prv @__secret_prv ||= _to_bcrypt_pw(secret_hash_prv) end
sign(data)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 47 def sign(data) key_id, key = _signing_key "\x01" + [key_id].pack('N') + Utils::Signer.new(key).sign(data) end
valid_secret?(secret)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 80 def valid_secret?(secret) !!secret && (secret_crt == secret || secret_prv == secret) end
verify(signed_data)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 52 def verify(signed_data) key_id = signed_data.slice(1, 4).unpack('N').first if (key=_retrieve_signing_key(key_id)) Utils::Signer.new(key).verify(signed_data[5..-1]) end end
with_lock(&block)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 92 def with_lock(&block) store.with_lock(self, &block) end
Private Instance Methods
_add_signing_key()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 189 def _add_signing_key (_latest_signing_key_id.to_i + 1).tap { |key_id| signing_keys[key_id] = { key: Utils::Signer.random_key, timestamp: Time.now.to_i } } end
_delete_expired_signing_keys()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 198 def _delete_expired_signing_keys signing_keys.reject! { |key_id| _signing_key_expired?(key_id) } end
_latest_signing_key_id()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 139 def _latest_signing_key_id ((signing_keys || {}).sort_by { |k, v| v[:timestamp] }.last || []).first end
_may?(required_perm)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 122 def _may?(required_perm) permissions.any? { |perm| regex = /\A#{Regexp.escape(perm).gsub('\*', '.*')}\z/ regex.match(required_perm) } end
_retrieve_signing_key(key_id)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 143 def _retrieve_signing_key(key_id) (data=_retrieve_signing_key_data(key_id)) && data[:key] end
_retrieve_signing_key_data(key_id)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 147 def _retrieve_signing_key_data(key_id) if key_id && (data=signing_keys[key_id]) data.merge(key_id: key_id) else {} end end
_run_validations()
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 133 def _run_validations raise Errors::ChannelNameMissingError unless name raise Errors::ChannelTokenMissingError unless token raise Errors::ChannelSecretMissingError unless secret_hash_crt end
_signing_key()
click to toggle source
Retrieve the latest signing key or generate a new one if the latest has expired (older than 1 hour).
# File lib/ribbon/intercom/service/channel.rb, line 170 def _signing_key latest_id = _latest_signing_key_id # Refresh the key if the current key has less than 5 minutes to live. if _signing_key_ttl(latest_id) < 300 with_lock { latest_id = _latest_signing_key_id if _signing_key_ttl(latest_id) < 300 latest_id = _add_signing_key _delete_expired_signing_keys save # Need to persist changes to signing keys. end } end [latest_id, _retrieve_signing_key(latest_id)] end
_signing_key_expired?(key_id)
click to toggle source
Returns whether the key is expired. Optionally, an offset may be specified to expire the key earlier or later.
# File lib/ribbon/intercom/service/channel.rb, line 158 def _signing_key_expired?(key_id) _signing_key_ttl(key_id) <= 0 end
_signing_key_ttl(key_id, ttl=3600)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 162 def _signing_key_ttl(key_id, ttl=3600) timestamp = _retrieve_signing_key_data(key_id)[:timestamp] (timestamp && (ttl - (Time.now.to_i - timestamp))).to_i end
_to_bcrypt_pw(pw)
click to toggle source
# File lib/ribbon/intercom/service/channel.rb, line 129 def _to_bcrypt_pw(pw) Password.new(pw) if pw && !pw.empty? end