Add SPF check API method to backend [MAILPOET-2295]

This commit is contained in:
wxa
2019-09-02 09:46:48 +03:00
committed by Jack Kitterhing
parent e72ea01f68
commit f67e356202
4 changed files with 103 additions and 0 deletions

View File

@ -8,12 +8,17 @@ use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl; use MailPoet\Config\AccessControl;
use MailPoet\Config\Installer; use MailPoet\Config\Installer;
use MailPoet\Services\Bridge; use MailPoet\Services\Bridge;
use MailPoet\Services\SPFCheck;
use MailPoet\Settings\SettingsController;
use MailPoet\WP\DateTime; use MailPoet\WP\DateTime;
use MailPoet\WP\Functions as WPFunctions; use MailPoet\WP\Functions as WPFunctions;
class Services extends APIEndpoint { class Services extends APIEndpoint {
public $bridge; public $bridge;
public $date_time; public $date_time;
public $settings;
public $spf_check;
public $permissions = [ public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS, 'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
]; ];
@ -21,6 +26,24 @@ class Services extends APIEndpoint {
function __construct() { function __construct() {
$this->bridge = new Bridge(); $this->bridge = new Bridge();
$this->date_time = new DateTime(); $this->date_time = new DateTime();
$this->settings = new SettingsController();
$this->spf_check = new SPFCheck();
}
function checkSPFRecord($data = []) {
$sender_address = $this->settings->get('sender.address');
$domain_name = mb_substr($sender_address, mb_strpos($sender_address, '@') + 1);
$result = $this->spf_check->checkSPFRecord($domain_name);
if (!$result) {
return $this->errorResponse(
[APIError::BAD_REQUEST => WPFunctions::get()->__('SPF check has failed.', 'mailpoet')],
['sender_address' => $sender_address, 'domain_name' => $domain_name]
);
}
return $this->successResponse();
} }
function checkMSSKey($data = []) { function checkMSSKey($data = []) {

31
lib/Services/SPFCheck.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace MailPoet\Services;
class SPFCheck {
function checkSPFRecord($domain) {
$record = $this->getSPFRecord($domain);
if (empty($record)) {
return true;
}
return strpos($record, 'include:spf.sendingservice.net') !== false;
}
private function getSPFRecord($domain) {
$records = $this->dnsGetRecord($domain, DNS_TXT);
if (empty($records[0])) {
return false;
}
foreach ($records as $record) {
if (empty($record['txt']) || !preg_match('/^v=spf1/', trim($record['txt']))) {
continue;
}
return $record['txt'];
}
return false;
}
protected function dnsGetRecord($domain, $type) {
return dns_get_record($domain, $type);
}
}

View File

@ -7,6 +7,7 @@ use MailPoet\API\JSON\v1\Services;
use MailPoet\API\JSON\Response as APIResponse; use MailPoet\API\JSON\Response as APIResponse;
use MailPoet\Config\Installer; use MailPoet\Config\Installer;
use MailPoet\Services\Bridge; use MailPoet\Services\Bridge;
use MailPoet\Services\SPFCheck;
use MailPoet\Settings\SettingsController; use MailPoet\Settings\SettingsController;
class ServicesTest extends \MailPoetTest { class ServicesTest extends \MailPoetTest {
@ -20,6 +21,30 @@ class ServicesTest extends \MailPoetTest {
$this->settings = new SettingsController(); $this->settings = new SettingsController();
} }
function testItRespondsWithErrorIfSPFCheckFails() {
$email = 'spf_test@example.com';
$this->settings->set('sender.address', $email);
$this->services_endpoint->spf_check = Stub::make(
SPFCheck::class,
['checkSPFRecord' => false],
$this
);
$response = $this->services_endpoint->checkSPFRecord([]);
expect($response->status)->equals(APIResponse::STATUS_NOT_FOUND);
expect($response->meta['sender_address'])->equals($email);
expect($response->meta['domain_name'])->equals('example.com');
}
function testItRespondsWithSuccessIfSPFCheckPasses() {
$this->services_endpoint->spf_check = Stub::make(
SPFCheck::class,
['checkSPFRecord' => true],
$this
);
$response = $this->services_endpoint->checkSPFRecord([]);
expect($response->status)->equals(APIResponse::STATUS_OK);
}
function testItRespondsWithErrorIfNoMSSKeyIsGiven() { function testItRespondsWithErrorIfNoMSSKeyIsGiven() {
$response = $this->services_endpoint->checkMSSKey(['key' => '']); $response = $this->services_endpoint->checkMSSKey(['key' => '']);
expect($response->status)->equals(APIResponse::STATUS_BAD_REQUEST); expect($response->status)->equals(APIResponse::STATUS_BAD_REQUEST);

View File

@ -0,0 +1,24 @@
<?php
namespace MailPoet\Services;
class SPFCheckTest extends \MailPoetUnitTest {
function testItChecksSPFRecord() {
$domain = 'example.com';
// Failed to get DNS records
$response = false;
$check = $this->make(SPFCheck::class, ['dnsGetRecord' => $response]);
expect($check->checkSPFRecord($domain))->equals(true);
// No SPF record
$response = [['txt' => '123'], ['txt' => 'abc']];
$check = $this->make(SPFCheck::class, ['dnsGetRecord' => $response]);
expect($check->checkSPFRecord($domain))->equals(true);
// Good SPF record
$response = [['txt' => 'v=spf1 include:spf.protection.outlook.com include:sendgrid.net include:spf.sendingservice.net -all']];
$check = $this->make(SPFCheck::class, ['dnsGetRecord' => $response]);
expect($check->checkSPFRecord($domain))->equals(true);
// Bad SPF record
$response = [['txt' => 'v=spf1 include:spf.protection.outlook.com include:sendgrid.net -all']];
$check = $this->make(SPFCheck::class, ['dnsGetRecord' => $response]);
expect($check->checkSPFRecord($domain))->equals(false);
}
}