Merge pull request #1041 from mailpoet/access_control

Allows granular control of access to various parts of the plugin [MAILPOET-1057] [MAILPOET-1048]
This commit is contained in:
Tautvidas Sipavičius
2017-08-28 16:48:47 +03:00
committed by GitHub
41 changed files with 890 additions and 323 deletions

View File

@ -1,11 +1,14 @@
<?php <?php
namespace MailPoet\API; namespace MailPoet\API;
use MailPoet\Config\AccessControl;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class API { class API {
static function JSON() { static function JSON(AccessControl $access_control) {
return new \MailPoet\API\JSON\API(); return new \MailPoet\API\JSON\API($access_control);
} }
static function MP($version) { static function MP($version) {

View File

@ -1,7 +1,7 @@
<?php <?php
namespace MailPoet\API\JSON; namespace MailPoet\API\JSON;
use MailPoet\Config\Env; use MailPoet\Config\AccessControl;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
use MailPoet\Util\Security; use MailPoet\Util\Security;
use MailPoet\WP\Hooks; use MailPoet\WP\Hooks;
@ -19,9 +19,11 @@ class API {
private $_available_api_versions = array( private $_available_api_versions = array(
'v1' 'v1'
); );
private $access_control;
const CURRENT_VERSION = 'v1'; const CURRENT_VERSION = 'v1';
function __construct() { function __construct(AccessControl $access_control) {
$this->access_control = $access_control;
foreach($this->_available_api_versions as $available_api_version) { foreach($this->_available_api_versions as $available_api_version) {
$this->addEndpointNamespace( $this->addEndpointNamespace(
sprintf('%s\%s', __NAMESPACE__, $available_api_version), sprintf('%s\%s', __NAMESPACE__, $available_api_version),
@ -130,17 +132,11 @@ class API {
// check the accessibility of the requested endpoint's action // check the accessibility of the requested endpoint's action
// by default, an endpoint's action is considered "private" // by default, an endpoint's action is considered "private"
$permissions = $endpoint->permissions; if(!$this->validatePermissions($this->_request_method, $endpoint->permissions)) {
if(array_key_exists($this->_request_method, $permissions) === false || $error_message = __('You do not have the required permissions.', 'mailpoet');
$permissions[$this->_request_method] !== Access::ALL $error_response = $this->createErrorResponse(Error::FORBIDDEN, $error_message, Response::STATUS_FORBIDDEN);
) { return $error_response;
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;
}
} }
$response = $endpoint->{$this->_request_method}($this->_request_data); $response = $endpoint->{$this->_request_method}($this->_request_data);
return $response; return $response;
} catch(\Exception $e) { } catch(\Exception $e) {
@ -150,8 +146,11 @@ class API {
} }
} }
function checkPermissions() { function validatePermissions($request_method, $permissions) {
return current_user_can(Env::$required_permission); // 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() { function checkToken() {

View File

@ -1,12 +0,0 @@
<?php
namespace MailPoet\API\JSON;
if(!defined('ABSPATH')) exit;
final class Access {
const ALL = 'all';
private function __construct() {
}
}

View File

@ -1,11 +1,16 @@
<?php <?php
namespace MailPoet\API\JSON; namespace MailPoet\API\JSON;
use MailPoet\Config\AccessControl;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
abstract class Endpoint { abstract class Endpoint {
public $permissions = array(
public $permissions = array(); 'global' => array(AccessControl::PERMISSION_MANAGE_SETTINGS),
'methods' => array()
);
function successResponse( function successResponse(
$data = array(), $meta = array(), $status = Response::STATUS_OK $data = array(), $meta = array(), $status = Response::STATUS_OK
@ -18,7 +23,7 @@ abstract class Endpoint {
) { ) {
if(empty($errors)) { if(empty($errors)) {
$errors = array( $errors = array(
Error::UNKNOWN => __('An unknown error occurred.', 'mailpoet') Error::UNKNOWN => __('An unknown error occurred.', 'mailpoet')
); );
} }
return new ErrorResponse($errors, $meta, $status); return new ErrorResponse($errors, $meta, $status);

View File

@ -1,12 +1,18 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Config\AccessControl;
use MailPoet\WP\Posts as WPPosts; use MailPoet\WP\Posts as WPPosts;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class AutomatedLatestContent extends APIEndpoint { class AutomatedLatestContent extends APIEndpoint {
public $ALC; public $ALC;
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_EMAILS
);
function __construct() { function __construct() {
$this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent(); $this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent();

View File

@ -1,12 +1,19 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Models\CustomField; use MailPoet\Models\CustomField;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class CustomFields extends APIEndpoint { class CustomFields extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_FORMS
);
function getAll() { function getAll() {
$collection = CustomField::orderByAsc('created_at')->findMany(); $collection = CustomField::orderByAsc('created_at')->findMany();
$custom_fields = array_map(function($custom_field) { $custom_fields = array_map(function($custom_field) {

View File

@ -1,17 +1,23 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Form\Renderer as FormRenderer;
use MailPoet\Form\Util;
use MailPoet\Listing;
use MailPoet\Models\Form; use MailPoet\Models\Form;
use MailPoet\Models\StatisticsForms; use MailPoet\Models\StatisticsForms;
use MailPoet\Form\Renderer as FormRenderer;
use MailPoet\Listing;
use MailPoet\Form\Util;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Forms extends APIEndpoint { class Forms extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_FORMS
);
function get($data = array()) { function get($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : false); $id = (isset($data['id']) ? (int)$data['id'] : false);
$form = Form::findOne($id); $form = Form::findOne($id);

View File

@ -1,13 +1,19 @@
<?php <?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Subscribers\ImportExport\Import\MailChimp; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Config\AccessControl;
use MailPoet\Models\Segment; use MailPoet\Models\Segment;
use MailPoet\Subscribers\ImportExport\Import\MailChimp;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class ImportExport extends APIEndpoint { class ImportExport extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS
);
function getMailChimpLists($data) { function getMailChimpLists($data) {
try { try {
$mailChimp = new MailChimp($data['api_key']); $mailChimp = new MailChimp($data['api_key']);

View File

@ -1,10 +1,16 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Config\AccessControl;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class MP2Migrator extends APIEndpoint { class MP2Migrator extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS
);
public function __construct() { public function __construct() {
$this->MP2Migrator = new \MailPoet\Config\MP2Migrator(); $this->MP2Migrator = new \MailPoet\Config\MP2Migrator();

View File

@ -1,12 +1,19 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Mailer\MailerLog; use MailPoet\Mailer\MailerLog;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Mailer extends APIEndpoint { class Mailer extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_EMAILS
);
function send($data = array()) { function send($data = array()) {
try { try {
$mailer = new \MailPoet\Mailer\Mailer( $mailer = new \MailPoet\Mailer\Mailer(

View File

@ -1,13 +1,19 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Models\NewsletterTemplate; use MailPoet\Models\NewsletterTemplate;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class NewsletterTemplates extends APIEndpoint { class NewsletterTemplates extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_EMAILS
);
function get($data = array()) { function get($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : false); $id = (isset($data['id']) ? (int)$data['id'] : false);
$template = NewsletterTemplate::findOne($id); $template = NewsletterTemplate::findOne($id);

View File

@ -1,17 +1,19 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use Carbon\Carbon; use Carbon\Carbon;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Listing; use MailPoet\Listing;
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterOption;
use MailPoet\Models\NewsletterOptionField;
use MailPoet\Models\NewsletterSegment;
use MailPoet\Models\NewsletterTemplate;
use MailPoet\Models\SendingQueue; use MailPoet\Models\SendingQueue;
use MailPoet\Models\Setting; use MailPoet\Models\Setting;
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterTemplate;
use MailPoet\Models\NewsletterSegment;
use MailPoet\Models\NewsletterOptionField;
use MailPoet\Models\NewsletterOption;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
use MailPoet\Newsletter\Renderer\Renderer; use MailPoet\Newsletter\Renderer\Renderer;
use MailPoet\Newsletter\Scheduler\Scheduler; use MailPoet\Newsletter\Scheduler\Scheduler;
@ -23,6 +25,10 @@ if(!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-includes/pluggable.php'); require_once(ABSPATH . 'wp-includes/pluggable.php');
class Newsletters extends APIEndpoint { class Newsletters extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_EMAILS
);
function get($data = array()) { function get($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : false); $id = (isset($data['id']) ? (int)$data['id'] : false);
$newsletter = Newsletter::findOne($id); $newsletter = Newsletter::findOne($id);

View File

@ -1,15 +1,21 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Models\Segment;
use MailPoet\Listing; use MailPoet\Listing;
use MailPoet\Models\Segment;
use MailPoet\Segments\WP; use MailPoet\Segments\WP;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Segments extends APIEndpoint { class Segments extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_SEGMENTS
);
function get($data = array()) { function get($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : false); $id = (isset($data['id']) ? (int)$data['id'] : false);
$segment = Segment::findOne($id); $segment = Segment::findOne($id);

View File

@ -1,18 +1,24 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Mailer\Mailer; use MailPoet\Mailer\Mailer;
use MailPoet\Models\Newsletter; use MailPoet\Models\Newsletter;
use MailPoet\Models\SendingQueue as SendingQueueModel;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
use MailPoet\Newsletter\Scheduler\Scheduler; use MailPoet\Newsletter\Scheduler\Scheduler;
use MailPoet\Models\SendingQueue as SendingQueueModel;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class SendingQueue extends APIEndpoint { class SendingQueue extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_EMAILS
);
function add($data = array()) { function add($data = array()) {
$newsletter_id = (isset($data['newsletter_id']) $newsletter_id = (isset($data['newsletter_id'])
? (int)$data['newsletter_id'] ? (int)$data['newsletter_id']

View File

@ -1,11 +1,12 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Config\Installer; use MailPoet\Config\Installer;
use MailPoet\Services\Bridge; use MailPoet\Services\Bridge;
use MailPoet\Util\License\License;
use MailPoet\WP\DateTime; use MailPoet\WP\DateTime;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
@ -13,6 +14,9 @@ if(!defined('ABSPATH')) exit;
class Services extends APIEndpoint { class Services extends APIEndpoint {
public $bridge; public $bridge;
public $date_time; public $date_time;
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS
);
function __construct() { function __construct() {
$this->bridge = new Bridge(); $this->bridge = new Bridge();

View File

@ -1,24 +1,31 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Models\Setting; use MailPoet\Models\Setting;
use MailPoet\Services\Bridge; use MailPoet\Services\Bridge;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Settings extends APIEndpoint { class Settings extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS
);
function get() { function get() {
return $this->successResponse(Setting::getAll()); return $this->successResponse(Setting::getAll());
} }
function set($settings = array()) { function set($settings = array()) {
if(empty($settings)) { if(empty($settings)) {
return $this->badRequest(array( return $this->badRequest(
APIError::BAD_REQUEST => array(
__('You have not specified any settings to be saved.', 'mailpoet') APIError::BAD_REQUEST =>
)); __('You have not specified any settings to be saved.', 'mailpoet')
));
} else { } else {
foreach($settings as $name => $value) { foreach($settings as $name => $value) {
Setting::setValue($name, $value); Setting::setValue($name, $value);

View File

@ -1,13 +1,19 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Config\AccessControl;
use MailPoet\Config\Activator; use MailPoet\Config\Activator;
use MailPoet\WP\Hooks; use MailPoet\WP\Hooks;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Setup extends APIEndpoint { class Setup extends APIEndpoint {
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS
);
function reset() { function reset() {
try { try {
$activator = new Activator(); $activator = new Activator();

View File

@ -1,21 +1,22 @@
<?php <?php
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError; use MailPoet\API\JSON\Error as APIError;
use MailPoet\API\JSON\Access as APIAccess; use MailPoet\Config\AccessControl;
use MailPoet\Form\Util\FieldNameObfuscator;
use MailPoet\Listing; use MailPoet\Listing;
use MailPoet\Models\Subscriber; use MailPoet\Form\Util\FieldNameObfuscator;
use MailPoet\Models\Form; use MailPoet\Models\Form;
use MailPoet\Models\StatisticsForms; use MailPoet\Models\StatisticsForms;
use MailPoet\Models\Subscriber;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Subscribers extends APIEndpoint { class Subscribers extends APIEndpoint {
public $permissions = array( public $permissions = array(
'subscribe' => APIAccess::ALL 'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
'methods' => array('subscribe' => AccessControl::NO_ACCESS_RESTRICTION)
); );
function get($data = array()) { function get($data = array()) {

View File

@ -0,0 +1,104 @@
<?php
namespace MailPoet\Config;
use MailPoet\WP\Hooks as WPHooks;
if(!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-includes/pluggable.php');
class AccessControl {
const PERMISSION_ACCESS_PLUGIN_ADMIN = 'access_plugin_admin';
const PERMISSION_MANAGE_SETTINGS = 'manage_settings';
const PERMISSION_MANAGE_EMAILS = 'manage_emails';
const PERMISSION_MANAGE_SUBSCRIBERS = 'manage_subscribers';
const PERMISSION_MANAGE_FORMS = 'manage_forms';
const PERMISSION_MANAGE_SEGMENTS = 'manage_segments';
const PERMISSION_UPDATE_PLUGIN = 'update_plugin';
const NO_ACCESS_RESTRICTION = 'no_access_restriction';
public $permissions;
public $current_user_roles;
public $user_capabilities;
function __construct() {
$this->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));
}
}

View File

@ -1,10 +1,11 @@
<?php <?php
namespace MailPoet\Config; namespace MailPoet\Config;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Activator { class Activator {
static function activate() { function activate() {
$migrator = new Migrator(); $migrator = new Migrator();
$migrator->up(); $migrator->up();
@ -14,7 +15,7 @@ class Activator {
update_option('mailpoet_db_version', Env::$version); update_option('mailpoet_db_version', Env::$version);
} }
static function deactivate() { function deactivate() {
$migrator = new Migrator(); $migrator = new Migrator();
$migrator->down(); $migrator->down();
} }

View File

@ -1,12 +1,11 @@
<?php <?php
namespace MailPoet\Config; namespace MailPoet\Config;
use MailPoet\Models\Setting; use MailPoet\Models\Setting;
use MailPoet\Util\Url; use MailPoet\Util\Url;
class Changelog { class Changelog {
function __construct() {
}
function init() { function init() {
$doing_ajax = (bool)(defined('DOING_AJAX') && DOING_AJAX); $doing_ajax = (bool)(defined('DOING_AJAX') && DOING_AJAX);

View File

@ -1,4 +1,5 @@
<?php <?php
namespace MailPoet\Config; namespace MailPoet\Config;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
@ -31,7 +32,6 @@ class Env {
static $db_collation; static $db_collation;
static $db_charset_collate; static $db_charset_collate;
static $db_timezone_offset; static $db_timezone_offset;
static $required_permission = 'manage_options';
static function init($file, $version) { static function init($file, $version) {
global $wpdb; global $wpdb;

View File

@ -13,17 +13,18 @@ if(!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-admin/includes/plugin.php'); require_once(ABSPATH . 'wp-admin/includes/plugin.php');
class Initializer { class Initializer {
const UNABLE_TO_CONNECT = 'Unable to connect to the database (the database is unable to open a file or folder), the connection is likely not configured correctly. Please read our [link] Knowledge Base article [/link] for steps how to resolve it.'; const UNABLE_TO_CONNECT = 'Unable to connect to the database (the database is unable to open a file or folder), the connection is likely not configured correctly. Please read our [link] Knowledge Base article [/link] for steps how to resolve it.';
const SOLVE_DB_ISSUE_URL = 'http://beta.docs.mailpoet.com/article/200-solving-database-connection-issues'; const SOLVE_DB_ISSUE_URL = 'http://beta.docs.mailpoet.com/article/200-solving-database-connection-issues';
protected $plugin_initialized = false; protected $plugin_initialized = false;
private $access_control;
function __construct($params = array( function __construct($params = array(
'file' => '', 'file' => '',
'version' => '1.0.0' 'version' => '1.0.0'
)) { )) {
Env::init($params['file'], $params['version']); Env::init($params['file'], $params['version']);
$this->access_control = new AccessControl();
} }
function init() { function init() {
@ -135,7 +136,11 @@ class Initializer {
// if current db version and plugin version differ // if current db version and plugin version differ
if(version_compare($current_db_version, Env::$version) !== 0) { 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() { function setupMenu() {
$menu = new Menu($this->renderer, Env::$assets_url); $menu = new Menu($this->renderer, Env::$assets_url, $this->access_control);
$menu->init(); $menu->init();
} }
@ -218,11 +223,11 @@ class Initializer {
} }
function setupJSONAPI() { function setupJSONAPI() {
API\API::JSON()->init(); API\API::JSON($this->access_control)->init();
} }
function setupRouter() { function setupRouter() {
$router = new Router\Router(); $router = new Router\Router($this->access_control);
$router->init(); $router->init();
} }
@ -246,7 +251,7 @@ class Initializer {
function handleFailedInitialization($exception) { function handleFailedInitialization($exception) {
// Check if we are able to add pages at this point // Check if we are able to add pages at this point
if(function_exists('wp_get_current_user')) { if(function_exists('wp_get_current_user')) {
Menu::addErrorPage(); Menu::addErrorPage($this->access_control);
} }
return WPNotice::displayError($exception); return WPNotice::displayError($exception);
} }

View File

@ -187,8 +187,9 @@ class MP2Migrator {
* *
*/ */
private function eraseMP3Data() { private function eraseMP3Data() {
Activator::deactivate(); $activator = new Activator();
Activator::activate(); $activator->deactivate();
$activator->activate();
$this->deleteSegments(); $this->deleteSegments();
$this->resetMigrationCounters(); $this->resetMigrationCounters();

View File

@ -1,4 +1,5 @@
<?php <?php
namespace MailPoet\Config; namespace MailPoet\Config;
use MailPoet\Cron\CronTrigger; use MailPoet\Cron\CronTrigger;
@ -25,9 +26,18 @@ use MailPoet\WP\Readme;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Menu { class Menu {
function __construct($renderer, $assets_url) { const MAIN_PAGE_SLUG = 'mailpoet-newsletters';
public $renderer;
public $assets_url;
private $access_control;
private $subscribers_over_limit;
function __construct($renderer, $assets_url, AccessControl $access_control) {
$this->renderer = $renderer; $this->renderer = $renderer;
$this->assets_url = $assets_url; $this->assets_url = $assets_url;
$this->access_control = $access_control;
$this->user_capability = $this->access_control->getUserFirstCapability();
$subscribers_feature = new SubscribersFeature(); $subscribers_feature = new SubscribersFeature();
$this->subscribers_over_limit = $subscribers_feature->check(); $this->subscribers_over_limit = $subscribers_feature->check();
$this->checkMailPoetAPIKey(); $this->checkMailPoetAPIKey();
@ -45,134 +55,204 @@ class Menu {
} }
function setup() { function setup() {
if(!$this->access_control->validatePermission(AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN)) return;
if(self::isOnMailPoetAdminPage()) { if(self::isOnMailPoetAdminPage()) {
do_action('mailpoet_conflict_resolver_styles'); do_action('mailpoet_conflict_resolver_styles');
do_action('mailpoet_conflict_resolver_scripts'); do_action('mailpoet_conflict_resolver_scripts');
} }
$main_page_slug = 'mailpoet-newsletters'; // Main page
add_menu_page( add_menu_page(
'MailPoet', 'MailPoet',
'MailPoet', 'MailPoet',
Env::$required_permission, $this->user_capability,
$main_page_slug, self::MAIN_PAGE_SLUG,
null, null,
$this->assets_url . '/img/menu_icon.png', $this->assets_url . '/img/menu_icon.png',
30 30
); );
$newsletters_page = add_submenu_page( // Emails page
$main_page_slug, if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_EMAILS)) {
$this->setPageTitle(__('Emails', 'mailpoet')), $newsletters_page = add_submenu_page(
__('Emails', 'mailpoet'), self::MAIN_PAGE_SLUG,
Env::$required_permission, $this->setPageTitle(__('Emails', 'mailpoet')),
$main_page_slug, __('Emails', 'mailpoet'),
array( $this->user_capability,
$this, self::MAIN_PAGE_SLUG,
'newsletters' array(
) $this,
); 'newsletters'
)
);
// add limit per page to screen options // add limit per page to screen options
add_action('load-' . $newsletters_page, function() { add_action('load-' . $newsletters_page, function() {
add_screen_option('per_page', array( add_screen_option('per_page', array(
'label' => _x( 'label' => _x(
'Number of newsletters per page', 'Number of newsletters per page',
'newsletters per page (screen options)', 'newsletters per page (screen options)',
'mailpoet' 'mailpoet'
), ),
'option' => 'mailpoet_newsletters_per_page' 'option' => 'mailpoet_newsletters_per_page'
)); ));
}); });
$forms_page = add_submenu_page( // newsletter editor
$main_page_slug, add_submenu_page(
$this->setPageTitle(__('Forms', 'mailpoet')), true,
__('Forms', 'mailpoet'), $this->setPageTitle(__('Newsletter', 'mailpoet')),
Env::$required_permission, __('Newsletter Editor', 'mailpoet'),
'mailpoet-forms', $this->user_capability,
array( 'mailpoet-newsletter-editor',
$this, array(
'forms' $this,
) 'newletterEditor'
); )
// 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'
));
});
$subscribers_page = add_submenu_page( // Forms page
$main_page_slug, if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_FORMS)) {
$this->setPageTitle(__('Subscribers', 'mailpoet')), $forms_page = add_submenu_page(
__('Subscribers', 'mailpoet'), self::MAIN_PAGE_SLUG,
Env::$required_permission, $this->setPageTitle(__('Forms', 'mailpoet')),
'mailpoet-subscribers', __('Forms', 'mailpoet'),
array( $this->user_capability,
$this, 'mailpoet-forms',
'subscribers' array(
) $this,
); 'forms'
// 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'
));
});
$segments_page = add_submenu_page( // add limit per page to screen options
$main_page_slug, add_action('load-' . $forms_page, function() {
$this->setPageTitle(__('Lists', 'mailpoet')), add_screen_option('per_page', array(
__('Lists', 'mailpoet'), 'label' => _x(
Env::$required_permission, 'Number of forms per page',
'mailpoet-segments', 'forms per page (screen options)',
array( 'mailpoet'
$this, ),
'segments' 'option' => 'mailpoet_forms_per_page'
) ));
); });
// add limit per page to screen options // form editor
add_action('load-' . $segments_page, function() { add_submenu_page(
add_screen_option('per_page', array( true,
'label' => _x( $this->setPageTitle(__('Form Editor', 'mailpoet')),
'Number of segments per page', __('Form Editor', 'mailpoet'),
'segments per page (screen options)', $this->user_capability,
'mailpoet' 'mailpoet-form-editor',
), array(
'option' => 'mailpoet_segments_per_page' $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( add_submenu_page(
$main_page_slug, self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Settings', 'mailpoet')),
__('Settings', 'mailpoet'),
Env::$required_permission,
'mailpoet-settings',
array(
$this,
'settings'
)
);
add_submenu_page(
$main_page_slug,
$this->setPageTitle(__('Help', 'mailpoet')), $this->setPageTitle(__('Help', 'mailpoet')),
__('Help', 'mailpoet'), __('Help', 'mailpoet'),
Env::$required_permission, $this->user_capability,
'mailpoet-help', 'mailpoet-help',
array( array(
$this, $this,
@ -180,12 +260,13 @@ class Menu {
) )
); );
// Premium page
// Only show this page in menu if the Premium plugin is not activated // Only show this page in menu if the Premium plugin is not activated
add_submenu_page( add_submenu_page(
License::getLicense() ? true : $main_page_slug, License::getLicense() ? true : self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Premium', 'mailpoet')), $this->setPageTitle(__('Premium', 'mailpoet')),
__('Premium', 'mailpoet'), __('Premium', 'mailpoet'),
Env::$required_permission, $this->user_capability,
'mailpoet-premium', 'mailpoet-premium',
array( array(
$this, $this,
@ -193,35 +274,12 @@ class Menu {
) )
); );
add_submenu_page( // Welcome 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'
)
);
add_submenu_page( add_submenu_page(
true, true,
$this->setPageTitle(__('Welcome', 'mailpoet')), $this->setPageTitle(__('Welcome', 'mailpoet')),
__('Welcome', 'mailpoet'), __('Welcome', 'mailpoet'),
Env::$required_permission, $this->user_capability,
'mailpoet-welcome', 'mailpoet-welcome',
array( array(
$this, $this,
@ -229,23 +287,12 @@ class Menu {
) )
); );
add_submenu_page( // Update page
true,
$this->setPageTitle(__('Migration', 'mailpoet')),
'',
Env::$required_permission,
'mailpoet-migration',
array(
$this,
'migration'
)
);
add_submenu_page( add_submenu_page(
true, true,
$this->setPageTitle(__('Update', 'mailpoet')), $this->setPageTitle(__('Update', 'mailpoet')),
__('Update', 'mailpoet'), __('Update', 'mailpoet'),
Env::$required_permission, $this->user_capability,
'mailpoet-update', 'mailpoet-update',
array( array(
$this, $this,
@ -253,27 +300,16 @@ class Menu {
) )
); );
// Migration page
add_submenu_page( add_submenu_page(
true, true,
$this->setPageTitle(__('Form Editor', 'mailpoet')), $this->setPageTitle(__('Migration', 'mailpoet')),
__('Form Editor', 'mailpoet'), '',
Env::$required_permission, $this->user_capability,
'mailpoet-form-editor', 'mailpoet-migration',
array( array(
$this, $this,
'formEditor' 'migration'
)
);
add_submenu_page(
true,
$this->setPageTitle(__('Newsletter', 'mailpoet')),
__('Newsletter Editor', 'mailpoet'),
Env::$required_permission,
'mailpoet-newsletter-editor',
array(
$this,
'newletterEditor'
) )
); );
} }
@ -293,20 +329,20 @@ class Menu {
or or
strpos($redirect_url, 'mailpoet') === false 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( $data = array(
'settings' => Setting::getAll(), 'settings' => Setting::getAll(),
'current_user' => wp_get_current_user(), 'current_user' => wp_get_current_user(),
'redirect_url' => $redirect_url, 'redirect_url' => $redirect_url,
'sub_menu' => 'mailpoet-newsletters' 'sub_menu' => self::MAIN_PAGE_SLUG
); );
$this->displayPage('welcome.html', $data); $this->displayPage('welcome.html', $data);
} }
function migration() { function migration() {
$mp2_migrator = new MP2Migrator(); $mp2_migrator = new MP2Migrator($this->access_control);
$mp2_migrator->init(); $mp2_migrator->init();
$data = array( $data = array(
'log_file_url' => $mp2_migrator->log_file_url, 'log_file_url' => $mp2_migrator->log_file_url,
@ -328,14 +364,14 @@ class Menu {
or or
strpos($redirect_url, 'mailpoet') === false 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( $data = array(
'settings' => Setting::getAll(), 'settings' => Setting::getAll(),
'current_user' => wp_get_current_user(), 'current_user' => wp_get_current_user(),
'redirect_url' => $redirect_url, 'redirect_url' => $redirect_url,
'sub_menu' => 'mailpoet-newsletters' 'sub_menu' => self::MAIN_PAGE_SLUG
); );
$readme_file = Env::$path . '/readme.txt'; $readme_file = Env::$path . '/readme.txt';
@ -352,7 +388,7 @@ class Menu {
function premium() { function premium() {
$data = array( $data = array(
'subscriber_count' => Subscriber::getTotalSubscribers(), 'subscriber_count' => Subscriber::getTotalSubscribers(),
'sub_menu' => 'mailpoet-newsletters' 'sub_menu' => self::MAIN_PAGE_SLUG
); );
$this->displayPage('premium.html', $data); $this->displayPage('premium.html', $data);
@ -505,7 +541,7 @@ class Menu {
'shortcodes' => ShortcodesHelper::getShortcodes(), 'shortcodes' => ShortcodesHelper::getShortcodes(),
'settings' => Setting::getAll(), 'settings' => Setting::getAll(),
'current_wp_user' => Subscriber::getCurrentWPUser(), 'current_wp_user' => Subscriber::getCurrentWPUser(),
'sub_menu' => 'mailpoet-newsletters' 'sub_menu' => self::MAIN_PAGE_SLUG
); );
wp_enqueue_media(); wp_enqueue_media();
wp_enqueue_script('tinymce-wplink', includes_url('js/tinymce/plugins/wplink/plugin.js')); 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 * This error page is used when the initialization is failed
* to display admin notices only * to display admin notices only
*/ */
static function addErrorPage() { static function addErrorPage(AccessControl $access_control) {
if(!self::isOnMailPoetAdminPage()) { if(!self::isOnMailPoetAdminPage()) {
return false; return false;
} }
// Check if page already exists // Check if page already exists
if(get_plugin_page_hook($_REQUEST['page'], '') 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; return false;
} }
@ -611,9 +647,12 @@ class Menu {
true, true,
'MailPoet', 'MailPoet',
'MailPoet', 'MailPoet',
Env::$required_permission, $access_control->getUserFirstCapability(),
$_REQUEST['page'], $_REQUEST['page'],
array(__CLASS__, 'errorPageCallback') array(
__CLASS__,
'errorPageCallback'
)
); );
} }
@ -624,7 +663,7 @@ class Menu {
function checkMailPoetAPIKey(ServicesChecker $checker = null) { function checkMailPoetAPIKey(ServicesChecker $checker = null) {
if(self::isOnMailPoetAdminPage()) { if(self::isOnMailPoetAdminPage()) {
$show_notices = isset($_REQUEST['page']) $show_notices = isset($_REQUEST['page'])
&& stripos($_REQUEST['page'], 'mailpoet-newsletters') === false; && stripos($_REQUEST['page'], self::MAIN_PAGE_SLUG) === false;
$checker = $checker ?: new ServicesChecker(); $checker = $checker ?: new ServicesChecker();
$this->mp_api_key_valid = $checker->isMailPoetAPIKeyValid($show_notices); $this->mp_api_key_valid = $checker->isMailPoetAPIKeyValid($show_notices);
} }
@ -633,7 +672,7 @@ class Menu {
function checkPremiumKey(ServicesChecker $checker = null) { function checkPremiumKey(ServicesChecker $checker = null) {
if(self::isOnMailPoetAdminPage()) { if(self::isOnMailPoetAdminPage()) {
$show_notices = isset($_REQUEST['page']) $show_notices = isset($_REQUEST['page'])
&& stripos($_REQUEST['page'], 'mailpoet-newsletters') === false; && stripos($_REQUEST['page'], self::MAIN_PAGE_SLUG) === false;
$checker = $checker ?: new ServicesChecker(); $checker = $checker ?: new ServicesChecker();
$this->premium_key_valid = $checker->isPremiumKeyValid($show_notices); $this->premium_key_valid = $checker->isPremiumKeyValid($show_notices);
} }

View File

@ -3,7 +3,7 @@
namespace MailPoet\Form; namespace MailPoet\Form;
use MailPoet\API\JSON\API; use MailPoet\API\JSON\API;
use MailPoet\Config\Renderer; use MailPoet\Config\Renderer as ConfigRenderer;
use MailPoet\Form\Renderer as FormRenderer; use MailPoet\Form\Renderer as FormRenderer;
use MailPoet\Models\Form; use MailPoet\Models\Form;
use MailPoet\Util\Security; use MailPoet\Util\Security;
@ -174,7 +174,7 @@ class Widget extends \WP_Widget {
$data['api_version'] = API::CURRENT_VERSION; $data['api_version'] = API::CURRENT_VERSION;
// render form // render form
$renderer = new Renderer(); $renderer = new ConfigRenderer();
try { try {
$output = $renderer->render('form/widget.html', $data); $output = $renderer->render('form/widget.html', $data);
$output = do_shortcode($output); $output = do_shortcode($output);

View File

@ -1,6 +1,8 @@
<?php <?php
namespace MailPoet\Router\Endpoints; namespace MailPoet\Router\Endpoints;
use MailPoet\Config\AccessControl;
use MailPoet\Cron\CronHelper; use MailPoet\Cron\CronHelper;
use MailPoet\Cron\Daemon; use MailPoet\Cron\Daemon;
@ -17,6 +19,9 @@ class CronDaemon {
self::ACTION_PING_RESPONSE self::ACTION_PING_RESPONSE
); );
public $data; public $data;
public $permissions = array(
'global' => AccessControl::NO_ACCESS_RESTRICTION
);
function __construct($data) { function __construct($data) {
$this->data = $data; $this->data = $data;

View File

@ -1,6 +1,8 @@
<?php <?php
namespace MailPoet\Router\Endpoints; namespace MailPoet\Router\Endpoints;
use MailPoet\Config\AccessControl;
use MailPoet\Subscription as UserSubscription; use MailPoet\Subscription as UserSubscription;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
@ -16,6 +18,9 @@ class Subscription {
self::ACTION_UNSUBSCRIBE self::ACTION_UNSUBSCRIBE
); );
public $data; public $data;
public $permissions = array(
'global' => AccessControl::NO_ACCESS_RESTRICTION
);
function __construct($data) { function __construct($data) {
$this->data = $data; $this->data = $data;

View File

@ -1,6 +1,8 @@
<?php <?php
namespace MailPoet\Router\Endpoints; namespace MailPoet\Router\Endpoints;
use MailPoet\Config\AccessControl;
use MailPoet\Models\Newsletter; use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterLink; use MailPoet\Models\NewsletterLink;
use MailPoet\Models\SendingQueue; use MailPoet\Models\SendingQueue;
@ -20,6 +22,9 @@ class Track {
self::ACTION_OPEN self::ACTION_OPEN
); );
public $data; public $data;
public $permissions = array(
'global' => AccessControl::NO_ACCESS_RESTRICTION
);
function __construct($data) { function __construct($data) {
$this->data = $this->_processTrackData($data); $this->data = $this->_processTrackData($data);
@ -38,8 +43,8 @@ class Track {
function _processTrackData($data) { function _processTrackData($data) {
$data = (object)Links::transformUrlDataObject($data); $data = (object)Links::transformUrlDataObject($data);
if(empty($data->queue_id) || if(empty($data->queue_id) ||
empty($data->subscriber_id) || empty($data->subscriber_id) ||
empty($data->subscriber_token) empty($data->subscriber_token)
) { ) {
return false; return false;
} }

View File

@ -1,7 +1,8 @@
<?php <?php
namespace MailPoet\Router\Endpoints; namespace MailPoet\Router\Endpoints;
use MailPoet\Config\Env; use MailPoet\Config\AccessControl;
use MailPoet\Models\Newsletter; use MailPoet\Models\Newsletter;
use MailPoet\Models\SendingQueue; use MailPoet\Models\SendingQueue;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
@ -17,8 +18,12 @@ class ViewInBrowser {
const ACTION_VIEW = 'view'; const ACTION_VIEW = 'view';
public $allowed_actions = array(self::ACTION_VIEW); public $allowed_actions = array(self::ACTION_VIEW);
public $data; public $data;
public $permissions = array(
'global' => AccessControl::NO_ACCESS_RESTRICTION
);
function __construct($data) { function __construct($data, AccessControl $access_control) {
$this->access_control = $access_control;
$this->data = $this->_processBrowserPreviewData($data); $this->data = $this->_processBrowserPreviewData($data);
} }
@ -69,8 +74,8 @@ class ViewInBrowser {
$data->queue = false; $data->queue = false;
} }
// allow users with 'manage_options' permission to preview any newsletter // allow users with permission to manage emails to preview any newsletter
if(!empty($data->preview) && current_user_can(Env::$required_permission) if(!empty($data->preview) && $this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_EMAILS)
) return $data; ) return $data;
// allow others to preview newsletters only when newsletter hash is defined // allow others to preview newsletters only when newsletter hash is defined

View File

@ -1,6 +1,8 @@
<?php <?php
namespace MailPoet\Router; namespace MailPoet\Router;
use MailPoet\Config\AccessControl;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
@ -12,19 +14,21 @@ class Router {
public $data; public $data;
const NAME = 'mailpoet_router'; const NAME = 'mailpoet_router';
const RESPONSE_ERROR = 404; const RESPONSE_ERROR = 404;
const RESPONE_FORBIDDEN = 403;
function __construct($api_data = false) { function __construct(AccessControl $access_control, $api_data = false) {
$api_data = ($api_data) ? $api_data : $_GET; $api_data = ($api_data) ? $api_data : $_GET;
$this->api_request = isset($api_data[self::NAME]); $this->api_request = isset($api_data[self::NAME]);
$this->endpoint = isset($api_data['endpoint']) ? $this->endpoint = isset($api_data['endpoint']) ?
Helpers::underscoreToCamelCase($api_data['endpoint']) : Helpers::underscoreToCamelCase($api_data['endpoint']) :
false; false;
$this->action = isset($api_data['action']) ? $this->endpoint_action = isset($api_data['action']) ?
Helpers::underscoreToCamelCase($api_data['action']) : Helpers::underscoreToCamelCase($api_data['action']) :
false; false;
$this->data = isset($api_data['data']) ? $this->data = isset($api_data['data']) ?
self::decodeRequestData($api_data['data']) : self::decodeRequestData($api_data['data']) :
false; false;
$this->access_control = $access_control;
} }
function init() { function init() {
@ -33,15 +37,18 @@ class Router {
if(!$this->endpoint || !class_exists($endpoint_class)) { if(!$this->endpoint || !class_exists($endpoint_class)) {
return $this->terminateRequest(self::RESPONSE_ERROR, __('Invalid router endpoint', 'mailpoet')); return $this->terminateRequest(self::RESPONSE_ERROR, __('Invalid router endpoint', 'mailpoet'));
} }
$endpoint = new $endpoint_class($this->data); $endpoint = new $endpoint_class($this->data, $this->access_control);
if(!method_exists($endpoint, $this->action) || !in_array($this->action, $endpoint->allowed_actions)) { 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')); 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'); do_action('mailpoet_conflict_resolver_router_url_query_parameters');
return call_user_func( return call_user_func(
array( array(
$endpoint, $endpoint,
$this->action $this->endpoint_action
) )
); );
} }
@ -74,4 +81,11 @@ class Router {
status_header($code, $message); status_header($code, $message);
exit; 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']);
}
} }

View File

@ -2,14 +2,15 @@
namespace MailPoet\Subscription; namespace MailPoet\Subscription;
use MailPoet\API\JSON\API; use MailPoet\API\API as API;
use MailPoet\API\JSON\Response as APIResponse; use MailPoet\API\JSON\Response as APIResponse;
use MailPoet\Config\AccessControl;
use MailPoet\Util\Url as UrlHelper; use MailPoet\Util\Url as UrlHelper;
class Form { class Form {
static function onSubmit($request_data = false) { static function onSubmit($request_data = false) {
$request_data = ($request_data) ? $request_data : $_REQUEST; $request_data = ($request_data) ? $request_data : $_REQUEST;
$api = new API(); $api = API::JSON(new AccessControl());
$api->setRequestData($request_data); $api->setRequestData($request_data);
$form_id = (!empty($request_data['data']['form_id'])) ? (int)$request_data['data']['form_id'] : false; $form_id = (!empty($request_data['data']['form_id'])) ? (int)$request_data['data']['form_id'] : false;
$response = $api->processRoute(); $response = $api->processRoute();

View File

@ -2,10 +2,11 @@
namespace MailPoet\Test\API; namespace MailPoet\Test\API;
use MailPoet\API\API; use MailPoet\API\API;
use MailPoet\Config\AccessControl;
class APITest extends \MailPoetTest { class APITest extends \MailPoetTest {
function testItCallsJSONAPI() { function testItCallsJSONAPI() {
expect(API::JSON())->isInstanceOf('MailPoet\API\JSON\API'); expect(API::JSON(new AccessControl()))->isInstanceOf('MailPoet\API\JSON\API');
} }
function testItCallsMPAPI() { function testItCallsMPAPI() {

View File

@ -1,13 +1,19 @@
<?php <?php
namespace MailPoet\Test\API\JSON; namespace MailPoet\Test\API\JSON;
use Codeception\Util\Stub; use Codeception\Util\Stub;
use MailPoet\API\JSON\API; use Helper\WordPressHooks as WPHooksHelper;
use MailPoet\API\JSON\SuccessResponse; use MailPoet\API\API;
use MailPoet\API\JSON\API as JSONAPI;
use MailPoet\API\JSON\Response;
use MailPoet\API\JSON\Response as APIResponse; use MailPoet\API\JSON\Response as APIResponse;
use MailPoet\API\JSON\SuccessResponse;
use MailPoet\Config\AccessControl;
use MailPoet\WP\Hooks;
// required to be able to use wp_delete_user() // required to be able to use wp_delete_user()
require_once(ABSPATH.'wp-admin/includes/user.php'); require_once(ABSPATH . 'wp-admin/includes/user.php');
require_once('APITestNamespacedEndpointStubV1.php'); require_once('APITestNamespacedEndpointStubV1.php');
require_once('APITestNamespacedEndpointStubV2.php'); require_once('APITestNamespacedEndpointStubV2.php');
@ -22,30 +28,16 @@ class APITest extends \MailPoetTest {
} else { } else {
$this->wp_user_id = $wp_user_id; $this->wp_user_id = $wp_user_id;
} }
$this->api = API::JSON(new AccessControl());
$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();
} }
function testItCallsAPISetupAction() { function testItCallsAPISetupAction() {
$called = false; $called = false;
add_action( Hooks::addAction(
'mailpoet_api_setup', 'mailpoet_api_setup',
function ($api) use (&$called) { function($api) use (&$called) {
$called = true; $called = true;
expect($api instanceof API)->true(); expect($api instanceof JSONAPI)->true();
} }
); );
$api = Stub::makeEmptyExcept( $api = Stub::makeEmptyExcept(
@ -136,11 +128,101 @@ class APITest extends \MailPoetTest {
); );
$this->api->setRequestData($data); $this->api->setRequestData($data);
$response = $this->api->processRoute(); $response = $this->api->processRoute();
expect($response->getData()['data'])->equals($data['api_version']); 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() { function _after() {
WPHooksHelper::releaseAllHooks();
wp_delete_user($this->wp_user_id); wp_delete_user($this->wp_user_id);
} }
} }

View File

@ -3,16 +3,24 @@
namespace MailPoet\API\JSON\v1; namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Access as APIAccess; use MailPoet\Config\AccessControl;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class APITestNamespacedEndpointStubV1 extends APIEndpoint { class APITestNamespacedEndpointStubV1 extends APIEndpoint {
public $permissions = array( 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) { function test($data) {
return $this->successResponse($data); return $this->successResponse($data);
} }
function restricted($data) {
return $this->successResponse($data);
}
} }

View File

@ -2,14 +2,17 @@
namespace MailPoet\API\JSON\v2; namespace MailPoet\API\JSON\v2;
use MailPoet\API\JSON\Access as APIAccess;
use MailPoet\API\JSON\Endpoint as APIEndpoint; use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Config\AccessControl;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class APITestNamespacedEndpointStubV2 extends APIEndpoint { class APITestNamespacedEndpointStubV2 extends APIEndpoint {
public $permissions = array( public $permissions = array(
'testVersion' => APIAccess::ALL 'global' => AccessControl::NO_ACCESS_RESTRICTION,
'methods' => array(
'test' => AccessControl::NO_ACCESS_RESTRICTION
)
); );
function testVersion() { function testVersion() {

View File

@ -0,0 +1,115 @@
<?php
namespace MailPoet\Test\Config;
use Helper\WordPressHooks as WPHooksHelper;
use MailPoet\Config\AccessControl;
use MailPoet\WP\Hooks;
class AccessControlTest extends \MailPoetTest {
function testItSetsDefaultPermissionsUponInitialization() {
$default_permissions = array(
AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN => 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();
}
}

View File

@ -1,7 +1,9 @@
<?php <?php
namespace MailPoet\Test\Config; namespace MailPoet\Test\Config;
use Codeception\Util\Stub; use Codeception\Util\Stub;
use MailPoet\Config\AccessControl;
use MailPoet\Config\Menu; use MailPoet\Config\Menu;
use MailPoet\Config\Renderer; use MailPoet\Config\Renderer;
use MailPoet\Config\ServicesChecker; use MailPoet\Config\ServicesChecker;
@ -39,7 +41,7 @@ class MenuTest extends \MailPoetTest {
function testItChecksMailpoetAPIKey() { function testItChecksMailpoetAPIKey() {
$renderer = Stub::make(new Renderer()); $renderer = Stub::make(new Renderer());
$assets_url = ''; $assets_url = '';
$menu = new Menu($renderer, $assets_url); $menu = new Menu($renderer, $assets_url, new AccessControl());
$_REQUEST['page'] = 'mailpoet-newsletters'; $_REQUEST['page'] = 'mailpoet-newsletters';
$checker = Stub::make( $checker = Stub::make(
@ -62,7 +64,7 @@ class MenuTest extends \MailPoetTest {
function testItChecksPremiumKey() { function testItChecksPremiumKey() {
$renderer = Stub::make(new Renderer()); $renderer = Stub::make(new Renderer());
$assets_url = ''; $assets_url = '';
$menu = new Menu($renderer, $assets_url); $menu = new Menu($renderer, $assets_url, new AccessControl());
$_REQUEST['page'] = 'mailpoet-newsletters'; $_REQUEST['page'] = 'mailpoet-newsletters';
$checker = Stub::make( $checker = Stub::make(

View File

@ -1,7 +1,9 @@
<?php <?php
namespace MailPoet\Test\Router\Endpoints; namespace MailPoet\Test\Router\Endpoints;
use Codeception\Util\Stub; use Codeception\Util\Stub;
use MailPoet\Config\AccessControl;
use MailPoet\Models\Newsletter; use MailPoet\Models\Newsletter;
use MailPoet\Models\SendingQueue; use MailPoet\Models\SendingQueue;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
@ -33,7 +35,7 @@ class ViewInBrowserTest extends \MailPoetTest {
'preview' => false 'preview' => false
); );
// instantiate class // 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() { function testItAbortsWhenBrowserPreviewDataIsMissing() {
@ -123,6 +125,7 @@ class ViewInBrowserTest extends \MailPoetTest {
} }
function testItDoesNotRequireWpAdministratorToBeOnProcessedListWhenPreviewIsEnabled() { function testItDoesNotRequireWpAdministratorToBeOnProcessedListWhenPreviewIsEnabled() {
$view_in_browser = $this->view_in_browser;
$data = (object)array_merge( $data = (object)array_merge(
$this->browser_preview_data, $this->browser_preview_data,
array( array(
@ -132,19 +135,25 @@ class ViewInBrowserTest extends \MailPoetTest {
) )
); );
$data->preview = true; $data->preview = true;
// when WP user is not logged, false should be returned // 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 // when WP user is logged in but does not have 'manage options' permission, false should be returned
wp_set_current_user(1); wp_set_current_user(1);
$wp_user = wp_get_current_user(); $wp_user = wp_get_current_user();
$wp_user->remove_role('administrator'); $wp_user->remove_role('administrator');
$view_in_browser->access_control = new AccessControl();
expect($this->view_in_browser->_validateBrowserPreviewData($data))->false(); expect($this->view_in_browser->_validateBrowserPreviewData($data))->false();
// when WP user is logged and has 'manage options' permission, data should be returned // when WP user is logged and has 'manage options' permission, data should be returned
$wp_user->add_role('administrator'); $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() { function testItSetsSubscriberToLoggedInWPUserWhenPreviewIsEnabled() {
$view_in_browser = $this->view_in_browser;
$data = (object)array_merge( $data = (object)array_merge(
$this->browser_preview_data, $this->browser_preview_data,
array( array(
@ -155,7 +164,8 @@ class ViewInBrowserTest extends \MailPoetTest {
); );
$data->preview = true; $data->preview = true;
wp_set_current_user(1); 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); expect($result->subscriber->id)->equals(1);
} }

View File

@ -1,24 +1,26 @@
<?php <?php
namespace MailPoet\Test\Router; namespace MailPoet\Test\Router;
use Codeception\Util\Stub; use Codeception\Util\Stub;
use MailPoet\Config\AccessControl;
use MailPoet\Router\Router; use MailPoet\Router\Router;
require_once('RouterTestMockEndpoint.php'); require_once('RouterTestMockEndpoint.php');
class RouterTest extends \MailPoetTest { class RouterTest extends \MailPoetTest {
public $access_control;
public $router_data; public $router_data;
public $router;
function __construct() { function _before() {
parent::__construct();
$this->router_data = array( $this->router_data = array(
Router::NAME => '', Router::NAME => '',
'endpoint' => 'router_test_mock_endpoint', 'endpoint' => 'router_test_mock_endpoint',
'action' => 'test', 'action' => 'test',
'data' => base64_encode(json_encode(array('data' => 'dummy data'))) '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() { function testItCanGetAPIDataFromGetRequest() {
@ -26,10 +28,10 @@ class RouterTest extends \MailPoetTest {
$url = 'http://example.com/?' . Router::NAME . '&endpoint=view_in_browser&action=view&data=' $url = 'http://example.com/?' . Router::NAME . '&endpoint=view_in_browser&action=view&data='
. base64_encode(json_encode($data)); . base64_encode(json_encode($data));
parse_str(parse_url($url, PHP_URL_QUERY), $_GET); 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->api_request)->equals(true);
expect($router->endpoint)->equals('viewInBrowser'); expect($router->endpoint)->equals('viewInBrowser');
expect($router->action)->equals('view'); expect($router->endpoint_action)->equals('view');
expect($router->data)->equals($data); expect($router->data)->equals($data);
} }
@ -37,8 +39,8 @@ class RouterTest extends \MailPoetTest {
$router_data = $this->router_data; $router_data = $this->router_data;
unset($router_data[Router::NAME]); unset($router_data[Router::NAME]);
$router = Stub::construct( $router = Stub::construct(
new Router(), '\MailPoet\Router\Router',
array($router_data) array($this->access_control, $router_data)
); );
$result = $router->init(); $result = $router->init();
expect($result)->null(); expect($result)->null();
@ -48,8 +50,8 @@ class RouterTest extends \MailPoetTest {
$router_data = $this->router_data; $router_data = $this->router_data;
$router_data['endpoint'] = 'invalid_endpoint'; $router_data['endpoint'] = 'invalid_endpoint';
$router = Stub::construct( $router = Stub::construct(
new Router(), '\MailPoet\Router\Router',
array($router_data), array($this->access_control, $router_data),
array( array(
'terminateRequest' => function($code, $error) { 'terminateRequest' => function($code, $error) {
return array( return array(
@ -72,8 +74,8 @@ class RouterTest extends \MailPoetTest {
$router_data = $this->router_data; $router_data = $this->router_data;
$router_data['action'] = 'invalid_action'; $router_data['action'] = 'invalid_action';
$router = Stub::construct( $router = Stub::construct(
new Router(), '\MailPoet\Router\Router',
array($router_data), array($this->access_control, $router_data),
array( array(
'terminateRequest' => function($code, $error) { 'terminateRequest' => function($code, $error) {
return array( 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() { function testItCallsEndpointAction() {
$data = array('data' => 'dummy data'); $data = array('data' => 'dummy data');
$result = $this->router->init(); $result = $this->router->init();
@ -99,8 +182,7 @@ class RouterTest extends \MailPoetTest {
} }
function testItExecutesUrlParameterConflictResolverAction() { function testItExecutesUrlParameterConflictResolverAction() {
$data = array('data' => 'dummy data'); $this->router->init();
$result = $this->router->init();
expect((boolean)did_action('mailpoet_conflict_resolver_router_url_query_parameters'))->true(); expect((boolean)did_action('mailpoet_conflict_resolver_router_url_query_parameters'))->true();
} }

View File

@ -2,12 +2,17 @@
namespace MailPoet\Router\Endpoints; namespace MailPoet\Router\Endpoints;
use MailPoet\Config\AccessControl;
class RouterTestMockEndpoint { class RouterTestMockEndpoint {
const ACTION_TEST = 'test'; const ACTION_TEST = 'test';
public $allowed_actions = array( public $allowed_actions = array(
self::ACTION_TEST self::ACTION_TEST
); );
public $data; public $data;
public $permissions = array(
'global' => AccessControl::NO_ACCESS_RESTRICTION
);
function __construct($data) { function __construct($data) {
$this->data = $data; $this->data = $data;