class Cloak::Redis
don't extend Redis
so we can confirm operations are safe before adding
Public Class Methods
# File lib/cloak/redis.rb, line 15 def initialize(key: nil, **options) @redis = ::Redis.new(**options) create_encryptor(key) end
Public Instance Methods
append not supported
# File lib/cloak/redis.rb, line 222 def bitcount(key, start = 0, stop = -1) on_result(@redis.get(encrypt_key(key))) do |res| decrypt_value(res)[start..stop].unpack1("B*").count("1") end end
bitop not supported
# File lib/cloak/redis.rb, line 230 def bitpos(key, bit, start = nil, stop = nil) on_result(@redis.get(encrypt_key(key))) do |res| pos = decrypt_value(res)[(start || 0)..(stop || -1)].unpack1("B*").index(bit.to_s) pos ? pos + (start.to_i * 8) : -1 end end
# File lib/cloak/redis.rb, line 282 def blpop(*args) _bpop(:blpop, args) end
# File lib/cloak/redis.rb, line 286 def brpop(*args) _bpop(:brpop, args) end
# File lib/cloak/redis.rb, line 290 def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout) @redis.brpoplpush(encrypt_key(source), encrypt_key(destination), timeout: timeout) end
# File lib/cloak/redis.rb, line 440 def bzpopmax(*args) _bpop(:bzpopmax, args, zset: true) end
# File lib/cloak/redis.rb, line 444 def bzpopmin(*args) _bpop(:bzpopmin, args, zset: true) end
# File lib/cloak/redis.rb, line 20 def debug(*args) args[1] = encrypt_key(args[1]) if args[0] == "object" @redis.debug(*args) end
# File lib/cloak/redis.rb, line 130 def decr(key) @redis.decr(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 134 def decrby(key, decrement) @redis.decrby(encrypt_key(key), decrement) end
# File lib/cloak/redis.rb, line 77 def del(*keys) @redis.del(*keys.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 69 def dump(key) @redis.dump(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 35 def echo(value) on_result(@redis.echo(encrypt_value(value))) do |res| decrypt_value(res) end end
# File lib/cloak/redis.rb, line 85 def exists(*keys) @redis.exists(*keys.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 89 def exists?(*keys) @redis.exists?(*keys.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 45 def expire(key, seconds) @redis.expire(encrypt_key(key), seconds) end
# File lib/cloak/redis.rb, line 49 def expireat(key, unix_time) @redis.expireat(encrypt_key(key), unix_time) end
# File lib/cloak/redis.rb, line 184 def get(key) on_result(@redis.get(encrypt_key(key))) do |res| decrypt_value(res) end end
TODO raise “ERR bit offset is not an integer or out of range” when needed
# File lib/cloak/redis.rb, line 213 def getbit(key, offset) on_result(@redis.get(encrypt_key(key))) do |res| v = decrypt_value(res) v.nil? ? 0 : v.unpack1("B*")[offset].to_i end end
setrange not supported
# File lib/cloak/redis.rb, line 204 def getrange(key, start, stop) on_result(@redis.get(encrypt_key(key))) do |res| decrypt_value(res)[start..stop] end end
# File lib/cloak/redis.rb, line 237 def getset(key, value) on_result(@redis.getset(encrypt_key(key), encrypt_value(value))) do |res| decrypt_value(res) end end
# File lib/cloak/redis.rb, line 575 def hdel(key, *fields) ek = encrypt_key(key) @redis.hdel(ek, *fields.map { |v| encrypt_field(ek, v) }) end
# File lib/cloak/redis.rb, line 580 def hexists(key, field) ek = encrypt_key(key) @redis.hexists(ek, encrypt_field(ek, field)) end
# File lib/cloak/redis.rb, line 554 def hget(key, field) ek = encrypt_key(key) on_result(@redis.hget(ek, encrypt_field(ek, field))) do |res| decrypt_value(res) end end
# File lib/cloak/redis.rb, line 609 def hgetall(key) ek = encrypt_key(key) on_result(@redis.hgetall(ek)) do |res| res.map { |f, v| [decrypt_field(ek, f), decrypt_value(v)] }.to_h end end
# File lib/cloak/redis.rb, line 585 def hincrby(key, field, increment) ek = encrypt_key(key) @redis.hincrby(ek, encrypt_field(ek, field), increment) end
# File lib/cloak/redis.rb, line 590 def hincrbyfloat(key, field, increment) ek = encrypt_key(key) @redis.hincrbyfloat(ek, encrypt_field(ek, field), increment) end
# File lib/cloak/redis.rb, line 595 def hkeys(key) ek = encrypt_key(key) on_result(@redis.hkeys(ek)) do |res| res.map { |v| decrypt_field(ek, v) } end end
# File lib/cloak/redis.rb, line 528 def hlen(key) @redis.hlen(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 561 def hmget(key, *fields, &blk) ek = encrypt_key(key) on_result(@redis.hmget(ek, *fields.map { |f| encrypt_field(ek, f) }, &blk)) do |res| res.map { |v| decrypt_value(v) } end end
# File lib/cloak/redis.rb, line 544 def hmset(key, *attrs) ek = encrypt_key(key) @redis.hset(ek, attrs.map.with_index { |v, i| i % 2 == 0 ? encrypt_field(ek, v) : encrypt_value(v) }) end
match option not supported
# File lib/cloak/redis.rb, line 639 def hscan(key, cursor, count: nil) ek = encrypt_key(key) on_result(@redis.hscan(ek, cursor, count: count)) do |res| [res[0], res[1].map { |v| [decrypt_field(ek, v[0]), decrypt_value(v[1])] }] end end
match redis
# File lib/cloak/redis.rb, line 647 def hscan_each(key, **options, &block) return to_enum(:hscan_each, key, **options) unless block_given? cursor = 0 loop do # hscan encrypts key cursor, values = hscan(key, cursor, **options) values.each(&block) break if cursor == "0" end end
# File lib/cloak/redis.rb, line 532 def hset(key, *attrs) attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash) ek = encrypt_key(key) @redis.hset(ek, attrs.map.with_index { |v, i| i % 2 == 0 ? encrypt_field(ek, v) : encrypt_value(v) }) end
# File lib/cloak/redis.rb, line 539 def hsetnx(key, field, value) ek = encrypt_key(key) @redis.hsetnx(ek, encrypt_field(ek, field), encrypt_value(value)) end
# File lib/cloak/redis.rb, line 602 def hvals(key) ek = encrypt_key(key) on_result(@redis.hvals(ek)) do |res| res.map { |v| decrypt_value(v) } end end
# File lib/cloak/redis.rb, line 138 def incr(key) @redis.incr(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 142 def incrby(key, increment) @redis.incrby(encrypt_key(key), increment) end
# File lib/cloak/redis.rb, line 146 def incrbyfloat(key, increment) @redis.incrbyfloat(encrypt_key(key), increment) end
could match in-memory
# File lib/cloak/redis.rb, line 94 def keys(pattern = "*") raise "Only * pattern supported" if pattern != "*" on_result(@redis.keys(pattern)) do |res| res.map { |k| decrypt_key(k) } end end
# File lib/cloak/redis.rb, line 294 def lindex(key, index) on_result(@redis.lindex(encrypt_key(key), index)) do |res| decrypt_element(res) end end
# File lib/cloak/redis.rb, line 300 def linsert(key, where, pivot, value) @redis.linsert(encrypt_key(key), where, pivot, encrypt_element(value)) end
# File lib/cloak/redis.rb, line 250 def llen(key) @redis.llen(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 270 def lpop(key) @redis.lpop(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 254 def lpush(key, value) @redis.lpush(encrypt_key(key), value.is_a?(Array) ? value.map { |v| encrypt_element(v) } : encrypt_element(value)) end
# File lib/cloak/redis.rb, line 258 def lpushx(key, value) @redis.lpushx(encrypt_key(key), encrypt_element(value)) end
# File lib/cloak/redis.rb, line 304 def lrange(key, start, stop) @redis.lrange(encrypt_key(key), start, stop) end
lrem not possible with random nonce
# File lib/cloak/redis.rb, line 310 def lset(key, index, value) @redis.lset(encrypt_key(key), index, encrypt_element(value)) end
# File lib/cloak/redis.rb, line 314 def ltrim(key, start, stop) @redis.ltrim(encrypt_key(key), start, stop) end
# File lib/cloak/redis.rb, line 568 def mapped_hmget(key, *fields) ek = encrypt_key(key) on_result(@redis.mapped_hmget(ek, *fields.map { |f| encrypt_field(ek, f) })) do |res| res.map { |f, v| [decrypt_field(ek, f), decrypt_value(v)] }.to_h end end
match redis
# File lib/cloak/redis.rb, line 550 def mapped_hmset(key, hash) hmset(key, hash.to_a.flatten) end
# File lib/cloak/redis.rb, line 196 def mapped_mget(*keys) on_result(@redis.mapped_mget(*keys.map { |k| encrypt_key(k) })) do |res| res.map { |k, v| [decrypt_key(k), decrypt_value(v)] }.to_h end end
match redis
# File lib/cloak/redis.rb, line 171 def mapped_mset(hash) mset(hash.to_a.flatten) end
match redis
# File lib/cloak/redis.rb, line 180 def mapped_msetnx(hash) msetnx(hash.to_a.flatten) end
# File lib/cloak/redis.rb, line 190 def mget(*keys, &blk) on_result(@redis.mget(*keys.map { |k| encrypt_key(k) }, &blk)) do |res| res.map { |v| decrypt_value(v) } end end
# File lib/cloak/redis.rb, line 101 def move(key, db) @redis.move(encrypt_key(key), db) end
# File lib/cloak/redis.rb, line 166 def mset(*args) @redis.mset(args.map.with_index { |v, i| i % 2 == 0 ? encrypt_key(v) : encrypt_value(v) }) end
# File lib/cloak/redis.rb, line 175 def msetnx(*args) @redis.msetnx(args.map.with_index { |v, i| i % 2 == 0 ? encrypt_key(v) : encrypt_value(v) }) end
# File lib/cloak/redis.rb, line 105 def object(*args) args[1] = encrypt_key(args[1]) if args.size > 1 @redis.object(*args) end
# File lib/cloak/redis.rb, line 41 def persist(key) @redis.persist(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 57 def pexpire(key, milliseconds) @redis.pexpire(encrypt_key(key), milliseconds) end
# File lib/cloak/redis.rb, line 61 def pexpireat(key, ms_unix_time) @redis.pexpireat(encrypt_key(key), ms_unix_time) end
# File lib/cloak/redis.rb, line 699 def pfadd(key, member) @redis.pfadd(encrypt_key(key), member.is_a?(Array) ? member.map { |v| encrypt_hll_element(v) } : encrypt_hll_element(member)) end
# File lib/cloak/redis.rb, line 703 def pfcount(*keys) @redis.pfcount(*keys.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 707 def pfmerge(dest_key, *source_key) @redis.pfmerge(encrypt_key(dest_key), *source_key.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 25 def ping(message = nil) if message.nil? @redis.ping else on_result(@redis.ping(encrypt_value(message))) do |res| decrypt_value(res) end end end
# File lib/cloak/redis.rb, line 158 def psetex(key, ttl, value) @redis.psetex(encrypt_key(key), ttl, encrypt_value(value)) end
# File lib/cloak/redis.rb, line 65 def pttl(key) @redis.pttl(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 110 def randomkey on_result(@redis.randomkey) do |res| res.nil? ? res : decrypt_key(res) end end
# File lib/cloak/redis.rb, line 116 def rename(old_name, new_name) @redis.rename(encrypt_key(old_name), encrypt_key(new_name)) end
# File lib/cloak/redis.rb, line 120 def renamenx(old_name, new_name) @redis.renamenx(encrypt_key(old_name), encrypt_key(new_name)) end
# File lib/cloak/redis.rb, line 73 def restore(key, ttl, serialized_value, replace: nil) @redis.restore(encrypt_key(key), ttl, serialized_value, replace: replace) end
# File lib/cloak/redis.rb, line 274 def rpop(key) @redis.rpop(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 278 def rpoplpush(source, destination) @redis.rpoplpush(encrypt_key(source), encrypt_key(destination)) end
# File lib/cloak/redis.rb, line 262 def rpush(key, value) @redis.rpush(encrypt_key(key), value.is_a?(Array) ? value.map { |v| encrypt_element(v) } : encrypt_element(value)) end
# File lib/cloak/redis.rb, line 266 def rpushx(key, value) @redis.rpushx(encrypt_key(key), encrypt_element(value)) end
# File lib/cloak/redis.rb, line 322 def sadd(key, member) @redis.sadd(encrypt_key(key), encrypt_member(member)) end
match option not supported
# File lib/cloak/redis.rb, line 620 def scan(cursor, count: nil) on_result(@redis.scan(cursor, count: count)) do |res| [res[0], res[1].map { |v| decrypt_key(v) }] end end
match redis
# File lib/cloak/redis.rb, line 627 def scan_each(**options, &block) return to_enum(:scan_each, **options) unless block_given? cursor = 0 loop do cursor, keys = scan(cursor, **options) keys.each(&block) break if cursor == "0" end end
# File lib/cloak/redis.rb, line 318 def scard(key) @redis.scard(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 364 def sdiff(*keys) on_result(@redis.sdiff(*keys.map { |k| encrypt_key(k) })) do |res| res.map { |v| decrypt_member(v) } end end
# File lib/cloak/redis.rb, line 370 def sdiffstore(destination, *keys) @redis.sdiffstore(encrypt_key(destination), *keys.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 150 def set(key, value, **options) @redis.set(encrypt_key(key), encrypt_value(value), **options) end
# File lib/cloak/redis.rb, line 154 def setex(key, ttl, value) @redis.setex(encrypt_key(key), ttl, encrypt_value(value)) end
# File lib/cloak/redis.rb, line 162 def setnx(key, value) @redis.setnx(encrypt_key(key), ttl, encrypt_value(value)) end
# File lib/cloak/redis.rb, line 374 def sinter(*keys) on_result(@redis.sinter(*keys.map { |k| encrypt_key(k) })) do |res| res.map { |v| decrypt_member(v) } end end
# File lib/cloak/redis.rb, line 380 def sinterstore(destination, *keys) @redis.sinterstore(encrypt_key(destination), *keys.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 354 def sismember(key, member) @redis.sismember(encrypt_key(key), encrypt_member(member)) end
# File lib/cloak/redis.rb, line 358 def smembers(key) on_result(@redis.smembers(encrypt_key(key))) do |res| res.map { |v| decrypt_member(v) } end end
# File lib/cloak/redis.rb, line 350 def smove(source, destination, member) @redis.smove(encrypt_key(source), encrypt_key(destination), encrypt_member(member)) end
# File lib/cloak/redis.rb, line 330 def spop(key, count = nil) on_result(@redis.spop(encrypt_key(key))) do |res| if count.nil? decrypt_member(res) else res.map { |v| decrypt_member(v) } end end end
# File lib/cloak/redis.rb, line 340 def srandmember(key, count = nil) on_result(@redis.srandmember(encrypt_key(key))) do |res| if count.nil? decrypt_member(res) else res.map { |v| decrypt_member(v) } end end end
# File lib/cloak/redis.rb, line 326 def srem(key, member) @redis.srem(encrypt_key(key), encrypt_member(member)) end
match option not supported
# File lib/cloak/redis.rb, line 680 def sscan(key, cursor, count: nil) on_result(@redis.sscan(encrypt_key(key), cursor, count: count)) do |res| [res[0], res[1].map { |v| decrypt_member(v) }] end end
match redis
# File lib/cloak/redis.rb, line 687 def sscan_each(key, **options, &block) return to_enum(:sscan_each, key, **options) unless block_given? cursor = 0 loop do # sscan encrypts key cursor, keys = sscan(key, cursor, **options) keys.each(&block) break if cursor == "0" end end
subtract nonce size (16) and auth tag (16)
# File lib/cloak/redis.rb, line 244 def strlen(key) on_result(@redis.strlen(encrypt_key(key))) do |res| res == 0 ? 0 : res - 32 end end
# File lib/cloak/redis.rb, line 384 def sunion(*keys) on_result(@redis.sunion(*keys.map { |k| encrypt_key(k) })) do |res| res.map { |v| decrypt_member(v) } end end
# File lib/cloak/redis.rb, line 390 def sunionstore(destination, *keys) @redis.sunionstore(encrypt_key(destination), *keys.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 53 def ttl(key) @redis.ttl(encrypt_key(key)) end
sort not supported
# File lib/cloak/redis.rb, line 126 def type(key) @redis.type(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 81 def unlink(*keys) @redis.unlink(*keys.map { |k| encrypt_key(k) }) end
# File lib/cloak/redis.rb, line 398 def zadd(key, *args, **options) if args.size == 1 && args[0].is_a?(Array) args = args[0] elsif args.size == 2 args = [args] else raise ArgumentError, "wrong number of arguments" end # convert score to numeric to avoid data leakage # if there's an issue with arguments @redis.zadd(encrypt_key(key), args.map { |v| [to_score(v[0]), encrypt_member(v[1])] }, **options) end
# File lib/cloak/redis.rb, line 394 def zcard(key) @redis.zcard(encrypt_key(key)) end
# File lib/cloak/redis.rb, line 516 def zcount(key, min, max) @redis.zcount(encrypt_key(key), min, max) end
# File lib/cloak/redis.rb, line 412 def zincrby(key, increment, member) @redis.zincrby(encrypt_key(key), increment, encrypt_member(member)) end
# File lib/cloak/redis.rb, line 520 def zinterstore(destination, keys, weights: nil, aggregate: nil) @redis.zinterstore(encrypt_key(destination), keys.map { |k| encrypt_key(k) }, weights: weights, aggregate: aggregate) end
# File lib/cloak/redis.rb, line 420 def zpopmax(key, count = nil) on_result(@redis.zpopmax(encrypt_key(key), count)) do |res| if count.to_i > 1 res.map { |v, s| [decrypt_member(v), s] } else [decrypt_member(res[0]), res[1]] end end end
# File lib/cloak/redis.rb, line 430 def zpopmin(key, count = nil) on_result(@redis.zpopmin(encrypt_key(key), count)) do |res| if count.to_i > 1 res.map { |v, s| [decrypt_member(v), s] } else [decrypt_member(res[0]), res[1]] end end end
can't guarantee lexographical order without potentially fetching all elements
# File lib/cloak/redis.rb, line 453 def zrange(key, start, stop, withscores: false, with_scores: withscores) on_result(@redis.zrange(encrypt_key(key), start, stop, with_scores: with_scores)) do |res| if with_scores res.map { |v, s| [decrypt_member(v), s] } else res.map { |v| decrypt_member(v) } end end end
could guarantee lexographical order when limit not used
# File lib/cloak/redis.rb, line 491 def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil) on_result(@redis.zrangebyscore(encrypt_key(key), min, max, with_scores: with_scores, limit: limit)) do |res| if with_scores res.map { |v, s| [decrypt_member(v), s] } else res.map { |v| decrypt_member(v) } end end end
# File lib/cloak/redis.rb, line 474 def zrank(key, member) @redis.zrank(encrypt_key(key), encrypt_member(member)) end
# File lib/cloak/redis.rb, line 416 def zrem(key, member) @redis.zrem(encrypt_key(key), member.is_a?(Array) ? member.map { |v| encrypt_member(v) } : encrypt_member(member)) end
# File lib/cloak/redis.rb, line 482 def zremrangebyrank(key, start, stop) @redis.zremrangebyrank(encrypt_key(key), start, stop) end
# File lib/cloak/redis.rb, line 512 def zremrangebyscore(key, min, max) @redis.zremrangebyscore(encrypt_key(key), min, max) end
can't guarantee lexographical order without potentially fetching all elements
# File lib/cloak/redis.rb, line 464 def zrevrange(key, start, stop, withscores: false, with_scores: withscores) on_result(@redis.zrevrange(encrypt_key(key), start, stop, with_scores: with_scores)) do |res| if with_scores res.map { |v, s| [decrypt_member(v), s] } else res.map { |v| decrypt_member(v) } end end end
could guarantee lexographical order when limit not used
# File lib/cloak/redis.rb, line 502 def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil) on_result(@redis.zrevrangebyscore(encrypt_key(key), max, min, with_scores: with_scores, limit: limit)) do |res| if with_scores res.map { |v, s| [decrypt_member(v), s] } else res.map { |v| decrypt_member(v) } end end end
# File lib/cloak/redis.rb, line 478 def zrevrank(key, member) @redis.zrevrank(encrypt_key(key), encrypt_member(member)) end
match option not supported
# File lib/cloak/redis.rb, line 660 def zscan(key, cursor, count: nil) on_result(@redis.zscan(encrypt_key(key), cursor, count: count)) do |res| [res[0], res[1].map { |v| [decrypt_member(v[0]), v[1]] }] end end
match redis
# File lib/cloak/redis.rb, line 667 def zscan_each(key, **options, &block) return to_enum(:zscan_each, key, **options) unless block_given? cursor = 0 loop do # zscan encrypts key cursor, values = zscan(key, cursor, **options) values.each(&block) break if cursor == "0" end end
# File lib/cloak/redis.rb, line 448 def zscore(key, member) @redis.zscore(encrypt_key(key), encrypt_member(member)) end
# File lib/cloak/redis.rb, line 524 def zunionstore(destination, keys, weights: nil, aggregate: nil) @redis.zunionstore(encrypt_key(destination), keys.map { |k| encrypt_key(k) }, weights: weights, aggregate: aggregate) end
Private Instance Methods
# File lib/cloak/redis.rb, line 731 def _bpop(cmd, args, zset: false, &blk) # match redis timeout = if args.last.is_a?(Hash) options = args.pop options[:timeout] elsif args.last.respond_to?(:to_int) args.pop.to_int end keys = args.flatten.map { |k| encrypt_key(k) } on_result(@redis._bpop(cmd, [keys, {timeout: timeout}], &blk)) do |res| if res.nil? res elsif zset [decrypt_key(res[0]), decrypt_member(res[1]), res[2]] else [decrypt_key(res[0]), decrypt_element(res[1])] end end end
geo not supported streams not supported
# File lib/cloak/redis.rb, line 716 def on_result(res, &block) if res.is_a?(::Redis::Future) res.instance_exec do if @transformation raise "Not implemented yet. Please create an issue." else @transformation = block end end res else block.call(res) end end
# File lib/cloak/redis.rb, line 753 def to_score(v) v.is_a?(Numeric) ? v : v.to_f end