fix file uploads, serve them properly, put behind auth wall, decrypt secret message in frontend

This commit is contained in:
2022-02-22 00:16:51 -05:00
parent cac3757723
commit 2ac596b8c8
20 changed files with 420 additions and 148 deletions

View File

@@ -8,6 +8,8 @@ defmodule EntenduWeb.AuthController do
plug Ueberauth
alias Entendu.UserFromAuth
alias EntenduWeb.LinkView
alias Entendu.EncryptedLink
def delete(conn, _params) do
conn
@@ -23,27 +25,32 @@ defmodule EntenduWeb.AuthController do
end
def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
# TODO: turn this into plug that only proceeds if current_link session var exists
%{ id: link_id, recipient: recipient } = get_session(conn, :current_link)
link = get_session(conn, :intended_link)
with {:ok, user} <- UserFromAuth.find_or_create(auth),
true <- UserFromAuth.can_access?(recipient, user.emails) do
# TODO: send over encrypted data that the frontend can decrypt
conn
|> put_session(:current_user, user)
|> configure_session(renew: true)
|> redirect(to: "/just/for/you/#{link_id}")
with %{id: link_id, recipient: recipient} <- link,
{:ok, user} <- UserFromAuth.find_or_create(auth),
true <- UserFromAuth.can_access?(recipient, user.emails) do
# TODO: send over encrypted data that the frontend can decrypt
conn
|> put_session(:current_user, user)
|> configure_session(renew: true)
|> redirect(to: "/just/for/you/#{link_id}")
else
nil ->
conn
|> put_flash(:error, "Could not find link to authenticate against")
|> redirect(to: "/just/for/you/")
false ->
conn
|> put_flash(:error, "#{recipient} was not found in your list of verified emails")
|> redirect(to: "/just/for/you/#{link_id}")
|> put_flash(:error, "#{link.recipient} was not found in your list of verified emails")
|> redirect(to: "/just/for/you/#{link.id}")
{:error, reason} ->
conn
|> put_flash(:error, reason)
|> redirect(to: "/just/for/you/#{link_id}")
|> redirect(to: "/just/for/you/#{link.id}")
end
end
end

View File

@@ -0,0 +1,9 @@
defmodule EntenduWeb.FileNotFoundController do
use EntenduWeb, :controller
def index(conn, _params) do
conn
|> put_status(404)
|> text("File Not Found")
end
end

View File

@@ -9,6 +9,11 @@ defmodule EntenduWeb.LinkController do
alias Entendu.Links
alias Links.Link
alias EntenduWeb.FallbackController
alias Entendu.EncryptedLink
alias Entendu.UserFromAuth
alias EntenduWeb.Plugs.AuthorizeLink
plug AuthorizeLink when action in [:text, :file]
action_fallback(FallbackController)
@@ -17,12 +22,9 @@ defmodule EntenduWeb.LinkController do
end
def just(conn, params) do
with {:ok, %Link{} = link} <- Links.create_link(params) do
with {:ok, %{link_with_file: %Link{} = link}} <- Links.create_link(params) do
conn
|> render("show_authorized.json", %{link: link})
else
test ->
IO.inspect(test)
end
end
@@ -32,7 +34,7 @@ defmodule EntenduWeb.LinkController do
def for(conn, %{"link_id" => link_id, "recipient" => recipient, "service" => service}) do
with %Link{} = link <- Links.get_link(link_id),
Links.update_link(link, %{ recipient: recipient, service: service}) do
Links.update_link(link, %{recipient: recipient, service: service}) do
conn
|> render("show_authorized.json", %{link: link})
end
@@ -42,11 +44,29 @@ defmodule EntenduWeb.LinkController do
render(conn, "you.html")
end
def auth_page(conn, %{ "id" => link_id}) do
def auth_page(conn, %{"id" => link_id}) do
with %Link{service: service, recipient: recipient} = link <- Links.get_link(link_id) do
conn
|> put_session(:current_link, link)
|> render("auth.html", %{ service: service, recipient: recipient })
|> put_session(:intended_link, link)
|> render("auth.html", %{intended_link: %{service: service, recipient: recipient}})
end
end
def text(conn, %{"id" => link_id}) do
with user = get_session(conn, :current_user),
%Link{recipient: recipient} = link <- Links.get_link(link_id),
true <- UserFromAuth.can_access?(recipient, user.emails) do
path = EncryptedLink.url({link.text_content, link})
send_file(conn, 200, path)
end
end
def file(conn, %{"id" => link_id}) do
with user = get_session(conn, :current_user),
%Link{recipient: recipient} = link <- Links.get_link(link_id),
true <- UserFromAuth.can_access?(recipient, user.emails) do
path = EncryptedLink.url({link.file_content, link})
send_file(conn, 200, path)
end
end
end

