From 872b29e783b4685ecccf5abc4f1df7d9959cb42a Mon Sep 17 00:00:00 2001 From: Lukas Vandermeersch Date: Tue, 17 Sep 2024 19:53:55 +0200 Subject: [PATCH 1/3] Added envy to facilitate loading .env variables also updated gitignore to ignore vscode generated files --- .gitignore | 2 ++ lib/mailgun_logger/application.ex | 2 ++ mix.exs | 1 + mix.lock | 1 + 4 files changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 3b0f885..6aa9005 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ Dockerfile-jackjoe .env.* docker-compose.yml node_modules + +.vscode/ diff --git a/lib/mailgun_logger/application.ex b/lib/mailgun_logger/application.ex index 65836e3..50de8d8 100644 --- a/lib/mailgun_logger/application.ex +++ b/lib/mailgun_logger/application.ex @@ -8,6 +8,8 @@ defmodule MailgunLogger.Application do def start(_type, _args) do check_store_raw() + Envy.auto_load + Envy.reload_config children = [ {Phoenix.PubSub, name: MailgunLogger.PubSub}, {MailgunLogger.Repo, []}, diff --git a/mix.exs b/mix.exs index 507614e..1b96f17 100644 --- a/mix.exs +++ b/mix.exs @@ -59,6 +59,7 @@ defmodule MailgunLogger.Mixfile do {:bamboo_phoenix, "~> 1.0"}, {:decimal, "~> 2.0"}, {:ecto_sql, "~> 3.11"}, + {:envy, "~> 1.1.1"}, {:ex_doc, "~> 0.21", only: :dev, runtime: false}, {:ex_machina, "~> 2.3", only: :test}, {:excoveralls, "~> 0.10", only: :test}, diff --git a/mix.lock b/mix.lock index 5ba488d..d72ca89 100644 --- a/mix.lock +++ b/mix.lock @@ -16,6 +16,7 @@ "ecto": {:hex, :ecto, "3.11.1", "4b4972b717e7ca83d30121b12998f5fcdc62ba0ed4f20fd390f16f3270d85c3e", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ebd3d3772cd0dfcd8d772659e41ed527c28b2a8bde4b00fe03e0463da0f1983b"}, "ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"}, "elixir_make": {:hex, :elixir_make, "0.7.8", "505026f266552ee5aabca0b9f9c229cbb496c689537c9f922f3eb5431157efc7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07"}, + "envy": {:hex, :envy, "1.1.1", "0bc9bd654dec24fcdf203f7c5aa1b8f30620f12cfb28c589d5e9c38fe1b07475", [:mix], [], "hexpm", "7061eb1a47415fd757145d8dec10dc0b1e48344960265cb108f194c4252c3a89"}, "esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"}, "ex_aws": {:hex, :ex_aws, "2.5.1", "7418917974ea42e9e84b25e88b9f3d21a861d5f953ad453e212f48e593d8d39f", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1b95431f70c446fa1871f0eb9b183043c5a625f75f9948a42d25f43ae2eff12b"}, "ex_aws_s3": {:hex, :ex_aws_s3, "2.5.3", "422468e5c3e1a4da5298e66c3468b465cfd354b842e512cb1f6fbbe4e2f5bdaf", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "4f09dd372cc386550e484808c5ac5027766c8d0cd8271ccc578b82ee6ef4f3b8"}, From 533f357f30a4e47aa9134fc406e4392d6df0e047 Mon Sep 17 00:00:00 2001 From: Lukas Vandermeersch Date: Tue, 17 Sep 2024 19:54:25 +0200 Subject: [PATCH 2/3] logging Ip adress to console as a sanity check --- config/config.exs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/config.exs b/config/config.exs index ecc6ce5..39a8a7e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -87,7 +87,9 @@ config :mailgun_logger, MailgunLogger.Repo, queue_target: 100, # default 1000ms queue_interval: 2_000 - +IO.puts """ +IP ADRESS: #{System.get_env("ML_DB_HOST")} +""" # Configures Elixir's Logger config :logger, backends: [:console], From 6d065586263ade63908b401bf8af42eceab82883 Mon Sep 17 00:00:00 2001 From: Lukas Vandermeersch Date: Fri, 20 Sep 2024 21:32:12 +0200 Subject: [PATCH 3/3] code_challenge complete cleaned up code, added field to edit user template, added step to changeset --- lib/mailgun_logger/roles/roles.ex | 15 ++++- lib/mailgun_logger/seeder.ex | 2 +- lib/mailgun_logger/users/user.ex | 5 +- .../controllers/user_controller.ex | 13 ++-- .../helpers/paging_helpers.ex | 2 - lib/mailgun_logger_web/plugs/permissions.ex | 66 +++++++++++++++++++ lib/mailgun_logger_web/router.ex | 1 + .../templates/user/edit.html.eex | 2 +- .../templates/user/form.html.eex | 8 +++ .../templates/user/new.html.eex | 2 +- lib/mailgun_logger_web/views/error_helpers.ex | 1 - lib/mailgun_logger_web/views/event_view.ex | 10 +-- 12 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 lib/mailgun_logger_web/plugs/permissions.ex diff --git a/lib/mailgun_logger/roles/roles.ex b/lib/mailgun_logger/roles/roles.ex index 8a873bb..091691d 100644 --- a/lib/mailgun_logger/roles/roles.ex +++ b/lib/mailgun_logger/roles/roles.ex @@ -7,12 +7,15 @@ defmodule MailgunLogger.Roles do @superuser_role "superuser" @admin_role "admin" + @member_role "member" ######################################################### - @default_actions ~w() + @default_actions ~w(undefined_permission) - @admin_actions ~w(do_stuff) ++ @default_actions + @member_actions ~w(view_events view_home view_events_detail view_events_detail_stored_message) ++ @default_actions + + @admin_actions ~w(view_stats view_users edit_users delete_users create_users view_accounts edit_accounts create_accounts delete_accounts) ++ @member_actions @superuser_actions ~w() ++ @admin_actions @@ -55,6 +58,12 @@ defmodule MailgunLogger.Roles do Enum.any?(roles, &can?(&1.name, action)) end + + for action <- @member_actions do + action = String.to_atom(action) + def can?(@member_role, unquote(action)), do: true + end + for action <- @admin_actions do action = String.to_atom(action) def can?(@admin_role, unquote(action)), do: true @@ -65,6 +74,7 @@ defmodule MailgunLogger.Roles do def can?(@superuser_role, unquote(action)), do: true end + def can?(_, _), do: false def is?(%User{roles: roles}, :superuser), do: is(roles, "superuser") @@ -77,6 +87,7 @@ defmodule MailgunLogger.Roles do def abilities(%User{roles: roles}), do: hd(roles) |> abilities() def abilities(%Role{name: "admin"}), do: @admin_actions def abilities(%Role{name: "superuser"}), do: @superuser_actions + def abilities(%Role{name: "member"}), do: @member_actions def roles(%User{roles: roles}), do: Enum.map(roles, & &1.name) end diff --git a/lib/mailgun_logger/seeder.ex b/lib/mailgun_logger/seeder.ex index 6fe0588..d2d25f4 100644 --- a/lib/mailgun_logger/seeder.ex +++ b/lib/mailgun_logger/seeder.ex @@ -6,7 +6,7 @@ defmodule MailgunLogger.Seeder do # alias MailgunLogger.UserRole - @roles [%Role{name: "superuser"}, %Role{name: "admin"}] + @roles [%Role{name: "superuser"}, %Role{name: "admin"}, %Role{name: "member"}] def run do Enum.each(@roles, &insert_if_new(&1)) diff --git a/lib/mailgun_logger/users/user.ex b/lib/mailgun_logger/users/user.ex index f137205..1f8f190 100644 --- a/lib/mailgun_logger/users/user.ex +++ b/lib/mailgun_logger/users/user.ex @@ -43,7 +43,7 @@ defmodule MailgunLogger.User do @spec changeset(User.t(), map()) :: Ecto.Changeset.t() def changeset(%User{} = user, attrs \\ %{}) do user - |> cast(attrs, [:firstname, :lastname, :email, :password]) + |> cast(attrs, [:firstname, :lastname, :email, :password,]) |> validate_required([:email, :password]) |> update_change(:email, &String.downcase/1) |> validate_format(:email, @email_format) @@ -56,11 +56,14 @@ defmodule MailgunLogger.User do @doc false @spec update_changeset(User.t(), map()) :: Ecto.Changeset.t() def update_changeset(%User{} = user, attrs \\ %{}) do + + IO.puts("ATTRS: #{inspect(attrs)}") user |> cast(attrs, [:firstname, :lastname, :email]) |> update_change(:email, &String.downcase/1) |> validate_format(:email, @email_format) |> unique_constraint(:email) + |> put_assoc(:roles, Roles.get_roles_by_id(attrs["roles"])) end @doc "Used when creating an admin, e.g. from the setup flow" diff --git a/lib/mailgun_logger_web/controllers/user_controller.ex b/lib/mailgun_logger_web/controllers/user_controller.ex index ed2ce30..b1f3a7b 100644 --- a/lib/mailgun_logger_web/controllers/user_controller.ex +++ b/lib/mailgun_logger_web/controllers/user_controller.ex @@ -3,6 +3,7 @@ defmodule MailgunLoggerWeb.UserController do alias MailgunLogger.Users alias MailgunLogger.User + alias MailgunLogger.Roles def index(conn, _) do users = Users.list_users() @@ -11,7 +12,8 @@ defmodule MailgunLoggerWeb.UserController do def new(conn, _) do changeset = User.changeset(%User{}) - render(conn, :new, changeset: changeset) + roles= Roles.list_roles() + render(conn, :new, changeset: changeset, roles: roles) end def create(conn, %{"user" => params}) do @@ -23,18 +25,19 @@ defmodule MailgunLoggerWeb.UserController do def edit(conn, %{"id" => id}) do user = Users.get_user!(id) + roles = Roles.list_roles() changeset = User.changeset(user) - render(conn, :edit, changeset: changeset, user: user) + render(conn, :edit, changeset: changeset, user: user, roles: roles) end def update(conn, %{"id" => id, "user" => params}) do user = Users.get_user!(id) - + roles = Roles.list_roles() case Users.update_user(user, params) do {:ok, _} -> redirect(conn, to: Routes.user_path(conn, :index)) - {:error, changeset} -> + {:error, changeset} -> IO.inspect(changeset) - render(conn, :edit, changeset: changeset, user: user) + render(conn, :edit, changeset: changeset, user: user, roles: roles) end end diff --git a/lib/mailgun_logger_web/helpers/paging_helpers.ex b/lib/mailgun_logger_web/helpers/paging_helpers.ex index f0c8245..e61e0fc 100644 --- a/lib/mailgun_logger_web/helpers/paging_helpers.ex +++ b/lib/mailgun_logger_web/helpers/paging_helpers.ex @@ -1,6 +1,4 @@ defmodule MailgunLoggerWeb.PagingHelpers do - import Phoenix.HTML - import Phoenix.HTML.Form use PhoenixHTMLHelpers def sort_link(conn, _page, field, name, opts \\ []) do diff --git a/lib/mailgun_logger_web/plugs/permissions.ex b/lib/mailgun_logger_web/plugs/permissions.ex new file mode 100644 index 0000000..6c520fb --- /dev/null +++ b/lib/mailgun_logger_web/plugs/permissions.ex @@ -0,0 +1,66 @@ +defmodule MailgunLoggerWeb.Plugs.Permissions do + import Plug.Conn + import Phoenix.Controller + alias MailgunLogger.Roles + + @moduledoc """ + Plug to enforce role based permissions. When called by a pipeline, the plug will try to determine the permission based on the connection info and compare it to the permissions of the logged in user's roles. + + The Plug contains private methods for each of these steps. + """ + @spec init(any) :: any + def init(opts), do: opts + + @spec call(Plug.Conn.t(), map()) :: Plug.Conn.t() + def call(conn, _) do + permission = determine_permission(conn) + IO.puts """ + PERMISSION: #{permission} + """ + check_permission(conn, permission) + end + + @spec determine_permission(Plug.Conn.t()) :: String.t() + defp determine_permission(conn) do + + + method=conn.method + path=conn.request_path + + cond do + method == "GET" && path == "/" -> "view_home" + method == "GET" && path == "/events" -> "view_events" + method == "GET" && String.match?(path, ~r/events\/[[:digit:]]+$/) -> "view_events_detail" + method == "GET" && String.match?(path, ~r/events\/[[:digit:]]+\/stored_message$/) -> "view_events_detail_stored_message" + method == "GET" && path == "/stats" -> "view_stats" + method == "GET" && path == "/accounts" -> "view_accounts" + method in ["GET","PUT"] && String.match?(path, ~r/accounts\/[[:digit:]]+/) -> "edit_accounts" + method == "DELETE" && String.match?(path, ~r/accounts\/[[:digit:]]+/) -> "delete_accounts" + method == "GET" && path == "/accounts/new" -> "create_accounts" + method =="POST" && path == "/accounts" -> "create_accounts" + method == "GET" && path == "/users" -> "view_users" + method in ["GET","PUT"] && String.match?(path, ~r/users\/[[:digit:]]+/) -> "edit_users" + method == "DELETE" && String.match?(path, ~r/users\/[[:digit:]]+/) -> "delete_users" + method == "GET" && path == "/users/new" -> "create_users" + method =="POST" && path == "/users" -> "create_users" + true -> "undefined_permission" + end + end + + @spec check_permission(Plug.Conn.t(), String.t()) :: Plug.Conn.t() + defp check_permission(conn, permission) do + if Roles.can?(conn.assigns[:current_user], String.to_atom(permission)) do + IO.puts "allowed" + conn + else + IO.puts "denied" + IO.puts""" + #{Roles.abilities(conn.assigns[:current_user])} + """ + conn + |> put_status(403) + |> render(MailgunLoggerWeb.ErrorView, "403.html") + |> halt() + end + end +end diff --git a/lib/mailgun_logger_web/router.ex b/lib/mailgun_logger_web/router.ex index b894666..4919399 100644 --- a/lib/mailgun_logger_web/router.ex +++ b/lib/mailgun_logger_web/router.ex @@ -20,6 +20,7 @@ defmodule MailgunLoggerWeb.Router do pipeline :auth do plug(MailgunLoggerWeb.Plugs.SetupCheck) plug(MailgunLoggerWeb.Plugs.Auth) + plug(MailgunLoggerWeb.Plugs.Permissions) end # Always except in prod diff --git a/lib/mailgun_logger_web/templates/user/edit.html.eex b/lib/mailgun_logger_web/templates/user/edit.html.eex index 17ef999..df253ac 100644 --- a/lib/mailgun_logger_web/templates/user/edit.html.eex +++ b/lib/mailgun_logger_web/templates/user/edit.html.eex @@ -1,4 +1,4 @@

Edit user

-<%= render "form.html", action: Routes.user_path(@conn, :update, @user), changeset: @changeset, new?: false, flash: @flash %> +<%= render "form.html", action: Routes.user_path(@conn, :update, @user), changeset: @changeset, new?: false, flash: @flash, roles: @roles, user: @user%>
<%= link "delete", to: Routes.user_path(@conn, :delete, @user), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-sm" %> diff --git a/lib/mailgun_logger_web/templates/user/form.html.eex b/lib/mailgun_logger_web/templates/user/form.html.eex index 5b5fcf6..f1cb659 100644 --- a/lib/mailgun_logger_web/templates/user/form.html.eex +++ b/lib/mailgun_logger_web/templates/user/form.html.eex @@ -31,6 +31,14 @@ <%= error_tag f, :email %> +
+ <%= label f, :roles, class: "control-label" %> + <%= multiple_select f, :roles, Enum.map(@roles, &{&1.name, &1.id}), + selected: Enum.map(@user.roles, &(&1.id)), class: "form-control" %> + <%= error_tag f, :roles %> +
+ + <%= if @new? do %>
<%= label f, :password, class: "control-label" %> diff --git a/lib/mailgun_logger_web/templates/user/new.html.eex b/lib/mailgun_logger_web/templates/user/new.html.eex index 138200d..7c5a11f 100644 --- a/lib/mailgun_logger_web/templates/user/new.html.eex +++ b/lib/mailgun_logger_web/templates/user/new.html.eex @@ -1,2 +1,2 @@

New user

-<%= render "form.html", action: Routes.user_path(@conn, :create), changeset: @changeset, flash: @flash, new?: true %> +<%= render "form.html", action: Routes.user_path(@conn, :create), changeset: @changeset, flash: @flash, new?: true, roles: @roles, user: %{roles: []} %> diff --git a/lib/mailgun_logger_web/views/error_helpers.ex b/lib/mailgun_logger_web/views/error_helpers.ex index 879fb56..f40b07a 100644 --- a/lib/mailgun_logger_web/views/error_helpers.ex +++ b/lib/mailgun_logger_web/views/error_helpers.ex @@ -3,7 +3,6 @@ defmodule MailgunLoggerWeb.ErrorHelpers do Conveniences for translating and building error messages. """ - import Phoenix.HTML use PhoenixHTMLHelpers @doc """ diff --git a/lib/mailgun_logger_web/views/event_view.ex b/lib/mailgun_logger_web/views/event_view.ex index e2abb94..2c317cb 100644 --- a/lib/mailgun_logger_web/views/event_view.ex +++ b/lib/mailgun_logger_web/views/event_view.ex @@ -15,7 +15,7 @@ defmodule MailgunLoggerWeb.EventView do def event_name("delivered") do assigns = %{} - ~L""" + ~H""" delivered """ end @@ -23,7 +23,7 @@ defmodule MailgunLoggerWeb.EventView do def event_name("failed") do assigns = %{} - ~L""" + ~H""" failed """ end @@ -33,7 +33,7 @@ defmodule MailgunLoggerWeb.EventView do def event_type(event_type) when event_type in ~w(warn) do assigns = %{event_type: event_type} - ~L""" + ~H""" <%= event_type %> """ end @@ -41,7 +41,7 @@ defmodule MailgunLoggerWeb.EventView do def event_type(event_type) when event_type in ~w(failed error) do assigns = %{event_type: event_type} - ~L""" + ~H""" <%= event_type %> """ end @@ -94,7 +94,7 @@ defmodule MailgunLoggerWeb.EventView do <%= for a <- @attachments do %>
  • <%= a %>
  • <% end %> - + """ end