Compare commits

..

No commits in common. "master" and "1.0.0" have entirely different histories.

13 changed files with 47 additions and 748 deletions

View File

@ -8,7 +8,6 @@ Core package for silex2 applications
* meta-tech/pws-auth * meta-tech/pws-auth
* meta-tech/silex-controller-service * meta-tech/silex-controller-service
* silex/silex (v2) * silex/silex (v2)
* symfony/security (~3.2)
* gecko-packages/gecko-silex-config-service * gecko-packages/gecko-silex-config-service
@ -23,7 +22,7 @@ Or add the package to your `composer.json`.
``` ```
"require": { "require": {
"meta-tech/silex-core" : "^1.0" "meta-tech/silex-core" : "~1.0"
} }
``` ```

View File

@ -17,12 +17,9 @@
} }
}, },
"require": { "require": {
"php" : "^7.0", "meta-tech/pws-auth" : "~2.1",
"meta-tech/pws-auth" : "@dev", "meta-tech/silex-controller-service" : "~1.0",
"meta-tech/silex-controller-service" : "^1.0", "silex/silex": "~2.0",
"silex/silex": "^2.0", "gecko-packages/gecko-silex-config-service": "^2.0"
"gecko-packages/gecko-silex-config-service": "^2.0",
"symfony/security": "^3.2",
"symfony/yaml": "^3.2"
} }
} }

View File

@ -1,7 +0,0 @@
default :
driver : pdo_mysql
host : db
dbname : test
user : dev
password : mysql
charset : utf8

View File

@ -1,12 +0,0 @@
security.firewalls :
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/ws
anonymous: true
security.params :
sleep : 3

View File

@ -1,56 +0,0 @@
<?php
/*
* This file is part of the silex-core package.
*
* (c) meta-tech.academy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MetaTech\Core;
/*!
* Singleton Pattern
*
* @package Mtc\Core
* @class Singleton
* @author a-Sansara
* @date 2014-11-05 23:45:12 CET
*/
class Singleton
{
/*! @protected @static @var $_instance the class instance */
protected static $_instance;
/*!
* @constructor
* @protected
*/
protected function __construct()
{
}
/*!
* @method __clone
* @protected
*/
protected function __clone()
{
}
/*!
* get the class instance
*
* @method getInstance
* @public
* @static
* @return Singleton
*/
public static function getInstance()
{
if (!(static::$_instance instanceof static)) {
static::$_instance = new static();
}
return static::$_instance;
}
}

View File

@ -1,88 +0,0 @@
<?php
/*
* This file is part of the silex-core package.
*
* (c) meta-tech.academy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MetaTech\Db;
use PDO;
use MetaTech\Core\Singleton;
/*!
* @package MetaTech\Db
* @class PdoConnector
* @extends MetaTech\Core\Singleton
* @author a-Sansara
* @date 2015-02-13 16:36:12 CET
*/
class PdoConnector extends Singleton
{
/*! @protected @var [] $conn */
protected $conn = array();
/*! @protected @var string $currentProfile */
protected $currentProfile;
/*!
* @private
* @param MetaTech\Db\Profile $profile
* @param bool $recreate
* @return \PDO
*/
private function getPdo(Profile $profile, $recreate=false)
{
$name = $profile->getName();
if ($recreate || !isset($this->conn[$name]) || $this->conn[$name] == null) {
$this->setPdo($profile);
}
return $this->conn[$name];
}
/*!
* @method setCurrentProfile
* @private
* @param str $name
*/
private function setCurrentProfile($name)
{
$this->currentProfile = $name;
}
/*!
* @method setPdo
* @private
* @param MetaTech\Db\Profile $profile
*/
private function setPdo(Profile $profile)
{
$pdo = new PDO($profile->getDsn(), $profile->getUser(), $profile->getPassword());
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->query("SET NAMES '".$profile->getCharset()."'");
$this->conn[$profile->getName()] = $pdo;
}
/*!
* @method switchDb
* @public
* @param MetaTech\Db\Profile $profile
* @param bool $recreate
*/
public function switchDb(Profile $profile, $recreate=false)
{
$this->currentProfile = $profile->getName();
$this->getPdo($profile, $recreate);
}
/*!
* @method conn
* @public
* @return PDO
*/
public function conn()
{
return $this->conn[$this->currentProfile];
}
}

