From 3ba492242353c34b1048997e01a0c32d2e739763 Mon Sep 17 00:00:00 2001 From: Ashley Ellis Date: Thu, 2 Feb 2017 09:01:39 -0500 Subject: [PATCH] Add ability to delete all redbird sessions * Prepends all session keys with "redbird_sessions" so that it will be easy to identify which keys in the users redis database were set through Redbird * Adds the ExRedis `keys` option to find all keys that start with the redbird session * Adds `delete_all_sessions` to delete all matching keys --- lib/mix/tasks/redbird/delete_all_sessions.ex | 22 +++++++++++++ lib/redbird/plug/session/redis.ex | 27 +++++++++++++--- lib/redis.ex | 4 +++ .../redbird/delete_all_sessions_test.exs | 31 +++++++++++++++++++ test/redbird_test.exs | 28 +++++++++++++++++ 5 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 lib/mix/tasks/redbird/delete_all_sessions.ex create mode 100644 test/mix/tasks/redbird/delete_all_sessions_test.exs diff --git a/lib/mix/tasks/redbird/delete_all_sessions.ex b/lib/mix/tasks/redbird/delete_all_sessions.ex new file mode 100644 index 0000000..3001b59 --- /dev/null +++ b/lib/mix/tasks/redbird/delete_all_sessions.ex @@ -0,0 +1,22 @@ +defmodule Mix.Tasks.Redbird.DeleteAllSessions do + use Mix.Task + + @shortdoc "Deletes all redbird sessions" + + @moduledoc """ + Deletes all redbird sessions. + mix redbird.delete_all_sessions my_app + The first argument is the app specific key_namespace you set in your plug + session config. If no argument is given, it will delete all redbird sessions. + """ + def run(_args) do + Redbird.start(nil, nil) + Plug.Session.REDIS.namespace + |> delete_all_sessions + end + + def delete_all_sessions(namespace) do + Redbird.Redis.keys("#{namespace}*") + |> Redbird.Redis.del + end +end diff --git a/lib/redbird/plug/session/redis.ex b/lib/redbird/plug/session/redis.ex index 85b0737..d7459ec 100644 --- a/lib/redbird/plug/session/redis.ex +++ b/lib/redbird/plug/session/redis.ex @@ -14,10 +14,10 @@ defmodule Plug.Session.REDIS do opts end - def get(_conn, redis_key, _init_options) do - case get(redis_key) do + def get(_conn, namespaced_key, _init_options) do + case get(namespaced_key) do :undefined -> {nil, %{}} - value -> {redis_key, value |> :erlang.binary_to_term} + value -> {namespaced_key, value |> :erlang.binary_to_term} end end @@ -25,8 +25,13 @@ defmodule Plug.Session.REDIS do put(conn, generate_random_key(), data, init_options) end def put(_conn, redis_key, data, init_options) do - setex(%{key: redis_key, value: data, seconds: session_expiration(init_options)}) - redis_key + key = add_namespace(redis_key) + setex(%{ + key: key, + value: data, + seconds: session_expiration(init_options) + }) + key end def delete(_conn, redis_key, _kinit_options) do @@ -34,6 +39,18 @@ defmodule Plug.Session.REDIS do :ok end + defp add_namespace(key) do + namespace <> key + end + + def namespace do + Application.get_env(:redbird, :key_namespace, redbird_namespace) + end + + def redbird_namespace do + "redbird_session_" + end + defp generate_random_key do :crypto.strong_rand_bytes(96) |> Base.encode64 end diff --git a/lib/redis.ex b/lib/redis.ex index 52e689f..85416d7 100644 --- a/lib/redis.ex +++ b/lib/redis.ex @@ -17,6 +17,10 @@ defmodule Redbird.Redis do Exredis.Api.del(pid(), key) end + def keys(pattern) do + Exredis.Api.keys(pattern) + end + def pid do :redbird_phoenix_session end diff --git a/test/mix/tasks/redbird/delete_all_sessions_test.exs b/test/mix/tasks/redbird/delete_all_sessions_test.exs new file mode 100644 index 0000000..6bab3cd --- /dev/null +++ b/test/mix/tasks/redbird/delete_all_sessions_test.exs @@ -0,0 +1,31 @@ +defmodule Mix.Tasks.Redbird.DeleteAllSessionsTest do + use ExUnit.Case + alias Plug.Session.REDIS + + setup do + on_exit fn -> + Mix.Tasks.Redbird.DeleteAllSessions.run([]) + end + end + + test "deletes all redbird session keys" do + key = "redis_session" + conn = %{} + options = [] + REDIS.put(conn, key, %{foo: :bar}, options) + + assert {nil, %{}} = REDIS.get(conn, key, options) + end + + test "deletes user defined namespaced session keys" do + Application.put_env(:redbird, :key_namespace, "test_") + conn = %{} + key = "redis_session" + options = [] + REDIS.put(conn, key, %{foo: :bar}, options) + + Mix.Tasks.Redbird.DeleteAllSessions.run([]) + + assert {nil, %{}} = REDIS.get(conn, "test_" <> key, options) + end +end diff --git a/test/redbird_test.exs b/test/redbird_test.exs index 48bdf99..a84471a 100644 --- a/test/redbird_test.exs +++ b/test/redbird_test.exs @@ -27,6 +27,13 @@ defmodule RedbirdTest do :ok = Application.start(:redbird) end + setup do + on_exit fn -> + Redbird.Redis.keys(Plug.Session.REDIS.namespace <> "*") + |> Redbird.Redis.del + end + end + describe "get" do test "when there is value stored it is retrieved" do conn = conn(:get, "/") @@ -90,4 +97,25 @@ defmodule RedbirdTest do assert {nil, %{}} = REDIS.get(conn, key, options) end end + + test "redbird_session is appended to key names by default" do + conn = %{} + key = "redis_session" + options = [] + REDIS.put(conn, key, %{foo: :bar}, options) + + assert {"redbird_session_redis_session", %{foo: :bar}} = REDIS.get(conn, "redbird_session_" <> key, options) + end + + test "user can set their own key namespace" do + Application.put_env(:redbird, :key_namespace, "test_") + Redbird.Redis.keys("test_*") + |> Redbird.Redis.del + conn = %{} + key = "redis_session" + options = [] + REDIS.put(conn, key, %{foo: :bar}, options) + + assert {"test_redis_session", %{foo: :bar}} = REDIS.get(conn, "test_" <> key, options) + end end