diff --git a/lib/API/API.php b/lib/API/API.php index 3c13d8e2f0..ef65f9dfd5 100644 --- a/lib/API/API.php +++ b/lib/API/API.php @@ -1,11 +1,14 @@ access_control = $access_control; foreach($this->_available_api_versions as $available_api_version) { $this->addEndpointNamespace( sprintf('%s\%s', __NAMESPACE__, $available_api_version), @@ -130,17 +132,11 @@ class API { // check the accessibility of the requested endpoint's action // by default, an endpoint's action is considered "private" - $permissions = $endpoint->permissions; - if(array_key_exists($this->_request_method, $permissions) === false || - $permissions[$this->_request_method] !== Access::ALL - ) { - if($this->checkPermissions() === false) { - $error_message = __('You do not have the required permissions.', 'mailpoet'); - $error_response = $this->createErrorResponse(Error::FORBIDDEN, $error_message, Response::STATUS_FORBIDDEN); - return $error_response; - } + if(!$this->validatePermissions($this->_request_method, $endpoint->permissions)) { + $error_message = __('You do not have the required permissions.', 'mailpoet'); + $error_response = $this->createErrorResponse(Error::FORBIDDEN, $error_message, Response::STATUS_FORBIDDEN); + return $error_response; } - $response = $endpoint->{$this->_request_method}($this->_request_data); return $response; } catch(\Exception $e) { @@ -150,8 +146,11 @@ class API { } } - function checkPermissions() { - return current_user_can(Env::$required_permission); + function validatePermissions($request_method, $permissions) { + // validate method permission if defined, otherwise validate global permission + return(!empty($permissions['methods'][$request_method])) ? + $this->access_control->validatePermission($permissions['methods'][$request_method]) : + $this->access_control->validatePermission($permissions['global']); } function checkToken() { diff --git a/lib/API/JSON/Access.php b/lib/API/JSON/Access.php deleted file mode 100644 index 28beab513e..0000000000 --- a/lib/API/JSON/Access.php +++ /dev/null @@ -1,12 +0,0 @@ - array(AccessControl::PERMISSION_MANAGE_SETTINGS), + 'methods' => array() + ); function successResponse( $data = array(), $meta = array(), $status = Response::STATUS_OK @@ -18,7 +23,7 @@ abstract class Endpoint { ) { if(empty($errors)) { $errors = array( - Error::UNKNOWN => __('An unknown error occurred.', 'mailpoet') + Error::UNKNOWN => __('An unknown error occurred.', 'mailpoet') ); } return new ErrorResponse($errors, $meta, $status); diff --git a/lib/API/JSON/v1/AutomatedLatestContent.php b/lib/API/JSON/v1/AutomatedLatestContent.php index 74d0ac4a29..e28be1642e 100644 --- a/lib/API/JSON/v1/AutomatedLatestContent.php +++ b/lib/API/JSON/v1/AutomatedLatestContent.php @@ -1,12 +1,18 @@ AccessControl::PERMISSION_MANAGE_EMAILS + ); function __construct() { $this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent(); diff --git a/lib/API/JSON/v1/CustomFields.php b/lib/API/JSON/v1/CustomFields.php index 529e7d476a..823f0552a5 100644 --- a/lib/API/JSON/v1/CustomFields.php +++ b/lib/API/JSON/v1/CustomFields.php @@ -1,12 +1,19 @@ AccessControl::PERMISSION_MANAGE_FORMS + ); + function getAll() { $collection = CustomField::orderByAsc('created_at')->findMany(); $custom_fields = array_map(function($custom_field) { diff --git a/lib/API/JSON/v1/Forms.php b/lib/API/JSON/v1/Forms.php index 179d805836..4789b728be 100644 --- a/lib/API/JSON/v1/Forms.php +++ b/lib/API/JSON/v1/Forms.php @@ -1,17 +1,23 @@ AccessControl::PERMISSION_MANAGE_FORMS + ); + function get($data = array()) { $id = (isset($data['id']) ? (int)$data['id'] : false); $form = Form::findOne($id); diff --git a/lib/API/JSON/v1/ImportExport.php b/lib/API/JSON/v1/ImportExport.php index 8e4f3b89c0..62ff5dead0 100644 --- a/lib/API/JSON/v1/ImportExport.php +++ b/lib/API/JSON/v1/ImportExport.php @@ -1,13 +1,19 @@ AccessControl::PERMISSION_MANAGE_SUBSCRIBERS + ); + function getMailChimpLists($data) { try { $mailChimp = new MailChimp($data['api_key']); diff --git a/lib/API/JSON/v1/MP2Migrator.php b/lib/API/JSON/v1/MP2Migrator.php index 67d851ae21..aa3bd41d75 100644 --- a/lib/API/JSON/v1/MP2Migrator.php +++ b/lib/API/JSON/v1/MP2Migrator.php @@ -1,18 +1,24 @@ AccessControl::PERMISSION_MANAGE_SETTINGS + ); + public function __construct() { $this->MP2Migrator = new \MailPoet\Config\MP2Migrator(); } - + /** * Import end point - * + * * @param object $data * @return object */ @@ -26,10 +32,10 @@ class MP2Migrator extends APIEndpoint { )); } } - + /** * Stop import end point - * + * * @param object $data * @return object */ @@ -43,10 +49,10 @@ class MP2Migrator extends APIEndpoint { )); } } - + /** * Skip import end point - * + * * @param object $data * @return object */ @@ -60,5 +66,5 @@ class MP2Migrator extends APIEndpoint { )); } } - + } diff --git a/lib/API/JSON/v1/Mailer.php b/lib/API/JSON/v1/Mailer.php index 4400453299..872ac860ff 100644 --- a/lib/API/JSON/v1/Mailer.php +++ b/lib/API/JSON/v1/Mailer.php @@ -1,12 +1,19 @@ AccessControl::PERMISSION_MANAGE_EMAILS + ); + function send($data = array()) { try { $mailer = new \MailPoet\Mailer\Mailer( diff --git a/lib/API/JSON/v1/NewsletterTemplates.php b/lib/API/JSON/v1/NewsletterTemplates.php index 69c4f5a4d5..fae112eaf1 100644 --- a/lib/API/JSON/v1/NewsletterTemplates.php +++ b/lib/API/JSON/v1/NewsletterTemplates.php @@ -1,13 +1,19 @@ AccessControl::PERMISSION_MANAGE_EMAILS + ); + function get($data = array()) { $id = (isset($data['id']) ? (int)$data['id'] : false); $template = NewsletterTemplate::findOne($id); diff --git a/lib/API/JSON/v1/Newsletters.php b/lib/API/JSON/v1/Newsletters.php index 34d52060ab..b9bf43c33f 100644 --- a/lib/API/JSON/v1/Newsletters.php +++ b/lib/API/JSON/v1/Newsletters.php @@ -1,17 +1,19 @@ AccessControl::PERMISSION_MANAGE_EMAILS + ); + function get($data = array()) { $id = (isset($data['id']) ? (int)$data['id'] : false); $newsletter = Newsletter::findOne($id); diff --git a/lib/API/JSON/v1/Segments.php b/lib/API/JSON/v1/Segments.php index 2ef6a07b89..6c8000564a 100644 --- a/lib/API/JSON/v1/Segments.php +++ b/lib/API/JSON/v1/Segments.php @@ -1,15 +1,21 @@ AccessControl::PERMISSION_MANAGE_SEGMENTS + ); + function get($data = array()) { $id = (isset($data['id']) ? (int)$data['id'] : false); $segment = Segment::findOne($id); diff --git a/lib/API/JSON/v1/SendingQueue.php b/lib/API/JSON/v1/SendingQueue.php index e155cbd0cc..5484e004a5 100644 --- a/lib/API/JSON/v1/SendingQueue.php +++ b/lib/API/JSON/v1/SendingQueue.php @@ -1,18 +1,24 @@ AccessControl::PERMISSION_MANAGE_EMAILS + ); + function add($data = array()) { $newsletter_id = (isset($data['newsletter_id']) ? (int)$data['newsletter_id'] diff --git a/lib/API/JSON/v1/Services.php b/lib/API/JSON/v1/Services.php index 7885e501ca..9fc447338f 100644 --- a/lib/API/JSON/v1/Services.php +++ b/lib/API/JSON/v1/Services.php @@ -1,11 +1,12 @@ AccessControl::PERMISSION_MANAGE_SETTINGS + ); function __construct() { $this->bridge = new Bridge(); diff --git a/lib/API/JSON/v1/Settings.php b/lib/API/JSON/v1/Settings.php index 1a2093c4ed..1db60103da 100644 --- a/lib/API/JSON/v1/Settings.php +++ b/lib/API/JSON/v1/Settings.php @@ -1,24 +1,31 @@ AccessControl::PERMISSION_MANAGE_SETTINGS + ); + function get() { return $this->successResponse(Setting::getAll()); } function set($settings = array()) { if(empty($settings)) { - return $this->badRequest(array( - APIError::BAD_REQUEST => - __('You have not specified any settings to be saved.', 'mailpoet') - )); + return $this->badRequest( + array( + APIError::BAD_REQUEST => + __('You have not specified any settings to be saved.', 'mailpoet') + )); } else { foreach($settings as $name => $value) { Setting::setValue($name, $value); diff --git a/lib/API/JSON/v1/Setup.php b/lib/API/JSON/v1/Setup.php index d0f6989226..1bf0647748 100644 --- a/lib/API/JSON/v1/Setup.php +++ b/lib/API/JSON/v1/Setup.php @@ -1,13 +1,19 @@ AccessControl::PERMISSION_MANAGE_SETTINGS + ); + function reset() { try { $activator = new Activator(); diff --git a/lib/API/JSON/v1/Subscribers.php b/lib/API/JSON/v1/Subscribers.php index 38ddefb9af..323d8f9d81 100644 --- a/lib/API/JSON/v1/Subscribers.php +++ b/lib/API/JSON/v1/Subscribers.php @@ -1,21 +1,22 @@ APIAccess::ALL + 'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS, + 'methods' => array('subscribe' => AccessControl::NO_ACCESS_RESTRICTION) ); function get($data = array()) { diff --git a/lib/Config/AccessControl.php b/lib/Config/AccessControl.php new file mode 100644 index 0000000000..97a142140d --- /dev/null +++ b/lib/Config/AccessControl.php @@ -0,0 +1,104 @@ +permissions = $this->getDefaultPermissions(); + $this->user_roles = $this->getUserRoles(); + $this->user_capabilities = $this->getUserCapabilities(); + } + + private function getDefaultPermissions() { + return array( + self::PERMISSION_ACCESS_PLUGIN_ADMIN => WPHooks::applyFilters( + 'mailpoet_permission_access_plugin_admin', + array( + 'administrator', + 'editor' + ) + ), + self::PERMISSION_MANAGE_SETTINGS => WPHooks::applyFilters( + 'mailpoet_permission_manage_settings', + array( + 'administrator' + ) + ), + self::PERMISSION_MANAGE_EMAILS => WPHooks::applyFilters( + 'mailpoet_permission_manage_emails', + array( + 'administrator', + 'editor' + ) + ), + self::PERMISSION_MANAGE_SUBSCRIBERS => WPHooks::applyFilters( + 'mailpoet_permission_manage_subscribers', + array( + 'administrator' + ) + ), + self::PERMISSION_MANAGE_FORMS => WPHooks::applyFilters( + 'mailpoet_permission_manage_forms', + array( + 'administrator' + ) + ), + self::PERMISSION_MANAGE_SEGMENTS => WPHooks::applyFilters( + 'mailpoet_permission_manage_segments', + array( + 'administrator' + ) + ), + self::PERMISSION_UPDATE_PLUGIN => WPHooks::applyFilters( + 'mailpoet_permission_update_plugin', + array( + 'administrator' + ) + ), + ); + } + + function getUserRoles() { + $user = wp_get_current_user(); + return $user->roles; + } + + function getUserCapabilities() { + $user = wp_get_current_user(); + return array_keys($user->allcaps); + } + + function getUserFirstCapability() { + return (!empty($this->user_capabilities)) ? + $this->user_capabilities[0] : + null; + } + + function validatePermission($permission) { + if($permission === self::NO_ACCESS_RESTRICTION) return true; + if(empty($this->permissions[$permission])) return false; + $permitted_roles = array_intersect( + $this->user_roles, + $this->permissions[$permission] + ); + return (!empty($permitted_roles)); + } +} \ No newline at end of file diff --git a/lib/Config/Activator.php b/lib/Config/Activator.php index e1834203a6..5b7e757061 100644 --- a/lib/Config/Activator.php +++ b/lib/Config/Activator.php @@ -1,10 +1,11 @@ up(); @@ -14,8 +15,8 @@ class Activator { update_option('mailpoet_db_version', Env::$version); } - static function deactivate() { + function deactivate() { $migrator = new Migrator(); $migrator->down(); } -} +} \ No newline at end of file diff --git a/lib/Config/Changelog.php b/lib/Config/Changelog.php index 59295e0beb..1bf9fbfb4f 100644 --- a/lib/Config/Changelog.php +++ b/lib/Config/Changelog.php @@ -1,12 +1,11 @@ '', 'version' => '1.0.0' )) { Env::init($params['file'], $params['version']); + $this->access_control = new AccessControl(); } function init() { @@ -135,7 +136,11 @@ class Initializer { // if current db version and plugin version differ if(version_compare($current_db_version, Env::$version) !== 0) { - Activator::activate(); + if(!$this->access_control->validatePermission(AccessControl::PERMISSION_UPDATE_PLUGIN)) { + throw new \Exception(__('You do not have permission to activate/deactivate MailPoet plugin.', 'mailpoet')); + } + $activator = new Activator(); + $activator->activate(); } } @@ -185,7 +190,7 @@ class Initializer { } function setupMenu() { - $menu = new Menu($this->renderer, Env::$assets_url); + $menu = new Menu($this->renderer, Env::$assets_url, $this->access_control); $menu->init(); } @@ -218,11 +223,11 @@ class Initializer { } function setupJSONAPI() { - API\API::JSON()->init(); + API\API::JSON($this->access_control)->init(); } function setupRouter() { - $router = new Router\Router(); + $router = new Router\Router($this->access_control); $router->init(); } @@ -246,7 +251,7 @@ class Initializer { function handleFailedInitialization($exception) { // Check if we are able to add pages at this point if(function_exists('wp_get_current_user')) { - Menu::addErrorPage(); + Menu::addErrorPage($this->access_control); } return WPNotice::displayError($exception); } diff --git a/lib/Config/MP2Migrator.php b/lib/Config/MP2Migrator.php index 289be0bc66..9ef7fed8c6 100644 --- a/lib/Config/MP2Migrator.php +++ b/lib/Config/MP2Migrator.php @@ -187,8 +187,9 @@ class MP2Migrator { * */ private function eraseMP3Data() { - Activator::deactivate(); - Activator::activate(); + $activator = new Activator(); + $activator->deactivate(); + $activator->activate(); $this->deleteSegments(); $this->resetMigrationCounters(); diff --git a/lib/Config/Menu.php b/lib/Config/Menu.php index 2edcc1d5b1..521a598345 100644 --- a/lib/Config/Menu.php +++ b/lib/Config/Menu.php @@ -1,4 +1,5 @@ renderer = $renderer; $this->assets_url = $assets_url; + $this->access_control = $access_control; + $this->user_capability = $this->access_control->getUserFirstCapability(); $subscribers_feature = new SubscribersFeature(); $this->subscribers_over_limit = $subscribers_feature->check(); $this->checkMailPoetAPIKey(); @@ -45,134 +55,204 @@ class Menu { } function setup() { + if(!$this->access_control->validatePermission(AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN)) return; if(self::isOnMailPoetAdminPage()) { do_action('mailpoet_conflict_resolver_styles'); do_action('mailpoet_conflict_resolver_scripts'); } - $main_page_slug = 'mailpoet-newsletters'; - + // Main page add_menu_page( 'MailPoet', 'MailPoet', - Env::$required_permission, - $main_page_slug, + $this->user_capability, + self::MAIN_PAGE_SLUG, null, $this->assets_url . '/img/menu_icon.png', 30 ); - $newsletters_page = add_submenu_page( - $main_page_slug, - $this->setPageTitle(__('Emails', 'mailpoet')), - __('Emails', 'mailpoet'), - Env::$required_permission, - $main_page_slug, - array( - $this, - 'newsletters' - ) - ); + // Emails page + if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_EMAILS)) { + $newsletters_page = add_submenu_page( + self::MAIN_PAGE_SLUG, + $this->setPageTitle(__('Emails', 'mailpoet')), + __('Emails', 'mailpoet'), + $this->user_capability, + self::MAIN_PAGE_SLUG, + array( + $this, + 'newsletters' + ) + ); - // add limit per page to screen options - add_action('load-' . $newsletters_page, function() { - add_screen_option('per_page', array( - 'label' => _x( - 'Number of newsletters per page', - 'newsletters per page (screen options)', - 'mailpoet' - ), - 'option' => 'mailpoet_newsletters_per_page' - )); - }); + // add limit per page to screen options + add_action('load-' . $newsletters_page, function() { + add_screen_option('per_page', array( + 'label' => _x( + 'Number of newsletters per page', + 'newsletters per page (screen options)', + 'mailpoet' + ), + 'option' => 'mailpoet_newsletters_per_page' + )); + }); - $forms_page = add_submenu_page( - $main_page_slug, - $this->setPageTitle(__('Forms', 'mailpoet')), - __('Forms', 'mailpoet'), - Env::$required_permission, - 'mailpoet-forms', - array( - $this, - 'forms' - ) - ); - // add limit per page to screen options - add_action('load-' . $forms_page, function() { - add_screen_option('per_page', array( - 'label' => _x( - 'Number of forms per page', - 'forms per page (screen options)', - 'mailpoet' - ), - 'option' => 'mailpoet_forms_per_page' - )); - }); + // newsletter editor + add_submenu_page( + true, + $this->setPageTitle(__('Newsletter', 'mailpoet')), + __('Newsletter Editor', 'mailpoet'), + $this->user_capability, + 'mailpoet-newsletter-editor', + array( + $this, + 'newletterEditor' + ) + ); + } - $subscribers_page = add_submenu_page( - $main_page_slug, - $this->setPageTitle(__('Subscribers', 'mailpoet')), - __('Subscribers', 'mailpoet'), - Env::$required_permission, - 'mailpoet-subscribers', - array( - $this, - 'subscribers' - ) - ); - // add limit per page to screen options - add_action('load-' . $subscribers_page, function() { - add_screen_option('per_page', array( - 'label' => _x( - 'Number of subscribers per page', - 'subscribers per page (screen options)', - 'mailpoet' - ), - 'option' => 'mailpoet_subscribers_per_page' - )); - }); + // Forms page + if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_FORMS)) { + $forms_page = add_submenu_page( + self::MAIN_PAGE_SLUG, + $this->setPageTitle(__('Forms', 'mailpoet')), + __('Forms', 'mailpoet'), + $this->user_capability, + 'mailpoet-forms', + array( + $this, + 'forms' + ) + ); - $segments_page = add_submenu_page( - $main_page_slug, - $this->setPageTitle(__('Lists', 'mailpoet')), - __('Lists', 'mailpoet'), - Env::$required_permission, - 'mailpoet-segments', - array( - $this, - 'segments' - ) - ); + // add limit per page to screen options + add_action('load-' . $forms_page, function() { + add_screen_option('per_page', array( + 'label' => _x( + 'Number of forms per page', + 'forms per page (screen options)', + 'mailpoet' + ), + 'option' => 'mailpoet_forms_per_page' + )); + }); - // add limit per page to screen options - add_action('load-' . $segments_page, function() { - add_screen_option('per_page', array( - 'label' => _x( - 'Number of segments per page', - 'segments per page (screen options)', - 'mailpoet' - ), - 'option' => 'mailpoet_segments_per_page' - )); - }); + // form editor + add_submenu_page( + true, + $this->setPageTitle(__('Form Editor', 'mailpoet')), + __('Form Editor', 'mailpoet'), + $this->user_capability, + 'mailpoet-form-editor', + array( + $this, + 'formEditor' + ) + ); + } + // Subscribers page + if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_SUBSCRIBERS)) { + $subscribers_page = add_submenu_page( + self::MAIN_PAGE_SLUG, + $this->setPageTitle(__('Subscribers', 'mailpoet')), + __('Subscribers', 'mailpoet'), + $this->user_capability, + 'mailpoet-subscribers', + array( + $this, + 'subscribers' + ) + ); + + // add limit per page to screen options + add_action('load-' . $subscribers_page, function() { + add_screen_option('per_page', array( + 'label' => _x( + 'Number of subscribers per page', + 'subscribers per page (screen options)', + 'mailpoet' + ), + 'option' => 'mailpoet_subscribers_per_page' + )); + }); + + // import + add_submenu_page( + 'admin.php?page=mailpoet-subscribers', + $this->setPageTitle(__('Import', 'mailpoet')), + __('Import', 'mailpoet'), + $this->user_capability, + 'mailpoet-import', + array( + $this, + 'import' + ) + ); + + // export + add_submenu_page( + true, + $this->setPageTitle(__('Export', 'mailpoet')), + __('Export', 'mailpoet'), + $this->user_capability, + 'mailpoet-export', + array( + $this, + 'export' + ) + ); + } + + // Segments page + if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_SEGMENTS)) { + $segments_page = add_submenu_page( + self::MAIN_PAGE_SLUG, + $this->setPageTitle(__('Lists', 'mailpoet')), + __('Lists', 'mailpoet'), + $this->user_capability, + 'mailpoet-segments', + array( + $this, + 'segments' + ) + ); + + // add limit per page to screen options + add_action('load-' . $segments_page, function() { + add_screen_option('per_page', array( + 'label' => _x( + 'Number of segments per page', + 'segments per page (screen options)', + 'mailpoet' + ), + 'option' => 'mailpoet_segments_per_page' + )); + }); + } + + // Settings page + if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_SETTINGS)) { + add_submenu_page( + self::MAIN_PAGE_SLUG, + $this->setPageTitle(__('Settings', 'mailpoet')), + __('Settings', 'mailpoet'), + $this->user_capability, + 'mailpoet-settings', + array( + $this, + 'settings' + ) + ); + } + + // Help page add_submenu_page( - $main_page_slug, - $this->setPageTitle(__('Settings', 'mailpoet')), - __('Settings', 'mailpoet'), - Env::$required_permission, - 'mailpoet-settings', - array( - $this, - 'settings' - ) - ); - - add_submenu_page( - $main_page_slug, + self::MAIN_PAGE_SLUG, $this->setPageTitle(__('Help', 'mailpoet')), __('Help', 'mailpoet'), - Env::$required_permission, + $this->user_capability, 'mailpoet-help', array( $this, @@ -180,12 +260,13 @@ class Menu { ) ); + // Premium page // Only show this page in menu if the Premium plugin is not activated add_submenu_page( - License::getLicense() ? true : $main_page_slug, + License::getLicense() ? true : self::MAIN_PAGE_SLUG, $this->setPageTitle(__('Premium', 'mailpoet')), __('Premium', 'mailpoet'), - Env::$required_permission, + $this->user_capability, 'mailpoet-premium', array( $this, @@ -193,35 +274,12 @@ class Menu { ) ); - add_submenu_page( - 'admin.php?page=mailpoet-subscribers', - $this->setPageTitle(__('Import', 'mailpoet')), - __('Import', 'mailpoet'), - Env::$required_permission, - 'mailpoet-import', - array( - $this, - 'import' - ) - ); - - add_submenu_page( - true, - $this->setPageTitle(__('Export', 'mailpoet')), - __('Export', 'mailpoet'), - Env::$required_permission, - 'mailpoet-export', - array( - $this, - 'export' - ) - ); - + // Welcome page add_submenu_page( true, $this->setPageTitle(__('Welcome', 'mailpoet')), __('Welcome', 'mailpoet'), - Env::$required_permission, + $this->user_capability, 'mailpoet-welcome', array( $this, @@ -229,23 +287,12 @@ class Menu { ) ); - add_submenu_page( - true, - $this->setPageTitle(__('Migration', 'mailpoet')), - '', - Env::$required_permission, - 'mailpoet-migration', - array( - $this, - 'migration' - ) - ); - + // Update page add_submenu_page( true, $this->setPageTitle(__('Update', 'mailpoet')), __('Update', 'mailpoet'), - Env::$required_permission, + $this->user_capability, 'mailpoet-update', array( $this, @@ -253,27 +300,16 @@ class Menu { ) ); + // Migration page add_submenu_page( true, - $this->setPageTitle(__('Form Editor', 'mailpoet')), - __('Form Editor', 'mailpoet'), - Env::$required_permission, - 'mailpoet-form-editor', + $this->setPageTitle(__('Migration', 'mailpoet')), + '', + $this->user_capability, + 'mailpoet-migration', array( $this, - 'formEditor' - ) - ); - - add_submenu_page( - true, - $this->setPageTitle(__('Newsletter', 'mailpoet')), - __('Newsletter Editor', 'mailpoet'), - Env::$required_permission, - 'mailpoet-newsletter-editor', - array( - $this, - 'newletterEditor' + 'migration' ) ); } @@ -293,20 +329,20 @@ class Menu { or strpos($redirect_url, 'mailpoet') === false ) { - $redirect_url = admin_url('admin.php?page=mailpoet-newsletters'); + $redirect_url = admin_url('admin.php?page=' . self::MAIN_PAGE_SLUG); } $data = array( 'settings' => Setting::getAll(), 'current_user' => wp_get_current_user(), 'redirect_url' => $redirect_url, - 'sub_menu' => 'mailpoet-newsletters' + 'sub_menu' => self::MAIN_PAGE_SLUG ); $this->displayPage('welcome.html', $data); } function migration() { - $mp2_migrator = new MP2Migrator(); + $mp2_migrator = new MP2Migrator($this->access_control); $mp2_migrator->init(); $data = array( 'log_file_url' => $mp2_migrator->log_file_url, @@ -328,14 +364,14 @@ class Menu { or strpos($redirect_url, 'mailpoet') === false ) { - $redirect_url = admin_url('admin.php?page=mailpoet-newsletters'); + $redirect_url = admin_url('admin.php?page=' . self::MAIN_PAGE_SLUG); } $data = array( 'settings' => Setting::getAll(), 'current_user' => wp_get_current_user(), 'redirect_url' => $redirect_url, - 'sub_menu' => 'mailpoet-newsletters' + 'sub_menu' => self::MAIN_PAGE_SLUG ); $readme_file = Env::$path . '/readme.txt'; @@ -352,7 +388,7 @@ class Menu { function premium() { $data = array( 'subscriber_count' => Subscriber::getTotalSubscribers(), - 'sub_menu' => 'mailpoet-newsletters' + 'sub_menu' => self::MAIN_PAGE_SLUG ); $this->displayPage('premium.html', $data); @@ -505,7 +541,7 @@ class Menu { 'shortcodes' => ShortcodesHelper::getShortcodes(), 'settings' => Setting::getAll(), 'current_wp_user' => Subscriber::getCurrentWPUser(), - 'sub_menu' => 'mailpoet-newsletters' + 'sub_menu' => self::MAIN_PAGE_SLUG ); wp_enqueue_media(); wp_enqueue_script('tinymce-wplink', includes_url('js/tinymce/plugins/wplink/plugin.js')); @@ -597,13 +633,13 @@ class Menu { * This error page is used when the initialization is failed * to display admin notices only */ - static function addErrorPage() { + static function addErrorPage(AccessControl $access_control) { if(!self::isOnMailPoetAdminPage()) { return false; } // Check if page already exists if(get_plugin_page_hook($_REQUEST['page'], '') - || get_plugin_page_hook($_REQUEST['page'], 'mailpoet-newsletters') + || get_plugin_page_hook($_REQUEST['page'], self::MAIN_PAGE_SLUG) ) { return false; } @@ -611,9 +647,12 @@ class Menu { true, 'MailPoet', 'MailPoet', - Env::$required_permission, + $access_control->getUserFirstCapability(), $_REQUEST['page'], - array(__CLASS__, 'errorPageCallback') + array( + __CLASS__, + 'errorPageCallback' + ) ); } @@ -624,7 +663,7 @@ class Menu { function checkMailPoetAPIKey(ServicesChecker $checker = null) { if(self::isOnMailPoetAdminPage()) { $show_notices = isset($_REQUEST['page']) - && stripos($_REQUEST['page'], 'mailpoet-newsletters') === false; + && stripos($_REQUEST['page'], self::MAIN_PAGE_SLUG) === false; $checker = $checker ?: new ServicesChecker(); $this->mp_api_key_valid = $checker->isMailPoetAPIKeyValid($show_notices); } @@ -633,7 +672,7 @@ class Menu { function checkPremiumKey(ServicesChecker $checker = null) { if(self::isOnMailPoetAdminPage()) { $show_notices = isset($_REQUEST['page']) - && stripos($_REQUEST['page'], 'mailpoet-newsletters') === false; + && stripos($_REQUEST['page'], self::MAIN_PAGE_SLUG) === false; $checker = $checker ?: new ServicesChecker(); $this->premium_key_valid = $checker->isPremiumKeyValid($show_notices); } diff --git a/lib/Form/Widget.php b/lib/Form/Widget.php index 9953961c4a..a93a95516e 100644 --- a/lib/Form/Widget.php +++ b/lib/Form/Widget.php @@ -3,7 +3,7 @@ namespace MailPoet\Form; use MailPoet\API\JSON\API; -use MailPoet\Config\Renderer; +use MailPoet\Config\Renderer as ConfigRenderer; use MailPoet\Form\Renderer as FormRenderer; use MailPoet\Models\Form; use MailPoet\Util\Security; @@ -174,7 +174,7 @@ class Widget extends \WP_Widget { $data['api_version'] = API::CURRENT_VERSION; // render form - $renderer = new Renderer(); + $renderer = new ConfigRenderer(); try { $output = $renderer->render('form/widget.html', $data); $output = do_shortcode($output); diff --git a/lib/Router/Endpoints/CronDaemon.php b/lib/Router/Endpoints/CronDaemon.php index 89f4d07096..626e82c010 100644 --- a/lib/Router/Endpoints/CronDaemon.php +++ b/lib/Router/Endpoints/CronDaemon.php @@ -1,6 +1,8 @@ AccessControl::NO_ACCESS_RESTRICTION + ); function __construct($data) { $this->data = $data; diff --git a/lib/Router/Endpoints/Subscription.php b/lib/Router/Endpoints/Subscription.php index 04dfbf11fc..19e428d064 100644 --- a/lib/Router/Endpoints/Subscription.php +++ b/lib/Router/Endpoints/Subscription.php @@ -1,6 +1,8 @@ AccessControl::NO_ACCESS_RESTRICTION + ); function __construct($data) { $this->data = $data; diff --git a/lib/Router/Endpoints/Track.php b/lib/Router/Endpoints/Track.php index 5ab6a1266e..fe998c97f8 100644 --- a/lib/Router/Endpoints/Track.php +++ b/lib/Router/Endpoints/Track.php @@ -1,6 +1,8 @@ AccessControl::NO_ACCESS_RESTRICTION + ); function __construct($data) { $this->data = $this->_processTrackData($data); @@ -38,8 +43,8 @@ class Track { function _processTrackData($data) { $data = (object)Links::transformUrlDataObject($data); if(empty($data->queue_id) || - empty($data->subscriber_id) || - empty($data->subscriber_token) + empty($data->subscriber_id) || + empty($data->subscriber_token) ) { return false; } diff --git a/lib/Router/Endpoints/ViewInBrowser.php b/lib/Router/Endpoints/ViewInBrowser.php index 1229d1a68a..7e8a961d63 100644 --- a/lib/Router/Endpoints/ViewInBrowser.php +++ b/lib/Router/Endpoints/ViewInBrowser.php @@ -1,7 +1,8 @@ AccessControl::NO_ACCESS_RESTRICTION + ); - function __construct($data) { + function __construct($data, AccessControl $access_control) { + $this->access_control = $access_control; $this->data = $this->_processBrowserPreviewData($data); } @@ -69,8 +74,8 @@ class ViewInBrowser { $data->queue = false; } - // allow users with 'manage_options' permission to preview any newsletter - if(!empty($data->preview) && current_user_can(Env::$required_permission) + // allow users with permission to manage emails to preview any newsletter + if(!empty($data->preview) && $this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_EMAILS) ) return $data; // allow others to preview newsletters only when newsletter hash is defined diff --git a/lib/Router/Router.php b/lib/Router/Router.php index 93d3f9735b..fba4148fcb 100644 --- a/lib/Router/Router.php +++ b/lib/Router/Router.php @@ -1,6 +1,8 @@ api_request = isset($api_data[self::NAME]); $this->endpoint = isset($api_data['endpoint']) ? Helpers::underscoreToCamelCase($api_data['endpoint']) : false; - $this->action = isset($api_data['action']) ? + $this->endpoint_action = isset($api_data['action']) ? Helpers::underscoreToCamelCase($api_data['action']) : false; $this->data = isset($api_data['data']) ? self::decodeRequestData($api_data['data']) : false; + $this->access_control = $access_control; } function init() { @@ -33,15 +37,18 @@ class Router { if(!$this->endpoint || !class_exists($endpoint_class)) { return $this->terminateRequest(self::RESPONSE_ERROR, __('Invalid router endpoint', 'mailpoet')); } - $endpoint = new $endpoint_class($this->data); - if(!method_exists($endpoint, $this->action) || !in_array($this->action, $endpoint->allowed_actions)) { + $endpoint = new $endpoint_class($this->data, $this->access_control); + if(!method_exists($endpoint, $this->endpoint_action) || !in_array($this->endpoint_action, $endpoint->allowed_actions)) { return $this->terminateRequest(self::RESPONSE_ERROR, __('Invalid router endpoint action', 'mailpoet')); } + if(!$this->validatePermissions($this->endpoint_action, $endpoint->permissions)) { + return $this->terminateRequest(self::RESPONE_FORBIDDEN, __('You do not have the required permissions.', 'mailpoet')); + } do_action('mailpoet_conflict_resolver_router_url_query_parameters'); return call_user_func( array( $endpoint, - $this->action + $this->endpoint_action ) ); } @@ -74,4 +81,11 @@ class Router { status_header($code, $message); exit; } -} + + function validatePermissions($endpoint_action, $permissions) { + // validate action permission if defined, otherwise validate global permission + return(!empty($permissions['actions'][$endpoint_action])) ? + $this->access_control->validatePermission($permissions['actions'][$endpoint_action]) : + $this->access_control->validatePermission($permissions['global']); + } +} \ No newline at end of file diff --git a/lib/Subscription/Form.php b/lib/Subscription/Form.php index ec3262be0c..0dea09a960 100644 --- a/lib/Subscription/Form.php +++ b/lib/Subscription/Form.php @@ -2,14 +2,15 @@ namespace MailPoet\Subscription; -use MailPoet\API\JSON\API; +use MailPoet\API\API as API; use MailPoet\API\JSON\Response as APIResponse; +use MailPoet\Config\AccessControl; use MailPoet\Util\Url as UrlHelper; class Form { static function onSubmit($request_data = false) { $request_data = ($request_data) ? $request_data : $_REQUEST; - $api = new API(); + $api = API::JSON(new AccessControl()); $api->setRequestData($request_data); $form_id = (!empty($request_data['data']['form_id'])) ? (int)$request_data['data']['form_id'] : false; $response = $api->processRoute(); diff --git a/tests/unit/API/APITest.php b/tests/unit/API/APITest.php index ac98bc1909..69d2c76cbe 100644 --- a/tests/unit/API/APITest.php +++ b/tests/unit/API/APITest.php @@ -2,10 +2,11 @@ namespace MailPoet\Test\API; use MailPoet\API\API; +use MailPoet\Config\AccessControl; class APITest extends \MailPoetTest { function testItCallsJSONAPI() { - expect(API::JSON())->isInstanceOf('MailPoet\API\JSON\API'); + expect(API::JSON(new AccessControl()))->isInstanceOf('MailPoet\API\JSON\API'); } function testItCallsMPAPI() { diff --git a/tests/unit/API/JSON/APITest.php b/tests/unit/API/JSON/APITest.php index 82c5e6f794..f92856ce21 100644 --- a/tests/unit/API/JSON/APITest.php +++ b/tests/unit/API/JSON/APITest.php @@ -1,13 +1,19 @@ wp_user_id = $wp_user_id; } - - $this->api = new API(); - } - - function testItChecksPermissions() { - // logged out user - expect($this->api->checkPermissions())->false(); - - // give administrator role to wp user - $wp_user = get_user_by('id', $this->wp_user_id); - $wp_user->add_role('administrator'); - wp_set_current_user($wp_user->ID, $wp_user->user_login); - - // administrator should have permission - expect($this->api->checkPermissions())->true(); + $this->api = API::JSON(new AccessControl()); } function testItCallsAPISetupAction() { $called = false; - add_action( + Hooks::addAction( 'mailpoet_api_setup', - function ($api) use (&$called) { + function($api) use (&$called) { $called = true; - expect($api instanceof API)->true(); + expect($api instanceof JSONAPI)->true(); } ); $api = Stub::makeEmptyExcept( @@ -136,11 +128,101 @@ class APITest extends \MailPoetTest { ); $this->api->setRequestData($data); $response = $this->api->processRoute(); - expect($response->getData()['data'])->equals($data['api_version']); } + function testItValidatesPermissionBeforeProcessingEndpointMethod() { + $namespace = array( + 'name' => 'MailPoet\API\JSON\v1', + 'version' => 'v1' + ); + $data = array( + 'endpoint' => 'a_p_i_test_namespaced_endpoint_stub_v1', + 'method' => 'restricted', + 'api_version' => 'v1', + 'data' => array('test' => 'data') + ); + $access_control = new AccessControl(); + $access_control->user_roles = $access_control->permissions[AccessControl::PERMISSION_MANAGE_SETTINGS]; + $api = Stub::make( + new \MailPoet\API\JSON\API($access_control), + array( + 'validatePermissions' => function($method, $permissions) use ($data) { + expect($method)->equals($data['method']); + expect($permissions)->equals( + array( + 'global' => AccessControl::NO_ACCESS_RESTRICTION, + 'methods' => array( + 'test' => AccessControl::NO_ACCESS_RESTRICTION, + 'restricted' => AccessControl::PERMISSION_MANAGE_SETTINGS + ) + ) + ); + return true; + } + ) + ); + $api->addEndpointNamespace($namespace['name'], $namespace['version']); + $api->setRequestData($data); + $response = $api->processRoute(); + expect($response->getData()['data'])->equals($data['data']); + } + + function testItReturnsForbiddenResponseWhenPermissionFailsValidation() { + $namespace = array( + 'name' => 'MailPoet\API\JSON\v1', + 'version' => 'v1' + ); + $data = array( + 'endpoint' => 'a_p_i_test_namespaced_endpoint_stub_v1', + 'method' => 'restricted', + 'api_version' => 'v1', + 'data' => array('test' => 'data') + ); + $access_control = new AccessControl(); + $access_control->user_roles = array(); + $api = new \MailPoet\API\JSON\API($access_control); + $api->addEndpointNamespace($namespace['name'], $namespace['version']); + $api->setRequestData($data); + $response = $api->processRoute(); + expect($response->status)->equals(Response::STATUS_FORBIDDEN); + } + + function testItValidatesGlobalPermission() { + $access_control = new AccessControl(); + $permissions = array( + 'global' => AccessControl::PERMISSION_MANAGE_SETTINGS, + ); + + $access_control->user_roles = array(); + $api = new JSONAPI($access_control); + expect($api->validatePermissions(null, $permissions))->false(); + + $access_control->user_roles = $access_control->permissions[AccessControl::PERMISSION_MANAGE_SETTINGS]; + $api = new JSONAPI($access_control); + expect($api->validatePermissions(null, $permissions))->true(); + } + + function testItValidatesEndpointMethodPermission() { + $access_control = new AccessControl(); + $permissions = array( + 'global' => null, + 'methods' => array( + 'test' => AccessControl::PERMISSION_MANAGE_SETTINGS + ) + ); + + $access_control->user_roles = array(); + $api = new JSONAPI($access_control); + expect($api->validatePermissions('test', $permissions))->false(); + + $access_control->user_roles = $access_control->permissions[AccessControl::PERMISSION_MANAGE_SETTINGS]; + $api = new JSONAPI($access_control); + expect($api->validatePermissions('test', $permissions))->true(); + } + function _after() { + WPHooksHelper::releaseAllHooks(); wp_delete_user($this->wp_user_id); } } \ No newline at end of file diff --git a/tests/unit/API/JSON/APITestNamespacedEndpointStubV1.php b/tests/unit/API/JSON/APITestNamespacedEndpointStubV1.php index 4f6d79abb5..c597d977af 100644 --- a/tests/unit/API/JSON/APITestNamespacedEndpointStubV1.php +++ b/tests/unit/API/JSON/APITestNamespacedEndpointStubV1.php @@ -3,16 +3,24 @@ namespace MailPoet\API\JSON\v1; use MailPoet\API\JSON\Endpoint as APIEndpoint; -use MailPoet\API\JSON\Access as APIAccess; +use MailPoet\Config\AccessControl; if(!defined('ABSPATH')) exit; class APITestNamespacedEndpointStubV1 extends APIEndpoint { public $permissions = array( - 'test' => APIAccess::ALL + 'global' => AccessControl::NO_ACCESS_RESTRICTION, + 'methods' => array( + 'test' => AccessControl::NO_ACCESS_RESTRICTION, + 'restricted' => AccessControl::PERMISSION_MANAGE_SETTINGS + ) ); function test($data) { return $this->successResponse($data); } + + function restricted($data) { + return $this->successResponse($data); + } } diff --git a/tests/unit/API/JSON/APITestNamespacedEndpointStubV2.php b/tests/unit/API/JSON/APITestNamespacedEndpointStubV2.php index b0029f2d81..1d3ffa51a2 100644 --- a/tests/unit/API/JSON/APITestNamespacedEndpointStubV2.php +++ b/tests/unit/API/JSON/APITestNamespacedEndpointStubV2.php @@ -2,14 +2,17 @@ namespace MailPoet\API\JSON\v2; -use MailPoet\API\JSON\Access as APIAccess; use MailPoet\API\JSON\Endpoint as APIEndpoint; +use MailPoet\Config\AccessControl; if(!defined('ABSPATH')) exit; class APITestNamespacedEndpointStubV2 extends APIEndpoint { public $permissions = array( - 'testVersion' => APIAccess::ALL + 'global' => AccessControl::NO_ACCESS_RESTRICTION, + 'methods' => array( + 'test' => AccessControl::NO_ACCESS_RESTRICTION + ) ); function testVersion() { diff --git a/tests/unit/Config/AccessControlTest.php b/tests/unit/Config/AccessControlTest.php new file mode 100644 index 0000000000..75302c4586 --- /dev/null +++ b/tests/unit/Config/AccessControlTest.php @@ -0,0 +1,115 @@ + array( + 'administrator', + 'editor' + ), + AccessControl::PERMISSION_MANAGE_SETTINGS => array( + 'administrator' + ), + AccessControl::PERMISSION_MANAGE_EMAILS => array( + 'administrator', + 'editor' + ), + AccessControl::PERMISSION_MANAGE_SUBSCRIBERS => array( + 'administrator' + ), + AccessControl::PERMISSION_MANAGE_FORMS => array( + 'administrator' + ), + AccessControl::PERMISSION_MANAGE_SEGMENTS => array( + 'administrator' + ), + AccessControl::PERMISSION_UPDATE_PLUGIN => array( + 'administrator' + ) + ); + $access_control = new AccessControl(); + expect($access_control->permissions)->equals($default_permissions); + } + + function testItAllowsSettingCustomPermissions() { + Hooks::addFilter( + 'mailpoet_permission_access_plugin_admin', + function() { + return array('custom_access_plugin_admin_role'); + } + ); + Hooks::addFilter( + 'mailpoet_permission_manage_settings', + function() { + return array('custom_manage_settings_role'); + } + ); + Hooks::addFilter( + 'mailpoet_permission_manage_emails', + function() { + return array('custom_manage_emails_role'); + } + ); + Hooks::addFilter( + 'mailpoet_permission_manage_subscribers', + function() { + return array('custom_manage_subscribers_role'); + } + ); + Hooks::addFilter( + 'mailpoet_permission_manage_forms', + function() { + return array('custom_manage_forms_role'); + } + ); + Hooks::addFilter( + 'mailpoet_permission_manage_segments', + function() { + return array('custom_manage_segments_role'); + } + ); + Hooks::addFilter( + 'mailpoet_permission_update_plugin', + function() { + return array('custom_update_plugin_role'); + } + ); + + $access_control = new AccessControl(); + expect($access_control->permissions)->equals( + array( + AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN => array( + 'custom_access_plugin_admin_role' + ), + AccessControl::PERMISSION_MANAGE_SETTINGS => array( + 'custom_manage_settings_role' + ), + AccessControl::PERMISSION_MANAGE_EMAILS => array( + 'custom_manage_emails_role' + ), + AccessControl::PERMISSION_MANAGE_SUBSCRIBERS => array( + 'custom_manage_subscribers_role' + ), + AccessControl::PERMISSION_MANAGE_FORMS => array( + 'custom_manage_forms_role' + ), + AccessControl::PERMISSION_MANAGE_SEGMENTS => array( + 'custom_manage_segments_role' + ), + AccessControl::PERMISSION_UPDATE_PLUGIN => array( + 'custom_update_plugin_role' + ), + ) + ); + } + + function _after() { + WPHooksHelper::releaseAllHooks(); + } +} \ No newline at end of file diff --git a/tests/unit/Config/MenuTest.php b/tests/unit/Config/MenuTest.php index 3312e262c1..7a81f40dee 100644 --- a/tests/unit/Config/MenuTest.php +++ b/tests/unit/Config/MenuTest.php @@ -1,7 +1,9 @@ false ); // instantiate class - $this->view_in_browser = new ViewInBrowser($this->browser_preview_data); + $this->view_in_browser = new ViewInBrowser($this->browser_preview_data, new AccessControl()); } function testItAbortsWhenBrowserPreviewDataIsMissing() { @@ -123,6 +125,7 @@ class ViewInBrowserTest extends \MailPoetTest { } function testItDoesNotRequireWpAdministratorToBeOnProcessedListWhenPreviewIsEnabled() { + $view_in_browser = $this->view_in_browser; $data = (object)array_merge( $this->browser_preview_data, array( @@ -132,19 +135,25 @@ class ViewInBrowserTest extends \MailPoetTest { ) ); $data->preview = true; + // when WP user is not logged, false should be returned - expect($this->view_in_browser->_validateBrowserPreviewData($data))->false(); + expect($view_in_browser->_validateBrowserPreviewData($data))->false(); + // when WP user is logged in but does not have 'manage options' permission, false should be returned wp_set_current_user(1); $wp_user = wp_get_current_user(); $wp_user->remove_role('administrator'); + $view_in_browser->access_control = new AccessControl(); expect($this->view_in_browser->_validateBrowserPreviewData($data))->false(); + // when WP user is logged and has 'manage options' permission, data should be returned $wp_user->add_role('administrator'); - expect($this->view_in_browser->_validateBrowserPreviewData($data))->equals($data); + $view_in_browser->access_control = new AccessControl(); + expect($view_in_browser->_validateBrowserPreviewData($data))->equals($data); } function testItSetsSubscriberToLoggedInWPUserWhenPreviewIsEnabled() { + $view_in_browser = $this->view_in_browser; $data = (object)array_merge( $this->browser_preview_data, array( @@ -155,7 +164,8 @@ class ViewInBrowserTest extends \MailPoetTest { ); $data->preview = true; wp_set_current_user(1); - $result = $this->view_in_browser->_validateBrowserPreviewData($data); + $view_in_browser->access_control = new AccessControl(); + $result = $view_in_browser->_validateBrowserPreviewData($data); expect($result->subscriber->id)->equals(1); } diff --git a/tests/unit/Router/RouterTest.php b/tests/unit/Router/RouterTest.php index 58d31d7603..27d1d75fc1 100644 --- a/tests/unit/Router/RouterTest.php +++ b/tests/unit/Router/RouterTest.php @@ -1,24 +1,26 @@ router_data = array( Router::NAME => '', 'endpoint' => 'router_test_mock_endpoint', 'action' => 'test', 'data' => base64_encode(json_encode(array('data' => 'dummy data'))) ); - $this->router = new Router($this->router_data); + $this->access_control = new AccessControl(); + $this->router = new Router($this->access_control, $this->router_data); } function testItCanGetAPIDataFromGetRequest() { @@ -26,10 +28,10 @@ class RouterTest extends \MailPoetTest { $url = 'http://example.com/?' . Router::NAME . '&endpoint=view_in_browser&action=view&data=' . base64_encode(json_encode($data)); parse_str(parse_url($url, PHP_URL_QUERY), $_GET); - $router = new Router(); + $router = new Router($this->access_control); expect($router->api_request)->equals(true); expect($router->endpoint)->equals('viewInBrowser'); - expect($router->action)->equals('view'); + expect($router->endpoint_action)->equals('view'); expect($router->data)->equals($data); } @@ -37,8 +39,8 @@ class RouterTest extends \MailPoetTest { $router_data = $this->router_data; unset($router_data[Router::NAME]); $router = Stub::construct( - new Router(), - array($router_data) + '\MailPoet\Router\Router', + array($this->access_control, $router_data) ); $result = $router->init(); expect($result)->null(); @@ -48,8 +50,8 @@ class RouterTest extends \MailPoetTest { $router_data = $this->router_data; $router_data['endpoint'] = 'invalid_endpoint'; $router = Stub::construct( - new Router(), - array($router_data), + '\MailPoet\Router\Router', + array($this->access_control, $router_data), array( 'terminateRequest' => function($code, $error) { return array( @@ -72,8 +74,8 @@ class RouterTest extends \MailPoetTest { $router_data = $this->router_data; $router_data['action'] = 'invalid_action'; $router = Stub::construct( - new Router(), - array($router_data), + '\MailPoet\Router\Router', + array($this->access_control, $router_data), array( 'terminateRequest' => function($code, $error) { return array( @@ -92,6 +94,87 @@ class RouterTest extends \MailPoetTest { ); } + function testItValidatesGlobalPermission() { + $access_control = new AccessControl(); + $router = $this->router; + + $permissions = array( + 'global' => AccessControl::PERMISSION_MANAGE_SETTINGS, + ); + $access_control->user_roles = array(); + $router->access_control = $access_control; + expect($router->validatePermissions(null, $permissions))->false(); + + $access_control->user_roles = $access_control->permissions[AccessControl::PERMISSION_MANAGE_SETTINGS]; + $router->access_control = $access_control; + expect($router->validatePermissions(null, $permissions))->true(); + } + + function testItValidatesEndpointActionPermission() { + $access_control = new AccessControl(); + $router = $this->router; + + $permissions = array( + 'global' => null, + 'actions' => array( + 'test' => AccessControl::PERMISSION_MANAGE_SETTINGS + ) + ); + + $access_control->user_roles = array(); + $router->access_control = $access_control; + expect($router->validatePermissions('test', $permissions))->false(); + + $access_control->user_roles = $access_control->permissions[AccessControl::PERMISSION_MANAGE_SETTINGS]; + $router->access_control = $access_control; + expect($router->validatePermissions('test', $permissions))->true(); + } + + function testItValidatesPermissionBeforeProcessingEndpointAction() { + $router = Stub::construct( + '\MailPoet\Router\Router', + array($this->access_control, $this->router_data), + array( + 'validatePermissions' => function($action, $permissions) { + expect($action)->equals($this->router_data['action']); + expect($permissions)->equals( + array( + 'global' => AccessControl::NO_ACCESS_RESTRICTION + ) + ); + return true; + } + ) + ); + $result = $router->init(); + expect($result)->equals( + array('data' => 'dummy data') + ); + } + + function testItReturnsForbiddenResponseWhenPermissionFailsValidation() { + $router = Stub::construct( + '\MailPoet\Router\Router', + array($this->access_control, $this->router_data), + array( + 'validatePermissions' => false, + 'terminateRequest' => function($code, $error) { + return array( + $code, + $error + ); + } + ) + ); + $result = $router->init(); + expect($result)->equals( + array( + 403, + 'You do not have the required permissions.' + ) + ); + } + function testItCallsEndpointAction() { $data = array('data' => 'dummy data'); $result = $this->router->init(); @@ -99,8 +182,7 @@ class RouterTest extends \MailPoetTest { } function testItExecutesUrlParameterConflictResolverAction() { - $data = array('data' => 'dummy data'); - $result = $this->router->init(); + $this->router->init(); expect((boolean)did_action('mailpoet_conflict_resolver_router_url_query_parameters'))->true(); } @@ -140,4 +222,4 @@ class RouterTest extends \MailPoetTest { ); expect($result)->contains(Router::NAME . '&endpoint=router_test_mock_endpoint&action=test&data=' . $encoded_data); } -} +} \ No newline at end of file diff --git a/tests/unit/Router/RouterTestMockEndpoint.php b/tests/unit/Router/RouterTestMockEndpoint.php index 24ab75f774..51c020f92a 100644 --- a/tests/unit/Router/RouterTestMockEndpoint.php +++ b/tests/unit/Router/RouterTestMockEndpoint.php @@ -2,12 +2,17 @@ namespace MailPoet\Router\Endpoints; +use MailPoet\Config\AccessControl; + class RouterTestMockEndpoint { const ACTION_TEST = 'test'; public $allowed_actions = array( self::ACTION_TEST ); public $data; + public $permissions = array( + 'global' => AccessControl::NO_ACCESS_RESTRICTION + ); function __construct($data) { $this->data = $data;