3 * Bacula(R) - The Network Backup Solution
4 * Baculum - Bacula web interface
6 * Copyright (C) 2013-2016 Kern Sibbald
8 * The main author of Baculum is Marcin Haba.
9 * The original author of Bacula is Kern Sibbald, with contributions
10 * from many others, a complete list can be found in the file AUTHORS.
12 * You may use this file and others of this release according to the
13 * license defined in the LICENSE file, which includes the Affero General
14 * Public License, v3.0 ("AGPLv3") and some additional permissions and
15 * terms pursuant to its AGPLv3 Section 7.
17 * This notice must be preserved when any source code is
18 * conveyed and/or propagated.
20 * Bacula(R) is a registered trademark of Kern Sibbald.
23 Prado::using('System.Exceptions.TException');
24 Prado::using('Application.Class.Errors');
27 * Abstract module from which inherits each of API module.
28 * The module contains methods that are common for all API pages.
30 * @author Marcin Haba <marcin.haba@bacula.pl>
32 abstract class BaculumAPI extends TPage {
35 * Storing output from API commands in numeric array.
40 * Storing error from API commands as integer value.
45 * Storing currently used Director name for bconsole commands.
50 * Web interface User name that sent request to API.
51 * Null value means administrator, any other value means normal user
61 const GET_METHOD = 'GET';
63 // create new elemenet
64 const POST_METHOD = 'POST';
67 const PUT_METHOD = 'PUT';
70 const DELETE_METHOD = 'DELETE';
73 * Get request, login user and do request action.
76 * @param mixed $params onInit action params
79 public function onInit($params) {
80 parent::onInit($params);
82 * Workaround to bug in PHP 5.6 by FastCGI that caused general protection error.
83 * TODO: Check on newer PHP if it is already fixed.
85 $db = new ActiveRecord();
86 $db->getDbConnection();
88 // set Director to bconsole execution
89 $this->director = isset($this->Request['director']) ? $this->Request['director'] : null;
92 * User and password are obligatory for each request. Otherwise authorization
94 * Password is provided in hashed form.
96 $user = isset($_SERVER['HTTP_X_BACULUM_USER']) ? $_SERVER['HTTP_X_BACULUM_USER']: null;
97 $pwd_hash = isset($_SERVER['HTTP_X_BACULUM_PWD']) ? $_SERVER['HTTP_X_BACULUM_PWD']: null;
98 if (!is_null($user) && !is_null($pwd_hash)) {
100 $logged = $this->getModule('users')->loginUser($user, $pwd_hash);
101 if ($logged === true) {
103 * User and password are valid.
104 * Log in action finished successfuly.
105 * Now check if logged in user is admin or normal user.
106 * Admin value is null. Normal user value is string with the user name.
108 $this->user = ($this->User->getIsAdmin() === false) ? $user : null;
110 // Invalid credentials. Authorization error.
111 $this->output = AuthorizationError::MSG_ERROR_AUTHORIZATION_TO_WEBGUI_PROBLEM;
112 $this->error = AuthorizationError::ERROR_AUTHORIZATION_TO_WEBGUI_PROBLEM;
116 // Not provided user or password. Authorization error.
117 $this->output = AuthorizationError::MSG_ERROR_AUTHORIZATION_TO_WEBGUI_PROBLEM;
118 $this->error = AuthorizationError::ERROR_AUTHORIZATION_TO_WEBGUI_PROBLEM;
122 switch($_SERVER['REQUEST_METHOD']) {
123 case self::PUT_METHOD: {
126 } catch(TDbException $e) {
127 $this->getModule('logging')->log(
130 Logging::CATEGORY_APPLICATION,
134 $this->output = DatabaseError::MSG_ERROR_DB_CONNECTION_PROBLEM;
135 $this->error = DatabaseError::ERROR_DB_CONNECTION_PROBLEM;
139 case self::GET_METHOD: {
142 } catch(TDbException $e) {
143 $this->getModule('logging')->log(
146 Logging::CATEGORY_APPLICATION,
150 $this->output = DatabaseError::MSG_ERROR_DB_CONNECTION_PROBLEM;
151 $this->error = DatabaseError::ERROR_DB_CONNECTION_PROBLEM;
155 case self::POST_METHOD: {
158 } catch(TDbException $e) {
159 $this->getModule('logging')->log(
162 Logging::CATEGORY_APPLICATION,
166 $this->output = DatabaseError::MSG_ERROR_DB_CONNECTION_PROBLEM;
167 $this->error = DatabaseError::ERROR_DB_CONNECTION_PROBLEM;
171 case self::DELETE_METHOD: {
174 } catch(TDbException $e) {
175 $this->getModule('logging')->log(
178 Logging::CATEGORY_APPLICATION,
182 $this->output = DatabaseError::MSG_ERROR_DB_CONNECTION_PROBLEM;
183 $this->error = DatabaseError::ERROR_DB_CONNECTION_PROBLEM;
191 * Get request result data and pack it in JSON format.
193 * "output": (list) output values
194 * "error" : (integer) result exit code (0 - OK, non-zero - error)
197 * @return string JSON value with output and error values
199 private function getOutput() {
200 $output = array('output' => $this->output, 'error' => $this->error);
201 $json = json_encode($output);
206 * Return action result which was realized in onInit() method.
207 * On standard output is printed JSON value with request results.
210 * @param mixed $params onInit action params
213 public function onLoad($params) {
214 parent::onLoad($params);
215 echo $this->getOutput();
219 * Each of API module should have get() method defined.
220 * Designed to getting data from API.
225 abstract protected function get();
228 * Changing/updating values via API.
233 private function put() {
234 $id = isset($this->Request['id']) ? $this->Request['id'] : null;
237 * Check if it is possible to read PUT method data.
238 * Note that some clients sends data in PUT request as PHP input stream which
239 * is not possible to read by $_REQUEST data. From this reason, when is
240 * not possible to ready by superglobal $_REQUEST variable, then is try to
241 * read PUT data by PHP input stream.
243 if (is_array($this->Request['update']) && count($this->Request['update']) > 0) {
244 // $_REQUEST available to read
245 $params = (object)$this->Request['update'];
246 $this->set($id, $params);
248 // no possibility to read data from $_REQUEST. Try to load from input stream.
249 $inputstr = file_get_contents("php://input");
252 * Read using chunks for case large updates (over 1000 values).
253 * Otherwise max_input_vars limitation in php.ini can be reached (usually
254 * set to 1000 variables)
255 * @see http://php.net/manual/en/info.configuration.php#ini.max-input-vars
257 $chunks = explode('&', $inputstr);
259 $response_data = array();
260 for($i = 0; $i<count($chunks); $i++) {
261 // if chunks would not be used, then here occurs reach max_input_vars limit
262 parse_str($chunks[$i], $response_el);
263 if (is_array($response_el) && array_key_exists('update', $response_el) && is_array($response_el['update'])) {
264 $key = key($response_el['update']);
265 $response_data['update'][$key] = $response_el['update'][$key];
268 if (is_array($response_data) && array_key_exists('update', $response_data)) {
269 $params = (object)$response_data['update'];
270 $this->set($id, $params);
273 * This case should never occur because it means that there is
274 * given nothing to update.
276 $this->set($id, array());
282 * Creating new elements.
287 private function post() {
288 if (is_array($this->Request['create']) && count($this->Request['create']) > 0) {
289 $params = (object)$this->Request['create'];
290 $this->create($params);
295 * Deleting element by element ID.
300 private function delete() {
301 if (isset($this->Request['id'])) {
302 $id = intval($this->Request['id']);
308 * Shortcut method for getting application modules instances by
312 * @param string $name application module name
313 * @return object module class instance
315 public function getModule($name) {
316 return $this->Application->getModule($name);