Category not accepting "anonymous email" from known users

Reproduce:

  1. Have a “private” category that only has create/reply/see access for members of a specific group. No other permissions.
  2. Enable “anonymous email in” for that specific via a unique working alias to the incoming POP account.
  3. Have a non-staff user that is not a member of the group send an email report in to the category email address from the email address associated with their account.
  4. Send in an email from an account not associated with any known Discourse account.

Expected Behavior:

  • Both #3 and #4 result in new topics in the private category, and the group can begin discussion.

Actual Behavior:

  • The email in #4 works but email in #3 is rejected with the can_create? failed error.
8 个赞

@zogstrip can you review this?

Is this still a problem?

I’m no longer running a site with incoming email enabled so unfortunately can’t reproduce to say either way.

2 个赞

Pretty sure #3 still isn’t working.

When we receive an email, we first try to associate it to a user, and then we check for permissions. Since the user isn’t part of the group, they can’t post.

The email_in_allow_strangers field on the category only works for staged users.

3 个赞

Can confirm #3 still not working. Is the best workaround right now to have emails directed at a group rather than a category? Not my preferred arrangement but I can switch until a patch comes out.

1 个赞

I don’t think we can ever support #3 unless we add another setting that opens up any incoming emails?

This scenario seems to be replaced these days with the group functionality as a type of workaround, although I could see some use cases that it might still be just as valuable for categories/topics/discussions.

Sounds plausible. Is this a big thing to do?

Like you say, this is just a (bad) workaround. IMO this should be controllable on a per-category level. Clearly a bug that this works only for anonymous but not for known users.

1 个赞

I just ran into this issue while trying to set up a category for our organizations info list. This category only allows a certain group to access it, but we selected the option to allow emails from anonymous users. Email addresses that are not associated with any of our discourse users can send a message that will show up in the category, but registered users that are not in the group get rejected due to “Insufficient Trust Level”. I think I understand the technical reason why it works this way (only works for staged users) but is there a reason why this would be the desired or expected behavior? It seems to me if we are choosing to allow anonymous users we probably want to allow all registered users as well.

1 个赞

我们上周也在自己的网站上遇到了同样的问题。对我们来说,情况如下:

虽然我们的大多数类别仅限注册用户访问,但我们希望提供一种方式,让非社区成员能够通过电子邮件联系核心开发者,提出简单问题、表达关切等。

为此,我们设置了一个类别,允许匿名用户通过自定义地址(例如 ourproject+contact@discoursemail.com)发送邮件。我们将该类别的权限设置为仅核心项目成员可读,以避免给整个社区带来过多的邮件或干扰(这意味着核心成员也可以创建和回复主题,因为无法在限制读取权限的同时不限制创建或回复权限)。

然而,我们随后发现,如果匿名用户之后创建了正式账户,他们将无法再向该联系邮箱发送邮件。这似乎有些不合逻辑:他们现在比之前更正式、已注册,但却能做的事情反而比之前更少了。

在看到这个主题之前,这让我感到非常困惑,而这种情况至今仍显得令人遗憾。

-Brad

2 个赞

此问题在 2.7.8 版本中仍然存在。令人困惑的是,当发件人在论坛中没有注册账户时,发送到与类别关联的电子邮件的邮件会被接受,但如果发件人有注册账户,邮件反而会被拒绝。

3 个赞

我认为以下补丁(针对当前的 main 分支)可以通过在 email_in_allow_strangers 为类别设置时跳过验证来解决此问题,其方式与为暂存用户跳过验证相同。这听起来合理吗?

diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb
index 7c76c44d61..dd3bc3cfb0 100644
--- a/lib/email/receiver.rb
+++ b/lib/email/receiver.rb
@@ -762,7 +762,7 @@ module Email
                      elided: elided,
                      title: subject,
                      category: destination.id,
-                     skip_validations: user.staged?)
+                     skip_validations: (user.staged? || destination.email_in_allow_strangers))
 
       elsif destination.is_a?(PostReplyKey)
         # 对于回复地址的邮件,我们不暂存新用户,如果用户为 nil 则退出

它不起作用,因为验证是关于帖子内容的,而不是关于是否有权创建帖子……

我测试了下面的补丁,它可以工作。这是一个黑客手段,请勿在家尝试:scream: 它的价值在于大致确定了需要更改的位置。挑战在于找出如何以正确的方式实现这一点。任何建议都将非常欢迎::pray:

