]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
baculum: Refactor authentication, authorization and exceptions
authorMarcin Haba <marcin.haba@bacula.pl>
Sun, 24 Nov 2019 02:50:55 +0000 (03:50 +0100)
committerMarcin Haba <marcin.haba@bacula.pl>
Sat, 14 Dec 2019 15:02:17 +0000 (16:02 +0100)
17 files changed:
gui/baculum/protected/API/Class/APIDbModule.php
gui/baculum/protected/API/Class/BAPIException.php [new file with mode: 0644]
gui/baculum/protected/API/Class/BaculaSetting.php
gui/baculum/protected/API/Class/BaculumAPIServer.php
gui/baculum/protected/API/Class/Bconsole.php
gui/baculum/protected/API/Class/Database.php
gui/baculum/protected/API/Pages/Panel/APIInstallWizard.php
gui/baculum/protected/Common/Class/AuthBase.php [new file with mode: 0644]
gui/baculum/protected/Common/Class/AuthBasic.php [new file with mode: 0644]
gui/baculum/protected/Common/Class/AuthOAuth2.php [new file with mode: 0644]
gui/baculum/protected/Common/Class/BException.php [moved from gui/baculum/protected/API/Class/BException.php with 77% similarity]
gui/baculum/protected/Common/Class/BasicUserConfig.php
gui/baculum/protected/Common/Class/Errors.php
gui/baculum/protected/Common/Class/Interfaces.php
gui/baculum/protected/Common/Class/Miscellaneous.php
gui/baculum/protected/Common/Class/OAuth2.php
gui/baculum/protected/application.xml

index 93fcf088a824cd430bcc64e94938cf5f33041649..63c25dc10716ece4d34ccb6b91a57fa0fc497a46 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2016 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
@@ -29,7 +29,7 @@
 
 Prado::using('Application.Common.Class.Errors');
 Prado::using('Application.API.Class.APIConfig');
-Prado::using('Application.API.Class.BException');
+Prado::using('Application.API.Class.BAPIException');
 Prado::using('Application.API.Class.Database');
 Prado::using('System.Data.ActiveRecord.TActiveRecord');
 
diff --git a/gui/baculum/protected/API/Class/BAPIException.php b/gui/baculum/protected/API/Class/BAPIException.php
new file mode 100644 (file)
index 0000000..9ff96b3
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2019 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('Application.Common.Class.BException');
+
+class BAPIException extends BException {
+}
+
+class BCatalogException extends BAPIException {
+}
+
+class BConsoleException extends BAPIException {
+}
+
+class BConfigException extends BAPIException {
+}
+?>
index c8ca53bfee33df632cd063cc671e8cf36d54d34e..887b6c129bceb22ca35e7d0701727cedf0b87139 100644 (file)
@@ -22,7 +22,7 @@
 
 Prado::using('Application.Common.Class.Params');
 Prado::using('Application.Common.Class.Errors');
-Prado::using('Application.API.Class.BException');
+Prado::using('Application.API.Class.BAPIException');
 Prado::using('Application.API.Class.APIModule');
 Prado::using('Application.API.Class.APIConfig');
 
index 960f94bffc22c67eddf9584739d5b97945f34c12..4865791aa64eed497ecedcde6d6cb02ac2a23eb8 100644 (file)
@@ -25,7 +25,7 @@ Prado::using('System.Exceptions.TException');
 Prado::using('Application.Common.Class.Errors');
 Prado::using('Application.Common.Class.OAuth2');
 Prado::using('Application.Common.Class.Logging');
-Prado::using('Application.API.Class.BException');
+Prado::using('Application.API.Class.BAPIException');
 Prado::using('Application.API.Class.APIDbModule');
 Prado::using('Application.API.Class.Bconsole');
 Prado::using('Application.API.Class.OAuth2.TokenRecord');
@@ -65,6 +65,9 @@ abstract class BaculumAPIServer extends TPage {
         */
        protected $user;
 
+       /**
+        * Endpoints available for every authenticated client.
+        */
        private $public_endpoints = array('auth', 'token', 'welcome', 'catalog', 'dbsize', 'directors');
 
        /**
@@ -83,6 +86,67 @@ abstract class BaculumAPIServer extends TPage {
        // delete element
        const DELETE_METHOD = 'DELETE';
 
+       /**
+        * API Server authentication.
+        *
+        * @return true if user is successfully authenticated, otherwise false
+        */
+       private function authenticate() {
+               $is_auth = false;
+               $config = $this->getModule('api_config')->getConfig('api');
+               if ($config['auth_type'] === 'basic' && $this->getModule('auth_basic')->isAuthRequest()) {
+                       $is_auth = true;
+               } elseif ($config['auth_type'] === 'oauth2' && $this->getModule('auth_oauth2')->isAuthRequest()) {
+                       $is_auth = $this->authorize();
+               }
+               if (!$is_auth && is_null($this->error)) {
+                       $this->output = AuthenticationError::MSG_ERROR_AUTHENTICATION_TO_API_PROBLEM;
+                       $this->error = AuthenticationError::ERROR_AUTHENTICATION_TO_API_PROBLEM;
+               }
+               return $is_auth;
+       }
+
+       /**
+        * API Server authorization.
+        * Check if authenticated user is allowed to get requested API endpoint.
+        *
+        * @return true if user is successfully authorized, otherwise false
+        */
+       private function authorize() {
+               $is_auth = false;
+               $is_token = false;
+
+               // deleting expired tokens
+               $this->getModule('oauth2_token')->deleteExpiredTokens();
+
+               $auth_oauth2 = $this->getModule('auth_oauth2');
+
+               // Check if token exists
+               $scopes = '';
+               $token = $auth_oauth2->getToken();
+               $auth = TokenRecord::findByPk($token);
+               if (is_array($auth)) {
+                       // Token found
+                       $scopes = $auth['scope'];
+                       $is_token = true;
+               }
+
+               // Check if requested scope is valid according allowed scopes assigned to token
+               if ($is_token) {
+                       $path = $this->getRequest()->getUrl()->getPath();
+                       if ($auth_oauth2->isScopeValid($path, $scopes, $this->public_endpoints)) {
+                               // Authorization valid
+                               $is_auth = true;
+                               $this->initAuthParams($auth);
+                       } else {
+                               // Scopes error. Access attempt to not allowed resource
+                               $this->output = AuthorizationError::MSG_ERROR_ACCESS_ATTEMPT_TO_NOT_ALLOWED_RESOURCE .' Endpoint: ' .  $path;
+                               $this->error = AuthorizationError::ERROR_ACCESS_ATTEMPT_TO_NOT_ALLOWED_RESOURCE;
+                       }
+               }
+               return $is_auth;
+       }
+
        /**
         * Get request, login user and do request action.
         *
@@ -92,55 +156,20 @@ abstract class BaculumAPIServer extends TPage {
         */
        public function onInit($params) {
                parent::onInit($params);
-               /*
-                * Workaround to bug in PHP 5.6 by FastCGI that caused general protection error.
-                * @TODO: Check on newer PHP if it is already fixed.
-                */
-               // @TODO: Move it to API module.
-               //$db_params = $this->getModule('api_config')->getConfig('db');
-               //APIDbModule::getAPIDbConnection($db_params);
+               // Initialize auth modules
+               $this->getModule('auth_basic')->initialize($this->Request);
+               $this->getModule('auth_oauth2')->initialize($this->Request);
 
                // set Director to bconsole execution
                $this->director = $this->Request->contains('director') ? $this->Request['director'] : null;
 
-               $is_auth = false;
                $config = $this->getModule('api_config')->getConfig('api');
-               Logging::$debug_enabled = (array_key_exists('debug', $config) && $config['debug'] == 1);
-               $headers = $this->getRequest()->getHeaders(CASE_LOWER);
-               if (array_key_exists('auth_type', $config) && array_key_exists('authorization', $headers) && preg_match('/^\w+ [\w=]+$/', $headers['authorization']) === 1) {
-                       list($type, $token) = explode(' ', $headers['authorization'], 2);
-                       if ($config['auth_type'] === 'oauth2' && $type === 'Bearer') {
-                               // deleting expired tokens
-                               $this->getModule('oauth2_token')->deleteExpiredTokens();
-
-                               $auth = TokenRecord::findByPk($token);
-                               if (is_array($auth)) {
-                                       if ($this->isScopeValid($auth['scope'])) {
-                                               // AUTH OK
-                                               $is_auth = true;
-                                               $this->init_auth($auth);
-                                       } else {
-                                               // Scopes error. Access to not allowed resource
-                                               header(OAuth2::HEADER_UNAUTHORIZED);
-                                               $url = $this->getRequest()->getUrl()->getPath();
-                                               $this->output = AuthorizationError::MSG_ERROR_ACCESS_ATTEMPT_TO_NOT_ALLOWED_RESOURCE .' Endpoint: ' .  $url;
-                                               $this->error = AuthorizationError::ERROR_ACCESS_ATTEMPT_TO_NOT_ALLOWED_RESOURCE;
-                                               return;
-
-                                       }
-                               }
-                       } elseif ($config['auth_type'] === 'basic' && $type === 'Basic') {
-                               // AUTH OK
-                               $is_auth = true;
-                       }
 
-               }
+               Logging::$debug_enabled = (key_exists('debug', $config) && $config['debug'] == 1);
 
-               if ($is_auth === false) {
+               if ($this->authenticate() === false) {
                        // Authorization error.
                        header(OAuth2::HEADER_UNAUTHORIZED);
-                       $this->output = AuthorizationError::MSG_ERROR_AUTHENTICATION_TO_API_PROBLEM;
-                       $this->error = AuthorizationError::ERROR_AUTHENTICATION_TO_API_PROBLEM;
                        return;
                }
                try {
@@ -170,7 +199,7 @@ abstract class BaculumAPIServer extends TPage {
                                __FILE__,
                                __LINE__
                        );
-                       if ($e instanceof BException) {
+                       if ($e instanceof BAPIException) {
                                $this->output = $e->getErrorMessage();
                                $this->error = $e->getErrorCode();
                        } else {
@@ -186,7 +215,7 @@ abstract class BaculumAPIServer extends TPage {
         * @param array $auth token params stored in TokenRecord session
         * @return none
         */
-       private function init_auth(array $auth) {
+       private function initAuthParams(array $auth) {
                // if client has own bconsole config, assign it here
                if (array_key_exists('bconsole_cfg_path', $auth) && !empty($auth['bconsole_cfg_path'])) {
                        Bconsole::setCfgPath($auth['bconsole_cfg_path'], true);
@@ -314,38 +343,6 @@ abstract class BaculumAPIServer extends TPage {
                $this->remove($id);
        }
 
-       /**
-        * Check if request is allowed to access basing on OAuth2 scope.
-        *
-        * @access private
-        * @param string scopes assigned with token
-        * @return bool true if scope in url and from token are valid, otherwise false
-        */
-       private function isScopeValid($scope) {
-               $is_valid = false;
-               $scopes = explode(' ', $scope);
-               $url = $this->getRequest()->getUrl()->getPath();
-               $params = explode('/', $url);
-               if (count($params) >= 3 && $params[1] === 'api') {
-                       $endpoint = $params[2];
-                       if (preg_match('/^v\d+$/', $params[2]) === 1 && count($params) >= 4) {
-                               // for versioned API (v1, v2 ...etc.)
-                               $endpoint = $params[3];
-                       }
-                       if (in_array($endpoint, $this->public_endpoints)) {
-                               $is_valid = true;
-                       } else {
-                               for ($i = 0; $i < count($scopes); $i++) {
-                                       if ($endpoint === $scopes[$i]) {
-                                               $is_valid = true;
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               return $is_valid;
-       }
-
        /**
         * Shortcut method for getting application modules instances by
         * module name.
index b6bd0596e62f6e011d5943241182b4919cfebcab..690b02daf3aec995ed71d95088c60fcc6a74fc3b 100644 (file)
@@ -21,7 +21,7 @@
  */
 
 Prado::using('Application.Common.Class.Errors');
-Prado::using('Application.API.Class.BException');
+Prado::using('Application.API.Class.BAPIException');
 Prado::using('Application.API.Class.APIModule');
 
 class Bconsole extends APIModule {
@@ -328,7 +328,7 @@ class Bconsole extends APIModule {
                try {
                        $director = array_shift($this->getDirectors()->output);
                        $result = $this->bconsoleCommand($director, $command);
-               } catch (BException $e) {
+               } catch (BAPIException $e) {
                        $result = (object)array(
                                'output' => $e->getErrorMessage(),
                                'exitcode' => $e->getErrorCode()
index 8c7e1dbbb51d1597e11566e46530ad68194800c2..4e608db3cbb50ada7f757cf8768edc19452175f5 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2016 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
@@ -21,7 +21,7 @@
  */
  
 Prado::using('Application.Common.Class.Errors');
-Prado::using('Application.API.Class.BException');
+Prado::using('Application.API.Class.BAPIException');
 Prado::using('Application.API.Class.APIModule');
 Prado::using('Application.API.Class.APIDbModule');
 
index 0ce83b3e4a1ee741bbcdbb2dd39ff06e023f0902..efad0169c2b9e6ab59117e92fa1b009093418bb3 100644 (file)
@@ -30,7 +30,7 @@ Prado::using('System.Web.UI.ActiveControls.TActiveRadioButton');
 Prado::using('System.Web.UI.ActiveControls.TActiveCustomValidator');
 Prado::using('Application.Common.Class.OAuth2');
 Prado::using('Application.API.Class.APIConfig');
-Prado::using('Application.API.Class.BException');
+Prado::using('Application.API.Class.BAPIException');
 Prado::using('Application.API.Class.BaculumAPIPage');
 Prado::using('Application.API.Class.Database');
 Prado::using('Application.API.Class.BasicAPIUserConfig');
@@ -409,12 +409,12 @@ class APIInstallWizard extends BaculumAPIPage {
                if ($validation === true) {
                        try {
                                $is_validate = $this->getModule('db')->testDbConnection($db_params);
-                       } catch (BException $e) {
+                       } catch (BAPIException $e) {
                                $emsg = $e;
                        }
                }
                $this->DbTestResultOk->Display = ($is_validate === true) ? 'Dynamic' : 'None';
-               if ($emsg instanceof BException) {
+               if ($emsg instanceof BAPIException) {
                        $this->DbTestResultErr->Text = $emsg;
                }
                $this->DbTestResultErr->Display = ($is_validate === false) ? 'Dynamic' : 'None';
diff --git a/gui/baculum/protected/Common/Class/AuthBase.php b/gui/baculum/protected/Common/Class/AuthBase.php
new file mode 100644 (file)
index 0000000..3ad4666
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2019 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('Application.Common.Class.CommonModule');
+
+/**
+ * Abstraction that defines common methods to inheirt by auth modules.
+ * Descendant classes can implement auth module interface.
+ * @see Application.Common.Class.Interfaces.AuthModule
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ */
+abstract class AuthBase extends CommonModule {
+
+       /**
+        * Stores HTTP request object.
+        */
+       private static $req = null;
+
+       /**
+        * Public constructor.
+        *
+        * @param THttpRequest $request HTTP request object.
+        * @return none
+        */
+       public function initialize(THttpRequest $request) {
+               self::$req = $request;
+       }
+
+       /**
+        * Get all HTTP request headers.
+        *
+        * @return array request headers
+        */
+       private function getRequestHeaders() {
+               return self::$req->getHeaders(CASE_LOWER);
+       }
+
+       /**
+        * Check if HTTP request contains authorization header
+        * ex: 'Authorization: Basic dGVzdGVyOnRlc3Q='
+        *
+        * @return boolean true if request contains valid authorization header
+        */
+       public function isAuthRequest() {
+               return ($this->getRequestHeader() !== null);
+       }
+
+       /**
+        * Get authorization request header.
+        *
+        * @return string|null authorization header or null if header is invalid
+        */
+       public function getRequestHeader() {
+               $auth_header = null;
+               $headers = $this->getRequestHeaders();
+               if (is_array($headers) && key_exists('authorization', $headers)) {
+                       if ($this->validateRequestHeader($headers['authorization'])) {
+                               $auth_header = $headers['authorization'];
+                       }
+               }
+               return $auth_header;
+       }
+
+       /**
+        * Validate request header.
+        *
+        * @return boolean true - success, false - validation error
+        */
+       abstract protected function validateRequestHeader($header);
+}
+?>
diff --git a/gui/baculum/protected/Common/Class/AuthBasic.php b/gui/baculum/protected/Common/Class/AuthBasic.php
new file mode 100644 (file)
index 0000000..e948754
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2019 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('Application.Common.Class.AuthBase');
+Prado::using('Application.Common.Class.Interfaces');
+
+/**
+ * Basic authentication auth module.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ */
+class AuthBasic extends AuthBase implements AuthModule {
+
+       /**
+        * Request header value pattern.
+        */
+       const REQUEST_HEADER_CREDENTIALS_PATTERN = '/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$/';
+
+       /**
+        * Get auth type.
+        *
+        * @return string auth type.
+        */
+       public function getAuthType() {
+               return 'Basic';
+       }
+
+       /**
+        * Validate auth request header.
+        *
+        * @param string $header auth request header value (ex: 'Basic dGVzdGVyOnRlc3Q=')
+        * @return boolean true - valid, false - validation error
+        */
+       public function validateRequestHeader($header) {
+               $valid = false;
+               $value = $this->getRequestHeaderValue($header);
+               if (is_array($value)) {
+                       $valid = ($value['type'] === $this->getAuthType() && preg_match(self::REQUEST_HEADER_CREDENTIALS_PATTERN, $value['credentials']) === 1);
+               }
+               return $valid;
+       }
+
+       /**
+        * Get parsed request header value.
+        *
+        * @param string $header auth request header value (ex: 'Basic dGVzdGVyOnRlc3Q=')
+        * @return array|null list with type and credentials or null if header is invalid
+        */
+       public function getRequestHeaderValue($header) {
+               $ret = null;
+               if (is_string($header)) {
+                       $values = explode(' ', $header, 2);
+                       if (count($values) == 2) {
+                               list($type, $credentials) = $values;
+                               $ret = ['type' => $type, 'credentials' => $credentials];
+                       }
+               }
+               return $ret;
+       }
+}
+?>
diff --git a/gui/baculum/protected/Common/Class/AuthOAuth2.php b/gui/baculum/protected/Common/Class/AuthOAuth2.php
new file mode 100644 (file)
index 0000000..31624f9
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2019 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('Application.Common.Class.AuthBase');
+Prado::using('Application.Common.Class.Interfaces');
+Prado::using('Application.Common.Class.OAuth2');
+
+/**
+ * OAuth2 authorization auth module.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ */
+class AuthOAuth2 extends AuthBase implements AuthModule {
+
+       /**
+        * Get auth type.
+        *
+        * @return string auth type.
+        */
+       public function getAuthType() {
+               return 'Bearer';
+       }
+
+       /**
+        * Validate auth request header.
+        *
+        * @param string $header auth request header value (ex: 'Bearer 39607568825eba6b72088b1ab054ed9d0f857eb7')
+        * @return boolean true - valid, false - validation error
+        */
+       public function validateRequestHeader($header) {
+               $valid = false;
+               $value = $this->getRequestHeaderValue($header);
+               if (is_array($value)) {
+                       $valid = ($value['type'] === $this->getAuthType() && OAuth2::validateAccessToken($value['token']) === true);
+               }
+               return $valid;
+       }
+
+       /**
+        * Get parsed request header value.
+        *
+        * @param string $header auth request header value (ex: 'Basic 39607568825eba6b72088b1ab054ed9d0f857eb7')
+        * @return array|null list with type and token or null if header is invalid
+        */
+       public function getRequestHeaderValue($header) {
+               $ret = null;
+               if (is_string($header)) {
+                       $values = explode(' ', $header, 2);
+                       if (count($values) == 2) {
+                               list($type, $token) = $values;
+                               $ret = ['type' => $type, 'token' => $token];
+                       }
+               }
+               return $ret;
+       }
+
+       /**
+        * Get token from authorization header.
+        *
+        * @return string token value or empty string if header is invalid
+        */
+       public function getToken() {
+               $token = '';
+               $header = $this->getRequestHeader();
+               $value = $this->getRequestHeaderValue($header);
+               if (is_array($value)) {
+                       $token = $value['token'];
+               }
+               return $token;
+       }
+
+       /**
+        * Check if request is allowed to access basing on OAuth2 scopes.
+        * Note, public endpoints are available for every client that uses
+        * valid token.
+        *
+        * @param string $path requested URL path
+        * @param string $tscopes scopes assigned to token
+        * @param array $public_endpoints endpoints that are public for all valid clients
+        * @return boolean true if scope in path and scope assigned to token are valid, otherwise false
+        */
+       public function isScopeValid($path, $tscopes, $public_endpoints) {
+               $valid = false;
+               $scopes = explode(' ', $tscopes);
+               $params = explode('/', $path);
+               if (count($params) >= 3 && $params[1] === 'api') {
+                       $endpoint = $params[2];
+                       if (preg_match('/^v\d+$/', $params[2]) === 1 && count($params) >= 4) {
+                               // for versioned API (v1, v2 ...etc.)
+                               $endpoint = $params[3];
+                       }
+                       if (in_array($endpoint, $public_endpoints)) {
+                               $valid = true;
+                       } else {
+                               for ($i = 0; $i < count($scopes); $i++) {
+                                       if ($endpoint === $scopes[$i]) {
+                                               $valid = true;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               return $valid;
+       }
+}
+?>
similarity index 77%
rename from gui/baculum/protected/API/Class/BException.php
rename to gui/baculum/protected/Common/Class/BException.php
index d838c292019d3b2fd5aee35bf32895b116855e50..f2486d2e247e393817dc9eccbc76ea7f60f343d8 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2017 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
@@ -20,9 +20,9 @@
  * Bacula(R) is a registered trademark of Kern Sibbald.
  */
 
-Prado::using('System.Exceptions.TException');
+use Prado\Exceptions;
 
-class BException extends TException {
+class BException extends \Prado\Exceptions\TException {
 
        private $error_code;
        private $error_message;
@@ -50,16 +50,14 @@ class BException extends TException {
        }
 
        public function __toString() {
-               $msg = sprintf('Error: %d, Message: %s', $this->getErrorCode(), $this->getErrorMessage());
-               return $msg;
+               return sprintf(
+                       'Error: %d, Message: %s',
+                       $this->getErrorCode(),
+                       $this->getErrorMessage()
+               );
        }
 }
 
-class BCatalogException extends BException {
-}
-
-class BConsoleException extends BException {
-}
-
-class BConfigException extends BException {
+class AuthException extends BException {
 }
+?>
index f1d6f5abed121c7c6fbc4296b3c4526157bdd59a..b65a6bdee787ed7750ad6cefe59d2987ea09bb49 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2017 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
@@ -56,7 +56,7 @@ abstract class BasicUserConfig extends CommonModule {
                }
 
                $all_users = $this->getAllUsers();
-               $password = $this->getModule('misc')->getCryptedPassword($password);
+               $password = $this->getModule('misc')->getHashedPassword($password);
 
                $userExists = array_key_exists($user, $all_users);
 
index de9a5a9d5d802ca295c6dfc88f9bf0cf5b786abe..b8b2e97c13e10761b4f212837a9205ef050548bb 100644 (file)
@@ -51,12 +51,17 @@ class BconsoleError extends GenericError {
        const MSG_ERROR_BCONSOLE_DISABLED = 'Bconsole support is disabled.';
 }
 
-class AuthorizationError extends GenericError {
+class AuthenticationError extends GenericError {
 
        const ERROR_AUTHENTICATION_TO_API_PROBLEM = 6;
-       const ERROR_ACCESS_ATTEMPT_TO_NOT_ALLOWED_RESOURCE = 7;
 
        const MSG_ERROR_AUTHENTICATION_TO_API_PROBLEM = 'Problem with authentication to Baculum API.';
+}
+
+class AuthorizationError extends GenericError {
+
+       const ERROR_ACCESS_ATTEMPT_TO_NOT_ALLOWED_RESOURCE = 7;
+
        const MSG_ERROR_ACCESS_ATTEMPT_TO_NOT_ALLOWED_RESOURCE = 'Access attempt to not allowed resource. Permission denied.';
 }
 
index 56c722783988efb07f6da76188cee3950d7dc022..a2ab45c2a928b97f0066acfb3b21a01e961c517b 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2016 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
  */
 
 /**
- * Interface that defines methods to work on config data.
+ * Common interfaces.
  *
  * @author Marcin Haba <marcin.haba@bacula.pl>
  */
+
+/**
+ * Defines methods to work on config data.
+ */
 interface ConfigFormat {
 
        public function write($source, $config);
@@ -34,6 +38,9 @@ interface ConfigFormat {
        public function prepareConfig($config);
 }
 
+/**
+ * Defines single session item.
+ */
 interface SessionItem {
 
        public static function getRecordId();
@@ -42,4 +49,18 @@ interface SessionItem {
 
        public static function getSessionFile();
 }
+
+/**
+ * Defines auth module methods.
+ */
+interface AuthModule {
+
+       public function getAuthType();
+
+       public function isAuthRequest();
+
+       public function validateRequestHeader($header);
+
+       public function getRequestHeaderValue($header);
+}
 ?>
index 678a82449ccc379537c39b1005e4a9da760b0dc3..b42ceec869b9b1ae60d4cbb9cd5c37312e963b46 100644 (file)
@@ -432,15 +432,14 @@ class Miscellaneous extends TModule {
        }
 
        /**
-        * Get encrypted password to use in HTTP Basic auth.
+        * Get hashed password to use in web server auth.
         *
         * @access public
         * @param string $password plain text password
-        * @return string encrypted password
+        * @return string hashed password
         */
-       public function getCryptedPassword($password) {
-               $enc_pwd = crypt($password, base64_encode($password));
-               return $enc_pwd;
+       public function getHashedPassword($password) {
+               return crypt($password, base64_encode($password));
        }
 }
 
index 753abc75729de24e186856bd2bde99ac8e8cd2a7..e1fd0961374dfb71f4206828f4c5fd0bd721701d 100644 (file)
@@ -3,7 +3,7 @@
  * Bacula(R) - The Network Backup Solution
  * Baculum   - Bacula web interface
  *
- * Copyright (C) 2013-2017 Kern Sibbald
+ * Copyright (C) 2013-2019 Kern Sibbald
  *
  * The main author of Baculum is Marcin Haba.
  * The original author of Bacula is Kern Sibbald, with contributions
@@ -197,7 +197,7 @@ abstract class OAuth2 extends CommonModule {
         * @param string $token access token value
         * @return true if access token is valid, otherwise false
         */
-       final public function validateAccessToken($token) {
+       final public static function validateAccessToken($token) {
                return (preg_match('/' . self::ACCESS_TOKEN_PATTERN . '/', $token) === 1);
        }
 
@@ -208,7 +208,7 @@ abstract class OAuth2 extends CommonModule {
         * @param string $token refresh token value
         * @return true if refresh token is valid, otherwise false
         */
-       final public function validateRefreshToken($token) {
+       final public static function validateRefreshToken($token) {
                return (preg_match('/' . self::REFRESH_TOKEN_PATTERN . '/', $token) === 1);
        }
 
index 22c61d9e6fdfd8a970a6924fe2ea5a907dd678bd..78af43d7f9cadcca603560dc6dad57dc8e67958d 100644 (file)
@@ -12,6 +12,9 @@
                <module id="misc" class="Application.Common.Class.Miscellaneous" />
                <module id="config_ini" class="Application.Common.Class.ConfigIni" />
                <module id="config_bacula" class="Application.Common.Class.ConfigBacula" />
+               <!-- authentication and authorization modules -->
+               <module id="auth_basic" class="Application.Common.Class.AuthBasic" />
+               <module id="auth_oauth2" class="Application.Common.Class.AuthOAuth2" />
                <!-- communication modules -->
                <module id="request" class="THttpRequest" UrlManager="url_manager" UrlFormat="HiddenPath" />
                <module id="url_manager" class="Application.Common.Class.BaculumUrlMapping" EnableCustomUrl="true" />