Pull merge.
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Access / AccessResultTest.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Tests\Core\Access\AccessResultTest.
6  */
7
8 namespace Drupal\Tests\Core\Access;
9
10 use Drupal\Core\Access\AccessResult;
11 use Drupal\Core\Access\AccessResultInterface;
12 use Drupal\Core\Access\AccessResultNeutral;
13 use Drupal\Core\Access\AccessResultReasonInterface;
14 use Drupal\Core\Cache\Cache;
15 use Drupal\Core\Cache\CacheableDependencyInterface;
16 use Drupal\Core\DependencyInjection\ContainerBuilder;
17 use Drupal\Tests\UnitTestCase;
18
19 /**
20  * @coversDefaultClass \Drupal\Core\Access\AccessResult
21  * @group Access
22  */
23 class AccessResultTest extends UnitTestCase {
24
25   /**
26    * The cache contexts manager.
27    *
28    * @var \Drupal\Core\Cache\Context\CacheContextsManager|\PHPUnit_Framework_MockObject_MockObject
29    */
30   protected $cacheContextsManager;
31
32   /**
33    * {@inheritdoc}
34    */
35   protected function setUp() {
36     parent::setUp();
37
38     $this->cacheContextsManager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
39       ->disableOriginalConstructor()
40       ->getMock();
41
42     $this->cacheContextsManager->method('assertValidTokens')->willReturn(TRUE);
43     $container = new ContainerBuilder();
44     $container->set('cache_contexts_manager', $this->cacheContextsManager);
45     \Drupal::setContainer($container);
46   }
47
48   protected function assertDefaultCacheability(AccessResult $access) {
49     $this->assertSame([], $access->getCacheContexts());
50     $this->assertSame([], $access->getCacheTags());
51     $this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
52   }
53
54   /**
55    * Tests the construction of an AccessResult object.
56    *
57    * @covers ::neutral
58    */
59   public function testConstruction() {
60     $verify = function (AccessResult $access) {
61       $this->assertFalse($access->isAllowed());
62       $this->assertFalse($access->isForbidden());
63       $this->assertTrue($access->isNeutral());
64       $this->assertDefaultCacheability($access);
65     };
66
67     // Verify the object when using the constructor.
68     $a = new AccessResultNeutral();
69     $verify($a);
70
71     // Verify the object when using the ::create() convenience method.
72     $b = AccessResult::neutral();
73     $verify($b);
74
75     $this->assertEquals($a, $b);
76   }
77
78   /**
79    * @covers ::allowed
80    * @covers ::isAllowed
81    * @covers ::isForbidden
82    * @covers ::isNeutral
83    */
84   public function testAccessAllowed() {
85     $verify = function (AccessResult $access) {
86       $this->assertTrue($access->isAllowed());
87       $this->assertFalse($access->isForbidden());
88       $this->assertFalse($access->isNeutral());
89       $this->assertDefaultCacheability($access);
90     };
91
92     // Verify the object when using the ::allowed() convenience static method.
93     $b = AccessResult::allowed();
94     $verify($b);
95   }
96
97   /**
98    * @covers ::forbidden
99    * @covers ::isAllowed
100    * @covers ::isForbidden
101    * @covers ::isNeutral
102    */
103   public function testAccessForbidden() {
104     $verify = function (AccessResult $access) {
105       $this->assertFalse($access->isAllowed());
106       $this->assertTrue($access->isForbidden());
107       $this->assertFalse($access->isNeutral());
108       $this->assertDefaultCacheability($access);
109     };
110
111     // Verify the object when using the ::forbidden() convenience static method.
112     $b = AccessResult::forbidden();
113     $verify($b);
114   }
115
116   /**
117    * @covers ::forbidden
118    */
119   public function testAccessForbiddenReason() {
120     $verify = function (AccessResult $access, $reason) {
121       $this->assertInstanceOf(AccessResultReasonInterface::class, $access);
122       $this->assertSame($reason, $access->getReason());
123     };
124
125     $b = AccessResult::forbidden();
126     $verify($b, NULL);
127
128     $reason = $this->getRandomGenerator()->string();
129     $b = AccessResult::forbidden($reason);
130     $verify($b, $reason);
131
132     $b = AccessResult::forbiddenIf(TRUE, $reason);
133     $verify($b, $reason);
134   }
135
136   /**
137    * @covers ::allowedIf
138    * @covers ::isAllowed
139    * @covers ::isForbidden
140    * @covers ::isNeutral
141    */
142   public function testAccessConditionallyAllowed() {
143     $verify = function (AccessResult $access, $allowed) {
144       $this->assertSame($allowed, $access->isAllowed());
145       $this->assertFalse($access->isForbidden());
146       $this->assertSame(!$allowed, $access->isNeutral());
147       $this->assertDefaultCacheability($access);
148     };
149
150     $b1 = AccessResult::allowedIf(TRUE);
151     $verify($b1, TRUE);
152     $b2 = AccessResult::allowedIf(FALSE);
153     $verify($b2, FALSE);
154   }
155
156   /**
157    * @covers ::forbiddenIf
158    * @covers ::isAllowed
159    * @covers ::isForbidden
160    * @covers ::isNeutral
161    */
162   public function testAccessConditionallyForbidden() {
163     $verify = function (AccessResult $access, $forbidden) {
164       $this->assertFalse($access->isAllowed());
165       $this->assertSame($forbidden, $access->isForbidden());
166       $this->assertSame(!$forbidden, $access->isNeutral());
167       $this->assertDefaultCacheability($access);
168     };
169
170     $b1 = AccessResult::forbiddenIf(TRUE);
171     $verify($b1, TRUE);
172     $b2 = AccessResult::forbiddenIf(FALSE);
173     $verify($b2, FALSE);
174   }
175
176   /**
177    * @covers ::andIf
178    */
179   public function testAndIf() {
180     $neutral = AccessResult::neutral('neutral message');
181     $allowed = AccessResult::allowed();
182     $forbidden = AccessResult::forbidden('forbidden message');
183     $unused_access_result_due_to_lazy_evaluation = $this->getMock('\Drupal\Core\Access\AccessResultInterface');
184     $unused_access_result_due_to_lazy_evaluation->expects($this->never())
185       ->method($this->anything());
186
187     // ALLOWED && ALLOWED === ALLOWED.
188     $access = $allowed->andIf($allowed);
189     $this->assertTrue($access->isAllowed());
190     $this->assertFalse($access->isForbidden());
191     $this->assertFalse($access->isNeutral());
192     $this->assertDefaultCacheability($access);
193
194     // ALLOWED && NEUTRAL === NEUTRAL.
195     $access = $allowed->andIf($neutral);
196     $this->assertFalse($access->isAllowed());
197     $this->assertFalse($access->isForbidden());
198     $this->assertTrue($access->isNeutral());
199     $this->assertEquals('neutral message', $access->getReason());
200     $this->assertDefaultCacheability($access);
201
202     // ALLOWED && FORBIDDEN === FORBIDDEN.
203     $access = $allowed->andIf($forbidden);
204     $this->assertFalse($access->isAllowed());
205     $this->assertTrue($access->isForbidden());
206     $this->assertFalse($access->isNeutral());
207     $this->assertEquals('forbidden message', $access->getReason());
208     $this->assertDefaultCacheability($access);
209
210     // NEUTRAL && ALLOW == NEUTRAL
211     $access = $neutral->andIf($allowed);
212     $this->assertFalse($access->isAllowed());
213     $this->assertFalse($access->isForbidden());
214     $this->assertTrue($access->isNeutral());
215     $this->assertEquals('neutral message', $access->getReason());
216     $this->assertDefaultCacheability($access);
217
218     // NEUTRAL && NEUTRAL === NEUTRAL.
219     $access = $neutral->andIf($neutral);
220     $this->assertFalse($access->isAllowed());
221     $this->assertFalse($access->isForbidden());
222     $this->assertTrue($access->isNeutral());
223     $this->assertEquals('neutral message', $access->getReason());
224     $this->assertDefaultCacheability($access);
225
226     // NEUTRAL && FORBIDDEN === FORBIDDEN.
227     $access = $neutral->andIf($forbidden);
228     $this->assertFalse($access->isAllowed());
229     $this->assertTrue($access->isForbidden());
230     $this->assertFalse($access->isNeutral());
231     $this->assertEquals('forbidden message', $access->getReason());
232     $this->assertDefaultCacheability($access);
233
234     // FORBIDDEN && ALLOWED = FORBIDDEN
235     $access = $forbidden->andif($allowed);
236     $this->assertFalse($access->isAllowed());
237     $this->assertTrue($access->isForbidden());
238     $this->assertFalse($access->isNeutral());
239     $this->assertEquals('forbidden message', $access->getReason());
240     $this->assertDefaultCacheability($access);
241
242     // FORBIDDEN && NEUTRAL = FORBIDDEN
243     $access = $forbidden->andif($neutral);
244     $this->assertFalse($access->isAllowed());
245     $this->assertTrue($access->isForbidden());
246     $this->assertFalse($access->isNeutral());
247     $this->assertEquals('forbidden message', $access->getReason());
248     $this->assertDefaultCacheability($access);
249
250     // FORBIDDEN && FORBIDDEN = FORBIDDEN
251     $access = $forbidden->andif($forbidden);
252     $this->assertFalse($access->isAllowed());
253     $this->assertTrue($access->isForbidden());
254     $this->assertFalse($access->isNeutral());
255     $this->assertEquals('forbidden message', $access->getReason());
256     $this->assertDefaultCacheability($access);
257
258     // FORBIDDEN && * === FORBIDDEN: lazy evaluation verification.
259     $access = $forbidden->andIf($unused_access_result_due_to_lazy_evaluation);
260     $this->assertFalse($access->isAllowed());
261     $this->assertTrue($access->isForbidden());
262     $this->assertFalse($access->isNeutral());
263     $this->assertEquals('forbidden message', $access->getReason());
264     $this->assertDefaultCacheability($access);
265   }
266
267   /**
268    * @covers ::orIf
269    */
270   public function testOrIf() {
271     $neutral = AccessResult::neutral('neutral message');
272     $neutral_other = AccessResult::neutral('other neutral message');
273     $neutral_reasonless = AccessResult::neutral();
274     $allowed = AccessResult::allowed();
275     $forbidden = AccessResult::forbidden('forbidden message');
276     $forbidden_other = AccessResult::forbidden('other forbidden message');
277     $forbidden_reasonless = AccessResult::forbidden();
278     $unused_access_result_due_to_lazy_evaluation = $this->getMock('\Drupal\Core\Access\AccessResultInterface');
279     $unused_access_result_due_to_lazy_evaluation->expects($this->never())
280       ->method($this->anything());
281
282     // ALLOWED || ALLOWED === ALLOWED.
283     $access = $allowed->orIf($allowed);
284     $this->assertTrue($access->isAllowed());
285     $this->assertFalse($access->isForbidden());
286     $this->assertFalse($access->isNeutral());
287     $this->assertDefaultCacheability($access);
288
289     // ALLOWED || NEUTRAL === ALLOWED.
290     $access = $allowed->orIf($neutral);
291     $this->assertTrue($access->isAllowed());
292     $this->assertFalse($access->isForbidden());
293     $this->assertFalse($access->isNeutral());
294     $this->assertDefaultCacheability($access);
295
296     // ALLOWED || FORBIDDEN === FORBIDDEN.
297     $access = $allowed->orIf($forbidden);
298     $this->assertFalse($access->isAllowed());
299     $this->assertTrue($access->isForbidden());
300     $this->assertFalse($access->isNeutral());
301     $this->assertEquals('forbidden message', $access->getReason());
302     $this->assertDefaultCacheability($access);
303
304     // NEUTRAL || NEUTRAL === NEUTRAL.
305     $access = $neutral->orIf($neutral);
306     $this->assertFalse($access->isAllowed());
307     $this->assertFalse($access->isForbidden());
308     $this->assertTrue($access->isNeutral());
309     $this->assertEquals('neutral message', $access->getReason());
310     $this->assertDefaultCacheability($access);
311     // Reason inheritance edge case: first reason is kept.
312     $access = $neutral->orIf($neutral_other);
313     $this->assertEquals('neutral message', $access->getReason());
314     $access = $neutral_other->orIf($neutral);
315     $this->assertEquals('other neutral message', $access->getReason());
316     // Reason inheritance edge case: one of the operands is reasonless.
317     $access = $neutral->orIf($neutral_reasonless);
318     $this->assertEquals('neutral message', $access->getReason());
319     $access = $neutral_reasonless->orIf($neutral);
320     $this->assertEquals('neutral message', $access->getReason());
321     $access = $neutral_reasonless->orIf($neutral_reasonless);
322     $this->assertNull($access->getReason());
323
324     // NEUTRAL || ALLOWED === ALLOWED.
325     $access = $neutral->orIf($allowed);
326     $this->assertTrue($access->isAllowed());
327     $this->assertFalse($access->isForbidden());
328     $this->assertFalse($access->isNeutral());
329     $this->assertDefaultCacheability($access);
330
331     // NEUTRAL || FORBIDDEN === FORBIDDEN.
332     $access = $neutral->orIf($forbidden);
333     $this->assertFalse($access->isAllowed());
334     $this->assertTrue($access->isForbidden());
335     $this->assertFalse($access->isNeutral());
336     $this->assertEquals('forbidden message', $access->getReason());
337     $this->assertDefaultCacheability($access);
338
339     // FORBIDDEN || ALLOWED === FORBIDDEN.
340     $access = $forbidden->orIf($allowed);
341     $this->assertFalse($access->isAllowed());
342     $this->assertTrue($access->isForbidden());
343     $this->assertFalse($access->isNeutral());
344     $this->assertEquals('forbidden message', $access->getReason());
345     $this->assertDefaultCacheability($access);
346
347     // FORBIDDEN || NEUTRAL === FORBIDDEN.
348     $access = $forbidden->orIf($neutral);
349     $this->assertFalse($access->isAllowed());
350     $this->assertTrue($access->isForbidden());
351     $this->assertFalse($access->isNeutral());
352     $this->assertEquals('forbidden message', $access->getReason());
353     $this->assertDefaultCacheability($access);
354
355     // FORBIDDEN || FORBIDDEN === FORBIDDEN.
356     $access = $forbidden->orIf($forbidden);
357     $this->assertFalse($access->isAllowed());
358     $this->assertTrue($access->isForbidden());
359     $this->assertFalse($access->isNeutral());
360     $this->assertEquals('forbidden message', $access->getReason());
361     $this->assertDefaultCacheability($access);
362     // Reason inheritance edge case: first reason is kept.
363     $access = $forbidden->orIf($forbidden_other);
364     $this->assertEquals('forbidden message', $access->getReason());
365     $access = $forbidden_other->orIf($forbidden);
366     $this->assertEquals('other forbidden message', $access->getReason());
367     // Reason inheritance edge case: one of the operands is reasonless.
368     $access = $forbidden->orIf($forbidden_reasonless);
369     $this->assertEquals('forbidden message', $access->getReason());
370     $access = $forbidden_reasonless->orIf($forbidden);
371     $this->assertEquals('forbidden message', $access->getReason());
372     $access = $forbidden_reasonless->orIf($forbidden_reasonless);
373     $this->assertNull($access->getReason());
374
375     // FORBIDDEN || * === FORBIDDEN.
376     $access = $forbidden->orIf($unused_access_result_due_to_lazy_evaluation);
377     $this->assertFalse($access->isAllowed());
378     $this->assertTrue($access->isForbidden());
379     $this->assertFalse($access->isNeutral());
380     $this->assertEquals('forbidden message', $access->getReason());
381     $this->assertDefaultCacheability($access);
382   }
383
384   /**
385    * @covers ::setCacheMaxAge
386    * @covers ::getCacheMaxAge
387    */
388   public function testCacheMaxAge() {
389     $this->assertSame(Cache::PERMANENT, AccessResult::neutral()->getCacheMaxAge());
390     $this->assertSame(1337, AccessResult::neutral()->setCacheMaxAge(1337)->getCacheMaxAge());
391   }
392
393   /**
394    * @covers ::addCacheContexts
395    * @covers ::resetCacheContexts
396    * @covers ::getCacheContexts
397    * @covers ::cachePerPermissions
398    * @covers ::cachePerUser
399    * @covers ::allowedIfHasPermission
400    */
401   public function testCacheContexts() {
402     $verify = function (AccessResult $access, array $contexts) {
403       $this->assertFalse($access->isAllowed());
404       $this->assertFalse($access->isForbidden());
405       $this->assertTrue($access->isNeutral());
406       $this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
407       $this->assertSame($contexts, $access->getCacheContexts());
408       $this->assertSame([], $access->getCacheTags());
409     };
410
411     $access = AccessResult::neutral()->addCacheContexts(['foo']);
412     $verify($access, ['foo']);
413     // Verify resetting works.
414     $access->resetCacheContexts();
415     $verify($access, []);
416     // Verify idempotency.
417     $access->addCacheContexts(['foo'])
418       ->addCacheContexts(['foo']);
419     $verify($access, ['foo']);
420     // Verify same values in different call order yields the same result.
421     $access->resetCacheContexts()
422       ->addCacheContexts(['foo'])
423       ->addCacheContexts(['bar']);
424     $verify($access, ['bar', 'foo']);
425     $access->resetCacheContexts()
426       ->addCacheContexts(['bar'])
427       ->addCacheContexts(['foo']);
428     $verify($access, ['bar', 'foo']);
429
430     // ::cachePerPermissions() convenience method.
431     $contexts = ['user.permissions'];
432     $a = AccessResult::neutral()->addCacheContexts($contexts);
433     $verify($a, $contexts);
434     $b = AccessResult::neutral()->cachePerPermissions();
435     $verify($b, $contexts);
436     $this->assertEquals($a, $b);
437
438     // ::cachePerUser() convenience method.
439     $contexts = ['user'];
440     $a = AccessResult::neutral()->addCacheContexts($contexts);
441     $verify($a, $contexts);
442     $b = AccessResult::neutral()->cachePerUser();
443     $verify($b, $contexts);
444     $this->assertEquals($a, $b);
445
446     // Both.
447     $contexts = ['user', 'user.permissions'];
448     $a = AccessResult::neutral()->addCacheContexts($contexts);
449     $verify($a, $contexts);
450     $b = AccessResult::neutral()->cachePerPermissions()->cachePerUser();
451     $verify($b, $contexts);
452     $c = AccessResult::neutral()->cachePerUser()->cachePerPermissions();
453     $verify($c, $contexts);
454     $this->assertEquals($a, $b);
455     $this->assertEquals($a, $c);
456
457     // ::allowIfHasPermission and ::allowedIfHasPermission convenience methods.
458     $account = $this->getMock('\Drupal\Core\Session\AccountInterface');
459     $account->expects($this->any())
460       ->method('hasPermission')
461       ->with('may herd llamas')
462       ->will($this->returnValue(FALSE));
463     $contexts = ['user.permissions'];
464
465     // Verify the object when using the ::allowedIfHasPermission() convenience
466     // static method.
467     $b = AccessResult::allowedIfHasPermission($account, 'may herd llamas');
468     $verify($b, $contexts);
469   }
470
471   /**
472    * @covers ::addCacheTags
473    * @covers ::addCacheableDependency
474    * @covers ::getCacheTags
475    * @covers ::resetCacheTags
476    */
477   public function testCacheTags() {
478     $verify = function (AccessResult $access, array $tags, array $contexts = [], $max_age = Cache::PERMANENT) {
479       $this->assertFalse($access->isAllowed());
480       $this->assertFalse($access->isForbidden());
481       $this->assertTrue($access->isNeutral());
482       $this->assertSame($max_age, $access->getCacheMaxAge());
483       $this->assertSame($contexts, $access->getCacheContexts());
484       $this->assertSame($tags, $access->getCacheTags());
485     };
486
487     $access = AccessResult::neutral()->addCacheTags(['foo:bar']);
488     $verify($access, ['foo:bar']);
489     // Verify resetting works.
490     $access->resetCacheTags();
491     $verify($access, []);
492     // Verify idempotency.
493     $access->addCacheTags(['foo:bar'])
494       ->addCacheTags(['foo:bar']);
495     $verify($access, ['foo:bar']);
496     // Verify same values in different call order yields the same result.
497     $access->resetCacheTags()
498       ->addCacheTags(['bar:baz'])
499       ->addCacheTags(['bar:qux'])
500       ->addCacheTags(['foo:bar'])
501       ->addCacheTags(['foo:baz']);
502     $verify($access, ['bar:baz', 'bar:qux', 'foo:bar', 'foo:baz']);
503     $access->resetCacheTags()
504       ->addCacheTags(['foo:bar'])
505       ->addCacheTags(['bar:qux'])
506       ->addCacheTags(['foo:baz'])
507       ->addCacheTags(['bar:baz']);
508     $verify($access, ['bar:baz', 'bar:qux', 'foo:bar', 'foo:baz']);
509
510     // ::addCacheableDependency() convenience method.
511     $node = $this->getMock('\Drupal\node\NodeInterface');
512     $node->expects($this->any())
513       ->method('getCacheTags')
514       ->will($this->returnValue(['node:20011988']));
515     $node->expects($this->any())
516       ->method('getCacheMaxAge')
517       ->willReturn(600);
518     $node->expects($this->any())
519       ->method('getCacheContexts')
520       ->willReturn(['user']);
521     $tags = ['node:20011988'];
522     $a = AccessResult::neutral()->addCacheTags($tags);
523     $verify($a, $tags);
524     $b = AccessResult::neutral()->addCacheableDependency($node);
525     $verify($b, $tags, ['user'], 600);
526
527     $non_cacheable_dependency = new \stdClass();
528     $non_cacheable = AccessResult::neutral()->addCacheableDependency($non_cacheable_dependency);
529     $verify($non_cacheable, [], [], 0);
530   }
531
532   /**
533    * @covers ::inheritCacheability
534    */
535   public function testInheritCacheability() {
536     // andIf(); 1st has defaults, 2nd has custom tags, contexts and max-age.
537     $access = AccessResult::allowed();
538     $other = AccessResult::allowed()->setCacheMaxAge(1500)->cachePerPermissions()->addCacheTags(['node:20011988']);
539     $this->assertTrue($access->inheritCacheability($other) instanceof AccessResult);
540     $this->assertSame(['user.permissions'], $access->getCacheContexts());
541     $this->assertSame(['node:20011988'], $access->getCacheTags());
542     $this->assertSame(1500, $access->getCacheMaxAge());
543
544     // andIf(); 1st has custom tags, max-age, 2nd has custom contexts and max-age.
545     $access = AccessResult::allowed()->cachePerUser()->setCacheMaxAge(43200);
546     $other = AccessResult::forbidden()->addCacheTags(['node:14031991'])->setCacheMaxAge(86400);
547     $this->assertTrue($access->inheritCacheability($other) instanceof AccessResult);
548     $this->assertSame(['user'], $access->getCacheContexts());
549     $this->assertSame(['node:14031991'], $access->getCacheTags());
550     $this->assertSame(43200, $access->getCacheMaxAge());
551   }
552
553   /**
554    * Provides a list of access result pairs and operations to test.
555    *
556    * This tests the propagation of cacheability metadata. Rather than testing
557    * every single bit of cacheability metadata, which would lead to a mind-
558    * boggling number of permutations, in this test, we only consider the
559    * permutations of all pairs of the following set:
560    * - Allowed, implements CDI and is cacheable.
561    * - Allowed, implements CDI and is not cacheable.
562    * - Allowed, does not implement CDI (hence not cacheable).
563    * - Forbidden, implements CDI and is cacheable.
564    * - Forbidden, implements CDI and is not cacheable.
565    * - Forbidden, does not implement CDI (hence not cacheable).
566    * - Neutral, implements CDI and is cacheable.
567    * - Neutral, implements CDI and is not cacheable.
568    * - Neutral, does not implement CDI (hence not cacheable).
569    *
570    * (Where "CDI" is CacheableDependencyInterface.)
571    *
572    * This leads to 72 permutations (9!/(9-2)! = 9*8 = 72) per operation. There
573    * are two operations to test (AND and OR), so that leads to a grand total of
574    * 144 permutations, all of which are tested.
575    *
576    * There are two "contagious" patterns:
577    * - Any operation with a forbidden access result yields a forbidden result.
578    *   This therefore also applies to the cacheability metadata associated with
579    *   a forbidden result. This is the case for bullets 4, 5 and 6 in the set
580    *   above.
581    * - Any operation yields an access result object that is of the same class
582    *   (implementation) as the first operand. This is because operations are
583    *   invoked on the first operand. Therefore, if the first implementation
584    *   does not implement CacheableDependencyInterface, then the result won't
585    *   either. This is the case for bullets 3, 6 and 9 in the set above.
586    */
587   public function andOrCacheabilityPropagationProvider() {
588     // ct: cacheable=true, cf: cacheable=false, un: uncacheable.
589     // Note: the test cases that have a "un" access result as the first operand
590     // test UncacheableTestAccessResult, not AccessResult. However, we
591     // definitely want to verify that AccessResult's orIf() and andIf() methods
592     // work correctly when given an AccessResultInterface implementation that
593     // does not implement CacheableDependencyInterface, and we want to test the
594     // full gamut of permutations, so that's not a problem.
595     $allowed_ct = AccessResult::allowed();
596     $allowed_cf = AccessResult::allowed()->setCacheMaxAge(0);
597     $allowed_un = new UncacheableTestAccessResult('ALLOWED');
598     $forbidden_ct = AccessResult::forbidden();
599     $forbidden_cf = AccessResult::forbidden()->setCacheMaxAge(0);
600     $forbidden_un = new UncacheableTestAccessResult('FORBIDDEN');
601     $neutral_ct = AccessResult::neutral();
602     $neutral_cf = AccessResult::neutral()->setCacheMaxAge(0);
603     $neutral_un = new UncacheableTestAccessResult('NEUTRAL');
604
605     // Structure:
606     // - First column: first access result.
607     // - Second column: operator ('OR' or 'AND').
608     // - Third column: second access result.
609     // - Fourth column: whether result implements CacheableDependencyInterface
610     // - Fifth column: whether the result is cacheable (if column 4 is TRUE)
611     return [
612       // Allowed (ct) OR allowed (ct,cf,un).
613       [$allowed_ct, 'OR', $allowed_ct, TRUE, TRUE],
614       [$allowed_ct, 'OR', $allowed_cf, TRUE, TRUE],
615       [$allowed_ct, 'OR', $allowed_un, TRUE, TRUE],
616       // Allowed (cf) OR allowed (ct,cf,un).
617       [$allowed_cf, 'OR', $allowed_ct, TRUE, TRUE],
618       [$allowed_cf, 'OR', $allowed_cf, TRUE, FALSE],
619       [$allowed_cf, 'OR', $allowed_un, TRUE, FALSE],
620       // Allowed (un) OR allowed (ct,cf,un).
621       [$allowed_un, 'OR', $allowed_ct, FALSE, NULL],
622       [$allowed_un, 'OR', $allowed_cf, FALSE, NULL],
623       [$allowed_un, 'OR', $allowed_un, FALSE, NULL],
624
625       // Allowed (ct) OR forbidden (ct,cf,un).
626       [$allowed_ct, 'OR', $forbidden_ct, TRUE, TRUE],
627       [$allowed_ct, 'OR', $forbidden_cf, TRUE, FALSE],
628       [$allowed_ct, 'OR', $forbidden_un, TRUE, FALSE],
629       // Allowed (cf) OR forbidden (ct,cf,un).
630       [$allowed_cf, 'OR', $forbidden_ct, TRUE, TRUE],
631       [$allowed_cf, 'OR', $forbidden_cf, TRUE, FALSE],
632       [$allowed_cf, 'OR', $forbidden_un, TRUE, FALSE],
633       // Allowed (un) OR forbidden (ct,cf,un).
634       [$allowed_un, 'OR', $forbidden_ct, FALSE, NULL],
635       [$allowed_un, 'OR', $forbidden_cf, FALSE, NULL],
636       [$allowed_un, 'OR', $forbidden_un, FALSE, NULL],
637
638       // Allowed (ct) OR neutral (ct,cf,un).
639       [$allowed_ct, 'OR', $neutral_ct, TRUE, TRUE],
640       [$allowed_ct, 'OR', $neutral_cf, TRUE, TRUE],
641       [$allowed_ct, 'OR', $neutral_un, TRUE, TRUE],
642       // Allowed (cf) OR neutral (ct,cf,un).
643       [$allowed_cf, 'OR', $neutral_ct, TRUE, FALSE],
644       [$allowed_cf, 'OR', $neutral_cf, TRUE, FALSE],
645       [$allowed_cf, 'OR', $neutral_un, TRUE, FALSE],
646       // Allowed (un) OR neutral (ct,cf,un).
647       [$allowed_un, 'OR', $neutral_ct, FALSE, NULL],
648       [$allowed_un, 'OR', $neutral_cf, FALSE, NULL],
649       [$allowed_un, 'OR', $neutral_un, FALSE, NULL],
650
651       // Forbidden (ct) OR allowed (ct,cf,un).
652       [$forbidden_ct, 'OR', $allowed_ct, TRUE, TRUE],
653       [$forbidden_ct, 'OR', $allowed_cf, TRUE, TRUE],
654       [$forbidden_ct, 'OR', $allowed_un, TRUE, TRUE],
655       // Forbidden (cf) OR allowed (ct,cf,un).
656       [$forbidden_cf, 'OR', $allowed_ct, TRUE, FALSE],
657       [$forbidden_cf, 'OR', $allowed_cf, TRUE, FALSE],
658       [$forbidden_cf, 'OR', $allowed_un, TRUE, FALSE],
659       // Forbidden (un) OR allowed (ct,cf,un).
660       [$forbidden_un, 'OR', $allowed_ct, FALSE, NULL],
661       [$forbidden_un, 'OR', $allowed_cf, FALSE, NULL],
662       [$forbidden_un, 'OR', $allowed_un, FALSE, NULL],
663
664       // Forbidden (ct) OR neutral (ct,cf,un).
665       [$forbidden_ct, 'OR', $neutral_ct, TRUE, TRUE],
666       [$forbidden_ct, 'OR', $neutral_cf, TRUE, TRUE],
667       [$forbidden_ct, 'OR', $neutral_un, TRUE, TRUE],
668       // Forbidden (cf) OR neutral (ct,cf,un).
669       [$forbidden_cf, 'OR', $neutral_ct, TRUE, FALSE],
670       [$forbidden_cf, 'OR', $neutral_cf, TRUE, FALSE],
671       [$forbidden_cf, 'OR', $neutral_un, TRUE, FALSE],
672       // Forbidden (un) OR neutral (ct,cf,un).
673       [$forbidden_un, 'OR', $neutral_ct, FALSE, NULL],
674       [$forbidden_un, 'OR', $neutral_cf, FALSE, NULL],
675       [$forbidden_un, 'OR', $neutral_un, FALSE, NULL],
676
677       // Forbidden (ct) OR forbidden (ct,cf,un).
678       [$forbidden_ct, 'OR', $forbidden_ct, TRUE, TRUE],
679       [$forbidden_ct, 'OR', $forbidden_cf, TRUE, TRUE],
680       [$forbidden_ct, 'OR', $forbidden_un, TRUE, TRUE],
681       // Forbidden (cf) OR forbidden (ct,cf,un).
682       [$forbidden_cf, 'OR', $forbidden_ct, TRUE, TRUE],
683       [$forbidden_cf, 'OR', $forbidden_cf, TRUE, FALSE],
684       [$forbidden_cf, 'OR', $forbidden_un, TRUE, FALSE],
685       // Forbidden (un) OR forbidden (ct,cf,un).
686       [$forbidden_un, 'OR', $forbidden_ct, FALSE, NULL],
687       [$forbidden_un, 'OR', $forbidden_cf, FALSE, NULL],
688       [$forbidden_un, 'OR', $forbidden_un, FALSE, NULL],
689
690       // Neutral (ct) OR allowed (ct,cf,un).
691       [$neutral_ct, 'OR', $allowed_ct, TRUE, TRUE],
692       [$neutral_ct, 'OR', $allowed_cf, TRUE, FALSE],
693       [$neutral_ct, 'OR', $allowed_un, TRUE, FALSE],
694       // Neutral (cf) OR allowed (ct,cf,un).
695       [$neutral_cf, 'OR', $allowed_ct, TRUE, TRUE],
696       [$neutral_cf, 'OR', $allowed_cf, TRUE, FALSE],
697       [$neutral_cf, 'OR', $allowed_un, TRUE, FALSE],
698       // Neutral (un) OR allowed (ct,cf,un).
699       [$neutral_un, 'OR', $allowed_ct, FALSE, NULL],
700       [$neutral_un, 'OR', $allowed_cf, FALSE, NULL],
701       [$neutral_un, 'OR', $allowed_un, FALSE, NULL],
702
703       // Neutral (ct) OR neutral (ct,cf,un).
704       [$neutral_ct, 'OR', $neutral_ct, TRUE, TRUE],
705       [$neutral_ct, 'OR', $neutral_cf, TRUE, TRUE],
706       [$neutral_ct, 'OR', $neutral_un, TRUE, TRUE],
707       // Neutral (cf) OR neutral (ct,cf,un).
708       [$neutral_cf, 'OR', $neutral_ct, TRUE, TRUE],
709       [$neutral_cf, 'OR', $neutral_cf, TRUE, FALSE],
710       [$neutral_cf, 'OR', $neutral_un, TRUE, FALSE],
711       // Neutral (un) OR neutral (ct,cf,un).
712       [$neutral_un, 'OR', $neutral_ct, FALSE, NULL],
713       [$neutral_un, 'OR', $neutral_cf, FALSE, NULL],
714       [$neutral_un, 'OR', $neutral_un, FALSE, NULL],
715
716       // Neutral (ct) OR forbidden (ct,cf,un).
717       [$neutral_ct, 'OR', $forbidden_ct, TRUE, TRUE],
718       [$neutral_ct, 'OR', $forbidden_cf, TRUE, FALSE],
719       [$neutral_ct, 'OR', $forbidden_un, TRUE, FALSE],
720       // Neutral (cf) OR forbidden (ct,cf,un).
721       [$neutral_cf, 'OR', $forbidden_ct, TRUE, TRUE],
722       [$neutral_cf, 'OR', $forbidden_cf, TRUE, FALSE],
723       [$neutral_cf, 'OR', $forbidden_un, TRUE, FALSE],
724       // Neutral (un) OR forbidden (ct,cf,un).
725       [$neutral_un, 'OR', $forbidden_ct, FALSE, NULL],
726       [$neutral_un, 'OR', $forbidden_cf, FALSE, NULL],
727       [$neutral_un, 'OR', $forbidden_un, FALSE, NULL],
728
729       // Allowed (ct) AND allowed (ct,cf,un).
730       [$allowed_ct, 'AND', $allowed_ct, TRUE, TRUE],
731       [$allowed_ct, 'AND', $allowed_cf, TRUE, FALSE],
732       [$allowed_ct, 'AND', $allowed_un, TRUE, FALSE],
733       // Allowed (cf) AND allowed (ct,cf,un).
734       [$allowed_cf, 'AND', $allowed_ct, TRUE, FALSE],
735       [$allowed_cf, 'AND', $allowed_cf, TRUE, FALSE],
736       [$allowed_cf, 'AND', $allowed_un, TRUE, FALSE],
737       // Allowed (un) AND allowed (ct,cf,un).
738       [$allowed_un, 'AND', $allowed_ct, FALSE, NULL],
739       [$allowed_un, 'AND', $allowed_cf, FALSE, NULL],
740       [$allowed_un, 'AND', $allowed_un, FALSE, NULL],
741
742       // Allowed (ct) AND forbidden (ct,cf,un).
743       [$allowed_ct, 'AND', $forbidden_ct, TRUE, TRUE],
744       [$allowed_ct, 'AND', $forbidden_cf, TRUE, FALSE],
745       [$allowed_ct, 'AND', $forbidden_un, TRUE, FALSE],
746       // Allowed (cf) AND forbidden (ct,cf,un).
747       [$allowed_cf, 'AND', $forbidden_ct, TRUE, TRUE],
748       [$allowed_cf, 'AND', $forbidden_cf, TRUE, FALSE],
749       [$allowed_cf, 'AND', $forbidden_un, TRUE, FALSE],
750       // Allowed (un) AND forbidden (ct,cf,un).
751       [$allowed_un, 'AND', $forbidden_ct, FALSE, NULL],
752       [$allowed_un, 'AND', $forbidden_cf, FALSE, NULL],
753       [$allowed_un, 'AND', $forbidden_un, FALSE, NULL],
754
755       // Allowed (ct) AND neutral (ct,cf,un).
756       [$allowed_ct, 'AND', $neutral_ct, TRUE, TRUE],
757       [$allowed_ct, 'AND', $neutral_cf, TRUE, FALSE],
758       [$allowed_ct, 'AND', $neutral_un, TRUE, FALSE],
759       // Allowed (cf) AND neutral (ct,cf,un).
760       [$allowed_cf, 'AND', $neutral_ct, TRUE, FALSE],
761       [$allowed_cf, 'AND', $neutral_cf, TRUE, FALSE],
762       [$allowed_cf, 'AND', $neutral_un, TRUE, FALSE],
763       // Allowed (un) AND neutral (ct,cf,un).
764       [$allowed_un, 'AND', $neutral_ct, FALSE, NULL],
765       [$allowed_un, 'AND', $neutral_cf, FALSE, NULL],
766       [$allowed_un, 'AND', $neutral_un, FALSE, NULL],
767
768       // Forbidden (ct) AND allowed (ct,cf,un).
769       [$forbidden_ct, 'AND', $allowed_ct, TRUE, TRUE],
770       [$forbidden_ct, 'AND', $allowed_cf, TRUE, TRUE],
771       [$forbidden_ct, 'AND', $allowed_un, TRUE, TRUE],
772       // Forbidden (cf) AND allowed (ct,cf,un).
773       [$forbidden_cf, 'AND', $allowed_ct, TRUE, FALSE],
774       [$forbidden_cf, 'AND', $allowed_cf, TRUE, FALSE],
775       [$forbidden_cf, 'AND', $allowed_un, TRUE, FALSE],
776       // Forbidden (un) AND allowed (ct,cf,un).
777       [$forbidden_un, 'AND', $allowed_ct, FALSE, NULL],
778       [$forbidden_un, 'AND', $allowed_cf, FALSE, NULL],
779       [$forbidden_un, 'AND', $allowed_un, FALSE, NULL],
780
781       // Forbidden (ct) AND neutral (ct,cf,un).
782       [$forbidden_ct, 'AND', $neutral_ct, TRUE, TRUE],
783       [$forbidden_ct, 'AND', $neutral_cf, TRUE, TRUE],
784       [$forbidden_ct, 'AND', $neutral_un, TRUE, TRUE],
785       // Forbidden (cf) AND neutral (ct,cf,un).
786       [$forbidden_cf, 'AND', $neutral_ct, TRUE, FALSE],
787       [$forbidden_cf, 'AND', $neutral_cf, TRUE, FALSE],
788       [$forbidden_cf, 'AND', $neutral_un, TRUE, FALSE],
789       // Forbidden (un) AND neutral (ct,cf,un).
790       [$forbidden_un, 'AND', $neutral_ct, FALSE, NULL],
791       [$forbidden_un, 'AND', $neutral_cf, FALSE, NULL],
792       [$forbidden_un, 'AND', $neutral_un, FALSE, NULL],
793
794       // Forbidden (ct) AND forbidden (ct,cf,un).
795       [$forbidden_ct, 'AND', $forbidden_ct, TRUE, TRUE],
796       [$forbidden_ct, 'AND', $forbidden_cf, TRUE, TRUE],
797       [$forbidden_ct, 'AND', $forbidden_un, TRUE, TRUE],
798       // Forbidden (cf) AND forbidden (ct,cf,un).
799       [$forbidden_cf, 'AND', $forbidden_ct, TRUE, FALSE],
800       [$forbidden_cf, 'AND', $forbidden_cf, TRUE, FALSE],
801       [$forbidden_cf, 'AND', $forbidden_un, TRUE, FALSE],
802       // Forbidden (un) AND forbidden (ct,cf,un).
803       [$forbidden_un, 'AND', $forbidden_ct, FALSE, NULL],
804       [$forbidden_un, 'AND', $forbidden_cf, FALSE, NULL],
805       [$forbidden_un, 'AND', $forbidden_un, FALSE, NULL],
806
807       // Neutral (ct) AND allowed (ct,cf,un).
808       [$neutral_ct, 'AND', $allowed_ct, TRUE, TRUE],
809       [$neutral_ct, 'AND', $allowed_cf, TRUE, TRUE],
810       [$neutral_ct, 'AND', $allowed_un, TRUE, TRUE],
811       // Neutral (cf) AND allowed (ct,cf,un).
812       [$neutral_cf, 'AND', $allowed_ct, TRUE, FALSE],
813       [$neutral_cf, 'AND', $allowed_cf, TRUE, FALSE],
814       [$neutral_cf, 'AND', $allowed_un, TRUE, FALSE],
815       // Neutral (un) AND allowed (ct,cf,un).
816       [$neutral_un, 'AND', $allowed_ct, FALSE, NULL],
817       [$neutral_un, 'AND', $allowed_cf, FALSE, NULL],
818       [$neutral_un, 'AND', $allowed_un, FALSE, NULL],
819
820       // Neutral (ct) AND neutral (ct,cf,un).
821       [$neutral_ct, 'AND', $neutral_ct, TRUE, TRUE],
822       [$neutral_ct, 'AND', $neutral_cf, TRUE, TRUE],
823       [$neutral_ct, 'AND', $neutral_un, TRUE, TRUE],
824       // Neutral (cf) AND neutral (ct,cf,un).
825       [$neutral_cf, 'AND', $neutral_ct, TRUE, FALSE],
826       [$neutral_cf, 'AND', $neutral_cf, TRUE, FALSE],
827       [$neutral_cf, 'AND', $neutral_un, TRUE, FALSE],
828       // Neutral (un) AND neutral (ct,cf,un).
829       [$neutral_un, 'AND', $neutral_ct, FALSE, NULL],
830       [$neutral_un, 'AND', $neutral_cf, FALSE, NULL],
831       [$neutral_un, 'AND', $neutral_un, FALSE, NULL],
832
833       // Neutral (ct) AND forbidden (ct,cf,un).
834       [$neutral_ct, 'AND', $forbidden_ct, TRUE, TRUE],
835       [$neutral_ct, 'AND', $forbidden_cf, TRUE, FALSE],
836       [$neutral_ct, 'AND', $forbidden_un, TRUE, FALSE],
837       // Neutral (cf) AND forbidden (ct,cf,un).
838       [$neutral_cf, 'AND', $forbidden_ct, TRUE, TRUE],
839       [$neutral_cf, 'AND', $forbidden_cf, TRUE, FALSE],
840       [$neutral_cf, 'AND', $forbidden_un, TRUE, FALSE],
841       // Neutral (un) AND forbidden (ct,cf,un).
842       [$neutral_un, 'AND', $forbidden_ct, FALSE, NULL],
843       [$neutral_un, 'AND', $forbidden_cf, FALSE, NULL],
844       [$neutral_un, 'AND', $forbidden_un, FALSE, NULL],
845     ];
846   }
847
848   /**
849    * @covers ::andIf
850    * @covers ::orIf
851    * @covers ::inheritCacheability
852    *
853    * @dataProvider andOrCacheabilityPropagationProvider
854    */
855   public function testAndOrCacheabilityPropagation(AccessResultInterface $first, $op, AccessResultInterface $second, $implements_cacheable_dependency_interface, $is_cacheable) {
856     if ($op === 'OR') {
857       $result = $first->orIf($second);
858     }
859     elseif ($op === 'AND') {
860       $result = $first->andIf($second);
861     }
862     else {
863       throw new \LogicException('Invalid operator specified');
864     }
865     if ($implements_cacheable_dependency_interface) {
866       $this->assertTrue($result instanceof CacheableDependencyInterface, 'Result is an instance of CacheableDependencyInterface.');
867       if ($result instanceof CacheableDependencyInterface) {
868         $this->assertSame($is_cacheable, $result->getCacheMaxAge() !== 0, 'getCacheMaxAge() matches expectations.');
869       }
870     }
871     else {
872       $this->assertFalse($result instanceof CacheableDependencyInterface, 'Result is not an instance of CacheableDependencyInterface.');
873     }
874   }
875
876   /**
877    * @covers ::orIf
878    *
879    * Tests the special case of ORing non-forbidden access results that are both
880    * cacheable but have different cacheability metadata.
881    * This is only the case for non-forbidden access results; we still abort the
882    * ORing process as soon as a forbidden access result is encountered. This is
883    * tested in ::testOrIf().
884    */
885   public function testOrIfCacheabilityMerging() {
886     $merge_both_directions = function (AccessResult $a, AccessResult $b) {
887       // A globally cacheable access result.
888       $a->setCacheMaxAge(3600);
889       // Another access result that is cacheable per permissions.
890       $b->setCacheMaxAge(86400)->cachePerPermissions();
891
892       $r1 = $a->orIf($b);
893       $this->assertTrue($r1->getCacheMaxAge() === 3600);
894       $this->assertSame(['user.permissions'], $r1->getCacheContexts());
895       $r2 = $b->orIf($a);
896       $this->assertTrue($r2->getCacheMaxAge() === 3600);
897       $this->assertSame(['user.permissions'], $r2->getCacheContexts());
898     };
899
900     // Merge either direction, get the same result.
901     $merge_both_directions(AccessResult::allowed(), AccessResult::allowed());
902     $merge_both_directions(AccessResult::allowed(), AccessResult::neutral());
903     $merge_both_directions(AccessResult::neutral(), AccessResult::neutral());
904     $merge_both_directions(AccessResult::neutral(), AccessResult::allowed());
905   }
906
907   /**
908    * Tests allowedIfHasPermissions().
909    *
910    * @covers ::allowedIfHasPermissions
911    *
912    * @dataProvider providerTestAllowedIfHasPermissions
913    *
914    * @param string[] $permissions
915    *   The permissions to check for.
916    * @param string $conjunction
917    *   The conjunction to use when checking for permission. 'AND' or 'OR'.
918    * @param \Drupal\Core\Access\AccessResult $expected_access
919    *   The expected access check result.
920    */
921   public function testAllowedIfHasPermissions($permissions, $conjunction, AccessResult $expected_access) {
922     $account = $this->getMock('\Drupal\Core\Session\AccountInterface');
923     $account->expects($this->any())
924       ->method('hasPermission')
925       ->willReturnMap([
926         ['allowed', TRUE],
927         ['denied', FALSE],
928       ]);
929
930     if ($permissions) {
931       $expected_access->cachePerPermissions();
932     }
933
934     $access_result = AccessResult::allowedIfHasPermissions($account, $permissions, $conjunction);
935     $this->assertEquals($expected_access, $access_result);
936   }
937
938   /**
939    * Provides data for the testAllowedIfHasPermissions() method.
940    *
941    * @return array
942    */
943   public function providerTestAllowedIfHasPermissions() {
944     $access_result = AccessResult::allowedIf(FALSE);
945     $data[] = [[], 'AND', $access_result];
946     $data[] = [[], 'OR', $access_result];
947
948     $access_result = AccessResult::allowedIf(TRUE);
949     $data[] = [['allowed'], 'OR', $access_result];
950     $data[] = [['allowed'], 'AND', $access_result];
951
952     $access_result = AccessResult::allowedIf(FALSE);
953     $access_result->setReason("The 'denied' permission is required.");
954     $data[] = [['denied'], 'OR', $access_result];
955     $data[] = [['denied'], 'AND', $access_result];
956
957     $access_result = AccessResult::allowedIf(TRUE);
958     $data[] = [['allowed', 'denied'], 'OR', $access_result];
959     $data[] = [['denied', 'allowed'], 'OR', $access_result];
960
961     $access_result = AccessResult::allowedIf(TRUE);
962     $data[] = [['allowed', 'denied', 'other'], 'OR', $access_result];
963
964     $access_result = AccessResult::allowedIf(FALSE);
965     $access_result->setReason("The following permissions are required: 'allowed' AND 'denied'.");
966     $data[] = [['allowed', 'denied'], 'AND', $access_result];
967
968     return $data;
969   }
970
971 }
972
973 class UncacheableTestAccessResult implements AccessResultInterface {
974
975   /**
976    * The access result value. 'ALLOWED', 'FORBIDDEN' or 'NEUTRAL'.
977    *
978    * @var string
979    */
980   protected $value;
981
982   /**
983    * Constructs a new UncacheableTestAccessResult object.
984    */
985   public function __construct($value) {
986     $this->value = $value;
987   }
988
989   /**
990    * {@inheritdoc}
991    */
992   public function isAllowed() {
993     return $this->value === 'ALLOWED';
994   }
995
996   /**
997    * {@inheritdoc}
998    */
999   public function isForbidden() {
1000     return $this->value === 'FORBIDDEN';
1001   }
1002
1003   /**
1004    * {@inheritdoc}
1005    */
1006   public function isNeutral() {
1007     return $this->value === 'NEUTRAL';
1008   }
1009
1010   /**
1011    * {@inheritdoc}
1012    */
1013   public function orIf(AccessResultInterface $other) {
1014     if ($this->isForbidden() || $other->isForbidden()) {
1015       return new static('FORBIDDEN');
1016     }
1017     elseif ($this->isAllowed() || $other->isAllowed()) {
1018       return new static('ALLOWED');
1019     }
1020     else {
1021       return new static('NEUTRAL');
1022     }
1023   }
1024
1025   /**
1026    * {@inheritdoc}
1027    */
1028   public function andIf(AccessResultInterface $other) {
1029     if ($this->isForbidden() || $other->isForbidden()) {
1030       return new static('FORBIDDEN');
1031     }
1032     elseif ($this->isAllowed() && $other->isAllowed()) {
1033       return new static('ALLOWED');
1034     }
1035     else {
1036       return new static('NEUTRAL');
1037     }
1038   }
1039
1040 }