defmodule OhMyExOhIrcd.IRC.Opers do
@moduledoc """
IRC operator authentication.
Opers are configured statically in `config.exs` (or runtime config)
under `:opers`, keyed by login name. Passwords are stored as bcrypt
hashes — generate one with `Bcrypt.hash_pwd_salt("yourpassword")`.
Example:
config :oh_my_ex_oh_ircd, :opers, %{
"admin" => %{
password_hash: "$2b$12$...",
host_mask: "*@*" # restrict where this oper can log in from
}
}
`host_mask` is an IRC glob (`*`, `?`) applied to the connection's
`user@host` part. `"*@*"` means anywhere.
"""
@spec verify(String.t(), String.t(), String.t()) ::
:ok | {:error, :no_such_oper | :bad_password | :bad_host}
def verify(name, password, user_at_host) do
case lookup(name) do
nil ->
# Avoid timing-leak: still run a bcrypt verify against a dummy hash.
_ = Bcrypt.no_user_verify()
{:error, :no_such_oper}
%{password_hash: hash, host_mask: mask} ->
cond do
not match_glob?(user_at_host, mask) -> {:error, :bad_host}
not Bcrypt.verify_pass(password, hash) -> {:error, :bad_password}
true -> :ok
end
end
end
defp lookup(name) when is_binary(name) do
Application.get_env(:oh_my_ex_oh_ircd, :opers, %{})
|> Map.get(name)
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.
1.5 KiB · text
History
4b6fe3b