overrideEnvConfig('policy.allow-public', true); $this->expectVisibility( $this->buildObject(PhabricatorPolicies::POLICY_PUBLIC), array( 'public' => true, 'user' => true, 'admin' => true, ), pht('Public Policy (Enabled in Config)')); } /** * Verify that POLICY_PUBLIC is interpreted as POLICY_USER when public * policies are disallowed. */ public function testPublicPolicyDisabled() { $env = PhabricatorEnv::beginScopedEnv(); $env->overrideEnvConfig('policy.allow-public', false); $this->expectVisibility( $this->buildObject(PhabricatorPolicies::POLICY_PUBLIC), array( 'public' => false, 'user' => true, 'admin' => true, ), pht('Public Policy (Disabled in Config)')); } /** * Verify that any logged-in user can view an object with POLICY_USER, but * logged-out users can not. */ public function testUsersPolicy() { $this->expectVisibility( $this->buildObject(PhabricatorPolicies::POLICY_USER), array( 'public' => false, 'user' => true, 'admin' => true, ), pht('User Policy')); } /** * Verify that only administrators can view an object with POLICY_ADMIN. */ public function testAdminPolicy() { $this->expectVisibility( $this->buildObject(PhabricatorPolicies::POLICY_ADMIN), array( 'public' => false, 'user' => false, 'admin' => true, ), pht('Admin Policy')); } /** * Verify that no one can view an object with POLICY_NOONE. */ public function testNoOnePolicy() { $this->expectVisibility( $this->buildObject(PhabricatorPolicies::POLICY_NOONE), array( 'public' => false, 'user' => false, 'admin' => false, ), pht('No One Policy')); } /** * Test offset-based filtering. */ public function testOffsets() { $results = array( $this->buildObject(PhabricatorPolicies::POLICY_NOONE), $this->buildObject(PhabricatorPolicies::POLICY_NOONE), $this->buildObject(PhabricatorPolicies::POLICY_NOONE), $this->buildObject(PhabricatorPolicies::POLICY_USER), $this->buildObject(PhabricatorPolicies::POLICY_USER), $this->buildObject(PhabricatorPolicies::POLICY_USER), ); $query = new PhabricatorPolicyAwareTestQuery(); $query->setResults($results); $query->setViewer($this->buildUser('user')); $this->assertEqual( 3, count($query->setLimit(3)->setOffset(0)->execute()), pht('Invisible objects are ignored.')); $this->assertEqual( 0, count($query->setLimit(3)->setOffset(3)->execute()), pht('Offset pages through visible objects only.')); $this->assertEqual( 2, count($query->setLimit(3)->setOffset(1)->execute()), pht('Offsets work correctly.')); $this->assertEqual( 2, count($query->setLimit(0)->setOffset(1)->execute()), pht('Offset with no limit works.')); } /** * Test limits. */ public function testLimits() { $results = array( $this->buildObject(PhabricatorPolicies::POLICY_USER), $this->buildObject(PhabricatorPolicies::POLICY_USER), $this->buildObject(PhabricatorPolicies::POLICY_USER), $this->buildObject(PhabricatorPolicies::POLICY_USER), $this->buildObject(PhabricatorPolicies::POLICY_USER), $this->buildObject(PhabricatorPolicies::POLICY_USER), ); $query = new PhabricatorPolicyAwareTestQuery(); $query->setResults($results); $query->setViewer($this->buildUser('user')); $this->assertEqual( 3, count($query->setLimit(3)->setOffset(0)->execute()), pht('Limits work.')); $this->assertEqual( 2, count($query->setLimit(3)->setOffset(4)->execute()), pht('Limit + offset work.')); } /** * Test that omnipotent users bypass policies. */ public function testOmnipotence() { $results = array( $this->buildObject(PhabricatorPolicies::POLICY_NOONE), ); $query = new PhabricatorPolicyAwareTestQuery(); $query->setResults($results); $query->setViewer(PhabricatorUser::getOmnipotentUser()); $this->assertEqual( 1, count($query->execute())); } /** * Test that invalid policies reject viewers of all types. */ public function testRejectInvalidPolicy() { $invalid_policy = 'the duck goes quack'; $object = $this->buildObject($invalid_policy); $this->expectVisibility( $object = $this->buildObject($invalid_policy), array( 'public' => false, 'user' => false, 'admin' => false, ), pht('Invalid Policy')); } /** * Test that extended policies work. */ public function testExtendedPolicies() { $object = $this->buildObject(PhabricatorPolicies::POLICY_USER) ->setPHID('PHID-TEST-1'); $this->expectVisibility( $object, array( 'public' => false, 'user' => true, 'admin' => true, ), pht('No Extended Policy')); // Add a restrictive extended policy. $extended = $this->buildObject(PhabricatorPolicies::POLICY_ADMIN) ->setPHID('PHID-TEST-2'); $object->setExtendedPolicies( array( PhabricatorPolicyCapability::CAN_VIEW => array( array($extended, PhabricatorPolicyCapability::CAN_VIEW), ), )); $this->expectVisibility( $object, array( 'public' => false, 'user' => false, 'admin' => true, ), pht('With Extended Policy')); // Depend on a different capability. $object->setExtendedPolicies( array( PhabricatorPolicyCapability::CAN_VIEW => array( array($extended, PhabricatorPolicyCapability::CAN_EDIT), ), )); $extended->setCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT)); $extended->setPolicies( array( PhabricatorPolicyCapability::CAN_EDIT => PhabricatorPolicies::POLICY_NOONE, )); $this->expectVisibility( $object, array( 'public' => false, 'user' => false, 'admin' => false, ), pht('With Extended Policy + Edit')); } /** * Test that cyclic extended policies are arrested properly. */ public function testExtendedPolicyCycles() { $object = $this->buildObject(PhabricatorPolicies::POLICY_USER) ->setPHID('PHID-TEST-1'); $this->expectVisibility( $object, array( 'public' => false, 'user' => true, 'admin' => true, ), pht('No Extended Policy')); // Set a self-referential extended policy on the object. This should // make it fail all policy checks. $object->setExtendedPolicies( array( PhabricatorPolicyCapability::CAN_VIEW => array( array($object, PhabricatorPolicyCapability::CAN_VIEW), ), )); $this->expectVisibility( $object, array( 'public' => false, 'user' => false, 'admin' => false, ), pht('Extended Policy with Cycle')); } /** * Test bulk checks of extended policies. * * This is testing an issue with extended policy filtering which allowed * unusual inputs to slip objects through the filter. See D14993. */ public function testBulkExtendedPolicies() { $object1 = $this->buildObject(PhabricatorPolicies::POLICY_USER) ->setPHID('PHID-TEST-1'); $object2 = $this->buildObject(PhabricatorPolicies::POLICY_USER) ->setPHID('PHID-TEST-2'); $object3 = $this->buildObject(PhabricatorPolicies::POLICY_USER) ->setPHID('PHID-TEST-3'); $extended = $this->buildObject(PhabricatorPolicies::POLICY_ADMIN) ->setPHID('PHID-TEST-999'); $object1->setExtendedPolicies( array( PhabricatorPolicyCapability::CAN_VIEW => array( array( $extended, array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ), ), ), )); $object2->setExtendedPolicies( array( PhabricatorPolicyCapability::CAN_VIEW => array( array($extended, PhabricatorPolicyCapability::CAN_VIEW), ), )); $object3->setExtendedPolicies( array( PhabricatorPolicyCapability::CAN_VIEW => array( array( $extended, array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ), ), ), )); $user = $this->buildUser('user'); $visible = id(new PhabricatorPolicyFilter()) ->setViewer($user) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, )) ->apply( array( $object1, $object2, $object3, )); $this->assertEqual(array(), $visible); } /** * An omnipotent user should be able to see even objects with invalid * policies. */ public function testInvalidPolicyVisibleByOmnipotentUser() { $invalid_policy = 'the cow goes moo'; $object = $this->buildObject($invalid_policy); $results = array( $object, ); $query = new PhabricatorPolicyAwareTestQuery(); $query->setResults($results); $query->setViewer(PhabricatorUser::getOmnipotentUser()); $this->assertEqual( 1, count($query->execute())); } public function testAllQueriesBelongToActualApplications() { $queries = id(new PhutilClassMapQuery()) ->setAncestorClass(PhabricatorPolicyAwareQuery::class) ->execute(); foreach ($queries as $qclass => $query) { $class = $query->getQueryApplicationClass(); if (!$class) { continue; } $this->assertTrue( (bool)PhabricatorApplication::getByClass($class), pht( "Application class '%s' for query '%s'.", $class, $qclass)); } } public function testMultipleCapabilities() { $object = new PhabricatorPolicyTestObject(); $object->setCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )); $object->setPolicies( array( PhabricatorPolicyCapability::CAN_VIEW => PhabricatorPolicies::POLICY_USER, PhabricatorPolicyCapability::CAN_EDIT => PhabricatorPolicies::POLICY_NOONE, )); $filter = new PhabricatorPolicyFilter(); $filter->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )); $filter->setViewer($this->buildUser('user')); $result = $filter->apply(array($object)); $this->assertEqual(array(), $result); } public function testPolicyStrength() { $public = PhabricatorPolicyQuery::getGlobalPolicy( PhabricatorPolicies::POLICY_PUBLIC); $user = PhabricatorPolicyQuery::getGlobalPolicy( PhabricatorPolicies::POLICY_USER); $admin = PhabricatorPolicyQuery::getGlobalPolicy( PhabricatorPolicies::POLICY_ADMIN); $noone = PhabricatorPolicyQuery::getGlobalPolicy( PhabricatorPolicies::POLICY_NOONE); $this->assertFalse($public->isStrongerThan($public)); $this->assertFalse($public->isStrongerThan($user)); $this->assertFalse($public->isStrongerThan($admin)); $this->assertFalse($public->isStrongerThan($noone)); $this->assertTrue($user->isStrongerThan($public)); $this->assertFalse($user->isStrongerThan($user)); $this->assertFalse($user->isStrongerThan($admin)); $this->assertFalse($user->isStrongerThan($noone)); $this->assertTrue($admin->isStrongerThan($public)); $this->assertTrue($admin->isStrongerThan($user)); $this->assertFalse($admin->isStrongerThan($admin)); $this->assertFalse($admin->isStrongerThan($noone)); $this->assertTrue($noone->isStrongerThan($public)); $this->assertTrue($noone->isStrongerThan($user)); $this->assertTrue($noone->isStrongerThan($admin)); $this->assertFalse($admin->isStrongerThan($noone)); } /** * Test an object for visibility across multiple user specifications. */ private function expectVisibility( PhabricatorPolicyTestObject $object, array $map, $description) { foreach ($map as $spec => $expect) { $viewer = $this->buildUser($spec); $query = new PhabricatorPolicyAwareTestQuery(); $query->setResults(array($object)); $query->setViewer($viewer); $caught = null; $result = null; try { $result = $query->executeOne(); } catch (PhabricatorPolicyException $ex) { $caught = $ex; } if ($expect) { $this->assertEqual( $object, $result, pht('%s with user %s should succeed.', $description, $spec)); } else { $this->assertTrue( $caught instanceof PhabricatorPolicyException, pht('%s with user %s should fail.', $description, $spec)); } } } /** * Build a test object to spec. */ private function buildObject($policy) { $object = new PhabricatorPolicyTestObject(); $object->setCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )); $object->setPolicies( array( PhabricatorPolicyCapability::CAN_VIEW => $policy, PhabricatorPolicyCapability::CAN_EDIT => $policy, )); return $object; } /** * Build a test user to spec. */ private function buildUser($spec) { $user = new PhabricatorUser(); switch ($spec) { case 'public': break; case 'user': $user->setPHID(1); break; case 'admin': $user->setPHID(1); $user->setIsAdmin(true); break; default: throw new Exception(pht("Unknown user spec '%s'.", $spec)); } return $user; } }