diff --git a/apps/fg_http/lib/fg_http/devices.ex b/apps/fg_http/lib/fg_http/devices.ex index 72ec5a3b7..65d0367af 100644 --- a/apps/fg_http/lib/fg_http/devices.ex +++ b/apps/fg_http/lib/fg_http/devices.ex @@ -52,6 +52,14 @@ defmodule FgHttp.Devices do """ def get_device!(id), do: Repo.get!(Device, id) + def get_device!(id, with_rules: true) do + Repo.one( + from d in Device, + where: d.id == ^id, + preload: :rules + ) + end + @doc """ Creates a device. diff --git a/apps/fg_http/lib/fg_http/devices/device.ex b/apps/fg_http/lib/fg_http/devices/device.ex index 5be9fdbb0..a06ba621e 100644 --- a/apps/fg_http/lib/fg_http/devices/device.ex +++ b/apps/fg_http/lib/fg_http/devices/device.ex @@ -6,13 +6,15 @@ defmodule FgHttp.Devices.Device do use Ecto.Schema import Ecto.Changeset + alias FgHttp.{Rules.Rule, Users.User} + schema "devices" do field :name, :string field :public_key, :string - field :user_id, :id field :last_ip, :map - has_many :rules, FgHttp.Rules.Rule + has_many :rules, Rule + belongs_to :user, User timestamps() end diff --git a/apps/fg_http/lib/fg_http/ecto_enums.ex b/apps/fg_http/lib/fg_http/ecto_enums.ex new file mode 100644 index 000000000..579ce3acd --- /dev/null +++ b/apps/fg_http/lib/fg_http/ecto_enums.ex @@ -0,0 +1,18 @@ +import EctoEnum + +# We only allow dropping or accepting packets for now +defenum(RuleActionEnum, :action, [:drop, :accept]) + +# See http://ipset.netfilter.org/iptables.man.html +defenum(RuleProtocolEnum, :protocol, [ + :all, + :tcp, + :udp, + :udplite, + :icmp, + :icmpv6, + :esp, + :ah, + :sctp, + :mh +]) diff --git a/apps/fg_http/lib/fg_http/rules.ex b/apps/fg_http/lib/fg_http/rules.ex index 362479eca..78f142f62 100644 --- a/apps/fg_http/lib/fg_http/rules.ex +++ b/apps/fg_http/lib/fg_http/rules.ex @@ -17,6 +17,10 @@ defmodule FgHttp.Rules do [%Rule{}, ...] """ + def list_rules(device_id) do + Repo.all(from r in Rule, where: r.device_id == ^device_id, select: "*") + end + def list_rules do Repo.all(Rule) end diff --git a/apps/fg_http/lib/fg_http/rules/rule.ex b/apps/fg_http/lib/fg_http/rules/rule.ex index b0e524322..f5b7fe1c0 100644 --- a/apps/fg_http/lib/fg_http/rules/rule.ex +++ b/apps/fg_http/lib/fg_http/rules/rule.ex @@ -6,13 +6,17 @@ defmodule FgHttp.Rules.Rule do use Ecto.Schema import Ecto.Changeset - schema "rules" do - field :destination, :map - field :enabled, :boolean, default: false - field :port, :string - field :protocol, :string + alias FgHttp.{Devices.Device} - belongs_to :device, FgHttp.Devices.Device + schema "rules" do + field :destination, EctoNetwork.INET + field :action, RuleActionEnum, default: "drop" + field :priority, :integer, default: 0 + field :enabled, :boolean, default: true + field :port, :string + field :protocol, RuleProtocolEnum, default: "all" + + belongs_to :device, Device timestamps() end @@ -20,7 +24,7 @@ defmodule FgHttp.Rules.Rule do @doc false def changeset(rule, attrs) do rule - |> cast(attrs, [:destination, :port, :protocol, :enabled]) - |> validate_required([:destination, :enabled]) + |> cast(attrs, [:device_id, :priority, :action, :destination, :port, :protocol, :enabled]) + |> validate_required([:device_id, :priority, :action, :destination, :protocol, :enabled]) end end diff --git a/apps/fg_http/lib/fg_http/sessions/session.ex b/apps/fg_http/lib/fg_http/sessions/session.ex index e9f2174a1..506e6075c 100644 --- a/apps/fg_http/lib/fg_http/sessions/session.ex +++ b/apps/fg_http/lib/fg_http/sessions/session.ex @@ -6,8 +6,10 @@ defmodule FgHttp.Sessions.Session do use Ecto.Schema import Ecto.Changeset + alias FgHttp.{Users.User} + schema "sessions" do - field :user_id, :id + belongs_to :user, User timestamps() end diff --git a/apps/fg_http/lib/fg_http/users/user.ex b/apps/fg_http/lib/fg_http/users/user.ex index c4fe12691..2df6319fe 100644 --- a/apps/fg_http/lib/fg_http/users/user.ex +++ b/apps/fg_http/lib/fg_http/users/user.ex @@ -6,12 +6,17 @@ defmodule FgHttp.Users.User do use Ecto.Schema import Ecto.Changeset + alias FgHttp.{Devices.Device, Sessions.Session} + schema "users" do field :email, :string field :confirmed_at, :utc_datetime field :last_signed_in_at, :utc_datetime field :password_digest, :string + has_many :devices, Device + has_many :sessions, Session + timestamps() end diff --git a/apps/fg_http/lib/fg_http_web/controllers/rule_controller.ex b/apps/fg_http/lib/fg_http_web/controllers/rule_controller.ex index 129ebfb0b..3d53ed9d2 100644 --- a/apps/fg_http/lib/fg_http_web/controllers/rule_controller.ex +++ b/apps/fg_http/lib/fg_http_web/controllers/rule_controller.ex @@ -9,8 +9,7 @@ defmodule FgHttpWeb.RuleController do plug FgHttpWeb.Plugs.Authenticator def index(conn, %{"device_id" => device_id}) do - device = Devices.get_device!(device_id) - + device = Devices.get_device!(device_id, with_rules: true) render(conn, "index.html", device: device, rules: device.rules) end diff --git a/apps/fg_http/lib/fg_http_web/templates/device/show.html.eex b/apps/fg_http/lib/fg_http_web/templates/device/show.html.eex index 8db5dae66..03c74a793 100644 --- a/apps/fg_http/lib/fg_http_web/templates/device/show.html.eex +++ b/apps/fg_http/lib/fg_http_web/templates/device/show.html.eex @@ -12,5 +12,9 @@ -<%= link "Edit", to: Routes.device_path(@conn, :edit, @device) %> -<%= link "Back", to: Routes.device_path(@conn, :index) %> +

