summaryrefslogtreecommitdiff
path: root/lib/chessh/schema
diff options
context:
space:
mode:
authorSimponic <loganhunt@simponic.xyz>2022-12-30 05:46:35 -0700
committerSimponic <loganhunt@simponic.xyz>2022-12-30 05:46:35 -0700
commit42425b02260d279cd9c12fb3e625282979b9e308 (patch)
tree0412bf9f39d44266cff94082c499e44e5f6e60f5 /lib/chessh/schema
parent60eea1b4ed65bc7cfce1e383dac6de9d004540eb (diff)
downloadchessh-42425b02260d279cd9c12fb3e625282979b9e308.tar.gz
chessh-42425b02260d279cd9c12fb3e625282979b9e308.zip
Add scalable session thresholds
Diffstat (limited to 'lib/chessh/schema')
-rw-r--r--lib/chessh/schema/node.ex4
-rw-r--r--lib/chessh/schema/player.ex7
-rw-r--r--lib/chessh/schema/player_session.ex52
3 files changed, 58 insertions, 5 deletions
diff --git a/lib/chessh/schema/node.ex b/lib/chessh/schema/node.ex
index 867a6e2..8834aef 100644
--- a/lib/chessh/schema/node.ex
+++ b/lib/chessh/schema/node.ex
@@ -5,7 +5,7 @@ defmodule Chessh.Node do
@primary_key {:id, :string, []}
schema "nodes" do
- field(:last_start, :utc_datetime)
+ field(:last_start, :utc_datetime_usec)
end
def changeset(node, attrs) do
@@ -18,7 +18,7 @@ defmodule Chessh.Node do
nil -> %Chessh.Node{id: node_id}
node -> node
end
- |> Chessh.Node.changeset(%{last_start: DateTime.utc_now()})
+ |> changeset(%{last_start: DateTime.utc_now()})
|> Repo.insert_or_update()
end
end
diff --git a/lib/chessh/schema/player.ex b/lib/chessh/schema/player.ex
index 4b6a324..8eaffee 100644
--- a/lib/chessh/schema/player.ex
+++ b/lib/chessh/schema/player.ex
@@ -9,11 +9,18 @@ defmodule Chessh.Player do
field(:password, :string, virtual: true)
field(:hashed_password, :string)
+ field(:authentications, :integer, default: 0)
+
has_many(:keys, Chessh.Key)
timestamps()
end
+ def authentications_changeset(player, attrs) do
+ player
+ |> cast(attrs, [:authentications])
+ end
+
def registration_changeset(player, attrs, opts \\ []) do
player
|> cast(attrs, [:username, :password])
diff --git a/lib/chessh/schema/player_session.ex b/lib/chessh/schema/player_session.ex
index 84f15ee..ce3fc1f 100644
--- a/lib/chessh/schema/player_session.ex
+++ b/lib/chessh/schema/player_session.ex
@@ -1,10 +1,12 @@
defmodule Chessh.PlayerSession do
- alias Chessh.Repo
+ alias Chessh.{Repo, Player, PlayerSession, Utils}
use Ecto.Schema
import Ecto.{Query, Changeset}
+ require Logger
schema "player_sessions" do
- field(:login, :utc_datetime)
+ field(:process, :string)
+ field(:login, :utc_datetime_usec)
belongs_to(:node, Chessh.Node, type: :string)
belongs_to(:player, Chessh.Player)
@@ -17,7 +19,7 @@ defmodule Chessh.PlayerSession do
def concurrent_sessions(player) do
Repo.aggregate(
- from(p in Chessh.PlayerSession,
+ from(p in PlayerSession,
where: p.player_id == ^player.id
),
:count
@@ -31,4 +33,48 @@ defmodule Chessh.PlayerSession do
)
)
end
+
+ def player_within_concurrent_sessions_and_satisfies(username, auth_fn) do
+ max_sessions =
+ Application.get_env(:chessh, RateLimits)
+ |> Keyword.get(:max_concurrent_user_sessions)
+
+ Repo.transaction(fn ->
+ case Repo.one(
+ from(p in Player,
+ where: p.username == ^String.Chars.to_string(username),
+ lock: "FOR UPDATE"
+ )
+ ) do
+ nil ->
+ Logger.error("Player with username #{username} does not exist")
+ send(self(), {:authed, false})
+
+ player ->
+ authed =
+ auth_fn.(player) &&
+ PlayerSession.concurrent_sessions(player) < max_sessions
+
+ Repo.insert(%PlayerSession{
+ login: DateTime.utc_now(),
+ node_id: System.fetch_env!("NODE_ID"),
+ player: player,
+ # TODO: This PID may be wrong - need to determine if this PID is shared with disconnectfun
+ process: Utils.pid_to_str(self())
+ })
+
+ player
+ |> Player.authentications_changeset(%{authentications: player.authentications + 1})
+ |> Repo.update()
+
+ send(self(), {:authed, authed})
+ end
+ end)
+
+ receive do
+ {:authed, authed} -> authed
+ after
+ 3_000 -> false
+ end
+ end
end