diff --git a/phpunit.xml b/phpunit.xml index b7e3e56..6929912 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,5 @@ - + test/SSODataTest.php diff --git a/src/PluginSession.php b/src/PluginSession.php index 7bec393..cbd0052 100644 --- a/src/PluginSession.php +++ b/src/PluginSession.php @@ -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'; @@ -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 * @@ -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.'); } @@ -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; } /** @@ -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 * @@ -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(); } @@ -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); } @@ -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; + } + } diff --git a/src/SSOData.php b/src/SSOData.php index 59ab506..f7acf15 100644 --- a/src/SSOData.php +++ b/src/SSOData.php @@ -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'; @@ -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; @@ -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. * diff --git a/test/PluginSessionTest.php b/test/PluginSessionTest.php index f455a57..b3cdb92 100644 --- a/test/PluginSessionTest.php +++ b/test/PluginSessionTest.php @@ -7,15 +7,16 @@ * * @category Authentication * @copyright 2017-2019 Staffbase, GmbH. - * @author Vitaliy Ivanov - * @license http://www.apache.org/licenses/LICENSE-2.0 - * @link https://github.com/staffbase/plugins-sdk-php + * @author Vitaliy Ivanov + * @license http://www.apache.org/licenses/LICENSE-2.0 + * @link https://github.com/staffbase/plugins-sdk-php */ namespace Staffbase\plugins\test; use ReflectionClass; use phpseclib\Crypt\RSA; use PHPUnit\Framework\TestCase; +use SessionHandlerInterface; use Staffbase\plugins\sdk\Exceptions\SSOAuthenticationException; use Staffbase\plugins\sdk\Exceptions\SSOException; use Staffbase\plugins\sdk\PluginSession; @@ -38,7 +39,7 @@ class PluginSessionTest extends TestCase */ public function __construct() { - parent::__construct(); + parent::__construct(); $rsa = new RSA(); $keypair = $rsa->createKey(); @@ -63,8 +64,11 @@ private function setupEnvironment($queryParamPid = null, $queryParamJwt = null, $_GET[PluginSession::QUERY_PARAM_PID] = $queryParamPid; $_GET[PluginSession::QUERY_PARAM_JWT] = $queryParamJwt; - if($clearSession) + if($clearSession) { + session_write_close(); + session_abort(); $_SESSION = []; + } } /** @@ -85,9 +89,9 @@ public function testConstructorWorksAsExpected() { ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); - $mock->expects($this->exactly(2)) - ->method('openSession') - ->with($this->pluginId); + $mock->expects($this->exactly(2)) + ->method('openSession') + ->with($this->pluginId); $reflectedClass = new ReflectionClass($this->classname); $constructor = $reflectedClass->getConstructor(); @@ -107,21 +111,18 @@ public function testConstructorWorksAsExpected() { */ public function testConstructorRejectsSpoofedPID() { - $this->setupEnvironment(null, $this->token); - $mock = $this->getMockBuilder($this->classname) ->disableOriginalConstructor() ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); + $this->setupEnvironment($this->pluginInstanceId. 'spoof', null, false); + + $this->expectException(SSOException::class); + $reflectedClass = new ReflectionClass($this->classname); $constructor = $reflectedClass->getConstructor(); $constructor->invoke($mock, $this->pluginId, $this->publicKey); - - $this->setupEnvironment($this->pluginInstanceId. 'spoof', null, false); - - $this->expectException(SSOException::class); - $constructor->invoke($mock, $this->pluginId, $this->publicKey); } /** @@ -140,12 +141,12 @@ public function testConstructorRefuseEmptyPluginId() { ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); - $this->expectException(SSOException::class); - $this->expectExceptionMessage('Empty plugin ID.'); + $this->expectException(SSOException::class); + $this->expectExceptionMessage('Empty plugin ID.'); - $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); - $constructor->invoke($mock, '', $this->publicKey); + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + $constructor->invoke($mock, '', $this->publicKey); } /** @@ -164,12 +165,12 @@ public function testConstructorRefuseEmptySecret() { ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); - $this->expectException(SSOException::class); - $this->expectExceptionMessage('Empty app secret.'); + $this->expectException(SSOException::class); + $this->expectExceptionMessage('Empty app secret.'); - $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); - $constructor->invoke($mock, $this->pluginId, ''); + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + $constructor->invoke($mock, $this->pluginId, ''); } /** @@ -188,12 +189,12 @@ public function testConstructorRefuseEmptyEnv() { ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); - $this->expectException(SSOAuthenticationException::class); - $this->expectExceptionMessage('Missing PID or JWT query parameter in Request.'); + $this->expectException(SSOAuthenticationException::class); + $this->expectExceptionMessage('Missing PID or JWT query parameter in Request.'); - $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); - $constructor->invoke($mock, $this->pluginId, $this->publicKey); + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + $constructor->invoke($mock, $this->pluginId, $this->publicKey); } /** @@ -212,12 +213,12 @@ public function testConstructorRefuseHavingBothJwtAndPid() { ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); - $this->expectException(SSOAuthenticationException::class); - $this->expectExceptionMessage('Tried to initialize the session with both PID and JWT provided.'); + $this->expectException(SSOAuthenticationException::class); + $this->expectExceptionMessage('Tried to initialize the session with both PID and JWT provided.'); - $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); - $constructor->invoke($mock, $this->pluginId, $this->publicKey); + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + $constructor->invoke($mock, $this->pluginId, $this->publicKey); } /** @@ -236,8 +237,8 @@ public function testConstructorUpdatesInfoOnJwt() { ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); - /** @var PluginSession $session */ - $session = new $mock($this->pluginId, $this->publicKey); + /** @var PluginSession $session */ + $session = new $mock($this->pluginId, $this->publicKey); $this->assertEquals($session->getRole(), $this->tokenData[PluginSession::CLAIM_USER_ROLE]); @@ -246,9 +247,10 @@ public function testConstructorUpdatesInfoOnJwt() { $newToken = SSOTokenTest::createSignedTokenFromData($this->privateKey, $tokenData); $this->setupEnvironment(null, $newToken, false); + + /** @var PluginSession $newSession */ $newSession = new $mock($this->pluginId, $this->publicKey); - /** @var PluginSession $newSession */ $this->assertEquals($newSession->getRole(), $tokenData[PluginSession::CLAIM_USER_ROLE]); $this->assertEquals($session->getRole(), $newSession->getRole()); } @@ -271,7 +273,7 @@ public function testConstructorSupportMultipleInstances() { ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); - /** @var PluginSession $session */ + /** @var PluginSession $session */ $session = new $mock($this->pluginId, $this->publicKey); @@ -282,14 +284,14 @@ public function testConstructorSupportMultipleInstances() { $this->setupEnvironment(null, $newToken, false); - /** @var PluginSession $newSession */ - $newSession = new $mock($this->pluginId, $this->publicKey); + /** @var PluginSession $newSession */ + $newSession = new $mock($this->pluginId, $this->publicKey); $this->assertEquals($newSession->getRole(), $tokenData[PluginSession::CLAIM_USER_ROLE]); $this->assertNotEquals($session->getRole(), $newSession->getRole()); - $sessionVar = 'myvariable'; - $sessionVal = 'mysessiontestvalue'; + $sessionVar = 'myvariable'; + $sessionVal = 'mysessiontestvalue'; $sessionVal2 = 'mysessiontestvalue2'; $session->setSessionVar($sessionVar, $sessionVal); @@ -317,8 +319,8 @@ public function testGetSessionData() { ->onlyMethods(array('openSession', 'closeSession')) ->getMock(); - /** @var PluginSession $session */ - $session = new $mock($this->pluginId, $this->publicKey); + /** @var PluginSession $session */ + $session = new $mock($this->pluginId, $this->publicKey); $sessionData = [ 'test1' => 'val1', @@ -415,4 +417,133 @@ public function testDeleteFailedCallInterface() { new $Session($this->pluginId, $this->publicKey, null, 0, $handler); } + /** + * @test + * + * Test that a session is created. + * + * @covers \Staffbase\plugins\sdk\PluginSession::__construct + */ + public function testSessionIsCreated() { + $tokenData = $this->tokenData; + $this->setupEnvironment(null, $this->token, true); + + $mock = $this->getMockBuilder($this->classname) + ->disableOriginalConstructor() + ->getMock(); + + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + + $this->assertEquals(PHP_SESSION_NONE, session_status()); + $constructor->invoke($mock, $this->pluginId, $this->publicKey); + $this->assertEquals(PHP_SESSION_ACTIVE, session_status()); + + $this->assertEquals($tokenData[PluginSession::CLAIM_SESSION_ID], session_id()); + } + + public function testSessionIdCheck() { + + $sessionHash = 'HOjLTR6+D5YIY0/waqJQp3Bg='; + $sessionId = 'HOjLTR6-D5YIY0-waqJQp3Bg-'; + + $tokenData = $this->tokenData; + $tokenData[PluginSession::CLAIM_SESSION_ID] = $sessionHash; + $token = SSOTokenTest::createSignedTokenFromData($this->privateKey, $tokenData); + + $this->setupEnvironment(null, $token, true); + + $mock = $this->getMockBuilder($this->classname) + ->disableOriginalConstructor() + ->getMock(); + + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + + $this->assertEquals(PHP_SESSION_NONE, session_status()); + $constructor->invoke($mock, $this->pluginId, $this->publicKey); + $this->assertEquals(PHP_SESSION_ACTIVE, session_status()); + + $this->assertEquals($sessionId, session_id()); + } + + public function testDestroyOtherSession() { + + $sessionHash = 'HOjLTR6+D5YIY0/waqJQp3Bg='; + $sessionId = 'HOjLTR6-D5YIY0-waqJQp3Bg-'; + + $tokenData = $this->tokenData; + $tokenData[PluginSession::CLAIM_SESSION_ID] = $sessionHash; + $token = SSOTokenTest::createSignedTokenFromData($this->privateKey, $tokenData); + + // successfull remote call handler mock + $handler = $this->getMockBuilder(SessionHandlerInterface::class) + ->setMethodsExcept() + ->getMock(); + + $handler->method('close')->willReturn(true); + $handler->method('destroy')->willReturn(true); + $handler->method('open')->willReturn(true); + $handler->method('write')->willReturn(true); + $handler->method('read')->willReturn($sessionId); + + $this->setupEnvironment(null, $token, true); + + /** @var SessionHandlerInterface $handler */ + new PluginSession($this->pluginId, $this->publicKey); + + $this->setupEnvironment(null, $this->token, false); + + /** @var PluginSession $session */ + $session = new PluginSession($this->pluginId, $this->publicKey, $handler); + + $handler->expects($this->once()) + ->method('destroy') + ->with($sessionId); + + $handler->expects($this->exactly(2)) + ->method('write') + ->with($this->logicalOr( + $this->equalTo($sessionId), + $this->equalTo($this->tokenData[PluginSession::CLAIM_SESSION_ID]) + )); + + $handler->expects($this->exactly(2)) + ->method('open'); + + $session->destroySession($sessionHash); + } + + public function testDestroyOwnSession() { + + $sessionId = $this->tokenData[PluginSession::CLAIM_SESSION_ID]; + $this->setupEnvironment(null, $this->token, false); + + // successfull remote call handler mock + $handler = $this->getMockBuilder(SessionHandlerInterface::class) + ->setMethodsExcept() + ->getMock(); + + $handler->method('close')->willReturn(true); + $handler->method('destroy')->willReturn(true); + $handler->method('open')->willReturn(true); + $handler->method('write')->willReturn(true); + $handler->method('read')->willReturn($sessionId); + + /** @var PluginSession $session */ + $session = new PluginSession($this->pluginId, $this->publicKey, $handler); + + $handler->expects($this->once()) + ->method('destroy') + ->with($sessionId); + + $handler->expects($this->once()) + ->method('write') + ->with($sessionId); + + $handler->expects($this->once()) + ->method('open'); + + $session->destroySession($sessionId); + } } diff --git a/test/SSODataTest.php b/test/SSODataTest.php index b7cda0f..589ff5d 100644 --- a/test/SSODataTest.php +++ b/test/SSODataTest.php @@ -52,6 +52,7 @@ public static function getTokenData() $tokenData[SSOData::CLAIM_USER_TAGS] = ['profile:field1:val', 'profile:field2:val']; $tokenData[SSOData::CLAIM_BRANCH_ID] = "dev-id"; $tokenData[SSOData::CLAIM_BRANCH_SLUG] = "dev-slug"; + $tokenData[SSOData::CLAIM_SESSION_ID] = "session-id"; return $tokenData; } @@ -86,6 +87,7 @@ public static function getTokenAccesors() $accessors[SSOData::CLAIM_USER_TAGS] = 'getTags'; $accessors[SSOData::CLAIM_BRANCH_ID] = "getBranchId"; $accessors[SSOData::CLAIM_BRANCH_SLUG] = "getBranchSlug"; + $accessors[SSOData::CLAIM_SESSION_ID] = 'getSessionId'; return $accessors; } @@ -115,6 +117,7 @@ public static function getTokenAccesors() * @covers \Staffbase\plugins\sdk\SSOData::getTags() * @covers \Staffbase\plugins\sdk\SSOData::getBranchId() * @covers \Staffbase\plugins\sdk\SSOData::getTags() + * @covers \Staffbase\plugins\sdk\SSOData::getSessionId() */ public function testAccessorsGiveCorrectValues() { diff --git a/test/SSOTokenTest.php b/test/SSOTokenTest.php index 4685b13..e4c7d6e 100644 --- a/test/SSOTokenTest.php +++ b/test/SSOTokenTest.php @@ -34,6 +34,8 @@ class SSOTokenTest extends TestCase * Constructor * * Creates an RSA-256 key pair. + * + * @return void */ public function setUp(): void { @@ -59,29 +61,30 @@ public static function createSignedTokenFromData($privateKey, $tokenData) { $signer = new Sha256(); $key = new Key($privateKey); - return (new Builder()) - ->issuedBy($tokenData[SSOToken::CLAIM_ISSUER]) - ->permittedFor($tokenData[SSOToken::CLAIM_AUDIENCE]) - ->issuedAt($tokenData[SSOToken::CLAIM_ISSUED_AT]) - ->canOnlyBeUsedAfter($tokenData[SSOToken::CLAIM_NOT_BEFORE]) - ->expiresAt($tokenData[SSOToken::CLAIM_EXPIRE_AT]) - ->withClaim(SSOToken::CLAIM_INSTANCE_ID, $tokenData[SSOToken::CLAIM_INSTANCE_ID]) - ->withClaim(SSOToken::CLAIM_INSTANCE_NAME, $tokenData[SSOToken::CLAIM_INSTANCE_NAME]) - ->withClaim(SSOToken::CLAIM_USER_ID, $tokenData[SSOToken::CLAIM_USER_ID]) - ->withClaim(SSOToken::CLAIM_USER_EXTERNAL_ID, $tokenData[SSOToken::CLAIM_USER_EXTERNAL_ID]) - ->withClaim(SSOToken::CLAIM_USER_FULL_NAME, $tokenData[SSOToken::CLAIM_USER_FULL_NAME]) - ->withClaim(SSOToken::CLAIM_USER_FIRST_NAME, $tokenData[SSOToken::CLAIM_USER_FIRST_NAME]) - ->withClaim(SSOToken::CLAIM_USER_LAST_NAME, $tokenData[SSOToken::CLAIM_USER_LAST_NAME]) - ->withClaim(SSOToken::CLAIM_USER_ROLE, $tokenData[SSOToken::CLAIM_USER_ROLE]) - ->withClaim(SSOToken::CLAIM_ENTITY_TYPE, $tokenData[SSOToken::CLAIM_ENTITY_TYPE]) - ->withClaim(SSOToken::CLAIM_THEME_TEXT_COLOR, $tokenData[SSOToken::CLAIM_THEME_TEXT_COLOR]) - ->withClaim(SSOToken::CLAIM_THEME_BACKGROUND_COLOR, $tokenData[SSOToken::CLAIM_THEME_BACKGROUND_COLOR]) - ->withClaim(SSOToken::CLAIM_USER_LOCALE, $tokenData[SSOToken::CLAIM_USER_LOCALE]) - ->withClaim(SSOToken::CLAIM_USER_TAGS, $tokenData[SSOToken::CLAIM_USER_TAGS]) - ->withClaim(SSOToken::CLAIM_BRANCH_ID, $tokenData[SSOToken::CLAIM_BRANCH_ID]) - ->withClaim(SSOToken::CLAIM_BRANCH_SLUG, $tokenData[SSOToken::CLAIM_BRANCH_SLUG]) - ->sign($signer, $key) - ->getToken(); + return (new Builder()) + ->issuedBy($tokenData[SSOToken::CLAIM_ISSUER]) + ->permittedFor($tokenData[SSOToken::CLAIM_AUDIENCE]) + ->issuedAt($tokenData[SSOToken::CLAIM_ISSUED_AT]) + ->canOnlyBeUsedAfter($tokenData[SSOToken::CLAIM_NOT_BEFORE]) + ->expiresAt($tokenData[SSOToken::CLAIM_EXPIRE_AT]) + ->withClaim(SSOToken::CLAIM_INSTANCE_ID, $tokenData[SSOToken::CLAIM_INSTANCE_ID]) + ->withClaim(SSOToken::CLAIM_INSTANCE_NAME, $tokenData[SSOToken::CLAIM_INSTANCE_NAME]) + ->withClaim(SSOToken::CLAIM_USER_ID, $tokenData[SSOToken::CLAIM_USER_ID]) + ->withClaim(SSOToken::CLAIM_USER_EXTERNAL_ID, $tokenData[SSOToken::CLAIM_USER_EXTERNAL_ID]) + ->withClaim(SSOToken::CLAIM_USER_FULL_NAME, $tokenData[SSOToken::CLAIM_USER_FULL_NAME]) + ->withClaim(SSOToken::CLAIM_USER_FIRST_NAME, $tokenData[SSOToken::CLAIM_USER_FIRST_NAME]) + ->withClaim(SSOToken::CLAIM_USER_LAST_NAME, $tokenData[SSOToken::CLAIM_USER_LAST_NAME]) + ->withClaim(SSOToken::CLAIM_USER_ROLE, $tokenData[SSOToken::CLAIM_USER_ROLE]) + ->withClaim(SSOToken::CLAIM_ENTITY_TYPE, $tokenData[SSOToken::CLAIM_ENTITY_TYPE]) + ->withClaim(SSOToken::CLAIM_THEME_TEXT_COLOR, $tokenData[SSOToken::CLAIM_THEME_TEXT_COLOR]) + ->withClaim(SSOToken::CLAIM_THEME_BACKGROUND_COLOR, $tokenData[SSOToken::CLAIM_THEME_BACKGROUND_COLOR]) + ->withClaim(SSOToken::CLAIM_USER_LOCALE, $tokenData[SSOToken::CLAIM_USER_LOCALE]) + ->withClaim(SSOToken::CLAIM_USER_TAGS, $tokenData[SSOToken::CLAIM_USER_TAGS]) + ->withClaim(SSOToken::CLAIM_BRANCH_ID, $tokenData[SSOToken::CLAIM_BRANCH_ID]) + ->withClaim(SSOToken::CLAIM_BRANCH_SLUG, $tokenData[SSOToken::CLAIM_BRANCH_SLUG]) + ->withClaim(SSOToken::CLAIM_SESSION_ID, $tokenData[SSOToken::CLAIM_SESSION_ID]) + ->sign($signer, $key) + ->getToken(); } /** @@ -93,28 +96,29 @@ public static function createSignedTokenFromData($privateKey, $tokenData) { */ private static function createUnsignedTokenFromData($tokenData) { - return (new Builder()) - ->issuedBy($tokenData[SSOToken::CLAIM_ISSUER]) - ->permittedFor($tokenData[SSOToken::CLAIM_AUDIENCE]) - ->issuedAt($tokenData[SSOToken::CLAIM_ISSUED_AT]) - ->canOnlyBeUsedAfter($tokenData[SSOToken::CLAIM_NOT_BEFORE]) - ->expiresAt($tokenData[SSOToken::CLAIM_EXPIRE_AT]) - ->withClaim(SSOToken::CLAIM_INSTANCE_ID, $tokenData[SSOToken::CLAIM_INSTANCE_ID]) - ->withClaim(SSOToken::CLAIM_INSTANCE_NAME, $tokenData[SSOToken::CLAIM_INSTANCE_NAME]) - ->withClaim(SSOToken::CLAIM_USER_ID, $tokenData[SSOToken::CLAIM_USER_ID]) - ->withClaim(SSOToken::CLAIM_USER_EXTERNAL_ID, $tokenData[SSOToken::CLAIM_USER_EXTERNAL_ID]) - ->withClaim(SSOToken::CLAIM_USER_FULL_NAME, $tokenData[SSOToken::CLAIM_USER_FULL_NAME]) - ->withClaim(SSOToken::CLAIM_USER_FIRST_NAME, $tokenData[SSOToken::CLAIM_USER_FIRST_NAME]) - ->withClaim(SSOToken::CLAIM_USER_LAST_NAME, $tokenData[SSOToken::CLAIM_USER_LAST_NAME]) - ->withClaim(SSOToken::CLAIM_USER_ROLE, $tokenData[SSOToken::CLAIM_USER_ROLE]) - ->withClaim(SSOToken::CLAIM_ENTITY_TYPE, $tokenData[SSOToken::CLAIM_ENTITY_TYPE]) - ->withClaim(SSOToken::CLAIM_THEME_TEXT_COLOR, $tokenData[SSOToken::CLAIM_THEME_TEXT_COLOR]) - ->withClaim(SSOToken::CLAIM_THEME_BACKGROUND_COLOR, $tokenData[SSOToken::CLAIM_THEME_BACKGROUND_COLOR]) - ->withClaim(SSOToken::CLAIM_USER_LOCALE, $tokenData[SSOToken::CLAIM_USER_LOCALE]) - ->withClaim(SSOToken::CLAIM_USER_TAGS, $tokenData[SSOToken::CLAIM_USER_TAGS]) - ->withClaim(SSOToken::CLAIM_BRANCH_ID, $tokenData[SSOToken::CLAIM_BRANCH_ID]) - ->withClaim(SSOToken::CLAIM_BRANCH_SLUG, $tokenData[SSOToken::CLAIM_BRANCH_SLUG]) - ->getToken(); + return (new Builder()) + ->issuedBy($tokenData[SSOToken::CLAIM_ISSUER]) + ->permittedFor($tokenData[SSOToken::CLAIM_AUDIENCE]) + ->issuedAt($tokenData[SSOToken::CLAIM_ISSUED_AT]) + ->canOnlyBeUsedAfter($tokenData[SSOToken::CLAIM_NOT_BEFORE]) + ->expiresAt($tokenData[SSOToken::CLAIM_EXPIRE_AT]) + ->withClaim(SSOToken::CLAIM_INSTANCE_ID, $tokenData[SSOToken::CLAIM_INSTANCE_ID]) + ->withClaim(SSOToken::CLAIM_INSTANCE_NAME, $tokenData[SSOToken::CLAIM_INSTANCE_NAME]) + ->withClaim(SSOToken::CLAIM_USER_ID, $tokenData[SSOToken::CLAIM_USER_ID]) + ->withClaim(SSOToken::CLAIM_USER_EXTERNAL_ID, $tokenData[SSOToken::CLAIM_USER_EXTERNAL_ID]) + ->withClaim(SSOToken::CLAIM_USER_FULL_NAME, $tokenData[SSOToken::CLAIM_USER_FULL_NAME]) + ->withClaim(SSOToken::CLAIM_USER_FIRST_NAME, $tokenData[SSOToken::CLAIM_USER_FIRST_NAME]) + ->withClaim(SSOToken::CLAIM_USER_LAST_NAME, $tokenData[SSOToken::CLAIM_USER_LAST_NAME]) + ->withClaim(SSOToken::CLAIM_USER_ROLE, $tokenData[SSOToken::CLAIM_USER_ROLE]) + ->withClaim(SSOToken::CLAIM_ENTITY_TYPE, $tokenData[SSOToken::CLAIM_ENTITY_TYPE]) + ->withClaim(SSOToken::CLAIM_THEME_TEXT_COLOR, $tokenData[SSOToken::CLAIM_THEME_TEXT_COLOR]) + ->withClaim(SSOToken::CLAIM_THEME_BACKGROUND_COLOR, $tokenData[SSOToken::CLAIM_THEME_BACKGROUND_COLOR]) + ->withClaim(SSOToken::CLAIM_USER_LOCALE, $tokenData[SSOToken::CLAIM_USER_LOCALE]) + ->withClaim(SSOToken::CLAIM_USER_TAGS, $tokenData[SSOToken::CLAIM_USER_TAGS]) + ->withClaim(SSOToken::CLAIM_BRANCH_ID, $tokenData[SSOToken::CLAIM_BRANCH_ID]) + ->withClaim(SSOToken::CLAIM_BRANCH_SLUG, $tokenData[SSOToken::CLAIM_BRANCH_SLUG]) + ->withClaim(SSOToken::CLAIM_SESSION_ID, $tokenData[SSOToken::CLAIM_SESSION_ID]) + ->getToken(); } /** @@ -131,12 +135,12 @@ public function testConstructorRefuseEmptySecret() { ->onlyMethods(array('parseToken')) ->getMock(); - $this->expectException(SSOException::class); - $this->expectExceptionMessage('Parameter appSecret for SSOToken is empty.'); + $this->expectException(SSOException::class); + $this->expectExceptionMessage('Parameter appSecret for SSOToken is empty.'); - $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); - $constructor->invoke($mock, ' ', 'fake token'); + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + $constructor->invoke($mock, ' ', 'fake token'); } /** @@ -153,35 +157,35 @@ public function testConstructorRefuseEmptyToken() { ->onlyMethods(array('parseToken')) ->getMock(); - $this->expectException(SSOException::class); - $this->expectExceptionMessage('Parameter tokenData for SSOToken is empty.'); + $this->expectException(SSOException::class); + $this->expectExceptionMessage('Parameter tokenData for SSOToken is empty.'); - $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); - $constructor->invoke($mock, 'fake secret', ' '); + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + $constructor->invoke($mock, 'fake secret', ' '); } - /** - * @test - * - * Test constructor throws exception on empty token. - * - * @covers \Staffbase\plugins\sdk\SSOToken::__construct - */ - public function testConstructorRefuseNonNumericLeeway() { + /** + * @test + * + * Test constructor throws exception on empty token. + * + * @covers \Staffbase\plugins\sdk\SSOToken::__construct + */ + public function testConstructorRefuseNonNumericLeeway() { - $mock = $this->getMockBuilder($this->classname) - ->disableOriginalConstructor() - ->onlyMethods(array('parseToken')) - ->getMock(); + $mock = $this->getMockBuilder($this->classname) + ->disableOriginalConstructor() + ->onlyMethods(array('parseToken')) + ->getMock(); - $this->expectException(SSOException::class); - $this->expectExceptionMessage('Parameter leeway has to be numeric.'); + $this->expectException(SSOException::class); + $this->expectExceptionMessage('Parameter leeway has to be numeric.'); - $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); - $constructor->invoke($mock, 'fake secret', 'fake token', 'dd'); - } + $reflectedClass = new ReflectionClass($this->classname); + $constructor = $reflectedClass->getConstructor(); + $constructor->invoke($mock, 'fake secret', 'fake token', 'dd'); + } /** * @test @@ -197,10 +201,10 @@ public function testConstructorToFailOnExpiredToken() { $token = self::createSignedTokenFromData($this->privateKey, $tokenData); - $this->expectException(SSOAuthenticationException::class); + $this->expectException(SSOAuthenticationException::class); - new SSOToken($this->publicKey, $token); - } + new SSOToken($this->publicKey, $token); + } /** * @test @@ -216,9 +220,9 @@ public function testConstructorToFailOnFutureToken() { $token = self::createSignedTokenFromData($this->privateKey, $tokenData); - $this->expectException(SSOAuthenticationException::class); + $this->expectException(SSOAuthenticationException::class); - new SSOToken($this->publicKey, $token); + new SSOToken($this->publicKey, $token); } /** @@ -235,9 +239,9 @@ public function testConstructorToFailOnTokenIssuedInTheFuture() { $token = self::createSignedTokenFromData($this->privateKey, $tokenData); - $this->expectException(SSOAuthenticationException::class); + $this->expectException(SSOAuthenticationException::class); - new SSOToken($this->publicKey, $token); + new SSOToken($this->publicKey, $token); } /** @@ -257,7 +261,7 @@ public function testConstructorAcceptsLeewayForTokenIssuedInTheFuture() { $sso = new SSOToken($this->publicKey, $token, $leeway); - $this->assertNotEmpty($sso); + $this->assertNotEmpty($sso); } /** @@ -274,10 +278,10 @@ public function testConstructorToFailOnMissingInstanceId() { $token = self::createSignedTokenFromData($this->privateKey, $tokenData); - $this->expectException(SSOAuthenticationException::class); - $this->expectExceptionMessage('Token lacks instance id.'); + $this->expectException(SSOAuthenticationException::class); + $this->expectExceptionMessage('Token lacks instance id.'); - new SSOToken($this->publicKey, $token); + new SSOToken($this->publicKey, $token); } /** @@ -293,10 +297,10 @@ public function testConstructorToFailOnUnsignedToken() { $token = self::createUnsignedTokenFromData($tokenData); - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage('This token is not signed'); + $this->expectException(BadMethodCallException::class); + $this->expectExceptionMessage('This token is not signed'); - new SSOToken($this->publicKey, $token); + new SSOToken($this->publicKey, $token); } /** @@ -327,6 +331,7 @@ public function testConstructorToFailOnUnsignedToken() { * @covers \Staffbase\plugins\sdk\SSOToken::getClaim() * @covers \Staffbase\plugins\sdk\SSOToken::getBranchId() * @covers \Staffbase\plugins\sdk\SSOToken::getBranchSlug() + * @covers \Staffbase\plugins\sdk\SSOData::getSessionId() */ public function testAccessorsGiveCorrectValues() {