View File

@@ -0,0 +1,70 @@
defmodule EntenduWeb.Plugs.AuthorizeLink do
import Plug.Conn
use EntenduWeb, :controller
alias Entendu.Repo
alias Entendu.UserFromAuth
alias Entendu.Links
alias Entendu.Links.Link
alias EntenduWeb.FallbackController
alias EntenduWeb.ErrorView
def init(_params) do
end
def call(conn, params) do
%{params: %{"path" => [_, link_id, _]}} = conn
user = get_session(conn, :current_user)
if !user do
conn
|> put_status(403)
|> put_view(EntenduWeb.ErrorView)
|> render("error_code.json", message: "Unauthorized", code: 403)
|> halt
else
with {:ok, user} <- get_user_from_path(conn),
%Link{recipient: recipient} = link <- Links.get_link(link_id),
true <- UserFromAuth.can_access?(recipient, user.emails) do
conn
|> assign(:link, link)
else
nil ->
conn
|> put_status(404)
|> put_view(EntenduWeb.ErrorView)
|> render("error_code.json", message: "Link could not be found", code: 404)
|> halt
false ->
conn
|> put_status(403)
|> put_view(EntenduWeb.ErrorView)
|> render("error_code.json", message: "Unauthorized", code: 403)
|> halt
{:error, reason} ->
conn
|> put_status(422)
|> put_view(EntenduWeb.ErrorView)
|> render("error_code.json", message: reason, code: 422)
|> halt
end
end
end
defp get_user_from_path(%{params: %{"path" => [_, link_id, _]}} = conn) do
get_session(conn, :current_user)
|> get_user_from_path()
end
defp get_user_from_path(nil) do
{:error, "User not authenticated"}
end
defp get_user_from_path(%{id: _, name: _, emails: _} = user) do
{:ok, user}
end
defp get_user_from_path(_), do: {:error, "Link does not exist"}
end

View File

@@ -1,6 +1,8 @@
defmodule EntenduWeb.Router do
use EntenduWeb, :router
alias EntenduWeb.Plugs.AuthorizeLink
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
@@ -14,6 +16,11 @@ defmodule EntenduWeb.Router do
plug :accepts, ["json"]
end
pipeline :authorized_links do
plug AuthorizeLink
plug Plug.Static, at: "/uploads", from: Path.expand('./uploads'), gzip: false
end
scope "/", EntenduWeb do
pipe_through :browser
@@ -24,6 +31,8 @@ defmodule EntenduWeb.Router do
post "/just/for", LinkController, :for
get "/just/for/you", LinkController, :you_page
get "/just/for/you/:id", LinkController, :auth_page
get "/links/:id/text", LinkController, :text
get "/links/:id/file", LinkController, :file
end
scope "/auth", EntenduWeb do
@@ -34,6 +43,11 @@ defmodule EntenduWeb.Router do
delete "/logout", AuthController, :delete
end
scope "/uploads", EntenduWeb do
pipe_through [:browser, :authorized_links]
get "/*path", FileNotFoundController, :index
end
# Other scopes may use custom stacks.
# scope "/api", EntenduWeb do
# pipe_through :api

View File

@@ -1 +1,7 @@
<%= react_component("Components.AuthPage", %{ csrf: Plug.CSRFProtection.get_csrf_token(), service: @service, recipient: @recipient, user: @current_user }) %>
<%= react_component("Components.AuthPage", %{
csrf: Plug.CSRFProtection.get_csrf_token(),
service: @intended_link.service,
recipient: @intended_link.recipient,
user: current_user(@conn),
link: current_link(@conn)
}) %>

View File

@@ -31,14 +31,14 @@ defmodule Entendu.EncryptedLink do
# end
# Override the persisted filenames:
# def filename(version, _) do
# version
# end
def filename(_version, {_file, %{filename: filename}}) do
if filename, do: filename, else: "text"
end
# Override the storage directory:
# def storage_dir(version, {file, scope}) do
# "uploads/user/avatars/#{scope.id}"
# end
def storage_dir(version, {_file, scope}) do
"uploads/links/#{scope.id}"
end
# Provide a default URL if there hasn't been a file uploaded
# def default_url(version, scope) do

View File

@@ -13,4 +13,13 @@ defmodule EntenduWeb.ErrorView do
def template_not_found(template, _assigns) do
Phoenix.Controller.status_message_from_template(template)
end
def render("error_code.json", %{message: message} = params) do
code = Map.get(params, :code, "")
%{
message: message,
code: code
}
end
end