Commit 63f88730 authored by François Agneray's avatar François Agneray
Browse files

Add encryption key (metamodel database password)

parent de086636
......@@ -24,11 +24,13 @@ final class DatabaseAction
private $logger;
private $em;
private $encryptionKey;
public function __construct(LoggerInterface $logger, EntityManagerInterface $em)
public function __construct(LoggerInterface $logger, EntityManagerInterface $em, string $encryptionKey)
{
$this->logger = $logger;
$this->em = $em;
$this->encryptionKey = $encryptionKey;
}
public function __invoke(Request $request, Response $response, array $args): Response
......@@ -89,7 +91,7 @@ final class DatabaseAction
$database->setHost($parsedBody['dbhost']);
$database->setPort($parsedBody['dbport']);
$database->setLogin($parsedBody['dblogin']);
$database->setPassword($parsedBody['dbpassword']);
$database->setPassword($this->encryptData($parsedBody['dbpassword']));
$this->em->flush();
}
}
......@@ -24,11 +24,13 @@ final class DatabaseListAction
private $logger;
private $em;
private $encryptionKey;
public function __construct(LoggerInterface $logger, EntityManagerInterface $em)
public function __construct(LoggerInterface $logger, EntityManagerInterface $em, string $encryptionKey)
{
$this->logger = $logger;
$this->em = $em;
$this->encryptionKey = $encryptionKey;
}
public function __invoke(Request $request, Response $response, array $args): Response
......@@ -74,7 +76,7 @@ final class DatabaseListAction
$database->setHost($parsedBody['dbhost']);
$database->setPort($parsedBody['dbport']);
$database->setLogin($parsedBody['dblogin']);
$database->setPassword($parsedBody['dbpassword']);
$database->setPassword($this->encryptData($parsedBody['dbpassword']));
$this->em->persist($database);
$this->em->flush();
......
......@@ -56,6 +56,14 @@ final class SearchDataAction
* @var AnisQueryBuilderFactory
*/
private $aqbf;
/**
* The encryption key used by anis to encrypt and decrypt sensitive data like passwords
* This key is provided by configuration (see the config file)
*
* @var string
*/
private $encryptionKey;
/**
* This class is creates before call __invoke to execute the action
......@@ -69,12 +77,14 @@ final class SearchDataAction
LoggerInterface $logger,
EntityManagerInterface $em,
DBALConnectionFactory $dcf,
AnisQueryBuilderFactory $aqbf
AnisQueryBuilderFactory $aqbf,
string $encryptionKey
) {
$this->logger = $logger;
$this->em = $em;
$this->dcf = $dcf;
$this->aqbf = $aqbf;
$this->encryptionKey = $encryptionKey;
}
/**
......@@ -123,7 +133,8 @@ final class SearchDataAction
}
$database = $dataset->getProject()->getDatabase();
$connection = $this->dcf->create($database);
$decryptedPassword = $this->decryptData($database->getPassword());
$connection = $this->dcf->create($database, $decryptedPassword);
$queryBuilder = $connection->createQueryBuilder();
$anisQueryBuilder = $this->aqbf->create($queryBuilder, $dataset);
......
......@@ -56,6 +56,14 @@ final class SearchMetaAction
* @var AnisQueryBuilderFactory
*/
private $aqbf;
/**
* The encryption key used by anis to encrypt and decrypt sensitive data like passwords
* This key is provided by configuration (see the config file)
*
* @var string
*/
private $encryptionKey;
/**
* This class is creates before call __invoke to execute the action
......@@ -69,12 +77,14 @@ final class SearchMetaAction
LoggerInterface $logger,
EntityManagerInterface $em,
DBALConnectionFactory $dcf,
AnisQueryBuilderFactory $aqbf
AnisQueryBuilderFactory $aqbf,
string $encryptionKey
) {
$this->logger = $logger;
$this->em = $em;
$this->dcf = $dcf;
$this->aqbf = $aqbf;
$this->encryptionKey = $encryptionKey;
}
/**
......@@ -113,7 +123,8 @@ final class SearchMetaAction
}
$database = $dataset->getProject()->getDatabase();
$connection = $this->dcf->create($database);
$decryptedPassword = $this->decryptData($database->getPassword());
$connection = $this->dcf->create($database, $decryptedPassword);
$queryBuilder = $connection->createQueryBuilder();
$anisQueryBuilder = $this->aqbf->create($queryBuilder, $dataset);
......
......@@ -32,4 +32,21 @@ trait ActionTrait
'error_description' => $error_description
)));
}
protected function encryptData(string $data): string
{
// Generate an initialization vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
// Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $this->encryptionKey, 0, $iv);
// The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
return base64_encode($encrypted . '::' . $iv);
}
protected function decryptData(string $data): string
{
// To decrypt, split the encrypted data from our IV - our unique separator used was "::"
list($encryptedData, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encryptedData, 'aes-256-cbc', $this->encryptionKey, 0, $iv);
}
}
......@@ -31,16 +31,17 @@ class DBALConnectionFactory
* the metamodel
*
* @param Database $database This object contains the database connection parameters
* @param string $decryptedPassword This string contains the decrypted password for the database connection
*
* @return Connection
*/
public static function create(Database $database): Connection
public static function create(Database $database, string $decryptedPassword): Connection
{
$config = new \Doctrine\DBAL\Configuration();
$connectionParams = array(
'dbname' => $database->getDbName(),
'user' => $database->getLogin(),
'password' => $database->getPassword(),
'password' => $decryptedPassword,
'host' => $database->getHost(),
'port' => $database->getPort(),
'driver' => $database->getType(),
......
......@@ -22,7 +22,7 @@ $container['em'] = function ($c) {
$settings = $c->get('settings');
$metadata = $settings['metadata'];
$c = \Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration(
$metadata['entity_path'],
array('src/Entity'),
$metadata['dev_mode']
);
$c->setProxyDir($metadata['path_proxy']);
......@@ -130,11 +130,13 @@ $container['App\Action\Settings\OptionAction'] = function ($c) {
// Metamodel actions
$container['App\Action\Meta\DatabaseListAction'] = function ($c) {
return new App\Action\Meta\DatabaseListAction($c->get('logger'), $c->get('em'));
$settings = $c->get('settings');
return new App\Action\Meta\DatabaseListAction($c->get('logger'), $c->get('em'), $settings['encryption_key']);
};
$container['App\Action\Meta\DatabaseAction'] = function ($c) {
return new App\Action\Meta\DatabaseAction($c->get('logger'), $c->get('em'));
$settings = $c->get('settings');
return new App\Action\Meta\DatabaseAction($c->get('logger'), $c->get('em'), $settings['encryption_key']);
};
$container['App\Action\Meta\TableListAction'] = function ($c) {
......@@ -223,9 +225,11 @@ $container['App\Action\Meta\UserAction'] = function ($c) {
// Search actions
$container['App\Action\Search\SearchMetaAction'] = function ($c) {
return new App\Action\Search\SearchMetaAction($c->get('logger'), $c->get('em'), $c->get('dcf'), $c->get('aqbf'));
$settings = $c->get('settings');
return new App\Action\Search\SearchMetaAction($c->get('logger'), $c->get('em'), $c->get('dcf'), $c->get('aqbf'), $settings['encryption_key']);
};
$container['App\Action\Search\SearchDataAction'] = function ($c) {
return new App\Action\Search\SearchDataAction($c->get('logger'), $c->get('em'), $c->get('dcf'), $c->get('aqbf'));
$settings = $c->get('settings');
return new App\Action\Search\SearchDataAction($c->get('logger'), $c->get('em'), $c->get('dcf'), $c->get('aqbf'), $settings['encryption_key']);
};
......@@ -11,13 +11,14 @@
return [
'settings' => [
// app config
'encryption_key' => 'r3Q8C7LgIrRcTtI8I6EPzFwrDXJ4adgnGQ9V/pWVI8M=',
// slim framework settings
'displayErrorDetails' => (bool) getenv('SLIM_DISPLAY_ERROR_DETAILS'),
'determineRouteBeforeAppMiddleware' => false,
'addContentLengthHeader' => false, // Allow the web server to send the content-length header
// metadata settings (doctrine 2)
'metadata' => [
'entity_path' => ['src/Entity'],
'path_proxy' => getenv('METADATA_DOCTRINE_PATH_PROXY'),
'dev_mode' => getenv('METADATA_DOCTRINE_DEV_MODE'),
'connection_options' => [
......@@ -32,7 +33,7 @@ return [
],
// monolog settings
'logger' => [
'name' => getenv('LOGGER_NAME'),
'name' => getenv('LOGGER_NAME'),
'path' => getenv('LOGGER_PATH'),
'level' => getenv('LOGGER_LEVEL')
],
......@@ -40,14 +41,6 @@ return [
'mailer' => [
'host' => getenv('MAILER_HOST'),
'port' => getenv('MAILER_PORT')
],
'anis' => [
'search_modules' => [
'criteria',
'from',
'order',
'select'
]
]
]
];
......@@ -10,7 +10,8 @@ final class DatabaseActionTest extends AbstractActionTestCase
parent::setUp();
$this->action = new \App\Action\Meta\DatabaseAction(
new \Psr\Log\NullLogger(),
$this->entityManager
$this->entityManager,
base64_decode('r3Q8C7LgIrRcTtI8I6EPzFwrDXJ4adgnGQ9V/pWVI8M=')
);
}
......@@ -102,7 +103,9 @@ final class DatabaseActionTest extends AbstractActionTestCase
$request = $this->getRequestForPut($editedDatabase);
$response = ($this->action)($request, new \Slim\Http\Response(), array('id' => 1));
$this->assertEquals(200, (int) $response->getStatusCode());
$this->assertSame((string) $response->getBody(), json_encode($editedDatabase));
$arrayResponse = json_decode((string) $response->getBody(), true);
$arrayResponse['dbpassword'] = 'password';
$this->assertSame($arrayResponse, $editedDatabase);
$this->assertSame(2, $this->getDatabaseTester()->getConnection()->getRowCount('database'));
}
......
......@@ -10,7 +10,8 @@ final class DatabaseListActionTest extends AbstractActionTestCase
parent::setUp();
$this->action = new \App\Action\Meta\DatabaseListAction(
new \Psr\Log\NullLogger(),
$this->entityManager
$this->entityManager,
base64_decode('r3Q8C7LgIrRcTtI8I6EPzFwrDXJ4adgnGQ9V/pWVI8M=')
);
}
......@@ -77,7 +78,9 @@ final class DatabaseListActionTest extends AbstractActionTestCase
$request = $this->getRequestForPost($newDatabase);
$response = ($this->action)($request, new \Slim\Http\Response(), array());
$this->assertEquals(201, (int) $response->getStatusCode());
$this->assertSame((string) $response->getBody(), json_encode(array_merge(array('id' => 3), $newDatabase)));
$arrayResponse = json_decode((string) $response->getBody(), true);
$arrayResponse['dbpassword'] = 'password';
$this->assertSame($arrayResponse, array_merge(array('id' => 3), $newDatabase));
$this->assertSame(3, $this->getDatabaseTester()->getConnection()->getRowCount('database'));
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment