Add authentication for web UI
This commit is contained in:
parent
762675b38c
commit
74a82967dd
|
@ -32,9 +32,9 @@ defmodule Frenzy.UpdateFeeds do
|
||||||
|
|
||||||
defp schedule_update() do
|
defp schedule_update() do
|
||||||
# 15 minutes
|
# 15 minutes
|
||||||
# Process.send_after(self(), :update_feeds, 15 * 60 * 1000)
|
Process.send_after(self(), :update_feeds, 15 * 60 * 1000)
|
||||||
# 1 minutes
|
# 1 minutes
|
||||||
Process.send_after(self(), :update_feeds, 60 * 1000)
|
# Process.send_after(self(), :update_feeds, 60 * 1000)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp update_feeds() do
|
defp update_feeds() do
|
||||||
|
|
|
@ -5,8 +5,28 @@ defmodule FrenzyWeb.FeedController do
|
||||||
alias FrenzyWeb.Endpoint
|
alias FrenzyWeb.Endpoint
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
plug :user_owns_feed
|
||||||
|
|
||||||
|
defp user_owns_feed(%Plug.Conn{path_params: %{"id" => id}} = conn, _opts) do
|
||||||
|
user = conn.assigns[:user]
|
||||||
|
|
||||||
|
feed = Repo.get(Feed, id)
|
||||||
|
|
||||||
|
if Enum.any?(user.groups, fn g -> g.id == feed.group_id end) do
|
||||||
|
conn
|
||||||
|
|> assign(:feed, feed)
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_flash(:error, "You do not have permission to access that resource.")
|
||||||
|
|> redirect(to: Routes.group_path(Endpoint, :index))
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp user_owns_feed(conn, _opts), do: conn
|
||||||
|
|
||||||
def show(conn, %{"id" => id}) do
|
def show(conn, %{"id" => id}) do
|
||||||
feed = Repo.get(Feed, id) |> Repo.preload(:filter)
|
feed = conn.assigns[:feed]
|
||||||
items = Repo.all(from Item, where: [feed_id: ^id, tombstone: false], order_by: [desc: :date])
|
items = Repo.all(from Item, where: [feed_id: ^id, tombstone: false], order_by: [desc: :date])
|
||||||
|
|
||||||
render(conn, "show.html", %{
|
render(conn, "show.html", %{
|
||||||
|
@ -32,28 +52,28 @@ defmodule FrenzyWeb.FeedController do
|
||||||
redirect(conn, to: Routes.group_path(Endpoint, :show, group_id))
|
redirect(conn, to: Routes.group_path(Endpoint, :show, group_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(conn, %{"id" => id}) do
|
def delete(conn, _params) do
|
||||||
feed = Repo.get(Feed, id)
|
feed = conn.assigns[:feed]
|
||||||
{:ok, _} = Repo.delete(feed)
|
{:ok, _} = Repo.delete(feed)
|
||||||
redirect(conn, to: Routes.group_path(Endpoint, :show, feed.group_id))
|
redirect(conn, to: Routes.group_path(Endpoint, :show, feed.group_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def enable_filter(conn, %{"id" => id}) do
|
def enable_filter(conn, _params) do
|
||||||
feed = Repo.get(Feed, id) |> Repo.preload(:filter)
|
feed = conn.assigns[:feed] |> Repo.preload(:filter)
|
||||||
changeset = Feed.changeset(feed, %{filter_enabled: true})
|
changeset = Feed.changeset(feed, %{filter_enabled: true})
|
||||||
Repo.update(changeset)
|
Repo.update(changeset)
|
||||||
redirect(conn, to: Routes.feed_path(Endpoint, :show, id))
|
redirect(conn, to: Routes.feed_path(Endpoint, :show, feed.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable_filter(conn, %{"id" => id}) do
|
def disable_filter(conn, _params) do
|
||||||
feed = Repo.get(Feed, id) |> Repo.preload(:filter)
|
feed = conn.assigns[:feed] |> Repo.preload(:filter)
|
||||||
changeset = Feed.changeset(feed, %{filter_enabled: false})
|
changeset = Feed.changeset(feed, %{filter_enabled: false})
|
||||||
Repo.update(changeset)
|
Repo.update(changeset)
|
||||||
redirect(conn, to: Routes.feed_path(Endpoint, :show, id))
|
redirect(conn, to: Routes.feed_path(Endpoint, :show, feed.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def refresh(conn, %{"id" => id}) do
|
def refresh(conn, _params) do
|
||||||
feed = Repo.get(Feed, id) |> Repo.preload(:filter)
|
feed = conn.assgins[: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
|
||||||
|
|
|
@ -3,20 +3,39 @@ defmodule FrenzyWeb.GroupController do
|
||||||
alias Frenzy.{Repo, Group, Feed}
|
alias Frenzy.{Repo, Group, Feed}
|
||||||
alias FrenzyWeb.Router.Helpers, as: Routes
|
alias FrenzyWeb.Router.Helpers, as: Routes
|
||||||
alias FrenzyWeb.Endpoint
|
alias FrenzyWeb.Endpoint
|
||||||
import Ecto.Query
|
|
||||||
|
plug :user_owns_group
|
||||||
|
|
||||||
|
defp user_owns_group(%Plug.Conn{path_params: %{"id" => id}} = conn, _opts) do
|
||||||
|
user = conn.assigns[:user]
|
||||||
|
|
||||||
|
group = Repo.get(Group, id)
|
||||||
|
|
||||||
|
if Enum.any?(user.groups, fn g -> g.id == group.id end) do
|
||||||
|
conn
|
||||||
|
|> assign(:group, group)
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_flash(:error, "You do not have permission to acess that resource.")
|
||||||
|
|> redirect(to: Routes.group_path(Endpoint, :index))
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp user_owns_group(conn, _opts), do: conn
|
||||||
|
|
||||||
def index(conn, _params) do
|
def index(conn, _params) do
|
||||||
groups = Repo.all(from group in Group, preload: [:feeds])
|
groups = conn.assigns[:user].groups
|
||||||
render(conn, "index.html", groups: groups)
|
render(conn, "index.html", groups: groups)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show(conn, %{"id" => id}) do
|
def show(conn, _params) do
|
||||||
group = Repo.get(Group, id) |> Repo.preload(:feeds)
|
group = conn.assigns[:group] |> Repo.preload(:feeds)
|
||||||
|
|
||||||
create_feed_changeset =
|
create_feed_changeset =
|
||||||
Feed.changeset(
|
Feed.changeset(
|
||||||
%Feed{
|
%Feed{
|
||||||
group_id: id
|
group_id: group.id
|
||||||
},
|
},
|
||||||
%{}
|
%{}
|
||||||
)
|
)
|
||||||
|
@ -34,15 +53,21 @@ defmodule FrenzyWeb.GroupController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(conn, %{"group" => %{"title" => title}}) do
|
def create(conn, %{"group" => %{"title" => title}}) do
|
||||||
changeset = Group.changeset(%Group{title: title}, %{})
|
user = conn.assigns[:user]
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
Ecto.build_assoc(user, :groups, %{
|
||||||
|
title: title
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, group} = Repo.insert(changeset)
|
{:ok, group} = Repo.insert(changeset)
|
||||||
|
|
||||||
redirect(conn, to: Routes.group_path(Endpoint, :index))
|
redirect(conn, to: Routes.group_path(Endpoint, :show, group.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(conn, %{"id" => id}) do
|
def delete(conn, _params) do
|
||||||
group = Repo.get(Group, id)
|
group = conn.assigns[:group]
|
||||||
|
|
||||||
{:ok, _} = Repo.delete(group)
|
{:ok, _} = Repo.delete(group)
|
||||||
redirect(conn, to: Routes.group_path(Endpoint, :index))
|
redirect(conn, to: Routes.group_path(Endpoint, :index))
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,33 +4,60 @@ defmodule FrenzyWeb.ItemController do
|
||||||
alias FrenzyWeb.Router.Helpers, as: Routes
|
alias FrenzyWeb.Router.Helpers, as: Routes
|
||||||
alias FrenzyWeb.Endpoint
|
alias FrenzyWeb.Endpoint
|
||||||
|
|
||||||
def show(conn, %{"id" => id}) do
|
plug :user_owns_item
|
||||||
|
|
||||||
|
defp user_owns_item(%Plug.Conn{path_params: %{"id" => id}} = conn, _opts) do
|
||||||
|
user = conn.assigns[:user]
|
||||||
|
|
||||||
item = Repo.get(Item, id)
|
item = Repo.get(Item, id)
|
||||||
feed = Repo.get(Feed, item.feed_id)
|
|
||||||
|
feeds = Enum.flat_map(user.groups, fn g -> g.feeds end)
|
||||||
|
|
||||||
|
if Enum.any?(feeds, fn f -> f.id == item.feed_id end) do
|
||||||
|
conn
|
||||||
|
|> assign(:item, item)
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_flash(:error, "You do not have permission to access that resource.")
|
||||||
|
|> redirect(to: Routes.group_path(Endpoint, :index))
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp user_owns_item(conn, _opts), do: conn
|
||||||
|
|
||||||
|
def show(conn, _params) do
|
||||||
|
item = conn.assigns[:item] |> Repo.preload(:feed)
|
||||||
|
|
||||||
render(conn, "show.html", %{
|
render(conn, "show.html", %{
|
||||||
item: item,
|
item: item,
|
||||||
feed: feed
|
feed: item.feed
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def read(conn, %{"id" => id}) do
|
def read(conn, _params) do
|
||||||
item = Repo.get(Item, id) |> Repo.preload(:feed)
|
item = conn.assigns[:item] |> Repo.preload(:feed)
|
||||||
changeset = Item.changeset(item, %{
|
|
||||||
read: true,
|
changeset =
|
||||||
read_date: Timex.now
|
Item.changeset(item, %{
|
||||||
|
read: true,
|
||||||
|
read_date: Timex.now()
|
||||||
})
|
})
|
||||||
|
|
||||||
Repo.update(changeset)
|
Repo.update(changeset)
|
||||||
redirect(conn, to: Routes.item_path(Endpoint, :show, id))
|
redirect(conn, to: Routes.item_path(Endpoint, :show, item.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def unread(conn, %{"id" => id}) do
|
def unread(conn, _params) do
|
||||||
item = Repo.get(Item, id) |> Repo.preload(:feed)
|
item = conn.assigns[:item] |> Repo.preload(:feed)
|
||||||
changeset = Item.changeset(item, %{
|
|
||||||
read: false,
|
changeset =
|
||||||
read_date: nil
|
Item.changeset(item, %{
|
||||||
|
read: false,
|
||||||
|
read_date: nil
|
||||||
})
|
})
|
||||||
Repo.update(changeset)
|
|
||||||
redirect(conn, to: Routes.item_path(Endpoint, :show, id))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
Repo.update(changeset)
|
||||||
|
redirect(conn, to: Routes.item_path(Endpoint, :show, item.id))
|
||||||
|
end
|
||||||
end
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
defmodule FrenzyWeb.LoginController do
|
||||||
|
use FrenzyWeb, :controller
|
||||||
|
alias Frenzy.{Repo, User}
|
||||||
|
alias FrenzyWeb.Router.Helpers, as: Routes
|
||||||
|
alias FrenzyWeb.Endpoint
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
def login(conn, _params) do
|
||||||
|
render(conn, "login.html")
|
||||||
|
end
|
||||||
|
|
||||||
|
@error_message "Invalid username or password"
|
||||||
|
|
||||||
|
def login_post(conn, %{"username" => username, "password" => password}) do
|
||||||
|
user = Repo.get_by(User, username: username)
|
||||||
|
|
||||||
|
case Bcrypt.check_pass(user, password) do
|
||||||
|
{:ok, user} ->
|
||||||
|
user_token = Phoenix.Token.sign(Endpoint, "user token", user.id)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_session(:user_token, user_token)
|
||||||
|
|> redirect(to: Routes.group_path(Endpoint, :index))
|
||||||
|
|
||||||
|
{:error, _reason} ->
|
||||||
|
conn
|
||||||
|
|> put_flash(:error, @error_message)
|
||||||
|
|> redirect(to: Routes.login_path(Endpoint, :login))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
defmodule FrenzyWeb.Plug.Authenticate do
|
||||||
|
import Plug.Conn
|
||||||
|
alias Frenzy.{Repo, User}
|
||||||
|
alias FrenzyWeb.Router.Helpers, as: Routes
|
||||||
|
alias FrenzyWeb.Endpoint
|
||||||
|
|
||||||
|
def init(opts), do: opts
|
||||||
|
|
||||||
|
def call(conn, _opts) do
|
||||||
|
user_token = get_session(conn, :user_token)
|
||||||
|
|
||||||
|
case Phoenix.Token.verify(Endpoint, "user token", user_token, max_age: 24 * 60 * 60) do
|
||||||
|
{:error, _reason} ->
|
||||||
|
conn
|
||||||
|
|> Phoenix.Controller.redirect(to: Routes.login_path(Endpoint, :login))
|
||||||
|
|> halt()
|
||||||
|
|
||||||
|
{:ok, user_id} ->
|
||||||
|
case Repo.get(User, user_id) do
|
||||||
|
nil ->
|
||||||
|
conn
|
||||||
|
|> Phoenix.Controller.redirect(to: Routes.login_path(Endpoint, :login))
|
||||||
|
|> halt()
|
||||||
|
|
||||||
|
user ->
|
||||||
|
user = Repo.preload(user, groups: [:feeds])
|
||||||
|
assign(conn, :user, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,7 +7,15 @@ defmodule FrenzyWeb.Router do
|
||||||
plug :fetch_flash
|
plug :fetch_flash
|
||||||
plug :protect_from_forgery
|
plug :protect_from_forgery
|
||||||
plug :put_secure_browser_headers
|
plug :put_secure_browser_headers
|
||||||
plug BasicAuth, use_config: {:frenzy, :auth}
|
end
|
||||||
|
|
||||||
|
pipeline :authenticate do
|
||||||
|
# plug :accepts, ["html"]
|
||||||
|
# plug :fetch_session
|
||||||
|
# plug :fetch_flash
|
||||||
|
# plug :protect_from_forgery
|
||||||
|
# plug :put_secure_browser_headers
|
||||||
|
plug FrenzyWeb.Plug.Authenticate
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :api do
|
pipeline :api do
|
||||||
|
@ -17,6 +25,14 @@ defmodule FrenzyWeb.Router do
|
||||||
scope "/", FrenzyWeb do
|
scope "/", FrenzyWeb do
|
||||||
pipe_through :browser
|
pipe_through :browser
|
||||||
|
|
||||||
|
get "/login", LoginController, :login
|
||||||
|
post "/login", LoginController, :login_post
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", FrenzyWeb do
|
||||||
|
pipe_through :browser
|
||||||
|
pipe_through :authenticate
|
||||||
|
|
||||||
get "/", GroupController, :index
|
get "/", GroupController, :index
|
||||||
resources "/groups", GroupController, except: [:edit, :update]
|
resources "/groups", GroupController, except: [:edit, :update]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<% IO.inspect(get_flash(@conn, :error)) %>
|
||||||
|
|
||||||
|
<%= form_tag Routes.login_path(@conn, :login_post), method: :post do %>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input type="text" name="username" id="username">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" name="password" id="password">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<%= submit "Log In" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule FrenzyWeb.LoginView do
|
||||||
|
use FrenzyWeb, :view
|
||||||
|
end
|
1
mix.exs
1
mix.exs
|
@ -47,7 +47,6 @@ defmodule Frenzy.MixProject do
|
||||||
{:fiet, git: "https://github.com/shadowfacts/fiet.git", branch: "master"},
|
{:fiet, git: "https://github.com/shadowfacts/fiet.git", branch: "master"},
|
||||||
{:timex, "~> 3.0"},
|
{:timex, "~> 3.0"},
|
||||||
{:readability, git: "https://github.com/shadowfacts/readability.git", branch: "master"},
|
{:readability, git: "https://github.com/shadowfacts/readability.git", branch: "master"},
|
||||||
{:basic_auth, "~> 2.2.2"},
|
|
||||||
{:bcrypt_elixir, "~> 2.0"}
|
{:bcrypt_elixir, "~> 2.0"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue