Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

use Ambta\DoctrineEncryptBundle\Tests\Functional\AbstractFunctionalTestCase;
use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\CascadeTarget;
use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\ClassTableInheritanceBase;
use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\ClassTableInheritanceChild;
use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\Owner;
use Doctrine\DBAL\Logging\DebugStack;

Expand Down Expand Up @@ -46,10 +48,10 @@ public function testEncryptionHappensOnOnlyAnnotatedFields()

public function testEncryptionCascades()
{
$secret = "It's a secret";
$notSecret = "You're all welcome to know this.";
$em = $this->entityManager;
$owner = new Owner();
$secret = "It's a secret";
$notSecret = "You're all welcome to know this.";
$em = $this->entityManager;
$owner = new Owner();
$em->persist($owner); // persist cascades
$em->flush();

Expand Down Expand Up @@ -82,6 +84,60 @@ public function testEncryptionCascades()
$this->assertEquals($secret, $decrypted);
}

public function testEncryptionClassTableInheritance()
{
$secretBase = "It's a secret. On the base class.";
$notSecretBase = "You're all welcome to know this. On the base class.";
$secretChild = "It's a secret. On the child class.";
$notSecretChild = "You're all welcome to know this. On the child class.";
$em = $this->entityManager;
$child = new ClassTableInheritanceChild();
$child->setSecretBase($secretBase);
$child->setNotSecretBase($notSecretBase);
$child->setSecretChild($secretChild);
$child->setNotSecretChild($notSecretChild);
$em->persist($child);
$em->flush();
$em->clear();
unset($child);

$connection = $em->getConnection();
$stmtBase = $connection->prepare('SELECT * from classTableInheritanceBase WHERE id = ?');
$stmtChild = $connection->prepare('SELECT * from classTableInheritanceChild WHERE id = ?');
$childs = $em->getRepository(ClassTableInheritanceBase::class)->findAll();
self::assertCount(1, $childs);
/** @var ClassTableInheritanceChild $child */
$child = $childs[0];
self::assertEquals($secretBase, $child->getSecretBase());
self::assertEquals($notSecretBase, $child->getNotSecretBase());
self::assertEquals($secretChild, $child->getSecretChild());
self::assertEquals($notSecretChild, $child->getNotSecretChild());

// Now check that the fields are encrypted in the database. First the base table.
$stmtBase->bindValue(1, $child->getId());
$stmtBase->execute();
$results = $stmtBase->fetchAll();
self::assertCount(1, $results);
$result = $results[0];
self::assertEquals($notSecretBase, $result['notSecretBase']);
self::assertNotEquals($secretBase, $result['secretBase']);
self::assertStringEndsWith('<ENC>', $result['secretBase']);
$decrypted = $this->encryptor->decrypt(str_replace('<ENC>', '', $result['secretBase']));
self::assertEquals($secretBase, $decrypted);

// and then the child table.
$stmtChild->bindValue(1, $child->getId());
$stmtChild->execute();
$results = $stmtChild->fetchAll();
self::assertCount(1, $results);
$result = $results[0];
self::assertEquals($notSecretChild, $result['notSecretChild']);
self::assertNotEquals($secretChild, $result['secretChild']);
self::assertStringEndsWith('<ENC>', $result['secretChild']);
$decrypted = $this->encryptor->decrypt(str_replace('<ENC>', '', $result['secretChild']));
self::assertEquals($secretChild, $decrypted);
}


