mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 20:18:00 +01:00
primitive sign in
This commit is contained in:
parent
77235013bf
commit
39470dc1ad
21 changed files with 234 additions and 101 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -35,3 +35,6 @@ npm-debug.log
|
|||
# we ignore priv/static. You may want to comment
|
||||
# this depending on your deployment strategy.
|
||||
/priv/static/
|
||||
|
||||
# Mnesia
|
||||
/Mnesia*
|
2
.iex.exs
Normal file
2
.iex.exs
Normal file
|
@ -0,0 +1,2 @@
|
|||
alias Philomena.{Repo, Users.User}
|
||||
import Ecto.Query
|
|
@ -16,7 +16,8 @@ config :philomena,
|
|||
config :philomena, :pow,
|
||||
user: Philomena.Users.User,
|
||||
repo: Philomena.Repo,
|
||||
extensions: [PhilomenaWeb.HaltTotp],
|
||||
web_module: PhilomenaWeb,
|
||||
extensions: [PowResetPassword, PowPersistentSession, PowMultiFactor],
|
||||
controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks
|
||||
|
||||
config :bcrypt_elixir,
|
||||
|
|
|
@ -11,9 +11,10 @@ defmodule Philomena.Application do
|
|||
# Start the Ecto repository
|
||||
Philomena.Repo,
|
||||
# Start the endpoint when the application starts
|
||||
PhilomenaWeb.Endpoint
|
||||
PhilomenaWeb.Endpoint,
|
||||
# Starts a worker by calling: Philomena.Worker.start_link(arg)
|
||||
# {Philomena.Worker, arg},
|
||||
Pow.Store.Backend.MnesiaCache
|
||||
]
|
||||
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
|
|
|
@ -53,12 +53,12 @@ defmodule Philomena.Images.Query do
|
|||
tag_include = %{terms: %{tag_ids: user.watched_tag_ids}}
|
||||
|
||||
{:ok, include_query} =
|
||||
Philomena.Images.Query.user_parser(ctx, user.watched_images_query |> normalize())
|
||||
Philomena.Images.Query.user_parser(ctx, user.watched_images_query_str |> normalize())
|
||||
|
||||
{:ok, exclude_query} =
|
||||
Philomena.Images.Query.user_parser(
|
||||
ctx,
|
||||
user.watched_images_exclude_query |> normalize()
|
||||
user.watched_images_exclude_str |> normalize()
|
||||
)
|
||||
|
||||
should = [tag_include, include_query]
|
||||
|
@ -125,12 +125,12 @@ defmodule Philomena.Images.Query do
|
|||
tag_include = %{terms: %{tag_ids: user.watched_tag_ids}}
|
||||
|
||||
{:ok, include_query} =
|
||||
Philomena.Images.Query.moderator_parser(ctx, user.watched_images_query |> normalize())
|
||||
Philomena.Images.Query.moderator_parser(ctx, user.watched_images_query_str |> normalize())
|
||||
|
||||
{:ok, exclude_query} =
|
||||
Philomena.Images.Query.moderator_parser(
|
||||
ctx,
|
||||
user.watched_images_exclude_query |> normalize()
|
||||
user.watched_images_exclude_str |> normalize()
|
||||
)
|
||||
|
||||
should = [tag_include, include_query]
|
||||
|
|
|
@ -6,6 +6,9 @@ defmodule Philomena.Users.User do
|
|||
use Pow.Ecto.Schema,
|
||||
password_hash_methods: {&Password.hash_pwd_salt/1, &Password.verify_pass/2}
|
||||
|
||||
use Pow.Extension.Ecto.Schema,
|
||||
extensions: [PowResetPassword]
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "users" do
|
||||
|
@ -100,6 +103,7 @@ defmodule Philomena.Users.User do
|
|||
def changeset(user, attrs) do
|
||||
user
|
||||
|> pow_changeset(attrs)
|
||||
|> pow_extension_changeset(attrs)
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
|
|
|
@ -43,7 +43,10 @@ defmodule PhilomenaWeb.Endpoint do
|
|||
signing_salt: "signed cookie",
|
||||
encryption_salt: "authenticated encrypted cookie"
|
||||
|
||||
plug PhilomenaWeb.Plugs.Session, otp_app: :philomena
|
||||
plug Pow.Plug.Session, otp_app: :philomena
|
||||
plug PowPersistentSession.Plug.Cookie, otp_app: :philomena
|
||||
|
||||
plug PhilomenaWeb.Plugs.ReloadUser
|
||||
plug PhilomenaWeb.Plugs.RenderTime
|
||||
plug PhilomenaWeb.Plugs.CurrentFilter
|
||||
plug PhilomenaWeb.Router
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
defmodule PhilomenaWeb.HaltTotp.Phoenix.ControllerCallbacks do
|
||||
use Pow.Extension.Phoenix.ControllerCallbacks.Base
|
||||
alias Pow.Plug
|
||||
import Phoenix.Controller
|
||||
|
||||
def before_respond(Pow.Phoenix.SessionController, :create, {:ok, conn}, _config) do
|
||||
conn
|
||||
|> Plug.current_user()
|
||||
|> halt_totp(conn)
|
||||
end
|
||||
|
||||
defp halt_totp(%{otp_required_for_login: true}, conn) do
|
||||
{:ok, conn} = Plug.clear_authenticated_user(conn)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_flash(:error, "Cannot yet authenticate accounts with TOTP enabled")
|
||||
|> redirect(to: "/")
|
||||
|
||||
{:halt, conn}
|
||||
end
|
||||
|
||||
defp halt_totp(_, conn) do
|
||||
{:ok, conn}
|
||||
end
|
||||
|
||||
def before_process(Pow.Phoenix.RegistrationController, _method, conn, _config) do
|
||||
conn =
|
||||
conn
|
||||
|> put_flash(:error, "Registrations are disabled")
|
||||
|> redirect(to: "/")
|
||||
|
||||
{:halt, conn}
|
||||
end
|
||||
end
|
|
@ -15,7 +15,7 @@ defmodule PhilomenaWeb.Plugs.CurrentFilter do
|
|||
|
||||
filter =
|
||||
if user do
|
||||
user = user |> preload(:current_filter)
|
||||
user = user |> Repo.preload(:current_filter)
|
||||
user.current_filter
|
||||
else
|
||||
filter_id = conn |> get_session(:filter_id)
|
||||
|
|
21
lib/philomena_web/plugs/reload_user.ex
Normal file
21
lib/philomena_web/plugs/reload_user.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule PhilomenaWeb.Plugs.ReloadUser do
|
||||
alias Pow.Plug
|
||||
alias Philomena.Users.User
|
||||
alias Philomena.Repo
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
def call(conn, _opts) do
|
||||
config = Plug.fetch_config(conn)
|
||||
|
||||
case Plug.current_user(conn, config) do
|
||||
nil ->
|
||||
conn
|
||||
|
||||
user ->
|
||||
reloaded_user = Repo.get!(User, user.id)
|
||||
|
||||
Plug.assign_current_user(conn, reloaded_user, config)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
defmodule PhilomenaWeb.Plugs.Session do
|
||||
use Pow.Plug.Base
|
||||
|
||||
alias Plug.Conn
|
||||
alias Philomena.{Repo, Users.User}
|
||||
|
||||
@session_key :philomena_session
|
||||
|
||||
def fetch(conn, _config) do
|
||||
conn = Conn.fetch_session(conn)
|
||||
user = Conn.get_session(conn, @session_key)
|
||||
|
||||
conn
|
||||
|> maybe_load_user(user)
|
||||
end
|
||||
|
||||
def create(conn, user, _config) do
|
||||
value = session_value(user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> Conn.fetch_session()
|
||||
|> Conn.put_session(@session_key, value)
|
||||
|
||||
{conn, user}
|
||||
end
|
||||
|
||||
def delete(conn, _config) do
|
||||
conn
|
||||
|> Conn.fetch_session()
|
||||
|> Conn.delete_session(@session_key)
|
||||
end
|
||||
|
||||
defp maybe_load_user(conn, {:ok, user}) do
|
||||
with {:ok, [user_id, hash]} <- Jason.decode(user),
|
||||
%User{} = user <- Repo.get(User, user_id),
|
||||
true <- SecureCompare.compare(hash, binary_part(user.encrypted_password, 0, 25)) do
|
||||
{conn, user}
|
||||
else
|
||||
_ ->
|
||||
{conn, nil}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_load_user(conn, _) do
|
||||
{conn, nil}
|
||||
end
|
||||
|
||||
defp session_value(user) do
|
||||
Jason.encode([user.id, binary_part(user.encrypted_password, 0, 25)])
|
||||
end
|
||||
end
|
|
@ -16,11 +16,11 @@ defmodule PhilomenaWeb.Router do
|
|||
plug :accepts, ["json"]
|
||||
end
|
||||
|
||||
#scope "/" do
|
||||
# pipe_through :browser
|
||||
#
|
||||
# pow_routes()
|
||||
#end
|
||||
scope "/" do
|
||||
pipe_through :browser
|
||||
|
||||
pow_routes()
|
||||
end
|
||||
|
||||
scope "/", PhilomenaWeb do
|
||||
pipe_through :browser
|
||||
|
|
30
lib/philomena_web/templates/pow/registration/edit.html.eex
Normal file
30
lib/philomena_web/templates/pow/registration/edit.html.eex
Normal file
|
@ -0,0 +1,30 @@
|
|||
<h1>Edit profile</h1>
|
||||
|
||||
<%= form_for @changeset, @action, [as: :user], fn f -> %>
|
||||
<%= if @changeset.action do %>
|
||||
<div class="alert alert-danger">
|
||||
<p>Oops, something went wrong! Please check the errors below.</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= label f, :current_password %>
|
||||
<%= password_input f, :current_password %>
|
||||
<%= error_tag f, :current_password %>
|
||||
|
||||
<%= label f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
<%= text_input f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
<%= error_tag f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
|
||||
<%= label f, :password %>
|
||||
<%= password_input f, :password %>
|
||||
<%= error_tag f, :password %>
|
||||
|
||||
<%= label f, :confirm_password %>
|
||||
<%= password_input f, :confirm_password %>
|
||||
<%= error_tag f, :confirm_password %>
|
||||
|
||||
<div>
|
||||
<%= submit "Update" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
28
lib/philomena_web/templates/pow/registration/new.html.eex
Normal file
28
lib/philomena_web/templates/pow/registration/new.html.eex
Normal file
|
@ -0,0 +1,28 @@
|
|||
<h1>Register</h1>
|
||||
|
||||
<%= form_for @changeset, @action, [as: :user], fn f -> %>
|
||||
<%= if @changeset.action do %>
|
||||
<div class="alert alert-danger">
|
||||
<p>Oops, something went wrong! Please check the errors below.</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= label f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
<%= text_input f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
<%= error_tag f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
|
||||
<%= label f, :password %>
|
||||
<%= password_input f, :password %>
|
||||
<%= error_tag f, :password %>
|
||||
|
||||
<%= label f, :confirm_password %>
|
||||
<%= password_input f, :confirm_password %>
|
||||
<%= error_tag f, :confirm_password %>
|
||||
|
||||
<div>
|
||||
<%= submit "Register" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
<span><%= link "Sign in", to: Routes.pow_session_path(@conn, :new) %></span>
|
24
lib/philomena_web/templates/pow/session/new.html.eex
Normal file
24
lib/philomena_web/templates/pow/session/new.html.eex
Normal file
|
@ -0,0 +1,24 @@
|
|||
<h1>Sign in</h1>
|
||||
|
||||
<%= form_for @changeset, @action, [as: :user], fn f -> %>
|
||||
<%= if @changeset.action do %>
|
||||
<div class="alert alert-danger">
|
||||
<p>Oops, something went wrong! Please check the errors below.</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= label f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
<%= text_input f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
<%= error_tag f, Pow.Ecto.Schema.user_id_field(@changeset) %>
|
||||
|
||||
<%= label f, :password %>
|
||||
<%= password_input f, :password %>
|
||||
<%= error_tag f, :password %>
|
||||
|
||||
<div>
|
||||
<%= submit "Sign in" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
<span><%= link "Register", to: Routes.pow_registration_path(@conn, :new) %></span>
|
3
lib/philomena_web/views/pow/registration_view.ex
Normal file
3
lib/philomena_web/views/pow/registration_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule PhilomenaWeb.Pow.RegistrationView do
|
||||
use PhilomenaWeb, :view
|
||||
end
|
3
lib/philomena_web/views/pow/session_view.ex
Normal file
3
lib/philomena_web/views/pow/session_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule PhilomenaWeb.Pow.SessionView do
|
||||
use PhilomenaWeb, :view
|
||||
end
|
69
lib/pow_multi_factor/phoenix/controller_callbacks.ex
Normal file
69
lib/pow_multi_factor/phoenix/controller_callbacks.ex
Normal file
|
@ -0,0 +1,69 @@
|
|||
defmodule PowMultiFactor.Phoenix.ControllerCallbacks do
|
||||
@moduledoc """
|
||||
Controller callback logic for multi-factor authentication.
|
||||
|
||||
### 2FA code not submitted
|
||||
|
||||
Triggers on `Pow.Phoenix.SessionController.create/2`.
|
||||
|
||||
When a user with 2FA enabled attempts to sign in without submitting their
|
||||
TOTP token, the session will be cleared, and the user redirected back to
|
||||
`Pow.Phoenix.Routes.session_path/1`.
|
||||
|
||||
### User updates account
|
||||
|
||||
Triggers on `Pow.Phoenix.RegistrationController.update/2`
|
||||
|
||||
When a user changes their account settings, they are required to confirm a
|
||||
current 2FA token.
|
||||
|
||||
See `PowMultiFactor.Ecto.Schema` for more.
|
||||
"""
|
||||
|
||||
use Pow.Extension.Phoenix.ControllerCallbacks.Base
|
||||
|
||||
alias Pow.Plug
|
||||
alias PowMultiFactor.Plug, as: PowMultiFactorPlug
|
||||
|
||||
def before_respond(Pow.Phoenix.SessionController, :create, {:ok, conn}, _config) do
|
||||
return_path = routes(conn).session_path(conn, :new)
|
||||
|
||||
clear_unauthorized(conn, {:ok, conn}, return_path)
|
||||
end
|
||||
|
||||
def before_respond(Pow.Phoenix.RegistrationController, :update, {:ok, user, conn}, _config) do
|
||||
return_path = routes(conn).registration_path(conn, :edit)
|
||||
|
||||
halt_unauthorized(conn, {:ok, user, conn}, return_path)
|
||||
end
|
||||
|
||||
defp clear_unauthorized(conn, success_response, return_path) do
|
||||
case PowMultiFactorPlug.mfa_unauthorized?(conn) do
|
||||
true -> clear_auth(conn) |> go_back(return_path)
|
||||
false -> success_response
|
||||
end
|
||||
end
|
||||
|
||||
defp halt_unauthorized(conn, success_response, return_path) do
|
||||
case PowMultiFactorPlug.mfa_unauthorized?(conn) do
|
||||
true -> go_back(conn, return_path)
|
||||
false -> success_response
|
||||
end
|
||||
end
|
||||
|
||||
def clear_auth(conn) do
|
||||
{:ok, conn} = Plug.clear_authenticated_user(conn)
|
||||
|
||||
conn
|
||||
end
|
||||
|
||||
defp go_back(conn, return_path) do
|
||||
error = extension_messages(conn).invalid_multi_factor(conn)
|
||||
conn =
|
||||
conn
|
||||
|> Phoenix.Controller.put_flash(:error, error)
|
||||
|> Phoenix.Controller.redirect(to: return_path)
|
||||
|
||||
{:halt, conn}
|
||||
end
|
||||
end
|
26
lib/pow_multi_factor/plug.ex
Normal file
26
lib/pow_multi_factor/plug.ex
Normal file
|
@ -0,0 +1,26 @@
|
|||
defmodule PowMultiFactor.Plug do
|
||||
@moduledoc """
|
||||
Plug helper methods.
|
||||
"""
|
||||
|
||||
alias Pow.Plug
|
||||
#alias PowMultiFactor.Ecto.Context
|
||||
|
||||
def mfa_unauthorized?(conn) do
|
||||
user = Plug.current_user(conn)
|
||||
|
||||
if user.otp_required_for_login do
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
#defp otp_secret(user) do
|
||||
|
||||
#end
|
||||
|
||||
#defp otp_shared_key do
|
||||
# Application.get_env
|
||||
#end
|
||||
end
|
3
mix.exs
3
mix.exs
|
@ -52,7 +52,8 @@ defmodule Philomena.MixProject do
|
|||
{:elastix, "~> 0.7.1"},
|
||||
{:nimble_parsec, "~> 0.5.1"},
|
||||
{:canary, "~> 1.1.1"},
|
||||
{:scrivener_ecto, "~> 2.0"}
|
||||
{:scrivener_ecto, "~> 2.0"},
|
||||
{:elixir2fa, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
1
mix.lock
1
mix.lock
|
@ -13,6 +13,7 @@
|
|||
"ecto_network": {:hex, :ecto_network, "1.1.0", "7062004b9324ff13e50c02dab84877f8a55e06db9eabbf2d04bda21da6fc6e8a", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.2.0", "751cea597e8deb616084894dd75cbabfdbe7255ff01e8c058ca13f0353a3921b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"elastix": {:hex, :elastix, "0.7.1", "8e199a764a0bc018e0a97afeea950a8069b988867d87f8d25ae121d8b3288612", [:mix], [{:httpoison, "~> 1.4", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}, {:retry, "~> 0.8", [hex: :retry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"elixir2fa": {:hex, :elixir2fa, "0.1.0", "4585154695ad13a01c17c46e61b0884b0ce1569ebcc667d1d09cd1cbbb4f4ba8", [:mix], [], "hexpm"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm"},
|
||||
"file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"},
|
||||
"gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
|
||||
|
|
Loading…
Reference in a new issue