analytics/lib/plausible_web/controllers/auth_controller.ex

219 lines
8.3 KiB
Elixir
Raw Normal View History

2019-09-02 14:29:19 +03:00
defmodule PlausibleWeb.AuthController do
use PlausibleWeb, :controller
use Plausible.Repo
alias Plausible.Auth
require Logger
plug PlausibleWeb.RequireLoggedOutPlug when action in [:register_form, :register, :login_form, :login]
plug PlausibleWeb.RequireAccountPlug when action in [:user_settings, :save_settings, :delete_me, :password_form, :set_password]
def register_form(conn, _params) do
changeset = Plausible.Auth.User.changeset(%Plausible.Auth.User{})
render(conn, "register_form.html", changeset: changeset, layout: {PlausibleWeb.LayoutView, "focus.html"})
end
def register(conn, %{"user" => params}) do
user = Plausible.Auth.User.changeset(%Plausible.Auth.User{}, params)
case Ecto.Changeset.apply_action(user, :insert) do
{:ok, user} ->
token = Auth.Token.sign_activation(user.name, user.email)
url = PlausibleWeb.Endpoint.clean_url() <> "/claim-activation?token=#{token}"
Logger.info(url)
email_template = PlausibleWeb.Email.activation_email(user, url)
Support for docker based self-hosting (#64) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details
2020-05-26 16:09:34 +03:00
Plausible.Mailer.send_email(email_template)
2019-09-02 14:29:19 +03:00
conn |> render("register_success.html", email: user.email, layout: {PlausibleWeb.LayoutView, "focus.html"})
{:error, changeset} ->
render(conn, "register_form.html", changeset: changeset, layout: {PlausibleWeb.LayoutView, "focus.html"})
end
end
def claim_activation_link(conn, %{"token" => token}) do
case Auth.Token.verify_activation(token) do
{:ok, %{name: name, email: email}} ->
case Auth.create_user(name, email) do
{:ok, user} ->
PlausibleWeb.Email.welcome_email(user)
Support for docker based self-hosting (#64) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details
2020-05-26 16:09:34 +03:00
|> Plausible.Mailer.send_email()
2019-09-02 14:29:19 +03:00
conn
|> put_session(:current_user_id, user.id)
2020-03-31 16:08:00 +03:00
|> put_resp_cookie("logged_in", "true", http_only: false)
2019-09-02 14:29:19 +03:00
|> redirect(to: "/password")
{:error, changeset} ->
send_resp(conn, 400, inspect(changeset.errors))
end
{:error, :expired} ->
render_error(conn, 401, "Your token has expired. Please request another activation link.")
{:error, _} ->
render_error(conn, 400, "Your token is invalid. Please request another activation link.")
end
end
def password_reset_request_form(conn, _) do
render(conn, "password_reset_request_form.html", layout: {PlausibleWeb.LayoutView, "focus.html"})
end
def password_reset_request(conn, %{"email" => ""}) do
render(conn, "password_reset_request_form.html", error: "Please enter an email address", layout: {PlausibleWeb.LayoutView, "focus.html"})
end
def password_reset_request(conn, %{"email" => email}) do
user = Repo.get_by(Plausible.Auth.User, email: email)
if user do
token = Auth.Token.sign_password_reset(email)
url = PlausibleWeb.Endpoint.clean_url() <> "/password/reset?token=#{token}"
Logger.debug("PASSWORD RESET LINK: " <> url)
email_template = PlausibleWeb.Email.password_reset_email(email, url)
Plausible.Mailer.deliver_now(email_template)
render(conn, "password_reset_request_success.html", email: email, layout: {PlausibleWeb.LayoutView, "focus.html"})
else
render(conn, "password_reset_request_success.html", email: email, layout: {PlausibleWeb.LayoutView, "focus.html"})
end
end
def password_reset_form(conn, %{"token" => token}) do
case Auth.Token.verify_password_reset(token) do
{:ok, _} ->
render(conn, "password_reset_form.html", token: token, layout: {PlausibleWeb.LayoutView, "focus.html"})
{:error, :expired} ->
render_error(conn, 401, "Your token has expired. Please request another password reset link.")
{:error, _} ->
render_error(conn, 401, "Your token is invalid. Please request another password reset link.")
end
end
def password_reset(conn, %{"token" => token, "password" => pw}) do
case Auth.Token.verify_password_reset(token) do
{:ok, %{email: email}} ->
user = Repo.get_by(Auth.User, email: email)
changeset = Auth.User.set_password(user, pw)
case Repo.update(changeset) do
{:ok, _updated} ->
conn
|> put_flash(:login_title, "Password updated successfully")
|> put_flash(:login_instructions, "Please log in with your new credentials")
|> put_session(:current_user_id, nil)
|> delete_resp_cookie("logged_in")
2019-09-02 14:29:19 +03:00
|> redirect(to: "/login")
{:error, changeset} ->
render(conn, "password_reset_form.html", changeset: changeset, token: token, layout: {PlausibleWeb.LayoutView, "focus.html"})
end
{:error, :expired} ->
render_error(conn, 401, "Your token has expired. Please request another password reset link.")
{:error, _} ->
render_error(conn, 401, "Your token is invalid. Please request another password reset link.")
end
end
def login(conn, %{"email" => email, "password" => password}) do
alias Plausible.Auth.Password
user = Repo.one(
from u in Plausible.Auth.User,
where: u.email == ^email
)
if user do
if Password.match?(password, user.password_hash || "") do
2020-04-01 10:37:30 +03:00
login_dest = get_session(conn, :login_dest) || "/sites"
2019-09-02 14:29:19 +03:00
conn
|> put_session(:current_user_id, user.id)
2020-03-31 16:08:00 +03:00
|> put_resp_cookie("logged_in", "true", http_only: false)
2019-09-02 14:29:19 +03:00
|> put_session(:login_dest, nil)
|> redirect(to: login_dest)
else
conn |> render("login_form.html", error: "Wrong email or password. Please try again.", layout: {PlausibleWeb.LayoutView, "focus.html"})
end
else
Password.dummy_calculation()
conn |> render("login_form.html", error: "Wrong email or password. Please try again.", layout: {PlausibleWeb.LayoutView, "focus.html"})
end
end
def login_form(conn, _params) do
render(conn, "login_form.html", layout: {PlausibleWeb.LayoutView, "focus.html"})
end
def password_form(conn, _params) do
render(conn, "password_form.html", layout: {PlausibleWeb.LayoutView, "focus.html"}, skip_plausible_tracking: true)
end
def set_password(conn, %{"password" => pw}) do
changeset = Auth.User.set_password(conn.assigns[:current_user], pw)
case Repo.update(changeset) do
{:ok, _user} ->
redirect(conn, to: "/sites/new")
{:error, changeset} ->
render(conn, "password_form.html", changeset: changeset, layout: {PlausibleWeb.LayoutView, "focus.html"})
end
end
def user_settings(conn, _params) do
changeset = Auth.User.changeset(conn.assigns[:current_user])
subscription = Plausible.Billing.active_subscription_for(conn.assigns[:current_user].id)
render(conn, "user_settings.html", changeset: changeset, subscription: subscription)
end
def save_settings(conn, %{"user" => user_params}) do
changes = Auth.User.changeset(conn.assigns[:current_user], user_params)
case Repo.update(changes) do
{:ok, _user} ->
conn
|> put_flash(:success, "Account settings saved succesfully")
|> redirect(to: "/settings")
{:error, changeset} ->
render(conn, "user_settings.html", changeset: changeset)
end
end
def delete_me(conn, params) do
user = conn.assigns[:current_user]
|> Repo.preload(:sites)
|> Repo.preload(:subscription)
2019-09-02 14:29:19 +03:00
for site_membership <- user.site_memberships do
Repo.delete!(site_membership)
end
for site <- user.sites do
Repo.delete!(site)
end
if user.subscription, do: Repo.delete!(user.subscription)
2019-09-02 14:29:19 +03:00
Repo.delete!(user)
logout(conn, params)
2019-09-02 14:29:19 +03:00
end
def logout(conn, _params) do
conn
|> configure_session(drop: true)
|> delete_resp_cookie("logged_in")
2019-09-02 14:29:19 +03:00
|> redirect(to: "/")
end
def google_auth_callback(conn, %{"code" => code, "state" => site_id}) do
res = Plausible.Google.Api.fetch_access_token(code)
id_token = res["id_token"]
[_, body, _] = String.split(id_token, ".")
id = body |> Base.decode64!(padding: false) |> Jason.decode!
Plausible.Site.GoogleAuth.changeset(%Plausible.Site.GoogleAuth{}, %{
email: id["email"],
refresh_token: res["refresh_token"],
access_token: res["access_token"],
expires: NaiveDateTime.utc_now() |> NaiveDateTime.add(res["expires_in"]),
user_id: conn.assigns[:current_user].id,
site_id: site_id
}) |> Repo.insert!
site = Repo.get(Plausible.Site, site_id)
redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings#google-auth")
2019-09-02 14:29:19 +03:00
end
end