+ <%= link "Show Rules for This Device", to: Routes.device_rule_path(@conn, :index, @device) %> +

+ +

<%= link "Edit", to: Routes.device_path(@conn, :edit, @device) %>

+

<%= link "Back", to: Routes.device_path(@conn, :index) %>

diff --git a/apps/fg_http/lib/fg_http_web/templates/rule/form.html.eex b/apps/fg_http/lib/fg_http_web/templates/rule/form.html.eex index 8a4461fbf..bdf4a802a 100644 --- a/apps/fg_http/lib/fg_http_web/templates/rule/form.html.eex +++ b/apps/fg_http/lib/fg_http_web/templates/rule/form.html.eex @@ -1,27 +1,41 @@ -<%= form_for @changeset, @action, fn f -> %> +<%= form_for @changeset, @action, [class: "black-80"], fn f -> %> <%= if @changeset.action do %> -
+

Oops, something went wrong! Please check the errors below.

<% end %> - <%= label f, :destination %> - <%= text_input f, :destination %> - <%= error_tag f, :destination %> +
+ <%= label f, :destination, class: "f6 b db mb2" %> + <%= text_input f, :destination, class: "input-reset ba b--black-20 pa2 mb2 db w-100" %> + <%= error_tag f, :destination %> +
- <%= label f, :port %> - <%= text_input f, :port %> - <%= error_tag f, :port %> +
+ <%= label f, :port, class: "f6 b db mb2" %> + <%= text_input f, :port, class: "input-reset ba b--black-20 pa2 mb2 db w-100" %> + <%= error_tag f, :port %> +
- <%= label f, :protocol %> - <%= text_input f, :protocol %> - <%= error_tag f, :protocol %> +
+ <%= label f, :protocol, class: "f6 b db mb2" %> + <%= text_input f, :protocol, class: "input-reset ba b--black-20 pa2 mb2 db w-100" %> + <%= error_tag f, :protocol %> +
- <%= label f, :enabled %> - <%= checkbox f, :enabled %> - <%= error_tag f, :enabled %> +
+ <%= label f, :priority, class: "f6 b db mb2" %> + <%= text_input f, :priority, class: "input-reset ba b--black-20 pa2 mb2 db w-100" %> + <%= error_tag f, :priority %> +
-
+
+ <%= label f, :enabled, class: "f6 b db mb2" %> + <%= checkbox f, :enabled %> + <%= error_tag f, :enabled %> +
+ +
<%= submit "Save" %>
<% end %> diff --git a/apps/fg_http/lib/fg_http_web/templates/rule/index.html.eex b/apps/fg_http/lib/fg_http_web/templates/rule/index.html.eex index 63ab8b4e3..a38306450 100644 --- a/apps/fg_http/lib/fg_http_web/templates/rule/index.html.eex +++ b/apps/fg_http/lib/fg_http_web/templates/rule/index.html.eex @@ -1,23 +1,21 @@ -

