Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F107785645
ConduitCall.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Thu, Apr 10, 19:11
Size
5 KB
Mime Type
text/x-php
Expires
Sat, Apr 12, 19:11 (2 d)
Engine
blob
Format
Raw Data
Handle
25478752
Attached To
rPH Phabricator
ConduitCall.php
View Options
<?php
/**
* Run a conduit method in-process, without requiring HTTP requests. Usage:
*
* $call = new ConduitCall('method.name', array('param' => 'value'));
* $call->setUser($user);
* $result = $call->execute();
*
*/
final
class
ConduitCall
{
private
$method
;
private
$request
;
private
$user
;
private
$servers
;
private
$forceLocal
;
public
function
__construct
(
$method
,
array
$params
)
{
$this
->
method
=
$method
;
$this
->
handler
=
$this
->
buildMethodHandler
(
$method
);
$this
->
servers
=
PhabricatorEnv
::
getEnvConfig
(
'conduit.servers'
);
$this
->
forceLocal
=
false
;
$invalid_params
=
array_diff_key
(
$params
,
$this
->
handler
->
defineParamTypes
());
if
(
$invalid_params
)
{
throw
new
ConduitException
(
"Method '{$method}' doesn't define these parameters: '"
.
implode
(
"', '"
,
array_keys
(
$invalid_params
))
.
"'."
);
}
if
(
$this
->
servers
)
{
$current_host
=
AphrontRequest
::
getHTTPHeader
(
'HOST'
);
foreach
(
$this
->
servers
as
$server
)
{
if
(
$current_host
===
id
(
new
PhutilURI
(
$server
))->
getDomain
())
{
$this
->
forceLocal
=
true
;
break
;
}
}
}
$this
->
request
=
new
ConduitAPIRequest
(
$params
);
}
public
function
setUser
(
PhabricatorUser
$user
)
{
$this
->
user
=
$user
;
return
$this
;
}
public
function
getUser
()
{
return
$this
->
user
;
}
public
function
setForceLocal
(
$force_local
)
{
$this
->
forceLocal
=
$force_local
;
return
$this
;
}
public
function
shouldForceLocal
()
{
return
$this
->
forceLocal
;
}
public
function
shouldRequireAuthentication
()
{
return
$this
->
handler
->
shouldRequireAuthentication
();
}
public
function
shouldAllowUnguardedWrites
()
{
return
$this
->
handler
->
shouldAllowUnguardedWrites
();
}
public
function
getRequiredScope
()
{
return
$this
->
handler
->
getRequiredScope
();
}
public
function
getErrorDescription
(
$code
)
{
return
$this
->
handler
->
getErrorDescription
(
$code
);
}
public
function
execute
()
{
$user
=
$this
->
getUser
();
if
(!
$user
)
{
$user
=
new
PhabricatorUser
();
}
$this
->
request
->
setUser
(
$user
);
if
(
$this
->
shouldRequireAuthentication
())
{
if
(!
$user
->
isLoggedIn
())
{
throw
new
ConduitException
(
"ERR-INVALID-AUTH"
);
}
// TODO: This would be slightly cleaner by just using a Query, but the
// Conduit auth workflow requires the Call and User be built separately.
// Just do it this way for the moment.
$application
=
$this
->
handler
->
getApplication
();
if
(
$application
)
{
$can_view
=
PhabricatorPolicyFilter
::
hasCapability
(
$user
,
$application
,
PhabricatorPolicyCapability
::
CAN_VIEW
);
if
(!
$can_view
)
{
throw
new
ConduitException
(
pht
(
"You do not have access to the application which provides this "
.
"API method."
));
}
}
}
if
(!
$this
->
shouldForceLocal
()
&&
$this
->
servers
)
{
$server
=
$this
->
pickRandomServer
(
$this
->
servers
);
$client
=
new
ConduitClient
(
$server
);
$params
=
$this
->
request
->
getAllParameters
();
$params
[
"__conduit__"
][
"isProxied"
]
=
true
;
if
(
$this
->
handler
->
shouldRequireAuthentication
())
{
$client
->
callMethodSynchronous
(
'conduit.connect'
,
array
(
'client'
=>
'PhabricatorConduit'
,
'clientVersion'
=>
'1.0'
,
'user'
=>
$this
->
getUser
()->
getUserName
(),
'certificate'
=>
$this
->
getUser
()->
getConduitCertificate
(),
'__conduit__'
=>
$params
[
"__conduit__"
],
));
}
return
$client
->
callMethodSynchronous
(
$this
->
method
,
$params
);
}
else
{
return
$this
->
handler
->
executeMethod
(
$this
->
request
);
}
}
protected
function
pickRandomServer
(
$servers
)
{
return
$servers
[
array_rand
(
$servers
)];
}
protected
function
buildMethodHandler
(
$method
)
{
$method_class
=
ConduitAPIMethod
::
getClassNameFromAPIMethodName
(
$method
);
// Test if the method exists.
$ok
=
false
;
try
{
$ok
=
class_exists
(
$method_class
);
}
catch
(
Exception
$ex
)
{
// Discard, we provide a more specific exception below.
}
if
(!
$ok
)
{
throw
new
ConduitException
(
"Conduit method '{$method}' does not exist."
);
}
$class_info
=
new
ReflectionClass
(
$method_class
);
if
(
$class_info
->
isAbstract
())
{
throw
new
ConduitException
(
"Method '{$method}' is not valid; the implementation is an abstract "
.
"base class."
);
}
$method
=
newv
(
$method_class
,
array
());
if
(!(
$method
instanceof
ConduitAPIMethod
))
{
throw
new
ConduitException
(
"Method '{$method_class}' is not valid; the implementation must be "
.
"a subclass of ConduitAPIMethod."
);
}
$application
=
$method
->
getApplication
();
if
(
$application
&&
!
$application
->
isInstalled
())
{
$app_name
=
$application
->
getName
();
throw
new
ConduitException
(
"Method '{$method_class}' belongs to application '{$app_name}', "
.
"which is not installed."
);
}
return
$method
;
}
}
Event Timeline
Log In to Comment