Implement internal token interface
This commit is contained in:
parent
64e9edec5a
commit
8524e90ac8
|
@ -1,6 +1,7 @@
|
||||||
defmodule Hostas.Token do
|
defmodule Hostas.Token do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query, only: [from: 2]
|
||||||
|
|
||||||
alias Hostas.Token
|
alias Hostas.Token
|
||||||
alias Hostas.Repo
|
alias Hostas.Repo
|
||||||
|
@ -11,12 +12,11 @@ defmodule Hostas.Token do
|
||||||
@duration_days 30
|
@duration_days 30
|
||||||
|
|
||||||
schema "tokens" do
|
schema "tokens" do
|
||||||
field :denizen_id, :integer
|
|
||||||
field :expires, :utc_datetime
|
field :expires, :utc_datetime
|
||||||
field :token, :string
|
field :token, :string
|
||||||
|
|
||||||
# Link tokens to denizens
|
# Link tokens to denizens
|
||||||
belongs_to :denizens, Hostas.Denizen
|
belongs_to :denizen, Hostas.Denizen
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
@ -24,8 +24,8 @@ defmodule Hostas.Token do
|
||||||
@doc false
|
@doc false
|
||||||
def changeset(token, attrs) do
|
def changeset(token, attrs) do
|
||||||
token
|
token
|
||||||
|> cast(attrs, [:denizen_id, :expires])
|
|> cast(attrs, [:denizen_id, :expires, :token])
|
||||||
|> validate_required([:denizen_id, :expires])
|
|> validate_required([:denizen_id, :expires, :token])
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -46,4 +46,41 @@ defmodule Hostas.Token do
|
||||||
# Register the token
|
# Register the token
|
||||||
Repo.insert(changeset(%Token{}, %{denizen_id: denizen_id, token: token, expires: expiry}))
|
Repo.insert(changeset(%Token{}, %{denizen_id: denizen_id, token: token, expires: expiry}))
|
||||||
end
|
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
|
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