Merge pull request #115 from mateusz/admin-access

Fix CMS Admin access issues
This commit is contained in:
Ingo Schommer 2013-10-22 16:49:24 +02:00
commit 7c100f90d2
10 changed files with 373 additions and 140 deletions

View File

@ -30,17 +30,4 @@ class SubsiteAdmin extends ModelAdmin {
return $form; return $form;
} }
public function getResponseNegotiator() {
$negotiator = parent::getResponseNegotiator();
$self = $this;
// Register a new callback
$negotiator->setCallback('SubsiteList', function() use(&$self) {
return $self->SubsiteList();
});
return $negotiator;
}
public function SubsiteList() {
return $this->renderWith('SubsiteList');
}
} }

View File

@ -0,0 +1,38 @@
<?php
/**
* Section-agnostic PJAX controller.
*/
class SubsiteXHRController extends LeftAndMain {
/**
* Relax the access permissions, so anyone who has access to any CMS subsite can access this controller.
*/
public function canView($member = null) {
if (parent::canView()) return true;
if (Subsite::all_accessible_sites()->count()>0) return true;
return false;
}
public function getResponseNegotiator() {
$negotiator = parent::getResponseNegotiator();
$self = $this;
// Register a new callback
$negotiator->setCallback('SubsiteList', function() use(&$self) {
return $self->SubsiteList();
});
return $negotiator;
}
/**
* Provide the list of available subsites as a cms-section-agnostic PJAX handler.
*/
public function SubsiteList() {
return $this->renderWith('SubsiteList');
}
}

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Decorator designed to add subsites support to LeftAndMain * Decorator designed to add subsites support to LeftAndMain
* *
* @package subsites * @package subsites
*/ */
class LeftAndMainSubsites extends Extension { class LeftAndMainSubsites extends Extension {
@ -9,26 +9,9 @@ class LeftAndMainSubsites extends Extension {
private static $allowed_actions = array('CopyToSubsite'); private static $allowed_actions = array('CopyToSubsite');
function init() { function init() {
//Use the session variable for current subsite in the CMS only
Subsite::$use_session_subsiteid = true;
Requirements::css('subsites/css/LeftAndMain_Subsites.css'); Requirements::css('subsites/css/LeftAndMain_Subsites.css');
Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js'); Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js');
Requirements::javascript('subsites/javascript/VirtualPage_Subsites.js'); Requirements::javascript('subsites/javascript/VirtualPage_Subsites.js');
if(isset($_GET['SubsiteID'])) {
// Clear current page when subsite changes (or is set for the first time)
if(!Session::get('SubsiteID') || $_GET['SubsiteID'] != Session::get('SubsiteID')) {
Session::clear("{$this->owner->class}.currentPage");
}
// Update current subsite in session
Subsite::changeSubsite($_GET['SubsiteID']);
//Redirect to clear the current page
return $this->owner->redirect('admin/');
}
// Set subsite ID based on currently shown record // Set subsite ID based on currently shown record
$req = $this->owner->getRequest(); $req = $this->owner->getRequest();
@ -50,24 +33,61 @@ class LeftAndMainSubsites extends Extension {
function updatePageOptions(&$fields) { function updatePageOptions(&$fields) {
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID())); $fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
} }
/* /**
* Returns a list of the subsites accessible to the current user * Find all subsites accessible for current user on this controller.
*
* @return ArrayList of {@link Subsite} instances.
*/ */
public function Subsites() { function sectionSites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null) {
// figure out what permission the controller needs // Rationalise member arguments
// Subsite::accessible_sites() expects something, so if there's no permission if(!$member) $member = Member::currentUser();
// then fallback to using CMS_ACCESS_LeftAndMain. if(!$member) return new ArrayList();
$permission = 'CMS_ACCESS_' . $this->owner->class; if(!is_object($member)) $member = DataObject::get_by_id('Member', $member);
$available = Permission::get_codes(false);
if(!isset($available[$permission])) { // Collect permissions - honour the LeftAndMain::required_permission_codes, current model requires
$permission = $this->owner->stat('required_permission_codes'); // us to check if the user satisfies ALL permissions. Code partly copied from LeftAndMain::canView.
if(!$permission) { $codes = array();
$permission = 'CMS_ACCESS_LeftAndMain'; $extraCodes = Config::inst()->get($this->owner->class, 'required_permission_codes');
if($extraCodes !== false) {
if($extraCodes) $codes = array_merge($codes, (array)$extraCodes);
else $codes[] = "CMS_ACCESS_{$this->owner->class}";
} else {
// Check overriden - all subsites accessible.
return Subsite::all_sites();
}
// Find subsites satisfying all permissions for the Member.
$codesPerSite = array();
$sitesArray = array();
foreach ($codes as $code) {
$sites = Subsite::accessible_sites($code, $includeMainSite, $mainSiteTitle, $member);
foreach ($sites as $site) {
// Build the structure for checking how many codes match.
$codesPerSite[$site->ID][$code] = true;
// Retain Subsite objects for later.
$sitesArray[$site->ID] = $site;
} }
} }
return Subsite::accessible_sites($permission); // Find sites that satisfy all codes conjuncitvely.
$accessibleSites = new ArrayList();
foreach ($codesPerSite as $siteID => $siteCodes) {
if (count($siteCodes)==count($codes)) {
$accessibleSites->push($sitesArray[$siteID]);
}
}
return $accessibleSites;
}
/*
* Returns a list of the subsites accessible to the current user.
* It's enough for any section to be accessible for the section to be included.
*/
public function Subsites() {
return Subsite::all_accessible_sites();
} }
/* /*
@ -101,38 +121,26 @@ class LeftAndMainSubsites extends Extension {
return $output; return $output;
} }
/* public function alternateMenuDisplayCheck($controllerName) {
* Returns a subset of the main menu, filtered by admins that have if(!class_exists($controllerName)){
* a subsiteCMSShowInMenu method returning true return false;
* }
* @return ArrayList
*/ // Check subsite support.
public function SubsiteMainMenu(){
if(Subsite::currentSubsiteID() == 0){ if(Subsite::currentSubsiteID() == 0){
return $this->owner->MainMenu(); // Main site always supports everything.
} return true;
// loop main menu items, add all items that have subsite support } else {
$mainMenu = $this->owner->MainMenu(); $controller = singleton($controllerName);
$subsitesMenu = new ArrayList(); if($controller->hasMethod('subsiteCMSShowInMenu') && $controller->subsiteCMSShowInMenu()){
return true;
foreach($mainMenu as $menuItem){
$controllerName = $menuItem->MenuItem->controller;
if(class_exists($controllerName)){
$controller = singleton($controllerName);
if($controller->hasMethod('subsiteCMSShowInMenu') && $controller->subsiteCMSShowInMenu()){
$subsitesMenu->push($menuItem);
}
} }
if($menuItem->Code == 'Help'){
$subsitesMenu->push($menuItem);
}
} }
return $subsitesMenu;
// It's not necessary to check access permissions here. Framework calls canView on the controller,
// which in turn uses the Permission API which is augmented by our GroupSubsites.
return false;
} }
public function CanAddSubsites() { public function CanAddSubsites() {
@ -140,58 +148,88 @@ class LeftAndMainSubsites extends Extension {
} }
/** /**
* Alternative security checker for LeftAndMain. * Do some pre-flight checks if a subsite switch is needed.
* If security isn't found, then it will switch to a subsite where we do have access. * We redirect the user to something accessible if the current section/subsite is forbidden.
*/ */
public function alternateAccessCheck() { public function onBeforeInit() {
// We are accessing the CMS, so we need to let Subsites know we will be using the session.
Subsite::$use_session_subsiteid = true;
// Do not try to be smart for AJAX requests.
if ($this->owner->request->isAjax()) {
return;
}
// Catch forced subsite changes that need to cause CMS reloads.
if(isset($_GET['SubsiteID'])) {
// Clear current page when subsite changes (or is set for the first time)
if(!Session::get('SubsiteID') || $_GET['SubsiteID'] != Session::get('SubsiteID')) {
Session::clear("{$this->owner->class}.currentPage");
}
// Update current subsite in session
Subsite::changeSubsite($_GET['SubsiteID']);
//Redirect to clear the current page
return $this->owner->redirect('admin/');
}
$className = $this->owner->class; $className = $this->owner->class;
// Switch to the subsite of the current page // Transparently switch to the subsite of the current page.
if ($this->owner->class == 'CMSMain' && $currentPage = $this->owner->currentPage()) { if ($this->owner->class == 'CMSMain' && $currentPage = $this->owner->currentPage()) {
if (Subsite::currentSubsiteID() != $currentPage->SubsiteID) { if (Subsite::currentSubsiteID() != $currentPage->SubsiteID) {
Subsite::changeSubsite($currentPage->SubsiteID); Subsite::changeSubsite($currentPage->SubsiteID);
} }
} }
// Switch to a subsite that this user can actually access.
$member = Member::currentUser();
if($member && Permission::checkMember($member, 'ADMIN')) return true; // admin can access all subsites
$sites = Subsite::accessible_sites("CMS_ACCESS_{$this->owner->class}", true)->map('ID', 'Title');
if(is_object($sites)) $sites = $sites->toArray();
if($sites && !isset($sites[Subsite::currentSubsiteID()])) { // If we can view current URL there is nothing to do.
$siteIDs = array_keys($sites); if ($this->owner->canView()) {
Subsite::changeSubsite($siteIDs[0]); return;
return true;
} }
// Switch to a different top-level menu item // Admin can access everything, no point in checking.
$member = Member::currentUser();
if($member && Permission::checkMember($member, 'ADMIN')) return;
// Check if we have access to current section on the current subsite.
$accessibleSites = $this->owner->sectionSites($member);
if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) {
// Current section can be accessed on the current site, all good.
return;
}
// If the current section is not accessible, try at least to stick to the same subsite.
$menu = CMSMenu::get_menu_items(); $menu = CMSMenu::get_menu_items();
foreach($menu as $candidate) { foreach($menu as $candidate) {
if($candidate->controller != $this->owner->class) { if($candidate->controller && $candidate->controller!=$this->owner->class) {
$sites = Subsite::accessible_sites("CMS_ACCESS_{$candidate->controller}", true)->map('ID', 'Title');
if(is_object($sites)) $sites = $sites->toArray(); $accessibleSites = singleton($candidate->controller)->sectionSites(true, 'Main site', $member);
if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) {
if($sites && !isset($sites[Subsite::currentSubsiteID()])) { // Section is accessible, redirect there.
$siteIDs = array_keys($sites); $this->owner->redirect(singleton($candidate->controller)->Link());
Subsite::changeSubsite($siteIDs[0]); return;
$cClass = $candidate->controller; }
$cObj = new $cClass(); }
$this->owner->redirect($cObj->Link()); }
return null;
// Finally, if no section is available, move to any other permitted subsite.
foreach($menu as $candidate) {
if($candidate->controller) {
$accessibleSites = singleton($candidate->controller)->sectionSites(true, 'Main site', $member);
if ($accessibleSites->count()) {
Subsite::changeSubsite($accessibleSites->First()->ID);
$this->owner->redirect(singleton($candidate->controller)->Link());
return;
} }
} }
} }
// If all of those fail, you really don't have access to the CMS
return null;
} }
function augmentNewSiteTreeItem(&$item) { function augmentNewSiteTreeItem(&$item) {
$item->SubsiteID = isset($_POST['SubsiteID']) ? $_POST['SubsiteID'] : Subsite::currentSubsiteID(); $item->SubsiteID = isset($_POST['SubsiteID']) ? $_POST['SubsiteID'] : Subsite::currentSubsiteID();
} }
function onAfterSave($record) { function onAfterSave($record) {
if($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) { if($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) {
$this->owner->response->addHeader('X-Status', rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.'))); $this->owner->response->addHeader('X-Status', rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.')));

View File

@ -310,10 +310,9 @@ JS;
if(isset($_GET['SubsiteID'])) { if(isset($_GET['SubsiteID'])) {
$id = (int)$_GET['SubsiteID']; $id = (int)$_GET['SubsiteID'];
} } else if (Subsite::$use_session_subsiteid) {
else if (Subsite::$use_session_subsiteid) {
$id = Session::get('SubsiteID'); $id = Session::get('SubsiteID');
} }
if($id === NULL) { if($id === NULL) {
$id = self::getSubsiteIDForDomain(); $id = self::getSubsiteIDForDomain();
@ -522,12 +521,64 @@ JS;
return $duplicate; return $duplicate;
} }
/**
* Return all subsites, regardless of permissions (augmented with main site).
*
* @return SS_List List of {@link Subsite} objects (DataList or ArrayList).
*/
public static function all_sites($includeMainSite = true, $mainSiteTitle = "Main site") {
$subsites = Subsite::get();
if($includeMainSite) {
$subsites = $subsites->toArray();
$mainSite = new Subsite();
$mainSite->Title = $mainSiteTitle;
array_unshift($subsites, $mainSite);
$subsites = ArrayList::create($subsites);
}
return $subsites;
}
/*
* Returns an ArrayList of the subsites accessible to the current user.
* It's enough for any section to be accessible for the site to be included.
*
* @return ArrayList of {@link Subsite} instances.
*/
public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null) {
// Rationalise member arguments
if(!$member) $member = Member::currentUser();
if(!$member) return new ArrayList();
if(!is_object($member)) $member = DataObject::get_by_id('Member', $member);
$subsites = new ArrayList();
// Collect subsites for all sections.
$menu = CMSMenu::get_viewable_menu_items();
foreach($menu as $candidate) {
if ($candidate->controller) {
$accessibleSites = singleton($candidate->controller)->sectionSites(
$includeMainSite,
$mainSiteTitle,
$member
);
// Replace existing keys so no one site appears twice.
$subsites->merge($accessibleSites);
}
}
$subsites->removeDuplicates();
return $subsites;
}
/** /**
* Return the subsites that the current user can access. * Return the subsites that the current user can access by given permission.
* Look for one of the given permission codes on the site. * Sites will only be included if they have a Title.
*
* Sites will only be included if they have a Title
* *
* @param $permCode array|string Either a single permission code or an array of permission codes. * @param $permCode array|string Either a single permission code or an array of permission codes.
* @param $includeMainSite If true, the main site will be included if appropriate. * @param $includeMainSite If true, the main site will be included if appropriate.

View File

@ -58,7 +58,7 @@
*/ */
$('.cms-container .cms-menu-list li a').entwine({ $('.cms-container .cms-menu-list li a').entwine({
onclick: function(e) { onclick: function(e) {
$('.cms-container').subsiteFetchPjaxFragment('admin/subsites/', 'SubsiteList'); $('.cms-container').subsiteFetchPjaxFragment('SubsiteXHRController', 'SubsiteList');
this._super(e); this._super(e);
} }
}); });
@ -68,7 +68,7 @@
*/ */
$('.cms-container .SubsiteAdmin .cms-edit-form fieldset.ss-gridfield').entwine({ $('.cms-container .SubsiteAdmin .cms-edit-form fieldset.ss-gridfield').entwine({
onreload: function(e) { onreload: function(e) {
$('.cms-container').subsiteFetchPjaxFragment('admin/subsites/', 'SubsiteList'); $('.cms-container').subsiteFetchPjaxFragment('SubsiteXHRController', 'SubsiteList');
this._super(e); this._super(e);
} }
}); });
@ -81,7 +81,7 @@
*/ */
$('.cms-container .cms-content-fields .subsite-model').entwine({ $('.cms-container .cms-content-fields .subsite-model').entwine({
onadd: function(e) { onadd: function(e) {
$('.cms-container').subsiteFetchPjaxFragment('admin/subsites/', 'SubsiteList'); $('.cms-container').subsiteFetchPjaxFragment('SubsiteXHRController', 'SubsiteList');
this._super(e); this._super(e);
} }
}); });

View File

@ -25,7 +25,7 @@
<div class="cms-panel-content center"> <div class="cms-panel-content center">
<ul class="cms-menu-list"> <ul class="cms-menu-list">
<% loop $SubsiteMainMenu %> <% loop $MainMenu %>
<li class="$LinkingMode $FirstLast <% if $LinkingMode == 'link' %><% else %>opened<% end_if %>" id="Menu-$Code" title="$Title.ATT"> <li class="$LinkingMode $FirstLast <% if $LinkingMode == 'link' %><% else %>opened<% end_if %>" id="Menu-$Code" title="$Title.ATT">
<a href="$Link" <% if $Code == 'Help' %>target="_blank"<% end_if %>> <a href="$Link" <% if $Code == 'Help' %>target="_blank"<% end_if %>>
<span class="icon icon-16 icon-{$Code.LowerCase}">&nbsp;</span> <span class="icon icon-16 icon-{$Code.LowerCase}">&nbsp;</span>

View File

@ -1,4 +1,5 @@
<?php <?php
class LeftAndMainSubsitesTest extends FunctionalTest { class LeftAndMainSubsitesTest extends FunctionalTest {
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; static $fixture_file = 'subsites/tests/SubsiteTest.yml';
@ -9,12 +10,34 @@ class LeftAndMainSubsitesTest extends FunctionalTest {
function objFromFixture($class, $id) { function objFromFixture($class, $id) {
Subsite::disable_subsite_filter(true); Subsite::disable_subsite_filter(true);
$obj = parent::objFromFixture($class, $id); $obj = parent::objFromFixture($class, $id);
Subsite::disable_subsite_filter(false); Subsite::disable_subsite_filter(false);
return $obj; return $obj;
} }
function testAlternateAccessCheck() { function testSectionSites() {
$member = $this->objFromFixture('Member', 'subsite1member');
$cmsmain = singleton('CMSMain');
$subsites = $cmsmain->sectionSites(true, "Main site", $member);
$this->assertDOSEquals(array(
array('Title' =>'Subsite1 Template')
), $subsites, 'Lists member-accessible sites for the accessible controller.');
$assetadmin = singleton('AssetAdmin');
$subsites = $assetadmin->sectionSites(true, "Main site", $member);
$this->assertDOSEquals(array(), $subsites, 'Does not list any sites for forbidden controller.');
$member = $this->objFromFixture('Member', 'editor');
$cmsmain = singleton('CMSMain');
$subsites = $cmsmain->sectionSites(true, "Main site", $member);
$this->assertDOSContains(array(
array('Title' =>'Main site')
), $subsites, 'Includes the main site for members who can access all sites.');
}
function testAccessChecksDontChangeCurrentSubsite() {
$admin = $this->objFromFixture("Member","admin"); $admin = $this->objFromFixture("Member","admin");
$this->loginAs($admin); $this->loginAs($admin);
$ids = array(); $ids = array();
@ -28,11 +51,17 @@ class LeftAndMainSubsitesTest extends FunctionalTest {
$ids[] = $subsite3->ID; $ids[] = $subsite3->ID;
$ids[] = 0; $ids[] = 0;
// Enable session-based subsite tracking.
Subsite::$use_session_subsiteid = true;
foreach($ids as $id) { foreach($ids as $id) {
Subsite::changeSubsite($id); //switch to main site (subsite ID zero) Subsite::changeSubsite($id);
$this->assertEquals($id, Subsite::currentSubsiteID());
$left = new LeftAndMain(); $left = new LeftAndMain();
$this->assertTrue($left->canView(), "Admin user can view subsites LeftAndMain with id = '$id'"); $this->assertTrue($left->canView(), "Admin user can view subsites LeftAndMain with id = '$id'");
$this->assertEquals($id, Subsite::currentSubsiteID(), "The current subsite has not been changed in the process of checking permissions for admin user."); $this->assertEquals($id, Subsite::currentSubsiteID(),
"The current subsite has not been changed in the process of checking permissions for admin user.");
} }
} }

View File

@ -41,6 +41,7 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest {
function testBasicSanity() { function testBasicSanity() {
$this->assertTrue(singleton('SiteTree')->getSiteConfig() instanceof SiteConfig); $this->assertTrue(singleton('SiteTree')->getSiteConfig() instanceof SiteConfig);
// The following assert is breaking in Translatable.
$this->assertTrue(singleton('SiteTree')->getCMSFields() instanceof FieldList); $this->assertTrue(singleton('SiteTree')->getCMSFields() instanceof FieldList);
$this->assertTrue(singleton('SubsitesVirtualPage')->getCMSFields() instanceof FieldList); $this->assertTrue(singleton('SubsitesVirtualPage')->getCMSFields() instanceof FieldList);
$this->assertTrue(is_array(singleton('SiteTreeSubsites')->extraStatics())); $this->assertTrue(is_array(singleton('SiteTreeSubsites')->extraStatics()));

View File

@ -6,6 +6,35 @@ class SubsiteAdminFunctionalTest extends FunctionalTest {
protected $autoFollowRedirection = false; protected $autoFollowRedirection = false;
/**
* Helper: FunctionalTest is only able to follow redirection once, we want to go all the way.
*/
function getAndFollowAll($url) {
$response = $this->get($url);
while ($location = $response->getHeader('Location')) {
$response = $this->mainSession->followRedirection();
}
echo $response->getHeader('Location');
return $response;
}
/**
* Anonymous user cannot access anything.
*/
function testAnonymousIsForbiddenAdminAccess() {
$response = $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
$response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is disallowed');
}
/** /**
* Admin should be able to access all subsites and the main site * Admin should be able to access all subsites and the main site
*/ */
@ -13,14 +42,18 @@ class SubsiteAdminFunctionalTest extends FunctionalTest {
$member = $this->objFromFixture('Member', 'admin'); $member = $this->objFromFixture('Member', 'admin');
Session::set("loggedInAs", $member->ID); Session::set("loggedInAs", $member->ID);
$this->get('admin/pages?SubsiteID=0&ajax=1'); $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->get('admin'); $this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$mainSubsite = $this->objFromFixture('Subsite', 'main'); $subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$this->get("admin/pages?SubsiteID={$mainSubsite->ID}&ajax=1"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->get('admin'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
$this->assertEquals(Subsite::currentSubsiteID(), $mainSubsite->ID, 'Can access the subsite'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable');
} }
/** /**
@ -30,14 +63,48 @@ class SubsiteAdminFunctionalTest extends FunctionalTest {
function testEditorCanAccessAllSubsites() { function testEditorCanAccessAllSubsites() {
$member = $this->objFromFixture('Member', 'editor'); $member = $this->objFromFixture('Member', 'editor');
Session::set("loggedInAs", $member->ID); Session::set("loggedInAs", $member->ID);
$this->get('admin/pages?SubsiteID=0&ajax=1');
$this->get('admin');
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site');
$mainSubsite = $this->objFromFixture('Subsite', 'main'); $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->get("admin/pages?SubsiteID={$mainSubsite->ID}&ajax=1"); $this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
$this->get('admin'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$this->assertEquals(Subsite::currentSubsiteID(), $mainSubsite->ID, 'Can access the subsite');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable');
}
/**
* Test a member who only has access to one subsite (subsite1) and only some sections (pages and security).
*/
function testSubsiteAdmin() {
$member = $this->objFromFixture('Member', 'subsite1member');
Session::set("loggedInAs", $member->ID);
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
// Check allowed URL.
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access own subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Can access permitted section.');
// Check forbidden section in allowed subsite.
$this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected within subsite.');
$this->assertNotRegExp('#^admin/assets/.*#', $this->mainSession->lastUrl(),
'Is redirected away from forbidden section');
// Check forbidden site.
$this->getAndFollowAll("admin/pages/?SubsiteID=0");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to permitted subsite.');
// Check the standalone XHR controller.
$response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable');
} }
} }

View File

@ -241,7 +241,29 @@ class SubsiteTest extends BaseSubsiteTest {
$_SERVER['HTTP_HOST'] = $originalHTTPHost; $_SERVER['HTTP_HOST'] = $originalHTTPHost;
} }
function testAllSites() {
$subsites = Subsite::all_sites();
$this->assertDOSEquals(array(
array('Title' =>'Main site'),
array('Title' =>'Template'),
array('Title' =>'Subsite1 Template'),
array('Title' =>'Subsite2 Template'),
array('Title' =>'Test 1'),
array('Title' =>'Test 2'),
array('Title' =>'Test 3')
), $subsites, 'Lists all subsites');
}
function testAllAccessibleSites() {
$member = $this->objFromFixture('Member', 'subsite1member');
$subsites = Subsite::all_accessible_sites(true, 'Main site', $member);
$this->assertDOSEquals(array(
array('Title' =>'Subsite1 Template')
), $subsites, 'Lists member-accessible sites.');
}
/** /**
* Test Subsite::accessible_sites() * Test Subsite::accessible_sites()
*/ */