Various updates.

This commit is contained in:
Julien Fontanet 2013-03-22 14:37:25 +01:00
parent 029a9293f0
commit 27178cfd09
16 changed files with 862 additions and 383 deletions

8
composer.lock generated
View File

@ -188,12 +188,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/vatesfr/rekodi.git", "url": "https://github.com/vatesfr/rekodi.git",
"reference": "7a8d126055acfe4e3dbbdf01d50e3143952b8b77" "reference": "b95faecb9b5b0f9e541a0cf8aac680cad834a4be"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/vatesfr/rekodi/zipball/7a8d126055acfe4e3dbbdf01d50e3143952b8b77", "url": "https://api.github.com/repos/vatesfr/rekodi/zipball/b95faecb9b5b0f9e541a0cf8aac680cad834a4be",
"reference": "7a8d126055acfe4e3dbbdf01d50e3143952b8b77", "reference": "b95faecb9b5b0f9e541a0cf8aac680cad834a4be",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -222,7 +222,7 @@
"keywords": [ "keywords": [
"event" "event"
], ],
"time": "2013-03-03 13:25:27" "time": "2013-03-22 12:24:15"
} }
], ],
"packages-dev": [ "packages-dev": [

View File

@ -25,7 +25,8 @@
return array( return array(
'database' => array( 'database' => array(
'json' => '#{root_dir}/database.json', 'type' => 'json',
'file' => '#{root_dir}/database.json',
), ),
'listen' => array( 'listen' => array(

View File

@ -22,31 +22,13 @@
* @package Xen Orchestra Server * @package Xen Orchestra Server
*/ */
use Bean\User;
/** /**
* *
*/ */
final class Application extends Base final class Application extends Base
{ {
/**
*
*/
const NONE = 0;
/**
*
*/
const READ = 1;
/**
*
*/
const WRITE = 2;
/**
*
*/
const ADMIN = 3;
/** /**
* *
*/ */
@ -75,30 +57,30 @@ final class Application extends Base
return array(0, 'already authenticated'); return array(0, 'already authenticated');
} }
$users = $this->_di->get('users');
// Checks the user exists. // Checks the user exists.
if (!isset($this->_usersByName[$name])) $user = $users->getBy('name', $name, false);
if (!$user)
{ {
return array(1, 'invalid credential'); return array(1, 'invalid credential');
} }
$uid = $this->_usersByName[$name];
$hash = &$this->_users[$uid]['password'];
// Checks the password matches. // Checks the password matches.
if (!password_verify($password, $hash)) if (!password_verify($password, $user->password))
{ {
return array(1, 'invalid credential'); return array(1, 'invalid credential');
} }
// Checks whether the hash needs to be updated. // Checks whether the hash needs to be updated.
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) if (password_needs_rehash($user->password, PASSWORD_DEFAULT))
{ {
$hash = password_hash($password, PASSWORD_DEFAULT); $user->password = password_hash($password, PASSWORD_DEFAULT);
$this->_saveDatabase(); $users->save($user);
} }
// Marks the client as authenticated. // Marks the client as authenticated.
$c->uid = $uid; $c->uid = $user->id;
// Returns success. // Returns success.
$c->respond($id, true); $c->respond($id, true);
@ -122,23 +104,24 @@ final class Application extends Base
return array(0, 'already authenticated'); return array(0, 'already authenticated');
} }
$tokens = $this->_di->get('tokens');
// Checks the token exists. // Checks the token exists.
if (!isset($this->_tokens[$token])) $token = $tokens->get($token, false);
if (!$token)
{ {
return array(1, 'invalid token'); return array(1, 'invalid token');
} }
$record = $this->_tokens[$token];
// Checks the token is valid. // Checks the token is valid.
if ($record['expiration'] < time()) if ($token->expiration < time())
{ {
unset($this->_tokens[$token]); $tokens->delete($token->id);
return array(1, 'invalid token'); return array(1, 'invalid token');
} }
// Marks the client as authenticated. // Marks the client as authenticated.
$c->uid = $record['uid']; $c->uid = $token->user_id;
// Returns success. // Returns success.
$c->respond($id, true); $c->respond($id, true);
@ -154,10 +137,12 @@ final class Application extends Base
return array(0, 'not authenticated'); return array(0, 'not authenticated');
} }
$user = $this->_di->get('users')->get($c->uid);
$c->respond($id, array( $c->respond($id, array(
'id' => (string) $c->uid, 'id' => $user->id,
'name' => $this->_users[$c->uid]['name'], 'name' => $user->name,
'permission' => $this->_users[$c->uid]['permission'], 'permission' => $user->permission,
)); ));
} }
@ -172,6 +157,8 @@ final class Application extends Base
return array(0, 'not authenticated'); return array(0, 'not authenticated');
} }
$tokens = $this->_di->get('tokens');
// Generates the token and makes sure it is unique. // Generates the token and makes sure it is unique.
do do
{ {
@ -188,14 +175,14 @@ final class Application extends Base
{ {
$token = uniqid('', true); $token = uniqid('', true);
} }
} while (isset($this->_tokens[$token])); } while ($tokens->get($token, false));
// Registers it. // Registers it.
$this->_tokens[$token] = array( $tokens->create(array(
'id' => $token,
'expiration' => time() + 604800, // One week 'expiration' => time() + 604800, // One week
'uid' => $c->uid, 'user_id' => $c->uid,
); ));
$this->_saveDatabase();
// Returns it. // Returns it.
$c->respond($id, $token); $c->respond($id, $token);
@ -206,15 +193,23 @@ final class Application extends Base
*/ */
function api_session_destroyToken($id, array $params, Client $c) function api_session_destroyToken($id, array $params, Client $c)
{ {
// Checks parameters.
if (!isset($params[0]))
{
return -32602; // Invalid params.
}
$token = $params[0];
$tokens = $this->_di->get('tokens');
// Checks the token exists. // Checks the token exists.
if (!isset($this->_tokens[$token])) if (!$tokens->get($token, false))
{ {
return array(0, 'invalid token'); return array(0, 'invalid token');
} }
// Deletes it. // Deletes it.
unset($this->_tokens[$token]); $tokens->delete($token);
$this->_saveDatabase();
// Returns success. // Returns success.
$c->respond($id, true); $c->respond($id, true);
@ -234,21 +229,19 @@ final class Application extends Base
// Checks credentials. // Checks credentials.
if (!$c->isAuthenticated() if (!$c->isAuthenticated()
|| !$this->_checkPermission($c->uid, self::ADMIN)) || !$this->_checkPermission($c->uid, User::ADMIN))
{ {
return array(0, 'not authorized'); return array(0, 'not authorized');
} }
// Checks the provided user name. // Checks the provided user name.
if (!is_string($name) if (!User::check('name', $name))
|| !preg_match('/^[a-z0-9]+(?:[-_.][a-z0-9]+)*$/', $name))
{ {
return array(1, 'invalid user name'); return array(1, 'invalid user name');
} }
// Checks the provided password. // Checks the provided password.
if (!is_string($password) if (!User::check('password', $password))
|| !preg_match('/^.{8,}$/', $password))
{ {
return array(2, 'invalid password'); return array(2, 'invalid password');
} }
@ -256,36 +249,34 @@ final class Application extends Base
// Checks provided permission. // Checks provided permission.
if (isset($params[2])) if (isset($params[2]))
{ {
$permission = self::_permissionFromString($params[2]); $permission = $params[2];
if ($permission === false) if (!User::check('permission', $permission))
{ {
return array(3, 'invalid permission'); return array(3, 'invalid permission');
} }
} }
else else
{ {
$permission = self::NONE; $permission = User::NONE;
} }
$users = $this->_di->get('users');
// Checks if the user name is already used. // Checks if the user name is already used.
if (isset($this->_usersByName[$name])) if ($users->getBy('name', $name, false))
{ {
return array(4, 'user name already taken'); return array(4, 'user name already taken');
} }
// Creates the user. // Creates the user.
$this->_users[] = array( $user = $users->create(array(
'name' => $name, 'name' => $name,
'password' => password_hash($password, PASSWORD_DEFAULT), 'password' => password_hash($password, PASSWORD_DEFAULT),
'permission' => $permission, 'permission' => $permission,
); ));
end($this->_users);
$uid = (string) key($this->_users);
$this->_usersByName[$name] = $uid;
$this->_saveDatabase();
// Returns the identifier. // Returns the identifier.
$c->respond($id, $uid); $c->respond($id, $user->id);
} }
/** /**
@ -302,22 +293,22 @@ final class Application extends Base
// Checks credentials. // Checks credentials.
if (!$c->isAuthenticated() if (!$c->isAuthenticated()
|| !$this->_checkPermission($c->uid, self::ADMIN)) || !$this->_checkPermission($c->uid, User::ADMIN))
{ {
return array(0, 'not authorized'); return array(0, 'not authorized');
} }
$users = $this->_di->get('users');
// Checks user exists and is not the current user. // Checks user exists and is not the current user.
if (!isset($this->_users[$uid]) if (($uid === $c->uid)
|| ($uid === $c->uid)) || !$users->get($uid, false))
{ {
return array(1, 'invalid user'); return array(1, 'invalid user');
} }
// Deletes the user. // Deletes the user.
$name = $this->_users[$uid]['name']; $users->delete($uid);
unset($this->_users[$uid], $this->_usersByName[$name]);
$this->_saveDatabase();
// Returns success. // Returns success.
$c->respond($id, true); $c->respond($id, true);
@ -341,23 +332,24 @@ final class Application extends Base
return array(0, 'not authenticated'); return array(0, 'not authenticated');
} }
$hash = &$this->_users[$c->uid]['password']; $users = $this->_di->get('users');
$user = $users->get($c->uid);
// Checks the old password matches. // Checks the old password matches.
if (!password_verify($old, $hash)) if (!password_verify($old, $user->password))
{ {
return array(1, 'invalid credential'); return array(1, 'invalid credential');
} }
// Checks the new password is valid. // Checks the new password is valid.
if (!is_string($new) if (($new === $old)
|| !preg_match('/^.{8,}$/', $new)) || !User::check('password', $new))
{ {
return array(2, 'invalid password'); return array(2, 'invalid password');
} }
$hash = password_hash($new, PASSWORD_DEFAULT); $user->password = password_hash($new, PASSWORD_DEFAULT);
$this->_saveDatabase(); $users->save($user);
// Returns success. // Returns success.
$c->respond($id, true); $c->respond($id, true);
@ -370,23 +362,56 @@ final class Application extends Base
{ {
// Checks credentials. // Checks credentials.
if (!$c->isAuthenticated() if (!$c->isAuthenticated()
|| !$this->_checkPermission($c->uid, self::ADMIN)) || !$this->_checkPermission($c->uid, User::ADMIN))
{ {
return array(0, 'not authorized'); return array(0, 'not authorized');
} }
$users = array(); $users = $this->_di->get('users')->getArray(
foreach ($this->_users as $uid => $user) null,
{ array('id', 'name', 'permission')
$users[] = array( );
'id' => $uid,
'name' => $user['name'],
'permission' => self::_permissionToString($user['permission']),
);
}
$c->respond($id, $users); $c->respond($id, $users);
} }
/**
*
*/
function api_user_set($id, array $params, Client $c)
{
// Checks parameter.
if (!isset($params[0], $params[1]))
{
return -32602; // Invalid params.
}
list($id, $properties) = $params;
if (!$c->isAuthenticated()
|| !$this->_checkPermission($c->uid, User::ADMIN))
{
return array(0, 'not authorized');
}
$users = $this->_di->get('users');
$user = $users->get($id);
foreach ($properties as $field => $value)
{
switch ($field)
{
case 'name':
case 'password':
case 'permission':
default:
return array(2, 'invalid property');
}
}
$users->save($user);
$c->respond($id, true);
}
/** /**
* *
*/ */
@ -394,7 +419,7 @@ final class Application extends Base
{ {
// @todo Handles parameter. // @todo Handles parameter.
$c->respond($id, $this->_xenVms); $c->respond($id, $this->_di->get('vms')->getArray());
} }
/** /**
@ -515,29 +540,27 @@ final class Application extends Base
*/ */
function updateXenVms(array $vms) function updateXenVms(array $vms)
{ {
foreach ($vms as $ref => $vm) $manager = $this->_di->get('vms');
foreach ($vms as $id => $properties)
{ {
if ($vm['is_a_template']) $properties['id'] = $id;
$vm = $manager->get($id, false);
if (!$vm)
{ {
$_ = 'template'; $manager->create($properties);
} echo "new VM: $id\n";
elseif ($vm['is_a_snapshot'])
{
$_ = 'snapshot';
}
elseif ($vm['is_control_domain'])
{
$_ = 'control_domain';
} }
else else
{ {
$_ = 'normal'; $vm->set($properties, true);
$keys = array_keys($vm->getDirty());
sort($keys);
$dirty = implode(', ', $keys);
$manager->save($vm);
echo "updated VM: $id ($dirty)\n";
} }
$this->_update(
$this->_xenVms[$_][$ref],
$vm
);
} }
} }
@ -546,10 +569,6 @@ final class Application extends Base
*/ */
function run() function run()
{ {
$this->_loadDatabase();
//--------------------------------------
$config = $this->_di->get('config'); $config = $this->_di->get('config');
$loop = $this->_di->get('loop'); $loop = $this->_di->get('loop');
@ -628,37 +647,6 @@ final class Application extends Base
*/ */
private $_di; private $_di;
/**
* Mapping from user identifier to record.
*
* Each record contains:
* - “name” (string): the user name used for sign in;
* - “password” (string): the user password hashed for sign in.
*
* @var array
*/
private $_users = array();
/**
* Mapping from user name to identifier.
*
* @var array
*/
private $_usersByName = array();
/**
* Tokens that may be used to authenticate clients.
*
* Each token record is an array containing:
* - “expiration” (integer): timestamp of when this token will be
* considered invalid;
* - “uid” (string): the identifier of the user authenticated with
* this token.
*
* @var array
*/
private $_tokens = array();
/** /**
* @var array * @var array
*/ */
@ -669,11 +657,6 @@ final class Application extends Base
*/ */
private $_xenHosts = array(); private $_xenHosts = array();
/**
* @var array
*/
private $_xenVms = array();
/** /**
* *
*/ */
@ -686,40 +669,6 @@ final class Application extends Base
return gettype($val); return gettype($val);
} }
/**
*
*/
private static function _permissionFromString($string)
{
$permissions = array(
'none' => self::NONE,
'read' => self::READ,
'write' => self::WRITE,
'admin' => self::ADMIN
);
return isset($permissions[$string])
? $permissions[$string]
: false;
}
/**
*
*/
private static function _permissionToString($permission)
{
$permissions = array(
self::NONE => 'none',
self::READ => 'read',
self::WRITE => 'write',
self::ADMIN => 'admin',
);
return isset($permissions[$permission])
? $permissions[$permission]
: false;
}
/** /**
* *
*/ */
@ -770,90 +719,13 @@ final class Application extends Base
$old = $new; $old = $new;
} }
/**
*
*/
private function _saveDatabase()
{
$data = json_encode(array(
'users' => $this->_users,
'usersByName' => $this->_usersByName,
'tokens' => $this->_tokens,
));
$bytes = @file_put_contents(
$this->_di->get('config')['database.json'],
$data
);
if ($bytes === false)
{
trigger_error(
'could not write the database',
E_USER_ERROR
);
}
}
/**
*
*/
private function _loadDatabase()
{
$file = $this->_di->get('config')['database.json'];
if (!file_exists($file))
{
trigger_error(
'no such database, using default values (admin:admin)',
E_USER_WARNING
);
// @todo Factorizes this code with api_user_create().
$this->_users = array(
1 => array(
'name' => 'admin',
'password' => '$2y$10$VzBQqiwnhG5zc2.MQmmW4ORcPW6FE7SLhPr1VBV2ubn5zJoesnmli',
'permission' => self::ADMIN,
),
);
$this->_usersByName = array(
'admin' => '1',
);
return;
}
$data = @file_get_contents(
$this->_di->get('config')['database.json']
);
if (($data === false)
|| (($data = json_decode($data, true)) === null))
{
trigger_error(
'could not read the database',
E_USER_ERROR
);
}
foreach (array('users', 'usersByName', 'tokens') as $entry)
{
if (!isset($data[$entry]))
{
trigger_error(
"missing entry from the database: $entry",
E_USER_ERROR
);
}
$this->{'_'.$entry} = $data[$entry];
}
}
/** /**
* *
*/ */
private function _checkPermission($uid, $permission, $object = null) private function _checkPermission($uid, $permission, $object = null)
{ {
return ($this->_users[$uid]['permission'] >= $permission); $user = $this->_di->get('users')->get($uid);
return ($user->permission >= $permission);
} }
} }

47
lib/Bean/Token.php Normal file
View File

@ -0,0 +1,47 @@
<?php
/**
* This file is a part of Xen Orchestra Server.
*
* Xen Orchestra Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Xen Orchestra Server is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xen Orchestra Server. If not, see
* <http://www.gnu.org/licenses/>.
*
* @author Julien Fontanet <julien.fontanet@vates.fr>
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
*
* @package Xen Orchestra Server
*/
namespace Bean;
/**
*
*/
final class Token extends \Rekodi\Bean
{
/**
* This function is not necessary but allow us to dynamically
* initialize our bean.
*/
static function init()
{
self::$_fields = array_flip(array(
'id',
'expiration',
'user_id',
));
}
protected static $_fields;
}
Token::init();

View File

@ -29,6 +29,60 @@ namespace Bean;
*/ */
final class User extends \Rekodi\Bean final class User extends \Rekodi\Bean
{ {
/**
*
*/
const NONE = 0;
/**
*
*/
const READ = 1;
/**
*
*/
const WRITE = 2;
/**
*
*/
const ADMIN = 3;
/**
*
*/
static function permissionFromString($string)
{
$permissions = array(
'none' => self::NONE,
'read' => self::READ,
'write' => self::WRITE,
'admin' => self::ADMIN
);
return isset($permissions[$string])
? $permissions[$string]
: false;
}
/**
*
*/
static function permissionToString($permission)
{
$permissions = array(
self::NONE => 'none',
self::READ => 'read',
self::WRITE => 'write',
self::ADMIN => 'admin',
);
return isset($permissions[$permission])
? $permissions[$permission]
: false;
}
/** /**
* This function is not necessary but allow us to dynamically * This function is not necessary but allow us to dynamically
* initialize our bean. * initialize our bean.
@ -39,9 +93,37 @@ final class User extends \Rekodi\Bean
'id', 'id',
'name', 'name',
'password', 'password',
'permission',
)); ));
} }
/**
*
*/
static function check($field, &$value)
{
switch ($field)
{
case 'id':
return true;
case 'name':
return (
is_string($value)
&& preg_match('/^[a-z0-9]+(?:[-_.][a-z0-9]+)*$/', $value)
);
case 'password':
return (
is_string($value)
&& preg_match('/^.{8,}$/', $value)
);
case 'permission':
$value = self::permissionFromString($value);
return (false !== $value);
}
return false;
}
protected static $_fields; protected static $_fields;
} }
User::init(); User::init();

173
lib/Bean/VM.php Normal file
View File

@ -0,0 +1,173 @@
<?php
/**
* This file is a part of Xen Orchestra Server.
*
* Xen Orchestra Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Xen Orchestra Server is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xen Orchestra Server. If not, see
* <http://www.gnu.org/licenses/>.
*
* @author Julien Fontanet <julien.fontanet@vates.fr>
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
*
* @package Xen Orchestra Server
*/
namespace Bean;
/**
*
*/
final class VM extends \Rekodi\Bean
{
/**
* This function is not necessary but allow us to dynamically
* initialize our bean.
*/
static function init()
{
self::$_fields = array_flip(array(
// Identifiers.
'id',
'name_label',
'resident_on', // The host this VM is currently resident on.
'domarch', // Domain architecture if available, null otherwise.
'domid', // Domain ID.
// Description.
'name_description',
'is_a_template',
'is_a_snapshot',
'is_control_domain',
'tags',
// Technical characteristics.
'attached_PCIs',
'platform',
'VBDs', // Virtual Block Devices.
'VCPUs_at_startup',
'VCPUs_max',
'VCPUs_params',
'VGPUs',
'VIFs', // Virtual Network Interface (string[]). @todo
//'VTPMs', // Virtual Trust Platform Modules ???
// Event-related actions.
'actions_after_crash',
'actions_after_reboot',
'actions_after_shutdown',
// Current state.
'guest_metrics',
'memory_dynamic_max',
'memory_dynamic_min',
'memory_overhead',
'memory_static_max',
'memory_static_min',
'memory_target',
'metrics',
'power_state',
// Snapshot-related info.
'shutdown_delay',
'snapshot_info',
'snapshot_metadata',
'snapshot_of',
'snapshot_time',
'snapshots',
'transportable_snapshot_id',
// Operations.
'allowed_operations',
'current_operations',
// Various.
'consoles',
// 'affinity',
// 'appliance',
// 'bios_strings',
// 'blobs',
// 'blocked_operations',
// 'children', // ???
// 'crash_dumps',
// 'ha_always_run',
// 'ha_restart_priority',
// 'HVM_boot_params',
// 'HVM_boot_policy',
// 'HVM_shadow_multiplier',
// 'is_snapshot_from_vmpp',
// 'last_boot_CPU_flags',
// 'last_booted_record',
// 'order',
// 'other_config',
// 'parent', // ???
// 'PCI_bus',
// 'protection_policy',
// 'PV_args',
// 'PV_bootloader',
// 'PV_bootloader_args',
// 'PV_kernel',
// 'PV_legacy_args',
// 'PV_ramdisk',
// 'recommendations',
// 'start_delay',
// 'suspend_SR',
// 'suspend_VDI',
// 'user_version',
// 'version',
// 'xenstore_data',
));
}
function __get($name)
{
static $json_encoded;
if (!$json_encoded)
{
$json_encoded = array_flip(array(
'VBDs',
'VCPUs_params',
'VGPUs',
'VIFs',
'allowed_operations',
'consoles',
'crash_dumps',
'current_operations',
'platform',
'snapshot_info',
'tags',
));
}
$value = parent::__get($name);
if (isset($json_encoded[$name]))
{
return json_decode($value, true);
}
return $value;
}
function __set($name, $value)
{
if (is_array($value)
|| is_object($value))
{
$value = json_encode($value);
}
return parent::__set($name, $value);
}
protected static $_fields;
}
VM::init();

View File

@ -30,7 +30,7 @@ final class Client extends Base
/** /**
* *
*/ */
public $uid = 0; public $uid = null;
/** /**
* @param Loop $loop * @param Loop $loop
@ -54,7 +54,7 @@ final class Client extends Base
*/ */
function isAuthenticated() function isAuthenticated()
{ {
return ($this->uid !== 0); return (null !== $this->uid);
} }
/** /**

View File

@ -89,7 +89,7 @@ final class Config extends Base implements
$entries = $entries->_entries; $entries = $entries->_entries;
} }
$this->_entries = array_merge_recursive($this->_entries, $entries); self::_merge($this->_entries, $entries);
} }
/** /**
@ -195,6 +195,26 @@ final class Config extends Base implements
//-------------------------------------- //--------------------------------------
/**
*
*/
static private function _merge(array &$ar1, array $ar2)
{
foreach ($ar2 as $key => $val)
{
if (is_array($val)
&& isset($ar1[$key])
&& is_array($ar1[$key]))
{
self::_merge($ar1[$key], $val);
}
else
{
$ar1[$key] = $val;
}
}
}
/** /**
* @var array * @var array
*/ */

View File

@ -71,6 +71,71 @@ final class DI extends Base
return new Application($this); return new Application($this);
} }
private function _init_database()
{
$config = $this->get('config');
$type = $config['database.type'];
if ('json' !== $type)
{
trigger_error(
'unsupported database type ('.$type.')',
E_USER_ERROR
);
}
$file = $config['database.file'];
if (file_exists($file))
{
$data = @file_get_contents($file);
if ((false === $data)
|| (null === ($data = json_decode($data, true))))
{
trigger_error(
'could not read the database',
E_USER_ERROR
);
}
return \Rekodi\Manager\Memory::createFromState($data);
}
$manager = new \Rekodi\Manager\Memory;
// Create tables.
$manager->createTable('tokens', function ($table) {
$table
->string('id')->unique()
->integer('expiration')
->string('user_id')
;
});
$manager->createTable('users', function ($table) {
$table
->integer('id')->autoIncremented()
->string('name')->unique()
->string('password')
->integer('permission')
;
});
// Insert initial data.
$manager->create('users', array(
array(
'name' => 'admin',
'password' => '$2y$10$VzBQqiwnhG5zc2.MQmmW4ORcPW6FE7SLhPr1VBV2ubn5zJoesnmli',
'permission' => \Bean\User::ADMIN,
),
));
trigger_error(
'no existing database, creating default user (admin:admin)',
E_USER_WARNING
);
return $manager;
}
private function _init_errorLogger() private function _init_errorLogger()
{ {
return new ErrorLogger($this->get('logger')); return new ErrorLogger($this->get('logger'));
@ -109,4 +174,30 @@ final class DI extends Base
{ {
return new Loop; return new Loop;
} }
private function _init_tokens()
{
return new \Manager\Tokens(
$this->get('database')
);
}
private function _init_users()
{
return new \Manager\Users(
$this->get('database')
);
}
private function _init_vms()
{
$database = new \Rekodi\Manager\Memory;
$database->createTable('vms', function ($table) {
$table
->string('id')->unique()
;
});
return new \Manager\VMs($database);
}
} }

View File

@ -119,8 +119,10 @@ final class Loop extends Base
foreach ($read as $handle) foreach ($read as $handle)
{ {
$callback = $this->_readCallbacks[(int) $handle]; $result = call_user_func(
$result = call_user_func($callback, $handle, $user_data); $this->_readCallbacks[(int) $handle],
$handle, $user_data
);
if (!is_resource($handle)) if (!is_resource($handle))
{ {
@ -133,8 +135,10 @@ final class Loop extends Base
} }
foreach ($write as $handle) foreach ($write as $handle)
{ {
$callback = $this->_writeCallbacks[(int) $handle]; $result = call_user_func(
$result = call_user_func($callback, $handle, $user_data); $this->_writeCallbacks[(int) $handle],
$handle, $user_data
);
if (!is_resource($handle)) if (!is_resource($handle))
{ {

View File

@ -0,0 +1,186 @@
<?php
/**
* This file is a part of Xen Orchestra Server.
*
* Xen Orchestra Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Xen Orchestra Server is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xen Orchestra Server. If not, see
* <http://www.gnu.org/licenses/>.
*
* @author Julien Fontanet <julien.fontanet@vates.fr>
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
*
* @package Xen Orchestra Server
*/
namespace Manager;
/**
*
*/
abstract class ManagerAbstract
{
/**
*
*/
protected function __construct(\Rekodi\Manager $manager, $table, $bean)
{
$this->_manager = $manager;
$this->_table = $table;
$this->_bean = $bean;
}
/**
*
*/
function __destruct()
{}
/**
*
*/
function create(array $properties)
{
$class = $this->_bean;
$bean = new $class($properties, true);
$new_props = $this->_manager->create(
$this->_table,
array($bean->getProperties())
);
if (count($new_props) !== 1)
{
trigger_error(
'unexpected number of created '.$this->_table.' ('.$n.')',
E_USER_ERROR
);
}
$bean->set($new_props[0]);
$bean->markAsClean();
return $bean;
}
/**
*
*/
function delete($id)
{
$n = $this->_manager->delete($this->_table, array('id' => $id));
if (1 !== $n)
{
trigger_error(
'unexpected number of deleted '.$this->_table.' ('.$n.')',
E_USER_ERROR
);
}
}
/**
*
*/
function getBy($field, $value, $default = 'fatal error')
{
$beans = $this->_manager->get(
$this->_table,
array($field => $value)
);
if ($beans)
{
$class = $this->_bean;
return new $class($beans[0], true);
}
if (func_num_args() >= 3)
{
return $default;
}
trigger_error(
'no such '.$this->_table.' ('.$field.' = '.$value.')',
E_USER_ERROR
);
}
/**
* @param string $id
* @param mixed $default
*
* @return Bean
*/
function get($id, $default = 'fatal error')
{
if (func_num_args() === 1)
{
return $this->getBy('id', $id);
}
return $this->getBy('id', $id, $default);
}
/**
*
*/
function getArray($filter = null, $fields = null)
{
return $this->_manager->get(
$this->_table,
$filter,
$fields
);
}
/**
*
*/
function save(\Rekodi\Bean $bean)
{
$dirty = $bean->getDirty();
if (!$dirty)
{
// Nothing has changed.
return;
}
$n = $this->_manager->update(
$this->_table,
array('id' => $bean->id),
$bean->getDirty()
);
if (1 !== $n)
{
trigger_error(
'unexpected number of updated '.$this->_table.' ('.$n.')',
E_USER_ERROR
);
}
}
/**
* @var \Rekodi\Manager
*/
private $_manager;
/**
* @var string
*/
private $_table;
/**
* @var string
*/
private $_bean;
}

39
lib/Manager/Tokens.php Normal file
View File

@ -0,0 +1,39 @@
<?php
/**
* This file is a part of Xen Orchestra Server.
*
* Xen Orchestra Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Xen Orchestra Server is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xen Orchestra Server. If not, see
* <http://www.gnu.org/licenses/>.
*
* @author Julien Fontanet <julien.fontanet@vates.fr>
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
*
* @package Xen Orchestra Server
*/
namespace Manager;
/**
*
*/
final class Tokens extends ManagerAbstract
{
/**
*
*/
function __construct(\Rekodi\Manager $manager)
{
parent::__construct($manager, 'tokens', '\Bean\Token');
}
}

39
lib/Manager/Users.php Normal file
View File

@ -0,0 +1,39 @@
<?php
/**
* This file is a part of Xen Orchestra Server.
*
* Xen Orchestra Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Xen Orchestra Server is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xen Orchestra Server. If not, see
* <http://www.gnu.org/licenses/>.
*
* @author Julien Fontanet <julien.fontanet@vates.fr>
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
*
* @package Xen Orchestra Server
*/
namespace Manager;
/**
*
*/
final class Users extends ManagerAbstract
{
/**
*
*/
function __construct(\Rekodi\Manager $manager)
{
parent::__construct($manager, 'users', '\Bean\User');
}
}

39
lib/Manager/VMs.php Normal file
View File

@ -0,0 +1,39 @@
<?php
/**
* This file is a part of Xen Orchestra Server.
*
* Xen Orchestra Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Xen Orchestra Server is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xen Orchestra Server. If not, see
* <http://www.gnu.org/licenses/>.
*
* @author Julien Fontanet <julien.fontanet@vates.fr>
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
*
* @package Xen Orchestra Server
*/
namespace Manager;
/**
*
*/
final class VMs extends ManagerAbstract
{
/**
*
*/
function __construct(\Rekodi\Manager $manager)
{
parent::__construct($manager, 'vms', '\Bean\VM');
}
}

View File

@ -1,113 +0,0 @@
<?php
/**
* This file is a part of Xen Orchestra Server.
*
* Xen Orchestra Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Xen Orchestra Server is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xen Orchestra Server. If not, see
* <http://www.gnu.org/licenses/>.
*
* @author Julien Fontanet <julien.fontanet@vates.fr>
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
*
* @package Xen Orchestra Server
*/
namespace Model;
/**
*
*/
final class Users
{
/**
*
*/
function __construct(\Rekodi\Manager $manager)
{
$this->_manager = $manager;
}
/**
*
*/
function delete($id)
{
$n = $this->_manager->delete(array('id' => $id));
if ($n !== 1)
{
trigger_error(
'unexpected number of deleted users ('.$n.')',
E_USER_ERROR
);
}
}
/**
* @param string $id
* @param mixed $default
*
* @return User
*/
function get($id, $default = 'fatal error')
{
$users = $this->_manager->get(array('id' => $id));
if ($users)
{
return $users[0];
}
if (func_num_args() >= 2)
{
return $default;
}
trigger_error(
'no such user (id = '.$id.')',
E_USER_ERROR
);
}
/**
*
*/
function save(User $user)
{
if (!isset($user->id))
{
// @todo Fills the user with its generated id.
return;
}
$n = $this->_manager->update(
$user->getOriginals(),
$user->getDirty()
);
if ($n !== 1)
{
trigger_error(
'unexpected number of updated users ('.$n.')',
E_USER_ERROR
);
}
}
/**
* @var \Rekodi\Manager
*/
private $_manager;
}

View File

@ -91,8 +91,7 @@ final class XCP extends Base
$request = array_shift($this->_queue); $request = array_shift($this->_queue);
if ($request[0] !== null) if ($request[0] !== null)
{ {
$callback = $request[0]; $result = call_user_func($request[0], $response, $this);
$result = call_user_func($callback, $response, $this);
if ($result === true) if ($result === true)
{ {
$this->_queue[] = $request; $this->_queue[] = $request;