Initial commit!
This commit is contained in:
commit
9cdfb6eb9c
4
.formatter.exs
Normal file
4
.formatter.exs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where third-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Also ignore archive artifacts (built via "mix archive.build").
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
|
server-*.tar
|
||||||
|
|
||||||
|
# Temporary files, for example, from tests.
|
||||||
|
/tmp/
|
||||||
|
|
||||||
|
# Private files, like configuration secrets or keys.
|
||||||
|
/priv/
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Server
|
||||||
|
|
||||||
|
**TODO: Add description**
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||||
|
by adding `server` to your list of dependencies in `mix.exs`:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def deps do
|
||||||
|
[
|
||||||
|
{:server, "~> 0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||||
|
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||||
|
be found at <https://hexdocs.pm/server>.
|
||||||
|
|
1
config/.gitignore
vendored
Normal file
1
config/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
prod.exs
|
11
config/config.exs
Normal file
11
config/config.exs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import Config
|
||||||
|
|
||||||
|
config :esshd,
|
||||||
|
enabled: true,
|
||||||
|
priv_dir: Path.join(Path.dirname(__DIR__), "priv/keys"),
|
||||||
|
handler: {Chessh.Shell, :on_shell, 4},
|
||||||
|
port: 42069,
|
||||||
|
public_key_authenticator: Chessh.Auth.KeyAuthenticator,
|
||||||
|
password_authenticator: Chessh.Auth.PasswordAuthenticator
|
||||||
|
|
||||||
|
import_config "#{config_env()}.exs"
|
9
config/dev.exs
Normal file
9
config/dev.exs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import Config
|
||||||
|
|
||||||
|
config :chessh, Chessh.Repo,
|
||||||
|
database: "chessh",
|
||||||
|
username: "postgres",
|
||||||
|
password: "postgres",
|
||||||
|
hostname: "localhost"
|
||||||
|
|
||||||
|
config :chessh, ecto_repos: [Chessh.Repo]
|
9
config/test.exs
Normal file
9
config/test.exs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import Config
|
||||||
|
|
||||||
|
config :chessh, Chessh.Repo,
|
||||||
|
database: "chessh-test",
|
||||||
|
username: "postgres",
|
||||||
|
password: "postgres",
|
||||||
|
hostname: "localhost"
|
||||||
|
|
||||||
|
config :chessh, ecto_repos: [Chessh.Repo]
|
8
lib/auth/keys.ex
Normal file
8
lib/auth/keys.ex
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
defmodule Chessh.Auth.KeyAuthenticator do
|
||||||
|
use Sshd.PublicKeyAuthenticator
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def authenticate(_, _, _) do
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
7
lib/auth/password.ex
Normal file
7
lib/auth/password.ex
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
defmodule Chessh.Auth.PasswordAuthenticator do
|
||||||
|
use Sshd.PasswordAuthenticator
|
||||||
|
|
||||||
|
def authenticate(_username, _password) do
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
7
lib/chessh.ex
Normal file
7
lib/chessh.ex
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
defmodule Chessh do
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def hello() do
|
||||||
|
:world
|
||||||
|
end
|
||||||
|
end
|
9
lib/chessh/application.ex
Normal file
9
lib/chessh/application.ex
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
defmodule Chessh.Application do
|
||||||
|
use Application
|
||||||
|
|
||||||
|
def start(_, _) do
|
||||||
|
children = [Chessh.Repo]
|
||||||
|
opts = [strategy: :one_for_one, name: Chessh.Supervisor]
|
||||||
|
Supervisor.start_link(children, opts)
|
||||||
|
end
|
||||||
|
end
|
18
lib/chessh/shell.ex
Normal file
18
lib/chessh/shell.ex
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
defmodule Chessh.Shell do
|
||||||
|
use Sshd.ShellHandler
|
||||||
|
|
||||||
|
def on_shell(_username, _public_key, _ip, _port) do
|
||||||
|
:ok =
|
||||||
|
IO.puts(
|
||||||
|
"Interactive example SSH shell - type exit ENTER to quit and it is running on #{inspect(self())}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_connect(_username, _ip, _port, _method) do
|
||||||
|
Logger.debug("Connection established")
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_disconnect(_username, _ip, _port) do
|
||||||
|
Logger.debug("Connection disestablished")
|
||||||
|
end
|
||||||
|
end
|
81
lib/schema/player.ex
Normal file
81
lib/schema/player.ex
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
defmodule Chessh.Player do
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
@derive {Inspect, except: [:password]}
|
||||||
|
schema "players" do
|
||||||
|
field(:username, :string)
|
||||||
|
|
||||||
|
field(:password, :string, virtual: true)
|
||||||
|
field(:hashed_password, :string)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def registration_changeset(user, attrs, opts \\ []) do
|
||||||
|
user
|
||||||
|
|> cast(attrs, [:username, :password])
|
||||||
|
|> validate_username()
|
||||||
|
|> validate_password(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def password_changeset(user, attrs, opts \\ []) do
|
||||||
|
user
|
||||||
|
|> cast(attrs, [:password])
|
||||||
|
|> validate_confirmation(:password, message: "does not match password")
|
||||||
|
|> validate_password(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_password?(%Chessh.Player{hashed_password: hashed_password}, password)
|
||||||
|
when is_binary(hashed_password) and byte_size(password) > 0 do
|
||||||
|
Bcrypt.verify_pass(password, hashed_password)
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_password?(_, _) do
|
||||||
|
Bcrypt.no_user_verify()
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_current_password(changeset, password) do
|
||||||
|
if valid_password?(changeset.data, password) do
|
||||||
|
changeset
|
||||||
|
else
|
||||||
|
add_error(changeset, :current_password, "is not valid")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_username(changeset) do
|
||||||
|
changeset
|
||||||
|
|> validate_required([:username])
|
||||||
|
|> validate_length(:username, min: 2, max: 12)
|
||||||
|
|> validate_format(:username, ~r/^[a-zA-Z0-9_\-]*$/,
|
||||||
|
message: "only letters, numbers, underscores, and hyphens allowed"
|
||||||
|
)
|
||||||
|
|> unique_constraint(:username)
|
||||||
|
|> lowercase(:username)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_password(changeset, opts) do
|
||||||
|
changeset
|
||||||
|
|> validate_required([:password])
|
||||||
|
|> validate_length(:password, min: 8, max: 80)
|
||||||
|
|> maybe_hash_password(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_hash_password(changeset, opts) do
|
||||||
|
hash_password? = Keyword.get(opts, :hash_password, true)
|
||||||
|
password = get_change(changeset, :password)
|
||||||
|
|
||||||
|
if hash_password? && password && changeset.valid? do
|
||||||
|
changeset
|
||||||
|
|> put_change(:hashed_password, Bcrypt.hash_pwd_salt(password))
|
||||||
|
|> delete_change(:password)
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp lowercase(changeset, field) do
|
||||||
|
Map.update!(changeset, field, &String.downcase/1)
|
||||||
|
end
|
||||||
|
end
|
5
lib/schema/repo.ex
Normal file
5
lib/schema/repo.ex
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
defmodule Chessh.Repo do
|
||||||
|
use Ecto.Repo,
|
||||||
|
otp_app: :chessh,
|
||||||
|
adapter: Ecto.Adapters.Postgres
|
||||||
|
end
|
32
mix.exs
Normal file
32
mix.exs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
defmodule Chessh.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :chessh,
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.14",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
extra_applications: [:esshd, :logger]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
{:chess, "~> 0.4.1"},
|
||||||
|
{:esshd, "~> 0.2.1"},
|
||||||
|
{:ecto, "~> 3.9"},
|
||||||
|
{:ecto_sql, "~> 3.9"},
|
||||||
|
{:postgrex, "~> 0.16.5"},
|
||||||
|
{:bcrypt_elixir, "~> 3.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
14
mix.lock
Normal file
14
mix.lock
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
%{
|
||||||
|
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.0.1", "9be815469e6bfefec40fa74658ecbbe6897acfb57614df1416eeccd4903f602c", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "486bb95efb645d1efc6794c1ddd776a186a9a713abf06f45708a6ce324fb96cf"},
|
||||||
|
"chess": {:hex, :chess, "0.4.1", "34c04abed2db81e0c56476c8e74fd85ef4e1bae23a4cd528e0ce8a052ada976f", [:mix], [], "hexpm", "692e0def99dc25af4af2413839a4605a2a0a713c2646f9afcf3a47c76a6de43d"},
|
||||||
|
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
|
||||||
|
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
|
||||||
|
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
|
||||||
|
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||||
|
"ecto": {:hex, :ecto, "3.9.2", "017db3bc786ff64271108522c01a5d3f6ba0aea5c84912cfb0dd73bf13684108", [: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 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "21466d5177e09e55289ac7eade579a642578242c7a3a9f91ad5c6583337a9d15"},
|
||||||
|
"ecto_sql": {:hex, :ecto_sql, "3.9.1", "9bd5894eecc53d5b39d0c95180d4466aff00e10679e13a5cfa725f6f85c03c22", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.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", "5fd470a4fff2e829bbf9dcceb7f3f9f6d1e49b4241e802f614de6b8b67c51118"},
|
||||||
|
"elixir_make": {:hex, :elixir_make, "0.7.2", "e83548b0500e654d1a595f1134af4862a2e92ec3282ec4c2a17641e9aa45ee73", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "05fb44abf9582381c2eb1b73d485a55288c581071de0ee3ee1084ee69d6a8e5f"},
|
||||||
|
"esshd": {:hex, :esshd, "0.2.1", "cded6a329c32bc3b3c15828bcd34203227bbef310db3c39a6f3c55cf5b29cd34", [:mix], [], "hexpm", "b058b56af53aba1c23522d72a3c39ab7f302e509af1c0ba1a748f00d93053c4d"},
|
||||||
|
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
|
||||||
|
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
|
||||||
|
}
|
8
test/server_test.exs
Normal file
8
test/server_test.exs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
defmodule ChesshTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest Chessh
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert Chessh.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
@ -0,0 +1 @@
|
|||||||
|
ExUnit.start()
|
Loading…
Reference in New Issue
Block a user