jenkins-bot has submitted this change and it was merged.
Change subject: Fix MobileContext::getBaseDomain() method
......................................................................
Fix MobileContext::getBaseDomain() method
Improvements around getting base domain for optin and stopMobileRedirect
cookies
Changes:
- extract logic to separate BaseDomainExtractor class
- added beta cluster handling
- improved handling WMF projects
- when no match return full domain instead of only two parts
Bug: T148975
Bug: T150768
Change-Id: Ieab107cdc32ff9d4911f3794d1662bdf80c98a2e
---
M extension.json
A includes/BaseDomainExtractorInterface.php
M includes/MobileContext.php
A includes/WMFBaseDomainExtractor.php
M tests/phpunit/MobileContextTest.php
A tests/phpunit/WMFBaseDomainExtractorTest.php
6 files changed, 204 insertions(+), 46 deletions(-)
Approvals:
Bmansurov: Looks good to me, but someone else must approve
jenkins-bot: Verified
Phuedx: Looks good to me, approved
diff --git a/extension.json b/extension.json
index 5682dbb..7bbe133 100644
--- a/extension.json
+++ b/extension.json
@@ -52,6 +52,8 @@
"ExtMobileFrontend": "includes/MobileFrontend.body.php",
"MobileFrontendHooks": "includes/MobileFrontend.hooks.php",
"MobileFrontendSkinHooks":
"includes/MobileFrontend.skin.hooks.php",
+ "MobileFrontend\\BaseDomainExtractorInterface":
"includes/BaseDomainExtractorInterface.php",
+ "MobileFrontend\\WMFBaseDomainExtractor":
"includes/WMFBaseDomainExtractor.php",
"MobileContext": "includes/MobileContext.php",
"MobileFormatter": "includes/MobileFormatter.php",
"MobileCollection": "includes/models/MobileCollection.php",
diff --git a/includes/BaseDomainExtractorInterface.php
b/includes/BaseDomainExtractorInterface.php
new file mode 100644
index 0000000..3b323e6
--- /dev/null
+++ b/includes/BaseDomainExtractorInterface.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * BaseDomainExtractorInterface.php
+ */
+namespace MobileFrontend;
+
+/**
+ * Helper for operations on domain names
+ *
+ * Interface DomainExtractorInterface
+ */
+interface BaseDomainExtractorInterface {
+
+ /**
+ * Try to extract the base domain from $server
+ * Returns $server if no base domain is found.
+ *
+ * @param string $server URL
+ * @return string Hostname
+ */
+ public function getCookieDomain( $server );
+}
diff --git a/includes/MobileContext.php b/includes/MobileContext.php
index 5556cb3..cc21c88 100644
--- a/includes/MobileContext.php
+++ b/includes/MobileContext.php
@@ -5,11 +5,13 @@
use MediaWiki\MediaWikiServices;
use MobileFrontend\Devices\DeviceDetectorService;
+use MobileFrontend\WMFBaseDomainExtractor;
/**
* Provide various request-dependant methods to use in mobile context
*/
class MobileContext extends ContextSource {
+
const USEFORMAT_COOKIE_NAME = 'mf_useformat';
const USER_MODE_PREFERENCE_NAME = 'mfMode';
const LAZY_LOAD_IMAGES_COOKIE_NAME = 'mfLazyLoadImages';
@@ -397,14 +399,10 @@
$user->setOption( self::USER_MODE_PREFERENCE_NAME, $mode );
$user->saveSettings();
- $host = $this->getBaseDomain();
- // Deal with people running off localhost. see
http://curl.haxx.se/rfc/cookie_spec.html
- if ( strpos( $host, '.' ) === false ) {
- $host = false;
- }
- $this->getRequest()->response()->setcookie( 'optin', $mode, 0,
- [ 'prefix' => '', 'domain' => $host ]
- );
+ $this->getRequest()->response()->setCookie( 'optin', $mode, 0, [
+ 'prefix' => '',
+ 'domain' => $this->getCookieDomain()
+ ] );
}
/**
@@ -666,38 +664,28 @@
}
/**
- * Return the basic second level domain or just IP adress
+ * Return the base level domain or IP address
+ *
* @return string
*/
- public function getBaseDomain() {
- $server = $this->getConfig()->get( 'Server' );
-
- $parsedUrl = wfParseUrl( $server );
- $host = $parsedUrl['host'];
- // Validates value as IP address
- if ( !IP::isValid( $host ) ) {
- $domainParts = explode( '.', $host );
- $domainParts = array_reverse( $domainParts );
- // Although some browsers will accept cookies without
the initial .,
- // ยป RFC 2109 requires it to be included.
- $host = count( $domainParts ) >= 2 ? '.' .
$domainParts[1] . '.' . $domainParts[0] : $host;
- }
-
- return $host;
+ public function getCookieDomain() {
+ $helper = new WMFBaseDomainExtractor();
+ return $helper->getCookieDomain( $this->getMFConfig()->get(
'Server' ) );
}
/**
* Determine the correct domain to use for the stopMobileRedirect cookie
*
* Will use $wgMFStopRedirectCookieHost if it's set, otherwise will use
- * result of getBaseDomain()
+ * result of getCookieDomain()
* @return string
*/
public function getStopMobileRedirectCookieDomain() {
$mfStopRedirectCookieHost = $this->getMFConfig()->get(
'MFStopRedirectCookieHost' );
if ( !$mfStopRedirectCookieHost ) {
- self::$mfStopRedirectCookieHost =
$this->getBaseDomain();
+ self::$mfStopRedirectCookieHost =
$this->getCookieDomain();
+
} else {
self::$mfStopRedirectCookieHost =
$mfStopRedirectCookieHost;
}
diff --git a/includes/WMFBaseDomainExtractor.php
b/includes/WMFBaseDomainExtractor.php
new file mode 100644
index 0000000..832b852
--- /dev/null
+++ b/includes/WMFBaseDomainExtractor.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * WMFBaseDomainExtractor.php
+ */
+namespace MobileFrontend;
+
+/**
+ * Utility class to find base domain for given host.
+ *
+ * This class contains a hardcoded list of all WMF hosts and WMF specific
domain logic. As we never
+ * experienced any bug requests from users and we do not change domains too
often there is no need
+ * to put that hosts list into settings.
+ *
+ * @see T148975
+ */
+class WMFBaseDomainExtractor implements BaseDomainExtractorInterface {
+ /**
+ * @var string[]
+ */
+ private $wmfWikiHosts = [
+ 'wikipedia.org',
+ 'wikibooks.org',
+ 'wikiversity.org',
+ 'wikinews.org',
+ 'wiktionary.org',
+ 'wikisource.org',
+ 'wikiquote.org',
+ 'wikivoyage.org',
+ 'wikidata.org',
+ 'mediawiki.org',
+ 'wikimediafoundation.org',
+ 'local.wmftest.net' // local vagrant instances
+ ];
+ /**
+ * @var string[]
+ */
+ private $wmfMultiDomainWikiHosts = [
+ '.wikimedia.org', // commons, office, meta, outreach,
wikimania, incubator, etc...
+ '.beta.wmflabs.org', // beta cluster
+ '.wmflabs.org' // all other labs
+ ];
+
+ /**
+ * Try to extract the WMF base domain from $server
+ * Returns $server if no WMF base domain is found.
+ *
+ * Although some browsers will accept cookies without the initial . in
domain
+ * RFC 2109 requires it to be included.
+ *
+ * @param string $server URL
+ * @return string Hostname
+ */
+ public function getCookieDomain( $server ) {
+ $parsedUrl = wfParseUrl( $server );
+ $host = $parsedUrl['host'];
+
+ $wikiHost = $this->matchBaseHostname( $host,
$this->wmfWikiHosts );
+ if ( $wikiHost !== false ) {
+ return '.' . $wikiHost;
+ }
+
+ $multiWikiHost = $this->matchBaseHostname( $host,
$this->wmfMultiDomainWikiHosts );
+ if ( $multiWikiHost !== false ) {
+ return '.' . $this->extractSubdomain( $host,
$multiWikiHost );
+ }
+ return $host;
+ }
+
+ /**
+ * Find out whether $hostname matches or is subdomain of any host from
$hosts array
+ *
+ * @param string $hostname Visited host
+ * @param string[] $hosts Array of all wikimedia hosts
+ * @return bool|string Returns wikimedia host basedomain, false when
not found
+ */
+ private function matchBaseHostname( $hostname, array $hosts ) {
+ foreach ( $hosts as $wmfHost ) {
+ if ( $this->endsWith( $hostname, $wmfHost ) ) {
+ return $wmfHost;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Parse $host and return $baseDomain with first subdomain
+ * ex: extractSubdomain('en.commons.wikimedia.org', '.wikimedia.org')
=> 'commons.wikimedia.org'
+ *
+ * This function assumes that $fullHostname is a subdomain of
$basedomain. Please
+ * do the endsWith() check first before calling this function
+ *
+ * @param string $fullHostname
+ * @param string $baseDomain
+ * @return string
+ */
+ private function extractSubdomain( $fullHostname, $baseDomain ) {
+ $length = strlen( $baseDomain );
+ $subdomains = explode( '.', substr( $fullHostname, 0, -$length
) );
+ if ( count( $subdomains ) == 0 ) {
+ return $baseDomain;
+ }
+ $subdomain = array_pop( $subdomains );
+ return $subdomain . $baseDomain;
+
+ }
+
+ /**
+ * Check that $haystack ends with $needle
+ *
+ * @param string $haystack
+ * @param string $needle
+ * @return bool
+ */
+ private function endsWith( $haystack, $needle ) {
+ $length = strlen( $needle );
+ if ( $length === 0 ) {
+ return true;
+ }
+ return substr( $haystack, -$length ) === $needle;
+ }
+}
diff --git a/tests/phpunit/MobileContextTest.php
b/tests/phpunit/MobileContextTest.php
index ff0571f..5208208 100644
--- a/tests/phpunit/MobileContextTest.php
+++ b/tests/phpunit/MobileContextTest.php
@@ -52,26 +52,6 @@
}
/**
- * @dataProvider getBaseDomainProvider
- * @covers MobileContext::getBaseDomain
- */
- public function testGetBaseDomain( $server, $baseDomain ) {
- $this->setMwGlobals( 'wgServer', $server );
- $this->assertEquals( $baseDomain,
$this->makeContext()->getBaseDomain() );
- }
-
- public function getBaseDomainProvider() {
- return [
- [ 'https://en.wikipedia.org', '.wikipedia.org' ],
- [ 'http://en.m.wikipedia.org', '.wikipedia.org' ],
- [ '//en.m.wikipedia.org', '.wikipedia.org' ],
- [ 'http://127.0.0.1', '127.0.0.1' ],
- [ 'http://127.0.0.1:8080', '127.0.0.1' ],
- [ 'http://localhost', 'localhost' ],
- ];
- }
-
- /**
* @covers MobileContext::getMobileUrl
*/
public function testGetMobileUrl() {
diff --git a/tests/phpunit/WMFBaseDomainExtractorTest.php
b/tests/phpunit/WMFBaseDomainExtractorTest.php
new file mode 100644
index 0000000..943c20f
--- /dev/null
+++ b/tests/phpunit/WMFBaseDomainExtractorTest.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @group MobileFrontend
+ */
+class WMFBaseDomainExtractorTest extends \PHPUnit_Framework_TestCase {
+ /**
+ * @dataProvider getBaseDomainProvider
+ * @covers MobileContext::getBaseDomain
+ */
+ public function testGetBaseDomain( $server, $baseDomain ) {
+ $extractor = new \MobileFrontend\WMFBaseDomainExtractor();
+ $this->assertEquals( $baseDomain, $extractor->getCookieDomain(
$server ) );
+ }
+
+ public function getBaseDomainProvider() {
+ return [
+ // Production wikis
+ [ 'http://wikipedia.org', '.wikipedia.org' ],
+ [ 'https://en.wikipedia.org', '.wikipedia.org' ],
+ [ 'http://en.m.wikipedia.org', '.wikipedia.org' ],
+ [ '//en.m.wikipedia.org', '.wikipedia.org' ],
+ [ 'http://wikimediafoundation.org',
'.wikimediafoundation.org' ],
+ [ 'http://wikiversity.org', '.wikiversity.org' ],
+ [ 'https://office.wikimedia.org',
'.office.wikimedia.org' ],
+ [ 'https://commons.wikimedia.org',
'.commons.wikimedia.org' ],
+ // Beta cluster
+ [ 'http://en.wikipedia.beta.wmflabs.org',
'.wikipedia.beta.wmflabs.org' ],
+ [ 'https://en.m.wikipedia.beta.wmflabs.org',
'.wikipedia.beta.wmflabs.org' ],
+ [ 'http://en.wikiversity.beta.wmflabs.org',
'.wikiversity.beta.wmflabs.org' ],
+ [ 'http://en.m.wikiversity.beta.wmflabs.org',
'.wikiversity.beta.wmflabs.org' ],
+ // IP address
+ [ 'http://127.0.0.1', '127.0.0.1' ],
+ [ 'http://127.0.0.1:8080', '127.0.0.1' ],
+ // Other possible domains/client instances
+ [ 'http://localhost', 'localhost' ],
+ [ 'http://mediawiki.dev', 'mediawiki.dev' ],
+ [ 'http://test.co.uk', 'test.co.uk' ],
+ [ 'http://wiki.test.com.pl', 'wiki.test.com.pl' ],
+ // Vagrant instances
+ [ 'http://php5.local.wmftest.net:8080/',
'.local.wmftest.net' ],
+ [ 'https://wiki.local.wmftest.net:8080/',
'.local.wmftest.net' ]
+ ];
+ }
+}
--
To view, visit https://gerrit.wikimedia.org/r/321796
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ieab107cdc32ff9d4911f3794d1662bdf80c98a2e
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Pmiazga <[email protected]>
Gerrit-Reviewer: Bmansurov <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: Phuedx <[email protected]>
Gerrit-Reviewer: Pmiazga <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits