Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1431199
Utils.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
Utils.php
View Options
<?php
namespace
MediaWiki\Extension\OAuth\Backend
;
use
MediaWiki\Context\RequestContext
;
use
MediaWiki\Deferred\AutoCommitUpdate
;
use
MediaWiki\Deferred\DeferredUpdates
;
use
MediaWiki\Extension\Notifications\Model\Event
;
use
MediaWiki\Extension\OAuth\Lib\OAuthSignatureMethodHmacSha1
;
use
MediaWiki\MediaWikiServices
;
use
MediaWiki\Request\WebRequest
;
use
MediaWiki\Title\Title
;
use
MediaWiki\User\CentralId\CentralIdLookup
;
use
MediaWiki\User\User
;
use
MediaWiki\WikiMap\WikiMap
;
use
MWException
;
use
Wikimedia\ObjectCache\BagOStuff
;
use
Wikimedia\Rdbms\IDatabase
;
/**
* Static utility functions for OAuth
*
* @file
* @ingroup OAuth
*/
class
Utils
{
/**
* @return bool
*/
public
static
function
isCentralWiki
()
{
global
$wgMWOAuthCentralWiki
;
return
(
WikiMap
::
getCurrentWikiId
()
===
$wgMWOAuthCentralWiki
);
}
/**
* @return string|bool
*/
public
static
function
getCentralWiki
()
{
global
$wgMWOAuthCentralWiki
;
return
$wgMWOAuthCentralWiki
;
}
/**
* @param int $index DB_PRIMARY/DB_REPLICA
* @return IDatabase
*/
public
static
function
getCentralDB
(
$index
)
{
$lbFactory
=
MediaWikiServices
::
getInstance
()->
getDBLoadBalancerFactory
();
// T244415: Use the primary database if there were changes
if
(
$index
===
DB_REPLICA
&&
$lbFactory
->
hasOrMadeRecentPrimaryChanges
()
)
{
$index
=
DB_PRIMARY
;
}
$wikiId
=
self
::
getCentralWiki
();
if
(
WikiMap
::
isCurrentWikiId
(
$wikiId
)
)
{
$wikiId
=
false
;
}
return
$lbFactory
->
getMainLB
(
$wikiId
)->
getConnection
(
$index
,
[],
$wikiId
);
}
/**
* @return BagOStuff
*/
public
static
function
getSessionCache
()
{
global
$wgMWOAuthSessionCacheType
;
global
$wgSessionCacheType
;
$sessionCacheType
=
$wgMWOAuthSessionCacheType
??
$wgSessionCacheType
;
return
MediaWikiServices
::
getInstance
()
->
getObjectCacheFactory
()->
getInstance
(
$sessionCacheType
);
}
/**
* Get the cache type for OAuth 1.0 nonces
* @return BagOStuff
*/
public
static
function
getNonceCache
()
{
global
$wgMWOAuthNonceCacheType
,
$wgMWOAuthSessionCacheType
,
$wgSessionCacheType
;
$cacheType
=
$wgMWOAuthNonceCacheType
??
$wgMWOAuthSessionCacheType
??
$wgSessionCacheType
;
return
MediaWikiServices
::
getInstance
()
->
getObjectCacheFactory
()->
getInstance
(
$cacheType
);
}
/**
* @param IDatabase $db
* @return int[]
*/
public
static
function
getConsumerStateCounts
(
IDatabase
$db
)
{
$res
=
$db
->
newSelectQueryBuilder
()
->
select
(
[
'oarc_stage'
,
'count'
=>
'COUNT(*)'
]
)
->
from
(
'oauth_registered_consumer'
)
->
groupBy
(
'oarc_stage'
)
->
caller
(
__METHOD__
)
->
fetchResultSet
();
$table
=
[
Consumer
::
STAGE_APPROVED
=>
0
,
Consumer
::
STAGE_DISABLED
=>
0
,
Consumer
::
STAGE_EXPIRED
=>
0
,
Consumer
::
STAGE_PROPOSED
=>
0
,
Consumer
::
STAGE_REJECTED
=>
0
,
];
foreach
(
$res
as
$row
)
{
$table
[(
int
)
$row
->
oarc_stage
]
=
(
int
)
$row
->
count
;
}
return
$table
;
}
/**
* Get request headers.
* Sanitizes the output of apache_request_headers because
* we always want the keys to be Cased-Like-This and arh()
* returns the headers in the same case as they are in the
* request
* @return array Header name => value
*/
public
static
function
getHeaders
()
{
$request
=
RequestContext
::
getMain
()->
getRequest
();
$headers
=
$request
->
getAllHeaders
();
$out
=
[];
foreach
(
$headers
as
$key
=>
$value
)
{
$key
=
str_replace
(
" "
,
"-"
,
ucwords
(
strtolower
(
str_replace
(
"-"
,
" "
,
$key
)
)
)
);
$out
[
$key
]
=
$value
;
}
return
$out
;
}
/**
* Test this request for an OAuth Authorization header
* @param WebRequest $request the MediaWiki request
* @return bool true if a header was found
*/
public
static
function
hasOAuthHeaders
(
WebRequest
$request
)
{
$header
=
$request
->
getHeader
(
'Authorization'
);
return
$header
!==
false
&&
strpos
(
$header
,
'OAuth '
)
===
0
;
}
/**
* Make a cache key for the given arguments, that (hopefully) won't clash with
* anything else in your cache
* @param string ...$args
* @return string
*/
public
static
function
getCacheKey
(
...
$args
)
{
global
$wgMWOAuthCentralWiki
;
return
"OAUTH:$wgMWOAuthCentralWiki:"
.
implode
(
':'
,
$args
);
}
/**
* @param IDatabase $dbw
* @return void
*/
public
static
function
runAutoMaintenance
(
IDatabase
$dbw
)
{
global
$wgMWOAuthRequestExpirationAge
;
if
(
$wgMWOAuthRequestExpirationAge
<=
0
)
{
return
;
}
$cutoff
=
time
()
-
$wgMWOAuthRequestExpirationAge
;
$fname
=
__METHOD__
;
DeferredUpdates
::
addUpdate
(
new
AutoCommitUpdate
(
$dbw
,
__METHOD__
,
static
function
(
IDatabase
$dbw
)
use
(
$cutoff
,
$fname
)
{
$dbw
->
newUpdateQueryBuilder
()
->
update
(
'oauth_registered_consumer'
)
->
set
(
[
'oarc_stage'
=>
Consumer
::
STAGE_EXPIRED
,
'oarc_stage_timestamp'
=>
$dbw
->
timestamp
()
]
)
->
where
(
[
'oarc_stage'
=>
Consumer
::
STAGE_PROPOSED
,
$dbw
->
expr
(
'oarc_stage_timestamp'
,
'<'
,
$dbw
->
timestamp
(
$cutoff
)
)
]
)
->
caller
(
$fname
)
->
execute
();
}
)
);
}
/**
* Get the pretty name of an OAuth wiki ID restriction value
*
* @param string $wikiId A wiki ID or '*'
* @return string
*/
public
static
function
getWikiIdName
(
$wikiId
)
{
if
(
$wikiId
===
'*'
)
{
return
wfMessage
(
'mwoauth-consumer-allwikis'
)->
text
();
}
$host
=
WikiMap
::
getWikiName
(
$wikiId
);
if
(
strpos
(
$host
,
'.'
)
)
{
// e.g. "en.wikipedia.org"
return
$host
;
}
return
$wikiId
;
}
/**
* Get the pretty names of all local wikis
*
* @return string[] associative array of local wiki names indexed by wiki ID
*/
public
static
function
getAllWikiNames
()
{
global
$wgConf
;
$wikiNames
=
[];
foreach
(
$wgConf
->
getLocalDatabases
()
as
$dbname
)
{
$name
=
self
::
getWikiIdName
(
$dbname
);
if
(
$name
!=
$dbname
)
{
$wikiNames
[
$dbname
]
=
$name
;
}
}
return
$wikiNames
;
}
/**
* Quickly get a new server with all the default configurations
*
* @return MWOAuthServer with default configurations
*/
public
static
function
newMWOAuthServer
()
{
$store
=
static
::
newMWOAuthDataStore
();
$server
=
new
MWOAuthServer
(
$store
);
$server
->
add_signature_method
(
new
OAuthSignatureMethodHmacSha1
()
);
$server
->
add_signature_method
(
new
MWOAuthSignatureMethodRsaSha1
(
$store
)
);
return
$server
;
}
public
static
function
newMWOAuthDataStore
()
{
$lb
=
MediaWikiServices
::
getInstance
()->
getDBLoadBalancer
();
$dbr
=
self
::
getCentralDB
(
DB_REPLICA
);
$dbw
=
$lb
->
getServerCount
()
>
1
?
self
::
getCentralDB
(
DB_PRIMARY
)
:
null
;
return
new
MWOAuthDataStore
(
$dbr
,
$dbw
,
self
::
getSessionCache
(),
self
::
getNonceCache
()
);
}
/**
* Given a central wiki user ID, get a central username
*
* @param int $userId
* @param bool|User|string $audience show hidden names based on this user, or false for public
* @throws MWException
* @return string|bool Username, false if not found, empty string if name is hidden
*/
public
static
function
getCentralUserNameFromId
(
$userId
,
$audience
=
false
)
{
global
$wgMWOAuthSharedUserIDs
,
$wgMWOAuthSharedUserSource
;
// global ID required via hook
if
(
$wgMWOAuthSharedUserIDs
)
{
$lookup
=
MediaWikiServices
::
getInstance
()
->
getCentralIdLookupFactory
()
->
getLookup
(
$wgMWOAuthSharedUserSource
);
$name
=
$lookup
->
nameFromCentralId
(
$userId
,
$audience
===
'raw'
?
CentralIdLookup
::
AUDIENCE_RAW
:
(
$audience
?:
CentralIdLookup
::
AUDIENCE_PUBLIC
)
);
if
(
$name
===
null
)
{
$name
=
false
;
}
}
else
{
$name
=
''
;
$user
=
User
::
newFromId
(
$userId
);
$permissionManager
=
MediaWikiServices
::
getInstance
()->
getPermissionManager
();
if
(
$audience
===
'raw'
||
!
$user
->
isHidden
()
||
(
$audience
instanceof
User
&&
$permissionManager
->
userHasRight
(
$audience
,
'hideuser'
)
)
)
{
$name
=
$user
->
getName
();
}
}
return
$name
;
}
/**
* Given a central wiki user ID, get a local User object
*
* @param int $userId
* @return User|false False if not found
*/
public
static
function
getLocalUserFromCentralId
(
$userId
)
{
global
$wgMWOAuthSharedUserIDs
,
$wgMWOAuthSharedUserSource
;
// global ID required via hook
if
(
$wgMWOAuthSharedUserIDs
)
{
$lookup
=
MediaWikiServices
::
getInstance
()
->
getCentralIdLookupFactory
()
->
getLookup
(
$wgMWOAuthSharedUserSource
);
$user
=
$lookup
->
localUserFromCentralId
(
$userId
);
if
(
$user
===
null
||
!
$lookup
->
isAttached
(
$user
)
)
{
return
false
;
}
return
User
::
newFromIdentity
(
$user
);
}
return
User
::
newFromId
(
$userId
);
}
/**
* Given a local User object, get the user ID for that user on the central wiki
*
* @param User $user
* @return int|bool ID or false if not found
*/
public
static
function
getCentralIdFromLocalUser
(
User
$user
)
{
global
$wgMWOAuthSharedUserIDs
,
$wgMWOAuthSharedUserSource
;
// global ID required via hook
if
(
$wgMWOAuthSharedUserIDs
)
{
// T227688 do not rely on array auto-creation for non-stdClass
if
(
!
isset
(
$user
->
oAuthUserData
)
)
{
$user
->
oAuthUserData
=
[];
}
if
(
isset
(
$user
->
oAuthUserData
[
'centralId'
]
)
)
{
$id
=
$user
->
oAuthUserData
[
'centralId'
];
}
else
{
$lookup
=
MediaWikiServices
::
getInstance
()
->
getCentralIdLookupFactory
()
->
getLookup
(
$wgMWOAuthSharedUserSource
);
if
(
!
$lookup
->
isAttached
(
$user
)
)
{
$id
=
false
;
}
else
{
$id
=
$lookup
->
centralIdFromLocalUser
(
$user
);
if
(
$id
===
0
)
{
$id
=
false
;
}
}
// Process cache the result to avoid queries
$user
->
oAuthUserData
[
'centralId'
]
=
$id
;
}
}
else
{
$id
=
$user
->
getId
();
}
return
$id
;
}
/**
* Given a username, get the user ID for that user on the central wiki.
* @param string $username
* @return int|bool ID or false if not found
*/
public
static
function
getCentralIdFromUserName
(
$username
)
{
global
$wgMWOAuthSharedUserIDs
,
$wgMWOAuthSharedUserSource
;
// global ID required via hook
if
(
$wgMWOAuthSharedUserIDs
)
{
$lookup
=
MediaWikiServices
::
getInstance
()
->
getCentralIdLookupFactory
()
->
getLookup
(
$wgMWOAuthSharedUserSource
);
$id
=
$lookup
->
centralIdFromName
(
$username
);
if
(
$id
===
0
)
{
$id
=
false
;
}
}
else
{
$id
=
false
;
$user
=
User
::
newFromName
(
$username
);
if
(
$user
instanceof
User
&&
$user
->
getId
()
>
0
)
{
$id
=
$user
->
getId
();
}
}
return
$id
;
}
/**
* Get the effective secret key/token to use for OAuth purposes.
*
* For example, the "secret key" and "access secret" values that are
* used for authenticating request should be the result of applying this
* function to the respective values stored in the DB. This means that
* a leak of DB values is not enough to impersonate consumers.
*
* @param string $secret
* @return string
*/
public
static
function
hmacDBSecret
(
$secret
)
{
global
$wgOAuthSecretKey
,
$wgSecretKey
;
$secretKey
=
$wgOAuthSecretKey
??
$wgSecretKey
;
return
$secretKey
?
hash_hmac
(
'sha1'
,
$secret
,
$secretKey
)
:
$secret
;
}
/**
* Get a link to the central wiki's user talk page of a user.
*
* @param string $username the username of the User Talk link
* @return string the (proto-relative, urlencoded) url of the central wiki's user talk page
*/
public
static
function
getCentralUserTalk
(
$username
)
{
global
$wgMWOAuthCentralWiki
,
$wgMWOAuthSharedUserIDs
;
if
(
$wgMWOAuthSharedUserIDs
)
{
$url
=
WikiMap
::
getForeignURL
(
$wgMWOAuthCentralWiki
,
"User_talk:$username"
);
}
else
{
$url
=
Title
::
makeTitleSafe
(
NS_USER_TALK
,
$username
)->
getFullURL
();
}
return
$url
;
}
/**
* @param array $grants
* @return bool
*/
public
static
function
grantsAreValid
(
array
$grants
)
{
// Remove our special grants before calling the core method
$grants
=
array_diff
(
$grants
,
[
'mwoauth-authonly'
,
'mwoauth-authonlyprivate'
]
);
return
MediaWikiServices
::
getInstance
()
->
getGrantsInfo
()
->
grantsAreValid
(
$grants
);
}
/**
* Given an OAuth consumer stage change event, find out who needs to be notified.
* Will be used as an EchoAttributeManager::ATTR_LOCATORS callback.
* @param Event $event
* @return User[]
*/
public
static
function
locateUsersToNotify
(
Event
$event
)
{
$agent
=
$event
->
getAgent
();
$owner
=
self
::
getLocalUserFromCentralId
(
$event
->
getExtraParam
(
'owner-id'
)
);
$users
=
[];
switch
(
$event
->
getType
()
)
{
case
'oauth-app-propose'
:
// notify OAuth admins about new proposed apps
$oauthAdmins
=
self
::
getOAuthAdmins
();
foreach
(
$oauthAdmins
as
$admin
)
{
if
(
$admin
->
equals
(
$owner
)
)
{
continue
;
}
$users
[
$admin
->
getId
()]
=
$admin
;
}
break
;
case
'oauth-app-update'
:
case
'oauth-app-approve'
:
case
'oauth-app-reject'
:
case
'oauth-app-disable'
:
case
'oauth-app-reenable'
:
// notify owner if someone else changed the status of the app
if
(
!
$owner
->
equals
(
$agent
)
)
{
$users
[
$owner
->
getId
()]
=
$owner
;
}
break
;
}
return
$users
;
}
/**
* Get the change tag name for a given consumer.
* @param int $consumerId
* @return string
*/
public
static
function
getTagName
(
$consumerId
)
{
return
'OAuth CID: '
.
(
int
)
$consumerId
;
}
/**
* Check if a given change tag name should be reserved for this extension.
* @param string $tagName
* @return bool
*/
public
static
function
isReservedTagName
(
$tagName
)
{
return
stripos
(
$tagName
,
'oauth cid:'
)
===
0
;
}
/**
* Return a list of all OAuth admins (or the first 5000 in the unlikely case that there is more
* than that).
* Should be called on the central OAuth wiki.
* @return User[]
*/
protected
static
function
getOAuthAdmins
()
{
global
$wgOAuthGroupsToNotify
;
if
(
!
$wgOAuthGroupsToNotify
)
{
return
[];
}
return
iterator_to_array
(
User
::
findUsersByGroup
(
$wgOAuthGroupsToNotify
)
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, May 16, 20:10 (2 h, 47 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
cb/f0/330b47f122f9ff55ea25c70eb23c
Default Alt Text
Utils.php (13 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment