From f393154f95702a226dc5494ac25fd4bc92faeab2 Mon Sep 17 00:00:00 2001 From: Max Strother Date: Mon, 2 Oct 2023 15:40:42 +0200 Subject: [PATCH 1/8] Bump GuardianDB from 2.1.1 to 3.0.0 --- mix.exs | 2 +- mix.lock | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mix.exs b/mix.exs index 02d9f97..f847181 100644 --- a/mix.exs +++ b/mix.exs @@ -36,7 +36,7 @@ defmodule GuardianRedis.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:guardian_db, "~> 2.0"}, + {:guardian_db, "~> 3.0"}, {:redix, "~> 1.1"}, {:jason, "~> 1.1"}, diff --git a/mix.lock b/mix.lock index 91b8181..6cb4a71 100644 --- a/mix.lock +++ b/mix.lock @@ -3,33 +3,33 @@ "certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "credo": {:hex, :credo, "1.5.5", "e8f422026f553bc3bebb81c8e8bf1932f498ca03339856c7fec63d3faac8424b", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dd8623ab7091956a855dc9f3062486add9c52d310dfd62748779c4315d8247de"}, - "db_connection": {:hex, :db_connection, "2.4.0", "d04b1b73795dae60cead94189f1b8a51cc9e1f911c234cc23074017c43c031e5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad416c21ad9f61b3103d254a71b63696ecadb6a917b36f563921e0de00d7d7c8"}, - "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, + "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, + "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, - "ecto": {:hex, :ecto, "3.6.1", "7bb317e3fd0179ad725069fd0fe8a28ebe48fec6282e964ea502e4deccb0bd0f", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cbb3294a990447b19f0725488a749f8cf806374e0d9d0dffc45d61e7aeaf6553"}, - "ecto_sql": {:hex, :ecto_sql, "3.6.1", "8774dc3fc0ff7b6be510858b99883640f990c0736b8ab54588f9a0c91807f909", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "66f35c3f2d5978b6bffebd1e6351ab8c9d6b68650d62abd1ab8d149de40e0779"}, + "ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"}, + "ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.24.2", "e4c26603830c1a2286dae45f4412a4d1980e1e89dc779fcd0181ed1d5a05c8d9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "e134e1d9e821b8d9e4244687fb2ace58d479b67b282de5158333b0d57c6fb7da"}, "excoveralls": {:hex, :excoveralls, "0.14.0", "4b562d2acd87def01a3d1621e40037fdbf99f495ed3a8570dfcf1ab24e15f76d", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "94f17478b0cca020bcd85ce7eafea82d2856f7ed022be777734a2f864d36091a"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "guardian": {:hex, :guardian, "2.1.1", "1f02b349f6ba765647cc834036a8d76fa4bd65605342fe3a031df3c99d0d411a", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "189b87ba7ce6b40d6ba029138098b96ffc4ae78f229f5b39539b9141af8bf0f8"}, - "guardian_db": {:hex, :guardian_db, "2.1.0", "ec95a9d99cdd1e550555d09a7bb4a340d8887aad0697f594590c2fd74be02426", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "f8e7d543ac92c395f3a7fd5acbe6829faeade57d688f7562e2f0fca8f94a0d70"}, + "guardian": {:hex, :guardian, "2.3.2", "78003504b987f2b189d76ccf9496ceaa6a454bb2763627702233f31eb7212881", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "b189ff38cd46a22a8a824866a6867ca8722942347f13c33f7d23126af8821b52"}, + "guardian_db": {:hex, :guardian_db, "3.0.0", "c42902e3f1af1ba1e2d0c10913b926a1421f3a7e38eb4fc382b715c17489abdb", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "9c2ec4278efa34f9f1cc6ba795e552d41fdc7ffba5319d67eeb533b89392d183"}, "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "inch_ex": {:hex, :inch_ex, "2.0.0", "24268a9284a1751f2ceda569cd978e1fa394c977c45c331bb52a405de544f4de", [:mix], [{:bunt, "~> 0.2", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "96d0ec5ecac8cf63142d02f16b7ab7152cf0f0f1a185a80161b758383c9399a8"}, - "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, - "jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, - "plug": {:hex, :plug, "1.11.1", "f2992bac66fdae679453c9e86134a4201f6f43a687d8ff1cd1b2862d53c80259", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "23524e4fefbb587c11f0833b3910bfb414bf2e2534d61928e920f54e3a1b881f"}, - "plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"}, + "plug": {:hex, :plug, "1.15.0", "f40df58e1277fc7189f260daf788d628f03ae3053ce7ac1ca63eaf0423238714", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e434478d1015d968cf98ae2073e78bd63c4a06a94fe328c2df45fcd01df8ae30"}, + "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "redix": {:hex, :redix, "1.1.0", "ba08c3a10cb4e6f63711b4fda5a4dcb27a919571d353ced65aa50ab3ac978efb", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c0f552af4140b213ca2d20897aac0e137a2d3a5da1d0cb73c54f0a6847c67dcb"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, From e398db15039e072cddb7e4ca81f5a6a731e0934f Mon Sep 17 00:00:00 2001 From: Max Strother Date: Mon, 2 Oct 2023 15:48:17 +0200 Subject: [PATCH 2/8] Update Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42082b3..f24e5f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased +### Breaking +- bump `GuardianDB` from 2.1.1 to 3.0.0 + ## V0.1.0 Initial release From ac565349a96f8bc5ffc756809548c89729c8e953 Mon Sep 17 00:00:00 2001 From: Max Strother Date: Mon, 2 Oct 2023 16:24:45 +0200 Subject: [PATCH 3/8] Remove outdated config --- config/config.exs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/config/config.exs b/config/config.exs index d30abfa..5c6fd26 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,11 +1,6 @@ use Mix.Config -config :guardian, Guardian.DB, - # Add your repository module - repo: GuardianRedis.Repo, - # default: 60 minutes - sweep_interval: 60 - +config :guardian, Guardian.DB, repo: GuardianRedis.Repo # store all token types if not set # token_types: ["refresh_token"], From 89b644cbbb3f1eadb3181ac8163abae909f889ae Mon Sep 17 00:00:00 2001 From: Max Strother Date: Mon, 2 Oct 2023 17:04:35 +0200 Subject: [PATCH 4/8] Modify code to use the changed adapter behaviour --- config/config.exs | 4 +- lib/adapter.ex | 135 +++++++++++++++++++---- lib/repo.ex | 132 ---------------------- test/{repo_test.exs => adapter_test.exs} | 89 +++++++++++---- 4 files changed, 182 insertions(+), 178 deletions(-) delete mode 100644 lib/repo.ex rename test/{repo_test.exs => adapter_test.exs} (64%) diff --git a/config/config.exs b/config/config.exs index 5c6fd26..3572d31 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,8 +1,6 @@ use Mix.Config -config :guardian, Guardian.DB, repo: GuardianRedis.Repo -# store all token types if not set -# token_types: ["refresh_token"], +config :guardian, Guardian.DB, adapter: GuardianRedis.Adapter config :guardian_redis, :redis, host: System.get_env("REDIS_HOST", "127.0.0.1"), diff --git a/lib/adapter.ex b/lib/adapter.ex index f983f26..e9b1e68 100644 --- a/lib/adapter.ex +++ b/lib/adapter.ex @@ -1,41 +1,128 @@ -defmodule Guardian.DB.Adapter do +defmodule GuardianRedis.Adapter do @moduledoc """ - The Guardian DB Adapter. + `GuardianRedis.Adapter` is a module that operates Guardian.DB.Token in Redis. - This behaviour allows to use any storage system - for Guardian Tokens. + The module serves only GuardianDB purpose, do not use it as a Redis Repo for your project. + + Dependant on :jason and :redix. + + Stores and deletes JWT token to/from Redis using key combined from JWT.jti and JWT.aud. + + Module stores JWT token in Redis using automatic expiry feature of Redis so we don't need to run token sweeper. + + Anyway, `delete_all` still implemented to allow manual sweeping if needed. """ + @behaviour Guardian.DB.Adapter - @typep query :: Ecto.Query.t() - @typep schema :: Ecto.Schema.t() - @typep changeset :: Ecto.Changeset.t() - @typep schema_or_changeset :: schema() | changeset() - @typep queryable :: query() | schema() - @typep opts :: keyword() - @typep id :: pos_integer() | binary() | Ecto.UUID.t() + alias Guardian.DB.Token + alias GuardianRedis.Redix, as: Redis @doc """ - Retrieves JWT token - Used in `Guardian.DB.Token.find_by_claims/1` + Fetches a single result from the query. + + Returns nil if no result was found. Raises if more than one entry. """ - @callback one(queryable()) :: schema() | nil + @impl true + def one(query, _opts) do + key = key(query) + + case Redis.command(["GET", key]) do + {:ok, nil} -> nil + {:ok, jwt_json} -> Jason.decode!(jwt_json) + _ -> nil + end + end @doc """ - Persists JWT token - Used in `Guardian.DB.Token.create/2` + Insert Token into Redis + Token is auto expired in `expired_in` seconds based on JWT `exp` value. """ - @callback insert(schema_or_changeset()) :: {:ok, schema()} | {:error, changeset()} + @impl true + def insert(struct, _opts) do + key = key(struct) + + expires_in = struct.changes.exp - System.system_time(:second) + utc_now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + + token = + struct.changes + |> Map.put(:inserted_at, utc_now) + |> Map.put(:updated_at, utc_now) + + case Redis.command(["SETEX", key, Integer.to_string(expires_in), Jason.encode!(token)]) do + {:ok, "OK"} -> + # Adding key to the set `sub` (user_id in JWT) so we can delete all user tokens in one go + sub = sub_elem(struct) + Redis.command(["SADD", set_name(sub), key]) + {:ok, struct(Token, token)} + + error -> + {:error, error} + end + end @doc """ - Deletes JWT token - Used in `Guardian.DB.Token.destroy_token/3` + Remove a user token, log out functionality, invalidation of a JWT token. """ - @callback delete(schema_or_changeset(), opts()) :: {:ok, schema()} | {:error, changeset()} + @impl true + def delete(model, _opts) do + key = key(model) + + case Redis.command(["DEL", key]) do + {:ok, _num} -> {:ok, struct(Token, model)} + _ -> {:error, model} + end + end @doc """ - Purges all JWT tokens - Used in `Guardian.DB.Token.purge_expired_tokens/0 and in `Guardian.DB.Token.destroy_by_sub/1` - Returns a tuple containing the number of entries and any returned result as second element. + Remove all tokens that belong to given `sub`. """ - @callback delete_all(queryable(), opts()) :: {integer(), nil | [term()]} + @impl true + def delete_by_sub(sub, _opts) do + set_name = set_name(sub) + + {:ok, keys} = Redis.command(["SMEMBERS", set_name]) + {:ok, amount_deleted} = Redis.command(["DEL", set_name] ++ keys) + + # yeah, vise-versa + {amount_deleted - 1, nil} + end + + @doc """ + Mock implementation as Redis has built in key expiration. + """ + @impl true + def purge_expired_tokens(_timestamp, _opts), do: {0, nil} + + # Generate Redis key from a changeset, %Token{} struct or Ecto.Query + defp key(%{changes: %{jti: jti, aud: aud}}), do: combine_key(jti, aud) + defp key(%{"jti" => jti, "aud" => aud}), do: combine_key(jti, aud) + defp key(query), do: combine_key(jti_elem(query), aud_elem(query)) + defp sub_elem(%{changes: %{sub: sub}}), do: sub + defp sub_elem(%{"sub" => sub}), do: sub + defp sub_elem(query), do: query_param(query, :sub) + defp jti_elem(query), do: query_param(query, :jti) + defp aud_elem(query), do: query_param(query, :aud) + defp combine_key(jti, aud), do: "#{jti}:#{aud}" + defp set_name(sub), do: "set:#{sub}" + + # Retrieves params from `query.wheres` by atom name (`:jti` and `:aud` in our case), example: + + # ``` + # [ + # %Ecto.Query.BooleanExpr{ + # ... + # params: [ + # {"2e024736-b4a6-4422-8b0a-4e89c7a7ebf9", {0, :jti}}, + # {"my_app", {0, :aud}} + # ], + # ... + # } + # ] + # ``` + defp query_param(query, param) do + (query.wheres |> List.first()).params + |> Enum.find(fn i -> i |> elem(1) |> elem(1) == param end) + |> elem(0) + end end diff --git a/lib/repo.ex b/lib/repo.ex deleted file mode 100644 index cd3e257..0000000 --- a/lib/repo.ex +++ /dev/null @@ -1,132 +0,0 @@ -defmodule GuardianRedis.Repo do - @moduledoc """ - `GuardianRedis.Repo` is a repo module that operates Guardian.DB.Token in Redis. - The repo module serves only GuardianDb purpose, do not use it as a Redis repo for your project. - Dependant on :jason and :redix. - - Stores and deletes JWT token to/from Redis using key combined from JWT.jti and JWT.aud. - Module stores JWT token in Redis using automatic expiry feature of Redis so we don't need to run token sweeper. - Anyway, `delete_all` still implemented to allow manual sweeping if needed. - """ - @behaviour Guardian.DB.Adapter - - alias Guardian.DB.Token - alias GuardianRedis.Redix, as: Redis - - @spec one(queryable :: Ecto.Queryable.t(), opts :: Keyword.t()) :: - Ecto.Schema.t() | nil - @doc """ - Fetches a single result from the query. - - Returns nil if no result was found. Raises if more than one entry. - """ - def one(query, _opts \\ []) do - key = key(query) - - case Redis.command(["GET", key]) do - {:ok, nil} -> nil - {:ok, jwt_json} -> Jason.decode!(jwt_json) - _ -> nil - end - end - - @doc """ - Insert Token into Redis - Token is auto expired in `expired_in` seconds based on JWT `exp` value - """ - @spec insert( - struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(), - opts :: Keyword.t() - ) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()} - def insert(struct, _opts \\ []) do - key = key(struct) - - expires_in = struct.changes.exp - System.system_time(:second) - utc_now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - - token = - struct.changes - |> Map.put(:inserted_at, utc_now) - |> Map.put(:updated_at, utc_now) - - case Redis.command(["SETEX", key, Integer.to_string(expires_in), Jason.encode!(token)]) do - {:ok, "OK"} -> - # Adding key to the set `sub` (user_id in JWT) so we can delete all user tokens in one go - sub = sub_elem(struct) - Redis.command(["SADD", set_name(sub), key]) - {:ok, struct(Token, token)} - - error -> - {:error, error} - end - end - - @doc """ - Remove all user tokens from Redis, useful for manual sweeping - """ - @spec delete_all( - queryable :: Ecto.Queryable.t(), - opts :: Keyword.t() - ) :: {integer(), nil | [term()]} - def delete_all(query, _opts \\ []) do - set_name = query |> sub_elem() |> set_name() - - {:ok, keys} = Redis.command(["SMEMBERS", set_name]) - {:ok, amount_deleted} = Redis.command(["DEL", set_name] ++ keys) - - # yeah, vise-versa - {amount_deleted - 1, :ok} - end - - @doc """ - Remove a user token, log out functionality, invalidation of a JWT token - """ - @spec delete( - struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(), - opts :: Keyword.t() - ) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()} - def delete(model, _opts \\ []) do - key = key(model) - - case Redis.command(["DEL", key]) do - {:ok, _num} -> {:ok, struct(Token, model)} - _ -> {:error, model} - end - end - - @doc """ - Generate Redis key from a changeset, %Token{} struct or Ecto.Query - """ - defp key(%{changes: %{jti: jti, aud: aud}}), do: combine_key(jti, aud) - defp key(%{"jti" => jti, "aud" => aud}), do: combine_key(jti, aud) - defp key(query), do: combine_key(jti_elem(query), aud_elem(query)) - defp sub_elem(%{changes: %{sub: sub}}), do: sub - defp sub_elem(%{"sub" => sub}), do: sub - defp sub_elem(query), do: query_param(query, :sub) - defp jti_elem(query), do: query_param(query, :jti) - defp aud_elem(query), do: query_param(query, :aud) - defp combine_key(jti, aud), do: "#{jti}:#{aud}" - defp set_name(sub), do: "set:#{sub}" - - @doc """ - Retrieves params from `query.wheres` by atom name (`:jti` and `:aud` in our case), example: - - ``` - [ - %Ecto.Query.BooleanExpr{ - ... - params: [ - {"2e024736-b4a6-4422-8b0a-4e89c7a7ebf9", {0, :jti}}, - {"my_app", {0, :aud}} - ], - ... - } - ] - ``` - """ - defp query_param(query, param) do - (query.wheres |> List.first()).params - |> Enum.find(fn i -> i |> elem(1) |> elem(1) == param end) - |> elem(0) - end -end diff --git a/test/repo_test.exs b/test/adapter_test.exs similarity index 64% rename from test/repo_test.exs rename to test/adapter_test.exs index 65cffbd..b9ff4b8 100644 --- a/test/repo_test.exs +++ b/test/adapter_test.exs @@ -1,4 +1,4 @@ -defmodule GuardianRedis.RepoTest do +defmodule GuardianRedis.AdapterTest do use ExUnit.Case alias Guardian.DB.Token @@ -106,25 +106,76 @@ defmodule GuardianRedis.RepoTest do assert token == nil end - test "revoke_all deletes all tokens of a sub" do - sub = "the_subject" - - Token.create( - %{"jti" => "token1", "aud" => "token", "exp" => Guardian.timestamp() + 5000, "sub" => sub}, - "Token 1" - ) - - Token.create( - %{"jti" => "token2", "aud" => "token", "exp" => Guardian.timestamp() + 5000, "sub" => sub}, - "Token 2" - ) + describe "delete_by_sub/2" do + test "deletes all tokens of a sub" do + sub = "the_subject" + + Token.create( + %{ + "jti" => "token1", + "aud" => "token", + "exp" => Guardian.timestamp() + 5000, + "sub" => sub + }, + "Token 1" + ) + + Token.create( + %{ + "jti" => "token2", + "aud" => "token", + "exp" => Guardian.timestamp() + 5000, + "sub" => sub + }, + "Token 2" + ) + + Token.create( + %{ + "jti" => "token3", + "aud" => "token", + "exp" => Guardian.timestamp() + 5000, + "sub" => sub + }, + "Token 3" + ) + + assert {:ok, 3} = Guardian.DB.revoke_all(sub) + assert {:ok, []} = GuardianRedis.Redix.command(["KEYS", "*"]) + end - Token.create( - %{"jti" => "token3", "aud" => "token", "exp" => Guardian.timestamp() + 5000, "sub" => sub}, - "Token 3" - ) + test "doesn't affect tokens from a different sub" do + sub = "the_subject" + + Token.create( + %{ + "jti" => "token1", + "aud" => "token", + "exp" => Guardian.timestamp() + 5000, + "sub" => sub + }, + "Token 1" + ) + + sub_2 = "another_subject" + + Token.create( + %{ + "jti" => "token2", + "aud" => "token", + "exp" => Guardian.timestamp() + 5000, + "sub" => sub_2 + }, + "Token 2" + ) + + assert {:ok, 1} = Guardian.DB.revoke_all(sub) + assert {:ok, keys} = GuardianRedis.Redix.command(["KEYS", "*"]) + assert ["set:#{sub_2}", "token2:token"] == keys + end + end - assert Guardian.DB.revoke_all(sub) == {:ok, 3} - assert GuardianRedis.Redix.command(["KEYS", "*"]) == {:ok, []} + test "purge_expired_tokens/2 can be called" do + assert {0, nil} = Guardian.DB.Token.purge_expired_tokens() end end From 99cc46c2cd54e3a17acd181cea73be118213620d Mon Sep 17 00:00:00 2001 From: Max Strother Date: Mon, 2 Oct 2023 17:07:13 +0200 Subject: [PATCH 5/8] Update text to reflect on the repo to adapter switch --- README.md | 20 ++++++++++---------- lib/adapter.ex | 2 +- mix.exs | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a571bdc..e304fe6 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ [![Hex.pm](https://img.shields.io/hexpm/v/guardian_redis.svg)](https://hex.pm/packages/guardian_redis) ![Build Status](https://github.com/alexfilatov/guardian_redis/workflows/Continuous%20Integration/badge.svg) -Redis repository for Guardian DB. +Redis adapter for Guardian DB. ## Installation -You can use `GuardianRedis` in case you use [Guardian](https://github.com/ueberauth/guardian) library for authentication -and [Guardian.DB](https://github.com/ueberauth/guardian_db) library for JWT tokens persistence. +You can use `GuardianRedis` in case you use [Guardian](https://github.com/ueberauth/guardian) library for authentication +and [Guardian.DB](https://github.com/ueberauth/guardian_db) library for JWT tokens persistence. If [available in Hex](https://hex.pm/docs/publish), the package can be installed by adding `guardian_redis` to your list of dependencies in `mix.exs`: @@ -24,12 +24,12 @@ end ## Configuration -All you need to install Guardian DB as per [Guardian.DB README](https://github.com/ueberauth/guardian_db#readme) -and just use `GuardianRedis.Repo` as a `repo` in settings. +All you need to install Guardian DB as per [Guardian.DB README](https://github.com/ueberauth/guardian_db#readme) +and just use `GuardianRedis.Adapter` as a `adapter` in settings. ```elixir config :guardian, Guardian.DB, - repo: GuardianRedis.Repo # Add this Redis repository module + adapter: GuardianRedis.Adapter # Add this Redis adapter module ``` Add GuardianRedis.Redix to your supervision tree: @@ -60,11 +60,11 @@ config :guardian_redis, :redis, ``` -## Implement Guardian.DB repo for a different storage +## Implement Guardian.DB adapter for a different storage -Initially, Guardian.DB was aimed to store and operate JWT tokens in a PostgreSQL database. -Sometimes round trip to Postgres database is expensive so this is why this Redis repo was born. -In case you want to implement a possibility for Guardian.DB to use different storage, e.g. ETS (or MySQL), +Initially, Guardian.DB was aimed to store and operate JWT tokens in a PostgreSQL database. +Sometimes round trip to Postgres database is expensive so this is why this Redis adapter was born. +In case you want to implement a possibility for Guardian.DB to use different storage, e.g. ETS (or MySQL), you need to implement `Guardian.DB.Adapter` behavior. Thanks to [@aleDsz](https://github.com/aleDsz) it's quite simple: https://github.com/ueberauth/guardian_db/blob/master/lib/guardian/db/adapter.ex diff --git a/lib/adapter.ex b/lib/adapter.ex index e9b1e68..9b18f8e 100644 --- a/lib/adapter.ex +++ b/lib/adapter.ex @@ -2,7 +2,7 @@ defmodule GuardianRedis.Adapter do @moduledoc """ `GuardianRedis.Adapter` is a module that operates Guardian.DB.Token in Redis. - The module serves only GuardianDB purpose, do not use it as a Redis Repo for your project. + The module serves only GuardianDB purpose, do not use it as a Redis adapter for your project. Dependant on :jason and :redix. diff --git a/mix.exs b/mix.exs index f847181..f808a9a 100644 --- a/mix.exs +++ b/mix.exs @@ -9,7 +9,7 @@ defmodule GuardianRedis.MixProject do name: "GuardianRedis", app: :guardian_redis, version: @version, - description: "Redis repo for Guardian DB", + description: "Redis adapter for Guardian DB", elixir: "~> 1.4 or ~> 1.5", elixirc_paths: elixirc_paths(Mix.env()), package: package(), From b7849576aa5d337720ef1e672116e8042767554c Mon Sep 17 00:00:00 2001 From: Max Strother Date: Mon, 2 Oct 2023 17:14:23 +0200 Subject: [PATCH 6/8] Add a micro migration guide --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f24e5f8..3e16998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Changelog ## Unreleased +GuardianDB 3.0 changes the way the it's configuration is done. +Change +```elixir +config :guardian, Guardian.DB, repo: GuardianRedis.Repo +``` +in your config to: +```elixir +config :guardian, Guardian.DB, adapter: GuardianRedis.Adapter +``` ### Breaking - bump `GuardianDB` from 2.1.1 to 3.0.0 From b3ece764dd49d7f598d34c339b06db38dd86932d Mon Sep 17 00:00:00 2001 From: Max Strother Date: Tue, 3 Oct 2023 13:18:54 +0200 Subject: [PATCH 7/8] Documentation improvements --- lib/adapter.ex | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/adapter.ex b/lib/adapter.ex index 9b18f8e..f981eb7 100644 --- a/lib/adapter.ex +++ b/lib/adapter.ex @@ -1,16 +1,14 @@ defmodule GuardianRedis.Adapter do @moduledoc """ - `GuardianRedis.Adapter` is a module that operates Guardian.DB.Token in Redis. + An adapter for `Guardian.DB` that uses Redis. - The module serves only GuardianDB purpose, do not use it as a Redis adapter for your project. + Serves only `Guardian.DB` purpose, do not use it as a Redis adapter for your project. - Dependant on :jason and :redix. + Dependant on `Jason` and `Redix`. - Stores and deletes JWT token to/from Redis using key combined from JWT.jti and JWT.aud. + Stores and deletes JWT token to/from Redis using key combined from JWT.jti and JWT.aud. - Module stores JWT token in Redis using automatic expiry feature of Redis so we don't need to run token sweeper. - - Anyway, `delete_all` still implemented to allow manual sweeping if needed. + Module stores JWT token in Redis using automatic expiry feature of Redis so we don't need to run token sweeper. """ @behaviour Guardian.DB.Adapter @@ -34,7 +32,8 @@ defmodule GuardianRedis.Adapter do end @doc """ - Insert Token into Redis + Insert Token into Redis. + Token is auto expired in `expired_in` seconds based on JWT `exp` value. """ @impl true @@ -89,7 +88,7 @@ defmodule GuardianRedis.Adapter do end @doc """ - Mock implementation as Redis has built in key expiration. + Mock implementation as Redis has built in key expiration that is in use. """ @impl true def purge_expired_tokens(_timestamp, _opts), do: {0, nil} @@ -107,7 +106,6 @@ defmodule GuardianRedis.Adapter do defp set_name(sub), do: "set:#{sub}" # Retrieves params from `query.wheres` by atom name (`:jti` and `:aud` in our case), example: - # ``` # [ # %Ecto.Query.BooleanExpr{ From 992226d96dbc2d6965c3436da52d7b0b057250df Mon Sep 17 00:00:00 2001 From: Max Strother Date: Tue, 3 Oct 2023 13:19:24 +0200 Subject: [PATCH 8/8] Fix delete_by_sub/2 returning -1 if nothing was deleted --- lib/adapter.ex | 5 ++-- test/adapter_test.exs | 65 ++++++++++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/lib/adapter.ex b/lib/adapter.ex index f981eb7..a935026 100644 --- a/lib/adapter.ex +++ b/lib/adapter.ex @@ -83,8 +83,9 @@ defmodule GuardianRedis.Adapter do {:ok, keys} = Redis.command(["SMEMBERS", set_name]) {:ok, amount_deleted} = Redis.command(["DEL", set_name] ++ keys) - # yeah, vise-versa - {amount_deleted - 1, nil} + if amount_deleted > 0, + do: {amount_deleted - 1, nil}, + else: {0, nil} end @doc """ diff --git a/test/adapter_test.exs b/test/adapter_test.exs index b9ff4b8..8e6c720 100644 --- a/test/adapter_test.exs +++ b/test/adapter_test.exs @@ -28,7 +28,7 @@ defmodule GuardianRedis.AdapterTest do {:ok, %{claims: claims}} end - test "after_encode_and_sign_in is successful", context do + test "after_encode_and_sign is successful", context do token = get_token() assert token == nil @@ -46,18 +46,21 @@ defmodule GuardianRedis.AdapterTest do assert token["claims"] == context.claims end - test "on_verify with a record in the db", context do - Token.create(context.claims, "The JWT") - token = get_token() - assert token != nil + describe "on_verify/2" do + test "with a record in the db", context do + Token.create(context.claims, "The JWT") + token = get_token() + assert token != nil - assert {:ok, {context.claims, "The JWT"}} == Guardian.DB.on_verify(context.claims, "The JWT") - end + assert {:ok, {context.claims, "The JWT"}} == + Guardian.DB.on_verify(context.claims, "The JWT") + end - test "on_verify without a record in the db", context do - token = get_token() - assert token == nil - assert {:error, :token_not_found} == Guardian.DB.on_verify(context.claims, "The JWT") + test "without a record in the db", context do + token = get_token() + assert token == nil + assert {:error, :token_not_found} == Guardian.DB.on_verify(context.claims, "The JWT") + end end test "on_refresh without a record in the db", context do @@ -84,26 +87,31 @@ defmodule GuardianRedis.AdapterTest do assert Guardian.DB.on_refresh(old_stuff, new_stuff) == {:ok, old_stuff, new_stuff} end - test "on_revoke without a record in the db", context do - token = get_token() - assert token == nil - assert Guardian.DB.on_revoke(context.claims, "The JWT") == {:ok, {context.claims, "The JWT"}} - end + describe "on_revoke/2" do + test "without a record in the db", context do + token = get_token() + assert token == nil - test "on_revoke with a record in the db", context do - Token.create(context.claims, "The JWT") + assert Guardian.DB.on_revoke(context.claims, "The JWT") == + {:ok, {context.claims, "The JWT"}} + end - {:ok, keys} = GuardianRedis.Redix.command(["KEYS", "*"]) - assert Enum.sort(keys) == ["set:initial_the_subject", "token-uuid:token"] + test "with a record in the db", context do + Token.create(context.claims, "The JWT") - token = get_token() + {:ok, keys} = GuardianRedis.Redix.command(["KEYS", "*"]) + assert Enum.sort(keys) == ["set:initial_the_subject", "token-uuid:token"] - assert token != nil + token = get_token() - assert Guardian.DB.on_revoke(context.claims, "The JWT") == {:ok, {context.claims, "The JWT"}} + assert token != nil - token = get_token() - assert token == nil + assert Guardian.DB.on_revoke(context.claims, "The JWT") == + {:ok, {context.claims, "The JWT"}} + + token = get_token() + assert token == nil + end end describe "delete_by_sub/2" do @@ -171,7 +179,12 @@ defmodule GuardianRedis.AdapterTest do assert {:ok, 1} = Guardian.DB.revoke_all(sub) assert {:ok, keys} = GuardianRedis.Redix.command(["KEYS", "*"]) - assert ["set:#{sub_2}", "token2:token"] == keys + assert Enum.all?(["set:#{sub_2}", "token2:token"], &(&1 in keys)) + end + + test "returns 0 if nothing was revoked" do + sub = "the_subject" + assert {:ok, 0} = Guardian.DB.revoke_all(sub) end end