Drupal Version

9.2.2

Domain module version

(8.x) 1.x-dev

Expected Behavior

[What did you try to do? What URL did you use to do it?]

Get the active domain from DomainNegotiator::getActiveDomain()

Actual Behavior

[What actually happened?]

DomainNegotiator::getActiveDomain() returns null in some circumstances, causing unexpected behaviors like 403 in domain front pages when combined with domain_site_settings & domain_access (see #3126141: Node access restricted for Anonymous), null pointer exceptions, etc.

It seems to be more frequent when called from event subscribers. Actually some existing implementations call getActiveDomain twice as a workaround (- 1 -) (- 2 -).

Steps to reproduce

[A bullet list of steps to reproduce the error. Note if the error always happens or sometimes happens.]

Unfortunately it is not clear what are the exact circumstances that makes DomainNegotiator::getActiveDomain() return null. It might be related to the state of cache or some other arbitrary condition. Commit 68ecdbf4 seems to be related.

Proposed solution

Ensure that DomainNegotiator::getActiveDomain() tries to look up the active domain when is is called for the first time, avoiding to do so by domain negotiator consumers as:

...
  $active = \Drupal::service('domain.negotiator')->getActiveDomain();
  if (empty($active)) {
    $active = \Drupal::service('domain.negotiator')->getActiveDomain(TRUE);
  }
...

Review the method documentation, there is no mention that it could return NULL, e.g., when no domain records defined.

Issue fork domain-3226427

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

manuel.adan created an issue. See original summary.

manuel.adan’s picture

Assigned: manuel.adan » Unassigned
Status: Active » Needs review
StatusFileSize
new4.85 KB
  • NULL return value documented
  • when called for the first time, it now looks up for the active domain, if fails, it will not retry unless reset is set
  • fixes getActiveId that potentially could end in a call on null exception
  • removes existing workarounds from domain_access and DomainSourcePathProcessor
  • some automatic CS fixings

Status: Needs review » Needs work

The last submitted patch, 2: domain-prevent_null_active_domain-3226427-2.patch, failed testing. View results

JeremyFrench’s picture

Trying a retest as the fails did not seem wholly related to the issue.

Also noting that I had issues with this in an access check hook, but calling `->getActiveId()` seemed to work.

JeremyFrench’s picture

This seems to cause a segfault on CR when I apply the patch.

JeremyFrench’s picture

I've spent a while looking into the segfault.

What I believe happens is that as part of resolving the domain a dummy domain can be created.

protected function setRequestDomain() ...
$domain = $this->domainStorage()->create($values);

Unfortunately in the process of creating a domain at least domain_alias_domain_load and Drupal\domain_language\LanguageDefault::get() are called, both of these call getActiveDomain() and we end up in a loop

It looks like these both assume that getActiveDomain can return null if not resolved and will handle it. This means that changing the behaviour is more of an API change than anticipated.

I'm not quite sure of the best approach, from here on in. It feels like there is some inconsistency around this. I note that there is some code to handle recursive call errors in LanguageDefault::get() so I am wondering if the dummy domain creation is causing more issues than we would expect. It doesn't seem obvious that resolving a domain would cause the creation of an entity. NB, I messed with this and it seems to trigger the recursive loop even if you load an entity before the resolver is complete.

One approach could be to have a method to explicitly check if the domain is negotiated rather than inferring this from the behaviour of getActiveDomain I've tested this locally and it seems to work, but it is an API change to what has been done previously. As one of the fixes which is needed is in domain_language I don't think we can do this.

This leaves the option of a new method (or attribute to getActiveDomain) which will explicitly load the domain if it is not loaded. Is getOrLoadActiveDomain() acceptable?

manuel.adan’s picture

Related issues on other modules from the domain ecosystem.

manuel.adan’s picture

Status: Needs work » Needs review
StatusFileSize
new4.89 KB
new430 bytes

Setting the negotiated flag before the actual domain negotiation prevents from infinite looping.

Status: Needs review » Needs work

The last submitted patch, 8: domain-prevent_null_active_domain-3226427-8.patch, failed testing. View results

JeremyFrench’s picture

Status: Needs work » Reviewed & tested by the community

This seems to work well now. I'm not sure about the remaining test fail but the code looks OK and It seems to have fix some long-standing issues I have seen with the domain resolver.

agentrickard’s picture

Status: Reviewed & tested by the community » Needs work

I still need to see a reliable test for this case. It's likely happening at some very early stage of the bootstrap process.

What we need to know is what other modules or conditions trigger this error.

codebymikey’s picture

Re: #11

Ran into this bug with the pate module enabled, and occurs when $node->access('create', $account); is called as part of the access checks (and before Domain's event subscriber is called and Drupal's had a chance to cache the node create access permission check).

domain_access_node_create_access() doesn't have the same NULL check behaviour as in domain_access_node_access().

See 68ecdbf4eebc8127476bd5154082e919bf0c7029 commit.

agentrickard’s picture

Thanks!

tim-diels’s picture

Re-rolled patch as the previous one could not be applied, and can not do an intertdiff.
The patch should focus on this issue and not fix other stuff. Will make it harder to review and get it applied.
Still needs tests.

agentrickard’s picture

Tests which seem unrelated may be failing because the patch changes the method signature of negotiateActiveDomain(), which should return the value of the current domain.

   /**
    * Constructs a DomainNegotiator object.
    *
@@ -131,16 +138,16 @@ class DomainNegotiator implements DomainNegotiatorInterface {
    * Determine the active domain.
    */
   protected function negotiateActiveDomain() {
+    $this->negotiated = TRUE;
     $httpHost = $this->negotiateActiveHostname();
     $this->setRequestDomain($httpHost);
-    return $this->domain;
   }

This return is a convenience for the caller.

Ajeet Tiwari’s picture

Status: Needs work » Needs review
StatusFileSize
new660 bytes

Please review.

mieg’s picture

StatusFileSize
new801 bytes

I actually had to do the same in domain_access_node_create_access after (some) editors started complaining.

mably’s picture

Version: 8.x-1.x-dev » 2.0.x-dev
Status: Needs review » Needs work

Could we have this rebased on the 2.0.x-dev branch please?

dieterholvoet made their first commit to this issue’s fork.

dieterholvoet’s picture

Status: Needs work » Needs review

I rebased against 2.0.x and I fixed another case I encountered when the domain could not be negotiated.

mably’s picture

Status: Needs review » Needs work

Thanks a lot! Looks like the MR needs to be rebase and some CI warnings and errors fixed.

mably’s picture

Still thinking that we are fixing the symptoms here and not the root causes of the problem...

Thinking out loud:

We are probably trying to implement a fix to avoid looping/reentrancy.

But doing so, once a piece of code has started negotiating the active domain, until negotiation is finished, all subsequent calls creating loops, will receive a NULL active domain. Looks like there is no way to avoid that, so all domain related modules should handle that properly.

Looks like we should probably replace negotiated by negotiating to fix that reentrancy/looop problem.

And add another boolean to simply indicate that no domain is available at all and that we should stop trying to negotiate one. Don't see the case when a domain would suddenly become available later on in the request (except when we are creating domains, as is done in some tests).

To be continued...

mably’s picture

Status: Needs work » Needs review

Pushed a few fixes to MR 146. Could anyone review it, please? Thanks.

dieterholvoet’s picture

Status: Needs review » Reviewed & tested by the community

MR !146 seems to be the same as MR !147 now, which means that there aren't any changes since you last asked me to test.

mably’s picture

Let's wait a few days before merging.

  • mably committed 3ca11aa8 on 2.0.x authored by dieterholvoet
    Issue #3226427 by manuel.adan, tim-diels, codebymikey, dieterholvoet,...
mably’s picture

Status: Reviewed & tested by the community » Fixed

Status: Fixed » Closed (fixed)

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