Skip to content

Commit

Permalink
Secure email passwords (close #27)
Browse files Browse the repository at this point in the history
  • Loading branch information
0m3r committed Apr 5, 2023
1 parent 3df9752 commit e53ad88
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 1 deletion.
21 changes: 21 additions & 0 deletions Api/EncryptorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
namespace Swissup\Email\Api;

interface EncryptorInterface
{
/**
* Encrypt a string
*
* @param string $data
* @return string
*/
public function encrypt($data);

/**
* Decrypt a string
*
* @param string $data
* @return string
*/
public function decrypt($data);
}
23 changes: 23 additions & 0 deletions Api/ServiceEncryptorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
namespace Swissup\Email\Api;

use Swissup\Email\Api\Data\ServiceInterface;

interface ServiceEncryptorInterface
{
/**
* Encrypt a string
*
* @param string $data
* @return string
*/
public function encrypt(ServiceInterface $object): void;

/**
* Decrypt a string
*
* @param string $data
* @return string
*/
public function decrypt(ServiceInterface $object): void;
}
76 changes: 76 additions & 0 deletions Model/Data/Encryptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php
namespace Swissup\Email\Model\Data;

use Swissup\Email\Api\EncryptorInterface;

class Encryptor implements EncryptorInterface
{
const PREFIX = 'encrypted:';

/**
* @var \Magento\Framework\Encryption\EncryptorInterface
*/
private $encryptor;

/**
* Constructor
*
* @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
*/
public function __construct(
\Magento\Framework\Encryption\EncryptorInterface $encryptor
) {
$this->encryptor = $encryptor;
}

private function isEncryptionEnabled()
{
return true;
}

/**
* Encrypt a string
*
* @param string $data
* @return string
*/
public function encrypt($data)
{
$isEncryptionEnabled = $this->isEncryptionEnabled();
$data = (string) $data;
if ($isEncryptionEnabled) {
$data = self::PREFIX . $this->encryptor->encrypt($data);
}
return $data;
}

/**
* @param string $data
* @return bool
*/
private function isEncryptedData(string $data)
{
return strpos($data, self::PREFIX) === 0;
}

/**
* Decrypt a string
*
* @param string $data
* @return string
*/
public function decrypt($data)
{
$isEncryptionEnabled = $this->isEncryptionEnabled();
if ($isEncryptionEnabled) {
$data = (string) $data;
$prefix = self::PREFIX;
if ($this->isEncryptedData($data)) {
$data = substr($data, strlen($prefix));
$data = $this->encryptor->decrypt($data);
}
}

return $data;
}
}
62 changes: 62 additions & 0 deletions Model/Service/Encryptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
namespace Swissup\Email\Model\Service;

use Swissup\Email\Api\EncryptorInterface;
use Swissup\Email\Api\ServiceEncryptorInterface;
use Swissup\Email\Api\Data\ServiceInterface;

class Encryptor implements ServiceEncryptorInterface
{
/**
* @var EncryptorInterface
*/
private $encryptor;

/**
* @param EncryptorInterface $encryptor
*/
public function __construct(EncryptorInterface $encryptor)
{
$this->encryptor = $encryptor;
}

/**
* @return string[]
*/
private function getAttributes()
{
return ['password'];
}

/**
* @param DataObject $object
* @return void
*/
public function encrypt(ServiceInterface $object): void
{
foreach ($this->getAttributes() as $attributeCode) {
$value = $object->getData($attributeCode);
if ($value) {
$object->setData($attributeCode, $this->encryptor->encrypt($value));
}
}
}

/**
* @param DataObject $object
* @return void
*/
public function decrypt(ServiceInterface $object): void
{
foreach ($this->getAttributes() as $attributeCode) {
$value = $object->getData($attributeCode);
if ($value) {
try {
$object->setData($attributeCode, $this->encryptor->decrypt($value));
} catch (\Exception $e) {
// value is not encrypted or something wrong with encrypted data
}
}
}
}
}
38 changes: 38 additions & 0 deletions Plugin/Model/ServicePlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);

namespace Swissup\Email\Plugin\Model;

use Magento\Framework\DataObject;
use Swissup\Email\Model\Service;

class ServicePlugin
{
/**
* @var \Swissup\Email\Api\ServiceEncryptorInterface
*/
private $serviceEncryptor;

/**
* @param \Swissup\Email\Api\ServiceEncryptorInterface $serviceEncryptor
*/
public function __construct(\Swissup\Email\Api\ServiceEncryptorInterface $serviceEncryptor)
{
$this->serviceEncryptor = $serviceEncryptor;
}

public function afterBeforeSave(Service $subject): void
{
$this->serviceEncryptor->encrypt($subject);
}

public function afterAfterSave(Service $subject): void
{
$this->serviceEncryptor->decrypt($subject);
}

public function afterAfterLoad(Service $subject): void
{
$this->serviceEncryptor->decrypt($subject);
}
}
76 changes: 76 additions & 0 deletions Setup/Patch/Data/EncryptPasswords.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Swissup\Email\Setup\Patch\Data;


use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchVersionInterface;

class EncryptPasswords implements DataPatchInterface, PatchVersionInterface
{
/**
* @var ModuleDataSetupInterface
*/
private $moduleDataSetup;

/**
* @var \Swissup\Email\Model\Service\CollectionFactory
*/
private $serviceCollectionFactory;

/**
* @param ModuleDataSetupInterface $moduleDataSetup
* @param \Swissup\Email\Model\Service\CollectionFactory $serviceCollectionFactory
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
\Swissup\Email\Model\Service\CollectionFactory $serviceCollectionFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->serviceCollectionFactory = $serviceCollectionFactory;
}

/**
* {@inheritdoc}
*/
public function apply()
{
$setup = $this->moduleDataSetup;
$setup->startSetup();

$collection = $this->serviceCollectionFactory->create();
foreach ($collection as $service) {
$service->load($service->getId());
$service->setDataChanges(true);
$service->save();
}

$setup->endSetup();
}

/**
* {@inheritdoc}
*/
public static function getDependencies()
{
return [];
}

/**
* {@inheritdoc}
*/
public static function getVersion()
{
return '2.3.0';
}

/**
* {@inheritdoc}
*/
public function getAliases()
{
return [];
}
}
6 changes: 6 additions & 0 deletions etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
<preference for="Magento\Framework\Mail\TransportInterface" type="Swissup\Email\Mail\Transport"/>

<preference for="Magento\Framework\Mail\EmailMessageInterface" type="Swissup\Email\Mail\EmailMessage"/>
<preference for="Swissup\Email\Api\EncryptorInterface" type="Swissup\Email\Model\Data\Encryptor" />
<preference for="Swissup\Email\Api\ServiceEncryptorInterface" type="Swissup\Email\Model\Service\Encryptor" />
<!--
<type name="Magento\Framework\Mail\TransportInterface">
<plugin name="swissup_email_transport" type="Swissup\Email\Mail\TransportPlugin" sortOrder="1" disabled="false"/>
Expand Down Expand Up @@ -44,4 +46,8 @@
<argument name="filterPool" xsi:type="object" shared="false">SwissupEmailServiceGridFilterPool</argument>
</arguments>
</virtualType>

<type name="Swissup\Email\Model\Service">
<plugin name="SecureProtectedDataPlugin" type="Swissup\Email\Plugin\Model\ServicePlugin" sortOrder="10"/>
</type>
</config>
2 changes: 1 addition & 1 deletion etc/module.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Swissup_Email" setup_version="1.1.0">
<module name="Swissup_Email" setup_version="1.7.4">
<sequence>
<module name="Magento_Email"/>
<module name="Magento_Backend"/>
Expand Down

0 comments on commit e53ad88

Please sign in to comment.