usernames: report reserved names as taken in the check paths too
account.checkUsername / channels.checkUsername / bots.checkUsername said a reserved name was available and only updateUsername rejected it. Add the blocklist check to peerUsernameAvailable (covers account + channel, both backends) and to bots.Service.CheckUsername, so the client shows "username is taken" immediately.
This commit is contained in:
parent
60537342d3
commit
f1c24e483c
7 changed files with 79 additions and 5 deletions
|
|
@ -128,6 +128,9 @@ func (s *ChannelStore) CheckUsername(_ context.Context, userID, channelID int64,
|
|||
return false, err
|
||||
}
|
||||
usernameLower := strings.ToLower(strings.TrimSpace(strings.TrimPrefix(username, "@")))
|
||||
if s.usernameRegistry != nil && s.usernameRegistry.nameReserved(usernameLower) {
|
||||
return false, nil
|
||||
}
|
||||
for id, channel := range s.channels {
|
||||
if channel.Deleted || channel.Username == "" {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ func (s *CollectibleUsernameStore) WithReservedUsernames(reserved *ReservedUsern
|
|||
return s
|
||||
}
|
||||
|
||||
func (s *CollectibleUsernameStore) nameReservedLocked(usernameLower string) bool {
|
||||
if s.reserved == nil {
|
||||
// nameReserved reports whether a name is on the operator blocklist. It touches
|
||||
// only s.reserved (its own lock), so it is safe from any context.
|
||||
func (s *CollectibleUsernameStore) nameReserved(usernameLower string) bool {
|
||||
if s == nil || s.reserved == nil {
|
||||
return false
|
||||
}
|
||||
r, _ := s.reserved.IsReserved(context.Background(), usernameLower)
|
||||
|
|
@ -119,7 +121,7 @@ func (s *CollectibleUsernameStore) SetEditableUsername(_ context.Context, peer d
|
|||
return false, domain.ErrUsernameInvalid
|
||||
}
|
||||
key := strings.ToLower(username)
|
||||
if s.nameReservedLocked(key) {
|
||||
if s.nameReserved(key) {
|
||||
return false, domain.ErrUsernameOccupied
|
||||
}
|
||||
if existing, ok := s.registry[key]; ok {
|
||||
|
|
@ -334,7 +336,7 @@ func (s *CollectibleUsernameStore) MintCollectibleUsername(_ context.Context, re
|
|||
if _, ok := s.registry[key]; ok {
|
||||
return domain.CollectibleUsername{}, false, domain.ErrUsernameOccupied
|
||||
}
|
||||
if s.nameReservedLocked(key) {
|
||||
if s.nameReserved(key) {
|
||||
return domain.CollectibleUsername{}, false, domain.ErrUsernameOccupied
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
|
|
|
|||
37
internal/store/memory/reserved_username_test.go
Normal file
37
internal/store/memory/reserved_username_test.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
func TestCheckUsernameReportsReservedAsTaken(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
reserved := NewReservedUsernameStore()
|
||||
if _, err := reserved.ReserveUsername(ctx, "support", "official", "ops"); err != nil {
|
||||
t.Fatalf("seed reserve: %v", err)
|
||||
}
|
||||
registry := NewCollectibleUsernameStore().WithReservedUsernames(reserved)
|
||||
|
||||
users := NewUserStore()
|
||||
users.AttachUsernameRegistry(registry)
|
||||
u, _ := users.Create(ctx, domain.User{AccessHash: 1, Phone: "15550001000", FirstName: "A"})
|
||||
if ok, err := users.CheckUsername(ctx, u.ID, "support"); err != nil || ok {
|
||||
t.Fatalf("CheckUsername(reserved) = %v, %v; want false, nil", ok, err)
|
||||
}
|
||||
if ok, err := users.CheckUsername(ctx, u.ID, "freename"); err != nil || !ok {
|
||||
t.Fatalf("CheckUsername(free) = %v, %v; want true, nil", ok, err)
|
||||
}
|
||||
|
||||
channels := NewChannelStore()
|
||||
channels.AttachUsernameRegistry(registry)
|
||||
created, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{CreatorUserID: u.ID, Title: "C", Megagroup: true, Date: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("create channel: %v", err)
|
||||
}
|
||||
if ok, err := channels.CheckUsername(ctx, u.ID, created.Channel.ID, "support"); err != nil || ok {
|
||||
t.Fatalf("channel CheckUsername(reserved) = %v, %v; want false, nil", ok, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -160,6 +160,9 @@ func (s *UserStore) CheckUsername(_ context.Context, userID int64, username stri
|
|||
if username == "" {
|
||||
return true, nil
|
||||
}
|
||||
if s.usernameRegistry != nil && s.usernameRegistry.nameReserved(username) {
|
||||
return false, nil
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
for id, u := range s.byID {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,11 @@ func usernameReservedTx(ctx context.Context, db sqlcgen.DBTX, usernameLower stri
|
|||
}
|
||||
|
||||
func peerUsernameAvailable(ctx context.Context, db sqlcgen.DBTX, usernameLower, peerType string, peerID int64) (bool, error) {
|
||||
if reserved, err := usernameReservedTx(ctx, db, usernameLower); err != nil {
|
||||
return false, err
|
||||
} else if reserved {
|
||||
return false, nil
|
||||
}
|
||||
owner, found, err := getPeerUsernameOwner(ctx, db, usernameLower, false)
|
||||
if err != nil || !found {
|
||||
return !found, err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue