damienharper / auditor Goto Github PK
View Code? Open in Web Editor NEWauditor, the missing audit log library
License: MIT License
auditor, the missing audit log library
License: MIT License
Q | A |
---|---|
auditor version |
2.0.2 |
PHP version | 7.4.28 |
Database | MySQL |
Computing the diff breaks when using a resource
column.
When the line:
[
'diffs' => json_encode($data['diff'], JSON_THROW_ON_ERROR),
]
is called when the diff
contains a column of type resource
, a JSON exception is thrown stating that the type
is not supported.
JsonException
Type is not supported
See my next PR which will introduce a failing test.
I had hoped that this would, at least, not be failing.
Q | A |
---|---|
auditor version |
dev-master 19d5c11 |
PHP version | 8.0.13 |
Database | MariaDB |
When #40 introduced PHP8 annotations, it still used $this->reader->getClassAnnotation
if there weren't any Entity attributes.
if (\PHP_VERSION_ID >= 80000 && $attributes = $reflection->getAttributes(Entity::class)) {
$annotation = $attributes[0]->newInstance();
} else {
$annotation = $this->reader->getClassAnnotation($reflection, Entity::class);
}
In #72, this changed so that on PHP8, it will always use the if
block rather than the else
block.
if (\PHP_VERSION_ID >= 80000 && method_exists($reflection, 'getAttributes')) {
$attributes = $reflection->getAttributes(Entity::class);
$annotation = \count($attributes) > 0 ? $attributes[0]->newInstance() : null;
} elseif (null !== $this->reader) {
$annotation = $this->reader->getClassAnnotation($reflection, Entity::class);
}
Same deal obviously with the other annotation checks for Auditable and Security
/**
* @ORM\Entity(repositoryClass="App\Repository\InventoryRepository")
* @Audit\Auditable
*/
class Inventory
{
// ...
}
doesn't recognize the class as Auditable
If I add the PHP8-style attributes then it does recognize it
#[
ORM\Entity(repositoryClass: InventoryRepository::class),
Audit\Auditable
]
Even with PHP8, old-style annotations are still recognized
As always, thanks for your work on this bundle. I wrote a PR on the old version, a "global" runtime enable/disable.
I am just updating to v4, and DH\Auditor\Provider\Doctrine\Configuration::disable()
no longer exists. I did a bit of digging around, and my code is still there, I just have to do DH\Auditor\Configuration::disable()
This is not a huge deal, and maybe that makes more sense with the new split setup. But, should there maybe be a "shortcut" in DH\Auditor\Provider\Doctrine\Configuration
to the DH\Auditor\Configuration
enable and disable?
What are your thoughts?
[2021-06-16T19:17:11.041340+02:00] deprecation.INFO: User Deprecated: Passing name and value is deprecated, you should pass a FilterInterface object instead. {"exception":"[object] (ErrorException(code: 0): User Deprecated: Passing name and value is deprecated, you should pass a FilterInterface object instead. at /home/mysite/sites/myapp/vendor/damienharper/auditor/src/Provider/Doctrine/Persistence/Reader/Query.php:109)"} []
TLDR; Attempting to create a PR to add support for Symfony 6, but many issues crop.
Would it be worth attempting to fix these issues on branch 1.x, and have support from Sf 3 to Sf 6 on this branch... or would this efforts better directed at the v2? Branch? Do you consider v2 branch to be near enough to be released in the near future?
Now, the long version:
Q | A |
---|---|
auditor version |
1.3.1 |
PHP version | >= 8 |
Right now the package is not installable/runnable on Symfony 6.
I've created a branch to see exactly what's blocking the upgrade.
First, getting the obvious out of the way, on composer.json
we have:
"symfony/event-dispatcher": "^3.4|^4.0|^5.0",
"symfony/lock": "^3.4|^4.0|^5.0",
"symfony/options-resolver": "^3.4|^4.0|^5.0"
and on require-dev
:
"symfony/var-dumper": "^4.0|^5.0"
Adding"|^6.0"
everywhere would make it installable on Sf 6 at least (with --no-dev
), but only for things to break elsewhere.
First, the less important issue (as I see it).
The use of "friendsofphp/php-cs-fixer": "^2.16
. This version is only compatible with Symfony 3, 4, or 5. But not 6. And PHP-CS-Fixer ^3,it's not compatible with Symfony ^3. (First version with Sf 6 support is 3.4). So the same version of PHP-CS-Fixer cannot be installed for versions 3 to 6 of Symfony. And PHP-CS-Fixer config for v2 is not compatible with PHP-CS-Fixer v3...
This is bothersome because this is not even a "real" dependency, but simply an external tool, without any runtime impact. In real life, this shouldn't even be a compatibility issue. But it is because of installing dev tools via composer.
This hurdle could be sidestepped. A couple of options:
friendsofphp/php-cs-fixer
as a PHAR e.g. using something like this, within composer; or like this, outisdefriendsofphp/php-cs-fixer
using something like this, so it's installed on its separate vendor-bin
directory.Considering the nature of the tool, any option should be better than allowing it to trigger dependency hell.
But even with this out of the way:
The project requires "doctrine/doctrine-migrations-bundle": "^1.3|^2.0"
, but only doctrine-migrations-bundle >= 3
declare support for Sf 6. So we could change this to "^1.3|^2.0|^3.0"
to allow --dev
installation...
If that's the case, on Sf 6 doctrine-migrations-bundle v^3 would be installed, and at least this wouldn't block the installation.
But without adding a conflict
rule for dbal >= 3
, DBAL v 3 would be installed, and this would case several problems, with many, as far as I can see only on happening on --dev
, since many tests break under DBAL 3 (the library itself works fine with DBAL 3, as far as I can see). So declaring the library to be incompatible with seems not great.
There is quite a bit to go to make this compatible with a Symfony ^3.4|^4.4|^5.4|^6.0
...
Dropping v3 would alleviate this a fair bit... but could be an unacceptable compromise.
Before I keep digging and trying to see if a PR would make sense, what do you think of this? Is dropping v3 a no-go? Is adding v6 (and DBAL 3 for tests) support too much for this version of the library? Would you welcome efforts on this, or rather have contributions focus on the future v2?
Hopefully I can lend a hand in some capacity.
And happy new year! :)
In a lot of use cases it would be really helpful to include the former data of a removed entity into the audit log. Right now the log only stores that an entity with a specific id was removed. If you for example, use the audit log to audit any user specific data, you might not be able to track for what exactly was deleted.
I would suggest to include the old values in the diff/changeset like it is done in the case of an update.
Is there a specific reason to depend on doctrine/doctrine-bundle
.
I am currently investigating the possibility to use it with a different framework, using components from symfony is completly fine, but doctrine-bundle pulls in the dependency-injection and with it more or less the complete framework.
From a code perspective at least none of the classes of Doctrine\Bundle
is used, so it would be great, if this would not be runtime dependency (ok for dev-dependencies, if it is needed for tests).
Other opinions? I was very lucky to have found a component not beeing already a bundle itself. ;)
Hi @DamienHarper,
After update doctrine wants to change type of all diffs
columns in audit tables from json
to text
. [db: postgres, json support: yes]
I'm not sure that was intended.
Fragment below forces TEXT as the type of all JSON columns when database actually supports json columns .
if (DoctrineHelper::getDoctrineType('JSON') === $struct['type'] && $isJsonSupported) {
$type = DoctrineHelper::getDoctrineType('TEXT');
} else {
$type = $struct['type'];
}
It looks like a bug. As commit message for these lines says the intent was to add compatibility for older versions of mariadb that don't support json.
I see that the package's goal is to make it universal and possible to write own implementations based on it.
I think we can increase community twice if we will create a package like https://github.com/DamienHarper/auditor-bundle for Laravel.
What do you think? I have some experience on this framework
Hi, Something is not clear to us how to use the reader. We don't know what provider to use. If we just want to use the default provider, Doctrine-provider, how to use this?
Anyway, the goal is just to be able to show on the "show" page of the audited entity the list of X lasts changes, with a very simple view. (That field has been changed from this to that) and nothing else.
What's the best way? We thought we can just query from inside the controller of that entity, but we have an error with the lien $reader = new Reader($provider); What Provider do we put here?? We probably missed a little thing stupid, sorry!
I upgraded yesterday to 4.0.1. I use a docker environment, but also have a console binary on my host system. Now since yesterday I experience that a bin/console cache:clear is ending in a PDO exception (which is correct since the host environment doesn't have one), but that wasn't the case before.
I think Auditor is doing something with the database connection during cache warmup? Is this correct and expected behaviour or did I do something wrong?
Exception trace:
at /home/paul/www/BO3/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:37
PDO->__construct() at /home/paul/www/BO3/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:37
Doctrine\DBAL\Driver\PDOConnection->__construct() at /home/paul/www/BO3/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php:24
Doctrine\DBAL\Driver\PDOMySql\Driver->connect() at /home/paul/www/BO3/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:360
Doctrine\DBAL\Connection->connect() at /home/paul/www/BO3/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:425
Doctrine\DBAL\Connection->getDatabasePlatformVersion() at /home/paul/www/BO3/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:385
Doctrine\DBAL\Connection->detectDatabasePlatform() at /home/paul/www/BO3/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:328
Doctrine\DBAL\Connection->getDatabasePlatform() at /home/paul/www/BO3/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:801
Doctrine\ORM\Mapping\ClassMetadataFactory->getTargetPlatform() at /home/paul/www/BO3/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:644
Doctrine\ORM\Mapping\ClassMetadataFactory->completeIdGeneratorMapping() at /home/paul/www/BO3/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:166
Doctrine\ORM\Mapping\ClassMetadataFactory->doLoadMetadata() at /home/paul/www/BO3/vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php:306
Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata() at /home/paul/www/BO3/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php:82
Doctrine\ORM\Mapping\ClassMetadataFactory->loadMetadata() at /home/paul/www/BO3/vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php:184
Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor() at /home/paul/www/BO3/vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php:90
Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getAllMetadata() at /home/paul/www/BO3/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Annotation/AnnotationLoader.php:32
DH\Auditor\Provider\Doctrine\Auditing\Annotation\AnnotationLoader->load() at /home/paul/www/BO3/vendor/damienharper/auditor/src/Provider/Doctrine/DoctrineProvider.php:236
DH\Auditor\Provider\Doctrine\DoctrineProvider->loadAnnotations() at /home/paul/www/BO3/vendor/damienharper/auditor/src/Provider/Doctrine/DoctrineProvider.php:45
DH\Auditor\Provider\Doctrine\DoctrineProvider->registerAuditingService() at /home/paul/www/BO3/var/cache/de_/ContainerJBNVfRG/App_KernelDevDebugContainer.php:1518
ContainerJBNVfRG\App_KernelDevDebugContainer->getDoctrineProviderService() at /home/paul/www/BO3/var/cache/de_/ContainerJBNVfRG/App_KernelDevDebugContainer.php:920
ContainerJBNVfRG\App_KernelDevDebugContainer->getDoctrine_Dbal_DefaultConnectionService() at /home/paul/www/BO3/var/cache/de_/ContainerJBNVfRG/App_KernelDevDebugContainer.php:933
ContainerJBNVfRG\App_KernelDevDebugContainer->getDoctrine_Orm_DefaultEntityManagerService() at /home/paul/www/BO3/var/cache/de_/ContainerJBNVfRG/App_KernelDevDebugContainer.php:2310
ContainerJBNVfRG\App_KernelDevDebugContainer->getValidator_BuilderService() at /home/paul/www/BO3/var/cache/de_/ContainerJBNVfRG/getValidator_Mapping_CacheWarmerService.php:24
ContainerJBNVfRG\getValidator_Mapping_CacheWarmerService::do() at /home/paul/www/BO3/var/cache/de_/ContainerJBNVfRG/App_KernelDevDebugContainer.php:597
ContainerJBNVfRG\App_KernelDevDebugContainer->load() at /home/paul/www/BO3/var/cache/de_/ContainerJBNVfRG/getCacheWarmerService.php:25
ContainerJBNVfRG\getCacheWarmerService::ContainerJBNVfRG\{closure}() at /home/paul/www/BO3/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php:90
Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate->warmUp() at /home/paul/www/BO3/vendor/symfony/http-kernel/Kernel.php:580
Symfony\Component\HttpKernel\Kernel->initializeContainer() at /home/paul/www/BO3/vendor/symfony/http-kernel/Kernel.php:780
Symfony\Component\HttpKernel\Kernel->preBoot() at /home/paul/www/BO3/vendor/symfony/http-kernel/Kernel.php:121
Symfony\Component\HttpKernel\Kernel->boot() at /home/paul/www/BO3/vendor/symfony/http-kernel/Kernel.php:139
Symfony\Component\HttpKernel\Kernel->reboot() at /home/paul/www/BO3/vendor/symfony/framework-bundle/Command/CacheClearCommand.php:224
Symfony\Bundle\FrameworkBundle\Command\CacheClearCommand->warmup() at /home/paul/www/BO3/vendor/symfony/framework-bundle/Command/CacheClearCommand.php:148
Symfony\Bundle\FrameworkBundle\Command\CacheClearCommand->execute() at /home/paul/www/BO3/vendor/symfony/console/Command/Command.php:255
Symfony\Component\Console\Command\Command->run() at /home/paul/www/BO3/vendor/symfony/console/Application.php:989
Symfony\Component\Console\Application->doRunCommand() at /home/paul/www/BO3/vendor/symfony/framework-bundle/Console/Application.php:96
Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /home/paul/www/BO3/vendor/symfony/console/Application.php:290
Symfony\Component\Console\Application->doRun() at /home/paul/www/BO3/vendor/symfony/framework-bundle/Console/Application.php:82
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /home/paul/www/BO3/vendor/symfony/console/Application.php:166
Symfony\Component\Console\Application->run() at /home/paul/www/BO3/bin/console:42
I'm using auditor with the auditor-bundle for Symfony on the latest version (5.3).
I have an entity A that has a OneToMany relation to entity B with orphanRemoval=true, since B depends on A and cannot be transferred or stand on its own.
If I remove an entity B from A (e.g. via a form with a CollectionType), I get a transaction that looks somewhat like this:
App\Entity\A
App\Entity\A#132 (My Entity) has been dissociated by username , IP: 127.0.0.1
App\Entity\B
App\Entity\B#321 (Some Name) has been deleted by username , IP: 127.0.0.1
While it's easy enough to work out what has happened in this simple instance, it's a different question when it comes to larger transactions that might contain multiple different entities.
The reason is AuditTrait's summarize()
method bailing early:
// An added guard for proxies that fail to initialize.
if (null === $pkValue) {
return null;
}
Without that guard, I'm getting this audit log entry:
App\Entity\A
App\Entity\A#132 (My Entity) has been dissociated from App\Entity\B# (Some Name) by username , IP: 127.0.0.1
App\Entity\B
App\Entity\B#321 (Some Name) has been deleted by username , IP: 127.0.0.1
Which is much better in my opinion. It lacks the ID because the EntityManager has already removed it at this stage in the Event, but it'll still have the label from __toString() and most importantly, it'll say what type of entity was dissociated from A.
Is there a better way to approach this?
I'm sure there's a reason to not summarize entities without primary keys and it probably can't simply be changed and create unexpected behavior legacy users. I haven't seen an obvious way to replace the TransactionProcessor
with another class implementing TransactionProcessorInterface
, have I missed it or is it currently impossible?
Sometimes when you encounter a bug you must change the data with an adhoc query in the database. Is there a way to insert a line of the diff in the Audit tables after making the update on the database through a query?
If not, I supposed the solution is to use the ORM to make the updates?
Thanks.
vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/AuditTrait::summarize
the method uses the actual name of the primary key (in my case the "code" field - $pkName variable)
vendor/damienharper/auditor-bundle/src/Resources/views/Audit/helpers/helper.html.twig:58-59
{% set subject = target.class~'#'~target.id %} {{ direction }} <code><a href="{{ path('dh_auditor_show_entity_history', { 'entity': helper.namespaceToParam(target.class), 'id': target.id }) }}">{{ subject }}</a></code>
but when rendering, we use the "id" field, but in my case it is not there, there is a "code" field
types:
uuid_binary: Ramsey\Uuid\Doctrine\UuidBinaryType
uuid_binary_ordered_time: Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType
mapping_types:
uuid_binary: binary
uuid_binary_ordered_time: binary
/**
* @ORM\Entity
* @ORM\Table(...)
* @Auditable()
*/
class Xxx implements Yyy
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidOrderedTimeGenerator")
* @ORM\Column(type="uuid_binary_ordered_time", unique=true)
* @Ignore()
*/
private Uuid $id;
/**
* @ORM\Column(...)
*/
public string $yyy;
}
Audit will fail to SQL error on insert. Example of error is following:
An exception occurred while executing 'INSERT INTO namespace_Xxx_audit (type, object_id, discriminator, transaction_hash, diffs, blame_id, blame_user, blame_user_fqdn, blame_user_firewall, ip, created_at) VALUES (:type, :object_id, :discriminator, :transaction_hash, :diffs, :blame_id, :blame_user, :blame_user_fqdn, :blame_user_firewall, :ip, :created_at)' with params ["insert", "\x11\xea\xb4\x47\xd5\x35\xc1\xd2\x99\x2f\x02\x42\xac\x1e\x00\x09", null, "16e29081336395cbe0f08971bb4f51b584dd94a0", "{\"firstName\":{\"old\":null,\"new\":\"F\"},\"lastName\":{\"old\":null,\"new\":\"L\"}}", null, null, null, "login", "172.30.0.1", "2020-06-22 06:18:40"]:
SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string value: '\xEA\xB4G\xD55\xC1...' for column `database`.`namespace_Xxx_audit`.`object_id` at row 1
The problem is caused due value is expected to be string value when in the DB, but when code runs convertToDatabaseValue()
here, it will convert it from string to binary which causes this error.
To solve this you need to be able specify which types are ran with $type->convertToPHPValue($value, $platform);
instead of $type->convertToDatabaseValue($value, $platform);
. Another solutions is to run (string)
or __toString()
cast on this value.
I am not honestly sure how this can be achived without hacky solutions, but I was thinking as you are refactoring everything for v4, this could be implemented in same rewrite.
auditor/src/Provider/Doctrine/Auditing/Transaction/AuditTrait.php
Lines 55 to 84 in 75ad5a1
I dont want push you, but I am waiting v4 to finish stable state and this feature to be published before I add this library to my new project. Thank you for your awesome work. 🥇
Q | A |
---|---|
auditor version |
4.x |
PHP version | all supported |
Database | MySQL / PostgreSQL / SQLite / Other |
Does this support EasyAdmin3 ?
When editing or creating items it doesn't seem to log it?
It use to work with the API Platform when it changes entities in the database however since upgrading to auditor-bundle 4.0 it doesn't seem to be working anymore
Q | A |
---|---|
auditor version |
1.3.0 |
PHP version | 8.0.10 |
Database | MariaDB |
Due to GDPR i've marked some fields with personal data to be encrypted.
The fields are encrypted with an subscriber 'onFlush' these changes are not picked up by the auditor so it always sees an diff because the auditor sees an plain text instead of the encrypted value.
It now sees every encrypted field als an change which isn't and is creating a lot of inserts in the _audit tables.
Perform an value change on a field with onFlush (Doctrine\ORM\Events::onFlush)
That it will perform an diff check on postFlush or add a priority number for the auditor onFlush so i can set mine flush with more priority
Q | A |
---|---|
auditor version |
2.0.3 |
PHP version | 8.1.2 |
Database | SQLite |
It fails to compute the diff when a custom Doctrine type mapped to binary database type is used. It checks for known types such as Ramsey or Symfony UUIDs, but doesn't provide for customization when it comes to custom types - it defaults to using convertToDatabaseValue
for all the unknown types.
convertToDatabaseValue
in this case is returning a binary value which breaks JSON encoding further down the line.
A custom doctrine type, e.g. a copy of AbstractUid would cause the following exception:
JsonException: Malformed UTF-8 characters, possibly incorrectly encoded
/opt/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php:221
/opt/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php:66
/opt/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php:139
/opt/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php:38
/opt/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionManager.php:32
/opt/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Event/DoctrineSubscriber.php:43
/opt/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Logger/Logger.php:28
/opt/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Logger/LoggerChain.php:30
/opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:1783
/opt/app/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:454
/opt/app/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:398
/opt/app/var/cache/test/ContainerPGPpJpP/EntityManager_9a5be93.php:136
JsonException: Malformed UTF-8 characters, possibly incorrectly encoded
We could maybe introduce an interface that could be used with custom types, and checked here?
Q | A |
---|---|
auditor version |
2.0.0 |
PHP version | 8.1.3 |
Database | MySQL |
Auditor fails to process entity disaccociation.
When attempting to deassociate inverse side of uni-directional ManyToMany relation (Report m..m Tags), I get a following error:
DH\Auditor\Provider\Doctrine\Auditing\Transaction\TransactionProcessor::dissociate(): Argument #4 ($mapping) must be of type array, int given, called in /srv/app/vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php on line 164
I tracked the cause to be incorrect array destructuring: https://github.com/DamienHarper/auditor/blob/master/src/Model/Transaction.php#L24 even says the return value is [$source, $target, $id, $mapping]
, but https://github.com/DamienHarper/auditor/blob/master/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php#L162 is workin with it as if the $id
was not expected: foreach ($transaction->getDissociated() as [$source, $target, $mapping]) {
Changing line 162 to foreach ($transaction->getDissociated() as [$source, $target, $id, $mapping]) {
fixes the problem.
I'll create a PoC if necessary - the cause seems to be obvious, fix as well - PoC might be just a wasted time.
It should work :)
Q | A |
---|---|
auditor version |
2.0.5 |
PHP version | 8.1.2 |
Database | MySQL |
ErrorException
Warning: Object of class App\Enum\ProductType could not be converted to int
vendor\damienharper\auditor\src\Provider\Doctrine\Auditing\Transaction\AuditTrait.php&line=89#line89)(line 89)
case DoctrineHelper::getDoctrineType('INTEGER'):
case DoctrineHelper::getDoctrineType('SMALLINT'):
$convertedValue = (int) $value; // @phpstan-ignore-line
break;
Bug fixed
case DoctrineHelper::getDoctrineType('INTEGER'):
case DoctrineHelper::getDoctrineType('SMALLINT'):
$convertedValue = (int) ($value instanceof \BackedEnum ? $value->value : $value) ; // @phpstan-ignore-line
break;
Add option include_columns
to specify the columns to be used for auditing an entity. (only those columns should be audited then, changes in all the rest should be ignored)
Right now one has to ignore a lot of columns per entity to achieve the same thing.
In my opinion, most entities only have a few columns that are relevant for auditing, so it makes sense to just specify those and ignore the rest.
Hi,
first thanks for this flexible bundle.
I developed an own "audit service" but this isn't flexible and professional then yours.
But I used a specific filed to log on related fields instead of the ID
Example:
Your approach: status old: App\Entity\Status#4 new: App\Entity\Status#1
My approach: status old: planing new: operation
The record #4 with the field name= planing
The record #1 with the field name = operation
The advantage is, it is traceable in the AuditLog to which exact value it was changed.
If somebody changed the field of the related table it isn't exact traceable which field was used with your approach.
Is this possible?
Thanks
Q | A |
---|---|
auditor version |
2.0.1 (auditor bundle 5.0.2) |
PHP version | 8.1.5 |
Database | MariaDB |
The Ignore
attribute is not respected when it's on a private property in a parent class and a field was changed in a child class.
Here is my class hierarchy:
BaseOrder
|- PizzaOrder
I have an updatedAt
property defined as follows:
// Defined on BaseOrder
#[ORM\Column(name: "updatedAt", type: "datetime_immutable", nullable: true)]
#[Audit\Ignore]
private ?\DateTimeImmutable $updatedAt = null;
When making changes to a PizzaOrder
the changes to updatedAt
appear in the audit log.
See above, please let me know if you'd like a working code sample.
Ignore
annotation can be defined in parent classes on private variables. Note that it does work as expected for protected variables.
Q | A |
---|---|
auditor version |
2.3.0 |
PHP version | 8.1.8 |
Database | MySQL / SQLite |
I have an abstract parent entity which is not audited, but uses doctrine/orm inheritance type "SINGLE_TABLE" and defines the database table for its child entities. Some child entities are configured to be audited.
With auditor 2.3.0, creating the audit database schema throws an exception.
When the database schema is created for this abstract parent entity, the CreateSchemaListener wants to create the corresponding audit table. This throws an exception while trying to determine the table name from the configuration with the new logic introduced in auditor 2.3.0, as the parent entity does not have any configuration: Undefined array key "App\AbstractParentEntity"
In versions before 2.3.0, the table name from the event was being used, which is independent of the configuration.
Define an abstract parent entity and an audited child entity:
namespace App;
/**
* @ORM\Table(name="entities")
* @ORM\Entity()
* @ORM\InheritanceType("SINGLE_TABLE")
*/
abstract class AbstractParentEntity {}
/**
* @ORM\Entity
*/
class Entity {}
Configure auditing for the child entity:
dh_auditor:
enabled: true
providers:
doctrine:
table_prefix: ~
table_suffix: '_audit'
entities:
App\Entity: ~
Create the database schema using Doctrine\ORM\Tools\SchemaTool::createSchema
for the parent entity to create an Doctrine\ORM\Tools\ToolEvents::postGenerateSchemaTable
event.
Creating the database schema for the abstract parent entity and its child entities does not throw an exception and creates the corresponding audit table schema.
Q | A |
---|---|
auditor version |
x.y.z |
PHP version | 7.1 |
Database | MySQL / PostgreSQL / SQLite / Other |
Hello! Critical error founded
The error is reproduced in this case:
$query->addFilter('object_id', 1);
$query->addFilter('object_id', 34);
This code will generate an error:
An exception occurred while executing 'SELECT * FROM contact_audit at WHERE object_id IN (?) ORDER BY created_at DESC, id DESC LIMIT 50' with params [[1,34]] Notice: Array to string conversion
Cause of error:
The parameter list support only works with Doctrine\DBAL\Connection::executeQuery() and Doctrine\DBAL\Connection::executeUpdate(), NOT with the binding methods of a prepared statement.
Sorry for my rude and bad english
When I upgrade to symfony 6.2 I get this error:
!! Error {#613
!! #message: "Call to a member function addEventSubscriber() on null"
!! #code: 0
!! #file: "./vendor/damienharper/auditor/src/Provider/Doctrine/DoctrineProvider.php"
!! #line: 51
!! trace: {
!! ./vendor/damienharper/auditor/src/Provider/Doctrine/DoctrineProvider.php:51 { …}
When using Doctrine DBAL 3.3.0 i got an error
"Error: Uncaught Error: Call to undefined method Doctrine\DBAL\Result::setFetchMode() in vendor/damienharper/auditor/src/Provider/Doctrine/Persistence/Reader/Query.php:72
Q | A |
---|---|
auditor version |
1.2.0 |
PHP version | 8.0.9 |
Database | PostgreSQL 12.06 |
Can't preload already declared class DH\Auditor\Event\AuditEvent in /var/www/wl-core/vendor/damienharper/auditor/src/Event/AuditEvent.php on line 44
Enable preload in php.ini (bug 100% reproduces on symfony)
You have no errors about preload. You're perfect.
Q | A |
---|---|
auditor version |
2.0.3 |
PHP version | 8.1.8 |
Database | SQLite |
The Doctrine\DBAL\Logging\SQLLogger Interface is deprecated
Symfony 6.1 shows a deprecation notice.
2022-07-12T09:45:13+00:00 [info] User Deprecated: The "DH\Auditor\Provider\Doctrine\Auditing\Logger\Logger" class implements "Doctrine\DBAL\Logging\SQLLogger" that is deprecated Use {@see \Doctrine\DBAL\Logging\Middleware} or implement {@see \Doctrine\DBAL\Driver\Middleware} instead.
2022-07-12T09:45:13+00:00 [info] User Deprecated: The "DH\Auditor\Provider\Doctrine\Auditing\Logger\LoggerChain" class implements "Doctrine\DBAL\Logging\SQLLogger" that is deprecated Use {@see \Doctrine\DBAL\Logging\Middleware} or implement {@see \Doctrine\DBAL\Driver\Middleware} instead.
No deprecations are shown when using the library.
Q | A |
---|---|
auditor version |
2.0.5 |
PHP version | 8.1.0 |
Database | MySQL / PostgreSQL / SQLite / Other |
Json diffs are encoded twice. At the end it doesn't show correctly in viewer
Message in viewer encoded twice
"\u041f\u0420\u0418\u041c\u0415\u0420"
ПРИМЕР
I see ПРИМЕР
in viewer
@DamienHarper 🙏 please can you make a release so we can use the attributes via a stable version? 🙏
I have a simple use case of logging an order with multiple order lines.
When changing the order line the order entity is not modified and no log is generated for it. Only the log for the order line entity is generated.
How can I query every change to an order + order lines using the order number only. So for every transaction i should get the changes for both order and orderline.
The diff for the order line entity doesn't have the order information to reference it.
The general question is how to link changes in child entities to their parent when the parent entity remains unchanged.
Q | A |
---|---|
auditor version |
1.3.1 |
PHP | 7.4 |
MySQL | 5.7 |
Symfony | 5.3 |
Given these insert and update operations:
$this->orm->beginTransaction();
$e1 = new Entity();
$e1->setProperty($value);
$this->orm->persist($e1);
$this->orm->flush();
$e1->setProperty($value);
$this->orm->flush();
$e1->setProperty($value);
$this->orm->flush();
$e2 = new Entity();
$e2->setProperty($value);
$this->orm->persist($e2);
$this->orm->flush();
$e2->setProperty($value);
$this->orm->flush();
$e2->setProperty($value);
$this->orm->flush();
$this->orm->commit();
On the audit table you'll have them in the reverse order:
$e2 update
$e2 update
$e2 insert
$e1 update
$e1 update
$e1 insert
If you don't use beginTransaction()
and commit()
, they are in the correct order:
$e1 insert
$e1 update
$e1 update
$e2 insert
$e2 update
$e2 update
Do you have any idea why is this and how should it be fixed?
I have an entity which should get audited and if I call flush more than once before calling commit, the audit-events will get logged with each subsequent commit again. In this example I end up with 11 identical insert audits:
$this->orm->beginTransaction();
$e = new Enity();
$this->orm->persist($e);
$this->orm->flush();
$this->orm->flush();
$this->orm->commit(); // Logs the insert once.
// Logs an additional 10 inserts for the entity added above:
for ($n=1;$n<=10;$n++) {
$this->orm->beginTransaction();
$this->orm->commit();
}
I believe the issue is caused by DoctrineSubscriber::onFlush
, which simply copies the current SQL-Logger to a class-variable and then tries to restore that value on commit, while not checking if the Audit-Logger is already installed.
Is there a way for us to audit log a database operation that happened through SQL? Maybe writing our own provider? Are there any examples of this?
Thanks!
Q | A |
---|---|
auditor version |
1.2.0 |
PHP version | any |
Database | any |
Throws MappingException
when tries to record audit log for Entity which has complex primary keys
I've made demo project for this bug. https://github.com/gam6itko/auditor-multi-id-bug
Look at MultiIdTest
Would it be possible to query all audited table for some criteria just like you can query a specific table ?
I know the query may be taking long time and consuming memory but this could be avoid with some kind of pagination maybe ?
I have an entity User
and another entity Post
which might have an author
and an editor
. Both of these reference User
.
If I associate and dissociate an author, there's no way to tell whether the User was assigned as the author or editor.
Is there a use-case where adding the affected field might lead to problems?
Hi,
I want to migrate to this package because the other package 'damienharper/doctrine-audit-bundle'is abandonded ( or replaced)
I already migrated my old yaml file to the new structure but i'm still stuck at the new subscriber.
In the old i ad a subscriber that needs to update some fields like ip because it's getting it from another place then the usual place.
In the old package i had the following:
$storage = $this->manager->selectStorageSpace($this->manager->getConfiguration()->getEntityManager());
/** @var Statement $statement */
$statement = $storage->getConnection()->prepare($query);
foreach ($payload as $key => $value) {
$statement->bindValue($key, $value);
}
$statement->execute();
// Prevent event from executing the original event
$event->stopPropagation();
but how can i migrate this to this new package ?
Thanks,
EDIT:
Found it by watching src/EventSubscriber/AuditEventSubscriber.php
/**
* @param LifecycleEvent $event
*
* @return LifecycleEvent
* @throws DBALException
* @throws InvalidArgumentException
*/
public function onLifecycleEvent(LifecycleEvent $event): LifecycleEvent
{
$payload = $event->getPayload();
$payload['ip'] = 'Use this ip';
$event->setPayload($payload);
foreach ($this->auditor->getProviders() as $provider) {
if ($provider->supportsStorage()) {
try {
$provider->persist($event);
} catch (Exception $e) {
// do nothing to ensure other providers are called
}
}
}
// Prevent event from executing the original event
$event->stopPropagation();
return $event;
}
Document path isn't correct
https://damienharper.github.io/docs/auditor/upgrading.html#upgrading-from-a-previous-version
should be this I think
https://damienharper.github.io/auditor-docs/docs/auditor/upgrading.html#upgrading-from-a-previous-version
Q | A |
---|---|
auditor version |
1.2.0 |
PHP version | 8.05 |
Database | MS-SQL |
After upgrading to symfony 5.2 the auditor doesn't work anymore.
symfony 4.4 had no problems.
I've looked into the debug-toolbar and the DH\Auditor\Event\LifecycleEvent is on the " Not Called Listeners" tab:
DH\Auditor\Event\LifecycleEvent
--
-1000000 | "DH\Auditor\EventSubscriber\AuditEventSubscriber::onAuditEvent(LifecycleEvent $event): LifecycleEvent"
Not creating audits
Update to sf5.2
Create audits, just as on sf4.4
Q | A |
---|---|
auditor version |
1.2.0 |
PHP version | 7.4.19 |
Database | MySQL 5.7 |
When running queries like:
SELECT *
FROM audit_User
WHERE object_id = 1
Explained:
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | audit_User | <null> |
ALL | object_id_875a3855f585b727b04ee34ace463a77_idx | <null> |
<null> |
<null> |
72046910 | 10 | Using where; Using filesort |
the queries take quite some time to run when a lot of data is present.
The queries are slow.
SELECT * FROM audit_Entity WHERE object_id = 1
I expected that the Database would be smart and use the index to speed up this query.
For some reason though, the database thinks that the cardinality of the index is too high and a full index scan is actually faster.
The SimpleThings EntityAudit uses basically the same structure but has a different index setup.
Instead of creating just a UNIQUE index on the object_id
it added a joined index on both the object_id
and theid
.
I tried to create a UNIQUE index on the id
and the object_id
as the id
is a primary key I expected the id
to be auto-incremented and unique, but I got an error that a duplicated key existed.
Perhaps this has something to do with #44?
Creating a non-unique index doesn't solve this issue, unfortunately.
The UI itself is fast because it limits the results to the 50 latest results. In the database, I'd like to see a complete overview of all the records (DataGrip limits the results to 1,500 by default) but that is quite slow.
What can be done to increase the database performance of the queries, as we really like this bundle but would also like to use this for investigative purposes.
On the 19th of April, two bugs have been fixed (#99 and #97). Currently, our project uses the dev-master
version of the project, because no new version has been released. When will the new version including these bug fixes be released?
And should a new version be released for the DamienHarper/auditor-bundle as well, with the updated dependency to 2.0.3?
Q | A |
---|---|
auditor version |
2.0.2 |
PHP version | 8.0 |
Database | MySQL |
Not properly log dissociation if collection is removed.
If i have 2 entities manyToMany and remove all items from one entity - no logs dissociation. If some item remains so it log correct with dissociation
It thinks function hydrateWithScheduledCollectionDeletions not work properly.
private function hydrateWithScheduledCollectionDeletions(Transaction $transaction, EntityManagerInterface $entityManager): void
{
$uow = $entityManager->getUnitOfWork();
/** @var PersistentCollection $collection */
foreach (array_reverse($uow->getScheduledCollectionDeletions()) as $collection) {
$owner = $collection->getOwner();
if (null !== $owner && $this->provider->isAudited($owner)) {
$mapping = $collection->getMapping();
if (null === $mapping) {
continue;
}
/** @var object $entity */
foreach ($collection->toArray() as $entity) {
if ($this->provider->isAudited($entity)) {
$transaction->dissociate(
$owner,
$entity,
$mapping,
);
}
}
}
}
}
$mapping has data.
$collection->toArray() is always empty array so code in this foreach is never done.
make 2 entity manyToMany (company , user)
add for example 3 users to company
remove 1 user
this will log correct with one dissociation
remove 2 remains users and it log nothing
(if no one user remains in collection so it not logging dissociation)
removed all items from collection (example 3) -> get 3 logs with dissociation data
Q | A |
---|---|
auditor version |
2.0.1 |
PHP version | 7.4.28 |
Database | MySQL |
The auditor fails to create the audit trails for entities that are created within a transaction and have a custom __toString()
method that depends on type hinted properties.
It currently throws an exception that a property must be initialized prior to accessing, or that null
can't be used if the property has a default value.
Error {#220741
#message: "Typed property Entity::$type must not be accessed before initialization"
#code: 0
#file: "./src/Entity.php"
#line: 35
trace: {
./src/Entity.php:35 {
Entity->__toString()
› {
› return $this->type;
› }
}
./var/cache/selenium/doctrine/orm/Proxies/__CG__Entity.php:203 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/AuditTrait.php:198 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/AuditTrait.php:164 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php:60 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php:139 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionProcessor.php:38 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Transaction/TransactionManager.php:32 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Event/DoctrineSubscriber.php:43 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Logger/Logger.php:28 { …}
./vendor/damienharper/auditor/src/Provider/Doctrine/Auditing/Logger/LoggerChain.php:30 { …}
./vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:1783 { …}
./vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:242 { …}
./vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php:76 { …}
./vendor/doctrine/doctrine-fixtures-bundle/Command/LoadDataFixturesDoctrineCommand.php:158 { …}
./vendor/symfony/console/Command/Command.php:298 { …}
./vendor/symfony/console/Application.php:1033 { …}
./vendor/symfony/framework-bundle/Console/Application.php:96 { …}
./vendor/symfony/console/Application.php:299 { …}
./vendor/symfony/framework-bundle/Console/Application.php:82 { …}
./vendor/symfony/console/Application.php:171 { …}
./vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php:54 { …}
./vendor/autoload_runtime.php:35 { …}
./bin/console:11 { …}
}
}
I think the snippet below is the general case what happens
/**
* @Auditable()
*/
class TestEntity {
private string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
public function __toString(): string
{
return $this->name;
}
}
$em = $container->get(EntityManagerInterface::class);
$em->transactional(function($manager) {
$test = new TestEntity('Hello');
$manager->persist($test);
$manager->flush();
};
I'll see whether I can create a PR that creates a test for this bug.
I expected that it would not crash, and create an insert log item.
I have been browsing the documentation trying to find a way to add a new field for each audit log, but I haven't found anything.
For our use case, the audit should be "tagged" with the project ID, and users within that project should be able to see the audit log. Users of course can be added and removed to the project.
At this time, audit logs seem to be owned by a user, but for our use case, it is owned by a project, and we still need to know who was the user who performed the action.
If it does not exist yet, then providing a way for devs to extend the audit log entity (like how FOSBundle does it with FOSUser) can do the job.
Thanks!
Q | A |
---|---|
auditor-bundle version |
4.1.0 |
PHP version | 8.0.3 |
Database | MSSQL |
Running a migration always terun a ivalid SQL statement for MSSQL.
ALTER TABLE category_audit ALTER COLUMN id INT IDENTITY NOT NULL
SQL error is:
Incorrect syntax near the keyword 'IDENTITY
COLLATE, NOT, NULL, SPARSE or WITH expected, got 'IDENTITY'
running
bin/console doctrine:schema:update
or
bin/console make:migration
when using MSSQL as db
Nothing, when schema is up tot date.
Hi! How can I make a query with a filter by array of object_ids?
Q | A |
---|---|
auditor version |
4.0.3 |
PHP version | 7.3 |
Database | MariaDB |
Argument 1 passed to DH\Auditor\Model\Entry::sort() must be of the type array, null given, called in /var/www/app/vendor/damienharper/auditor/src/Model/Entry.php on line 160
When viewing audit log for an entity that have done a insert and does not have any diff.
Use the viewer and press "view audit" on a entity with entries that do not have a diff.
Not an error
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.