Defect #24156

Updated by Toshi MARUYAMA over 1 year ago

Under certain conditions (and due to Rails' decision to hold global query state on models), under certain conditions, there might be multiple instances of the @AnonymousUser@ and @AnonymousGroup@ being created. Consider this example:

<pre><code class="ruby"> &lt;pre&gt;
# => #<AnonymousUser id: 4 ...>

# Create a new public projects with no project members
project = Project.create(name: 'Public test', identifier: 'public-test', public: true)

# Now, gather the list of visible principals of the project
# This is e.g. called by IssueQuery#initialize_available_filters
</code></pre> &lt;/pre&gt;

As you can see, this creates (depending a bit on the settings) a new @AnonymousGroup@ and a new @AnonymousUser@ object. The reason for that with the call above this is called:

<pre><code class="ruby">"#{Member.table_name}.project_id = ?", project,id).uniq.visible

Now, in the @visible@ scope of the @Principle@, a call to @AnonymousUser.first@ class is used to find the anonymous user (and equivalently the @AnonymousGroup@ later). Unfortunately this happens while the query scope for the outer query is still active. The new and unrelated query for the anonymous user appears to inherits the existing scope on the @Principle@ class of the outer query which results in Redmine not finding the anonymous user and subsequently creating a new one.

This behavior can be reliably reproduced when browsing as Anonymous a public project without any explicit members.

The attached patch against current trunk was developed for one of our customers at "Planio": who experiences this issue. The problem does also occur in all Redmine versions which support the user visibility feature (that is all Redmine 3 version iirc).

The patch fixes the problem by performing the queries for the qlobal anonymous objects with an explicit call to @unscoped@ which should remove the inherited outer scope. When merging this, it might be a good idea to also provide a migration which removes any erroneous @AnonymousUser@, @AnonymousGroup@, @NonMemberGroup@ and @AnonymousRole@ instances in the database.

(Boy, this was no fun to find and fix...)