admin: allow shorter operator-assigned usernames, add reference docs
Lower the username floor to 3 characters on the admin-only write path (account/channel set-username), separate from the 5-character self-service minimum, since a deliberately short operator handle isn't the squatting the higher floor guards against. Channel admin username assignment previously had no length/format validation at all; this adds it. Also adds three reference docs: the client's deep-link (tg://, owpg://) surface, an OpenAPI 3.1 spec for both admin HTTP APIs (built from source, not the stale docs/admin-panel-api.en.md), and a starter reserved-username blocklist.
This commit is contained in:
parent
6af4576fed
commit
c39011e542
5 changed files with 3861 additions and 5 deletions
|
|
@ -635,11 +635,19 @@ func (s *Service) AdminSetSettings(ctx context.Context, channelID int64, patch d
|
|||
return s.channels.SetChannelAdminSettings(ctx, channelID, patch)
|
||||
}
|
||||
|
||||
// AdminSetUsername force-sets or clears a channel username through the admin path.
|
||||
// AdminSetUsername force-sets or clears a channel username through the admin
|
||||
// path. Unlike UpdateUsername it does not consult config.ReservedUsernames --
|
||||
// an operator who deliberately reserved a word still needs to be able to hand
|
||||
// it to a specific channel -- but format/length validity is still enforced,
|
||||
// just against the lower minChannelUsernameLenAdmin floor.
|
||||
func (s *Service) AdminSetUsername(ctx context.Context, channelID int64, username string) (domain.Channel, error) {
|
||||
if s == nil || s.channels == nil || channelID == 0 {
|
||||
return domain.Channel{}, domain.ErrChannelInvalid
|
||||
}
|
||||
username = normalizeChannelUsername(username)
|
||||
if username != "" && !validChannelUsernameMinLen(username, minChannelUsernameLenAdmin) {
|
||||
return domain.Channel{}, domain.ErrUsernameInvalid
|
||||
}
|
||||
return s.channels.SetChannelUsernameAdmin(ctx, channelID, username)
|
||||
}
|
||||
|
||||
|
|
@ -2520,8 +2528,20 @@ func normalizeChannelUsername(username string) string {
|
|||
return strings.TrimSpace(username)
|
||||
}
|
||||
|
||||
// minChannelUsernameLenAdmin is the floor for an operator-assigned channel
|
||||
// username via AdminSetUsername (the admin API/panel) -- shorter than what
|
||||
// self-service channels.updateUsername allows, mirroring
|
||||
// users.minUsernameLenAdmin: a deliberate short handle assigned by an
|
||||
// operator is not the squatting the higher self-service minimum guards
|
||||
// against.
|
||||
const minChannelUsernameLenAdmin = 3
|
||||
|
||||
func validChannelUsername(username string) bool {
|
||||
if len(username) < 5 || len(username) > 32 {
|
||||
return validChannelUsernameMinLen(username, 5)
|
||||
}
|
||||
|
||||
func validChannelUsernameMinLen(username string, minLen int) bool {
|
||||
if len(username) < minLen || len(username) > 32 {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(username); i++ {
|
||||
|
|
|
|||
|
|
@ -116,7 +116,13 @@ func WithReservedUsernames(names []string) Option {
|
|||
}
|
||||
|
||||
const (
|
||||
minUsernameLen = 5
|
||||
minUsernameLen = 5
|
||||
// minUsernameLenAdmin is the floor for an operator-assigned username via
|
||||
// UpdateUsernameAdmin (the admin API/panel) -- shorter than what self-service
|
||||
// account.updateUsername allows, since a deliberate short handle assigned by
|
||||
// an operator (e.g. a 3-4 char official/brand account) is not the squatting
|
||||
// the higher self-service minimum guards against.
|
||||
minUsernameLenAdmin = 3
|
||||
maxUsernameLen = 32
|
||||
maxProfileNameRunes = 64
|
||||
// bio 长度双档,对齐 appConfig about_length_limit_default=70 /
|
||||
|
|
@ -383,7 +389,13 @@ func (s *Service) updateUsername(ctx context.Context, userID int64, username str
|
|||
return s.projectOne(ctx, self.ID, self)
|
||||
}
|
||||
if username != "" {
|
||||
if !validUsername(username) || (enforceReserved && s.reserved.Contains(username)) {
|
||||
minLen := minUsernameLen
|
||||
if !enforceReserved {
|
||||
// enforceReserved=false is exactly the admin-bypass path
|
||||
// (UpdateUsernameAdmin) -- see minUsernameLenAdmin's doc comment.
|
||||
minLen = minUsernameLenAdmin
|
||||
}
|
||||
if !validUsernameMinLen(username, minLen) || (enforceReserved && s.reserved.Contains(username)) {
|
||||
return domain.User{}, domain.ErrUsernameInvalid
|
||||
}
|
||||
var (
|
||||
|
|
@ -1040,7 +1052,11 @@ func normalizeUsername(username string) string {
|
|||
}
|
||||
|
||||
func validUsername(username string) bool {
|
||||
if len(username) < minUsernameLen || len(username) > maxUsernameLen {
|
||||
return validUsernameMinLen(username, minUsernameLen)
|
||||
}
|
||||
|
||||
func validUsernameMinLen(username string, minLen int) bool {
|
||||
if len(username) < minLen || len(username) > maxUsernameLen {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(username); i++ {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue