Skip to content
This repository
Browse code

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...
commit a5f00ae2c3bc6dd6aa27d56b530b75408539b067 1 parent a2c2be2
Hamish Friedlander authored July 04, 2013
23  code/controllers/ContentController.php
@@ -102,19 +102,24 @@ public function init() {
102 102
 
103 103
 		// Check page permissions
104 104
 		if($this->dataRecord && $this->URLSegment != 'Security' && !$this->dataRecord->canView()) {
105  
-			return Security::permissionFailure($this);
106  
-		}
  105
+			$permissionMessage = null;
107 106
 
108  
-		// Draft/Archive security check - only CMS users should be able to look at stage/archived content
109  
-		if($this->URLSegment != 'Security' && !Session::get('unsecuredDraftSite') && (Versioned::current_archived_date() || (Versioned::current_stage() && Versioned::current_stage() != 'Live'))) {
110  
-			if(!$this->dataRecord->canViewStage(Versioned::current_stage())) {
111  
-				$link = $this->Link();
112  
-				$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>');
  107
+			// Check if we could view the live version, offer redirect if so
  108
+			if($this->canViewStage('Live')) {
113 109
 				Session::clear('currentStage');
114 110
 				Session::clear('archiveDate');
115  
-				
116  
-				return Security::permissionFailure($this, sprintf($message, Controller::join_links($link, "?stage=Live")));
  111
+
  112
+				$permissionMessage = sprintf(
  113
+					_t(
  114
+						"ContentController.DRAFT_SITE_ACCESS_RESTRICTION",
  115
+						'You must log in with your CMS password in order to view the draft or archived content. '.
  116
+						'<a href="%s">Click here to go back to the published site.</a>'
  117
+					),
  118
+					Controller::join_links($this->Link(), "?stage=Live")
  119
+				);
117 120
 			}
  121
+
  122
+			return Security::permissionFailure($this, $permissionMessage);
118 123
 		}
119 124
 		
120 125
 		// Use theme from the site config
39  code/model/SiteTree.php
@@ -830,6 +830,23 @@ public function canView($member = null) {
830 830
 		// admin override
831 831
 		if($member && Permission::checkMember($member, array("ADMIN", "SITETREE_VIEW_ALL"))) return true;
832 832
 
  833
+		// make sure we were loaded off an allowed stage
  834
+
  835
+		// Were we definitely loaded directly off Live during our query?
  836
+		$fromLive = true;
  837
+
  838
+		foreach (array('mode' => 'stage', 'stage' => 'live') as $param => $match) {
  839
+			$fromLive = $fromLive && strtolower((string)$this->getSourceQueryParam("Versioned.$param")) == $match;
  840
+		}
  841
+
  842
+		if(!$fromLive
  843
+		   && !Session::get('unsecuredDraftSite')
  844
+		   && !Permission::checkMember($member, array('CMS_ACCESS_CMSMain', 'VIEW_DRAFT_CONTENT'))) {
  845
+			// If we weren't definitely loaded from live, and we can't view non-live content, we need to
  846
+			// check to make sure this version is the live version and so can be viewed
  847
+			if (Versioned::get_versionnumber_by_stage($this->class, 'Live', $this->ID) != $this->Version) return false;
  848
+		}
  849
+
833 850
 		// Standard mechanism for accepting permission changes from extensions
834 851
 		$extended = $this->extendedCan('canView', $member);
835 852
 		if($extended !== null) return $extended;
@@ -858,27 +875,25 @@ public function canView($member = null) {
858 875
 		
859 876
 		return false;
860 877
 	}
861  
-	
  878
+
862 879
 	/**
863  
-	 * Determines permissions for a specific stage (see {@link Versioned}).
  880
+	 * Determines canView permissions for the latest version of this Page on a specific stage (see {@link Versioned}).
864 881
 	 * Usually the stage is read from {@link Versioned::current_stage()}.
865  
-	 * Falls back to {@link canView}.
866  
-	 * 
  882
+	 *
867 883
 	 * @todo Implement in CMS UI.
868 884
 	 * 
869 885
 	 * @param String $stage
870 886
 	 * @param Member $member
871 887
 	 * @return boolean
872 888
 	 */
873  
-	public function canViewStage($stage, $member = null) {
874  
-		if(!$member) $member = Member::currentUser();
  889
+	public function canViewStage($stage = 'Live', $member = null) {
  890
+		$oldMode = Versioned::get_reading_mode();
  891
+		Versioned::reading_stage($stage);
875 892
 
876  
-		if(
877  
-			strtolower($stage) == 'stage' && 
878  
-			!(Permission::checkMember($member, 'CMS_ACCESS_CMSMain') || Permission::checkMember($member, 'VIEW_DRAFT_CONTENT'))
879  
-		) return false;
880  
-		
881  
-		return $this->canView($member);
  893
+		$versionFromStage = DataObject::get($this->class)->byID($this->ID);
  894
+
  895
+		Versioned::set_reading_mode($oldMode);
  896
+		return $versionFromStage ? $versionFromStage->canView($member) : false;
882 897
 	}
883 898
 
884 899
 	/**
7  tests/controller/ContentControllerPermissionsTest.php
@@ -10,11 +10,16 @@ class ContentControllerPermissionsTest extends FunctionalTest {
10 10
 	protected $autoFollowRedirection = false;
11 11
 	
12 12
 	public function testCanViewStage() {
  13
+		// Create a new page
13 14
 		$page = new Page();
14 15
 		$page->URLSegment = 'testpage';
15 16
 		$page->write();
16 17
 		$page->publish('Stage', 'Live');
17  
-		
  18
+
  19
+		// Add a stage-only version
  20
+		$page->Content = "Version2";
  21
+		$page->write();
  22
+
18 23
 		$response = $this->get('/testpage');
19 24
 		$this->assertEquals($response->getStatusCode(), 200, 'Doesnt require login for implicit live stage');
20 25
 		
11  tests/model/SiteTreePermissionsTest.php
@@ -126,7 +126,16 @@ public function testCanEditOnPageDeletedFromStageAndLiveReturnsFalse() {
126 126
 	}
127 127
 
128 128
 	public function testCanViewStage() {
  129
+		$this->useDraftSite(false); // useDraftSite deliberately disables checking the stage as part of canView
  130
+
  131
+		// Get page & make sure it exists on Live
129 132
 		$page = $this->objFromFixture('Page', 'standardpage');
  133
+		$page->publish('Stage', 'Live');
  134
+
  135
+		// Then make sure there's a new version on Stage
  136
+		$page->Title = 1;
  137
+		$page->write();
  138
+
130 139
 		$editor = $this->objFromFixture('Member', 'editor');
131 140
 		$websiteuser = $this->objFromFixture('Member', 'websiteuser');
132 141
 		
@@ -135,6 +144,8 @@ public function testCanViewStage() {
135 144
 		
136 145
 		$this->assertTrue($page->canViewStage('Live', $editor));
137 146
 		$this->assertTrue($page->canViewStage('Stage', $editor));
  147
+
  148
+		$this->useDraftSite();
138 149
 	}
139 150
 	
140 151
 	public function testAccessTabOnlyDisplaysWithGrantAccessPermissions() {
20  tests/search/SearchFormTest.php
@@ -11,7 +11,7 @@
11 11
 class ZZZSearchFormTest extends FunctionalTest {
12 12
 	
13 13
 	protected static $fixture_file = 'SearchFormTest.yml';
14  
-	
  14
+
15 15
 	protected $mockController;
16 16
 	
17 17
 	public function waitUntilIndexingFinished() {
@@ -88,7 +88,6 @@ public function testDoubleQuotesPublishedPagesMatchedByTitle() {
88 88
 		);
89 89
 	}
90 90
 	
91  
-	/*
92 91
 	public function testUnpublishedPagesNotIncluded() {
93 92
 		if(!$this->checkFulltextSupport()) return;
94 93
 
@@ -102,14 +101,15 @@ public function testUnpublishedPagesNotIncluded() {
102 101
 			'Unpublished pages are not found by searchform'
103 102
 		);
104 103
 	}
105  
-	*/
106  
-	
  104
+
107 105
 	public function testPagesRestrictedToLoggedinUsersNotIncluded() {
108 106
 		if(!$this->checkFulltextSupport()) return;
109 107
 
110 108
 		$sf = new SearchForm($this->mockController, 'SearchForm');
111 109
 		
112 110
 		$page = $this->objFromFixture('SiteTree', 'restrictedViewLoggedInUsers');
  111
+		$page->publish('Stage', 'Live');
  112
+
113 113
 		$results = $sf->getResults(null, array('Search'=>'restrictedViewLoggedInUsers'));
114 114
 		$this->assertNotContains(
115 115
 			$page->ID,
@@ -134,6 +134,8 @@ public function testPagesRestrictedToSpecificGroupNotIncluded() {
134 134
 		$sf = new SearchForm($this->mockController, 'SearchForm');
135 135
 		
136 136
 		$page = $this->objFromFixture('SiteTree', 'restrictedViewOnlyWebsiteUsers');
  137
+		$page->publish('Stage', 'Live');
  138
+
137 139
 		$results = $sf->getResults(null, array('Search'=>'restrictedViewOnlyWebsiteUsers'));
138 140
 		$this->assertNotContains(
139 141
 			$page->ID,
@@ -162,13 +164,17 @@ public function testPagesRestrictedToSpecificGroupNotIncluded() {
162 164
 		$member->logOut();
163 165
 	}
164 166
 	
165  
-	public function testInheritedRestrictedPagesNotInlucded() {
  167
+	public function testInheritedRestrictedPagesNotIncluded() {
166 168
 		if(!$this->checkFulltextSupport()) return;
167 169
 
168 170
 		$sf = new SearchForm($this->mockController, 'SearchForm');
169  
-		
  171
+
  172
+		$parent = $this->objFromFixture('SiteTree', 'restrictedViewLoggedInUsers');
  173
+		$parent->publish('Stage', 'Live');
  174
+
170 175
 		$page = $this->objFromFixture('SiteTree', 'inheritRestrictedView');
171  
-		
  176
+		$page->publish('Stage', 'Live');
  177
+
172 178
 		$results = $sf->getResults(null, array('Search'=>'inheritRestrictedView'));
173 179
 		$this->assertNotContains(
174 180
 			$page->ID,

0 notes on commit a5f00ae

Please sign in to comment.
Something went wrong with that request. Please try again.