This commit is contained in:
Juan Ferrer Toribio 2016-09-24 00:47:34 +02:00
parent 5c159f3ceb
commit ee3e4cf041
31 changed files with 414 additions and 379 deletions

View File

@ -34,8 +34,8 @@ Vn.Catalog = new Class
this.$('items-model').setInfo ('a', 'Articles', 'vn2008', ['item_id']);
if (Vn.Cookie.check ('hedera_view'))
this.setView (Vn.Cookie.getInt ('hedera_view'));
if (localStorage.getItem ('hederaView'))
this.setView (parsetInt (localStorage.getItem ('hederaView')));
else
this.setView (Vn.Catalog.View.GRID);
}
@ -70,7 +70,7 @@ Vn.Catalog = new Class
var node = this.$('grid-view').getNode ();
node.className = className;
Vn.Cookie.set ('hedera_view', this.view);
localStorage.setItem ('hederaView', this.view);
}
,onSwitchViewClick: function ()

View File

@ -26,93 +26,7 @@ Db.Conn = new Class ().extend
Db.Conn.implement
({
Extends: Vn.Object
,_connected: false
,_requestsCount: 0
/**
* Initilizes the connection object.
**/
,initialize: function ()
{
this.parent ();
}
/**
* Opens the connection to the database.
*
* @param {string} user The user name
* @param {String} password The user password
* @param {Boolean} remember Specifies if the user should be remembered
* @param {Function} openCallback The function to call when operation is done
**/
,open: function (user, pass, remember, callback, guest)
{
this.signalEmit ('loading-changed', true);
if (user != null)
{
var params = {
'user': user
,'password': pass
,'remember': remember
};
}
else if (guest)
var params = {'guest': true};
var request = new Vn.JsonRequest ('core/login');
request.send (params,
this._onOpen.bind (this, callback));
}
/*
* Called when open operation is done.
*/
,_onOpen: function (callback, request, json, error)
{
this._connected = json == true;
this.signalEmit ('loading-changed', false);
if (this._connected)
this.signalEmit ('openned');
if (error)
this.signalEmit ('error', error);
if (callback)
callback (this, this._connected, error);
}
/**
* Closes the connection to the database.
*
* @param {Function} callback The function to call when operation is done
**/
,close: function (callback)
{
this.signalEmit ('loading-changed', true);
var request = new Vn.JsonRequest ('core/logout');
request.send (null,
this._onClose.bind (this, callback));
}
/*
* Called when close operation is done.
*/
,_onClose: function (callback, request, json, error)
{
this._connected = false;
this.signalEmit ('loading-changed', false);
this.signalEmit ('closed');
if (error)
this.signalEmit ('error', error);
if (callback)
callback (this, json == true, error);
}
Extends: Vn.JsonConnection
/**
* Runs a SQL query on the database.
@ -122,13 +36,7 @@ Db.Conn.implement
**/
,execSql: function (sql, callback)
{
this._requestsCount++;
if (this._requestsCount === 1)
this.signalEmit ('loading-changed', true);
var request = new Vn.JsonRequest ('core/query');
request.send ({'sql': sql},
this.send ('core/query', {'sql': sql},
this._onExec.bind (this, callback));
}
@ -167,13 +75,8 @@ Db.Conn.implement
/*
* Called when a query is executed.
*/
,_onExec: function (callback, request, json, error)
,_onExec: function (callback, json, error)
{
this._requestsCount--;
if (this._requestsCount === 0)
this.signalEmit ('loading-changed', false);
if (json)
try {
if (json && json instanceof Array)

View File

@ -118,21 +118,28 @@ Vn.App = new Class
,_onConnError: function (conn, error)
{
if (error instanceof Vn.JsonException)
switch (error.exception)
{
case 'Vn\\Web\\BadLoginException':
Htk.Toast.showError (_('Invalid login'));
this._logout ();
break;
case 'Vn\\Web\\SessionExpiredException':
Htk.Toast.showError (_('You\'ve been too idle'));
this._logout ();
break;
case 'Vn\\Web\\OutdatedVersionException':
this._newVersion (error);
break;
default:
Htk.Toast.showError (error.message);
var exception = error.exception
.replace (/\\/g, '.')
.replace (/Exception$/, '')
.replace (/^Vn\.Web\./, '');
switch (exception)
{
case 'BadLogin':
Htk.Toast.showError (_('Invalid login'));
this._logout ();
break;
case 'SessionExpired':
Htk.Toast.showError (_('You\'ve been too idle'));
this._logout ();
break;
case 'OutdatedVersion':
this._newVersion (error);
break;
default:
Htk.Toast.showError (error.message);
}
}
else
{
@ -168,9 +175,7 @@ Vn.App = new Class
,'message': error.message
,'stack': error.stack
};
var request = new Vn.JsonRequest ('core/log');
request.send (params);
this._conn.send ('core/log', params);
}
,_freeLogin: function ()

View File

@ -84,9 +84,9 @@ Vn.Gui = new Class
this.formParam = new Vn.HashParam ({key: 'form'});
this.formParam.on ('changed', this._onFormChange, this);
if (!Vn.Cookie.check ('hedera_cookies'))
if (!localStorage.getItem ('hederaCookies'))
{
Vn.Cookie.set ('hedera_cookies', true);
localStorage.setItem ('hederaCookies', true);
Htk.Toast.showWarning (_('By using this site you accept cookies'));
}
}

View File

@ -14,10 +14,10 @@ Vn.Login = new Class
,set: function (x)
{
this.link ({_conn: x}, {'loading-changed': this._onConnLoadChange});
/* x.execQuery (
x.execQuery (
'SELECT title, link, icon FROM social ORDER BY priority',
this._onSocialQueryDone.bind (this));
*/ }
}
,get: function ()
{
return this._conn;
@ -100,20 +100,21 @@ Vn.Login = new Class
,onPasswordLost: function ()
{
if (!this.$('user').value)
{
Htk.Toast.showError (_('Please write your user name'));
return;
}
var user = this.$('user').value;
var request = new Vn.JsonRequest ('core/recover-password');
request.send (null, this._onPasswordRecovered.bind (this));
if (!user)
Htk.Toast.showError (_('Please write your user name'));
else
this._conn.send ('core/recover-password', {'user': user},
this._onPasswordRecovered.bind (this));
}
,_onPasswordRecovered: function (request, json, error)
,_onPasswordRecovered: function (json, error)
{
if (json)
Htk.Toast.showMessage (_('It sent an email with your new password'));
Htk.Toast.showMessage (_('A mail has been sent with your new password'));
else
Htk.Toast.showError (error.message);
}
,_onSocialQueryDone: function (resultSet)

237
js/vn/json-connection.js Normal file
View File

@ -0,0 +1,237 @@
/**
* Handler for JSON rest connections.
**/
Vn.JsonConnection = new Class
({
Extends: Vn.Object
,_connected: false
,_requestsCount: 0
,_token: null
/**
* Initilizes the connection object.
**/
,initialize: function ()
{
this.parent ();
if (localStorage.getItem ('token'))
this._token = localStorage.getItem ('token');
if (sessionStorage.getItem ('token'))
this._token = sessionStorage.getItem ('token');
}
/**
* Opens the connection to the REST service.
*
* @param {string} user The user name
* @param {String} password The user password
* @param {Boolean} remember Specifies if the user should be remembered
* @param {Function} openCallback The function to call when operation is done
**/
,open: function (user, pass, remember, callback)
{
if (user != null)
{
var params = {
'user': user
,'password': pass
,'remember': remember
};
}
else
var params = null;
this.send ('core/login', params,
this._onOpen.bind (this, callback, remember));
}
/*
* Called when open operation is done.
*/
,_onOpen: function (callback, remember, json, error)
{
if (json && json.login)
{
this._connected = true;
this._token = json.token;
var storage = remember ? localStorage : sessionStorage;
storage.setItem ('token', this._token);
this.signalEmit ('openned');
}
if (callback)
callback (this, this._connected, error);
}
/**
* Closes the connection to the REST service.
*
* @param {Function} callback The function to call when operation is done
**/
,close: function (callback)
{
this.send ('core/logout', null,
this._onClose.bind (this, callback));
}
/*
* Called when close operation is done.
*/
,_onClose: function (callback, json, error)
{
this._connected = false;
this.signalEmit ('closed');
if (callback)
callback (this, json == true, error);
}
/**
* Executes the specified REST service with the given params and calls
* the callback when response is received.
*
* @param {String} restService The service path
* @param {Map} params The params to pass to the service
* @param {Function} callback The response callback
**/
,send: function (restService, params, callback)
{
if (!params)
params = {};
params['srv'] = 'json:'+ restService;
this.sendWithUrl (params, callback, 'post', '.');
}
,sendForm: function (form, callback)
{
var params = {};
var elements = form.elements;
for (var i = 0; i < elements.length; i++)
if (elements[i].name)
params[elements[i].name] = elements[i].value;
this.sendWithUrl (params, callback, 'post', form.action);
}
,sendFormMultipart: function (form, callback)
{
var formData = new FormData (form);
if (this._token)
formData.append ('token', this._token);
var request = new XMLHttpRequest ();
request.open ('post', form.action, true);
request.onreadystatechange =
this._onStateChange.bind (this, request, callback);
request.send (formData);
this._addRequest ();
}
/*
* Called when REST response is received.
*/
,sendWithUrl: function (params, callback, method, url)
{
if (this._token)
params['token'] = this._token;
var request = new XMLHttpRequest ();
request.open (method, url, true);
request.setRequestHeader ('Content-Type',
'application/x-www-form-urlencoded');
request.onreadystatechange =
this._onStateChange.bind (this, request, callback);
request.send (Vn.Url.makeUri (params));
this._addRequest ();
}
,_addRequest: function ()
{
this._requestsCount++;
if (this._requestsCount === 1)
this.signalEmit ('loading-changed', true);
}
,_onStateChange: function (request, callback)
{
if (request.readyState !== 4)
return;
this._requestsCount--;
if (this._requestsCount === 0)
this.signalEmit ('loading-changed', false);
var data = null;
var error = null;
try {
if (request.status == 0)
{
var ex = new Vn.JsonException ();
ex.message = _('The server does not respond');
throw ex;
}
var contentType = null;
try {
contentType = request
.getResponseHeader ('Content-Type')
.split (';')[0]
.trim ();
}
catch (e) {}
if (contentType != 'application/json')
{
var ex = new Vn.JsonException ();
ex.message = request.statusText;
ex.code = request.status;
throw ex;
}
var json = JSON.parse (request.responseText);
var jsData = json.data;
var jsWarns = json.warnings;
if (request.status == 200)
{
data = jsData;
}
else
{
var ex = new Vn.JsonException ();
ex.exception = jsData.exception;
ex.message = jsData.message;
ex.code = jsData.code;
ex.file = jsData.file;
ex.line = jsData.line;
ex.trace = jsData.trace;
throw ex;
}
}
catch (e)
{
data = null;
error = e;
}
if (error)
this.signalEmit ('error', error);
if (callback)
callback (data, error);
}
});

View File

@ -1,116 +0,0 @@
/**
* Handler for JSON rest requests.
**/
Vn.JsonRequest = new Class
({
initialize: function (methodName)
{
this._methodName = methodName;
}
,sendWithUrl: function (params, callback, method, url)
{
var request = new XMLHttpRequest ();
request.open (method, url, true);
request.setRequestHeader ('Content-Type',
'application/x-www-form-urlencoded');
request.onreadystatechange =
this._onStateChange.bind (this, request, callback);
request.send (Vn.Url.makeUri (params));
}
,send: function (params, callback)
{
if (!params)
params = {};
params['srv'] = 'json:'+ this._methodName;
this.sendWithUrl (params, callback, 'post', '.');
}
,sendForm: function (form, callback)
{
var params = {};
var elements = form.elements;
for (var i = 0; i < elements.length; i++)
if (elements[i].name)
params[elements[i].name] = elements[i].value;
this.sendWithUrl (params, callback, 'post', form.action);
}
,sendFormMultipart: function (form, callback)
{
var request = new XMLHttpRequest ();
request.open ('post', form.action, true);
request.onreadystatechange =
this._onStateChange.bind (this, request, callback);
request.send (new FormData (form));
}
,_onStateChange: function (request, callback)
{
if (request.readyState !== 4)
return;
var data = null;
var error = null;
try {
if (request.status == 0)
{
var ex = new Vn.JsonException ();
ex.message = _('The server does not respond');
throw ex;
}
var contentType = null;
try {
contentType = request
.getResponseHeader ('Content-Type')
.split (';')[0]
.trim ();
}
catch (e) {}
if (contentType != 'application/json')
{
var ex = new Vn.JsonException ();
ex.message = request.statusText;
ex.code = request.status;
throw ex;
}
var json = JSON.parse (request.responseText);
var jsData = json.data;
var jsWarns = json.warnings;
if (request.status == 200)
{
data = jsData;
}
else
{
var ex = new Vn.JsonException ();
ex.exception = jsData.exception;
ex.message = jsData.message;
ex.code = jsData.code;
ex.file = jsData.file;
ex.line = jsData.line;
ex.trace = jsData.trace;
throw ex;
}
}
catch (e)
{
data = null;
error = e;
}
if (callback)
callback (this, data, error);
}
});

View File

@ -16,6 +16,6 @@ Vn.includeLib ('vn',
,'node'
,'builder'
,'json-exception'
,'json-request'
,'json-connection'
]);

View File

@ -16,7 +16,7 @@ var Vn =
{
if (this._version === undefined)
{
var re = /[; ]vn_version=([^\\s;]*)/;
var re = /[; ]vnVersion=([^\\s;]*)/;
var sMatch = (' '+ document.cookie).match (re);
this._version = (sMatch) ? '?'+ unescape (sMatch[1]) : '';
}

View File

@ -6,7 +6,7 @@ require_once ('Text/CAPTCHA.php');
class Captcha extends Vn\Web\RestRequest
{
function run ()
function run ($db)
{
$options =
[

View File

@ -11,7 +11,7 @@ class Log extends Vn\Web\JsonRequest
,'stack'
];
function run ()
function run ($db)
{
$user = isset ($_SESSION['user']) ? $_SESSION['user'] : 'guest';
error_log (sprintf ("Javascript: User: %s: %s(%d): %s.\n%s"

View File

@ -3,31 +3,43 @@
require_once ('vn/web/json-request.php');
require_once ('vn/web/jwt.php');
const MIN = 60;
const HOUR = 60 * MIN;
const DAY = 24 * HOUR;
const WEEK = 7 * DAY;
class Login extends Vn\Web\JsonRequest
{
function run ()
function run ($db)
{
$token = Jwt::encode ([
$this->updateCredentials ($db);
if (isset ($_POST['remember']))
$tokenLife = WEEK;
else
$tokenLife = 30 * MIN;
$token = Vn\Web\Jwt::encode ([
'userName' => $_SESSION['user'],
'timestamp' => time ()
'exp' => time () + 7 * 24 * 60 * 60
'timestamp' => time (),
'exp' => time () + $tokenLife
]);
$this->updateCredentials ();
return $token;
return [
'login' => TRUE,
'token' => $token
];
}
/**
* Updates the user credentials in other user databases like Samba.
**/
function updateCredentials ()
function updateCredentials ($db)
{
$db = $this->getSysConn ();
$hasAccount = $db->getValue (
'SELECT COUNT(*) > 0
FROM account.user u
JOIN account.account a ON u.id = a.user_id
JOIN account.account a ON u.id = a.id
WHERE u.name = #',
[$_SESSION['user']]
);

View File

@ -4,7 +4,7 @@ require_once ('vn/web/json-request.php');
class Logout extends Vn\Web\JsonRequest
{
function run ()
function run ($db)
{
$this->logout ();
return TRUE;

View File

@ -3,17 +3,15 @@
require_once ('vn/web/json-request.php');
use Vn\Lib;
use Vn\Web\Security;
class Query extends Vn\Web\JsonRequest
{
const PARAMS = ['sql'];
const SECURITY = Security::INVOKER;
function run ($db)
{
$password = $db->getValue (
'SELECT password FROM user WHERE name = #', $_SESSION['user']);
$userDb = $this->createConnection ($_SESSION['user'], $password);
$results = [];
try {

View File

@ -21,20 +21,20 @@ class RecoverPassword extends Vn\Web\JsonRequest
return TRUE;
$restrictions = $db->getRow (
'SELECT length, nupper, ndigits, npunct FROM account.user_password');
'SELECT length, nUpper, nDigits, nPunct FROM account.userPassword');
$pass = [];
$newPass = '';
$nAlpha = $restrictions['length'] - (
$restrictions['nupper'] +
$restrictions['ndigits'] +
$restrictions['npunct']);
$restrictions['nUpper'] +
$restrictions['nDigits'] +
$restrictions['nPunct']);
$this->genRands ($pass, self::LOWERS, $nAlpha);
$this->genRands ($pass, self::UPPERS, $restrictions['nupper']);
$this->genRands ($pass, self::DIGITS, $restrictions['ndigits']);
$this->genRands ($pass, self::SYMBOLS, $restrictions['npunct']);
$this->genRands ($pass, self::UPPERS, $restrictions['nUpper']);
$this->genRands ($pass, self::DIGITS, $restrictions['nDigits']);
$this->genRands ($pass, self::SYMBOLS, $restrictions['nPunct']);
for ($i = count ($pass) - 1; $i >= 0; $i--)
{

View File

@ -9,13 +9,11 @@ use Vn\Lib;
**/
class Add extends Vn\Web\JsonRequest
{
function run ()
function run ($db)
{
// XXX: Uncomment only to test the script
//$_REQUEST['description'] = 'description';
$db = $this->getSysConn ();
$description = empty ($_REQUEST['description']) ?
NULL : $_REQUEST['description'];

View File

@ -3,9 +3,12 @@
require_once ('vn/web/rest-request.php');
require_once ('vn/web/util.php');
use Vn\Web\Security;
class Invoice extends Vn\Web\RestRequest
{
const PARAMS = ['invoice'];
const SECURITY = Security::INVOKER;
function run ($db)
{

View File

@ -16,7 +16,7 @@ class Contact extends Vn\Web\JsonRequest
,'captcha'
];
function run ()
function run ($db)
{
// Checks the antispam code
@ -28,8 +28,6 @@ class Contact extends Vn\Web\JsonRequest
// Sends the mail
$db = $this->getSysConn ();
// TODO: Change form fields
//$db->queryFromFile (__DIR__.'/contact', $_REQUEST);
//$customerId = $db->getValue ('SELECT @id');

View File

@ -8,9 +8,8 @@ require_once ('vn/lib/method.php');
**/
class ExchangeRate extends Vn\Lib\Method
{
function run ()
function run ($db)
{
$db = $this->getSysConn ();
$db->selectDb ('vn2008');
// Indica la URL del archivo

View File

@ -5,9 +5,8 @@ require_once ('libphp-phpmailer/PHPMailerAutoload.php');
class Mail extends Vn\Lib\Method
{
function run ()
function run ($db)
{
$db = $this->getSysConn ();
$db->selectDb ('vn2008');
$db->query ('START TRANSACTION');

View File

@ -4,9 +4,8 @@ require_once ('vn/lib/method.php');
class VisitsSync extends Vn\Lib\Method
{
function run ()
function run ($db)
{
$db = $this->getSysConn ();
$result = $db->query ("SELECT id, agent FROM visit_agent
WHERE version = '0.0' OR platform = 'unknown' OR cookies IS NULL ORDER BY id DESC");

View File

@ -8,10 +8,8 @@ require_once (__DIR__.'/tpv.php');
**/
class ConfirmMail extends Vn\Lib\Method
{
function run ()
function run ($db)
{
$db = $this->getSysConn ();
$imap = NULL;
$imapConf = $db->getRow (
'SELECT host, user, pass, clean_period, success_folder, error_folder

View File

@ -8,7 +8,7 @@ require_once (__DIR__.'/tpv.php');
**/
class ConfirmPost extends Vn\Web\RestRequest
{
function run ()
function run ($db)
{
Tpv::confirm ($db, $_POST);
}

View File

@ -9,7 +9,7 @@ require_once (__DIR__.'/tpv.php');
**/
class ConfirmSoap extends Vn\Web\RestRequest
{
function run ()
function run ($db)
{
global $tpvConfirmSoap;
@ -26,7 +26,7 @@ function procesaNotificacionSIS ($XML)
{
global $tpvConfirmSoap;
$db = $tpvConfirmSoap->getSysConn ();
$db = $tpvConfirmSoap->app->getSysConn ();
$status = 'OK';
$requestString = $XML;

View File

@ -3,7 +3,6 @@
namespace Vn\Web;
require_once ('vn/lib/app.php');
require_once (__DIR__.'/jwt.php');
/**
* Main class for web applications.
@ -48,6 +47,8 @@ class App extends \Vn\Lib\App
http_response_code (400);
}
function deinit () {}
/**
* Gets the configuration file name associated to the current vhost
* or the default config file if isn't defined a file for the vhost.
@ -58,8 +59,11 @@ class App extends \Vn\Lib\App
&& preg_match ('/^[\w\-\.]+$/', $_SERVER['SERVER_NAME']))
{
$hostSplit = explode ('.', $_SERVER['SERVER_NAME']);
array_splice ($hostSplit, -2);
$subdomain = implode ('.', $hostSplit);
$configDir = _CONFIG_DIR .'/'. $this->name;
$hostFile = "$configDir/config/{$hostSplit[0]}.php";
$hostFile = "$configDir/config.$subdomain.php";
}
if (isset ($hostFile) && file_exists ($hostFile))

View File

@ -2,7 +2,7 @@
namespace Vn\Web;
class DbSessionHandler implements SessionHandlerInterface
class DbSessionHandler implements \SessionHandlerInterface
{
private $db;
@ -18,13 +18,13 @@ class DbSessionHandler implements SessionHandlerInterface
function destroy ($sessionId)
{
$db->query ('DELETE FROM userSession WHERE ssid = #', [$sessionId]);
$this->db->query ('DELETE FROM userSession WHERE ssid = #', [$sessionId]);
return TRUE;
}
function gc ($maxLifeTime)
{
$db->query ('DELETE FROM userSession
$this->db->query ('DELETE FROM userSession
WHERE lastUpdate < TIMESTAMPADD(SECOND, -#, NOW())',
[$maxLifeTime]
);
@ -39,14 +39,14 @@ class DbSessionHandler implements SessionHandlerInterface
function read ($sessionId)
{
//$db->query ('DO GET_LOCK(#, 30)', [$sessionId]);
$sessionData = $db->getValue (
$sessionData = $this->db->getValue (
'SELECT data FROM userSession WHERE ssid = #', [$sessionId]);
return isset ($sessionData) ? $sessionData : '';
}
function write ($sessionId, $sessionData)
{
$db->query ('REPLACE INTO userSession SET ssid = #, data = #',
$this->db->query ('REPLACE INTO userSession SET ssid = #, data = #',
[$sessionId, $sessionData]);
//$db->query ('DO RELEASE_LOCK(#)', [$sessionId]);
return TRUE;

View File

@ -13,7 +13,7 @@ class HtmlService extends Service
{
function run ()
{
$db = $this->app->getSysConn ();
$db = $this->db;
if (!$this->isHttps ()
&& $db->getValue ('SELECT https FROM config'))
@ -70,7 +70,7 @@ class HtmlService extends Service
// Setting the version
setcookie ('vn_version', $this->getVersion ());
setcookie ('vnVersion', $this->getVersion ());
// Loading the requested page
@ -125,7 +125,7 @@ class HtmlService extends Service
$this->includeJs ($localeJs);
for ($i = 1; $i < count ($args); $i++)
$this->includeJs ("js/$libName/${args[$i]}.js");
$this->includeJs ("js/$libName/{$args[$i]}.js");
}
function includeCss ($fileName)

View File

@ -25,16 +25,14 @@ class JsonService extends RestService
// Checks the client version
if (!empty ($_COOKIE['vn_version']))
$clientVersion = (int) $_COOKIE['vn_version'];
if (!empty ($_COOKIE['vnVersion']))
$clientVersion = (int) $_COOKIE['vnVersion'];
if (isset ($clientVersion)
&& $clientVersion < $this->getVersion ())
throw new OutdatedVersionException ();
$method = $this->app->loadMethod (
$_REQUEST['method'], __NAMESPACE__.'\JsonRequest', './rest');
$json = $method->runRest ();
$json = $this->loadMethod (__NAMESPACE__.'\JsonRequest');
$this->replyJson ($json);
}

View File

@ -4,7 +4,11 @@ namespace Vn\Web;
require_once (__DIR__.'/rest-service.php');
use Vn\Lib;
class Security
{
const DEFINER = 1;
const INVOKER = 2;
}
/**
* Base class for REST services.
@ -12,43 +16,7 @@ use Vn\Lib;
abstract class RestRequest extends \Vn\Lib\Method
{
const PARAMS = NULL;
const LOGIN_REQUIRED = TRUE;
function runRest ()
{
try {
$db = $this->login ();
}
catch (Exception $e)
{
if (self::LOGIN_REQUIRED)
throw $e;
}
if (self::PARAMS !== NULL && !$this->checkParams ($_REQUEST, self::PARAMS))
throw new Lib\UserException (s('Missing parameters'));
return $this->run ($db);
}
/**
* Authenticates the user agaisnt database and returns its associated
* database connection.
*
* return Db\Conn The database connection
**/
function login ()
{
return $this->app->login ();
}
/**
* Logouts the current user.
**/
function logout ()
{
$this->app->logout ();
}
const SECURITY = Security::DEFINER;
}
?>

View File

@ -19,22 +19,7 @@ class RestService extends Service
set_exception_handler ([$this, 'exceptionHandler']);
$this->startSession ();
$method = $this->app->loadMethod (
$_REQUEST['method'], __NAMESPACE__.'\RestRequest', './rest');
$method->runRest ();
}
/**
* Deinitializes the Application. When init method is called, this
* function is called automatically at the end of the script .
**/
function deinit ()
{
if ($this->conn)
$this->conn->query ('CALL user_session_end ()');
parent::deinit ();
$this->loadMethod (__NAMESPACE__.'\RestRequest');
}
function statusFromException ($e)

View File

@ -3,24 +3,26 @@
namespace Vn\Web;
require_once ('vn/lib/app.php');
require_once (__DIR__.'/jwt.php');
require_once (__DIR__.'/db-session-handler.php');
use Vn\Lib\Locale;
use Vn\Lib\UserException;
/**
* Thrown when user credentials could not be fetched.
**/
class SessionExpiredException extends Lib\UserException {}
class SessionExpiredException extends UserException {}
/**
* Thrown when user credentials are invalid.
**/
class BadLoginException extends Lib\UserException {}
class BadLoginException extends UserException {}
/**
* Thrown when client version is outdated.
**/
class OutdatedVersionException extends Lib\UserException {}
class OutdatedVersionException extends UserException {}
/**
* Main class for web applications.
@ -28,10 +30,13 @@ class OutdatedVersionException extends Lib\UserException {}
abstract class Service
{
protected $app;
protected $db;
protected $userDb = NULL;
function __construct ($app)
{
$this->app = $app;
$this->db = $app->getSysConn ();
}
/**
@ -39,12 +44,12 @@ abstract class Service
**/
function startSession ()
{
$db = $this->app->getSysConn ();
$db = $this->db;
ini_set ('session.cookie_secure', TRUE);
ini_set ('session.use_only_cookies', FALSE);
ini_set ('session.cookie_path', 'cookies');
ini_set ('session.hash_function', 'sha512');
ini_set ('session.hash_function', 'sha256');
session_set_save_handler (new DbSessionHandler ($db));
session_start ();
@ -128,7 +133,7 @@ abstract class Service
**/
function login ()
{
$db = $this->getSysConn ();
$db = $this->db;
$user = NULL;
$wasLoged = isset ($_SESSION['user']);
@ -175,7 +180,7 @@ abstract class Service
// Registering the user access
if (isset ($_SESSION['access'])
&& (!isset ($_SESSION['visitUser'] || $wasLoged)))
&& (!isset ($_SESSION['visitUser']) || $wasLoged))
{
$_SESSION['visitUser'] = TRUE;
@ -184,15 +189,6 @@ abstract class Service
[$_SESSION['access'], session_id ()]
);
}
$db->query ('CALL userSessionStart (#)', [session_id ()]);
}
function deinit ()
{
$db = $this->getSysConn ();
$db->query ('CALL userSessionEnd ()');
$db->query ('CALL account.userLogout ()');
}
/**
@ -204,6 +200,56 @@ abstract class Service
unset ($_SESSION['user']);
}
/**
* Creates or returns a database connection where the authenticated user
* is the current logged user.
*
* @return {Db\Conn} The database connection
**/
function getUserDb ($user)
{
if ($this->userDb)
return $this->userDb;
$password = $sysDb->getValue (
'SELECT password FROM user WHERE name = #', [$user]);
return $this->userDb = $this->createConnection ($user, $password);
}
/**
* Runs a method.
**/
function loadMethod ($class)
{
$db = $this->db;
$this->login ();
$method = $this->app->loadMethod (
$_REQUEST['method'], $class, './rest');
if ($method::SECURITY == Security::DEFINER)
{
$isAuthorized = $db->getValue ('SELECT userCheckRestPriv (#)',
[$_REQUEST['method']]);
if (!$isAuthorized)
throw new UserException ('You don\'t have enough privileges');
$methodDb = $db;
}
else
$methodDb = $this->getUserDb ();
if ($method::PARAMS !== NULL && !$method->checkParams ($_REQUEST, $method::PARAMS))
throw new UserException (s('Missing parameters'));
$res = $method->run ($methodDb);
$db->query ('CALL account.userLogout ()');
return $res;
}
/**
* Checks if the HTTP connection is secure.
*