View File

@ -1,243 +0,0 @@
<?php
/*
* This file is part of the silex-core package.
*
* (c) meta-tech.academy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MetaTech\Db;
use MetaTech\Db\PdoConnector;
use MetaTech\Db\Profile;
/*!
* Little Db utility to improve db interractions
*
* @package Mtc\Db
* @class PdoWrapper
* @author a-Sansara
* @date 2015-02-13 22:40:12 CET
*/
class PdoWrapper
{
/*! @protected @var Monolog\Handler\StreamHandler $logger */
protected $logger;
/*! @protected @var MetaTech\Db\Profile $profile */
protected $profile;
/*! @protected @var $bypasslog */
protected $bypasslog;
/*!
* @constructor
* @public
* @param MetaTech\Db\Profile $profile
* @param Monolog\Handler\StreamHandler $logger
*/
public function __construct(Profile $profile, $logger = null)
{
$this->profile = $profile;
$this->logger = $logger;
$this->switchDb($profile);
}
/*!
* Return the PDO connection object
*
* @method getPdoConnection
* @public
* @return PDO
*/
public function getPdoConnection()
{
return PdoConnector::getInstance()->conn();
}
/*!
* @method switchDb
* @public
* @param Mtc\Core\Db\Profile $profile
* @return PDO
*/
public function switchDb(Profile $profile = null, $recreate=false)
{
if (is_null($profile)) {
$profile = $this->profile;
}
return PdoConnector::getInstance()->switchDb($profile, $recreate);
}
/*!
* @method getLogger
* @public
* @return Monolog\Handler\StreamHandler
*/
public function getLogger()
{
return $this->logger;
}
/*!
* @method log
* @private
* @param str $query
* @param [] $data
* @param bool $start
*/
private function log($query, $data, $start=true, $forceLog=false)
{
if ($this->logger != null) {
$minisql = substr($query, 0, 35);
$bypasslog = strpos(substr($query, 0, 35), 'SELECT')!==false;
if (!$this->bypasslog || $forceLog) {
$this->bypasslog = $bypasslog;
if ($start) {
if (!$bypasslog || $forceLog) {
$this->logger->addDebug(" => ".str_pad("QUERY", 8, " ", STR_PAD_LEFT).' '.preg_replace('/[ ]{2,}/', ' ', $query));
if( !empty($data)) $this->logger->addDebug(str_pad("PARAMS", 12, " ", STR_PAD_LEFT), $data);
}
}
else {
$this->logger->addDebug(" <= $query", $data);
}
}
elseif (!$start) $this->bypasslog = false;
}
}
/*!
* execute a query and get Result Statement for the specified $data
*
* @method exec
* @public
* @param str $query
* @param [] $data
* @param int $fetch
* @return PdoStatement
*/
public function exec($query, $data = array(), $fetch = null, $forceLog=false)
{
$this->switchDb(null, true);
$this->log($query, $data, true, $forceLog);
if ($fetch == null) {
$fetch = \PDO::FETCH_OBJ;
}
$stmt = $this->getPdoConnection()->prepare($query);
if (is_array($data)) {
foreach ($data as $cl => $f) {
if (!is_null($f)) {
@$stmt->bindParam(':'.$cl, $data[$cl], ($cl == 'queryIndex' || $cl == 'queryLimit' ? \PDO::PARAM_INT : \PDO::PARAM_STR)); // don't use $f, cause value pass by reference
}
else {
$stmt->bindValue(':'.$cl, null, \PDO::PARAM_INT /* prefer to \PDO::PARAM_NULL for compat*/);
}
}
}
try {
$stmt->execute();
if ($fetch !== false) {
$stmt->setFetchMode($fetch);
}
$rowCount = $stmt!=null ? $stmt->rowCount() : 0;
$lastInsertId = $this->getLastInsertId();
$this->log(str_pad("RS", 8, " ", STR_PAD_LEFT), compact('rowCount', 'lastInsertId'), false, $forceLog);
}
catch(\Exception $e) {
if (!is_null($this->logger)) {
$this->bypasslog = false;
$this->logger->addError($e->getMessage());
foreach (preg_split('/#/', $e->getTraceAsString()) as $error) {
if (!empty($error)) {
$this->logger->addDebug("#$error");
}
}
}
throw $e;
}
return $stmt;
}
/*!
* get last insert id in db
*
* @method getLastInsertId
* @public
* @return int
*/
public function getLastInsertId()
{
return $this->getPdoConnection()->lastInsertId();
}
/*!
* persist $data in table $table
*
* @method persist
* @public
* @param str $table
* @param [] $data
* @param bool $updateOnDuplicate
* @return PdoStatement
*/
public function persist($table, $data, $updateOnDuplicate = true)
{
if (isset($data['id']) && is_null($data['id'])) {
unset($data['id']);
$updateOnDuplicate = false;
}
$argnames = array_keys($data);
$updateDef = '';
if ($updateOnDuplicate) {
foreach ($argnames as $field) {
$updateDef .= ($updateDef == '' ? '' : ',')." `$field` = VALUES(`$field`)";
}
$updateDef = "ON DUPLICATE KEY UPDATE $updateDef";
}
return $this->exec(
"INSERT INTO $table (`".implode('`, `', $argnames).'`) VALUES (:'.implode(', :', $argnames).") $updateDef",
$data
);
}
/*!
* get autoincrement
*
* @method nextIncrement
* @public
* @param str $table
* @return int
*/
public function nextIncrement($table)
{
$data = $this->exec('SHOW TABLE STATUS WHERE `Name`= :table', compact('table'))->fetch();
return $data != false ? $data->Auto_increment : null;
}
/*!
* @method encodeJsonBase64
* @public
* @static
* @param mixed $data
* @return str
*/
public static function encodeJsonBase64($data)
{
return base64_encode(json_encode($data));
}
/*!
* @method decodeJsonBase64
* @public
* @static
* @param str $data
* @param bool $onlyb64
* @return stdclass
*/
public static function decodeJsonBase64($data, $onlyb64 = false)
{
return $onlyb64 ? base64_decode($data) : json_decode(base64_decode($data));
}
}

View File

@ -1,87 +0,0 @@
<?php
/*
* This file is part of the silex-core package.
*
* (c) meta-tech.academy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MetaTech\Db;
/*!
* Db Profile
*
* @package MetaTech\Db
* @class Profile
* @author a-Sansara
* @date 2014-02-13 12:30:12 CET
*/
class Profile
{
/*! @public @var $config */
private $config;
/*!
* @constructor
* @public
* @param [assoc] $config
*/
public function __construct(array $config = [])
{
if (is_array($config) && !empty($config)) {
$this->config = $config;
} else {
throw new \Exception("$config must be associative array");
}
}
/*!
* @method getName
* @public
* @return str
*/
public function getName()
{
return !isset($this->config['name']) ? $this->config['dbname'] : $this->config['name'];
}
/*!
* @method getUser
* @public
* @return str
*/
public function getUser()
{
return $this->config['user'];
}
/*!
* @method getPassword
* @public
* @return str
*/
public function getPassword()
{
return $this->config['password'];
}
/*!
* @method getCharset
* @public
* @return str
*/
public function getCharset()
{
return $this->config['charset'];
}
/*!
* @method getDsn
* @public
* @return str
*/
public function getDsn()
{
return 'mysql:host='.$this->config['host'].';port=3306;dbname='.$this->config['dbname'];
}
}

View File

@ -12,7 +12,6 @@ namespace MetaTech\Silex;
use Silex\Application as BaseApplication; use Silex\Application as BaseApplication;
use Silex\Provider\SessionServiceProvider; use Silex\Provider\SessionServiceProvider;
use Silex\Provider\ServiceControllerServiceProvider; use Silex\Provider\ServiceControllerServiceProvider;
use Silex\Provider\SecurityServiceProvider;
use GeckoPackages\Silex\Services\Config\ConfigServiceProvider; use GeckoPackages\Silex\Services\Config\ConfigServiceProvider;
/*! /*!
@ -53,9 +52,6 @@ class Application extends BaseApplication
]); ]);
$this->register(new SessionServiceProvider()); $this->register(new SessionServiceProvider());
$this->register(new ServiceControllerServiceProvider()); $this->register(new ServiceControllerServiceProvider());
if (!empty($this['config']['security'])) {
$this->register(new SecurityServiceProvider(), $this['config']['security']);
}
} }
/*! /*!

View File

@ -51,17 +51,6 @@ abstract class Base implements ControllerProviderInterface
} }
/*!
* @method routing
* @public
* @param Silex\ControllerCollection $collection
* @return Silex\ControllerCollection
*/
public function routing(ControllerCollection $collection) : ControllerCollection
{
return $collection;
}
/*! /*!
* @method connect * @method connect
* @public * @public

View File

@ -1,150 +0,0 @@
<?php
/*
* This file is part of the silex-core package.
*
* (c) meta-tech.academy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace MetaTech\Silex\Provider;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use MetaTech\Db\PdoWrapper;
/*!
* desc
*
* @package MetaTech\Silex\Provider
* @class UserProvider
* @implements Symfony\Component\Security\Core\User\UserInterface
* @author a-Sansara
* @date 2016-02-08 18:29:06 CET
*/
class UserProvider implements UserProviderInterface
{
/*! @private @var MetaTech\Db\PdoWrapper $pdo */
private $pdo;
/*! @private @var str $table */
private $table;
/*!
* @constructor
* @public
* @param MetaTech\Db\PdoWrapper $pdo
*/
public function __construct(PdoWrapper $pdo, $table='`users`')
{
$this->pdo = $pdo;
$this->table = $table;
}
/*!
* @method loadUser
* @private
* @param str $login
* @return Symfony\Component\Security\Core\User\User
*/
private function loadUser($login)
{
$username = strtolower($login);
$stmt = $this->pdo->exec('SELECT * FROM ' . $this->table . ' WHERE username = :username', compact('username'));
if (!$user = $stmt->fetch()) {
throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
}
return $user;
}
/*!
* @method getUserNameById
* @public
* @param int $id
* @return Symfony\Component\Security\Core\User\User
*/
public function getUserNameById($id)
{
$stmt = $this->pdo->exec('SELECT name FROM ' . $this->table . ' WHERE id = :id', compact('id'));
if (!$user = $stmt->fetch()) {
throw new UsernameNotFoundException(sprintf('Userid "%s" does not exist.', $id));
}
return $user;
}
/*!
* @method loadUserByRole
* @public
* @return Symfony\Component\Security\Core\User\User
*/
private function loadUserByRole($role)
{
return $this->pdo->exec('SELECT * FROM ' . $this->table . ' WHERE roles LIKE :role', compact('role'))->fetchAll();
}
/*!
* @method loadUserByUsername
* @public
* @param str $username
* @return Symfony\Component\Security\Core\User\User
*/
public function loadUserByUsername($username)
{
$user = $this->loadUser($username);
$u = new User($user->username, $user->password, explode(',', $user->roles), true, true, true, true);
$u->labelName = $user->name;
$u->key = $user->key;
return $u;
}
/*!
* @method getUserKey
* @public
* @param str $username
* @return Symfony\Component\Security\Core\User\User
*/
public function getUserKey($username)
{
$user = $this->loadUser($username);
return $user->key;
}
/*!
* @method getIdUser
* @public
* @param str $username
* @return int|null
*/
public function getIdUser($username)
{
$user = $this->loadUser($username);
return isset($user->id) ? $user->id : null;
}
/*!
* @method refreshUser
* @public
* @param Symfony\Component\Security\Core\User\UserInterface $user
* @return Symfony\Component\Security\Core\User\User
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
return $this->loadUserByUsername($user->getUsername());
}
/*!
* @method supportsClass
* @public
* @param str $class
* @return bool
*/
public function supportsClass($class) {
return $class === User::class;
}
}

View File

@ -13,7 +13,6 @@ use Silex\Application;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
use MetaTech\PwsAuth\Authenticator; use MetaTech\PwsAuth\Authenticator;
use MetaTech\PwsAuth\Token; use MetaTech\PwsAuth\Token;
@ -25,25 +24,21 @@ use MetaTech\PwsAuth\Token;
*/ */
class Authentication class Authentication
{ {
/*! @protected @var Symfony\Component\HttpFoundation\Session\Session $session */ /*! @protected @®ar Symfony\Component\HttpFoundation\Session\Session $session */
protected $session; protected $session;
/*! @protected @var MetaTech\PwsAuth\Authenticator $authenticator */ /*! @protected @®ar MetaTech\PwsAuth\Authenticator $authenticator */
protected $authenticator; protected $authenticator;
/*! @protected @var Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface $passEncoder */
protected $passEncoder;
/*! /*!
* @constructor * @constructor
* @public * @public
* @param Symfony\Component\HttpFoundation\Session\Session $session * @param Symfony\Component\HttpFoundation\Session\Session $session
* @param MetaTech\PwsAuth\Authenticator $authenticator * @param MetaTech\PwsAuth\Authenticator $authenticator
* @param Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface $passEncoder
*/ */
public function __construct(Session $session, Authenticator $authenticator, PasswordEncoderInterface $passEncoder = null) public function __construct(Session $session, Authenticator $authenticator)
{ {
$this->session = $session; $this->session = $session;
$this->authenticator = $authenticator; $this->authenticator = $authenticator;
$this->passEncoder = $passEncoder;
} }
/*! /*!
@ -75,16 +70,15 @@ class Authentication
/*! /*!
* @method checkUser * @method checkUser
* @public * @public
* @param str $login * @param str $login
* @param str $password * @param str $password
* @param str $key * @param str $key
* @param Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface $passEncoder * @return bool
* @return bool
*/ */
public function checkUser($login, $password, $key, PasswordEncoderInterface $passEncoder = null) public function checkUser($login, $password, $key)
{ {
// implements on subclass // implements with userProvider on subclass
return false; return true;
} }
/*! /*!
@ -95,45 +89,21 @@ class Authentication
public function auth(Request $request) public function auth(Request $request)
{ {
$this->sessionInvalidate(); $this->sessionInvalidate();
$done = false; $done = false;
$msg = 'authentication require'; $msg = 'authentication require';
$token = $this->authenticator->getToken(); $token = $this->authenticator->getToken();
$login = $request->get('login');
$responseToken = $this->authenticator->generateResponseHeader($token);
$headers = $this->getResponseHeaders([], $responseToken);
if ($this->authenticator->isValid($token)) { if ($this->authenticator->isValid($token)) {
$login = $request->get('login');
$password = $request->get('password'); $password = $request->get('password');
if ($this->authenticator->check($token, $login)) { if ($done = $this->authenticator->check($token, $login)) {
try { if ($this->checkUser($login, $password, $token->getIdent())) {
if ($done = $this->checkUser($login, $password, $token->getIdent(), $this->passEncoder)) { $sid = $this->onSuccess($token, $login);
$sid = $this->onSuccess($token, $login); $msg = "authentication sucessful ! logged as $login";
$msg = "authentication sucessful ! logged as $login"; $data = compact('sid');
$data = compact('sid');
}
}
catch(\Exception $e) {
$msg = 'invalid user or password';
} }
} }
} }
if (!$done) { return new JsonResponse(compact('done', 'msg', 'data'), $done ? 200 : 401);
sleep(3);
}
return new JsonResponse(compact('done', 'msg', 'data'), $done ? 200 : 401, $headers);
}
/*!
* @method getResponseHeaders
* @private
* @param [assoc] $headers
* @return [assoc]
*/
private function getResponseHeaders($headers=[], $tokenResponse=null)
{
if (!empty($tokenResponse) || !empty($tokenResponse = $this->session->get('pwsauth.response'))) {
$headers['Pws-Response'] = $tokenResponse;
}
return $headers;
} }
/*! /*!
@ -163,27 +133,23 @@ class Authentication
public function check(Request $request) public function check(Request $request)
{ {
if (!$this->isAllowedRoute($request->getPathInfo())) { if (!$this->isAllowedRoute($request->getPathInfo())) {
$done = false; $this->sessionInvalidate();
$msg = "authentication require"; $done = false;
$headers = []; $msg = "authentication require";
try { try {
$token = $this->authenticator->getToken(); $token = $this->authenticator->getToken();
$tokenResponse = $this->authenticator->generateResponseHeader($token);
$headers = $this->getResponseHeaders($headers, $tokenResponse);
if ($this->authenticator->isValid($token)) { if ($this->authenticator->isValid($token)) {
if (!empty($sid = $this->authenticator->getSessionId($token))) { $sid = $this->authenticator->getSessionId($token);
$this->session->setId($sid);
$this->session->start();
$user = $this->session->get('user');
// done : lets controller takes hand
if (!is_null($user) && $user->key == $token->getIdent()) {
return;
}
else {
$this->sessionInvalidate(); $this->sessionInvalidate();
$this->session->setId($sid);
$this->session->start();
$user = $this->session->get('user');
// done : lets controller takes hand
if (!is_null($user) && $user->key == $token->getIdent()) {
$this->session->set('pwsauth.response', $tokenResponse);
return;
}
else {
$this->sessionInvalidate();
}
} }
} }
} }
@ -191,7 +157,7 @@ class Authentication
$done = false; $done = false;
$msg = $e->getMessage(); $msg = $e->getMessage();
} }
return new JsonResponse(compact('done', 'msg', 'data'), 401, $headers); return new JsonResponse(compact('done', 'msg'), 401);
} }
} }
} }

View File

@ -38,7 +38,7 @@ class Controller extends Base
public function __construct(Application $app = null) public function __construct(Application $app = null)
{ {
$this->session = $app['session']; $this->session = $app['session'];
$this->handler = new Authentication($this->session, $app['ws.authenticator'], $app['security.default_encoder']); $this->handler = new Authentication($this->session, $app['ws.authenticator']);
} }
/*! /*!
@ -49,16 +49,12 @@ class Controller extends Base
* @param [] $data * @param [] $data
* @return Symfony\Component\HttpFoundation\JsonResponse * @return Symfony\Component\HttpFoundation\JsonResponse
*/ */
public function response($done = false, $msg = "fail", $data = null, $tokenResponse = null) public function response($done = false, $msg = "fail", $data = null)
{ {
if (is_null($data)) { if (is_null($data)) {
unset($data); unset($data);
} }
$headers = []; $response = new JsonResponse(compact('done', 'msg', 'data'), 200);
if (!empty($tokenResponse) || !empty($tokenResponse = $this->session->get('pwsauth.response'))) {
$headers['Pws-Response'] = $tokenResponse;
}
$response = new JsonResponse(compact('done', 'msg', 'data'), 200, $headers);
return $response; return $response;
} }
@ -107,12 +103,11 @@ class Controller extends Base
*/ */
public function logout() public function logout()
{ {
$tokenResponse = $this->session->isStarted() ? $this->session->get('pwsauth.response') : null;
$this->handler->sessionInvalidate(); $this->handler->sessionInvalidate();
$sessid = $this->session->getId(); $sessid = $this->session->getId();
$done = true; $done = true;
$msg = 'session logout'; $msg = 'session logout';
return $this->response($done, $msg, null, $tokenResponse); return $this->response($done, $msg);
} }
/*! /*!