fix: reprovision IM member identities across tenants
Deploy olons-server production / deploy (push) Successful in 2m40s
Deploy olons-server production / deploy (push) Successful in 2m40s
This commit is contained in:
@@ -175,7 +175,13 @@ func (s *Service) OpenSession(ctx context.Context, tenantID, memberID, serviceID
|
||||
func (s *Service) ensureIdentity(ctx context.Context, tenantID, memberID int64, imTenantCode string) (*store.MemberIMIdentity, error) {
|
||||
identity, err := s.repository.MemberIMIdentity(ctx, tenantID, memberID)
|
||||
if err == nil {
|
||||
if !strings.EqualFold(identity.IMTenantCode, imTenantCode) || identity.IMUserID == "" || identity.IMUsername == "" {
|
||||
if !strings.EqualFold(identity.IMTenantCode, imTenantCode) {
|
||||
// A tenant switch creates a distinct IM user. Keep the old tenant's IM
|
||||
// account intact, but replace this member's local mapping with a newly
|
||||
// provisioned account in the active tenant.
|
||||
return s.createIdentity(ctx, tenantID, memberID, imTenantCode)
|
||||
}
|
||||
if identity.IMUserID == "" || identity.IMUsername == "" {
|
||||
return nil, ErrIMUnavailable
|
||||
}
|
||||
if identity.IMPasswordCipher != "" {
|
||||
@@ -186,6 +192,10 @@ func (s *Service) ensureIdentity(ctx context.Context, tenantID, memberID int64,
|
||||
if !errors.Is(err, store.ErrResourceNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
return s.createIdentity(ctx, tenantID, memberID, imTenantCode)
|
||||
}
|
||||
|
||||
func (s *Service) createIdentity(ctx context.Context, tenantID, memberID int64, imTenantCode string) (*store.MemberIMIdentity, error) {
|
||||
member, err := s.repository.MemberIMProfile(ctx, tenantID, memberID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -220,7 +230,7 @@ func (s *Service) ensureIdentity(ctx context.Context, tenantID, memberID int64,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
identity = &store.MemberIMIdentity{TenantID: tenantID, MemberID: memberID, IMTenantCode: imTenantCode,
|
||||
identity := &store.MemberIMIdentity{TenantID: tenantID, MemberID: memberID, IMTenantCode: imTenantCode,
|
||||
IMUserID: issued.UserID, IMUsername: created.Username, IMPasswordCipher: ciphertext}
|
||||
return s.repository.SaveMemberIMIdentity(ctx, identity)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
type repositoryStub struct {
|
||||
agent *store.CustomerService
|
||||
identity *store.MemberIMIdentity
|
||||
profile *store.MemberIMProfile
|
||||
}
|
||||
|
||||
func (r *repositoryStub) CustomerServiceByID(context.Context, int64, int64) (*store.CustomerService, error) {
|
||||
@@ -23,9 +24,14 @@ func (r *repositoryStub) MemberIMIdentity(context.Context, int64, int64) (*store
|
||||
return r.identity, nil
|
||||
}
|
||||
func (r *repositoryStub) MemberIMProfile(context.Context, int64, int64) (*store.MemberIMProfile, error) {
|
||||
return nil, store.ErrResourceNotFound
|
||||
if r.profile == nil {
|
||||
return nil, store.ErrResourceNotFound
|
||||
}
|
||||
return r.profile, nil
|
||||
}
|
||||
func (r *repositoryStub) SaveMemberIMIdentity(context.Context, *store.MemberIMIdentity) (*store.MemberIMIdentity, error) {
|
||||
func (r *repositoryStub) SaveMemberIMIdentity(_ context.Context, identity *store.MemberIMIdentity) (*store.MemberIMIdentity, error) {
|
||||
copy := *identity
|
||||
r.identity = ©
|
||||
return r.identity, nil
|
||||
}
|
||||
|
||||
@@ -41,8 +47,8 @@ func (i *imStub) Config() config.IMConfig {
|
||||
return config.IMConfig{TenantCode: "allrich", HTTPAPIURL: "https://api.118im.com/v1/im",
|
||||
WebSocketURL: "wss://api.118im.com/v0/channels", FileURL: "https://api.118im.com/v0/file", APIKey: "key"}
|
||||
}
|
||||
func (i *imStub) CreateUser(context.Context, string, string, string) (*imclient.CreatedUser, error) {
|
||||
return nil, nil
|
||||
func (i *imStub) CreateUser(_ context.Context, username, nickname, _ string) (*imclient.CreatedUser, error) {
|
||||
return &imclient.CreatedUser{UserID: imclient.FlexibleID("1001"), Username: username, Nickname: nickname}, nil
|
||||
}
|
||||
func (i *imStub) FindUserByUsername(context.Context, string) (*imclient.CreatedUser, error) {
|
||||
return nil, nil
|
||||
@@ -141,3 +147,22 @@ func TestMemberIMUsernameMatchesIMConstraint(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureIdentityReprovisionsMemberWhenIMTenantChanges(t *testing.T) {
|
||||
repository := &repositoryStub{
|
||||
identity: &store.MemberIMIdentity{TenantID: 0, MemberID: 9, IMTenantCode: "allrich", IMUserID: "usr_old", IMUsername: "olduser"},
|
||||
profile: &store.MemberIMProfile{MemberID: 9, Username: "member01"},
|
||||
}
|
||||
im := &imStub{}
|
||||
service := New(repository, im, config.AuthConfig{FinanceMasterKey: strings.Repeat("k", 32)})
|
||||
identity, err := service.ensureIdentity(context.Background(), 0, 9, "default")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if identity.IMTenantCode != "default" || identity.IMUserID != "usr_member" || identity.IMUsername != memberIMUsername(0, 9, "default") {
|
||||
t.Fatalf("unexpected migrated identity: %+v", identity)
|
||||
}
|
||||
if im.loginAttempts != 1 {
|
||||
t.Fatalf("provisioning login attempts = %d, want 1", im.loginAttempts)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user