@ExtensionPointMethod
    @Override
    public Mono<List<StorageResourceInfo>> queryMessageAttachmentInfosInPrivateConversations(
            Long requesterId,
            @Nullable Set<Long> userIds,
            @Nullable DateRange creationDateRange,
            @Nullable Boolean areSharedByRequester) {
        if (CollectionUtil.getSize(userIds) != 1) {
            return Mono.error(ResponseException.get(ResponseStatusCode.ILLEGAL_ARGUMENT,
                    "The size of user IDs must be 1"));
        }
        Long userId = userIds.iterator()
                .next();
        Flux<MessageAttachment> attachmentFlux;
        if (requesterId.equals(userId)) {
            attachmentFlux = messageAttachmentRepository
                    .findSimpleAttachmentsInPrivateConversation(requesterId, creationDateRange);
        } else {
            attachmentFlux =
                    userRelationshipService.hasRelationshipAndNotBlocked(userId, requesterId, false)
                            .flatMapMany(hasRelationshipAndNotBlocked -> {
                                if (!hasRelationshipAndNotBlocked) {
                                    return Mono.error(ResponseException.get(
                                            ResponseStatusCode.NOT_FRIEND_TO_QUERY_MESSAGE_ATTACHMENT_INFO_IN_PRIVATE_CONVERSATION));
                                }
                                return messageAttachmentRepository
                                        .findSimpleAttachmentsInPrivateConversation(requesterId,
                                                userId,
                                                creationDateRange,
                                                areSharedByRequester);
                            });
        }
        return simpleAttachmentFlux2infos(attachmentFlux);
    }
@implNote 1. We use HMAC(key, message) instead of a HASH(key + message) to avoid the length
          extension attack. To put simply, if a hacker knows the signature of the resource
          "1", and he can also know the signature of resource "12", "13", "123", and so on
          without knowledge of the key.
          <p>
          2. Use MD5 because its output size (128 bits) is small, and it is a 22-character
          Base62-encoded string.
turms-plugins/turms-plugin-minio/src/main/java/im/turms/plugin/minio/MinioStorageServiceProvider.java