Steps to reproduce:

1. Enable REST, HAL and Basic Auth modules.
2. Allow permission on GET method for Content on Authenticated users.
3. Create a user.
4. Create a node.
5. Run the following command replacing username and password: curl --request GET --user username:password --header 'Accept: application/hal+json' http://d8.local/node/1

Expected: The hal+json version of the node.
Actual: The following circular reference error raises because Comment service needs an authenticated user:

Circular reference

Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException: Circular reference detected for service "current_user", path: "router_listener -> router -> router.dynamic -> url_generator -> route_processor_manager -> route_processor_csrf -> csrf_token -> current_user -> comment.manager". in Symfony\Component\DependencyInjection\Container->get() (line 282 of /var/www/d8/core/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Container.php).

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

juampynr’s picture

Status: Active » Needs review
FileSize
553 bytes
970 bytes

Here is a patch that demonstrates this issue. I simply enabled comment module to the Authentication test and verified that the REST request returns a JSON response.

dawehner’s picture

Issue summary: View changes
dawehner’s picture

Issue tags: +D8MA, +SprintWeekend2014
FileSize
3.22 KB

Here is some research we (webflo, dawehner) have done.

The problem is basically the following: The csrf_token service needs the current user, so the authentication starts.
Normally (for all cases in tests) we either use the Cookie provider (which does not load the user). In this case csrf token can get the user
and the request continues.

In the case "basic authentication" usecase we authenticate the user and then load the new authenticated user. This will trigger hook_entity_load,
which will call comment_entity_load() which requires the comment.manager service. That one requires the current user and we have the circular dependency.

While debugging we realized a couple of points:

  • comment_entity_load() also takes non content entities / non fieldable entities into account
  • comment.manager does not pass in the current user as synchronized service
  • comment.manager does basically way too much. It does not use the current user for the loading related parts of the code.
    This is a potential sign that we should better split up the service. The word "manager" does not tell us anything about the actual work done by the class. If we split it up we would have solved the circular dependency

In general @damiankloip wanted to work on making the current user not get fetched automatically but '@current_user' should be just some kind of factory, so we authenticate later. This should also solve the problem described in this issue.

The patch does fixes the first and second point but certainly does not fix the actual root of the issue.

The last submitted patch, 1: drupal-circular_reference-2182055-1.patch, failed testing.

The last submitted patch, 1: drupal-circular_reference-2182055-1.patch, failed testing.

The last submitted patch, 1: drupal-circular_reference-2182055-1.patch, failed testing.

The last submitted patch, 1: drupal-circular_reference-2182055-1.patch, failed testing.

Berdir’s picture

In general @damiankloip wanted to work on making the current user not get fetched automatically but '@current_user' should be just some kind of factory, so we authenticate later. This should also solve the problem described in this issue.

That's similar to my suggestion to only have the authentication manager as a service that's passed around (We can still have Drupal::currentUser()), which would essentially be the same.

larowlan’s picture

Patch looks OK, good catch on content entities, I did ask for a hook_content_entity_load somewhere, but forget to come back for this cleanup

ssemashka’s picture

Just tried authencation.patch and went through listed steps and got the error:

Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException: in Drupal\Core\EventSubscriber\AccessSubscriber->onKernelRequestAccessCheck() (line 87 of /home/s77aec018cc5a771/www/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php)

ssemashka’s picture

Status: Needs review » Needs work
linclark’s picture

Status: Needs work » Needs review
FileSize
3.89 KB

Let's add the comment module to the test as juampy did, if only temporarily, so that we can know when this is actually passing.

Status: Needs review » Needs work

The last submitted patch, 12: 2182055-11-basic-auth-circular-reference.patch, failed testing.

andypost’s picture

To fix that properly the CommentManager::forbiddenMessage() should be moved out manager, probably to CommentViewBuilder

From IRC:
<alexpott> andypost: I'd be tempted to split CommentManager into two - CommentManager that does the higher level stuff with users etc... and CommentFieldManager (or something like that) which does the low level entity management stuff.

Related #2180109: Change the current_user service to a proxy

  1. +++ b/core/modules/comment/comment.module
    @@ -854,6 +854,9 @@ function comment_translation_configuration_element_submit($form, &$form_state) {
     function comment_entity_load($entities, $entity_type) {
    +  if (!\Drupal::entityManager()->getDefinition($entity_type)->isSubclassOf('Drupal\Core\Entity\ContentEntityInterface')) {
    +    return;
    

    comment_entity_load() should gone in #148849: Refactor {comment_entity_statistics} into performant Field

  2. +++ b/core/modules/comment/comment.services.yml
    @@ -7,7 +7,9 @@ services:
       comment.manager:
    ...
    +    calls:
    +      - [setCurrentUser, ['@current_user=']]
    
    +++ b/core/modules/comment/lib/Drupal/comment/CommentManager.php
    @@ -86,15 +84,18 @@ class CommentManager implements CommentManagerInterface {
    +  public function setCurrentUser(AccountInterface $current_user) {
    +    $this->currentUser = $current_user;
    

    Wrong way!
    Manager should not depends on current_user at all

blainelang’s picture

This error occurs as well for a content type 'article' on a clean install without a comment field.

andypost’s picture

Status: Needs work » Needs review
FileSize
7.84 KB
9.55 KB

Comment manager should not depend on user.
So patch removes this dependency.

PS: there's issue for split #2188287: Split CommentManager in two

andypost’s picture

FileSize
685 bytes
10.22 KB

Now with comment module dependency as #12 to test dependecies

Status: Needs review » Needs work

The last submitted patch, 17: 2182055-comment-auth-17.patch, failed testing.

The last submitted patch, 17: 2182055-comment-auth-17.patch, failed testing.

andypost’s picture

Debugged this, actual problem caused by url_generator dependency!
Current user calls user_authenticate() that calls user_load_by_name() that invokes entity system too early, so other services is trying to initialize... the comment manager needs url_generator to up but container detects that this service in progress of creation... and fails.

andypost’s picture

discussed in IRC with @dawehner, my point is - no code (csrf is not a exceptional) should be executed before auth,
So #2180109: Change the current_user service to a proxy can't help with the issue

damiankloip’s picture

Yes, it could have done if we went with one of the earlier patches. But people seem to be against that. That issue already mentions that point, so maybe have a look.

juampynr’s picture

Re-rolling #16

juampynr’s picture

As mentioned by @andypost, the patch fixes the issue at the comment service level, but it still exists at the url_generator service.

I just repeated the steps listed at the top and got the following error:

If you have just changed code (for example deployed a new module or moved an existing one) read <a href="http://drupal.org/documentation/rebuild">http://drupal.org/documentation/rebuild</a><h1>Additional uncaught exception thrown while handling exception.</h1><h2>Original</h2><p><em class="placeholder">Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException</em>: Circular reference detected for service &quot;url_generator&quot;, path: &quot;router_listener -&gt; router -&gt; router.dynamic -&gt; url_generator -&gt; route_processor_manager -&gt; route_processor_csrf -&gt; csrf_token -&gt; current_user -&gt; comment.manager&quot;. in <em class="placeholder">Symfony\Component\DependencyInjection\Container-&gt;get()</em> (line <em class="placeholder">282</em> of <em class="placeholder">/home/juampy/projects/drupal8/core/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Container.php</em>).<pre>Symfony\Component\DependencyInjection\Container->get('url_generator', 1)
Drupal\Core\DependencyInjection\Container->get('url_generator')
service_container_prod->getComment_ManagerService()
Symfony\Component\DependencyInjection\Container->get('comment.manager', 1)
Drupal\Core\DependencyInjection\Container->get('comment.manager')
Drupal::service('comment.manager')
comment_entity_load(Array, 'user')
Drupal\Core\Entity\EntityStorageBase->postLoad(Array)
Drupal\Core\Entity\ContentEntityDatabaseStorage->postLoad(Array)
Drupal\user\UserStorage->postLoad(Array)
Drupal\Core\Entity\ContentEntityDatabaseStorage->loadMultiple(Array)
Drupal\Core\Entity\EntityStorageBase->loadByProperties(Array)
Drupal\user\UserAuth->authenticate('test', 'test')
Drupal\basic_auth\Authentication\Provider\BasicAuth->authenticate(Object)
Drupal\Core\Authentication\AuthenticationManager->authenticate(Object)
service_container_prod->getCurrentUserService()
Symfony\Component\DependencyInjection\Container->get('current_user', 2)
Drupal\Core\DependencyInjection\Container->get('current_user', 2)
service_container_prod->getCsrfTokenService()
Symfony\Component\DependencyInjection\Container->get('csrf_token', 1)
Drupal\Core\DependencyInjection\Container->get('csrf_token')
service_container_prod->getRouteProcessorCsrfService()
Symfony\Component\DependencyInjection\Container->get('route_processor_csrf', 1)
Drupal\Core\DependencyInjection\Container->get('route_processor_csrf')
service_container_prod->getRouteProcessorManagerService()
Symfony\Component\DependencyInjection\Container->get('route_processor_manager', 1)
Drupal\Core\DependencyInjection\Container->get('route_processor_manager')
service_container_prod->getUrlGeneratorService()
Symfony\Component\DependencyInjection\Container->get('url_generator', 1)
Drupal\Core\DependencyInjection\Container->get('url_generator')
service_container_prod->getRouter_DynamicService()
Symfony\Component\DependencyInjection\Container->get('router.dynamic', 1)
Drupal\Core\DependencyInjection\Container->get('router.dynamic')
service_container_prod->getRouterService()
Symfony\Component\DependencyInjection\Container->get('router', 1)
Drupal\Core\DependencyInjection\Container->get('router')
service_container_prod->getRouterListenerService()
Symfony\Component\DependencyInjection\Container->get('router_listener', 1)
Drupal\Core\DependencyInjection\Container->get('router_listener')
Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher->lazyLoad('kernel.finish_request')
Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.finish_request', Object)
Symfony\Component\HttpKernel\HttpKernel->finishRequest(Object, 1)
Symfony\Component\HttpKernel\HttpKernel->handleException(Object, Object, 1)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1)
Drupal\Core\HttpKernel->handle(Object, 1, 1)
Drupal\Core\DrupalKernel->handle(Object)
drupal_handle_request()
</pre></p><h2>Additional</h2><p><em class="placeholder">Symfony\Component\DependencyInjection\Exception\RuntimeException</em>: You have requested a synthetic service (&quot;request&quot;). The DIC does not know how to construct this service. in <em class="placeholder">service_container_prod-&gt;getRequestService()</em> (line <em class="placeholder">3503</em> of <em class="placeholder">/home/juampy/projects/drupal8/sites/default/files/php/service_container/service_container_prod.php/cfee7c2b6847b582ea18afe6e7d50ccdd8f232877a3d30451ae73be46b852694.php</em>).<pre>service_container_prod->getRequestService()
Symfony\Component\DependencyInjection\Container->get('request', 1)
Drupal\Core\DependencyInjection\Container->get('request')
Drupal::request()
drupal_render_cache_set('', Array)
drupal_render(Array)
_drupal_theme_initialize(Object, Array)
_drupal_maintenance_theme()
drupal_maintenance_theme()
_drupal_log_error(Array, 1)
_drupal_exception_handler(Object)
</pre></p><hr >
damiankloip’s picture

dawehner’s picture

Status: Needs work » Needs review
FileSize
3.93 KB

This worked for me.

andypost’s picture

Status: Needs review » Needs work
+++ b/core/modules/comment/lib/Drupal/comment/CommentManager.php
@@ -37,13 +37,6 @@ class CommentManager implements CommentManagerInterface {
-  protected $currentUser;

Once you remove variable then you need to remove it from constructor and service definition as well

andypost’s picture

dawehner’s picture

Status: Needs work » Needs review
FileSize
3.31 KB
696 bytes

Actually it is the other way round.

andypost’s picture

  1. +++ b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
    @@ -241,14 +241,14 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
    +      if (empty($links) && \Drupal::currentUser()->isAnonymous()) {
             $links['comment-forbidden']['title'] = \Drupal::service('comment.manager')->forbiddenMessage($commented_entity, $entity->getFieldName());
    

    then no reason to check the same in manager

  2. +++ b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
    @@ -241,14 +241,14 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
    -    if ($container->get('module_handler')->moduleExists('content_translation') && content_translation_translate_access($entity)) {
    +    if (\Drupal::moduleHandler()->moduleExists('content_translation') && content_translation_translate_access($entity)) {
    

    Then remove $container aswell

dawehner’s picture

then no reason to check the same in manager

I kinda dislike to do access checking in the manager ...

Then remove $container aswell

Maybe at the same time we could already inject the module handler.

dawehner’s picture

FileSize
7.39 KB

Fixed the points of andrey

andypost’s picture

+++ b/core/modules/comment/lib/Drupal/comment/CommentManager.php
@@ -277,39 +277,37 @@ public function getFieldUIPageTitle($commented_entity_type, $field_name) {
-    if ($this->currentUser->isAnonymous()) {

That's why I remove current_user from dependencies of manager in #16, this one is only needed here

dawehner’s picture

FileSize
9.72 KB
2.62 KB

Aaaaaaah, here is a patch. Thank you for the pointer.

juampynr’s picture

Issue summary: View changes
juampynr’s picture

I repeated the steps listed at the issue description without the above patch and could not reproduce the issue anymore. I will test it further to see if I see if I can reproduce it again.

dawehner’s picture

@juampy
The reason might be probably because we have the account proxy now.

andypost’s picture

Part of patch included in #2188287: Split CommentManager in two

dawehner’s picture

34: comment-2182055-34.patch queued for re-testing.

andypost’s picture

Status: Needs review » Reviewed & tested by the community

All this changes are needed and critically

+++ b/core/modules/comment/comment.module
 function comment_entity_load($entities, $entity_type) {
+  if (!\Drupal::entityManager()->getDefinition($entity_type)->isSubclassOf('Drupal\Core\Entity\ContentEntityInterface')) {
+    return;

The primary purpose of the patch

catch’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/comment/comment.module
 function comment_entity_load($entities, $entity_type) {
+  if (!\Drupal::entityManager()->getDefinition($entity_type)->isSubclassOf('Drupal\Core\Entity\ContentEntityInterface')) {
+    return;

It's not clear from the issue summary, nor a quick scan through the comments. What else is getting passed in here? At least needs an inline comment.

dawehner’s picture

Status: Needs work » Needs review
FileSize
9.8 KB
662 bytes

Does that clarify it?

andypost’s picture

Status: Needs review » Reviewed & tested by the community

agreed, exactly

  • Commit 5d05703 on 8.x by catch:
    Issue #2182055 by dawehner, andypost, juampy, linclark: Comment module...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Oh doh it's a generic entity hook so it runs on everything. I'd forgotten we're still loading comment statistics onto entity objects. We should just rip that out, but not here.

Committed/pushed to 8.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.