Skip to content

Commit

Permalink
Merge pull request #15 from Staffbase/NFS-616-session-id-from-token
Browse files Browse the repository at this point in the history
Nfs 616 session id from token
  • Loading branch information
Ninerian committed Apr 24, 2020
2 parents eae1e4f + 976b2ac commit 09def76
Show file tree
Hide file tree
Showing 6 changed files with 382 additions and 170 deletions.
2 changes: 1 addition & 1 deletion phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="vendor/autoload.php">
<phpunit colors="true" bootstrap="vendor/autoload.php" stderr="true" >
<testsuites>
<testsuite name="Plugins SDK unit test suite">
<file>test/SSODataTest.php</file>
Expand Down
132 changes: 96 additions & 36 deletions src/PluginSession.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class PluginSession extends SSOData
{
const QUERY_PARAM_JWT = 'jwt';
const QUERY_PARAM_PID = 'pid';
const QUERY_PARAM_SID = 'sessionID';
const QUERY_PARAM_USERVIEW = 'userView';

const KEY_SSO = 'sso';
Expand All @@ -37,11 +38,21 @@ class PluginSession extends SSOData
*/
private $pluginInstanceId = null;

/**
* @var String $sessionId the id of the current session.
*/
private $sessionId = null;

/**
* @var boolean $userView flag for userView mode.
*/
private $userView = true;

/**
* @var SSOToken token data from the parsed jwt
*/
private $sso = null;

/**
* Constructor
*
Expand All @@ -64,13 +75,12 @@ public function __construct($pluginId, $appSecret, SessionHandlerInterface $sess
if ($sessionHandler)
session_set_save_handler($sessionHandler, true);

$this->openSession($pluginId);

$pid = isset($_GET[self::QUERY_PARAM_PID]) ? $_GET[self::QUERY_PARAM_PID] : null;
$jwt = isset($_GET[self::QUERY_PARAM_JWT]) ? $_GET[self::QUERY_PARAM_JWT] : null;
$sid = isset($_GET[self::QUERY_PARAM_SID]) ? $_GET[self::QUERY_PARAM_SID] : null;

// lets hint to bad class usage, as these cases should never happen.

if($pid && $jwt) {
throw new SSOAuthenticationException('Tried to initialize the session with both PID and JWT provided.');
}
Expand All @@ -80,50 +90,35 @@ public function __construct($pluginId, $appSecret, SessionHandlerInterface $sess
}

$this->pluginInstanceId = $pid;
$this->sessionId = $sid;

// we update the SSO info every time we get a token
if ($jwt) {

// decrypt the token
$sso = new SSOToken($appSecret, $jwt, $leeway);
$ssoData = $sso->getData();

// dispatch remote calls from Staffbase
if ($sso->isDeleteInstanceCall() && $remoteCallHandler) {

// we will accept unhandled calls with a warning
$result = true;

$instanceId = $sso->getInstanceId();
$this->sso = new SSOToken($appSecret, $jwt, $leeway);

if ($remoteCallHandler instanceOf DeleteInstanceCallHandlerInterface) {
$result = $remoteCallHandler->deleteInstance($instanceId);
} else {
error_log("Warning: An instance deletion call for instance $instanceId was not handled.");
}
$this->pluginInstanceId = $this->sso->getInstanceId();
$this->sessionId = $this->sso->getSessionId();
}

// finish the remote call
if($result)
$remoteCallHandler->exitSuccess();
else
$remoteCallHandler->exitFailure();
// dispatch remote calls from Staffbase
if ($this->sso) {
$this->deleteInstance($remoteCallHandler);
}

$this->exitRemoteCall();
}
$this->openSession($pluginId);

// update data
$this->pluginInstanceId = $sso->getInstanceId();
$_SESSION[$this->pluginInstanceId][self::KEY_SSO] = $ssoData;
if ($this->sso !== null) {
$_SESSION[$this->pluginInstanceId][self::KEY_SSO] = $this->sso->getData();
}

// decide if we are in user view or not
$this->userView = !$this->isAdminView();

// requests with spoofed PID are not allowed
if (!isset($_SESSION[$this->pluginInstanceId][self::KEY_SSO])
|| empty($_SESSION[$this->pluginInstanceId][self::KEY_SSO]))
|| empty($_SESSION[$this->pluginInstanceId][self::KEY_SSO]))
throw new SSOAuthenticationException('Tried to access an instance without previous authentication.');

// decide if we are in user view or not
if($this->isEditor() && (!isset($_GET[self::QUERY_PARAM_USERVIEW]) || $_GET[self::QUERY_PARAM_USERVIEW] !== 'true'))
$this->userView = false;
}

/**
Expand All @@ -134,6 +129,40 @@ public function __destruct() {
$this->closeSession();
}

private function isAdminView() {
return $this->isEditor() && (!isset($_GET[self::QUERY_PARAM_USERVIEW]) || $_GET[self::QUERY_PARAM_USERVIEW] !== 'true');
}

private function deleteInstance($remoteCallHandler){
if (!$this->sso->isDeleteInstanceCall() || !$remoteCallHandler) {
return;
}

$instanceId = $this->sso->getInstanceId();

if ($remoteCallHandler instanceOf DeleteInstanceCallHandlerInterface) {
$result = $remoteCallHandler->deleteInstance($instanceId);
} else {
// we will accept unhandled calls with a warning
$result = true;
error_log("Warning: An instance deletion call for instance $instanceId was not handled.");
}

// finish the remote call
if($result)
$remoteCallHandler->exitSuccess();
else
$remoteCallHandler->exitFailure();

$this->exitRemoteCall();
}

private function createCompatibleSessionId(String $string): String
{
$allowedChars = '/[^a-zA-Z0-9,-]/';
return preg_replace($allowedChars, '-', $string);
}

/**
* Exit the script
*
Expand All @@ -149,8 +178,11 @@ protected function exitRemoteCall() {
*
* @param string $name of the session
*/
protected function openSession($name) {
protected function openSession(string $name) {

$sessionId = $this->createCompatibleSessionId($this->sessionId);

session_id($sessionId);
session_name($name);
session_start();
}
Expand All @@ -167,12 +199,12 @@ protected function closeSession() {
* (DEPRECATED) Translate a base64 string to PEM encoded public key.
*
* @param string $data base64 encoded key
*
* @deprecated
* @return string PEM encoded key
*/
public static function base64ToPEMPublicKey($data) {

error_log("Warning: PluginSession::base64ToPEMPublicKey() is deprecated. Please switch over to SSOToken::base64ToPEMPublicKey().");
error_log("Warning: PluginSession::base64ToPEMPublicKey() is deprecated. Please switch over to SSOToken::base64ToPEMPublicKey().");

return SSOToken::base64ToPEMPublicKey($data);
}
Expand Down Expand Up @@ -260,4 +292,32 @@ public function isUserView() {
return $this->userView;
}

/**
* Destroy the session with the given id
*
* @param String $sessionId
* @return bool true on success or false on failure.
*/
public function destroySession(String $sessionId = null) {

$sessionId = $sessionId ?: $this->sessionId;

// save the current session
$currentId = session_id();
session_write_close();

// switch to the target session and removes it
session_id($this->createCompatibleSessionId($sessionId));
session_start();
$result = session_destroy();

// switches back to the original session
if ($currentId !== $sessionId) {
session_id($currentId);
session_start();
}

return $result;
}

}
15 changes: 14 additions & 1 deletion src/SSOData.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ abstract class SSOData
const CLAIM_NOT_BEFORE = 'nbf';
const CLAIM_ISSUED_AT = 'iat';
const CLAIM_ISSUER = 'iss';
const CLAIM_SESSION_ID = 'sid';
const CLAIM_INSTANCE_ID = 'instance_id';
const CLAIM_INSTANCE_NAME = 'instance_name';
const CLAIM_BRANCH_ID = 'branch_id';
Expand Down Expand Up @@ -81,7 +82,7 @@ abstract protected function getAllClaims();
*/
protected function getClaimSafe($name) {

if ($this->hasClaim($name))
if ($this->hasClaim($name))
return $this->getClaim($name);

return null;
Expand Down Expand Up @@ -159,6 +160,18 @@ public function getBranchSlug() {
return $this->getClaimSafe(self::CLAIM_BRANCH_SLUG);
}

/**
* Get the cipher of the session id for the session the token was issued.
*
* The id will always be present.
*
* @return string
*/
public function getSessionId() {

return $this->getClaimSafe(self::CLAIM_SESSION_ID);
}

/**
* Get the (plugin) instance id for which the token was issued.
*
Expand Down
Loading

0 comments on commit 09def76

Please sign in to comment.