Implement internal token interface
This commit is contained in:
parent
64e9edec5a
commit
8524e90ac8
|
@ -1,6 +1,7 @@
|
|||
defmodule Hostas.Token do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query, only: [from: 2]
|
||||
|
||||
alias Hostas.Token
|
||||
alias Hostas.Repo
|
||||
|
@ -11,12 +12,11 @@ defmodule Hostas.Token do
|
|||
@duration_days 30
|
||||
|
||||
schema "tokens" do
|
||||
field :denizen_id, :integer
|
||||
field :expires, :utc_datetime
|
||||
field :token, :string
|
||||
|
||||
# Link tokens to denizens
|
||||
belongs_to :denizens, Hostas.Denizen
|
||||
belongs_to :denizen, Hostas.Denizen
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
@ -24,8 +24,8 @@ defmodule Hostas.Token do
|
|||
@doc false
|
||||
def changeset(token, attrs) do
|
||||
token
|
||||
|> cast(attrs, [:denizen_id, :expires])
|
||||
|> validate_required([:denizen_id, :expires])
|
||||
|> cast(attrs, [:denizen_id, :expires, :token])
|
||||
|> validate_required([:denizen_id, :expires, :token])
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -46,4 +46,41 @@ defmodule Hostas.Token do
|
|||
# Register the token
|
||||
Repo.insert(changeset(%Token{}, %{denizen_id: denizen_id, token: token, expires: expiry}))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns {:ok, struct} if the token hasn't expired. Otherwise, returns :expired
|
||||
"""
|
||||
def get(key) do
|
||||
struct = Repo.one!(from t in Token, where: t.token == ^key)
|
||||
|
||||
unless expired?(struct) do
|
||||
{:ok, struct}
|
||||
else
|
||||
:expired
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the given token has expired
|
||||
"""
|
||||
def expired?(%Token{expires: expiry}) do
|
||||
{:ok, now} = DateTime.now("Etc/UTC")
|
||||
case DateTime.compare(expiry, now) do
|
||||
:lt -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
def expired?(token) when is_binary(token) do
|
||||
expired?(Repo.one(from t in Token, where: t.token == ^token))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a token. Does nothing if the token doesn't exist
|
||||
"""
|
||||
def revoke(%Token{token: token}), do: revoke(token)
|
||||
def revoke(token) when is_binary(token) do
|
||||
Repo.delete_all(from t in Token, where: t.token == ^token)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
defmodule Hostas.TokenTest do
|
||||
use Hostas.DataCase
|
||||
|
||||
# For testing with Ecto
|
||||
import Ecto.Query, only: [from: 2]
|
||||
alias Hostas.Repo
|
||||
alias Hostas.Token
|
||||
|
||||
describe "token generation" do
|
||||
test "succeeds" do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
assert Repo.one!(from t in Token, where: t.id == ^struct.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "check token expiry" do
|
||||
test "by struct" do
|
||||
{:ok, struct} = Token.new(create_denizen().id, -30)
|
||||
assert Token.expired?(struct)
|
||||
end
|
||||
|
||||
test "by key" do
|
||||
{:ok, struct} = Token.new(create_denizen().id, -30)
|
||||
assert Token.expired?(struct.token)
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch guarded against expiry" do
|
||||
test "succeeds" do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
assert Token.get(struct.token) == {:ok, struct}
|
||||
end
|
||||
|
||||
test "indicates the token is expired" do
|
||||
{:ok, struct} = Token.new(create_denizen().id, -30)
|
||||
assert Token.get(struct.token) == :expired
|
||||
end
|
||||
|
||||
test "by key" do
|
||||
{:ok, struct} = Token.new(create_denizen().id, -30)
|
||||
assert Token.expired?(struct.token)
|
||||
end
|
||||
end
|
||||
|
||||
describe "revoke token" do
|
||||
test "succeeds, given struct" do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
assert Token.revoke(struct) == :ok
|
||||
assert Repo.one(from t in Token, where: t.token == ^struct.token) == nil
|
||||
end
|
||||
|
||||
test "succeeds, given key" do
|
||||
{:ok, struct} = Token.new(create_denizen().id)
|
||||
assert Token.revoke(struct.token) == :ok
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue