Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4074225
SharedDomainUtils.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
SharedDomainUtils.php
View Options
<?php
namespace
MediaWiki\Extension\CentralAuth
;
use
MediaWiki\Config\Config
;
use
MediaWiki\Extension\CentralAuth\Config\CAMainConfigNames
;
use
MediaWiki\Extension\CentralAuth\Hooks\Handlers\SharedDomainHookHandler
;
use
MediaWiki\HookContainer\HookRunner
;
use
MediaWiki\MainConfigNames
;
use
MediaWiki\Request\WebRequest
;
use
MediaWiki\SpecialPage\SpecialPageFactory
;
use
MediaWiki\WikiMap\WikiMap
;
use
MobileContext
;
use
RuntimeException
;
use
Wikimedia\Assert\Assert
;
/**
* Utilities for handling the shared domain name used for SUL3 login.
* This class is kept lightweight, so it can be used in early hooks.
*/
class
SharedDomainUtils
{
private
Config
$config
;
private
SpecialPageFactory
$specialPageFactory
;
private
HookRunner
$hookRunner
;
private
?
bool
$isSharedDomain
=
null
;
private
?
MobileContext
$mobileContext
;
private
bool
$isApiRequest
;
public
function
__construct
(
Config
$config
,
SpecialPageFactory
$specialPageFactory
,
HookRunner
$hookRunner
,
?
MobileContext
$mobileContext
,
bool
$isApiRequest
)
{
$this
->
config
=
$config
;
$this
->
specialPageFactory
=
$specialPageFactory
;
$this
->
hookRunner
=
$hookRunner
;
$this
->
mobileContext
=
$mobileContext
;
$this
->
isApiRequest
=
$isApiRequest
;
}
/**
* Return the URL prefix for using the shared authentication domain. The server will
* handle a request to this URL mostly the same as a request to the specified wiki.
* The returned prefix is equivalent to $wgCanonicalServer in how it should be used to
* construct URLs.
*
* @param ?string $wikiId The ID of the wiki that should be used for configuration when the
* shared domain URL is processed. Defaults to the current wiki ID.
* @return string|null URL prefix for the shared authentication domain, without a trailing
* slash; or null if the shared domain is not configured.
*/
public
function
getSharedDomainPrefix
(
?
string
$wikiId
=
null
):
?
string
{
$wikiId
??=
WikiMap
::
getCurrentWikiId
();
$sharedDomainCallback
=
$this
->
config
->
get
(
CAMainConfigNames
::
CentralAuthSharedDomainCallback
);
return
$sharedDomainCallback
?
$sharedDomainCallback
(
$wikiId
)
:
null
;
}
/**
* Whether the current request is to the shared domain used for SUL3 login.
*
* This assumes:
* - $wgCentralAuthSharedDomainCallback contains the shared domain.
* - $wgCanonicalServer is set in site configuration to the current domain
* (instead of the actual canonical domain) for requests to the shared domain.
*/
public
function
isSharedDomain
():
bool
{
if
(
$this
->
isSharedDomain
===
null
)
{
$sharedDomainPrefix
=
$this
->
getSharedDomainPrefix
();
if
(
!
$sharedDomainPrefix
)
{
$this
->
isSharedDomain
=
false
;
}
else
{
$sharedDomain
=
parse_url
(
$sharedDomainPrefix
,
PHP_URL_HOST
);
$currentDomain
=
parse_url
(
$this
->
config
->
get
(
MainConfigNames
::
CanonicalServer
),
PHP_URL_HOST
);
$this
->
isSharedDomain
=
$sharedDomain
&&
$currentDomain
===
$sharedDomain
;
}
}
return
$this
->
isSharedDomain
;
}
/**
* Whether the current request must deny non-auth actions.
*
* If $wgCentralAuthRestrictSharedDomain is enabled, then requests to the "fake"
* shared domain within $wgCentralAuthSharedDomainCallback must only be for authentication
* purposes. All non-authentication-related actions should be prevented.
*
* SUL3 login supports both using a dedicated login wiki for the domain where the central
* session cookies are stored, and a shared domain which serve any wiki (from a virtual
* subdirectory). In the latter case, we want to prevent non-authentication actions
* to prevent complications like cache splits. This flag differentiates between the two
* setups.
* @see SharedDomainHookHandler
*/
public
function
shouldRestrictCurrentDomain
():
bool
{
return
$this
->
isSharedDomain
()
&&
$this
->
config
->
get
(
CAMainConfigNames
::
CentralAuthRestrictSharedDomain
);
}
/**
* Whether the wiki supports SUL3 at all. This does not necessarily mean SUL3 is enabled on
* the wiki, but at a minimum, when this function returns true, it can be enabled for a given
* request by using the usesul3=1 URL parameter.
*/
public
function
canSul3BeEnabled
():
bool
{
return
(
bool
)
$this
->
config
->
get
(
CAMainConfigNames
::
CentralAuthSharedDomainCallback
);
}
/**
* Whether SUL3 mode is enabled on this wiki and/or this request.
*
* In order to facilitate testing and rollout of SUL3 migration,
* this method provides mechanisms for testing the SUL3 feature.
*
* SUL3 mode is available if $wgCentralAuthSharedDomainCallback is configured.
* It's enabled or disabled by default using $wgCentralAuthEnableSul3,
* which can be overridden by the user by setting the 'usesul3'
* URL query parameter to "1" or "0" (or other fuzzy bool values).
*
* @param WebRequest $request
* @param bool $excludeApiRequest Whether to exclude this API request from the
* SUL3 mode.
*/
public
function
isSul3Enabled
(
WebRequest
$request
,
bool
$excludeApiRequest
=
true
):
bool
{
if
(
!
$this
->
canSul3BeEnabled
()
)
{
return
false
;
}
// T379816: The `clientlogin` API should still work in SUL3 mode as if
// we're in SUL2 mode regardless of whether SUL3 is enabled or not.
// There are some edge-cases handled below like:
// - edits coming from VisualEditor that will trigger CentralLogin
// via the action API. Shouldn't really happen because we don't
// have VE enabled for anon users in production today but let's
// handle these;
// - a user trying to authenticate (login/signup) with their permanent
// account with a temporary account session active.
// T384523, T383812: Users sometimes will try to authenticate (login/signup)
// with an existing temporary session active. When this happens, we want
// to still assume SUL2 mode rather than try to trigger SUL3 login flow.
// This can happen for mobile apps (iOS for example) users.
// T390751: There is a special case where we want to create an account on-behalf
// of another user and mail them the temp password. In this case, the user
// that is creating the account is authenticated (API or regular web request).
// So in all cases, where we're using the API, regardless of the user (temp,
// anon or named), we want to just disable SUL3 and fallback to SUL2.
if
(
$this
->
isApiRequest
&&
$excludeApiRequest
&&
!
$this
->
isSharedDomain
()
)
{
return
false
;
}
if
(
$request
->
getCheck
(
'usesul3'
)
)
{
return
$request
->
getFuzzyBool
(
'usesul3'
);
}
return
(
bool
)
$this
->
config
->
get
(
CAMainConfigNames
::
CentralAuthEnableSul3
);
}
/**
* Assert that the SUL3 mode is allowed.
*
* @return void
*/
public
function
assertSul3Enabled
(
WebRequest
$request
)
{
Assert
::
precondition
(
$this
->
isSul3Enabled
(
$request
),
'SUL3 is not enabled. Set $wgCentralAuthEnableSul3 to boolean true.'
);
}
/**
* Assert that we're on the shared login domain.
*
* @return void
*/
public
function
assertIsSharedDomain
()
{
Assert
::
precondition
(
$this
->
isSharedDomain
(),
'This action is not allowed because the domain is not the shared login domain.'
);
}
/**
* Assert that we're not on the shared login domain.
*
* @return void
*/
public
function
assertIsNotSharedDomain
()
{
Assert
::
precondition
(
!(
$this
->
isSharedDomain
()
),
'This action is not allowed because the domain is not the shared login domain.'
);
}
/**
* Get the login/signup URL on the shared domain in SUL3 mode. Should only be called during
* authentication.
*
* @note This utility method supports URLs for SUL3 mode only. It
* doesn't have any concept of SUL2 mode. For shared domain
* URLs that support SUL2 and SUL3, see get the generic method
* CentralDomainUtils::getCentralDomainURL().
*
* @param string $action 'login' or 'signup' action
* @param WebRequest $request There could be more to look at
* in the request like if we're coming from a campaign link.
*/
public
function
getUrlForSharedDomainAction
(
string
$action
,
WebRequest
$request
):
string
{
switch
(
$action
)
{
case
'login'
:
$localUrl
=
$this
->
specialPageFactory
->
getTitleForAlias
(
'Userlogin'
)->
getLocalURL
();
break
;
case
'signup'
:
$localUrl
=
$this
->
specialPageFactory
->
getTitleForAlias
(
'CreateAccount'
)->
getLocalURL
();
break
;
default
:
throw
new
RuntimeException
(
'Unknown action: '
.
$action
);
}
$sharedDomainPrefix
=
$this
->
getSharedDomainPrefix
();
if
(
!
$sharedDomainPrefix
)
{
throw
new
RuntimeException
(
'SUL3 action used but $wgCentralAuthSharedDomainCallback not configured'
);
}
$url
=
$this
->
makeUrlDeviceCompliant
(
$sharedDomainPrefix
.
$localUrl
);
$params
=
[];
$this
->
hookRunner
->
onAuthPreserveQueryParams
(
$params
,
[
'request'
=>
$request
]
);
// replicate the non-hook part of LoginSignupSpecialPage::getPreservedParams()
$params
+=
[
'display'
=>
$request
->
getRawVal
(
'display'
),
'uselang'
=>
$request
->
getRawVal
(
'uselang'
),
'variant'
=>
$request
->
getRawVal
(
'variant'
),
'returnto'
=>
$request
->
getRawVal
(
'sul3-returnto'
)
??
$request
->
getRawVal
(
'returnto'
),
'returntoquery'
=>
$request
->
getRawVal
(
'sul3-returntoquery'
)
??
$request
->
getRawVal
(
'returntoquery'
),
'returntoanchor'
=>
$request
->
getRawVal
(
'sul3-returntoanchor'
)
??
$request
->
getRawVal
(
'returntoanchor'
),
'redoLocalAuthentication'
=>
$request
->
getRawVal
(
'redoLocalAuthentication'
),
];
// already handled in makeUrlDeviceCompliant()
unset
(
$params
[
'useformat'
]
);
// Make sure SUL3 opt-in state is preserved, e.g. in case of the user changing networks
$params
[
'usesul3'
]
=
'1'
;
// Skip the login form if the user is logged out on the local domain, but already logged in on the central
// domain. See LoginSignupSpecialPage: "In the case where the user is already logged in"...
if
(
(
$params
[
'returnto'
]
??
''
)
===
''
&&
(
$params
[
'returntoquery'
]
??
''
)
===
''
&&
!
$request
->
getSession
()->
getUser
()->
isNamed
()
&&
$action
===
'login'
)
{
// This is enough to pass the check in LoginSignupSpecialPage and make sure
// PostLoginRedirect is called even when the user is already logged in.
$params
[
'returntoquery'
]
=
'?'
;
// In the future maybe we'll want to use a more robust redirection mechanism instead of
// relying on PostLoginRedirect (see also T369467).
}
return
wfAppendQuery
(
$url
,
$params
);
}
/**
* @return bool True if on mobile device
*/
public
function
shouldUseMobile
():
bool
{
return
$this
->
mobileContext
&&
$this
->
mobileContext
->
shouldDisplayMobileView
();
}
/**
* Check the URL and apply transformation based on the device
* that is currently looking at it. If mobile, apply the mobile
* transformation to the URL so we view the correct rendering.
*
* Get the mobile domain (m.) version of the URL if available
* configured (in that WMF is currently configured to have separate
* domain for mobile and desktop versions of sites) and we want that
* instead of just appending a `useformat` query parameter, if the
* domain is a mobile domain, just return it but if it's not, we
* detect that and append a `useformat` query param..
*/
public
function
makeUrlDeviceCompliant
(
string
$url
):
string
{
// Assume either all or none of the wikis in the farm have MobileFrontend
if
(
!
$this
->
mobileContext
)
{
return
$url
;
}
$mobileUrl
=
$this
->
mobileContext
->
getMobileUrl
(
$url
);
// Some wikis don't have separate mobile and desktop versions at different URLs,
// in which case getMobileUrl() is a no-op.
$hasMobileUrl
=
(
$mobileUrl
!==
$url
);
if
(
$this
->
mobileContext
->
shouldDisplayMobileView
()
)
{
return
$hasMobileUrl
?
$mobileUrl
:
wfAppendQuery
(
$url
,
[
'useformat'
=>
'mobile'
]
);
}
else
{
// useformat=desktop is the default, and so we don't really need to set it,
// but we want to consider the possibility that the user has previously used
// the central domain and set it to mobile mode via a cookie. In that case,
// we want to prioritize the consistency of the current mode over that setting.
return
$hasMobileUrl
?
$url
:
wfAppendQuery
(
$url
,
[
'useformat'
=>
'desktop'
]
);
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 17 2026, 15:00 (10 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
20/33/0cce32bd1f281178350964fc81f5
Default Alt Text
SharedDomainUtils.php (11 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment