summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--codeception.yml21
-rw-r--r--composer.json6
-rw-r--r--examples/ajax-broker/ajax.php50
-rw-r--r--examples/ajax-broker/helpers.js66
-rw-r--r--examples/ajax-broker/token.php1
-rw-r--r--examples/broker/index.php10
-rw-r--r--examples/broker/login.php3
-rw-r--r--examples/server/example-server.php34
-rw-r--r--examples/server/index.php15
-rw-r--r--src/Broker.php79
-rw-r--r--src/Exception.php9
-rw-r--r--src/Server.php152
-rw-r--r--src/TestServer.php51
-rw-r--r--tests/_support/FunctionalTester.php26
-rw-r--r--tests/_support/UnitTester.php26
-rw-r--r--tests/acceptance.suite.yml12
-rw-r--r--tests/acceptance/WelcomeCept.php23
-rw-r--r--tests/brokerApi.suite.yml7
-rw-r--r--tests/brokerApi/BrokerTesterCept.php55
-rw-r--r--tests/brokerApi/_bootstrap.php (renamed from tests/acceptance/_bootstrap.php)0
-rw-r--r--tests/functional.suite.yml11
-rw-r--r--tests/serverApi.suite.yml7
-rw-r--r--tests/serverApi/ServerApiCept.php57
-rw-r--r--tests/serverApi/_bootstrap.php (renamed from tests/functional/_bootstrap.php)0
-rw-r--r--tests/unit.suite.yml9
-rw-r--r--tests/unit/ServerTest.php25
-rw-r--r--tests/unit/_bootstrap.php2
27 files changed, 414 insertions, 343 deletions
diff --git a/codeception.yml b/codeception.yml
new file mode 100644
index 0000000..3a8fca8
--- /dev/null
+++ b/codeception.yml
@@ -0,0 +1,21 @@
+actor: Tester
+paths:
+ tests: tests
+ log: tests/_output
+ data: tests/_data
+ support: tests/_support
+ envs: tests/_envs
+settings:
+ bootstrap: _bootstrap.php
+ colors: true
+ memory_limit: 1024M
+extensions:
+ enabled:
+ - Codeception\Extension\RunFailed
+modules:
+ config:
+ Db:
+ dsn: ''
+ user: ''
+ password: ''
+ dump: tests/_data/dump.sql
diff --git a/composer.json b/composer.json
index 28b4b23..8f6bee0 100644
--- a/composer.json
+++ b/composer.json
@@ -17,11 +17,15 @@
},
"require": {
"php": ">=5.3.0",
- "codeception/codeception": "*"
+ "codeception/codeception": "*",
+ "desarrolla2/cache": "dev-master"
},
"autoload": {
"psr-4": {
"Jasny\\SSO\\": "src/"
}
+ },
+ "require-dev": {
+ "jasny/php-code-quality": "^1.1"
}
}
diff --git a/examples/ajax-broker/ajax.php b/examples/ajax-broker/ajax.php
index 68d252f..63679ec 100644
--- a/examples/ajax-broker/ajax.php
+++ b/examples/ajax-broker/ajax.php
@@ -1,41 +1,37 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/src/Broker.php';
-function send_error($message) {
- header("Content-Type: application/json");
- header("HTTP/1.1 406 Not Acceptable");
- echo '{error: "$message"}';
-}
+$command = $_REQUEST['command'];
+$broker = new Jasny\SSO\Broker('http://localhost:9000/examples/server/', 'BrokerApi', 'BrokerApi');
if (empty($_REQUEST['command'])) {
- send_error('command not specified');
- exit();
-}
-else if ($_REQUEST['command'] == 'on') {
- send_error('unsupported command');
+ header("Content-Type: application/json");
+ header("HTTP/1.1 406 Not Acceptable");
+ echo json_encode(['error' => 'Command not specified']);
exit();
}
-
-$command = $_REQUEST['command'];
-$broker = new Jasny\SSO\Broker('http://localhost:9000/examples/server/', 'Alice', 'Bob');
-
-if (!empty($_REQUEST['token'])) {
- $broker->token = $_REQUEST['token'];
-}
-
-if (realpath($_SERVER["SCRIPT_FILENAME"]) == realpath(__FILE__) && isset($_REQUEST['command'])) {
+else if (realpath($_SERVER["SCRIPT_FILENAME"]) == realpath(__FILE__)) {
error_log('executing: '. $_REQUEST['command']);
try {
- $result = $broker->$_GET['command']();
- }
- catch (\Exception $ex) {
- $result = $ex->getMessage();
+ $result = $broker->$_REQUEST['command']();
+ header("Content-Type: application/json");
+ echo json_encode($result);
+ } catch (Exception $ex) {
+ $errorCode = $ex->getCode();
+ error_log('error code' . $errorCode);
+
+ header("Content-Type: application/json");
+ if ($errorCode == 401) header("HTTP/1.1 401 Unauthorized");
+ if ($errorCode == 406) header("HTTP/1.1 406 Not Acceptable");
+
+ echo json_encode(['error' => $ex->getMessage()]);
}
}
else {
error_log('nothing to execute');
-}
-header("Content-Type: application/json");
-echo json_encode($result);
-?> \ No newline at end of file
+ header("Content-Type: application/json");
+ header("HTTP/1.1 406 Not Acceptable");
+ echo json_encode(['error' => 'Command not supported']);
+ exit();
+}
diff --git a/examples/ajax-broker/helpers.js b/examples/ajax-broker/helpers.js
index 5e1b4c3..95b5d0e 100644
--- a/examples/ajax-broker/helpers.js
+++ b/examples/ajax-broker/helpers.js
@@ -1,41 +1,57 @@
-function microAjax(B,A){this.bindFunction=function(E,D){return function(){return E.apply(D,[D]);};};this.stateChange=function(D){if(this.request.readyState==4){this.callbackFunction(this.request.responseText);}};this.getRequest=function(){if(window.ActiveXObject){return new ActiveXObject("Microsoft.XMLHTTP");}else{if(window.XMLHttpRequest){return new XMLHttpRequest();}}return false;};this.postBody=(arguments[2]||"");this.callbackFunction=A;this.url=B;this.request=this.getRequest();if(this.request){var C=this.request;C.onreadystatechange=this.bindFunction(this.stateChange,this);if(this.postBody!==""){C.open("POST",B,true);C.setRequestHeader("X-Requested-With","XMLHttpRequest");C.setRequestHeader("Content-type","application/x-www-form-urlencoded");C.setRequestHeader("Connection","close");}else{C.open("GET",B,true);}C.send(this.postBody);}};
+function microAjax(B,A)
+{
+ this.bindFunction=function (E,D) {
+ return function () {
+ return E.apply(D,[D]);};};this.stateChange=function (D) {
+ if (this.request.readyState==4) {
+ this.callbackFunction(this.request.responseText);}};this.getRequest=function () {
+ if (window.ActiveXObject) {
+ return new ActiveXObject("Microsoft.XMLHTTP");} else {
+ if (window.XMLHttpRequest) {
+ return new XMLHttpRequest();}}return false;};this.postBody=(arguments[2]||"");this.callbackFunction=A;this.url=B;this.request=this.getRequest();if (this.request) {
+ var C=this.request;C.onreadystatechange=this.bindFunction(this.stateChange,this);if (this.postBody!=="") {
+ C.open("POST",B,true);C.setRequestHeader("X-Requested-With","XMLHttpRequest");C.setRequestHeader("Content-type","application/x-www-form-urlencoded");C.setRequestHeader("Connection","close");} else {
+ C.open("GET",B,true);}C.send(this.postBody);}};
var token;
-function attachSession() {
- microAjax('/examples/ajax-broker/ajax.php?command=attach&token='+ token, function(data) {
- console.log(data);
- });
+function attachSession()
+{
+ microAjax('/examples/ajax-broker/ajax.php?command=attach&token='+ token, function (data) {
+ console.log(data);
+ });
}
-function getToken(f) {
- microAjax('/examples/ajax-broker/ajax.php?command=getToken', function(data) {
- token = data;
- console.log('token is ready');
- });
+function getToken(f)
+{
+ microAjax('/examples/ajax-broker/ajax.php?command=getToken', function (data) {
+ token = data;
+ console.log('token is ready');
+ });
}
-function login() {
- var username = document.querySelector('input[name="username"]').value;
- var password = document.querySelector('input[name="password"]').value;
- var query = [
+function login()
+{
+ var username = document.querySelector('input[name="username"]').value;
+ var password = document.querySelector('input[name="password"]').value;
+ var query = [
'command=login',
'username='+username,
'password='+password,
'token='+token
- ];
+ ];
- microAjax('/examples/ajax-broker/ajax.php?' + query.join('&'), function(data) {
- console.log(data);
- var outputDiv = document.querySelector('#output');
- var output = "";
- var jsonData = JSON.parse(data);
+ microAjax('/examples/ajax-broker/ajax.php?' + query.join('&'), function (data) {
+ console.log(data);
+ var outputDiv = document.querySelector('#output');
+ var output = "";
+ var jsonData = JSON.parse(data);
- for (var key in jsonData) {
- output += key + ": " + jsonData[key] + "<br>";
- }
- outputDiv.innerHTML = output;
- });
+ for (var key in jsonData) {
+ output += key + ": " + jsonData[key] + "<br>";
+ }
+ outputDiv.innerHTML = output;
+ });
}
getToken();
diff --git a/examples/ajax-broker/token.php b/examples/ajax-broker/token.php
index 60aa0fb..9c3e3f6 100644
--- a/examples/ajax-broker/token.php
+++ b/examples/ajax-broker/token.php
@@ -8,4 +8,3 @@ $result = array(
header("Content-Type: application/json");
echo json_encode($result);
-?> \ No newline at end of file
diff --git a/examples/broker/index.php b/examples/broker/index.php
index 258b760..3ada49d 100644
--- a/examples/broker/index.php
+++ b/examples/broker/index.php
@@ -20,14 +20,16 @@ if (!$user) {
<body>
<h1>Single Sign-On demo</h1>
<h2><?= $broker->broker ?></h2>
- <?php if ($user): ?>
+ <?php if ($user) : ?>
<h3>Logged in</h3>
- <?php endif ?>
+ <?php
+endif ?>
<dl>
- <?php foreach($user as $key => $value): ?>
+ <?php foreach ($user as $key => $value) : ?>
<dt><?= $key ?></dt><dd><?= $value ?></dd>
- <?php endforeach; ?>
+ <?php
+endforeach; ?>
</dl>
<a id="logout" href="login.php?logout=1">Logout</a>
</body>
diff --git a/examples/broker/login.php b/examples/broker/login.php
index a1226cd..2265756 100644
--- a/examples/broker/login.php
+++ b/examples/broker/login.php
@@ -6,7 +6,8 @@ $broker = new Jasny\SSO\Broker('http://localhost:9000/examples/server/', 'Alice'
if (!empty($_GET['logout'])) {
$broker->logout();
-} elseif ($broker->getUserInfo() || ($_SERVER['REQUEST_METHOD'] == 'POST' && $broker->login($_POST['username'], $_POST['password']))) {
+} elseif ($broker->getUserInfo()
+ || ($_SERVER['REQUEST_METHOD'] == 'POST' && $broker->login($_POST['username'], $_POST['password']))) {
header("Location: index.php", true, 303);
exit;
}
diff --git a/examples/server/example-server.php b/examples/server/example-server.php
deleted file mode 100644
index ca4043b..0000000
--- a/examples/server/example-server.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-session_save_path('/tmp/SSO1');
-require_once __DIR__ . '/../../vendor/autoload.php';
-
-class ExampleServer extends Jasny\SSO\Server
-{
- private static $brokers = array (
- 'Alice' => array('secret'=>"Bob"),
- 'Greg' => array('secret'=>'Geraldo')
- );
-
- private static $users = array (
- 'admin' => array(
- 'fullname' => 'jackie',
- 'email' => 'jackie@admin.com'
- )
- );
-
- protected function getBrokerInfo($broker)
- {
- return self::$brokers[$broker];
- }
-
- protected function checkLogin($username, $password)
- {
- return $username == 'admin' && $password == 'admin';
- }
-
- protected function getUserInfo($user)
- {
- return self::$users[$user];
- }
-}
-?>
diff --git a/examples/server/index.php b/examples/server/index.php
index a4a2c67..828598a 100644
--- a/examples/server/index.php
+++ b/examples/server/index.php
@@ -1,13 +1,16 @@
<?php
-require_once("example-server.php");
+require_once __DIR__ . '/../../vendor/autoload.php';
+session_save_path('/tmp/SSO1');
// Execute controller command
if (realpath($_SERVER["SCRIPT_FILENAME"]) == realpath(__FILE__) && isset($_REQUEST['command'])) {
- error_log('executing: '. $_REQUEST['command']);
- $sso = new ExampleServer();
- $sso->$_GET['command']();
+ $sso = new Jasny\SSO\TestServer();
+ $sso->$_REQUEST['command']();
}
else {
- error_log('nothing to execute');
+ error_log('Unkown command');
+ header("HTTP/1.1 406 Not Acceptable");
+ header('Content-type: application/json; charset=UTF-8');
+
+ echo "{error: 'Uknown command'}";
}
-?> \ No newline at end of file
diff --git a/src/Broker.php b/src/Broker.php
index c4f4029..621cf9c 100644
--- a/src/Broker.php
+++ b/src/Broker.php
@@ -1,6 +1,8 @@
<?php
namespace Jasny\SSO;
+require_once __DIR__ . '/../vendor/autoload.php';
+
/**
* Single sign-on broker.
*
@@ -31,7 +33,7 @@ class Broker
* Session token of the client
* @var string
*/
- public $token;
+ public $token;
/**
* User info recieved from the server.
@@ -42,18 +44,19 @@ class Broker
/**
* Class constructor
*
- * @param string $url Url of SSO server
- * @param string $broker My identifier, given by SSO provider.
- * @param string $secret My secret word, given by SSO provider.
+ * @param string $url Url of SSO server
+ * @param string $broker My identifier, given by SSO provider.
+ * @param string $secret My secret word, given by SSO provider.
*/
public function __construct($url, $broker, $secret)
{
$this->url = $url;
$this->broker = $broker;
$this->secret = $secret;
- session_start();
- //error_log('userinfo: '. $_SESSION['SSO']['userinfo']);
- error_log('session ' . json_encode($_SESSION));
+
+ if (session_status() === PHP_SESSION_NONE) session_start();
+ //error_log('session ' . json_encode($_SESSION));
+
if (isset($_SESSION['SSO']['token'])) $this->token = $_SESSION['SSO']['token'];
// if (isset($_SESSION['SSO']['userinfo'])) $this->userinfo = $_SESSION['SSO']['userinfo'];
@@ -67,8 +70,11 @@ class Broker
*/
protected function getSessionId()
{
- if (!isset($this->token)) return null;
- return "SSO-{$this->broker}-{$this->token}-" . md5('session' . $this->token . $_SERVER['REMOTE_ADDR'] . $this->secret);
+ if (!isset($this->token)) return null;
+
+ $checksum = md5('session' . $this->token . $_SERVER['REMOTE_ADDR'] . $this->secret);
+
+ return "SSO-{$this->broker}-{$this->token}-" . $checksum;
}
/**
@@ -111,15 +117,21 @@ class Broker
/**
* Attach our session to the user's session on the SSO server.
*
- * @param string $returnUrl The URL the client should be returned to after attaching
+ * @param string $returnUrl The URL the client should be returned to after attaching
*/
public function attach($returnUrl = null)
{
error_log('trying to attach');
if ($this->isAttached()) return;
- if (!isset($returnUrl)) $returnUrl = "http://{$_SERVER["SERVER_NAME"]}{$_SERVER["REQUEST_URI"]}";
- $url = $this->getAttachUrl() . "&returnUrl=" . urlencode($returnUrl);
+ $url = $this->getAttachUrl();
+
+ if (isset($returnUrl)) {
+ $url .= "&returnUrl=" . urlencode("http://{$_SERVER["SERVER_NAME"]}{$_SERVER["REQUEST_URI"]}");
+ }
+ else if (!empty($_REQUEST['returnUrl'])) {
+ $url .= "&returnUrl=" . urlencode($_REQUEST['returnUrl']);
+ }
header("Location: $url", true, 307);
echo "You're redirected to <a href=\"$url\">$url</a>";
@@ -141,7 +153,7 @@ class Broker
/**
* Get the request url for a command
*
- * @param string $command
+ * @param string $command
* @return string
*/
protected function getRequestUrl($command)
@@ -159,34 +171,36 @@ class Broker
/**
* Execute on SSO server.
*
- * @param string $command Command
- * @param array $params Post parameters
+ * @param string $command Command
+ * @param array $params Post parameters
* @return array
*/
protected function request($command, $params = array())
{
$ch = curl_init($this->getRequestUrl($command));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- error_log($this->getSessionId());
curl_setopt($ch, CURLOPT_POST, true);
$params[session_name()] = $this->getSessionId();
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
$response = curl_exec($ch);
- if (curl_errno($ch) != 0) throw new \Exception("Server request failed: " . curl_error($ch));
+ if (curl_errno($ch) != 0) {
+ throw new Exception("Server request failed: " . curl_error($ch));
+ }
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE );
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$contentType = explode('; ', $contentType)[0];
if ($contentType != 'application/json') {
- throw new \Exception("Response did not come from the SSO server. $response", $httpCode);
+ throw new Exception("Response did not come from the SSO server. $response", $httpCode);
}
error_log('response ' . $response);
- $data = json_decode(stripslashes(trim($response)), true);
- if ($httpCode != 200) throw new \Exception($httpCode);
+ $data = json_decode($response, true);
+ if ($httpCode != 200) throw new Exception($data['error'], $httpCode);
+
return $data;
}
@@ -197,21 +211,21 @@ class Broker
* Only brokers marked trused can collect and send the user's credentials. Other brokers should omit $username and
* $password.
*
- * @param string $username
- * @param string $password
+ * @param string $username
+ * @param string $password
* @return object
*/
public function login($username = null, $password = null)
{
- if (!isset($username)) $username = $_REQUEST['username'];
- if (!isset($password)) $password = $_REQUEST['password'];
+ if (!isset($username)) $username = $_POST['username'];
+ if (!isset($password)) $password = $_POST['password'];
+
$result = $this->request('login', compact('username', 'password'));
if (!array_key_exists('error', $result)) {
$this->userinfo = $result;
// $_SESSION['SSO']['userinfo'] = $result;
error_log('success');
- }
- else {
+ } else {
error_log('failure');
}
return $result;
@@ -232,15 +246,13 @@ class Broker
{
error_log('trying to get user info');
try {
- # TODO: the data is not updated
+ // TODO: the data is not updated
if (!isset($this->userinfo)) {
$this->userinfo = $this->request('userInfo');
}
return $this->userinfo;
- }
- catch (\Exception $ex) {
- error_log($ex);
+ } catch (Exception $ex) {
return null;
}
}
@@ -253,9 +265,7 @@ class Broker
*/
public function on($event, $data)
{
- if (method_exists($this, "on{$event}")) {
- $this->{"on{$event}"}($data);
- }
+ if (method_exists($this, "on{$event}")) $this->{"on{$event}"}($data);
}
/**
@@ -282,4 +292,3 @@ class Broker
$this->userinfo = $data;
}
}
-?> \ No newline at end of file
diff --git a/src/Exception.php b/src/Exception.php
new file mode 100644
index 0000000..3188b89
--- /dev/null
+++ b/src/Exception.php
@@ -0,0 +1,9 @@
+<?php
+namespace Jasny\SSO;
+
+/**
+ * SSO Exception
+ */
+class Exception extends \Exception
+{
+}
diff --git a/src/Server.php b/src/Server.php
index 790cd79..dff06ef 100644
--- a/src/Server.php
+++ b/src/Server.php
@@ -1,7 +1,11 @@
<?php
-
namespace Jasny\SSO;
+require_once __DIR__ . '/../vendor/autoload.php';
+
+use Desarrolla2\Cache\Cache;
+use Desarrolla2\Cache\Adapter\File;
+
/**
* Single sign-on server.
*
@@ -10,19 +14,10 @@ namespace Jasny\SSO;
abstract class Server
{
/**
- * Path to link files.
- *
- * If you run the SSO server on a shared hosting system, be sure to change this to a path in your home folder.
- * This path MUST NOT be accessable by the web.
- *
- * @var string
- */
- public static $linkPath;
-
- /**
* Probability that the garbage collector is activated to remove of link files.
*
* Similar to gc_probability/gc_divisor
+ *
* @link http://www.php.net/manual/en/session.configuration.php#ini.session.gc-probability
*
* @var float
@@ -31,39 +26,46 @@ abstract class Server
private $started = false;
-
/**
- * Get path to link files
+ * Cache that stores the special session data for the brokers.
+ *
+ * @var Desarrolla2\Cache\Cache
*/
- public static function getLinkPath()
+ public $cache;
+
+ public function __construct()
{
- if (!isset(self::$linkPath)) self::$linkPath = sys_get_temp_dir() . '/sso';
- if (file_exists(self::$linkPath)) mkdir(self::$linkPath, 1777, true);
+ $this->cache = $this->createCacheAdapter();
+ $this->cache->set('hello world', 'bonjour');
+ error_log('cache: ' . $this->cache->get('hello world'));
+ error_log('request:'. json_encode($_REQUEST));
}
-
/**
* Start session and protect against session hijacking
*/
protected function sessionStart()
{
- if ($this->started)
- return;
+ if ($this->started) return;
+
$this->started = true;
// Broker session
$matches = null;
+
error_log('request: ' . json_encode($_REQUEST));
- if (isset($_REQUEST[session_name()]) && preg_match('/^SSO-(\w*+)-(\w*+)-([a-z0-9]*+)$/', $_REQUEST[session_name()], $matches)) {
+ if (isset($_REQUEST[session_name()])
+ && preg_match('/^SSO-(\w*+)-(\w*+)-([a-z0-9]*+)$/', $_REQUEST[session_name()], $matches)) {
+
error_log('starting broker session');
$sid = $_REQUEST[session_name()];
+ error_log('retrieved sid: '. $sid);
-
- $link = (session_save_path() ? session_save_path() : sys_get_temp_dir()) . "/sess_" . $this->generateSessionId($_REQUEST['broker'], $_REQUEST['token']);
- if (file_exists($link)) {
- session_id(file_get_contents($link));
+ $linkedId = $this->cache->get($sid);
+ if ($linkedId) {
+ session_id($linkedId);
session_start();
- #TODO: the session cookie expires in 1 second.
+ // TODO: the session cookie expires in 1 second.
setcookie(session_name(), "", 1);
} else {
session_start();
@@ -89,12 +91,12 @@ abstract class Server
error_log('starting user session');
session_start();
- if (isset($_SESSION['client_addr']) && $_SESSION['client_addr'] != $_SERVER['REMOTE_ADDR'])
+ if (isset($_SESSION['client_addr']) && $_SESSION['client_addr'] != $_SERVER['REMOTE_ADDR']) {
session_regenerate_id(true);
- if (!isset($_SESSION['client_addr']))
+ }
+ if (!isset($_SESSION['client_addr'])) {
$_SESSION['client_addr'] = $_SERVER['REMOTE_ADDR'];
-
- error_log('session ' . json_encode($_SESSION));
+ }
}
/**
@@ -105,11 +107,10 @@ abstract class Server
protected function generateSessionId($broker, $token, $client_addr = null)
{
$brokerInfo = $this->getBrokerInfo($broker);
- if (!isset($brokerInfo))
- return null;
- if (!isset($client_addr))
- $client_addr = $_SERVER['REMOTE_ADDR'];
+ if (!isset($brokerInfo)) return null;
+ if (!isset($client_addr)) $client_addr = $_SERVER['REMOTE_ADDR'];
+
return "SSO-{$broker}-{$token}-" . md5('session' . $token . $client_addr . $brokerInfo['secret']);
}
@@ -121,8 +122,8 @@ abstract class Server
protected function generateAttachChecksum($broker, $token)
{
$brokerInfo = $this->getBrokerInfo($broker);
- if (!isset($brokerInfo))
- return null;
+ if (!isset($brokerInfo)) return null;
+
return md5('attach' . $token . $_SERVER['REMOTE_ADDR'] . $brokerInfo['secret']);
}
@@ -133,13 +134,12 @@ abstract class Server
{
$this->sessionStart();
- if (empty($_POST['username']))
- $this->failLogin("No user specified");
- if (empty($_POST['password']))
- $this->failLogin("No password specified");
+ if (empty($_POST['username'])) $this->failLogin("No user specified");
+ if (empty($_POST['password'])) $this->failLogin("No password specified");
- if (!$this->checkLogin($_POST['username'], $_POST['password']))
+ if (!$this->checkLogin($_POST['username'], $_POST['password'])) {
$this->failLogin("Incorrect credentials");
+ }
$_SESSION['username'] = $_POST['username'];
$this->userInfo();
@@ -164,43 +164,33 @@ abstract class Server
{
$this->sessionStart();
- if (empty($_REQUEST['broker']))
- $this->fail("No broker specified");
- if (empty($_REQUEST['token']))
- $this->fail("No token specified");
- if (empty($_REQUEST['checksum']) || $this->generateAttachChecksum($_REQUEST['broker'], $_REQUEST['token']) != $_REQUEST['checksum'])
- $this->fail("Invalid checksum");
+ if (empty($_REQUEST['broker'])) $this->fail("No broker specified");
+ if (empty($_REQUEST['token'])) $this->fail("No token specified");
- if (!isset(self::$linkPath)) {
- $link = (session_save_path() ? session_save_path() : sys_get_temp_dir()) . "/sess_" . $this->generateSessionId($_REQUEST['broker'], $_REQUEST['token']);
- error_log('writing file|' . $link . '|');
- error_log('session id: ' . session_id());
- if (!file_exists($link))
- $attached = file_put_contents($link, session_id());
- if (!$attached)
- trigger_error("Failed to attach; Link file wasn't created.", E_USER_ERROR);
-
- if (!file_exists($link))
- trigger_error("Failed to attach; Link file wasn't created.", E_USER_ERROR);
- error_log('number of bytes written: ' . $attached);
- error_log(error_get_last());
- } else {
- $link = "{self::$linkPath}/" . $this->generateSessionId($_REQUEST['broker'], $_REQUEST['token']);
- if (!file_exists($link))
- $attached = file_put_contents($link, session_id());
- if (!$attached)
- trigger_error("Failed to attach; Link file wasn't created.", E_USER_ERROR);
+ $checksum = $this->generateAttachChecksum($_REQUEST['broker'], $_REQUEST['token']);
+ $sid = $this->generateSessionId($_REQUEST['broker'], $_REQUEST['token']);
+ error_log('sid: ' . $sid);
+ error_log('checksum: ' . $checksum);
+ if (empty($_REQUEST['checksum'])
+ || $checksum != $_REQUEST['checksum']) {
+ $this->fail("Invalid checksum");
}
- //error_log ('request '. json_encode($_REQUEST));
- if (isset($_REQUEST['returnUrl'])) {
+ // what if there already exists an entry ?
+ $this->cache->set($sid, session_id());
+
+ if (!empty($_REQUEST['returnUrl'])) {
header('Location: ' . $_REQUEST['returnUrl'], true, 307);
exit();
}
// Output an image specially for AJAX apps
- header("Content-Type: image/png");
- readfile("empty.png");
+
+ header('Content-type: application/json; charset=UTF-8');
+ echo json_encode(['token' => $_REQUEST['token']]);
+ //echo "{success:true, token:'", $_REQUEST['token'], "'}";
+ //header("Content-Type: image/png");
+ //readfile("empty.png");
}
/**
@@ -210,15 +200,13 @@ abstract class Server
public function userInfo()
{
$this->sessionStart();
- if (!isset($_SESSION['username']))
- $this->failLogin("Not logged in");
+ if (!isset($_SESSION['username'])) $this->failLogin("Not logged in");
$userData = $this->getUserInfo($_SESSION['username']);
$userData['username'] = $_SESSION['username'];
- forEach($userData as $key => $value)
- {
- # TODO: find alternative for htmlspecialchars, as this can be a vulnerability.
+ foreach ($userData as $key => $value) {
+ // TODO: find alternative for htmlspecialchars, as this can be a vulnerability.
$userData[$key] = htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
}
@@ -236,6 +224,8 @@ abstract class Server
{
header("HTTP/1.1 406 Not Acceptable");
header('Content-type: application/json; charset=UTF-8');
+ error_log($message);
+
echo json_encode(array('error' => $message));
exit;
}
@@ -250,12 +240,24 @@ abstract class Server
{
header("HTTP/1.1 401 Unauthorized");
header('Content-type: application/json; charset=UTF-8');
+ error_log($message);
echo json_encode(array('error' => $message));
exit;
}
+ /**
+ * Create a cache.
+ *
+ * This method is called in the constructor to make a cache to store the broker session id.
+ */
+ protected function createCacheAdapter()
+ {
+ $adapter = new File('/tmp');
+ $adapter->setOption('ttl', 10 * 3600);
+ return new Cache($adapter);
+ }
+
abstract protected function checkLogin($username, $password);
abstract protected function getBrokerInfo($brokerId);
abstract protected function getUserInfo($brokerId);
}
-?> \ No newline at end of file
diff --git a/src/TestServer.php b/src/TestServer.php
new file mode 100644
index 0000000..b6ed2ac
--- /dev/null
+++ b/src/TestServer.php
@@ -0,0 +1,51 @@
+<?php
+namespace Jasny\SSO;
+
+require_once __DIR__ . '/../vendor/autoload.php';
+
+use Desarrolla2\Cache\Cache;
+use Desarrolla2\Cache\Adapter\Memory;
+
+class TestServer extends Server
+{
+ private static $brokers = array (
+ 'Alice' => array('secret'=>"Bob"),
+ 'Greg' => array('secret'=>'Geraldo'),
+ 'BrokerApi' => array('secret'=>'BrokerApi'),
+ 'ServerApi' => array('secret' => 'ServerApi')
+ );
+
+ private static $users = array (
+ 'admin' => array(
+ 'fullname' => 'jackie',
+ 'email' => 'jackie@admin.com'
+ )
+ );
+
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ protected function getBrokerInfo($broker)
+ {
+ return self::$brokers[$broker];
+ }
+
+ protected function checkLogin($username, $password)
+ {
+ return $username == 'admin' && $password == 'admin';
+ }
+
+ protected function getUserInfo($user)
+ {
+ return self::$users[$user];
+ }
+
+ // protected function createCacheAdapter() {
+ // $adapter = new Memory();
+ // $adapter->setOption('ttl', 10 * 3600);
+ // return new Cache($adapter);
+ // }
+}
+?>
diff --git a/tests/_support/FunctionalTester.php b/tests/_support/FunctionalTester.php
deleted file mode 100644
index 8f9d5f2..0000000
--- a/tests/_support/FunctionalTester.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-
-/**
- * Inherited Methods
- * @method void wantToTest($text)
- * @method void wantTo($text)
- * @method void execute($callable)
- * @method void expectTo($prediction)
- * @method void expect($prediction)
- * @method void amGoingTo($argumentation)
- * @method void am($role)
- * @method void lookForwardTo($achieveValue)
- * @method void comment($description)
- * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
- *
- * @SuppressWarnings(PHPMD)
-*/
-class FunctionalTester extends \Codeception\Actor
-{
- use _generated\FunctionalTesterActions;
-
- /**
- * Define custom actions here
- */
-}
diff --git a/tests/_support/UnitTester.php b/tests/_support/UnitTester.php
deleted file mode 100644
index 68c09cf..0000000
--- a/tests/_support/UnitTester.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-
-/**
- * Inherited Methods
- * @method void wantToTest($text)
- * @method void wantTo($text)
- * @method void execute($callable)
- * @method void expectTo($prediction)
- * @method void expect($prediction)
- * @method void amGoingTo($argumentation)
- * @method void am($role)
- * @method void lookForwardTo($achieveValue)
- * @method void comment($description)
- * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
- *
- * @SuppressWarnings(PHPMD)
-*/
-class UnitTester extends \Codeception\Actor
-{
- use _generated\UnitTesterActions;
-
- /**
- * Define custom actions here
- */
-}
diff --git a/tests/acceptance.suite.yml b/tests/acceptance.suite.yml
deleted file mode 100644
index 75b6853..0000000
--- a/tests/acceptance.suite.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-# Codeception Test Suite Configuration
-#
-# Suite for acceptance tests.
-# Perform tests in browser using the WebDriver or PhpBrowser.
-# If you need both WebDriver and PHPBrowser tests - create a separate suite.
-
-class_name: AcceptanceTester
-modules:
- enabled:
- - PhpBrowser:
- url: http://localhost:9001/examples/broker
- - \Helper\Acceptance \ No newline at end of file
diff --git a/tests/acceptance/WelcomeCept.php b/tests/acceptance/WelcomeCept.php
deleted file mode 100644
index 6645347..0000000
--- a/tests/acceptance/WelcomeCept.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-$I = new AcceptanceTester($scenario);
-$I->wantTo('ensure that frontpage works');
-$I->amOnPage('/');
-$I->see('Single Sign-On demo - Login', 'h1');
-
-$I->amOnPage('/login.php');
-$I->seeCookie('PHPSESSID');
-$I->submitForm('#login', [
- 'username' => 'admin',
- 'password' => 'admin'
-]);
-
-$I->amOnPage('/');
-$I->see('Logged in', 'h3');
-$I->click('#logout');
-
-
-$I->amOnPage('/');
-$I->dontSee('Logged in', 'h3');
-
-$I->amOnPage('/login.php');
-?>
diff --git a/tests/brokerApi.suite.yml b/tests/brokerApi.suite.yml
new file mode 100644
index 0000000..655361f
--- /dev/null
+++ b/tests/brokerApi.suite.yml
@@ -0,0 +1,7 @@
+class_name: ServerApiTester
+modules:
+ enabled:
+ - REST:
+ url: http://localhost:9001/examples/ajax-broker/ajax.php
+ depends: PhpBrowser
+ part: Json \ No newline at end of file
diff --git a/tests/brokerApi/BrokerTesterCept.php b/tests/brokerApi/BrokerTesterCept.php
new file mode 100644
index 0000000..7fc7cbc
--- /dev/null
+++ b/tests/brokerApi/BrokerTesterCept.php
@@ -0,0 +1,55 @@
+<?php
+$token = 'hello_world';
+$broker = "Api";
+$checksum = '6ff4f638b06327544f26a248d6ad6890';
+$password = 'admin';
+$username = 'admin';
+
+$I = new ServerApiTester($scenario);
+
+$I->wantTo('login through broker and view user data');
+$I->sendServerRequest('getUserInfo');
+$I->seeResponseIsJson();
+$I->seeResponseCodeIs(200);
+$I->seeResponseEquals('null');
+
+$I->sendServerRequest('attach');
+$I->seeResponseIsJson();
+
+$I->sendServerRequest('getUserInfo');
+$I->seeResponseIsJson();
+$I->seeResponseCodeIs(200);
+$I->seeResponseEquals('null');
+
+
+$I->sendServerRequest('login', [
+ 'password' => $username,
+ 'username' => $password
+]);
+$I->seeResponseCodeIs(200);
+$I->seeResponseIsJson(['token' => $token]);
+
+$I->sendServerRequest('getUserInfo');
+$I->seeResponseCodeIs(200);
+$I->seeResponseIsJson();
+$I->seeResponseContainsJson([
+ 'fullname' => 'jackie',
+ 'email' => 'jackie@admin.com',
+ 'username' => 'admin'
+]);
+
+$I->sendServerRequest('detach');
+$I->sendServerRequest('attach');
+
+$I->sendServerRequest('getUserInfo');
+$I->seeResponseCodeIs(200);
+$I->seeResponseIsJson();
+$I->seeResponseContainsJson([
+ 'fullname' => 'jackie',
+ 'email' => 'jackie@admin.com',
+ 'username' => 'admin'
+]);
+
+$I->sendServerRequest('logout');
+$I->seeResponseCodeIs(200);
+$I->seeResponseIsJson('null'); \ No newline at end of file
diff --git a/tests/acceptance/_bootstrap.php b/tests/brokerApi/_bootstrap.php
index 8a88555..8a88555 100644
--- a/tests/acceptance/_bootstrap.php
+++ b/tests/brokerApi/_bootstrap.php
diff --git a/tests/functional.suite.yml b/tests/functional.suite.yml
deleted file mode 100644
index 075765b..0000000
--- a/tests/functional.suite.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-# Codeception Test Suite Configuration
-#
-# Suite for functional (integration) tests
-# Emulate web requests and make application process them
-# Include one of framework modules (Symfony2, Yii2, Laravel5) to use it
-
-class_name: FunctionalTester
-modules:
- enabled:
- # add framework module here
- - \Helper\Functional \ No newline at end of file
diff --git a/tests/serverApi.suite.yml b/tests/serverApi.suite.yml
new file mode 100644
index 0000000..5fd2cd6
--- /dev/null
+++ b/tests/serverApi.suite.yml
@@ -0,0 +1,7 @@
+class_name: ServerApiTester
+modules:
+ enabled:
+ - REST:
+ url: http://localhost:9000/examples/server
+ depends: PhpBrowser
+ part: Json \ No newline at end of file
diff --git a/tests/serverApi/ServerApiCept.php b/tests/serverApi/ServerApiCept.php
new file mode 100644
index 0000000..d580f65
--- /dev/null
+++ b/tests/serverApi/ServerApiCept.php
@@ -0,0 +1,57 @@
+<?php
+$token = 'hello_world';
+$broker = "ServerApi";
+$checksum = 'fe3892f0d85b3b92bdda31cbbf993ace';
+$password = 'admin';
+$username = 'admin';
+
+$I = new ServerApiTester($scenario);
+$I->defaultArgs = [
+ 'token' => $token,
+ 'broker' => $broker, 'checksum' => $checksum,
+ 'PHPSESSID' => 'SSO-ServerApi-hello_world-ec31fb7dff02625359acc8d1bd6d9dc0'
+];
+
+$I->wantTo('attach session and view user info and logout');
+$I->sendServerRequest('attach', ['PHPSESSID' => '']);
+$I->seeResponseIsJson();
+$I->seeResponseCodeIs(200);
+$I->seeResponseContainsJson(['token' => $token]);
+
+$I->sendServerRequest('userInfo');
+$I->seeResponseCodeIs(401);
+$I->seeResponseIsJson();
+$I->seeResponseContainsJson(['error' => 'Not logged in']);
+
+$I->sendServerRequest('login', [
+ 'password' => 'wrong',
+ 'username' => 'wrong'
+]);
+
+$I->seeResponseCodeIs(401);
+$I->seeResponseIsJson(['error' => 'Incorrect credentials']);
+
+$I->sendServerRequest('login', [
+ 'password' => $username,
+ 'username' => $password
+]);
+$I->seeResponseCodeIs(200);
+$I->seeResponseIsJson(['token' => $token]);
+
+$I->sendServerRequest('userInfo');
+$I->seeResponseCodeIs(200);
+$I->seeResponseIsJson();
+$I->seeResponseContainsJson([
+ 'fullname' => 'jackie',
+ 'email' => 'jackie@admin.com',
+ 'username' => 'admin'
+]);
+
+$I->sendServerRequest('logout');
+$I->seeResponseCodeIs(200);
+$I->seeResponseIsJson();
+
+$I->sendServerRequest('userInfo');
+$I->seeResponseCodeIs(401);
+$I->seeResponseIsJson();
+$I->seeResponseContainsJson(['error' => 'Not logged in']); \ No newline at end of file
diff --git a/tests/functional/_bootstrap.php b/tests/serverApi/_bootstrap.php
index 8a88555..8a88555 100644
--- a/tests/functional/_bootstrap.php
+++ b/tests/serverApi/_bootstrap.php
diff --git a/tests/unit.suite.yml b/tests/unit.suite.yml
deleted file mode 100644
index 02dc9b1..0000000
--- a/tests/unit.suite.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-# Codeception Test Suite Configuration
-#
-# Suite for unit (internal) tests.
-
-class_name: UnitTester
-modules:
- enabled:
- - Asserts
- - \Helper\Unit \ No newline at end of file
diff --git a/tests/unit/ServerTest.php b/tests/unit/ServerTest.php
deleted file mode 100644
index c3cc1f3..0000000
--- a/tests/unit/ServerTest.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-require_once __DIR__ . '/../../vendor/autoload.php';
-
-class ServerTest extends \Codeception\TestCase\Test
-{
- /**
- * @var \UnitTester
- */
- protected $tester;
-
- protected function _before()
- {
- }
-
- protected function _after()
- {
- }
-
- // tests
- public function testMe()
- {
- $broker = new Jasny\SSO\Broker('http://localhost:9000/examples/server/', 'Alice', 'Bob');
-
- }
-}
diff --git a/tests/unit/_bootstrap.php b/tests/unit/_bootstrap.php
deleted file mode 100644
index 8a88555..0000000
--- a/tests/unit/_bootstrap.php
+++ /dev/null
@@ -1,2 +0,0 @@
-<?php
-// Here you can initialize variables that will be available to your tests