/**
* @throws \Doctrine\DBAL\DBALException
Expand All @@ -92,11 +148,11 @@ public function testEncryptionDoesNotHappenWhenThereIsNoChange()
$secret = "It's a secret";
$notSecret = "You're all welcome to know this.";
$em = $this->entityManager;
$owner1 = new Owner();
$owner1 = new Owner();
$owner1->setSecret($secret);
$owner1->setNotSecret($notSecret);
$em->persist($owner1);
$owner2 = new Owner();
$owner2 = new Owner();
$owner2->setSecret($secret);
$owner2->setNotSecret($notSecret);
$em->persist($owner2);
Expand All @@ -114,7 +170,7 @@ public function testEncryptionDoesNotHappenWhenThereIsNoChange()
$stmt->execute();
$results = $stmt->fetchAll();
$this->assertCount(1, $results);
$result = $results[0];
$result = $results[0];
$originalEncryption = $result['secret'];
$this->assertStringEndsWith('<ENC>', $originalEncryption); // is encrypted

Expand All @@ -133,7 +189,7 @@ public function testEncryptionDoesNotHappenWhenThereIsNoChange()
// No encryption should have happened because we didn't change anything.
$this->assertEquals($beforeFlush, $afterFlush);
// No queries happened because we didn't change anything.
$this->assertCount(0, $stack->queries, "Unexpected queries:\n".var_export($stack->queries, true));
$this->assertCount(0, $stack->queries, "Unexpected queries:\n" . var_export($stack->queries, true));

// flush again
$beforeFlush = $this->subscriber->encryptCounter;
Expand All @@ -142,17 +198,98 @@ public function testEncryptionDoesNotHappenWhenThereIsNoChange()
// No encryption should have happened because we didn't change anything.
$this->assertEquals($beforeFlush, $afterFlush);
// No queries happened because we didn't change anything.
$this->assertCount(0, $stack->queries, "Unexpected queries:\n".var_export($stack->queries, true));
$this->assertCount(0, $stack->queries, "Unexpected queries:\n" . var_export($stack->queries, true));

$stmt->bindValue(1, $owner1Id);
$stmt->execute();
$results = $stmt->fetchAll();
$this->assertCount(1, $results);
$result = $results[0];
$result = $results[0];
$shouldBeTheSameAsBefore = $result['secret'];
$this->assertStringEndsWith('<ENC>', $shouldBeTheSameAsBefore); // is encrypted
$this->assertEquals($originalEncryption, $shouldBeTheSameAsBefore);
}

/**
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function testEncryptionDoesNotHappenWhenThereIsNoChangeClassInheritance()
{
$secretBase = "It's a secret. On the base class.";
$notSecretBase = "You're all welcome to know this. On the base class.";
$secretChild = "It's a secret. On the child class.";
$notSecretChild = "You're all welcome to know this. On the child class.";
$em = $this->entityManager;
$child = new ClassTableInheritanceChild();
$child->setSecretBase($secretBase);
$child->setNotSecretBase($notSecretBase);
$child->setSecretChild($secretChild);
$child->setNotSecretChild($notSecretChild);
$em->persist($child);
$em->flush();
$em->clear();
$childId = $child->getId();
unset($child);

// test that it was encrypted correctly
$connection = $em->getConnection();
$stmtBase = $connection->prepare('SELECT * from classTableInheritanceBase WHERE id = ?');
$stmtBase->bindValue(1, $childId);
$stmtBase->execute();
$result = $stmtBase->fetch();
$originalEncryptionBase = $result['secretBase'];
self::assertStringEndsWith('<ENC>', $originalEncryptionBase); // is encrypted

// do the same for the child.
$connection = $em->getConnection();
$stmtChild = $connection->prepare('SELECT * from classTableInheritanceChild WHERE id = ?');
$stmtChild->bindValue(1, $childId);
$stmtChild->execute();
$result = $stmtChild->fetch();
$originalEncryptionChild = $result['secretChild'];
self::assertStringEndsWith('<ENC>', $originalEncryptionChild); // is encrypted

$childs = $em->getRepository(ClassTableInheritanceChild::class)->findAll();
$child = $childs[0];
self::assertEquals($secretBase, $child->getSecretBase());
self::assertEquals($notSecretBase, $child->getNotSecretBase());
self::assertEquals($secretChild, $child->getSecretChild());
self::assertEquals($notSecretChild, $child->getNotSecretChild());

$stack = new DebugStack();
$connection->getConfiguration()->setSQLLogger($stack);
self::assertCount(0, $stack->queries);
$beforeFlush = $this->subscriber->encryptCounter;
$em->flush();
$afterFlush = $this->subscriber->encryptCounter;
// No encryption should have happened because we didn't change anything.
self::assertEquals($beforeFlush, $afterFlush);
// No queries happened because we didn't change anything.
self::assertCount(0, $stack->queries, "Unexpected queries:\n" . var_export($stack->queries, true));

// flush again
$beforeFlush = $this->subscriber->encryptCounter;
$em->flush();
$afterFlush = $this->subscriber->encryptCounter;
// No encryption should have happened because we didn't change anything.
self::assertEquals($beforeFlush, $afterFlush);
// No queries happened because we didn't change anything.
self::assertCount(0, $stack->queries, "Unexpected queries:\n" . var_export($stack->queries, true));

$stmtBase->bindValue(1, $childId);
$stmtBase->execute();
$result = $stmtBase->fetch();
$shouldBeTheSameAsBeforeBase = $result['secretBase'];
self::assertStringEndsWith('<ENC>', $shouldBeTheSameAsBeforeBase); // is encrypted
self::assertEquals($originalEncryptionBase, $shouldBeTheSameAsBeforeBase);

$stmtChild->bindValue(1, $childId);
$stmtChild->execute();
$result = $stmtChild->fetch();
$shouldBeTheSameAsBeforeChild = $result['secretChild'];
self::assertStringEndsWith('<ENC>', $shouldBeTheSameAsBeforeChild); // is encrypted
self::assertEquals($originalEncryptionChild, $shouldBeTheSameAsBeforeChild);
}

public function testEncryptionDoesHappenWhenASecretIsChanged()
Expand All @@ -176,7 +313,7 @@ public function testEncryptionDoesHappenWhenASecretIsChanged()
$stmt->execute();
$results = $stmt->fetchAll();
$this->assertCount(1, $results);
$result = $results[0];
$result = $results[0];
$originalEncryption = $result['secret'];
$this->assertStringEndsWith('<ENC>', $originalEncryption); // is encrypted

Expand All @@ -193,7 +330,7 @@ public function testEncryptionDoesHappenWhenASecretIsChanged()
$stmt->execute();
$results = $stmt->fetchAll();
$this->assertCount(1, $results);
$result = $results[0];
$result = $results[0];
$shouldBeDifferentFromBefore = $result['secret'];
$this->assertStringEndsWith('<ENC>', $shouldBeDifferentFromBefore); // is encrypted
$this->assertNotEquals($originalEncryption, $shouldBeDifferentFromBefore);
Expand Down
66 changes: 66 additions & 0 deletions Tests/Functional/fixtures/Entity/ClassTableInheritanceBase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="discr", type="string")
*/
class ClassTableInheritanceBase
{

/**
* @var int
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
private $id;

/**
* @Ambta\DoctrineEncryptBundle\Configuration\Encrypted()
* @ORM\Column(type="string", nullable=true)
*/
private $secretBase;

/**
* @ORM\Column(type="string", nullable=true)
*/
private $notSecretBase;


public function getId()
{
return $this->id;
}

public function getSecretBase()
{
return $this->secretBase;
}

public function setSecretBase($secretBase)
{
$this->secretBase = $secretBase;
}

/**
* @return mixed
*/
public function getNotSecretBase()
{
return $this->notSecretBase;
}

/**
* @param mixed $notSecretBase
*/
public function setNotSecretBase($notSecretBase)
{
$this->notSecretBase = $notSecretBase;
}

}
49 changes: 49 additions & 0 deletions Tests/Functional/fixtures/Entity/ClassTableInheritanceChild.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php


namespace Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity;

use Doctrine\ORM\Mapping as ORM;

/** @ORM\Entity */
class ClassTableInheritanceChild extends ClassTableInheritanceBase
{

/**
* @Ambta\DoctrineEncryptBundle\Configuration\Encrypted()
* @ORM\Column(type="string", nullable=true)
*/
private $secretChild;

/**
* @ORM\Column(type="string", nullable=true)
*/
private $notSecretChild;

public function getSecretChild()
{
return $this->secretChild;
}

public function setSecretChild($secretChild)
{
$this->secretChild = $secretChild;
}

/**
* @return mixed
*/
public function getNotSecretChild()
{
return $this->notSecretChild;
}

/**
* @param mixed $notSecretChild
*/
public function setNotSecretChild($notSecretChild)
{
$this->notSecretChild = $notSecretChild;
}

}
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<directory suffix="Test.php">./Tests/Unit</directory>
</testsuite>
<testsuite name="functional">
<directory suffix="Test.php">./Tests/Functional</directory>
<directory suffix="TestCase.php">./Tests/Functional</directory>
</testsuite>
</testsuites>

Expand Down
14 changes: 6 additions & 8 deletions src/Subscribers/DoctrineEncryptSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,9 @@ public function postLoad(LifecycleEventArgs $args)
public function preFlush(PreFlushEventArgs $preFlushEventArgs)
{
$unitOfWOrk = $preFlushEventArgs->getEntityManager()->getUnitOfWork();
foreach ($unitOfWOrk->getIdentityMap() as $entityName => $entityArray) {
if (isset($this->cachedDecryptions[$entityName])) {
foreach ($entityArray as $entityId => $instance) {
$this->processFields($instance);
}
foreach ($unitOfWOrk->getIdentityMap() as $entityArray) {
foreach ($entityArray as $entityId => $instance) {
$this->processFields($instance);
}
}
$this->cachedDecryptions = [];
Expand Down Expand Up @@ -260,13 +258,13 @@ public function processFields($entity, $isEncryptOperation = true)
$this->decryptCounter++;
$currentPropValue = $this->encryptor->decrypt(substr($value, 0, -5));
$pac->setValue($entity, $refProperty->getName(), $currentPropValue);
$this->cachedDecryptions[get_class($entity)][spl_object_id($entity)][$refProperty->getName()][$currentPropValue] = $value;
$this->cachedDecryptions[$realClass][spl_object_id($entity)][$refProperty->getName()][$currentPropValue] = $value;
}
}
} else {
if (!is_null($value) and !empty($value)) {
if (isset($this->cachedDecryptions[get_class($entity)][spl_object_id($entity)][$refProperty->getName()][$value])) {
$pac->setValue($entity, $refProperty->getName(), $this->cachedDecryptions[get_class($entity)][spl_object_id($entity)][$refProperty->getName()][$value]);
if (isset($this->cachedDecryptions[$realClass][spl_object_id($entity)][$refProperty->getName()][$value])) {
$pac->setValue($entity, $refProperty->getName(), $this->cachedDecryptions[$realClass][spl_object_id($entity)][$refProperty->getName()][$value]);
} elseif (substr($value, -strlen(self::ENCRYPTION_MARKER)) != self::ENCRYPTION_MARKER) {
$this->encryptCounter++;
$currentPropValue = $this->encryptor->encrypt($value).self::ENCRYPTION_MARKER;
Expand Down