signing_key: ensure only one key per user exists
Fixes: AkkomaGang/akkoma issue 858
This commit is contained in:
parent
2a4587f201
commit
ea2de1f28a
2 changed files with 47 additions and 2 deletions
|
@ -201,13 +201,22 @@ def fetch_remote_key(key_id) do
|
||||||
{:ok, user} <- User.get_or_fetch_by_ap_id(ap_id) do
|
{:ok, user} <- User.get_or_fetch_by_ap_id(ap_id) do
|
||||||
Logger.debug("Fetched remote key: #{ap_id}")
|
Logger.debug("Fetched remote key: #{ap_id}")
|
||||||
# store the key
|
# store the key
|
||||||
key = %__MODULE__{
|
key = %{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
public_key: public_key_pem,
|
public_key: public_key_pem,
|
||||||
key_id: key_id
|
key_id: key_id
|
||||||
}
|
}
|
||||||
|
|
||||||
Repo.insert(key, on_conflict: :replace_all, conflict_target: :key_id)
|
key_cs =
|
||||||
|
cast(%__MODULE__{}, key, [:user_id, :public_key, :key_id])
|
||||||
|
|> unique_constraint(:user_id)
|
||||||
|
|
||||||
|
Repo.insert(key_cs,
|
||||||
|
# while this should never run for local users anyway, etc make sure we really never loose privkey info!
|
||||||
|
on_conflict: {:replace_all_except, [:inserted_at, :private_key]},
|
||||||
|
# if the key owner overlaps with a distinct existing key entry, this intetionally still errros
|
||||||
|
conflict_target: :key_id
|
||||||
|
)
|
||||||
else
|
else
|
||||||
e ->
|
e ->
|
||||||
Logger.debug("Failed to fetch remote key: #{inspect(e)}")
|
Logger.debug("Failed to fetch remote key: #{inspect(e)}")
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.SigningKeyUniqueUserId do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
def up() do
|
||||||
|
# If dupes exists for any local user we do NOT want to delete the genuine privkey alongside the fake.
|
||||||
|
# Instead just filter out anything pertaining to local users, if dupes exists manual intervention
|
||||||
|
# is required anyway and index creation will just fail later (check against legacy field in users table)
|
||||||
|
dupes =
|
||||||
|
Pleroma.User.SigningKey
|
||||||
|
|> join(:inner, [s], u in Pleroma.User, on: s.user_id == u.id)
|
||||||
|
|> group_by([s], s.user_id)
|
||||||
|
|> having([], count() > 1)
|
||||||
|
|> having([_s, u], not fragment("bool_or(?)", u.local))
|
||||||
|
|> select([s], s.user_id)
|
||||||
|
|
||||||
|
# Delete existing remote duplicates
|
||||||
|
# they’ll be reinserted on the next user update
|
||||||
|
# or proactively fetched when receiving a signature from it
|
||||||
|
Pleroma.User.SigningKey
|
||||||
|
|> where([s], s.user_id in subquery(dupes))
|
||||||
|
|> Pleroma.Repo.delete_all()
|
||||||
|
|
||||||
|
drop_if_exists(index(:signing_keys, [:user_id]))
|
||||||
|
|
||||||
|
create_if_not_exists(
|
||||||
|
index(:signing_keys, [:user_id], name: :signing_keys_user_id_index, unique: true)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down() do
|
||||||
|
drop_if_exists(index(:signing_keys, [:user_id]))
|
||||||
|
create_if_not_exists(index(:signing_keys, [:user_id], name: :signing_keys_user_id_index))
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue