diff --git a/config/config.exs b/config/config.exs index 61bf72f..bafbdfe 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,11 +1,5 @@ import Config -config :chessh, Chessh.Repo, - database: "chessh", - username: "postgres", - password: "postgres", - hostname: "localhost" - config :chessh, ecto_repos: [Chessh.Repo] config :esshd, diff --git a/config/dev.exs b/config/dev.exs index becde76..88ee30b 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -1 +1,7 @@ import Config + +config :chessh, Chessh.Repo, + database: "chessh", + username: "postgres", + password: "postgres", + hostname: "localhost" diff --git a/config/test.exs b/config/test.exs index b46bc25..8ab016e 100644 --- a/config/test.exs +++ b/config/test.exs @@ -4,6 +4,5 @@ config :chessh, Chessh.Repo, database: "chessh-test", username: "postgres", password: "postgres", - hostname: "localhost" - -config :chessh, ecto_repos: [Chessh.Repo] + hostname: "localhost", + pool: Ecto.Adapters.SQL.Sandbox diff --git a/lib/chessh/schema/key.ex b/lib/chessh/schema/key.ex new file mode 100644 index 0000000..f8c14cf --- /dev/null +++ b/lib/chessh/schema/key.ex @@ -0,0 +1,46 @@ +defmodule Chessh.Key do + use Ecto.Schema + import Ecto.Changeset + + schema "keys" do + field(:key, :string) + field(:name, :string) + + belongs_to(:player, Chessh.Player) + + timestamps() + end + + def changeset(key, attrs) do + key + |> cast(update_encode_key(attrs, :key), [:key]) + |> cast(attrs, [:name]) + |> validate_required([:key, :name]) + |> validate_format(:key, ~r/[\-\w\d]+ [^ ]+$/, message: "invalid ssh key") + |> validate_format(:key, ~r/^(?!ssh-dss).+/, message: "DSA keys are not supported") + end + + defp update_encode_key(attrs, field) do + if Map.has_key?(attrs, field) do + Map.update!(attrs, field, &encode_key/1) + else + attrs + end + end + + def encode_key(key) do + if is_tuple(key) do + case key do + {pub, [opts]} -> [{pub, [opts]}] + key -> [{key, [comment: '']}] + end + |> :ssh_file.encode(:openssh_key) + else + key + end + # Remove comment at end of key + |> String.replace(~r/ [^ ]+\@[^ ]+$/, "") + # Remove potential spaces / newline + |> String.trim() + end +end diff --git a/lib/chessh/schema/player.ex b/lib/chessh/schema/player.ex index 7d9bb6e..d04ed3e 100644 --- a/lib/chessh/schema/player.ex +++ b/lib/chessh/schema/player.ex @@ -9,6 +9,8 @@ defmodule Chessh.Player do field(:password, :string, virtual: true) field(:hashed_password, :string) + has_many(:keys, Chessh.Key) + timestamps() end @@ -52,7 +54,6 @@ defmodule Chessh.Player do message: "only letters, numbers, underscores, and hyphens allowed" ) |> unique_constraint(:username) - |> lowercase(:username) end defp validate_password(changeset, opts) do @@ -74,8 +75,4 @@ defmodule Chessh.Player do changeset end end - - defp lowercase(changeset, field) do - Map.update!(changeset, field, &String.downcase/1) - end end diff --git a/mix.exs b/mix.exs index b73b943..0a40f28 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,9 @@ defmodule Chessh.MixProject do version: "0.1.0", elixir: "~> 1.14", start_permanent: Mix.env() == :prod, - deps: deps() + deps: deps(), + elixirc_paths: elixirc_paths(Mix.env()), + aliases: aliases() ] end @@ -19,6 +21,9 @@ defmodule Chessh.MixProject do ] end + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + # Run "mix help deps" to learn about dependencies. defp deps do [ @@ -30,4 +35,10 @@ defmodule Chessh.MixProject do {:bcrypt_elixir, "~> 3.0"} ] end + + defp aliases do + [ + test: ["ecto.create --quiet", "ecto.migrate", "test"] + ] + end end diff --git a/test/chessh_test.exs b/test/chessh_test.exs deleted file mode 100644 index 0fa7da8..0000000 --- a/test/chessh_test.exs +++ /dev/null @@ -1,4 +0,0 @@ -defmodule ChesshTest do - use ExUnit.Case - doctest Chessh -end diff --git a/test/schema/key_test.exs b/test/schema/key_test.exs new file mode 100644 index 0000000..2c5409d --- /dev/null +++ b/test/schema/key_test.exs @@ -0,0 +1,41 @@ +defmodule Chessh.Schema.KeyTest do + use Chessh.RepoCase + use ExUnit.Case + alias Chessh.Key + + @valid_attrs %{ + name: "Logan's Key", + key: + {{{:ECPoint, + <<159, 246, 44, 226, 70, 24, 71, 127, 118, 17, 96, 71, 18, 121, 48, 203, 244, 140, 156, + 56, 179, 138, 64, 242, 169, 140, 109, 156, 174, 148, 222, 56>>}, + {:namedCurve, {1, 3, 101, 112}}}, [comment: 'logan@yagami']} + } + @valid_key_attrs %{ + name: "asdf key", + key: + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC7Mpf2QIL32MmKxcrXAoZM3l7/hBy+8d+WqTRMun+tC/XYNiXSIDuZv01an3D1d22fmSpZiprFQzjB4yEz23qw= logan@yagami" + } + @invalid_key_attrs %{ + name: "An Invalid Key", + key: "AAAAC3NzaC1lZDI1NTE5AAAAIJ/2LOJGGEd/dhFgRxJ5MMv0jJw4s4pA8qmMbZyulN44" + } + @dsa_key_attrs %{ + name: "A DSA Key", + key: + "ssh-dss AAAAB3NzaC1kc3MAAACBAKkpMO6EbCb0BdA9m5ZJ0fGtpqJRXhyC7i4WWAdqFXxDPL0wakOmn2Vu3e4Z7UUwjSNB4jHQFzcrFKLAuXSCCMX5/nXTR5kFF3D7eSb8FApplh0+BKJn1B04A3atEqXrne6oDzl+eGbVTBL6rftFK90mi0FOHyYmT88gsbBEKgSHAAAAFQDqHCZC7aORvYqF8v9ONVOXAkUaTQAAAIAx3XEupb+JdXNak1TExQ1568M7CFj5GqBlSuKnBmEq6g24WIu7v1SQ2l3+YpOQv30+7GczpF1paPHnitOrDcMuwWM1HqbHkc6UPIjIhoaVeogOKIYw2gVMIQImdgS6ky3HADVrmOPvjakPIoCyk70zBWuwc82QC4Bc6yd58Uu1GQAAAIEAgdYvKFo7y6zq/PGVfnEfRtxstE2HxdxNe7n/FEHuRfWYEhNkoEqbVGEFg9OsAOXML8/6C7iEXXgqO8BT6lEJg4TbHZVPTfqCVwxDFrjSJ3aDm/22IjChkX9QTTDzJquA13iTNWlY7Z5yrxVhD+Pyjz3kXL1GvaphtCVp+K5P+GU=" + } + @empty_attrs %{} + + test "changeset with valid attributes" do + IO.puts(inspect(Key.changeset(%Key{}, @valid_attrs))) + assert Key.changeset(%Key{}, @valid_attrs).valid? + assert Key.changeset(%Key{}, @valid_key_attrs).valid? + end + + test "changeset with invalid attributes" do + refute Key.changeset(%Key{}, @empty_attrs).valid? + refute Key.changeset(%Key{}, @invalid_key_attrs).valid? + refute Key.changeset(%Key{}, @dsa_key_attrs).valid? + end +end diff --git a/test/support/repo_case.ex b/test/support/repo_case.ex new file mode 100644 index 0000000..73abcff --- /dev/null +++ b/test/support/repo_case.ex @@ -0,0 +1,25 @@ +defmodule Chessh.RepoCase do + use ExUnit.CaseTemplate + + using do + quote do + alias Chessh.Repo + + import Ecto + import Ecto.Query + import Chessh.RepoCase + + # and any other stuff + end + end + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Chessh.Repo) + + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Chessh.Repo, {:shared, self()}) + end + + :ok + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs index 869559e..e568cdd 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1 +1,3 @@ ExUnit.start() + +Ecto.Adapters.SQL.Sandbox.mode(Chessh.Repo, :manual)