Listing Rules

+

Listing Rules for Device <%= @device.name %>

- - - - - - - +
DestinationPortProtocolEnabled
+ + + + + + - - <%= for rule <- @rules do %> - - - - - + + + + +
DestinationPortProtocolEnabled
<%= rule.destination %><%= rule.port %><%= rule.protocol %><%= rule.enabled %>
<%= rule.destination %><%= rule.port %><%= rule.protocol %><%= rule.enabled %> <%= link "Show", to: Routes.rule_path(@conn, :show, rule) %> @@ -30,5 +28,5 @@

- <%= link "New Rule", to: Routes.rule_path(@conn, :new, @device) %> + <%= link "New Rule", to: Routes.device_rule_path(@conn, :new, @device) %>

diff --git a/apps/fg_http/lib/fg_http_web/templates/rule/new.html.eex b/apps/fg_http/lib/fg_http_web/templates/rule/new.html.eex index 5c88667cd..e7963ed7d 100644 --- a/apps/fg_http/lib/fg_http_web/templates/rule/new.html.eex +++ b/apps/fg_http/lib/fg_http_web/templates/rule/new.html.eex @@ -1,5 +1,5 @@ -

New Rule

+

New Rule for Device <%= @device.name %>

-<%= render "form.html", Map.put(assigns, :action, Routes.rule_path(@conn, :create, @device)) %> +<%= render "form.html", Map.put(assigns, :action, Routes.device_rule_path(@conn, :create, @device)) %> -<%= link "Back", to: Routes.rule_path(@conn, :index, @device) %> +<%= link "Back", to: Routes.device_rule_path(@conn, :index, @device) %> diff --git a/apps/fg_http/mix.exs b/apps/fg_http/mix.exs index f9896260a..e0bbd12d3 100644 --- a/apps/fg_http/mix.exs +++ b/apps/fg_http/mix.exs @@ -41,6 +41,8 @@ defmodule FgHttp.MixProject do {:phoenix_pubsub, "~> 2.0"}, {:phoenix_ecto, "~> 4.0"}, {:ecto_sql, "~> 3.1"}, + {:ecto_enum, "~> 1.4.0"}, + {:ecto_network, "~> 1.3.0"}, {:postgrex, ">= 0.0.0"}, {:phoenix_html, "~> 2.11"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, diff --git a/apps/fg_http/priv/repo/migrations/20200228145810_create_devices.exs b/apps/fg_http/priv/repo/migrations/20200228145810_create_devices.exs index 931569ea6..1b0c692d9 100644 --- a/apps/fg_http/priv/repo/migrations/20200228145810_create_devices.exs +++ b/apps/fg_http/priv/repo/migrations/20200228145810_create_devices.exs @@ -6,7 +6,7 @@ defmodule FgHttp.Repo.Migrations.CreateDevices do add :name, :string add :public_key, :string add :last_ip, :inet - add :user_id, references(:users, on_delete: :delete_all) + add :user_id, references(:users, on_delete: :delete_all), null: false timestamps() end diff --git a/apps/fg_http/priv/repo/migrations/20200228154815_create_rules.exs b/apps/fg_http/priv/repo/migrations/20200228154815_create_rules.exs index e83298300..eda009fb9 100644 --- a/apps/fg_http/priv/repo/migrations/20200228154815_create_rules.exs +++ b/apps/fg_http/priv/repo/migrations/20200228154815_create_rules.exs @@ -2,12 +2,17 @@ defmodule FgHttp.Repo.Migrations.CreateRules do use Ecto.Migration def change do + RuleActionEnum.create_type() + RuleProtocolEnum.create_type() + create table(:rules) do add :destination, :inet - add :port, :string - add :protocol, :string + add :protocol, RuleProtocolEnum.type(), default: "all", null: false + add :action, RuleActionEnum.type(), default: "drop", null: false + add :priority, :integer, default: 0, null: false add :enabled, :boolean, default: false, null: false - add :device_id, references(:devices, on_delete: :delete_all) + add :port, :string + add :device_id, references(:devices, on_delete: :delete_all), null: false timestamps() end diff --git a/apps/fg_http/priv/repo/seeds.exs b/apps/fg_http/priv/repo/seeds.exs index e4f1922a7..adccbb974 100644 --- a/apps/fg_http/priv/repo/seeds.exs +++ b/apps/fg_http/priv/repo/seeds.exs @@ -23,9 +23,9 @@ Repo.transaction(fn -> user_id: user.id }) - {:ok, rule} = + {:ok, _rule} = FgHttp.Rules.create_rule(%{ - destination: %Postgrex.INET{address: {0, 0, 0, 0}, netmask: 0}, - device_id: device.id + device_id: device.id, + destination: %Postgrex.INET{address: {0, 0, 0, 0}, netmask: 0} }) end) diff --git a/mix.lock b/mix.lock index 42340985a..11058b065 100644 --- a/mix.lock +++ b/mix.lock @@ -7,6 +7,7 @@ "db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "ecto": {:hex, :ecto, "3.4.4", "a2c881e80dc756d648197ae0d936216c0308370332c5e77a2325a10293eef845", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4bd3ad62abc3b21fb629f0f7a3dab23a192fca837d257dd08449fba7373561"}, + "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"}, "ecto_network": {:hex, :ecto_network, "1.3.0", "1e77fa37c20e0f6a426d3862732f3317b0fa4c18f123d325f81752a491d7304e", [: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", "053a5e46ef2837e8ea5ea97c82fa0f5494699209eddd764e663c85f11b2865bd"}, "ecto_sql": {:hex, :ecto_sql, "3.4.3", "c552aa8a7ccff2b64024f835503b3155d8e73452c180298527fbdbcd6e79710b", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ec9e59d6fa3f8cfda9963ada371e9e6659167c2338a997bd7ea23b10b245842b"}, "file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},