fix: handle private pending support relationships
Deploy olons-server production / deploy (push) Successful in 3m23s
Deploy olons-server production / deploy (push) Successful in 3m23s
This commit is contained in:
@@ -388,6 +388,11 @@ func IsConflict(err error) bool {
|
||||
return errors.As(err, &target) && target.Status == http.StatusConflict
|
||||
}
|
||||
|
||||
func IsPermissionDenied(err error) bool {
|
||||
var target *Error
|
||||
return errors.As(err, &target) && target.Status == http.StatusForbidden
|
||||
}
|
||||
|
||||
// IsAuthenticationFailed reports whether the IM server rejected user
|
||||
// credentials. Callers may use this to repair credentials which have drifted
|
||||
// from the local encrypted copy, while leaving transport failures untouched.
|
||||
|
||||
@@ -125,11 +125,24 @@ func (s *Service) OpenSession(ctx context.Context, tenantID, memberID, serviceID
|
||||
if !strings.EqualFold(strings.TrimSpace(imSession.UserID), strings.TrimSpace(identity.IMUserID)) {
|
||||
return nil, ErrIMUnavailable
|
||||
}
|
||||
var conversation *imclient.Conversation
|
||||
profile, err := s.im.UserProfile(ctx, imSession.Token, agent.IMUserID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query customer service relationship: %w", err)
|
||||
if !imclient.IsPermissionDenied(err) {
|
||||
return nil, fmt.Errorf("query customer service relationship: %w", err)
|
||||
}
|
||||
conversation, err = s.im.EnsureP2P(ctx, imSession.Token, agent.IMUserID,
|
||||
fmt.Sprintf("p2p:%s:%s", identity.IMUserID, agent.IMUserID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query customer service relationship: %w", err)
|
||||
}
|
||||
}
|
||||
relationship := ""
|
||||
if profile != nil {
|
||||
relationship = strings.TrimSpace(profile.Relationship)
|
||||
} else {
|
||||
relationship = strings.TrimSpace(conversation.Relationship)
|
||||
}
|
||||
relationship := strings.TrimSpace(profile.Relationship)
|
||||
switch relationship {
|
||||
case "friend":
|
||||
case "none", "":
|
||||
@@ -139,13 +152,15 @@ func (s *Service) OpenSession(ctx context.Context, tenantID, memberID, serviceID
|
||||
return nil, fmt.Errorf("create customer service friend request: %w", requestErr)
|
||||
}
|
||||
if requestErr != nil {
|
||||
profile, err = s.im.UserProfile(ctx, imSession.Token, agent.IMUserID)
|
||||
conversation, err = s.im.EnsureP2P(ctx, imSession.Token, agent.IMUserID,
|
||||
fmt.Sprintf("p2p:%s:%s", identity.IMUserID, agent.IMUserID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("refresh customer service relationship: %w", err)
|
||||
}
|
||||
relationship = strings.TrimSpace(profile.Relationship)
|
||||
relationship = strings.TrimSpace(conversation.Relationship)
|
||||
} else {
|
||||
relationship = strings.TrimSpace(request.Status)
|
||||
conversation = nil
|
||||
}
|
||||
if relationship != "accepted" && relationship != "friend" {
|
||||
return nil, ErrRelationPending
|
||||
@@ -155,10 +170,12 @@ func (s *Service) OpenSession(ctx context.Context, tenantID, memberID, serviceID
|
||||
default:
|
||||
return nil, ErrRelationDenied
|
||||
}
|
||||
conversation, err := s.im.EnsureP2P(ctx, imSession.Token, agent.IMUserID,
|
||||
fmt.Sprintf("p2p:%s:%s", identity.IMUserID, agent.IMUserID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ensure customer service conversation: %w", err)
|
||||
if conversation == nil {
|
||||
conversation, err = s.im.EnsureP2P(ctx, imSession.Token, agent.IMUserID,
|
||||
fmt.Sprintf("p2p:%s:%s", identity.IMUserID, agent.IMUserID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ensure customer service conversation: %w", err)
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(conversation.Topic) == "" || (conversation.Relationship != "" && conversation.Relationship != "friend") || !conversation.CanSend {
|
||||
return nil, ErrRelationDenied
|
||||
|
||||
@@ -2,6 +2,7 @@ package support
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -40,6 +41,9 @@ type imStub struct {
|
||||
loginAttempts int
|
||||
resetPasswords int
|
||||
loginError error
|
||||
profileError error
|
||||
conversation *imclient.Conversation
|
||||
ensureP2PCalls int
|
||||
}
|
||||
|
||||
func (i *imStub) Ready() bool { return true }
|
||||
@@ -67,6 +71,9 @@ func (i *imStub) ResetPassword(context.Context, string, string) error {
|
||||
return nil
|
||||
}
|
||||
func (i *imStub) UserProfile(context.Context, string, string) (*imclient.UserProfile, error) {
|
||||
if i.profileError != nil {
|
||||
return nil, i.profileError
|
||||
}
|
||||
return &imclient.UserProfile{UserID: "usr_agent", Relationship: "none"}, nil
|
||||
}
|
||||
func (i *imStub) CreateFriendRequest(context.Context, string, string, string) (*imclient.FriendRequest, error) {
|
||||
@@ -74,6 +81,10 @@ func (i *imStub) CreateFriendRequest(context.Context, string, string, string) (*
|
||||
return &imclient.FriendRequest{RequestID: "frq_1", Status: "accepted"}, nil
|
||||
}
|
||||
func (i *imStub) EnsureP2P(context.Context, string, string, string) (*imclient.Conversation, error) {
|
||||
i.ensureP2PCalls++
|
||||
if i.conversation != nil {
|
||||
return i.conversation, nil
|
||||
}
|
||||
return &imclient.Conversation{Topic: "p2p-topic", Relationship: "friend", CanSend: true}, nil
|
||||
}
|
||||
|
||||
@@ -115,6 +126,27 @@ func TestOpenSessionReturnsSameOriginEndpoints(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenSessionReturnsPendingWhenPrivateProfileHidesPendingRelationship(t *testing.T) {
|
||||
repository := &repositoryStub{
|
||||
agent: &store.CustomerService{ID: 3, Name: "客服01", IMTenantCode: "allrich", IMUserID: "usr_agent", OnlineStatus: 1},
|
||||
identity: &store.MemberIMIdentity{TenantID: 0, MemberID: 9, IMTenantCode: "allrich", IMUserID: "usr_member", IMUsername: "member01"},
|
||||
}
|
||||
im := &imStub{
|
||||
profileError: &imclient.Error{Status: 403, Reason: "PERMISSION_DENIED"},
|
||||
conversation: &imclient.Conversation{Topic: "p2p-topic", Relationship: "pending_outbound", CanSend: false},
|
||||
}
|
||||
service := New(repository, im, config.AuthConfig{FinanceMasterKey: strings.Repeat("k", 32)})
|
||||
repository.identity.IMPasswordCipher, _ = service.encryptCredential("password")
|
||||
|
||||
_, err := service.OpenSession(context.Background(), 0, 9, 3, SessionRequest{DeviceID: "web-1", Platform: "web"})
|
||||
if !errors.Is(err, ErrRelationPending) {
|
||||
t.Fatalf("error = %v, want ErrRelationPending", err)
|
||||
}
|
||||
if im.ensureP2PCalls != 1 || im.friendRequests != 0 {
|
||||
t.Fatalf("ensure p2p calls=%d friend requests=%d, want 1 and 0", im.ensureP2PCalls, im.friendRequests)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenSessionRepairsDriftedIMCredential(t *testing.T) {
|
||||
repository := &repositoryStub{
|
||||
agent: &store.CustomerService{ID: 3, Name: "客服01", IMTenantCode: "allrich", IMUserID: "usr_agent", OnlineStatus: 1},
|
||||
|
||||
Reference in New Issue
Block a user