diff --git a/app/jobs/regular/notify_mailing_list_subscribers.rb b/app/jobs/regular/notify_mailing_list_subscribers.rb
index c535296105..1d3bf79637 100644
--- a/app/jobs/regular/notify_mailing_list_subscribers.rb
+++ b/app/jobs/regular/notify_mailing_list_subscribers.rb
@@ -74,7 +74,7 @@ module Jobs
 
       DiscourseEvent.trigger(:notify_mailing_list_subscribers, users, post)
       users.find_each do |user|
-        if Guardian.new(user).can_see?(post)
+        if Guardian.new(user).can_see?(post) && Guardian.new(user).can_see_category_staged?(post.topic.category)
           if EmailLog.reached_max_emails?(user)
             skip(user.email, user.id, post.id,
               SkippedEmailLog.reason_types[:exceeded_emails_limit]
diff --git a/app/models/category.rb b/app/models/category.rb
index 630a74c425..6c253650c6 100644
--- a/app/models/category.rb
+++ b/app/models/category.rb
@@ -201,7 +201,7 @@ class Category < ActiveRecord::Base
       end
     else
       permissions = permission_types.map { |p| CategoryGroup.permission_types[p] }
-      where("(:staged AND LENGTH(COALESCE(email_in, '')) > 0 AND email_in_allow_strangers)
+      where("(LENGTH(COALESCE(email_in, '')) > 0 AND email_in_allow_strangers)
           OR categories.id NOT IN (SELECT category_id FROM category_groups)
           OR categories.id IN (
                 SELECT category_id
@@ -209,7 +209,6 @@ class Category < ActiveRecord::Base
                  WHERE permission_type IN (:permissions)
                    AND (group_id = :everyone OR group_id IN (SELECT group_id FROM group_users WHERE user_id = :user_id))
              )",
-        staged: guardian.is_staged?,
         permissions: permissions,
         user_id: guardian.user.id,
         everyone: Group[:everyone].id)
diff --git a/lib/guardian/category_guardian.rb b/lib/guardian/category_guardian.rb
index 94a48466d6..2a4ba8015c 100644
--- a/lib/guardian/category_guardian.rb
+++ b/lib/guardian/category_guardian.rb
@@ -64,6 +64,14 @@ module CategoryGuardian
   end
 
   def can_see_category?(category)
+    return false unless category
+    return true if is_admin?
+    return true if !category.read_restricted
+    return true if category.email_in.present? && category.email_in_allow_strangers
+    secure_category_ids.include?(category.id)
+  end
+
+  def can_see_category_staged?(category)
     return false unless category
     return true if is_admin?
     return true if !category.read_restricted
2 个赞

你觉得这个建议怎么样 @zogstrip

1 个赞

我认为这里的行为是故意的,修复此处需要相当全面的方案。

允许人们将主题与现有账户关联的问题在于,任何人都可以伪造任何人的电子邮件。

我认为长期解决此问题的方法是:

  1. sam@somewhere.com 在 Discourse 上拥有一个账户。
  2. sam@somewhere.com 向某个分类发送邮件,其中包含“匿名邮件”。
  3. Discourse 向 sam@somewhere.com 发送一封邮件:“看起来您发布了:帖子内容,到 Discourse 的 URL。这真的是您吗?”
  4. 点击“是”。
  5. 主题被创建。

如果没有此保护机制,该功能将完全暴露于身份冒充的风险之下,这是非常危险的。

5 个赞

这个问题是不是已经存在了,无论是否勾选了“接受没有账户的匿名用户的电子邮件”参数?

我同意这个观点:当一封电子邮件与一个分类相关联并且勾选了“接受没有账户的匿名用户的电子邮件”复选框时,一封传入的电子邮件

  1. 与 Discourse 账户相关联
  2. 根据分类的“安全”参数不允许读取消息

应该被接受。

1 个赞

SPF/DKIM/DMARC 的使用,现在比 2016 年要普遍得多,难道不能在很大程度上防止这种情况吗?

任何好的电子邮件提供商(对用户而言)都不会允许其他用户从不属于他们的地址发送邮件,任何好的电子邮件提供商或接收服务(对 Discourse 实例而言)都应该拒绝未能通过原始验证的电子邮件。

会存在一些提供商,它们不验证发件人地址和/或不设置 SPF/etc. 记录,但如果用户选择使用一个不采取任何措施来防止冒充的电子邮件提供商,那么他们被冒充似乎是合理的。

我还没有深入研究,但看起来 Email::AuthenticationResults 已经进行了一些来源验证。也许在(较)短期内,可以提供裁决(如果还没有的话),以便这些电子邮件可以被接受并带有通过裁决。

在你的长期解决方案中,在裁决通过的情况下,也可以跳过“真的是你吗?”的验证。