class RedisToken

Constants

DEFAULT_PREFIX
DEFAULT_TTL

Token lives 14 days by default

VERSION

Attributes

created_value[R]
default_ttl[RW]
prefix[RW]
redis[R]

Public Class Methods

new(args = {}, opts = {}) click to toggle source

Create RedisToken instance

Implicit redis instance creation (redis parameters can be passed in args):

RedisToken.new(ttl: 5.days, prefix: 'project.tokens.', host: '127.0.0.1')

Explicit redis instance injection:

redis = Redis.new(host: '192.168.0.1', port: 33221)
RedisToken.new(redis, ttl: 5.days, prefix: 'project.tokens.')

@param [Hash] args @option args [String] :prefix (DEFAULT_PREFIX) redis keys prefix (e.g. 'myproject.tokens.') @option args [Integer] :ttl token time to live value (14 days by default) @option args [Class] :serializer_class serialization class, see RedisToken::Serializers::Native, or use method

@return [RedisToken] a new RedisToken instance

# File lib/redis_token.rb, line 34
def initialize(args = {}, opts = {})
  @redis = if args.nil? || args.is_a?(Hash)
             init_params(args)
             Redis.new(args)
           else
             init_params(opts)
             args
           end
end

Public Instance Methods

all() click to toggle source

All tokens

@return [Enumerator]

# File lib/redis_token.rb, line 142
def all
  all_tokens
end
create(args = {}) click to toggle source

Create a new token

@param [Hash] args @option args [String] :owner owner of a token, e.g. 'client.1' or 'user-123' @option args [String] :token (SecureRandom.hex(16)) user defined token @option args :payload @option args [Integer] :ttl redefines the default ttl

@return [String] a new token

# File lib/redis_token.rb, line 53
def create(args = {})
  token = args[:token] || generate_token
  value = { at: Time.now.to_i }

  owner = args[:owner]
  value[:owner] = owner if owner

  payload = args[:payload]
  value[:payload] = payload if payload

  @created_value = value
  key_ttl = args[:ttl] || @default_ttl

  @redis.multi do |multi|
    multi.set(token_to_key(token), @serializer.pack(value), ex: key_ttl)
    multi.set(token_to_owner(owner, token), nil, ex: key_ttl)
  end

  token
end
del(token)
Alias for: delete
delete(token) click to toggle source

Delete a token

@param [String] token

@return [Boolean]

# File lib/redis_token.rb, line 151
def delete(token)
  key = token_to_key(token)
  value = redis_get(key)
  return false unless value

  @redis.multi do |multi|
    multi.del(key)
    multi.del(token_to_owner(hash_get(value, :owner), token))
  end

  true
end
Also aliased as: del
delete_all() click to toggle source

Delete all tokens

@return [Integer] number of deleted tokens

# File lib/redis_token.rb, line 185
def delete_all
  delete_tokens(all_tokens)
end
delete_owned_by(owner) click to toggle source

Delete all tokens of an owner

@params [String] owner

@return [Integer] number of deleted tokens

# File lib/redis_token.rb, line 171
def delete_owned_by(owner)
  delete_tokens(owned_tokens(owner))
end
delete_without_owner() click to toggle source

Delete tokens without an owner

@return [Integer] number of deleted tokens

# File lib/redis_token.rb, line 178
def delete_without_owner
  delete_tokens(owned_tokens)
end
get(token, args = {}) click to toggle source

Get value of a token and slide ttl

@param [String] token @param [Hash] args @option args [Integer] :ttl @option args [Boolean] :slide_expire (true) slide ttl of a token

@return [Hash] value of a token

# File lib/redis_token.rb, line 82
def get(token, args = {})
  key = token_to_key(token)
  value = redis_get(key)
  return unless value
  return value if args[:slide_expire] === false

  key_ttl = args[:ttl] || @default_ttl

  @redis.multi do |multi|
    multi.expire(key, key_ttl)
    multi.expire(token_to_owner(hash_get(value, :owner), token), key_ttl)
  end

  value
end
owned_by(owner) click to toggle source

Iterate all exist tokens of an owner

@param [String] owner

@return [Enumerator]

# File lib/redis_token.rb, line 128
def owned_by(owner)
  owned_tokens(owner)
end
set(token, args = {}) click to toggle source

