Add authentication for Fever API
This commit is contained in:
parent
74a82967dd
commit
9fa6968f6f
|
@ -6,6 +6,8 @@ defmodule Frenzy.User do
|
||||||
field :username, :string
|
field :username, :string
|
||||||
field :password, :string, virtual: true
|
field :password, :string, virtual: true
|
||||||
field :password_hash, :string
|
field :password_hash, :string
|
||||||
|
field :fever_password, :string, virtual: true
|
||||||
|
field :fever_auth_token, :string
|
||||||
|
|
||||||
has_many :groups, Frenzy.Group, on_delete: :delete_all
|
has_many :groups, Frenzy.Group, on_delete: :delete_all
|
||||||
|
|
||||||
|
@ -21,16 +23,23 @@ defmodule Frenzy.User do
|
||||||
|
|
||||||
def registration_changeset(user, attrs) do
|
def registration_changeset(user, attrs) do
|
||||||
user
|
user
|
||||||
|> cast(attrs, [:username, :password])
|
|> cast(attrs, [:username, :password, :fever_password])
|
||||||
|> validate_length(:password, min: 8)
|
|> validate_length(:password, min: 8)
|
||||||
|
|> validate_length(:fever_password, min: 8)
|
||||||
|> put_password_hash()
|
|> put_password_hash()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp put_password_hash(
|
defp put_password_hash(
|
||||||
%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
|
%Ecto.Changeset{
|
||||||
|
valid?: true,
|
||||||
|
changes: %{username: username, password: password, fever_password: fever_password}
|
||||||
|
} = changeset
|
||||||
) do
|
) do
|
||||||
change(changeset, Bcrypt.add_hash(password))
|
changeset
|
||||||
|
|> change(Bcrypt.add_hash(password))
|
||||||
|
|> change(%{
|
||||||
|
fever_auth_token:
|
||||||
|
:crypto.hash(:md5, "#{username}:#{fever_password}") |> Base.encode16(case: :lower)
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp put_password_hash(changeset), do: changeset
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -73,7 +73,7 @@ defmodule FrenzyWeb.FeedController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def refresh(conn, _params) do
|
def refresh(conn, _params) do
|
||||||
feed = conn.assgins[:feed] |> Repo.preload(:filter)
|
feed = conn.assigns[:feed] |> Repo.preload(:filter)
|
||||||
feed = Frenzy.UpdateFeeds.refresh(Frenzy.UpdateFeeds, feed)
|
feed = Frenzy.UpdateFeeds.refresh(Frenzy.UpdateFeeds, feed)
|
||||||
redirect(conn, to: Routes.feed_path(Endpoint, :show, feed.id))
|
redirect(conn, to: Routes.feed_path(Endpoint, :show, feed.id))
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule FrenzyWeb.FeverController do
|
defmodule FrenzyWeb.FeverController do
|
||||||
use FrenzyWeb, :controller
|
use FrenzyWeb, :controller
|
||||||
alias Frenzy.{Repo, Group, Feed, Item}
|
alias Frenzy.{Repo, User, Group, Feed, Item}
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
plug :api_check
|
plug :api_check
|
||||||
|
@ -22,95 +22,133 @@ defmodule FrenzyWeb.FeverController do
|
||||||
:invalid ->
|
:invalid ->
|
||||||
resp(conn, 401, "Invalid API key")
|
resp(conn, 401, "Invalid API key")
|
||||||
|
|
||||||
:ok ->
|
{:ok, user} ->
|
||||||
json(conn, fever_response(params))
|
user = Repo.preload(user, groups: [:feeds])
|
||||||
|
res = fever_response(user, params)
|
||||||
|
json(conn, res)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_key(api_key) do
|
defp validate_key(api_key) do
|
||||||
auth = Application.get_env(:frenzy, :auth)
|
case Repo.get_by(User, fever_auth_token: api_key) do
|
||||||
username = auth[:username]
|
nil ->
|
||||||
password = auth[:password]
|
:invalid
|
||||||
expected = :crypto.hash(:md5, "#{username}:#{password}") |> Base.encode16(case: :lower)
|
|
||||||
|
|
||||||
case api_key |> String.downcase() do
|
user ->
|
||||||
^expected -> :ok
|
{:ok, user}
|
||||||
_ -> :invalid
|
end
|
||||||
|
|
||||||
|
# auth = Application.get_env(:frenzy, :auth)
|
||||||
|
# username = auth[:username]
|
||||||
|
# password = auth[:password]
|
||||||
|
# expected = :crypto.hash(:md5, "#{username}:#{password}") |> Base.encode16(case: :lower)
|
||||||
|
|
||||||
|
# case api_key |> String.downcase() do
|
||||||
|
# ^expected -> :ok
|
||||||
|
# _ -> :invalid
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fever_response(user, params) do
|
||||||
|
%{api_version: 2, auth: 1}
|
||||||
|
|> mark(user, params)
|
||||||
|
|> unread_recently_read(user, params)
|
||||||
|
|> feeds(user, params)
|
||||||
|
|> groups(user, params)
|
||||||
|
|> feeds_groups(user, params)
|
||||||
|
|> favicons(user, params)
|
||||||
|
|> links(user, params)
|
||||||
|
|> unread(user, params)
|
||||||
|
|> saved(user, params)
|
||||||
|
|> items(user, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_user_item(user, id) do
|
||||||
|
item = Repo.get(Item, id) |> Repo.preload(:feed)
|
||||||
|
|
||||||
|
if not is_nil(item) do
|
||||||
|
feeds = Enum.flat_map(user.groups, fn g -> g.feeds end)
|
||||||
|
|
||||||
|
if Enum.any?(feeds, fn f -> f.id == item.feed_id end) do
|
||||||
|
{:ok, item}
|
||||||
|
else
|
||||||
|
{:error, "item does not belong to given user"}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{:error, "item does not exist"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fever_response(params) do
|
defp mark(res, user, %{"mark" => "item", "id" => id, "as" => as}) do
|
||||||
%{api_version: 2, auth: 1}
|
with {:ok, item} <- get_user_item(user, id) do
|
||||||
|> mark(params)
|
diff =
|
||||||
|> unread_recently_read(params)
|
case as do
|
||||||
|> feeds(params)
|
"read" ->
|
||||||
|> groups(params)
|
%{read: true, read_date: Timex.now()}
|
||||||
|> feeds_groups(params)
|
|
||||||
|> favicons(params)
|
|
||||||
|> links(params)
|
|
||||||
|> unread(params)
|
|
||||||
|> saved(params)
|
|
||||||
|> items(params)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp mark(res, %{"mark" => "item", "id" => id, "as" => as}) do
|
"unread" ->
|
||||||
item = Repo.get(Item, id) |> Repo.preload(:feed)
|
%{read: false, read_date: nil}
|
||||||
|
|
||||||
diff =
|
_ ->
|
||||||
case as do
|
%{}
|
||||||
"read" ->
|
end
|
||||||
%{read: true, read_date: Timex.now()}
|
|
||||||
|
|
||||||
"unread" ->
|
changeset = Item.changeset(item, diff)
|
||||||
%{read: false, read_date: nil}
|
Repo.update(changeset)
|
||||||
|
end
|
||||||
|
|
||||||
_ ->
|
|
||||||
%{}
|
|
||||||
end
|
|
||||||
|
|
||||||
changeset = Item.changeset(item, diff)
|
|
||||||
Repo.update(changeset)
|
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
defp mark(res, _), do: res
|
defp mark(res, _, _), do: res
|
||||||
|
|
||||||
defp unread_recently_read(res, %{"unread_recently_read" => 1}) do
|
defp unread_recently_read(res, user, %{"unread_recently_read" => 1}) do
|
||||||
Repo.all(from i in Item, where: i.read, where: i.read_date >= from_now(-1, "hour"))
|
feed_ids =
|
||||||
|
user.groups
|
||||||
|
|> Enum.flat_map(fn g -> g.feeds end)
|
||||||
|
|> Enum.map(fn f -> f.id end)
|
||||||
|
|
||||||
|
Repo.all(
|
||||||
|
from i in Item,
|
||||||
|
where: i.feed_id in ^feed_ids,
|
||||||
|
where: i.read,
|
||||||
|
where: i.read_date >= from_now(-1, "hour")
|
||||||
|
)
|
||||||
|> Enum.map(fn i -> Item.changeset(i, %{read: false, read_date: nil}) end)
|
|> Enum.map(fn i -> Item.changeset(i, %{read: false, read_date: nil}) end)
|
||||||
|> Enum.map(&Repo.update/1)
|
|> Enum.map(&Repo.update/1)
|
||||||
|
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
defp unread_recently_read(res, _), do: res
|
defp unread_recently_read(res, _, _), do: res
|
||||||
|
|
||||||
defp feeds(res, %{"feeds" => _}) do
|
defp feeds(res, user, %{"feeds" => _}) do
|
||||||
feeds =
|
feeds =
|
||||||
Repo.all(Feed)
|
user.groups
|
||||||
|
|> Enum.flat_map(fn g -> g.feeds end)
|
||||||
|> Enum.map(&Feed.to_fever/1)
|
|> Enum.map(&Feed.to_fever/1)
|
||||||
|
|
||||||
res
|
res
|
||||||
|> Map.put(:feeds, feeds)
|
|> Map.put(:feeds, feeds)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp feeds(res, _), do: res
|
defp feeds(res, _, _), do: res
|
||||||
|
|
||||||
defp groups(res, %{"groups" => _}) do
|
defp groups(res, user, %{"groups" => _}) do
|
||||||
groups =
|
groups =
|
||||||
Repo.all(Group)
|
user.groups
|
||||||
|> Enum.map(&Group.to_fever_group/1)
|
|> Enum.map(&Group.to_fever_group/1)
|
||||||
|
|
||||||
res
|
res
|
||||||
|> Map.put(:groups, groups)
|
|> Map.put(:groups, groups)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp groups(res, _), do: res
|
defp groups(res, _, _), do: res
|
||||||
|
|
||||||
defp feeds_groups(res, params) do
|
defp feeds_groups(res, user, params) do
|
||||||
if Map.has_key?(params, "feeds") or Map.has_key?(params, "groups") do
|
if Map.has_key?(params, "feeds") or Map.has_key?(params, "groups") do
|
||||||
feeds_groups =
|
feeds_groups =
|
||||||
Repo.all(from Group, preload: [:feeds])
|
user.groups
|
||||||
|> Enum.map(&Group.to_fever_feeds_group/1)
|
|> Enum.map(&Group.to_fever_feeds_group/1)
|
||||||
|
|
||||||
res
|
res
|
||||||
|
@ -120,23 +158,28 @@ defmodule FrenzyWeb.FeverController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp favicons(res, %{"favicons" => _}) do
|
defp favicons(res, _user, %{"favicons" => _}) do
|
||||||
res
|
res
|
||||||
|> Map.put(:favicons, [])
|
|> Map.put(:favicons, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp favicons(res, _), do: res
|
defp favicons(res, _, _), do: res
|
||||||
|
|
||||||
defp links(res, %{"links" => _}) do
|
defp links(res, _user, %{"links" => _}) do
|
||||||
res
|
res
|
||||||
|> Map.put(:links, [])
|
|> Map.put(:links, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp links(res, _), do: res
|
defp links(res, _, _), do: res
|
||||||
|
|
||||||
|
defp unread(res, user, %{"unread_item_ids" => _}) do
|
||||||
|
feed_ids =
|
||||||
|
user.groups
|
||||||
|
|> Enum.flat_map(fn g -> g.feeds end)
|
||||||
|
|> Enum.map(fn f -> f.id end)
|
||||||
|
|
||||||
defp unread(res, %{"unread_item_ids" => _}) do
|
|
||||||
unread =
|
unread =
|
||||||
Repo.all(from Item, where: [read: false])
|
Repo.all(from i in Item, where: i.feed_id in ^feed_ids, where: [read: false])
|
||||||
|> Enum.map(fn item -> item.id end)
|
|> Enum.map(fn item -> item.id end)
|
||||||
|> Enum.join(",")
|
|> Enum.join(",")
|
||||||
|
|
||||||
|
@ -144,16 +187,21 @@ defmodule FrenzyWeb.FeverController do
|
||||||
|> Map.put(:unread_item_ids, unread)
|
|> Map.put(:unread_item_ids, unread)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp unread(res, _), do: res
|
defp unread(res, _, _), do: res
|
||||||
|
|
||||||
defp saved(res, %{"saved_item_ids" => _}) do
|
defp saved(res, _user, %{"saved_item_ids" => _}) do
|
||||||
res
|
res
|
||||||
|> Map.put(:saved_item_ids, "")
|
|> Map.put(:saved_item_ids, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp saved(res, _), do: res
|
defp saved(res, _, _), do: res
|
||||||
|
|
||||||
|
defp items(res, user, %{"items" => _} = params) do
|
||||||
|
feed_ids =
|
||||||
|
user.groups
|
||||||
|
|> Enum.flat_map(fn g -> g.feeds end)
|
||||||
|
|> Enum.map(fn f -> f.id end)
|
||||||
|
|
||||||
defp items(res, %{"items" => _} = params) do
|
|
||||||
items =
|
items =
|
||||||
cond do
|
cond do
|
||||||
Map.has_key?(params, "with_ids") ->
|
Map.has_key?(params, "with_ids") ->
|
||||||
|
@ -161,8 +209,15 @@ defmodule FrenzyWeb.FeverController do
|
||||||
|> String.split(",")
|
|> String.split(",")
|
||||||
|> Enum.map(fn id ->
|
|> Enum.map(fn id ->
|
||||||
{id, _} = id |> String.trim() |> Integer.parse()
|
{id, _} = id |> String.trim() |> Integer.parse()
|
||||||
Repo.get(Item, id)
|
item = Repo.get(Item, id)
|
||||||
|
|
||||||
|
if item.feed_id in feed_ids do
|
||||||
|
item
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Enum.reject(&is_nil/1)
|
||||||
|
|
||||||
Map.has_key?(params, "since_id") ->
|
Map.has_key?(params, "since_id") ->
|
||||||
since = Repo.get(Item, params["since_id"])
|
since = Repo.get(Item, params["since_id"])
|
||||||
|
@ -170,6 +225,7 @@ defmodule FrenzyWeb.FeverController do
|
||||||
|
|
||||||
Repo.all(
|
Repo.all(
|
||||||
from i in Item,
|
from i in Item,
|
||||||
|
where: i.feed_id in ^feed_ids,
|
||||||
where: i.inserted_at > ^since.inserted_at,
|
where: i.inserted_at > ^since.inserted_at,
|
||||||
order_by: [asc: :id],
|
order_by: [asc: :id],
|
||||||
limit: 50
|
limit: 50
|
||||||
|
@ -181,6 +237,7 @@ defmodule FrenzyWeb.FeverController do
|
||||||
|
|
||||||
Repo.all(
|
Repo.all(
|
||||||
from i in Item,
|
from i in Item,
|
||||||
|
where: i.feed_id in ^feed_ids,
|
||||||
where: i.inserted_at < ^max.inserted_at,
|
where: i.inserted_at < ^max.inserted_at,
|
||||||
order_by: [desc: :id],
|
order_by: [desc: :id],
|
||||||
limit: 50
|
limit: 50
|
||||||
|
@ -201,5 +258,5 @@ defmodule FrenzyWeb.FeverController do
|
||||||
|> Map.put(:total_items, Enum.count(items))
|
|> Map.put(:total_items, Enum.count(items))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp items(res, _), do: res
|
defp items(res, _, _), do: res
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,11 +6,13 @@ defmodule Mix.Tasks.Frenzy.User do
|
||||||
def run(["add"]) do
|
def run(["add"]) do
|
||||||
username = IO.gets("Username: ") |> String.trim()
|
username = IO.gets("Username: ") |> String.trim()
|
||||||
password = IO.gets("Password: ") |> String.trim()
|
password = IO.gets("Password: ") |> String.trim()
|
||||||
|
fever_password = IO.gets("Fever Password: ") |> String.trim()
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
User.registration_changeset(%User{}, %{
|
User.registration_changeset(%User{}, %{
|
||||||
username: username,
|
username: username,
|
||||||
password: password
|
password: password,
|
||||||
|
fever_password: fever_password
|
||||||
})
|
})
|
||||||
|
|
||||||
Mix.Task.run("app.start")
|
Mix.Task.run("app.start")
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Frenzy.Repo.Migrations.UsersAddFeverToken do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add :fever_auth_token, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue