fix: align in-memory authentication semantics
This commit is contained in:
@@ -106,6 +106,9 @@ func NewClientWithAuthData(authData AuthData, options ...ClientOption) (*Client,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !authData.zero() {
|
||||
authData = client.authDataWithIdentity(authData)
|
||||
}
|
||||
client.setAuthData(authData)
|
||||
return client, nil
|
||||
}
|
||||
@@ -131,8 +134,11 @@ func newClient(options ...ClientOption) (*Client, error) {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// WithAuthDataChanged configures serialized persistence for in-memory auth updates.
|
||||
// The callback runs under login serialization, but never while the auth data lock is held.
|
||||
// WithAuthDataChanged configures synchronous persistence for in-memory auth updates.
|
||||
// The callback is serialized by the client's login lock and may acquire unrelated
|
||||
// application locks, but it must not call Client methods other than AuthData.
|
||||
// In particular, calling Login or another method that may refresh authentication
|
||||
// will deadlock. Returning an error leaves the client's authentication unchanged.
|
||||
func WithAuthDataChanged(callback func(AuthData) error) ClientOption {
|
||||
return func(client *Client) error {
|
||||
if callback == nil {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -47,6 +48,31 @@ func TestNewClientWithAuthDataRejectsPartialData(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClientWithAuthDataGeneratesMissingIdentityWithoutMutatingInput(t *testing.T) {
|
||||
input := completeAuthData()
|
||||
input.DeviceID = ""
|
||||
input.PassO = ""
|
||||
|
||||
client, err := NewClientWithAuthData(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if input.DeviceID != "" || input.PassO != "" {
|
||||
t.Fatalf("caller auth data mutated: %#v", input)
|
||||
}
|
||||
stored := client.AuthData()
|
||||
if stored.DeviceID == "" || stored.PassO == "" {
|
||||
t.Fatalf("stored identity not generated: %#v", stored)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithAuthDataChangedRejectsNil(t *testing.T) {
|
||||
_, err := NewClientWithAuthData(AuthData{}, WithAuthDataChanged(nil))
|
||||
if err == nil || !strings.Contains(err.Error(), "must not be nil") {
|
||||
t.Fatalf("error = %v, want nil callback error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClientWithAuthDataDoesNotAccessFilesystem(t *testing.T) {
|
||||
home := filepath.Join(t.TempDir(), "must-not-exist")
|
||||
t.Setenv("HOME", home)
|
||||
@@ -110,6 +136,43 @@ func TestMemoryRefreshCallbackCanReadCurrentAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryRefreshCallbackCanAcquireApplicationLock(t *testing.T) {
|
||||
var persistenceMu sync.Mutex
|
||||
client, _, server := newMemoryRefreshClient(t, func(AuthData) error {
|
||||
persistenceMu.Lock()
|
||||
defer persistenceMu.Unlock()
|
||||
return nil
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
if err := client.refreshToken(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemoryRefreshPersistsGeneratedIdentity(t *testing.T) {
|
||||
initial := completeAuthData()
|
||||
initial.DeviceID = ""
|
||||
initial.PassO = ""
|
||||
var changed AuthData
|
||||
client, err := NewClientWithAuthData(initial, WithAuthDataChanged(func(authData AuthData) error {
|
||||
changed = authData.clone()
|
||||
return nil
|
||||
}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server := configureRefreshServer(t, client)
|
||||
defer server.Close()
|
||||
|
||||
if err := client.refreshToken(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if changed.DeviceID == "" || changed.PassO == "" {
|
||||
t.Fatalf("persisted identity not generated: %#v", changed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFreshMemoryClientQRLoginPersistsThroughCallback(t *testing.T) {
|
||||
var changed AuthData
|
||||
var server *httptest.Server
|
||||
|
||||
Reference in New Issue
Block a user