Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,10 @@
<!--<exclude>tests/WAMP/AuthorizingRealmTest.php</exclude>-->
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>

</phpunit>
36 changes: 23 additions & 13 deletions src/Call.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,21 @@ class Call

private $invocationRequestId;

private $overrideCallerSession;

/**
* Constructor
*
* @param \Thruway\Session $callerSession
* @param \Thruway\Message\CallMessage $callMessage
* @param Procedure $procedure
* @param Session|null $overrideCallerSession
*/
public function __construct(
Session $callerSession,
CallMessage $callMessage,
Procedure $procedure
Procedure $procedure,
Session $overrideCallerSession = null
)
{
$this->callMessage = $callMessage;
Expand All @@ -101,6 +105,8 @@ public function __construct(

$this->callStart = microtime(true);
$this->invocationRequestId = Utils::getUniqueId();

$this->overrideCallerSession = $overrideCallerSession;
}

/**
Expand Down Expand Up @@ -296,19 +302,23 @@ public function getInvocationMessage()

$details = [];

if ($this->getRegistration()->getDiscloseCaller() === true && $this->getCallerSession()->getAuthenticationDetails()) {
$authenticationDetails = $this->getCallerSession()->getAuthenticationDetails();
$details = [
'caller' => $this->getCallerSession()->getSessionId(),
'authid' => $authenticationDetails->getAuthId(),
'authrole' => $authenticationDetails->getAuthRole(),
'authroles' => $authenticationDetails->getAuthRoles(),
'authmethod' => $authenticationDetails->getAuthMethod(),
];

if ($authenticationDetails->getAuthExtra() !== null) {
$details['_thruway_authextra'] = $authenticationDetails->getAuthExtra();
$sessionForDetails = $this->overrideCallerSession === null ? $this->getCallerSession() : $this->overrideCallerSession;

if ($this->getRegistration()->getDiscloseCaller() === true) {
if ($sessionForDetails->getAuthenticationDetails()) {
$authenticationDetails = $sessionForDetails->getAuthenticationDetails();
$details = [
'authid' => $authenticationDetails->getAuthId(),
'authrole' => $authenticationDetails->getAuthRole(),
'authroles' => $authenticationDetails->getAuthRoles(),
'authmethod' => $authenticationDetails->getAuthMethod(),
];

if ($authenticationDetails->getAuthExtra() !== null) {
$details['_thruway_authextra'] = $authenticationDetails->getAuthExtra();
}
}
$details['caller'] = $sessionForDetails->getSessionId();
}

// TODO: check to see if callee supports progressive call
Expand Down
48 changes: 45 additions & 3 deletions src/Procedure.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ class Procedure
*/
private $callQueue;

/**
* If this procedure is a hook, the original procedure lives here
*
* @var Procedure
*/
private $hookedProcedure;

/**
* Constructor
*
Expand All @@ -65,6 +72,13 @@ public function __construct($procedureName)
$this->callQueue = new SplQueue();
}

public static function createForHook($procedureName, Procedure $hookedProcedure) {
$new = new Procedure($procedureName);
$new->hookedProcedure = $hookedProcedure;

return $new;
}

/**
* Process register
*
Expand All @@ -77,6 +91,14 @@ public function processRegister(Session $session, RegisterMessage $msg)
{
$registration = Registration::createRegistrationFromRegisterMessage($session, $msg);

if ($registration->getAllowMultipleRegistrations() && $this->getHookedProcedure() !== null) {
$errorMsg = ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.failed');
$errorMsg->setArguments(['\'Registration for hooks can only use "\' . $registration::SINGLE_REGISTRATION . \'" invocation type.\'']);
$session->sendMessage($errorMsg);

return false;
}

if (count($this->registrations) > 0) {
// we already have something registered
if ($this->getAllowMultipleRegistrations()) {
Expand Down Expand Up @@ -181,6 +203,10 @@ public function getRegistrationById($registrationId)
}
}

if ($this->hookedProcedure !== null) {
return $this->hookedProcedure->getRegistrationById($registrationId);
}

return false;
}

Expand All @@ -196,7 +222,7 @@ public function processUnregister(Session $session, UnregisterMessage $msg)
for ($i = 0; $i < count($this->registrations); $i++) {
/** @var Registration $registration */
$registration = $this->registrations[$i];
if ($registration->getId() == $msg->getRegistrationId()) {
if ($registration->getId() === $msg->getRegistrationId()) {

// make sure the session is the correct session
if ($registration->getSession() !== $session) {
Expand All @@ -214,8 +240,6 @@ public function processUnregister(Session $session, UnregisterMessage $msg)
}
}

$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'wamp.error.no_such_registration'));

return false;
}

Expand Down Expand Up @@ -479,6 +503,24 @@ public function getRegistrations()
return $this->registrations;
}

/**
* @return Procedure
*/
public function getHookedProcedure()
{
return $this->hookedProcedure;
}

/**
* @param Procedure $hookedProcedure
*/
public function setHookedProcedure($hookedProcedure)
{
$this->hookedProcedure = $hookedProcedure;
}



/**
* process session leave
*
Expand Down
151 changes: 139 additions & 12 deletions src/Role/Dealer.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,25 @@ private function processRegister(Session $session, RegisterMessage $msg)
$this->procedures[$msg->getProcedureName()] = $procedure;
}

if (isset($msg->getOptions()->x_thruway_hook) && $msg->getOptions()->x_thruway_hook === true) {
if (count($procedure->getRegistrations()) === 0) {
$errorMsg = ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.failed');
$errorMsg->setArguments(['Unable to hook non-existent procedure ' . $msg->getProcedureName()]);
$session->sendMessage($errorMsg);

return;
}
// This is treated as a new procedure and the existing one is subjugated
$procedure = Procedure::createForHook($msg->getProcedureName(), $this->procedures[$msg->getProcedureName()]);
$this->procedures[$msg->getProcedureName()] = $procedure;
} else {
// This is not a hook - we need to traverse the hook-chain so any regular registrations
// end up on the regular one
while ($procedure->getHookedProcedure() !== null) {
$procedure = $procedure->getHookedProcedure();
}
}

if ($procedure->processRegister($session, $msg)) {
// registration succeeded
// make sure we have the registration in the collection
Expand All @@ -203,6 +222,18 @@ private function processRegister(Session $session, RegisterMessage $msg)

$this->registrationsBySession[$session] = $registrationsForThisSession;
}

return;
}

// Registration failed
if ($procedure->getHookedProcedure() !== null) {
// restore previous procedure as hook registration failed
$this->procedures[$msg->getProcedureName()] = $procedure->getHookedProcedure();
}

if (count($procedure->getRegistrations()) === 0) {
unset($this->procedures[$msg->getProcedureName()]);
}
}

Expand All @@ -218,25 +249,51 @@ private function processUnregister(Session $session, UnregisterMessage $msg)
$registration = $this->getRegistrationById($msg->getRegistrationId());

if ($registration && $this->procedures[$registration->getProcedureName()]) {
$prevProcedure = null;
$procedure = $this->procedures[$registration->getProcedureName()];
if ($procedure) {
if ($procedure->processUnregister($session, $msg)) {
// Unregistration was successful - remove from this sessions
// list of registrations
if ($this->registrationsBySession->contains($session) &&
in_array($procedure, $this->registrationsBySession[$session], true)
) {
$registrationsInSession = $this->registrationsBySession[$session];
array_splice($registrationsInSession, array_search($procedure, $registrationsInSession, true), 1);
while ($procedure !== null && !$procedure->processUnregister($session, $msg)) {
$prevProcedure = $procedure;
$procedure = $procedure->getHookedProcedure();
}
if ($procedure === null) {
// this appears to be unreachable as the registration has already been found somewhere
// in the procedure
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'wamp.error.no_such_registration'));
return false;
}
// Unregistration was successful - remove from this sessions
// list of registrations
if ($this->registrationsBySession->contains($session) &&
in_array($procedure, $this->registrationsBySession[$session], true)
) {
$registrationsInSession = $this->registrationsBySession[$session];
array_splice($registrationsInSession, array_search($procedure, $registrationsInSession, true), 1);
}

if (count($procedure->getRegistrations()) === 0) {
// if this is the top of the stack
if ($procedure === $this->procedures[$registration->getProcedureName()]) {
$this->procedures[$registration->getProcedureName()] = $procedure->getHookedProcedure();
if ($this->procedures[$registration->getProcedureName()] === null) {
unset($this->procedures[$registration->getProcedureName()]);
}

return true;
}

// this is not the top of the stack, so we need to
// reset the hookedProcedure of the procedure above to
// our hooked procedure
if ($prevProcedure !== null) {
$prevProcedure->setHookedProcedure($procedure->getHookedProcedure());
}
}

return;
return true;
}

// apparently we didn't find anything to unregister
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'wamp.error.no_such_procedure'));
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'wamp.error.no_such_registration'));
}

/**
Expand All @@ -263,7 +320,77 @@ private function processCall(Session $session, CallMessage $msg)
/* @var $procedure \Thruway\Procedure */
$procedure = $this->procedures[$msg->getProcedureName()];

$call = new Call($session, $msg, $procedure);
$overrideCallerSession = null;

// A Hook wants to call the hooked RPC
if (isset($msg->getOptions()->x_thruway_call_hooked)) {
$callHookedOptions = $msg->getOptions()->x_thruway_call_hooked;
if (!is_object($callHookedOptions)) {
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.invalid_call_options'));

return;
}
if (!isset($callHookedOptions->registration_id) || !is_numeric($callHookedOptions->registration_id)) {
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.invalid_call_registration_id'));

return;
}
$registrationId = $callHookedOptions->registration_id;
while ($procedure !== null) {
$registration = $procedure->getRegistrationById($registrationId);
if ($registration) {
if (!in_array($registration, $procedure->getRegistrations(), true)) {
$procedure = $procedure->getHookedProcedure();
continue;
}
// we have found the registration in the current procedure
// sanity and security checks
if ($session->getSessionId() !== $registration->getSession()->getSessionId()) {
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.not_yours'));

return;
}
if ($procedure->getHookedProcedure() === null) {
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.not_hooked'));

return;
}
$procedure = $procedure->getHookedProcedure();

// see if they want to use a concurrent call to override the
// caller info so it will look like the original call to the
// hooked procedure with_caller_from is an invocation request id
if (isset($callHookedOptions->with_caller_from)) {
if (!is_numeric($callHookedOptions->with_caller_from)) {
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.caller_from_invalid'));
return;
}
$callerFromInvocationId = $callHookedOptions->with_caller_from;
if (!isset($this->callInvocationIndex[$callerFromInvocationId])) {
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.caller_from_invalid'));
return;
}
$callerFromCall = $this->callInvocationIndex[$callerFromInvocationId];

if ($callerFromCall->getCalleeSession() !== $session) {
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.caller_from_not_yours'));
return;
}

$overrideCallerSession = $callerFromCall->getCallerSession();
}
break;
}
$procedure = $procedure->getHookedProcedure();
}
if ($procedure === null) {
$session->sendMessage(ErrorMessage::createErrorMessageFromMessage($msg, 'thruway.error.hook.bad_registration'));

return;
}
}

$call = new Call($session, $msg, $procedure, $overrideCallerSession);

$this->callInvocationIndex[$call->getInvocationRequestId()] = $call;
$this->callRequestIndex[$msg->getRequestId()] = $call;
Expand Down
8 changes: 1 addition & 7 deletions tests/Unit/ProcedureTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ public function testGetCallWithRequestIDAndGetRegistrationById()
/** @var \Thruway\Message\InvocationMessage $invocationMsg */
$invocationMsg = null;

$session->expects($this->exactly(4))
$session->expects($this->exactly(3))
->method("sendMessage")
->withConsecutive(
[
Expand All @@ -435,12 +435,6 @@ public function testGetCallWithRequestIDAndGetRegistrationById()
$this->assertInstanceOf('\Thruway\Message\InvocationMessage', $msg);
$invocationMsg = $msg;

return true;
})
], [
$this->callback(function ($msg) {
$this->assertInstanceOf('\Thruway\Message\ErrorMessage', $msg);
$this->assertEquals('wamp.error.no_such_registration', $msg->getErrorUri());
return true;
})
], [$this->isInstanceOf('\Thruway\Message\UnregisteredMessage')]
Expand Down
Loading