Set new payload of a token

@param [String] token @param [Hash] args @option args [Integer] :ttl set new time to live value @option args :payload new payload value

@return [Boolean]

# File lib/redis_token.rb, line 106
def set(token, args = {})
  key = token_to_key(token)
  value = redis_get(key)
  return false unless value

  value[:payload] = args[:payload]

  key_ttl = args[:ttl] || @redis.ttl(key)

  @redis.multi do |multi|
    multi.set(key, @serializer.pack(value), ex: key_ttl)
    multi.expire(token_to_owner(hash_get(value, :owner), token), key_ttl)
  end

  true
end
ttl(token) click to toggle source

Retrieve the remaining ttl of a token

@return [Integer] ttl

# File lib/redis_token.rb, line 192
def ttl(token)
  @redis.ttl(token_to_key(token))
end
use(serializer_class) click to toggle source

Use custom serialization class

Base serializer example:

class RedisToken
  class Serializers
    class Native
      def pack(value)
        Marshal.dump(value)
      end

      def unpack(value)
        Marshal.load(value)
      end
    end
  end
end

MessagePack example:

require 'msgpack'

class MsgPackSerializer
  def pack(value)
    MessagePack.pack(value)
  end

  def unpack(value)
    MessagePack.unpack(value)
  end
end

r = RedisToken.new.use(MsgPackSerializer)

@param [Object] serializer_class

@return [RedisToken]

# File lib/redis_token.rb, line 231
def use(serializer_class)
  @serializer = serializer_class.new
  self
end
without_owner() click to toggle source

Tokens without an owner

@return [Enumerator]

# File lib/redis_token.rb, line 135
def without_owner
  owned_tokens
end

Private Instance Methods

all_tokens() click to toggle source
# File lib/redis_token.rb, line 274
def all_tokens
  iterator(nil, true)
end
delete_tokens(enum) click to toggle source
# File lib/redis_token.rb, line 295
def delete_tokens(enum)
  enum.reduce(0) do |deleted, token|
    del(token)
    deleted += 1
  end
end
generate_token() click to toggle source
# File lib/redis_token.rb, line 238
def generate_token
  SecureRandom.hex(16)
end
hash_get(hash, sym) click to toggle source

Some serializers can't store symbols out of the box

# File lib/redis_token.rb, line 303
def hash_get(hash, sym)
  hash.fetch(sym, hash[sym.to_s])
end
init_params(args) click to toggle source
# File lib/redis_token.rb, line 242
def init_params(args)
  @default_ttl = args[:ttl] || DEFAULT_TTL
  @prefix = args[:prefix] || DEFAULT_PREFIX
  @serializer = (args[:serializer_class] || Serializers::Native).new
end
iterator(owner = nil, all = false) click to toggle source
# File lib/redis_token.rb, line 278
def iterator(owner = nil, all = false)
  mask = all ? "#{@prefix}.t.*" : "#{@prefix}.o.#{owner}.*"

  Enumerator.new do |y|
    cursor = '0'
    loop do
      cursor, r = @redis.scan(cursor, match: mask)

      r.each do |key|
        y << (all ? key_to_token(key) : owner_key_to_token(owner, key))
      end

      break if cursor == '0'
    end
  end
end
key_to_token(key) click to toggle source
# File lib/redis_token.rb, line 260
def key_to_token(key)
  key.sub("#{@prefix}.t.", '')
end
owned_tokens(owner = nil) click to toggle source
# File lib/redis_token.rb, line 270
def owned_tokens(owner = nil)
  iterator(owner)
end
owner_key_to_token(owner, key) click to toggle source
# File lib/redis_token.rb, line 256
def owner_key_to_token(owner, key)
  key.sub("#{@prefix}.o.#{owner}.", '')
end
redis_get(key) click to toggle source
# File lib/redis_token.rb, line 264
def redis_get(key)
  value = @redis.get(key)
  return unless value
  @serializer.unpack(value)
end
token_to_key(token) click to toggle source
# File lib/redis_token.rb, line 248
def token_to_key(token)
  "#{@prefix}.t.#{token}"
end
token_to_owner(owner, token) click to toggle source
# File lib/redis_token.rb, line 252
def token_to_owner(owner, token)
  "#{@prefix}.o.#{owner}.#{token}"
end