feat: sync langpack and auth updates
Sync telesrv commits 49f9bab and 04d9563 into the public mirror. Exclude private docs and runtime key material per sync rules.
This commit is contained in:
parent
6af61f26ba
commit
1292540350
244 changed files with 1552172 additions and 27668 deletions
|
|
@ -34,7 +34,7 @@ func (s *testMailSender) Deliver(_ context.Context, req otpdelivery.Request) (ot
|
|||
return otpdelivery.Result{}, nil
|
||||
}
|
||||
|
||||
func TestConfiguredEmailLoginSendsAndLimitsAttempts(t *testing.T) {
|
||||
func TestConfiguredEmailLoginSharesAttemptsAcrossOfficialCodeCarriers(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
users := memory.NewUserStore()
|
||||
authz := memory.NewAuthorizationStore()
|
||||
|
|
@ -76,14 +76,14 @@ func TestConfiguredEmailLoginSendsAndLimitsAttempts(t *testing.T) {
|
|||
if bad2 == bad1 {
|
||||
bad2 = wrongCode(sender.code, '2')
|
||||
}
|
||||
if _, _, _, err := svc.SignInWithEmail(ctx, domain.Authorization{}, "+15550009101", hash, bad1); !errors.Is(err, ErrCodeInvalid) {
|
||||
t.Fatalf("first bad SignInWithEmail err = %v, want ErrCodeInvalid", err)
|
||||
if _, _, _, err := svc.SignIn(ctx, domain.Authorization{}, "+15550009101", hash, bad1); !errors.Is(err, ErrCodeInvalid) {
|
||||
t.Fatalf("first bad WebK SignIn err = %v, want ErrCodeInvalid", err)
|
||||
}
|
||||
if _, _, _, err := svc.SignInWithEmail(ctx, domain.Authorization{}, "+15550009101", hash, bad2); !errors.Is(err, ErrCodeInvalid) {
|
||||
t.Fatalf("second bad SignInWithEmail err = %v, want ErrCodeInvalid", err)
|
||||
t.Fatalf("second bad native SignInWithEmail err = %v, want ErrCodeInvalid", err)
|
||||
}
|
||||
if _, _, _, err := svc.SignInWithEmail(ctx, domain.Authorization{}, "+15550009101", hash, sender.code); !errors.Is(err, ErrCodeExpired) {
|
||||
t.Fatalf("SignInWithEmail after max attempts err = %v, want ErrCodeExpired", err)
|
||||
if _, _, _, err := svc.SignIn(ctx, domain.Authorization{}, "+15550009101", hash, sender.code); !errors.Is(err, ErrCodeExpired) {
|
||||
t.Fatalf("WebK SignIn after shared max attempts err = %v, want ErrCodeExpired", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,40 +104,160 @@ func wrongCode(code string, digit byte) string {
|
|||
return string(out)
|
||||
}
|
||||
|
||||
func TestConfiguredEmailLoginAcceptsCorrectCode(t *testing.T) {
|
||||
func TestConfiguredEmailLoginAcceptsOfficialCodeCarriers(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
phone string
|
||||
email string
|
||||
webK bool
|
||||
}{
|
||||
{name: "webk_phone_code", phone: "15550009102", email: "webk@example.test", webK: true},
|
||||
{name: "native_email_verification", phone: "15550009103", email: "native@example.test"},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
users := memory.NewUserStore()
|
||||
authz := memory.NewAuthorizationStore()
|
||||
u, err := users.Create(ctx, domain.User{Phone: tc.phone, FirstName: "Email"})
|
||||
if err != nil {
|
||||
t.Fatalf("create user: %v", err)
|
||||
}
|
||||
emails := &testLoginEmailStore{emails: map[string]string{tc.phone: tc.email}}
|
||||
sender := &testMailSender{}
|
||||
appDelivery := &captureLoginCodeDelivery{}
|
||||
var key [8]byte
|
||||
key[0] = byte(0x91 + i)
|
||||
svc := NewService(users, authz, memory.NewCodeStore(), nil, nil, "12345",
|
||||
WithLoginCodeDelivery(appDelivery),
|
||||
WithLoginEmail(LoginEmailOptions{
|
||||
Enabled: true,
|
||||
CodeLength: 6,
|
||||
Store: emails,
|
||||
Sender: sender,
|
||||
}))
|
||||
|
||||
hash, err := svc.SendCode(ctx, tc.phone)
|
||||
if err != nil {
|
||||
t.Fatalf("SendCode: %v", err)
|
||||
}
|
||||
if len(appDelivery.requests) != 1 || appDelivery.requests[0].Code != sender.code {
|
||||
t.Fatalf("App-code delivery=%+v, want same email code", appDelivery.requests)
|
||||
}
|
||||
|
||||
var got domain.User
|
||||
var needSignUp bool
|
||||
if tc.webK {
|
||||
if _, _, _, err := svc.SignIn(ctx, domain.Authorization{AuthKeyID: key}, tc.phone, hash, "12345"); !errors.Is(err, ErrCodeInvalid) {
|
||||
t.Fatalf("WebK development code err=%v, want ErrCodeInvalid for random email channel", err)
|
||||
}
|
||||
got, _, needSignUp, err = svc.SignIn(ctx, domain.Authorization{AuthKeyID: key}, tc.phone, hash, sender.code)
|
||||
} else {
|
||||
got, _, needSignUp, err = svc.SignInWithEmail(ctx, domain.Authorization{AuthKeyID: key}, tc.phone, hash, sender.code)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("sign in: %v", err)
|
||||
}
|
||||
if needSignUp || got.ID != u.ID {
|
||||
t.Fatalf("sign in got user=%d needSignUp=%v, want %d/false", got.ID, needSignUp, u.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfiguredEmailLoginViaWebKStillHonorsTwoFactor(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
users := memory.NewUserStore()
|
||||
authz := memory.NewAuthorizationStore()
|
||||
u, err := users.Create(ctx, domain.User{Phone: "15550009102", FirstName: "Email"})
|
||||
passwords := memory.NewPasswordStore()
|
||||
u, err := users.Create(ctx, domain.User{Phone: "15550009104", FirstName: "Email"})
|
||||
if err != nil {
|
||||
t.Fatalf("create user: %v", err)
|
||||
}
|
||||
emails := &testLoginEmailStore{emails: map[string]string{"15550009102": "bob@example.test"}}
|
||||
if err := passwords.Save(ctx, u.ID, domain.PasswordSettings{HasPassword: true}); err != nil {
|
||||
t.Fatalf("save password settings: %v", err)
|
||||
}
|
||||
sender := &testMailSender{}
|
||||
appDelivery := &captureLoginCodeDelivery{}
|
||||
var key [8]byte
|
||||
key[0] = 0x91
|
||||
svc := NewService(users, authz, memory.NewCodeStore(), nil, nil, "12345",
|
||||
WithLoginCodeDelivery(appDelivery),
|
||||
WithPasswords(passwords),
|
||||
WithLoginCodeDelivery(&captureLoginCodeDelivery{}),
|
||||
WithLoginEmail(LoginEmailOptions{
|
||||
Enabled: true,
|
||||
CodeLength: 5,
|
||||
Store: emails,
|
||||
CodeLength: 6,
|
||||
Store: &testLoginEmailStore{emails: map[string]string{u.Phone: "2fa@example.test"}},
|
||||
Sender: sender,
|
||||
}))
|
||||
var key [8]byte
|
||||
key[0] = 0x94
|
||||
|
||||
hash, err := svc.SendCode(ctx, "+15550009102")
|
||||
hash, err := svc.SendCode(ctx, u.Phone)
|
||||
if err != nil {
|
||||
t.Fatalf("SendCode: %v", err)
|
||||
}
|
||||
if len(appDelivery.requests) != 1 || appDelivery.requests[0].Code != sender.code {
|
||||
t.Fatalf("App-code delivery=%+v, want same email code", appDelivery.requests)
|
||||
got, _, _, err := svc.SignIn(ctx, domain.Authorization{AuthKeyID: key}, u.Phone, hash, sender.code)
|
||||
if !errors.Is(err, domain.ErrSessionPasswordNeeded) {
|
||||
t.Fatalf("WebK email SignIn err=%v, want ErrSessionPasswordNeeded", err)
|
||||
}
|
||||
got, _, needSignUp, err := svc.SignInWithEmail(ctx, domain.Authorization{AuthKeyID: key}, "+15550009102", hash, sender.code)
|
||||
if err != nil {
|
||||
t.Fatalf("SignInWithEmail: %v", err)
|
||||
if got.ID != u.ID {
|
||||
t.Fatalf("WebK email SignIn user=%d, want pending 2FA user %d", got.ID, u.ID)
|
||||
}
|
||||
if needSignUp || got.ID != u.ID {
|
||||
t.Fatalf("SignInWithEmail got user=%d needSignUp=%v, want %d/false", got.ID, needSignUp, u.ID)
|
||||
if bound, found, err := svc.UserID(ctx, key); err != nil || found || bound != 0 {
|
||||
t.Fatalf("UserID after WebK email SignIn with 2FA=%d found=%v err=%v, want not-found", bound, found, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfiguredEmailLoginHasSingleConsumerAcrossOfficialCodeCarriers(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
users := memory.NewUserStore()
|
||||
u, err := users.Create(ctx, domain.User{Phone: "15550009105", FirstName: "Email"})
|
||||
if err != nil {
|
||||
t.Fatalf("create user: %v", err)
|
||||
}
|
||||
sender := &testMailSender{}
|
||||
svc := NewService(users, memory.NewAuthorizationStore(), memory.NewCodeStore(), nil, nil, "12345",
|
||||
WithLoginCodeDelivery(&captureLoginCodeDelivery{}),
|
||||
WithLoginEmail(LoginEmailOptions{
|
||||
Enabled: true,
|
||||
CodeLength: 6,
|
||||
Store: &testLoginEmailStore{emails: map[string]string{u.Phone: "race@example.test"}},
|
||||
Sender: sender,
|
||||
}))
|
||||
hash, err := svc.SendCode(ctx, u.Phone)
|
||||
if err != nil {
|
||||
t.Fatalf("SendCode: %v", err)
|
||||
}
|
||||
|
||||
start := make(chan struct{})
|
||||
results := make(chan error, 2)
|
||||
var webKKey, nativeKey [8]byte
|
||||
webKKey[0] = 0x95
|
||||
nativeKey[0] = 0x96
|
||||
go func() {
|
||||
<-start
|
||||
_, _, _, err := svc.SignIn(ctx, domain.Authorization{AuthKeyID: webKKey}, u.Phone, hash, sender.code)
|
||||
results <- err
|
||||
}()
|
||||
go func() {
|
||||
<-start
|
||||
_, _, _, err := svc.SignInWithEmail(ctx, domain.Authorization{AuthKeyID: nativeKey}, u.Phone, hash, sender.code)
|
||||
results <- err
|
||||
}()
|
||||
close(start)
|
||||
|
||||
accepted, expired := 0, 0
|
||||
for range 2 {
|
||||
err := <-results
|
||||
switch {
|
||||
case err == nil:
|
||||
accepted++
|
||||
case errors.Is(err, ErrCodeExpired):
|
||||
expired++
|
||||
default:
|
||||
t.Fatalf("concurrent sign in err=%v, want nil or ErrCodeExpired", err)
|
||||
}
|
||||
}
|
||||
if accepted != 1 || expired != 1 {
|
||||
t.Fatalf("concurrent results accepted=%d expired=%d, want 1/1", accepted, expired)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue