Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code challenge justified #36

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ Dockerfile-jackjoe
.env.*
docker-compose.yml
node_modules

.vscode/
4 changes: 3 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
2 changes: 2 additions & 0 deletions lib/mailgun_logger/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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, []},
Expand Down
15 changes: 13 additions & 2 deletions lib/mailgun_logger/roles/roles.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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")
Expand All @@ -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
2 changes: 1 addition & 1 deletion lib/mailgun_logger/seeder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
5 changes: 4 additions & 1 deletion lib/mailgun_logger/users/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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"
Expand Down
13 changes: 8 additions & 5 deletions lib/mailgun_logger_web/controllers/user_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -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

Expand Down
2 changes: 0 additions & 2 deletions lib/mailgun_logger_web/helpers/paging_helpers.ex
Original file line number Diff line number Diff line change
@@ -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
Expand Down
66 changes: 66 additions & 0 deletions lib/mailgun_logger_web/plugs/permissions.ex
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions lib/mailgun_logger_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/mailgun_logger_web/templates/user/edit.html.eex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1>Edit user</h1>
<%= 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%>
<br />
<%= link "delete", to: Routes.user_path(@conn, :delete, @user), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-sm" %>
8 changes: 8 additions & 0 deletions lib/mailgun_logger_web/templates/user/form.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@
<%= error_tag f, :email %>
</div>

<div class="form-group">
<%= 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 %>
</div>


<%= if @new? do %>
<div class="form-group">
<%= label f, :password, class: "control-label" %>
Expand Down
2 changes: 1 addition & 1 deletion lib/mailgun_logger_web/templates/user/new.html.eex
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<h1>New user</h1>
<%= 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: []} %>
1 change: 0 additions & 1 deletion lib/mailgun_logger_web/views/error_helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ defmodule MailgunLoggerWeb.ErrorHelpers do
Conveniences for translating and building error messages.
"""

import Phoenix.HTML
use PhoenixHTMLHelpers

@doc """
Expand Down
10 changes: 5 additions & 5 deletions lib/mailgun_logger_web/views/event_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ defmodule MailgunLoggerWeb.EventView do
def event_name("delivered") do
assigns = %{}

~L"""
~H"""
<span style="background-color: lime; padding: 3px 5px; color: green;">delivered</span>
"""
end

def event_name("failed") do
assigns = %{}

~L"""
~H"""
<span style="background-color: red; padding: 3px 5px; color: white;">failed</span>
"""
end
Expand All @@ -33,15 +33,15 @@ defmodule MailgunLoggerWeb.EventView do
def event_type(event_type) when event_type in ~w(warn) do
assigns = %{event_type: event_type}

~L"""
~H"""
<span style="background-color: orange; padding: 3px 5px; color: #fff;"><%= event_type %></span>
"""
end

def event_type(event_type) when event_type in ~w(failed error) do
assigns = %{event_type: event_type}

~L"""
~H"""
<span style="background-color: red; padding: 3px 5px; color: #fff;"><%= event_type %></span>
"""
end
Expand Down Expand Up @@ -94,7 +94,7 @@ defmodule MailgunLoggerWeb.EventView do
<%= for a <- @attachments do %>
<li><%= a %></li>
<% end %>
</ul>
</ul>
"""
end

Expand Down
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down