defmodule OhMyExOhIrcd.IRC.Klines do
@moduledoc """
K-line / G-line ban context. Since everything is stored in the
cluster-wide Postgres instance, K-lines and G-lines are functionally
identical in this implementation — both are persisted host-mask bans
enforced on every node.
Enforcement runs at registration time (`OhMyExOhIrcd.IRC.Connection`
calls `match/1` once nick/user/host are all known) and on demand for
existing connections (`disconnect_matching!/1` after KLINE).
"""
import Ecto.Query
alias OhMyExOhIrcd.Repo
alias OhMyExOhIrcd.IRC.Klines.Kline
@doc """
Add a new K-line. `duration_seconds` is optional — `nil` means
permanent. `set_by` is the oper's nick.
"""
def add(mask, set_by, opts \\ []) do
expires_at =
case Keyword.get(opts, :duration_seconds) do
nil -> nil
secs when is_integer(secs) and secs > 0 -> DateTime.add(DateTime.utc_now(), secs)
end
attrs = %{
mask: mask,
set_by: set_by,
reason: Keyword.get(opts, :reason, ""),
expires_at: expires_at
}
%Kline{}
|> Kline.changeset(attrs)
|> Repo.insert()
end
@doc "Remove the K-line matching `mask` exactly. Returns the number of rows deleted."
def remove(mask) do
{n, _} = Repo.delete_all(from k in Kline, where: k.mask == ^mask)
n
end
@doc "All active (non-expired) K-lines."
def list_active do
now = DateTime.utc_now()
Repo.all(
from k in Kline,
where: is_nil(k.expires_at) or k.expires_at > ^now,
order_by: [asc: k.inserted_at]
)
end
@doc """
Match `user_at_host` (a `user@host` string built from the joining
client's prefix) against every active K-line. Returns the first
matching record or `nil`.
"""
def match(user_at_host) do
Enum.find(list_active(), fn k -> match_glob?(user_at_host, k.mask) end)
rescue
_ -> nil
end
defp match_glob?(string, mask) do
pattern =
mask
|> Regex.escape()
|> String.replace("\\*", ".*")
|> String.replace("\\?", ".")
Regex.match?(~r/\A#{pattern}\z/i, string)
end
end
neiam
/oh-my-ex-oh-ircd
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
2.1 KiB · text
History
4b6fe3b