Skip to content

Commit 09def76

Browse files
authored
Merge pull request #15 from Staffbase/NFS-616-session-id-from-token
Nfs 616 session id from token
2 parents eae1e4f + 976b2ac commit 09def76

File tree

6 files changed

+382
-170
lines changed

6 files changed

+382
-170
lines changed

phpunit.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<phpunit colors="true" bootstrap="vendor/autoload.php">
2+
<phpunit colors="true" bootstrap="vendor/autoload.php" stderr="true" >
33
<testsuites>
44
<testsuite name="Plugins SDK unit test suite">
55
<file>test/SSODataTest.php</file>

src/PluginSession.php

Lines changed: 96 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class PluginSession extends SSOData
2727
{
2828
const QUERY_PARAM_JWT = 'jwt';
2929
const QUERY_PARAM_PID = 'pid';
30+
const QUERY_PARAM_SID = 'sessionID';
3031
const QUERY_PARAM_USERVIEW = 'userView';
3132

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

41+
/**
42+
* @var String $sessionId the id of the current session.
43+
*/
44+
private $sessionId = null;
45+
4046
/**
4147
* @var boolean $userView flag for userView mode.
4248
*/
4349
private $userView = true;
4450

51+
/**
52+
* @var SSOToken token data from the parsed jwt
53+
*/
54+
private $sso = null;
55+
4556
/**
4657
* Constructor
4758
*
@@ -64,13 +75,12 @@ public function __construct($pluginId, $appSecret, SessionHandlerInterface $sess
6475
if ($sessionHandler)
6576
session_set_save_handler($sessionHandler, true);
6677

67-
$this->openSession($pluginId);
6878

6979
$pid = isset($_GET[self::QUERY_PARAM_PID]) ? $_GET[self::QUERY_PARAM_PID] : null;
7080
$jwt = isset($_GET[self::QUERY_PARAM_JWT]) ? $_GET[self::QUERY_PARAM_JWT] : null;
81+
$sid = isset($_GET[self::QUERY_PARAM_SID]) ? $_GET[self::QUERY_PARAM_SID] : null;
7182

7283
// lets hint to bad class usage, as these cases should never happen.
73-
7484
if($pid && $jwt) {
7585
throw new SSOAuthenticationException('Tried to initialize the session with both PID and JWT provided.');
7686
}
@@ -80,50 +90,35 @@ public function __construct($pluginId, $appSecret, SessionHandlerInterface $sess
8090
}
8191

8292
$this->pluginInstanceId = $pid;
93+
$this->sessionId = $sid;
8394

8495
// we update the SSO info every time we get a token
8596
if ($jwt) {
86-
8797
// decrypt the token
88-
$sso = new SSOToken($appSecret, $jwt, $leeway);
89-
$ssoData = $sso->getData();
90-
91-
// dispatch remote calls from Staffbase
92-
if ($sso->isDeleteInstanceCall() && $remoteCallHandler) {
93-
94-
// we will accept unhandled calls with a warning
95-
$result = true;
96-
97-
$instanceId = $sso->getInstanceId();
98+
$this->sso = new SSOToken($appSecret, $jwt, $leeway);
9899

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

105-
// finish the remote call
106-
if($result)
107-
$remoteCallHandler->exitSuccess();
108-
else
109-
$remoteCallHandler->exitFailure();
104+
// dispatch remote calls from Staffbase
105+
if ($this->sso) {
106+
$this->deleteInstance($remoteCallHandler);
107+
}
110108

111-
$this->exitRemoteCall();
112-
}
109+
$this->openSession($pluginId);
113110

114-
// update data
115-
$this->pluginInstanceId = $sso->getInstanceId();
116-
$_SESSION[$this->pluginInstanceId][self::KEY_SSO] = $ssoData;
111+
if ($this->sso !== null) {
112+
$_SESSION[$this->pluginInstanceId][self::KEY_SSO] = $this->sso->getData();
117113
}
118114

115+
// decide if we are in user view or not
116+
$this->userView = !$this->isAdminView();
117+
119118
// requests with spoofed PID are not allowed
120119
if (!isset($_SESSION[$this->pluginInstanceId][self::KEY_SSO])
121-
|| empty($_SESSION[$this->pluginInstanceId][self::KEY_SSO]))
120+
|| empty($_SESSION[$this->pluginInstanceId][self::KEY_SSO]))
122121
throw new SSOAuthenticationException('Tried to access an instance without previous authentication.');
123-
124-
// decide if we are in user view or not
125-
if($this->isEditor() && (!isset($_GET[self::QUERY_PARAM_USERVIEW]) || $_GET[self::QUERY_PARAM_USERVIEW] !== 'true'))
126-
$this->userView = false;
127122
}
128123

129124
/**
@@ -134,6 +129,40 @@ public function __destruct() {
134129
$this->closeSession();
135130
}
136131

132+
private function isAdminView() {
133+
return $this->isEditor() && (!isset($_GET[self::QUERY_PARAM_USERVIEW]) || $_GET[self::QUERY_PARAM_USERVIEW] !== 'true');
134+
}
135+
136+
private function deleteInstance($remoteCallHandler){
137+
if (!$this->sso->isDeleteInstanceCall() || !$remoteCallHandler) {
138+
return;
139+
}
140+
141+
$instanceId = $this->sso->getInstanceId();
142+
143+
if ($remoteCallHandler instanceOf DeleteInstanceCallHandlerInterface) {
144+
$result = $remoteCallHandler->deleteInstance($instanceId);
145+
} else {
146+
// we will accept unhandled calls with a warning
147+
$result = true;
148+
error_log("Warning: An instance deletion call for instance $instanceId was not handled.");
149+
}
150+
151+
// finish the remote call
152+
if($result)
153+
$remoteCallHandler->exitSuccess();
154+
else
155+
$remoteCallHandler->exitFailure();
156+
157+
$this->exitRemoteCall();
158+
}
159+
160+
private function createCompatibleSessionId(String $string): String
161+
{
162+
$allowedChars = '/[^a-zA-Z0-9,-]/';
163+
return preg_replace($allowedChars, '-', $string);
164+
}
165+
137166
/**
138167
* Exit the script
139168
*
@@ -149,8 +178,11 @@ protected function exitRemoteCall() {
149178
*
150179
* @param string $name of the session
151180
*/
152-
protected function openSession($name) {
181+
protected function openSession(string $name) {
182+
183+
$sessionId = $this->createCompatibleSessionId($this->sessionId);
153184

185+
session_id($sessionId);
154186
session_name($name);
155187
session_start();
156188
}
@@ -167,12 +199,12 @@ protected function closeSession() {
167199
* (DEPRECATED) Translate a base64 string to PEM encoded public key.
168200
*
169201
* @param string $data base64 encoded key
170-
*
202+
* @deprecated
171203
* @return string PEM encoded key
172204
*/
173205
public static function base64ToPEMPublicKey($data) {
174206

175-
error_log("Warning: PluginSession::base64ToPEMPublicKey() is deprecated. Please switch over to SSOToken::base64ToPEMPublicKey().");
207+
error_log("Warning: PluginSession::base64ToPEMPublicKey() is deprecated. Please switch over to SSOToken::base64ToPEMPublicKey().");
176208

177209
return SSOToken::base64ToPEMPublicKey($data);
178210
}
@@ -260,4 +292,32 @@ public function isUserView() {
260292
return $this->userView;
261293
}
262294

295+
/**
296+
* Destroy the session with the given id
297+
*
298+
* @param String $sessionId
299+
* @return bool true on success or false on failure.
300+
*/
301+
public function destroySession(String $sessionId = null) {
302+
303+
$sessionId = $sessionId ?: $this->sessionId;
304+
305+
// save the current session
306+
$currentId = session_id();
307+
session_write_close();
308+
309+
// switch to the target session and removes it
310+
session_id($this->createCompatibleSessionId($sessionId));
311+
session_start();
312+
$result = session_destroy();
313+
314+
// switches back to the original session
315+
if ($currentId !== $sessionId) {
316+
session_id($currentId);
317+
session_start();
318+
}
319+
320+
return $result;
321+
}
322+
263323
}

src/SSOData.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ abstract class SSOData
2525
const CLAIM_NOT_BEFORE = 'nbf';
2626
const CLAIM_ISSUED_AT = 'iat';
2727
const CLAIM_ISSUER = 'iss';
28+
const CLAIM_SESSION_ID = 'sid';
2829
const CLAIM_INSTANCE_ID = 'instance_id';
2930
const CLAIM_INSTANCE_NAME = 'instance_name';
3031
const CLAIM_BRANCH_ID = 'branch_id';
@@ -81,7 +82,7 @@ abstract protected function getAllClaims();
8182
*/
8283
protected function getClaimSafe($name) {
8384

84-
if ($this->hasClaim($name))
85+
if ($this->hasClaim($name))
8586
return $this->getClaim($name);
8687

8788
return null;
@@ -159,6 +160,18 @@ public function getBranchSlug() {
159160
return $this->getClaimSafe(self::CLAIM_BRANCH_SLUG);
160161
}
161162

163+
/**
164+
* Get the cipher of the session id for the session the token was issued.
165+
*
166+
* The id will always be present.
167+
*
168+
* @return string
169+
*/
170+
public function getSessionId() {
171+
172+
return $this->getClaimSafe(self::CLAIM_SESSION_ID);
173+
}
174+
162175
/**
163176
* Get the (plugin) instance id for which the token was issued.
164177
*

0 commit comments

Comments
 (0)