ENHANCEMENT Allowing strict subdomain checks on 'www.example.com' vs. 'example.com' via Subsite::$strict_domain_matching (AIR-54)

This commit is contained in:
Ingo Schommer 2011-09-09 11:59:46 +02:00
parent 32d51ed986
commit 2b506b02b1
3 changed files with 171 additions and 11 deletions

View File

@ -65,6 +65,13 @@ You can mix the two together, if you want to have some subsites hosted off subdo
Note that every site also has a ''www.''-prefixed version of the domain available. For example, if your subsite is accessible from ''wellington.example.org'' then it will also be accessible from '''www.wellington.example.org''. Note that every site also has a ''www.''-prefixed version of the domain available. For example, if your subsite is accessible from ''wellington.example.org'' then it will also be accessible from '''www.wellington.example.org''.
### Strict Subdomain Matching ###
The module tries to provide sensible defaults, in which it regards `example.com` and `www.example.com`
as the same domains. In case you want to distinguish between these variations,
set `Subsite::$strict_subdomain_matching` to TRUE. This won't affect wildcard/asterisk checks,
but removes the ambiguity about default subdomains.
### Permissions ### ### Permissions ###
Groups can be associated with one or more subsites, in which case the granted permissions Groups can be associated with one or more subsites, in which case the granted permissions

View File

@ -20,6 +20,7 @@ class Subsite extends DataObject implements PermissionProvider {
static $force_subsite = null; static $force_subsite = null;
static $write_hostmap = true; static $write_hostmap = true;
static $default_sort = "\"Title\" ASC"; static $default_sort = "\"Title\" ASC";
static $db = array( static $db = array(
@ -76,6 +77,13 @@ class Subsite extends DataObject implements PermissionProvider {
* are listed. * are listed.
*/ */
protected static $allowed_themes = array(); protected static $allowed_themes = array();
/**
* @var Boolean If set to TRUE, don't assume 'www.example.com' and 'example.com' are the same.
* Doesn't affect wildcard matching, so '*.example.com' will match 'www.example.com' (but not 'example.com')
* in both TRUE or FALSE setting.
*/
static $strict_subdomain_matching = false;
static function set_allowed_domains($domain){ static function set_allowed_domains($domain){
user_error('Subsite::set_allowed_domains() is deprecated; it is no longer necessary ' user_error('Subsite::set_allowed_domains() is deprecated; it is no longer necessary '
@ -320,7 +328,7 @@ JS;
static function getSubsiteIDForDomain($host = null, $returnMainIfNotFound = true) { static function getSubsiteIDForDomain($host = null, $returnMainIfNotFound = true) {
if($host == null) $host = $_SERVER['HTTP_HOST']; if($host == null) $host = $_SERVER['HTTP_HOST'];
$host = str_replace('www.','',$host); if(!Subsite::$strict_subdomain_matching) $host = preg_replace('/^www\./', '', $host);
$SQL_host = Convert::raw2sql($host); $SQL_host = Convert::raw2sql($host);
$matchingDomains = DataObject::get("SubsiteDomain", "'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')", $matchingDomains = DataObject::get("SubsiteDomain", "'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')",
@ -329,7 +337,15 @@ JS;
if($matchingDomains) { if($matchingDomains) {
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID')); $subsiteIDs = array_unique($matchingDomains->column('SubsiteID'));
if(sizeof($subsiteIDs) > 1) user_error("Multiple subsites match '$host'", E_USER_WARNING); $subsiteDomains = array_unique($matchingDomains->column('Domain'));
if(sizeof($subsiteIDs) > 1) {
throw new UnexpectedValueException(sprintf(
"Multiple subsites match on '%s': %s",
$host,
implode(',', $subsiteDomains)
));
}
return $subsiteIDs[0]; return $subsiteIDs[0];
} }
@ -538,7 +554,9 @@ JS;
if ($subsites) foreach($subsites as $subsite) { if ($subsites) foreach($subsites as $subsite) {
$domains = $subsite->Domains(); $domains = $subsite->Domains();
if ($domains) foreach($domains as $domain) { if ($domains) foreach($domains as $domain) {
$hostmap[str_replace('www.', '', $domain->Domain)] = $subsite->domain(); $domainStr = $domain->Domain;
if(!Subsite::$strict_subdomain_matching) $domainStr = preg_replace('/^www\./', '', $domainStr);
$hostmap[$domainStr] = $subsite->domain();
} }
if ($subsite->DefaultSite) $hostmap['default'] = $subsite->domain(); if ($subsite->DefaultSite) $hostmap['default'] = $subsite->domain();
} }

View File

@ -1,7 +1,21 @@
<?php <?php
class SubsiteTest extends SapphireTest { class SubsiteTest extends SapphireTest {
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; static $fixture_file = 'subsites/tests/SubsiteTest.yml';
function setUp() {
parent::setUp();
$this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching;
Subsite::$strict_subdomain_matching = false;
}
function tearDown() {
parent::tearDown();
Subsite::$strict_subdomain_matching = $this->origStrictSubdomainMatching;
}
/** /**
* Create a new subsite from the template and verify that all the template's pages are copied * Create a new subsite from the template and verify that all the template's pages are copied
@ -57,28 +71,65 @@ class SubsiteTest extends SapphireTest {
* Confirm that domain lookup is working * Confirm that domain lookup is working
*/ */
function testDomainLookup() { function testDomainLookup() {
// Clear existing fixtures
foreach(DataObject::get('Subsite') as $subsite) $subsite->delete();
foreach(DataObject::get('SubsiteDomain') as $domain) $domain->delete();
// Much more expressive than YML in this case
$subsite1 = $this->createSubsiteWithDomains(array(
'one.example.org' => true,
'one.*' => false,
));
$subsite2 = $this->createSubsiteWithDomains(array(
'two.mysite.com' => true,
'*.mysite.com' => false,
'subdomain.onmultiplesubsites.com' => false,
));
$subsite3 = $this->createSubsiteWithDomains(array(
'three.*' => true, // wildcards in primary domain are not recommended
'subdomain.unique.com' => false,
'*.onmultiplesubsites.com' => false,
));
$this->assertEquals( $this->assertEquals(
$this->idFromFixture('Subsite','domaintest1'), $subsite3->ID,
Subsite::getSubsiteIDForDomain('subdomain.unique.com'),
'Full unique match'
);
$this->assertEquals(
$subsite1->ID,
Subsite::getSubsiteIDForDomain('one.example.org'), Subsite::getSubsiteIDForDomain('one.example.org'),
'Full match' 'Full match, doesn\'t complain about multiple matches within a single subsite'
);
$failed = false;
try {
Subsite::getSubsiteIDForDomain('subdomain.onmultiplesubsites.com');
} catch(UnexpectedValueException $e) {
$failed = true;
}
$this->assertTrue(
$failed,
'Fails on multiple matches with wildcard vs. www across multiple subsites'
); );
$this->assertEquals( $this->assertEquals(
$this->idFromFixture('Subsite','domaintest1'), $subsite1->ID,
Subsite::getSubsiteIDForDomain('one.localhost'), Subsite::getSubsiteIDForDomain('one.unique.com'),
'Fuzzy match suffixed with asterisk (rule "one.*")' 'Fuzzy match suffixed with wildcard (rule "one.*")'
); );
$this->assertEquals( $this->assertEquals(
$this->idFromFixture('Subsite','domaintest2'), $subsite2->ID,
Subsite::getSubsiteIDForDomain('two.mysite.com'), Subsite::getSubsiteIDForDomain('two.mysite.com'),
'Matches correct subsite for rule' 'Matches correct subsite for rule'
); );
$this->assertEquals( $this->assertEquals(
$this->idFromFixture('Subsite','domaintest2'), $subsite2->ID,
Subsite::getSubsiteIDForDomain('other.mysite.com'), Subsite::getSubsiteIDForDomain('other.mysite.com'),
'Fuzzy match prefixed with asterisk (rule "*.mysite.com")' 'Fuzzy match prefixed with wildcard (rule "*.mysite.com")'
); );
$this->assertEquals( $this->assertEquals(
@ -88,6 +139,90 @@ class SubsiteTest extends SapphireTest {
); );
} }
function testStrictSubdomainMatching() {
// Clear existing fixtures
foreach(DataObject::get('Subsite') as $subsite) $subsite->delete();
foreach(DataObject::get('SubsiteDomain') as $domain) $domain->delete();
// Much more expressive than YML in this case
$subsite1 = $this->createSubsiteWithDomains(array(
'example.org' => true,
'example.com' => false,
'*.wildcard.com' => false,
));
$subsite2 = $this->createSubsiteWithDomains(array(
'www.example.org' => true,
'www.wildcard.com' => false,
));
Subsite::$strict_subdomain_matching = false;
$this->assertEquals(
$subsite1->ID,
Subsite::getSubsiteIDForDomain('example.org'),
'Exact matches without strict checking when not using www prefix'
);
$this->assertEquals(
$subsite1->ID,
Subsite::getSubsiteIDForDomain('www.example.org'),
'Matches without strict checking when using www prefix, still matching first domain regardless of www prefix (falling back to subsite primary key ordering)'
);
$this->assertEquals(
$subsite1->ID,
Subsite::getSubsiteIDForDomain('www.example.com'),
'Fuzzy matches without strict checking with www prefix'
);
$this->assertEquals(
0,
Subsite::getSubsiteIDForDomain('www.wildcard.com'),
'Doesn\'t match www prefix without strict check, even if a wildcard subdomain is in place'
);
Subsite::$strict_subdomain_matching = true;
$this->assertEquals(
$subsite1->ID,
Subsite::getSubsiteIDForDomain('example.org'),
'Matches with strict checking when not using www prefix'
);
$this->assertEquals(
$subsite2->ID, // not 1
Subsite::getSubsiteIDForDomain('www.example.org'),
'Matches with strict checking when using www prefix'
);
$this->assertEquals(
0,
Subsite::getSubsiteIDForDomain('www.example.com'),
'Doesn\'t fuzzy match with strict checking when using www prefix'
);
$failed = false;
try {
Subsite::getSubsiteIDForDomain('www.wildcard.com');
} catch(UnexpectedValueException $e) {
$failed = true;
}
$this->assertTrue(
$failed,
'Fails on multiple matches with strict checking and wildcard vs. www'
);
}
protected function createSubsiteWithDomains($domains) {
$subsite = new Subsite();
$subsite->write();
foreach($domains as $domainStr => $isPrimary) {
$domain = new SubsiteDomain(array(
'Domain' => $domainStr,
'IsPrimary' => $isPrimary,
'SubsiteID' => $subsite->ID
));
$domain->write();
}
return $subsite;
}
/** /**
* Test the Subsite->domain() method * Test the Subsite->domain() method