{"id":13684056,"url":"https://github.com/lorenzo/audit-stash","last_synced_at":"2025-04-04T22:05:47.199Z","repository":{"id":35090583,"uuid":"39250084","full_name":"lorenzo/audit-stash","owner":"lorenzo","description":"Flexible and rock solid audit log tracking for CakePHP 3","archived":false,"fork":false,"pushed_at":"2024-08-30T12:07:47.000Z","size":240,"stargazers_count":79,"open_issues_count":7,"forks_count":37,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-03-28T21:02:38.338Z","etag":null,"topics":["audit","audit-log","audit-table","cakephp","elasticsearch"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lorenzo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-07-17T11:03:57.000Z","updated_at":"2025-01-28T10:28:31.000Z","dependencies_parsed_at":"2024-01-11T22:56:55.702Z","dependency_job_id":"faae5346-fd66-415d-b649-5e88be2262b0","html_url":"https://github.com/lorenzo/audit-stash","commit_stats":{"total_commits":137,"total_committers":19,"mean_commits":"7.2105263157894735","dds":"0.35766423357664234","last_synced_commit":"58f0c6564b9b6784a106ab67510af9e51a70a73c"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lorenzo%2Faudit-stash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lorenzo%2Faudit-stash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lorenzo%2Faudit-stash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lorenzo%2Faudit-stash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lorenzo","download_url":"https://codeload.github.com/lorenzo/audit-stash/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247256110,"owners_count":20909240,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audit","audit-log","audit-table","cakephp","elasticsearch"],"created_at":"2024-08-02T14:00:24.163Z","updated_at":"2025-04-04T22:05:47.179Z","avatar_url":"https://github.com/lorenzo.png","language":"PHP","funding_links":[],"categories":["Auditing / Logging"],"sub_categories":[],"readme":"# AuditStash Plugin For CakePHP\n\n[![Build Status](https://github.com/lorenzo/audit-stash/actions/workflows/ci.yml/badge.svg)](https://github.com/lorenzo/audit-stash/actions/workflows/ci.yml)\n[![Coverage Status](https://img.shields.io/codecov/c/github/lorenzo/audit-stash/master.svg?style=flat-square)](https://codecov.io/github/lorenzo/audit-stash)\n[![Total Downloads](https://img.shields.io/packagist/dt/lorenzo/audit-stash.svg?style=flat-square)](https://packagist.org/packages/lorenzo/audit-stash)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE.txt)\n\nThis plugin implements an \"audit trail\" for any of your Table classes in your application, that is,\nthe ability of recording any creation, modification or delete of the entities of any particular table.\n\nBy default, this plugin stores the audit logs into [Elasticsearch](https://www.elastic.co/products/elasticsearch),\nas we have found that it is a fantastic storage engine for append-only streams of data and provides really\npowerful features for finding changes in the historic data.\n\nEven though we suggest storing the logs in Elasticsearch, this plugin is generic enough so you can implement your\nown persisting strategies, if so you wish.\n\n## Installation\n\nYou can install this plugin into your CakePHP application using [composer](https://getcomposer.org) and executing the\nfollowing lines in the root of your application.\n\n```\ncomposer require lorenzo/audit-stash\nbin/cake plugin load AuditStash\n```\n\nFor using the default storage engine (ElasticSearch) you need to install the official `elastic-search` plugin, by executing\nthe following lines:\n\n```\ncomposer require cakephp/elastic-search\nbin/cake plugin load Cake/ElasticSearch\n```\n\n## Configuration\n\n### Elastic Search\n\nYou now need to add the datasource configuration to your `config/app.php` file:\n\n```php\n'Datasources' =\u003e [\n    'auditlog_elastic' =\u003e [\n        'className' =\u003e 'Cake\\ElasticSearch\\Datasource\\Connection',\n        'driver' =\u003e 'Cake\\ElasticSearch\\Datasource\\Connection',\n        'host' =\u003e '127.0.0.1', // server where elasticsearch is running\n        'port' =\u003e 9200\n    ],\n    ...\n]\n```\n\n### Tables / Regular Databases\n\nIf you want to use a regular database, respectively an engine that can be used via the CakePHP ORM API, then you can use\nthe table persister that ships with this plugin.\n\nTo do so you need to configure the `AuditStash.persister` option accordingly. In your `config/app_local.php` file add the\nfollowing configuration:\n\n```php\n'AuditStash' =\u003e [\n    'persister' =\u003e \\AuditStash\\Persister\\TablePersister::class,\n],\n```\n\nThe plugin will then by default try to store the logs in a table named `audit_logs`, via a table class with the alias\n`AuditLogs`, which you could create/overwrite in your application if you need.\n\nYou can find a migration in the `config/migration` folder of this plugin which you can apply to your database, this will\nadd a table named `audit_logs` with all the default columns - alternatively create the table manually. After that you\ncan bake the corresponding table class.\n\n```\nbin/cake migrations migrate -p AuditStash\nbin/cake bake model AuditLogs\n```\n\n#### Table Persister Configuration\n\nThe table persister supports various configuration options, please refer to\n[its API documentation](/src/Persister/TablePersister.php) for further information. Generally configuration can be\napplied via its `setConfig()` method:\n\n```php\n$this-\u003eaddBehavior('AuditStash.AuditLog');\n$this-\u003ebehaviors()-\u003eget('AuditLog')-\u003epersister()-\u003esetConfig([\n    'extractMetaFields' =\u003e [\n        'user.id' =\u003e 'user_id'\n    ]\n]);\n```\n\n## Using AuditStash\n\nEnabling the Audit Log in any of your table classes is as simple as adding a behavior in the `initialize()` function:\n\n```php\nclass ArticlesTable extends Table\n{\n    public function initialize(array $config = []): void\n    {\n        ...\n        $this-\u003eaddBehavior('AuditStash.AuditLog');\n    }\n}\n```\n\nRemember to execute the command line each time you change the schema of your table!\n\n### Configuring The Behavior\n\nThe `AuditLog` behavior can be configured to ignore certain fields of your table, by default it ignores the `created` and `modified` fields:\n\n```php\nclass ArticlesTable extends Table\n{\n    public function initialize(array $config = []): void\n    {\n        ...\n        $this-\u003eaddBehavior('AuditStash.AuditLog', [\n            'blacklist' =\u003e ['created', 'modified', 'another_field_name']\n        ]);\n    }\n}\n```\n\nIf you prefer, you can use a `whitelist` instead. This means that only the fields listed in that array will be tracked by the behavior:\n\n```php\npublic function initialize(array $config = []): void\n{\n    ...\n    $this-\u003eaddBehavior('AuditStash.AuditLog', [\n        'whitelist' =\u003e ['title', 'description', 'author_id']\n    ]);\n}\n```\n\nIf you have fields that contain sensitive information but still want to track their changes you can use the `sensitive` configuration:\n\n```php\npublic function initialize(array $config = []): void\n{\n    ...\n    $this-\u003eaddBehavior('AuditStash.AuditLog', [\n        'sensitive' =\u003e ['body']\n    ]);\n}\n```\n\n### Storing The Logged In User\n\nIt is often useful to store the identifier of the user that is triggering the changes in a certain table. For this purpose, `AuditStash`\nprovides the `RequestMetadata` listener class, that is capable of storing the current URL, IP and logged in user. You need to add this\nlistener to your application in the `AppController::beforeFilter()` method:\n\n```php\nuse AuditStash\\Meta\\RequestMetadata;\n...\n\nclass AppController extends Controller\n{\n    public function beforeFilter(EventInterface $event)\n    {\n        ...\n        $eventManager = $this-\u003efetchTable()-\u003egetEventManager();\n        $eventManager-\u003eon(\n            new RequestMetadata(\n                request: $this-\u003egetRequest(),\n                user: $this-\u003egetRequest()-\u003egetAttribute('identity')?-\u003egetIdentifier()\n            )\n        );\n    }\n}\n```\n\nThe above code assumes that you will trigger the table operations from the controller, using the default Table class for the controller.\nIf you plan to use other Table classes for saving or deleting inside the same controller, it is advised that you attach the listener\nglobally:\n\n\n```php\nuse AuditStash\\Meta\\RequestMetadata;\nuse Cake\\Event\\EventManager;\n...\n\nclass AppController extends Controller\n{\n    public function beforeFilter(EventInterface $event)\n    {\n        ...\n        EventManager::instance()-\u003eon(\n            new RequestMetadata(\n                request: $this-\u003egetRequest(),\n                user: $this-\u003egetRequest()-\u003egetAttribute('identity')?-\u003egetIdentifier()\n            )\n        );\n    }\n}\n```\n\n### Storing Extra Information In Logs\n\n`AuditStash` is also capable of storing arbitrary data for each of the logged events. You can use the `ApplicationMetadata` listener or\ncreate your own. If you choose to use `ApplicationMetadata`, your logs will contain the `app_name` key stored and any extra information\nyour may have provided. You can configure this listener anywhere in your application, such as the `bootstrap.php` file or, again, directly\nin your AppController.\n\n\n```php\nuse AuditStash\\Meta\\ApplicationMetadata;\nuse Cake\\Event\\EventManager;\n\nEventManager::instance()-\u003eon(new ApplicationMetadata('my_blog_app', [\n    'server' =\u003e $theServerID,\n    'extra' =\u003e $somExtraInformation,\n    'moon_phase' =\u003e $currentMoonPhase\n]));\n\n```\n\nImplementing your own metadata listeners is as simple as attaching the listener to the `AuditStash.beforeLog` event. For example:\n\n```php\nEventManager::instance()-\u003eon('AuditStash.beforeLog', function (EventInterface $event, array $logs): void {\n    foreach ($logs as $log) {\n        $log-\u003esetMetaInfo($log-\u003egetMetaInfo() + ['extra' =\u003e 'This is extra data to be stored']);\n    }\n});\n```\n\n### Implementing Your Own Persister Strategies\n\nThere are valid reasons for wanting to use a different persist engine for your audit logs. Luckily, this plugin allows you to implement\nyour own storage engines. It is as simple as implementing the `PersisterInterface` interface:\n\n```php\nuse AuditStash\\PersisterInterface;\n\nclass MyPersister implements PersisterInterface\n{\n    public function logEvents(array $auditLogs): void\n    {\n        foreach ($auditLogs as $log) {\n            $eventType = $log-\u003egetEventType();\n            $data = [\n                'timestamp' =\u003e $log-\u003egetTimestamp(),\n                'transaction' =\u003e $log-\u003egetTransactionId(),\n                'type' =\u003e $log-\u003egetEventType(),\n                'primary_key' =\u003e $log-\u003egetId(),\n                'source' =\u003e $log-\u003egetSourceName(),\n                'parent_source' =\u003e $log-\u003egetParentSourceName(),\n                'original' =\u003e json_encode($log-\u003egetOriginal()),\n                'changed' =\u003e $eventType === 'delete' ? null : json_encode($log-\u003egetChanged()),\n                'meta' =\u003e json_encode($log-\u003egetMetaInfo())\n            ];\n            $storage = new MyStorage();\n            $storage-\u003esave($data);\n        }\n    }\n}\n```\n\nFinally, you need to configure `AuditStash` to use your new persister. In the `config/app.php` file add the following\nlines:\n\n```php\n'AuditStash' =\u003e [\n    'persister' =\u003e 'App\\Namespace\\For\\Your\\Persister'\n]\n```\n\nor if you are using as standalone via\n\n```php\n\\Cake\\Core\\Configure::write('AuditStash.persister', 'App\\Namespace\\For\\Your\\DatabasePersister');\n```\n\nThe configuration contains the fully namespaced class name of your persister.\n\n### Working With Transactional Queries\n\nOccasionally, you may want to wrap a number of database changes in a transaction, so that it can be rolled back if one\npart of the process fails. There are two ways to accomplish this. The easiest is to change your save strategy to use\n`afterSave` instead of `afterCommit`. In your applications configuration, such as `config/app.php`:\n\n```php\n'AuditStash' =\u003e [\n    'saveType' =\u003e 'afterSave',\n]\n```\n\nThat's it if you use afterSave. You should read up on the difference between the two as there are drawbacks:\nhttps://book.cakephp.org/4/en/orm/table-objects.html#aftersave\n\nIf you are using the default afterCommit, in order to create audit logs during a transaction, some additional setup is\nrequired. First create the file `src/Model/Audit/AuditTrail.php` with the following:\n\n```php\n\u003c?php\ndeclare(strict_types=1);\n\nnamespace App\\Model\\Audit;\n\nuse Cake\\Utility\\Text;\nuse SplObjectStorage;\n\nclass AuditTrail\n{\n    protected SplObjectStorage $_auditQueue;\n    protected string $_auditTransaction;\n\n    public function __construct()\n    {\n        $this-\u003e_auditQueue = new SplObjectStorage;\n        $this-\u003e_auditTransaction = Text::uuid();\n    }\n\n    public function toSaveOptions(): array\n    {\n        return [\n            '_auditQueue' =\u003e $this-\u003e_auditQueue,\n            '_auditTransaction' =\u003e $this-\u003e_auditTransaction\n        ];\n    }\n}\n```\n\nAnywhere you wish to use `Connection::transactional()`, you will need to first include the following at the top of the file:\n\n```php\nuse App\\Model\\Audit\\AuditTrail;\nuse Cake\\Event\\Event;\nuse \\ArrayObject\n```\n\nYour transaction should then look similar to this example of a BookmarksController:\n\n```php\n$trail = new AuditTrail();\n$success = $this-\u003eBookmarks-\u003econnection()-\u003etransactional(function () use ($trail) {\n    $bookmark = $this-\u003eBookmarks-\u003enewEntity();\n    $bookmark1-\u003esave($data1, $trail-\u003etoSaveOptions());\n    $bookmark2 = $this-\u003eBookmarks-\u003enewEntity();\n    $bookmark2-\u003esave($data2, $trail-\u003etoSaveOptions());\n    ...\n    $bookmarkN = $this-\u003eBookmarks-\u003enewEntity();\n    $bookmarkN-\u003esave($dataN, $trail-\u003etoSaveOptions());\n});\n\nif ($success) {\n    $event = new Event('Model.afterCommit', $this-\u003eBookmarks);\n    $this-\u003eBookmarks-\u003e-\u003ebehaviors()-\u003eget('AuditLog')-\u003eafterCommit(\n        $event,\n        $result,\n        new ArrayObject($auditTrail-\u003etoSaveOptions())\n    );\n}\n```\n\nThis will save all audit info for your objects, as well as audits for any associated data. Please note, `$result` must\nbe an instance of an Object. Do not change the text \"Model.afterCommit\".\n\n## Testing\n\nBy default, the test suite will not run elastic. If you are an elastic user and wish to test against a local instance\nthen you will need to set the environment variable:\n\n```console\nelastic_dsn=\"Cake\\ElasticSearch\\Datasource\\Connection://127.0.0.1:9200?driver=Cake\\ElasticSearch\\Datasource\\Connection\" vendor/bin/phpunit\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Florenzo%2Faudit-stash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Florenzo%2Faudit-stash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Florenzo%2Faudit-stash/lists"}