usernames: operator reserved-username blocklist
A plain blocklist for names like @support - separate from the collectible
system, so a reservation has no owner, no price and no "bought on Fragment"
badge.
- reserved_usernames table + migration.
- Enforced in replacePeerUsernameTx (the single editable-username write point:
account.updateUsername, channels.updateUsername, @BotFather /setusername) and
in the collectible mint path; a reserved name returns USERNAME_OCCUPIED.
- admin.Service: ReserveUsername / UnreserveUsername (journalled commands) and
the ReservedUsernames listing.
- adminapi: /v1/reserved-usernames{,/reserve,/unreserve}.
- telesrv-admin panel + a "Reserved Usernames" page in the web UI (dist rebuilt).
- Postgres and in-memory store implementations; the memory registry gains an
optional reserved-name check so tests exercise the same rule.
This commit is contained in:
parent
22846e340f
commit
2bdb1ecf37
21 changed files with 843 additions and 6 deletions
|
|
@ -65,6 +65,21 @@ func getPeerUsernameOwner(ctx context.Context, db sqlcgen.DBTX, usernameLower st
|
|||
return owner, true, nil
|
||||
}
|
||||
|
||||
// usernameReservedTx reports whether a name is on the operator blocklist. It is
|
||||
// consulted before every editable-username write and before a collectible mint.
|
||||
func usernameReservedTx(ctx context.Context, db sqlcgen.DBTX, usernameLower string) (bool, error) {
|
||||
if usernameLower == "" {
|
||||
return false, nil
|
||||
}
|
||||
var exists bool
|
||||
if err := db.QueryRow(ctx,
|
||||
`SELECT EXISTS (SELECT 1 FROM reserved_usernames WHERE username_lower = $1)`,
|
||||
usernameLower).Scan(&exists); err != nil {
|
||||
return false, fmt.Errorf("check reserved username: %w", err)
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func peerUsernameAvailable(ctx context.Context, db sqlcgen.DBTX, usernameLower, peerType string, peerID int64) (bool, error) {
|
||||
owner, found, err := getPeerUsernameOwner(ctx, db, usernameLower, false)
|
||||
if err != nil || !found {
|
||||
|
|
@ -115,6 +130,11 @@ WHERE peer_type = $1
|
|||
// otherwise account.updateUsername would silently release a minted asset.
|
||||
func replacePeerUsernameTx(ctx context.Context, tx pgx.Tx, peerType string, peerID int64, username, usernameLower string) error {
|
||||
if usernameLower != "" {
|
||||
if reserved, err := usernameReservedTx(ctx, tx, usernameLower); err != nil {
|
||||
return err
|
||||
} else if reserved {
|
||||
return domain.ErrUsernameOccupied
|
||||
}
|
||||
owner, found, err := getPeerUsernameOwner(ctx, tx, usernameLower, true)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue