Permalink
Browse files

FIX Not checking stage in SiteTree#canView

SiteTree versions that arent the live version shouldnt be accessed by
regular users, but the logic to check that was split off into canViewStage,
which wasnt checked by code that isnt specifically SiteTree aware
(like RestfulServer)
  • Loading branch information...
Hamish Friedlander
Hamish Friedlander committed Jul 4, 2013
1 parent a2c2be2 commit a5f00ae2c3bc6dd6aa27d56b530b75408539b067
@@ -102,19 +102,24 @@ public function init() {
// Check page permissions
if($this->dataRecord && $this->URLSegment != 'Security' && !$this->dataRecord->canView()) {
- return Security::permissionFailure($this);
- }
+ $permissionMessage = null;
- // Draft/Archive security check - only CMS users should be able to look at stage/archived content
- if($this->URLSegment != 'Security' && !Session::get('unsecuredDraftSite') && (Versioned::current_archived_date() || (Versioned::current_stage() && Versioned::current_stage() != 'Live'))) {
- if(!$this->dataRecord->canViewStage(Versioned::current_stage())) {
- $link = $this->Link();
- $message = _t("ContentController.DRAFT_SITE_ACCESS_RESTRICTION", 'You must log in with your CMS password in order to view the draft or archived content. <a href="%s">Click here to go back to the published site.</a>');
+ // Check if we could view the live version, offer redirect if so
+ if($this->canViewStage('Live')) {
Session::clear('currentStage');
Session::clear('archiveDate');
-
- return Security::permissionFailure($this, sprintf($message, Controller::join_links($link, "?stage=Live")));
+
+ $permissionMessage = sprintf(
+ _t(
+ "ContentController.DRAFT_SITE_ACCESS_RESTRICTION",
+ 'You must log in with your CMS password in order to view the draft or archived content. '.
+ '<a href="%s">Click here to go back to the published site.</a>'
+ ),
+ Controller::join_links($this->Link(), "?stage=Live")
+ );
}
+
+ return Security::permissionFailure($this, $permissionMessage);
}
// Use theme from the site config
View
@@ -830,6 +830,23 @@ public function canView($member = null) {
// admin override
if($member && Permission::checkMember($member, array("ADMIN", "SITETREE_VIEW_ALL"))) return true;
+ // make sure we were loaded off an allowed stage
+
+ // Were we definitely loaded directly off Live during our query?
+ $fromLive = true;
+
+ foreach (array('mode' => 'stage', 'stage' => 'live') as $param => $match) {
+ $fromLive = $fromLive && strtolower((string)$this->getSourceQueryParam("Versioned.$param")) == $match;
+ }
+
+ if(!$fromLive
+ && !Session::get('unsecuredDraftSite')
+ && !Permission::checkMember($member, array('CMS_ACCESS_CMSMain', 'VIEW_DRAFT_CONTENT'))) {
+ // If we weren't definitely loaded from live, and we can't view non-live content, we need to
+ // check to make sure this version is the live version and so can be viewed
+ if (Versioned::get_versionnumber_by_stage($this->class, 'Live', $this->ID) != $this->Version) return false;
+ }
+
// Standard mechanism for accepting permission changes from extensions
$extended = $this->extendedCan('canView', $member);
if($extended !== null) return $extended;
@@ -858,27 +875,25 @@ public function canView($member = null) {
return false;
}
-
+
/**
- * Determines permissions for a specific stage (see {@link Versioned}).
+ * Determines canView permissions for the latest version of this Page on a specific stage (see {@link Versioned}).
* Usually the stage is read from {@link Versioned::current_stage()}.
- * Falls back to {@link canView}.
- *
+ *
* @todo Implement in CMS UI.
*
* @param String $stage
* @param Member $member
* @return boolean
*/
- public function canViewStage($stage, $member = null) {
- if(!$member) $member = Member::currentUser();
+ public function canViewStage($stage = 'Live', $member = null) {
+ $oldMode = Versioned::get_reading_mode();
+ Versioned::reading_stage($stage);
- if(
- strtolower($stage) == 'stage' &&
- !(Permission::checkMember($member, 'CMS_ACCESS_CMSMain') || Permission::checkMember($member, 'VIEW_DRAFT_CONTENT'))
- ) return false;
-
- return $this->canView($member);
+ $versionFromStage = DataObject::get($this->class)->byID($this->ID);
+
+ Versioned::set_reading_mode($oldMode);
+ return $versionFromStage ? $versionFromStage->canView($member) : false;
}
/**
@@ -10,11 +10,16 @@ class ContentControllerPermissionsTest extends FunctionalTest {
protected $autoFollowRedirection = false;
public function testCanViewStage() {
+ // Create a new page
$page = new Page();
$page->URLSegment = 'testpage';
$page->write();
$page->publish('Stage', 'Live');
-
+
+ // Add a stage-only version
+ $page->Content = "Version2";
+ $page->write();
+
$response = $this->get('/testpage');
$this->assertEquals($response->getStatusCode(), 200, 'Doesnt require login for implicit live stage');
@@ -126,7 +126,16 @@ public function testCanEditOnPageDeletedFromStageAndLiveReturnsFalse() {
}
public function testCanViewStage() {
+ $this->useDraftSite(false); // useDraftSite deliberately disables checking the stage as part of canView
+
+ // Get page & make sure it exists on Live
$page = $this->objFromFixture('Page', 'standardpage');
+ $page->publish('Stage', 'Live');
+
+ // Then make sure there's a new version on Stage
+ $page->Title = 1;
+ $page->write();
+
$editor = $this->objFromFixture('Member', 'editor');
$websiteuser = $this->objFromFixture('Member', 'websiteuser');
@@ -135,6 +144,8 @@ public function testCanViewStage() {
$this->assertTrue($page->canViewStage('Live', $editor));
$this->assertTrue($page->canViewStage('Stage', $editor));
+
+ $this->useDraftSite();
}
public function testAccessTabOnlyDisplaysWithGrantAccessPermissions() {
@@ -11,7 +11,7 @@
class ZZZSearchFormTest extends FunctionalTest {
protected static $fixture_file = 'SearchFormTest.yml';
-
+
protected $mockController;
public function waitUntilIndexingFinished() {
@@ -88,7 +88,6 @@ public function testDoubleQuotesPublishedPagesMatchedByTitle() {
);
}
- /*
public function testUnpublishedPagesNotIncluded() {
if(!$this->checkFulltextSupport()) return;
@@ -102,14 +101,15 @@ public function testUnpublishedPagesNotIncluded() {
'Unpublished pages are not found by searchform'
);
}
- */
-
+
public function testPagesRestrictedToLoggedinUsersNotIncluded() {
if(!$this->checkFulltextSupport()) return;
$sf = new SearchForm($this->mockController, 'SearchForm');
$page = $this->objFromFixture('SiteTree', 'restrictedViewLoggedInUsers');
+ $page->publish('Stage', 'Live');
+
$results = $sf->getResults(null, array('Search'=>'restrictedViewLoggedInUsers'));
$this->assertNotContains(
$page->ID,
@@ -134,6 +134,8 @@ public function testPagesRestrictedToSpecificGroupNotIncluded() {
$sf = new SearchForm($this->mockController, 'SearchForm');
$page = $this->objFromFixture('SiteTree', 'restrictedViewOnlyWebsiteUsers');
+ $page->publish('Stage', 'Live');
+
$results = $sf->getResults(null, array('Search'=>'restrictedViewOnlyWebsiteUsers'));
$this->assertNotContains(
$page->ID,
@@ -162,13 +164,17 @@ public function testPagesRestrictedToSpecificGroupNotIncluded() {
$member->logOut();
}
- public function testInheritedRestrictedPagesNotInlucded() {
+ public function testInheritedRestrictedPagesNotIncluded() {
if(!$this->checkFulltextSupport()) return;
$sf = new SearchForm($this->mockController, 'SearchForm');
-
+
+ $parent = $this->objFromFixture('SiteTree', 'restrictedViewLoggedInUsers');
+ $parent->publish('Stage', 'Live');
+
$page = $this->objFromFixture('SiteTree', 'inheritRestrictedView');
-
+ $page->publish('Stage', 'Live');
+
$results = $sf->getResults(null, array('Search'=>'inheritRestrictedView'));
$this->assertNotContains(
$page->ID,

0 comments on commit a5f00ae

Please sign in to comment.