merged from gramsrv upstream
This commit is contained in:
parent
79c64ee916
commit
21a0856587
651 changed files with 54774 additions and 4590 deletions
|
|
@ -19,6 +19,22 @@ import (
|
|||
|
||||
const accountDeletionDelay = 7 * 24 * time.Hour
|
||||
|
||||
func (s *Service) RevenueWithdrawalPasswordState(ctx context.Context, userID int64) (domain.RevenueWithdrawalPasswordState, error) {
|
||||
if s == nil || s.lifecycle == nil || userID == 0 {
|
||||
return domain.RevenueWithdrawalPasswordState{}, fmt.Errorf("revenue withdrawal password state is unavailable")
|
||||
}
|
||||
snapshot, found, err := s.lifecycle.AccountDeletionSnapshot(ctx, userID)
|
||||
if err != nil {
|
||||
return domain.RevenueWithdrawalPasswordState{}, err
|
||||
}
|
||||
if !found {
|
||||
return domain.RevenueWithdrawalPasswordState{}, domain.ErrUserNotFound
|
||||
}
|
||||
return domain.RevenueWithdrawalPasswordState{
|
||||
HasPassword: snapshot.HasPassword, PasswordChangedAt: snapshot.PasswordUpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteAccount implements the official 2FA deletion decision. A supplied and
|
||||
// valid SRP proof always deletes immediately. Without a proof, an account whose
|
||||
// password is older than seven days and which was active during the last seven
|
||||
|
|
@ -345,17 +361,3 @@ func (s *Service) SweepDueAccountDeletions(ctx context.Context, now time.Time, l
|
|||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Service) ClaimAccountDeletionNotifications(ctx context.Context, now time.Time, limit int, lease time.Duration) ([]domain.AccountDeletionNotification, error) {
|
||||
if s == nil || s.lifecycle == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return s.lifecycle.ClaimAccountDeletionNotifications(ctx, now, limit, lease)
|
||||
}
|
||||
|
||||
func (s *Service) CompleteAccountDeletionNotification(ctx context.Context, id int64, now time.Time) error {
|
||||
if s == nil || s.lifecycle == nil {
|
||||
return nil
|
||||
}
|
||||
return s.lifecycle.CompleteAccountDeletionNotification(ctx, id, now)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,9 +141,3 @@ func (f *fakeAccountLifecycleStore) CancelAccountDeletion(_ context.Context, use
|
|||
func (*fakeAccountLifecycleStore) DueAccountDeletions(context.Context, time.Time, int) ([]domain.AccountDeletionCandidate, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (*fakeAccountLifecycleStore) ClaimAccountDeletionNotifications(context.Context, time.Time, int, time.Duration) ([]domain.AccountDeletionNotification, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (*fakeAccountLifecycleStore) CompleteAccountDeletionNotification(context.Context, int64, time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,18 +15,6 @@ import (
|
|||
"telesrv/internal/store"
|
||||
)
|
||||
|
||||
type reliablePhoneChangeDispatcher interface {
|
||||
UsesReliableDispatch() bool
|
||||
}
|
||||
|
||||
func (s *Service) PhoneChangeUsesReliableDispatch() bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
reporter, ok := s.phoneChanges.(reliablePhoneChangeDispatcher)
|
||||
return ok && reporter.UsesReliableDispatch()
|
||||
}
|
||||
|
||||
// SendChangePhoneCode 创建只允许当前 user + perm auth_key 消费的改号验证码。
|
||||
// CodeStore 会按 purpose+user+auth_key+phone 原子轮换:同一作用域的新请求
|
||||
// 立即使旧 hash 失效,避免 Android 返回重进页面时留下并行有效验证码。
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ func TestPhoneChangeScopesCodeAndPersistsDurableEvent(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("change phone after session reconnect: %v", err)
|
||||
}
|
||||
if !result.Changed || result.User.Phone != "15550012002" || result.Event.Type != domain.UpdateEventUserPhone || result.Event.Phone != "15550012002" || result.Event.Pts != 1 {
|
||||
if !result.Changed || result.User.Phone != "15550012002" {
|
||||
t.Fatalf("change result = %+v", result)
|
||||
}
|
||||
if got := f.changes.lastRequest().ExcludeAuthKeyID; got != rawAuthKeyID {
|
||||
|
|
@ -160,7 +160,7 @@ func TestPhoneChangeScopesCodeAndPersistsDurableEvent(t *testing.T) {
|
|||
t.Fatalf("new phone resolves to %+v found=%v", got, found)
|
||||
}
|
||||
events, err := f.events.ListAfter(f.ctx, f.user.ID, 0, 10)
|
||||
if err != nil || len(events) != 1 || events[0].Type != domain.UpdateEventUserPhone || events[0].Phone != "15550012002" {
|
||||
if err != nil || len(events) != 0 {
|
||||
t.Fatalf("durable events = %+v err=%v", events, err)
|
||||
}
|
||||
if _, found, _ := f.codes.Get(f.ctx, hash); found {
|
||||
|
|
@ -194,6 +194,26 @@ func TestPhoneChangeRejectsOccupiedAndCrossAuthCode(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPhoneChangeRejectsOccupiedNationalTrunkVariant(t *testing.T) {
|
||||
f := newPhoneChangeFixture(t)
|
||||
occupied, err := f.users.Create(f.ctx, domain.User{AccessHash: 103, Phone: "989981679461", FirstName: "Iran"})
|
||||
if err != nil {
|
||||
t.Fatalf("create occupied user: %v", err)
|
||||
}
|
||||
if _, _, err := f.service.SendChangePhoneCode(
|
||||
f.ctx,
|
||||
f.user.ID,
|
||||
f.authKeyID,
|
||||
77,
|
||||
"+98 0998 167 9461",
|
||||
); !errors.Is(err, domain.ErrPhoneNumberOccupied) {
|
||||
t.Fatalf("occupied trunk variant err = %v", err)
|
||||
}
|
||||
if got, found, err := f.users.ByPhone(f.ctx, "989981679461"); err != nil || !found || got.ID != occupied.ID {
|
||||
t.Fatalf("canonical owner user=%+v found=%v err=%v", got, found, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPhoneChangeWrongCodeExhaustsAttempts(t *testing.T) {
|
||||
f := newPhoneChangeFixture(t)
|
||||
hash, _, err := f.service.SendChangePhoneCode(f.ctx, f.user.ID, f.authKeyID, 77, "15550012005")
|
||||
|
|
@ -233,12 +253,12 @@ func TestPhoneChangeNewSendInvalidatesPreviousHash(t *testing.T) {
|
|||
t.Fatalf("new hash change: %v", err)
|
||||
}
|
||||
events, err := f.events.ListAfter(f.ctx, f.user.ID, 0, 10)
|
||||
if err != nil || len(events) != 1 || events[0].Type != domain.UpdateEventUserPhone {
|
||||
if err != nil || len(events) != 0 {
|
||||
t.Fatalf("events = %+v err=%v", events, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPhoneChangeConcurrentReplayAppendsOneEvent(t *testing.T) {
|
||||
func TestPhoneChangeConcurrentReplayChangesOnceWithoutPTSEvent(t *testing.T) {
|
||||
f := newPhoneChangeFixture(t)
|
||||
hash, _, err := f.service.SendChangePhoneCode(f.ctx, f.user.ID, f.authKeyID, 77, "15550012007")
|
||||
if err != nil {
|
||||
|
|
@ -273,7 +293,7 @@ func TestPhoneChangeConcurrentReplayAppendsOneEvent(t *testing.T) {
|
|||
t.Fatalf("successes=%d expired=%d", successes, expired)
|
||||
}
|
||||
events, err := f.events.ListAfter(f.ctx, f.user.ID, 0, 10)
|
||||
if err != nil || len(events) != 1 || events[0].Pts != 1 {
|
||||
if err != nil || len(events) != 0 {
|
||||
t.Fatalf("events = %+v err=%v", events, err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1053,6 +1053,12 @@ func (s *Service) SetLoginEmail(ctx context.Context, userID int64, email string)
|
|||
return s.passwords.Save(ctx, userID, settings)
|
||||
}
|
||||
|
||||
// ValidLoginEmail exposes the same normalization/shape gate to trusted
|
||||
// administrative dry-runs without duplicating the address policy.
|
||||
func (s *Service) ValidLoginEmail(email string) bool {
|
||||
return validLoginEmail(normalizeLoginEmail(email))
|
||||
}
|
||||
|
||||
// LoginEmail 返回已登录用户的登录邮箱原始地址(用于 verifyEmail 回显 emailVerified.email)。
|
||||
func (s *Service) LoginEmail(ctx context.Context, userID int64) (string, bool, error) {
|
||||
if s == nil || s.passwords == nil || userID == 0 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue