mirror of
https://gitlab.com/signalytic/client-external/streamline/streamline-emr.git
synced 2026-09-12 19:21:33 +00:00
Build modified streamline images
The official image from streamline does not currently work for the arm64 platform. As a temporary measure, the source code and docker build scripts have been lifted from the official images and are used to build locally. Some additional modifications are made to reduce overall image size, these are documented in docker/README.md
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
use OwenIt\Auditing\Contracts\AttributeEncoder;
|
||||
|
||||
trait Audit
|
||||
{
|
||||
/**
|
||||
* Audit data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* The Audit attributes that belong to the metadata.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $metadata = [];
|
||||
|
||||
/**
|
||||
* The Auditable attributes that were modified.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modified = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function auditable()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
$morphPrefix = Config::get('audit.user.morph_prefix', 'user');
|
||||
|
||||
return $this->morphTo(__FUNCTION__, $morphPrefix . '_type', $morphPrefix . '_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConnectionName()
|
||||
{
|
||||
return Config::get('audit.drivers.database.connection');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTable(): string
|
||||
{
|
||||
return Config::get('audit.drivers.database.table', parent::getTable());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolveData(): array
|
||||
{
|
||||
$morphPrefix = Config::get('audit.user.morph_prefix', 'user');
|
||||
|
||||
// Metadata
|
||||
$this->data = [
|
||||
'audit_id' => $this->id,
|
||||
'audit_event' => $this->event,
|
||||
'audit_tags' => $this->tags,
|
||||
'audit_created_at' => $this->serializeDate($this->{$this->getCreatedAtColumn()}),
|
||||
'audit_updated_at' => $this->serializeDate($this->{$this->getUpdatedAtColumn()}),
|
||||
'user_id' => $this->getAttribute($morphPrefix . '_id'),
|
||||
'user_type' => $this->getAttribute($morphPrefix . '_type'),
|
||||
];
|
||||
|
||||
// add resolvers data to metadata
|
||||
$resolverData = [];
|
||||
foreach (array_keys(Config::get('audit.resolvers', [])) as $name) {
|
||||
$resolverData['audit_' . $name] = $this->$name;
|
||||
}
|
||||
$this->data = array_merge($this->data, $resolverData);
|
||||
|
||||
if ($this->user) {
|
||||
foreach ($this->user->getArrayableAttributes() as $attribute => $value) {
|
||||
$this->data['user_' . $attribute] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->metadata = array_keys($this->data);
|
||||
|
||||
// Modified Auditable attributes
|
||||
foreach ($this->new_values as $key => $value) {
|
||||
$this->data['new_' . $key] = $value;
|
||||
}
|
||||
|
||||
foreach ($this->old_values as $key => $value) {
|
||||
$this->data['old_' . $key] = $value;
|
||||
}
|
||||
|
||||
$this->modified = array_diff_key(array_keys($this->data), $this->metadata);
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted value of an Eloquent model.
|
||||
*
|
||||
* @param Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFormattedValue(Model $model, string $key, $value)
|
||||
{
|
||||
// Apply defined get mutator
|
||||
if ($model->hasGetMutator($key)) {
|
||||
return $model->mutateAttribute($key, $value);
|
||||
}
|
||||
|
||||
if (method_exists($model, 'hasAttributeMutator') && $model->hasAttributeMutator($key)) {
|
||||
return $model->mutateAttributeMarkedAttribute($key, $value);
|
||||
}
|
||||
|
||||
if (array_key_exists(
|
||||
$key,
|
||||
$model->getCasts()
|
||||
) && $model->getCasts()[$key] == 'Illuminate\Database\Eloquent\Casts\AsArrayObject') {
|
||||
$arrayObject = new \Illuminate\Database\Eloquent\Casts\ArrayObject(json_decode($value, true) ?: []);
|
||||
return $arrayObject;
|
||||
}
|
||||
|
||||
// Cast to native PHP type
|
||||
if ($model->hasCast($key)) {
|
||||
if ($model->getCastType($key) == 'datetime' ) {
|
||||
$value = $this->castDatetimeUTC($model, $value);
|
||||
}
|
||||
|
||||
unset($model->classCastCache[$key]);
|
||||
|
||||
return $model->castAttribute($key, $value);
|
||||
}
|
||||
|
||||
// Honour DateTime attribute
|
||||
if ($value !== null && in_array($key, $model->getDates(), true)) {
|
||||
return $model->asDateTime($this->castDatetimeUTC($model, $value));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function castDatetimeUTC($model, $value)
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value)) {
|
||||
return Date::instance(Carbon::createFromFormat('Y-m-d', $value, Date::now('UTC')->getTimezone())->startOfDay());
|
||||
}
|
||||
|
||||
if (preg_match('/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/', $value)) {
|
||||
return Date::instance(Carbon::createFromFormat('Y-m-d H:i:s', $value, Date::now('UTC')->getTimezone()));
|
||||
}
|
||||
|
||||
try {
|
||||
return Date::createFromFormat($model->getDateFormat(), $value, Date::now('UTC')->getTimezone());
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDataValue(string $key)
|
||||
{
|
||||
if (!array_key_exists($key, $this->data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = $this->data[$key];
|
||||
|
||||
// User value
|
||||
if ($this->user && Str::startsWith($key, 'user_')) {
|
||||
return $this->getFormattedValue($this->user, substr($key, 5), $value);
|
||||
}
|
||||
|
||||
// Auditable value
|
||||
if ($this->auditable && Str::startsWith($key, ['new_', 'old_'])) {
|
||||
$attribute = substr($key, 4);
|
||||
|
||||
return $this->getFormattedValue(
|
||||
$this->auditable,
|
||||
$attribute,
|
||||
$this->decodeAttributeValue($this->auditable, $attribute, $value)
|
||||
);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode attribute value.
|
||||
*
|
||||
* @param Contracts\Auditable $auditable
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function decodeAttributeValue(Contracts\Auditable $auditable, string $attribute, $value)
|
||||
{
|
||||
$attributeModifiers = $auditable->getAttributeModifiers();
|
||||
|
||||
if (!array_key_exists($attribute, $attributeModifiers)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$attributeDecoder = $attributeModifiers[$attribute];
|
||||
|
||||
if (is_subclass_of($attributeDecoder, AttributeEncoder::class)) {
|
||||
return call_user_func([$attributeDecoder, 'decode'], $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadata(bool $json = false, int $options = 0, int $depth = 512)
|
||||
{
|
||||
if (empty($this->data)) {
|
||||
$this->resolveData();
|
||||
}
|
||||
|
||||
$metadata = [];
|
||||
|
||||
foreach ($this->metadata as $key) {
|
||||
$value = $this->getDataValue($key);
|
||||
$metadata[$key] = $value;
|
||||
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
$metadata[$key] = !is_null($this->auditable) ? $this->auditable->serializeDate($value) : $this->serializeDate($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $json ? json_encode($metadata, $options, $depth) : $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getModified(bool $json = false, int $options = 0, int $depth = 512)
|
||||
{
|
||||
if (empty($this->data)) {
|
||||
$this->resolveData();
|
||||
}
|
||||
|
||||
$modified = [];
|
||||
|
||||
foreach ($this->modified as $key) {
|
||||
$attribute = substr($key, 4);
|
||||
$state = substr($key, 0, 3);
|
||||
|
||||
$value = $this->getDataValue($key);
|
||||
$modified[$attribute][$state] = $value;
|
||||
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
$modified[$attribute][$state] = !is_null($this->auditable) ? $this->auditable->serializeDate($value) : $this->serializeDate($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $json ? json_encode($modified, $options, $depth) : $modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Audit tags as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTags(): array
|
||||
{
|
||||
return preg_split('/,/', $this->tags, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,774 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use OwenIt\Auditing\Contracts\AttributeEncoder;
|
||||
use OwenIt\Auditing\Contracts\AttributeRedactor;
|
||||
use OwenIt\Auditing\Contracts\Resolver;
|
||||
use OwenIt\Auditing\Events\AuditCustom;
|
||||
use OwenIt\Auditing\Exceptions\AuditableTransitionException;
|
||||
use OwenIt\Auditing\Exceptions\AuditingException;
|
||||
|
||||
trait Auditable
|
||||
{
|
||||
/**
|
||||
* Auditable attributes excluded from the Audit.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $excludedAttributes = [];
|
||||
|
||||
/**
|
||||
* Audit event name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $auditEvent;
|
||||
|
||||
/**
|
||||
* Is auditing disabled?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $auditingDisabled = false;
|
||||
|
||||
/**
|
||||
* Property may set custom event data to register
|
||||
* @var null|array
|
||||
*/
|
||||
public $auditCustomOld = null;
|
||||
|
||||
/**
|
||||
* Property may set custom event data to register
|
||||
* @var null|array
|
||||
*/
|
||||
public $auditCustomNew = null;
|
||||
|
||||
/**
|
||||
* If this is a custom event (as opposed to an eloquent event
|
||||
* @var bool
|
||||
*/
|
||||
public $isCustomEvent = false;
|
||||
|
||||
/**
|
||||
* @var array Preloaded data to be used by resolvers
|
||||
*/
|
||||
public $preloadedResolverData = [];
|
||||
|
||||
/**
|
||||
* Auditable boot logic.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function bootAuditable()
|
||||
{
|
||||
if (static::isAuditingEnabled()) {
|
||||
static::observe(new AuditableObserver());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function audits(): MorphMany
|
||||
{
|
||||
return $this->morphMany(
|
||||
Config::get('audit.implementation', Models\Audit::class),
|
||||
'auditable'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the Auditable attributes to exclude from the Audit.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function resolveAuditExclusions()
|
||||
{
|
||||
$this->excludedAttributes = $this->getAuditExclude();
|
||||
|
||||
// When in strict mode, hidden and non visible attributes are excluded
|
||||
if ($this->getAuditStrict()) {
|
||||
// Hidden attributes
|
||||
$this->excludedAttributes = array_merge($this->excludedAttributes, $this->hidden);
|
||||
|
||||
// Non visible attributes
|
||||
if ($this->visible) {
|
||||
$invisible = array_diff(array_keys($this->attributes), $this->visible);
|
||||
|
||||
$this->excludedAttributes = array_merge($this->excludedAttributes, $invisible);
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude Timestamps
|
||||
if (!$this->getAuditTimestamps()) {
|
||||
if ($this->getCreatedAtColumn()) {
|
||||
$this->excludedAttributes[] = $this->getCreatedAtColumn();
|
||||
}
|
||||
if ($this->getUpdatedAtColumn()) {
|
||||
$this->excludedAttributes[] = $this->getUpdatedAtColumn();
|
||||
}
|
||||
if (in_array(SoftDeletes::class, class_uses_recursive(get_class($this)))) {
|
||||
$this->excludedAttributes[] = $this->getDeletedAtColumn();
|
||||
}
|
||||
}
|
||||
|
||||
// Valid attributes are all those that made it out of the exclusion array
|
||||
$attributes = Arr::except($this->attributes, $this->excludedAttributes);
|
||||
|
||||
foreach ($attributes as $attribute => $value) {
|
||||
// Apart from null, non scalar values will be excluded
|
||||
if (
|
||||
is_array($value) ||
|
||||
(is_object($value) &&
|
||||
!method_exists($value, '__toString') &&
|
||||
!($value instanceof \UnitEnum))
|
||||
) {
|
||||
$this->excludedAttributes[] = $attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAuditExclude(): array
|
||||
{
|
||||
return $this->auditExclude ?? Config::get('audit.exclude', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAuditInclude(): array
|
||||
{
|
||||
return $this->auditInclude ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old/new attributes of a retrieved event.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRetrievedEventAttributes(): array
|
||||
{
|
||||
// This is a read event with no attribute changes,
|
||||
// only metadata will be stored in the Audit
|
||||
|
||||
return [
|
||||
[],
|
||||
[],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old/new attributes of a created event.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getCreatedEventAttributes(): array
|
||||
{
|
||||
$new = [];
|
||||
|
||||
foreach ($this->attributes as $attribute => $value) {
|
||||
if ($this->isAttributeAuditable($attribute)) {
|
||||
$new[$attribute] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
[],
|
||||
$new,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getCustomEventAttributes(): array
|
||||
{
|
||||
return [
|
||||
$this->auditCustomOld,
|
||||
$this->auditCustomNew
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old/new attributes of an updated event.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUpdatedEventAttributes(): array
|
||||
{
|
||||
$old = [];
|
||||
$new = [];
|
||||
|
||||
foreach ($this->getDirty() as $attribute => $value) {
|
||||
if ($this->isAttributeAuditable($attribute)) {
|
||||
$old[$attribute] = Arr::get($this->original, $attribute);
|
||||
$new[$attribute] = Arr::get($this->attributes, $attribute);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$old,
|
||||
$new,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old/new attributes of a deleted event.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDeletedEventAttributes(): array
|
||||
{
|
||||
$old = [];
|
||||
|
||||
foreach ($this->attributes as $attribute => $value) {
|
||||
if ($this->isAttributeAuditable($attribute)) {
|
||||
$old[$attribute] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$old,
|
||||
[],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the old/new attributes of a restored event.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRestoredEventAttributes(): array
|
||||
{
|
||||
// A restored event is just a deleted event in reverse
|
||||
return array_reverse($this->getDeletedEventAttributes());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function readyForAuditing(): bool
|
||||
{
|
||||
if (static::$auditingDisabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isCustomEvent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->isEventAuditable($this->auditEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify attribute value.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
* @throws AuditingException
|
||||
*
|
||||
*/
|
||||
protected function modifyAttributeValue(string $attribute, $value)
|
||||
{
|
||||
$attributeModifiers = $this->getAttributeModifiers();
|
||||
|
||||
if (!array_key_exists($attribute, $attributeModifiers)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$attributeModifier = $attributeModifiers[$attribute];
|
||||
|
||||
if (is_subclass_of($attributeModifier, AttributeRedactor::class)) {
|
||||
return call_user_func([$attributeModifier, 'redact'], $value);
|
||||
}
|
||||
|
||||
if (is_subclass_of($attributeModifier, AttributeEncoder::class)) {
|
||||
return call_user_func([$attributeModifier, 'encode'], $value);
|
||||
}
|
||||
|
||||
throw new AuditingException(sprintf('Invalid AttributeModifier implementation: %s', $attributeModifier));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toAudit(): array
|
||||
{
|
||||
if (!$this->readyForAuditing()) {
|
||||
throw new AuditingException('A valid audit event has not been set');
|
||||
}
|
||||
|
||||
$attributeGetter = $this->resolveAttributeGetter($this->auditEvent);
|
||||
|
||||
if (!method_exists($this, $attributeGetter)) {
|
||||
throw new AuditingException(sprintf(
|
||||
'Unable to handle "%s" event, %s() method missing',
|
||||
$this->auditEvent,
|
||||
$attributeGetter
|
||||
));
|
||||
}
|
||||
|
||||
$this->resolveAuditExclusions();
|
||||
|
||||
list($old, $new) = $this->$attributeGetter();
|
||||
|
||||
if ($this->getAttributeModifiers() && !$this->isCustomEvent) {
|
||||
foreach ($old as $attribute => $value) {
|
||||
$old[$attribute] = $this->modifyAttributeValue($attribute, $value);
|
||||
}
|
||||
|
||||
foreach ($new as $attribute => $value) {
|
||||
$new[$attribute] = $this->modifyAttributeValue($attribute, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$morphPrefix = Config::get('audit.user.morph_prefix', 'user');
|
||||
|
||||
$tags = implode(',', $this->generateTags());
|
||||
|
||||
$user = $this->resolveUser();
|
||||
|
||||
return $this->transformAudit(array_merge([
|
||||
'old_values' => $old,
|
||||
'new_values' => $new,
|
||||
'event' => $this->auditEvent,
|
||||
'auditable_id' => $this->getKey(),
|
||||
'auditable_type' => $this->getMorphClass(),
|
||||
$morphPrefix . '_id' => $user ? $user->getAuthIdentifier() : null,
|
||||
$morphPrefix . '_type' => $user ? $user->getMorphClass() : null,
|
||||
'tags' => empty($tags) ? null : $tags,
|
||||
], $this->runResolvers()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transformAudit(array $data): array
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the User.
|
||||
*
|
||||
* @return mixed|null
|
||||
* @throws AuditingException
|
||||
*
|
||||
*/
|
||||
protected function resolveUser()
|
||||
{
|
||||
if (! empty($this->preloadedResolverData['user'] ?? null)) {
|
||||
return $this->preloadedResolverData['user'];
|
||||
}
|
||||
|
||||
$userResolver = Config::get('audit.user.resolver');
|
||||
|
||||
if (is_null($userResolver) && Config::has('audit.resolver') && !Config::has('audit.user.resolver')) {
|
||||
trigger_error(
|
||||
'The config file audit.php is not updated to the new version 13.0. Please see https://laravel-auditing.com/guide/upgrading.html',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$userResolver = Config::get('audit.resolver.user');
|
||||
}
|
||||
|
||||
if (is_subclass_of($userResolver, \OwenIt\Auditing\Contracts\UserResolver::class)) {
|
||||
return call_user_func([$userResolver, 'resolve'], $this);
|
||||
}
|
||||
|
||||
throw new AuditingException('Invalid UserResolver implementation');
|
||||
}
|
||||
|
||||
protected function runResolvers(): array
|
||||
{
|
||||
$resolved = [];
|
||||
$resolvers = Config::get('audit.resolvers', []);
|
||||
if (empty($resolvers) && Config::has('audit.resolver')) {
|
||||
trigger_error(
|
||||
'The config file audit.php is not updated to the new version 13.0. Please see https://laravel-auditing.com/guide/upgrading.html',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$resolvers = Config::get('audit.resolver', []);
|
||||
}
|
||||
|
||||
foreach ($resolvers as $name => $implementation) {
|
||||
if (empty($implementation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_subclass_of($implementation, Resolver::class)) {
|
||||
throw new AuditingException('Invalid Resolver implementation for: ' . $name);
|
||||
}
|
||||
$resolved[$name] = call_user_func([$implementation, 'resolve'], $this);
|
||||
}
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
public function preloadResolverData()
|
||||
{
|
||||
$this->preloadedResolverData = $this->runResolvers();
|
||||
|
||||
if (!empty ($this->resolveUser())) {
|
||||
$this->preloadedResolverData['user'] = $this->resolveUser();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an attribute is eligible for auditing.
|
||||
*
|
||||
* @param string $attribute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAttributeAuditable(string $attribute): bool
|
||||
{
|
||||
// The attribute should not be audited
|
||||
if (in_array($attribute, $this->excludedAttributes, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The attribute is auditable when explicitly
|
||||
// listed or when the include array is empty
|
||||
$include = $this->getAuditInclude();
|
||||
|
||||
return empty($include) || in_array($attribute, $include, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an event is auditable.
|
||||
*
|
||||
* @param string $event
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isEventAuditable($event): bool
|
||||
{
|
||||
return is_string($this->resolveAttributeGetter($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute getter method resolver.
|
||||
*
|
||||
* @param string $event
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function resolveAttributeGetter($event)
|
||||
{
|
||||
if (empty($event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isCustomEvent) {
|
||||
return 'getCustomEventAttributes';
|
||||
}
|
||||
|
||||
foreach ($this->getAuditEvents() as $key => $value) {
|
||||
$auditableEvent = is_int($key) ? $value : $key;
|
||||
|
||||
$auditableEventRegex = sprintf('/%s/', preg_replace('/\*+/', '.*', $auditableEvent));
|
||||
|
||||
if (preg_match($auditableEventRegex, $event)) {
|
||||
return is_int($key) ? sprintf('get%sEventAttributes', ucfirst($event)) : $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAuditEvent(string $event): Contracts\Auditable
|
||||
{
|
||||
$this->auditEvent = $this->isEventAuditable($event) ? $event : null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuditEvent()
|
||||
{
|
||||
return $this->auditEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuditEvents(): array
|
||||
{
|
||||
return $this->auditEvents ?? Config::get('audit.events', [
|
||||
'created',
|
||||
'updated',
|
||||
'deleted',
|
||||
'restored',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable Auditing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function disableAuditing()
|
||||
{
|
||||
static::$auditingDisabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable Auditing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function enableAuditing()
|
||||
{
|
||||
static::$auditingDisabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether auditing is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAuditingEnabled(): bool
|
||||
{
|
||||
if (App::runningInConsole()) {
|
||||
return Config::get('audit.enabled', true) && Config::get('audit.console', false);
|
||||
}
|
||||
|
||||
return Config::get('audit.enabled', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuditStrict(): bool
|
||||
{
|
||||
return $this->auditStrict ?? Config::get('audit.strict', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuditTimestamps(): bool
|
||||
{
|
||||
return $this->auditTimestamps ?? Config::get('audit.timestamps', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuditDriver()
|
||||
{
|
||||
return $this->auditDriver ?? Config::get('audit.driver', 'database');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAuditThreshold(): int
|
||||
{
|
||||
return $this->auditThreshold ?? Config::get('audit.threshold', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttributeModifiers(): array
|
||||
{
|
||||
return $this->attributeModifiers ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generateTags(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transitionTo(Contracts\Audit $audit, bool $old = false): Contracts\Auditable
|
||||
{
|
||||
// The Audit must be for an Auditable model of this type
|
||||
if ($this->getMorphClass() !== $audit->auditable_type) {
|
||||
throw new AuditableTransitionException(sprintf(
|
||||
'Expected Auditable type %s, got %s instead',
|
||||
$this->getMorphClass(),
|
||||
$audit->auditable_type
|
||||
));
|
||||
}
|
||||
|
||||
// The Audit must be for this specific Auditable model
|
||||
if ($this->getKey() !== $audit->auditable_id) {
|
||||
throw new AuditableTransitionException(sprintf(
|
||||
'Expected Auditable id (%s)%s, got (%s)%s instead',
|
||||
gettype($this->getKey()),
|
||||
$this->getKey(),
|
||||
gettype($audit->auditable_id),
|
||||
$audit->auditable_id
|
||||
));
|
||||
}
|
||||
|
||||
// Redacted data should not be used when transitioning states
|
||||
foreach ($this->getAttributeModifiers() as $attribute => $modifier) {
|
||||
if (is_subclass_of($modifier, AttributeRedactor::class)) {
|
||||
throw new AuditableTransitionException('Cannot transition states when an AttributeRedactor is set');
|
||||
}
|
||||
}
|
||||
|
||||
// The attribute compatibility between the Audit and the Auditable model must be met
|
||||
$modified = $audit->getModified();
|
||||
|
||||
if ($incompatibilities = array_diff_key($modified, $this->getAttributes())) {
|
||||
throw new AuditableTransitionException(sprintf(
|
||||
'Incompatibility between [%s:%s] and [%s:%s]',
|
||||
$this->getMorphClass(),
|
||||
$this->getKey(),
|
||||
get_class($audit),
|
||||
$audit->getKey()
|
||||
), array_keys($incompatibilities));
|
||||
}
|
||||
|
||||
$key = $old ? 'old' : 'new';
|
||||
|
||||
foreach ($modified as $attribute => $value) {
|
||||
if (array_key_exists($key, $value)) {
|
||||
$this->setAttribute($attribute, $value[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pivot help methods
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Methods for auditing pivot actions
|
||||
|
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param string $relationName
|
||||
* @param mixed $id
|
||||
* @param array $attributes
|
||||
* @param bool $touch
|
||||
* @param array $columns
|
||||
* @return void
|
||||
* @throws AuditingException
|
||||
*/
|
||||
public function auditAttach(string $relationName, $id, array $attributes = [], $touch = true, $columns = ['*'])
|
||||
{
|
||||
if (!method_exists($this, $relationName) || !method_exists($this->{$relationName}(), 'attach')) {
|
||||
throw new AuditingException('Relationship ' . $relationName . ' was not found or does not support method attach');
|
||||
}
|
||||
|
||||
$old = $this->{$relationName}()->get($columns);
|
||||
$this->{$relationName}()->attach($id, $attributes, $touch);
|
||||
$new = $this->{$relationName}()->get($columns);
|
||||
$this->dispatchRelationAuditEvent($relationName, 'attach', $old, $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $relationName
|
||||
* @param mixed $ids
|
||||
* @param bool $touch
|
||||
* @param array $columns
|
||||
* @return int
|
||||
* @throws AuditingException
|
||||
*/
|
||||
public function auditDetach(string $relationName, $ids = null, $touch = true, $columns = ['*'])
|
||||
{
|
||||
if (!method_exists($this, $relationName) || !method_exists($this->{$relationName}(), 'detach')) {
|
||||
throw new AuditingException('Relationship ' . $relationName . ' was not found or does not support method detach');
|
||||
}
|
||||
|
||||
$old = $this->{$relationName}()->get($columns);
|
||||
$results = $this->{$relationName}()->detach($ids, $touch);
|
||||
$new = $this->{$relationName}()->get($columns);
|
||||
$this->dispatchRelationAuditEvent($relationName, 'detach', $old, $new);
|
||||
|
||||
return empty($results) ? 0 : $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $relationName
|
||||
* @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array $ids
|
||||
* @param bool $detaching
|
||||
* @param array $columns
|
||||
* @return array
|
||||
* @throws AuditingException
|
||||
*/
|
||||
public function auditSync($relationName, $ids, $detaching = true, $columns = ['*'])
|
||||
{
|
||||
if (!method_exists($this, $relationName) || !method_exists($this->{$relationName}(), 'sync')) {
|
||||
throw new AuditingException('Relationship ' . $relationName . ' was not found or does not support method sync');
|
||||
}
|
||||
|
||||
$old = $this->{$relationName}()->get($columns);
|
||||
$changes = $this->{$relationName}()->sync($ids, $detaching);
|
||||
if (collect($changes)->flatten()->isEmpty()) {
|
||||
$old = $new = collect([]);
|
||||
} else {
|
||||
$new = $this->{$relationName}()->get($columns);
|
||||
}
|
||||
$this->dispatchRelationAuditEvent($relationName, 'sync', $old, $new);
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $relationName
|
||||
* @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array $ids
|
||||
* @param array $columns
|
||||
* @return array
|
||||
* @throws AuditingException
|
||||
*/
|
||||
public function auditSyncWithoutDetaching(string $relationName, $ids, $columns = ['*'])
|
||||
{
|
||||
if (!method_exists($this, $relationName) || !method_exists($this->{$relationName}(), 'syncWithoutDetaching')) {
|
||||
throw new AuditingException('Relationship ' . $relationName . ' was not found or does not support method syncWithoutDetaching');
|
||||
}
|
||||
|
||||
return $this->auditSync($relationName, $ids, false, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $relationName
|
||||
* @param string $event
|
||||
* @param \Illuminate\Support\Collection $old
|
||||
* @param \Illuminate\Support\Collection $new
|
||||
* @return void
|
||||
*/
|
||||
private function dispatchRelationAuditEvent($relationName, $event, $old, $new)
|
||||
{
|
||||
$this->auditCustomOld[$relationName] = $old->diff($new)->toArray();
|
||||
$this->auditCustomNew[$relationName] = $new->diff($old)->toArray();
|
||||
|
||||
if (
|
||||
empty($this->auditCustomOld[$relationName]) &&
|
||||
empty($this->auditCustomNew[$relationName])
|
||||
) {
|
||||
$this->auditCustomOld = $this->auditCustomNew = [];
|
||||
}
|
||||
|
||||
$this->auditEvent = $event;
|
||||
$this->isCustomEvent = true;
|
||||
Event::dispatch(AuditCustom::class, [$this]);
|
||||
$this->isCustomEvent = false;
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing;
|
||||
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
use OwenIt\Auditing\Events\DispatchAudit;
|
||||
use OwenIt\Auditing\Events\DispatchingAudit;
|
||||
use OwenIt\Auditing\Facades\Auditor;
|
||||
|
||||
class AuditableObserver
|
||||
{
|
||||
/**
|
||||
* Is the model being restored?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $restoring = false;
|
||||
|
||||
/**
|
||||
* Handle the retrieved event.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function retrieved(Auditable $model)
|
||||
{
|
||||
$this->dispatchAudit($model->setAuditEvent('retrieved'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the created event.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function created(Auditable $model)
|
||||
{
|
||||
$this->dispatchAudit($model->setAuditEvent('created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the updated event.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updated(Auditable $model)
|
||||
{
|
||||
// Ignore the updated event when restoring
|
||||
if (!static::$restoring) {
|
||||
$this->dispatchAudit($model->setAuditEvent('updated'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the deleted event.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(Auditable $model)
|
||||
{
|
||||
$this->dispatchAudit($model->setAuditEvent('deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the restoring event.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function restoring(Auditable $model)
|
||||
{
|
||||
// When restoring a model, an updated event is also fired.
|
||||
// By keeping track of the main event that took place,
|
||||
// we avoid creating a second audit with wrong values
|
||||
static::$restoring = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the restored event.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function restored(Auditable $model)
|
||||
{
|
||||
$this->dispatchAudit($model->setAuditEvent('restored'));
|
||||
|
||||
// Once the model is restored, we need to put everything back
|
||||
// as before, in case a legitimate update event is fired
|
||||
static::$restoring = false;
|
||||
}
|
||||
|
||||
protected function dispatchAudit(Auditable $model)
|
||||
{
|
||||
if (!$model->readyForAuditing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$model->preloadResolverData();
|
||||
if (!Config::get('audit.queue.enable', false)) {
|
||||
return Auditor::execute($model);
|
||||
}
|
||||
|
||||
if (!$this->fireDispatchingAuditEvent($model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Unload the relations to prevent large amounts of unnecessary data from being serialized.
|
||||
app()->make('events')->dispatch(new DispatchAudit($model->withoutRelations()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the Auditing event.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function fireDispatchingAuditEvent(Auditable $model): bool
|
||||
{
|
||||
return app()->make('events')
|
||||
->until(new DispatchingAudit($model)) !== false;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing;
|
||||
|
||||
if (app() instanceof \Illuminate\Foundation\Application) {
|
||||
class_alias(\Illuminate\Foundation\Support\Providers\EventServiceProvider::class, '\OwenIt\Auditing\ServiceProvider');
|
||||
} else {
|
||||
class_alias(\Laravel\Lumen\Providers\EventServiceProvider::class, '\OwenIt\Auditing\ServiceProvider');
|
||||
}
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use OwenIt\Auditing\Events\AuditCustom;
|
||||
use OwenIt\Auditing\Events\DispatchAudit;
|
||||
use OwenIt\Auditing\Listeners\RecordCustomAudit;
|
||||
use OwenIt\Auditing\Listeners\ProcessDispatchAudit;
|
||||
|
||||
class AuditingEventServiceProvider extends ServiceProvider
|
||||
{
|
||||
protected $listen = [
|
||||
AuditCustom::class => [
|
||||
RecordCustomAudit::class,
|
||||
],
|
||||
DispatchAudit::class => [
|
||||
ProcessDispatchAudit::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing;
|
||||
|
||||
use Illuminate\Contracts\Support\DeferrableProvider;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use OwenIt\Auditing\Console\AuditDriverCommand;
|
||||
use OwenIt\Auditing\Console\AuditResolverCommand;
|
||||
use OwenIt\Auditing\Console\InstallCommand;
|
||||
use OwenIt\Auditing\Contracts\Auditor;
|
||||
|
||||
class AuditingServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->registerPublishing();
|
||||
$this->mergeConfigFrom(__DIR__ . '/../config/audit.php', 'audit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->commands([
|
||||
AuditDriverCommand::class,
|
||||
AuditResolverCommand::class,
|
||||
InstallCommand::class,
|
||||
]);
|
||||
|
||||
$this->app->singleton(Auditor::class, function ($app) {
|
||||
return new \OwenIt\Auditing\Auditor($app);
|
||||
});
|
||||
|
||||
$this->app->register(AuditingEventServiceProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the package's publishable resources.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function registerPublishing()
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
// Lumen lacks a config_path() helper, so we use base_path()
|
||||
$this->publishes([
|
||||
__DIR__ . '/../config/audit.php' => base_path('config/audit.php'),
|
||||
], 'config');
|
||||
|
||||
if (!class_exists('CreateAuditsTable')) {
|
||||
$this->publishes([
|
||||
__DIR__ . '/../database/migrations/audits.stub' => database_path(
|
||||
sprintf('migrations/%s_create_audits_table.php', date('Y_m_d_His'))
|
||||
),
|
||||
], 'migrations');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing;
|
||||
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Manager;
|
||||
use InvalidArgumentException;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
use OwenIt\Auditing\Contracts\AuditDriver;
|
||||
use OwenIt\Auditing\Drivers\Database;
|
||||
use OwenIt\Auditing\Events\Audited;
|
||||
use OwenIt\Auditing\Events\Auditing;
|
||||
use OwenIt\Auditing\Exceptions\AuditingException;
|
||||
|
||||
class Auditor extends Manager implements Contracts\Auditor
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultDriver()
|
||||
{
|
||||
return 'database';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createDriver($driver)
|
||||
{
|
||||
try {
|
||||
return parent::createDriver($driver);
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
if (class_exists($driver)) {
|
||||
return $this->container->make($driver);
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function auditDriver(Auditable $model): AuditDriver
|
||||
{
|
||||
$driver = $this->driver($model->getAuditDriver());
|
||||
|
||||
if (!$driver instanceof AuditDriver) {
|
||||
throw new AuditingException('The driver must implement the AuditDriver contract');
|
||||
}
|
||||
|
||||
return $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute(Auditable $model): void
|
||||
{
|
||||
if (!$model->readyForAuditing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$driver = $this->auditDriver($model);
|
||||
|
||||
if (!$this->fireAuditingEvent($model, $driver)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we want to avoid storing empty values
|
||||
$allowEmpty = Config::get('audit.empty_values');
|
||||
$explicitAllowEmpty = in_array($model->getAuditEvent(), Config::get('audit.allowed_empty_values', []));
|
||||
|
||||
if (!$allowEmpty && !$explicitAllowEmpty) {
|
||||
if (
|
||||
empty($model->toAudit()['new_values']) &&
|
||||
empty($model->toAudit()['old_values'])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$audit = $driver->audit($model);
|
||||
if (!$audit) {
|
||||
return;
|
||||
}
|
||||
|
||||
$driver->prune($model);
|
||||
|
||||
$this->container->make('events')->dispatch(
|
||||
new Audited($model, $driver, $audit)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the Database audit driver.
|
||||
*
|
||||
* @return \OwenIt\Auditing\Drivers\Database
|
||||
*/
|
||||
protected function createDatabaseDriver(): Database
|
||||
{
|
||||
return $this->container->make(Database::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the Auditing event.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
* @param \OwenIt\Auditing\Contracts\AuditDriver $driver
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function fireAuditingEvent(Auditable $model, AuditDriver $driver): bool
|
||||
{
|
||||
return $this
|
||||
->container
|
||||
->make('events')
|
||||
->until(new Auditing($model, $driver)) !== false;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
|
||||
class AuditDriverCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $name = 'auditing:audit-driver';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $description = 'Create a new audit driver';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'AuditDriver';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return __DIR__ . '/../../stubs/driver.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace . '\AuditDrivers';
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Console;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
|
||||
class AuditResolverCommand extends GeneratorCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $name = 'auditing:audit-resolver';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $description = 'Create a new audit resolver';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'AuditResolver';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getStub()
|
||||
{
|
||||
return __DIR__ . '/../../stubs/resolver.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return $rootNamespace . '\AuditResolvers';
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Add your new resolver to the resolvers array in audit.php config file.');
|
||||
return parent::handle();
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class InstallCommand extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $signature = 'auditing:install';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $description = 'Install all of the Auditing resources';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->comment('Publishing Auditing Configuration...');
|
||||
$this->callSilent('vendor:publish', ['--tag' => 'config']);
|
||||
|
||||
$this->comment('Publishing Auditing Migrations...');
|
||||
$this->callSilent('vendor:publish', ['--tag' => 'migrations']);
|
||||
|
||||
$this->registerAuditingServiceProvider();
|
||||
|
||||
$this->info('Auditing installed successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Auditing service provider in the application configuration file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerAuditingServiceProvider()
|
||||
{
|
||||
$namespace = Str::replaceLast('\\', '', app()->getNamespace());
|
||||
|
||||
$appConfig = file_get_contents(config_path('app.php'));
|
||||
|
||||
if (Str::contains($appConfig, 'OwenIt\\Auditing\\AuditingServiceProvider::class')) {
|
||||
return;
|
||||
}
|
||||
|
||||
file_put_contents(config_path('app.php'), str_replace(
|
||||
"{$namespace}\\Providers\EventServiceProvider::class," . PHP_EOL,
|
||||
"{$namespace}\\Providers\EventServiceProvider::class," . PHP_EOL . " OwenIt\Auditing\AuditingServiceProvider::class," . PHP_EOL,
|
||||
$appConfig
|
||||
));
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
interface AttributeEncoder extends AttributeModifier
|
||||
{
|
||||
/**
|
||||
* Encode an attribute value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function encode($value);
|
||||
|
||||
/**
|
||||
* Decode an attribute value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decode($value);
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
interface AttributeModifier
|
||||
{
|
||||
//
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
interface AttributeRedactor extends AttributeModifier
|
||||
{
|
||||
/**
|
||||
* Redact an attribute value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function redact($value): string;
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
/**
|
||||
* @mixin \OwenIt\Auditing\Models\Audit
|
||||
*/
|
||||
interface Audit
|
||||
{
|
||||
/**
|
||||
* Get the current connection name for the model.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getConnectionName();
|
||||
|
||||
/**
|
||||
* Get the table associated with the model.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTable();
|
||||
|
||||
/**
|
||||
* Get the auditable model to which this Audit belongs.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function auditable();
|
||||
|
||||
/**
|
||||
* User responsible for the changes.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function user();
|
||||
|
||||
/**
|
||||
* Audit data resolver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function resolveData(): array;
|
||||
|
||||
/**
|
||||
* Get an Audit data value.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDataValue(string $key);
|
||||
|
||||
/**
|
||||
* Get the Audit metadata.
|
||||
*
|
||||
* @param bool $json
|
||||
* @param int $options
|
||||
* @param int $depth
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function getMetadata(bool $json = false, int $options = 0, int $depth = 512);
|
||||
|
||||
/**
|
||||
* Get the Auditable modified attributes.
|
||||
*
|
||||
* @param bool $json
|
||||
* @param int $options
|
||||
* @param int $depth
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function getModified(bool $json = false, int $options = 0, int $depth = 512);
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
interface AuditDriver
|
||||
{
|
||||
/**
|
||||
* Perform an audit.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return \OwenIt\Auditing\Contracts\Audit
|
||||
*/
|
||||
public function audit(Auditable $model): ?Audit;
|
||||
|
||||
/**
|
||||
* Remove older audits that go over the threshold.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function prune(Auditable $model): bool;
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
|
||||
interface Auditable
|
||||
{
|
||||
/**
|
||||
* Auditable Model audits.
|
||||
*
|
||||
* @return MorphMany<\OwenIt\Auditing\Models\Audit>
|
||||
*/
|
||||
public function audits(): MorphMany;
|
||||
|
||||
/**
|
||||
* Set the Audit event.
|
||||
*
|
||||
* @param string $event
|
||||
*
|
||||
* @return Auditable
|
||||
*/
|
||||
public function setAuditEvent(string $event): Auditable;
|
||||
|
||||
/**
|
||||
* Get the Audit event that is set.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAuditEvent();
|
||||
|
||||
/**
|
||||
* Get the events that trigger an Audit.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAuditEvents(): array;
|
||||
|
||||
/**
|
||||
* Is the model ready for auditing?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function readyForAuditing(): bool;
|
||||
|
||||
/**
|
||||
* Return data for an Audit.
|
||||
*
|
||||
* @throws \OwenIt\Auditing\Exceptions\AuditingException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toAudit(): array;
|
||||
|
||||
/**
|
||||
* Get the (Auditable) attributes included in audit.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAuditInclude(): array;
|
||||
|
||||
/**
|
||||
* Get the (Auditable) attributes excluded from audit.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAuditExclude(): array;
|
||||
|
||||
/**
|
||||
* Get the strict audit status.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getAuditStrict(): bool;
|
||||
|
||||
/**
|
||||
* Get the audit (Auditable) timestamps status.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getAuditTimestamps(): bool;
|
||||
|
||||
/**
|
||||
* Get the Audit Driver.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAuditDriver();
|
||||
|
||||
/**
|
||||
* Get the Audit threshold.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAuditThreshold(): int;
|
||||
|
||||
/**
|
||||
* Get the Attribute modifiers.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributeModifiers(): array;
|
||||
|
||||
/**
|
||||
* Transform the data before performing an audit.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function transformAudit(array $data): array;
|
||||
|
||||
/**
|
||||
* Generate an array with the model tags.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function generateTags(): array;
|
||||
|
||||
/**
|
||||
* Transition to another model state from an Audit.
|
||||
*
|
||||
* @param Audit $audit
|
||||
* @param bool $old
|
||||
*
|
||||
* @throws \OwenIt\Auditing\Exceptions\AuditableTransitionException
|
||||
*
|
||||
* @return Auditable
|
||||
*/
|
||||
public function transitionTo(Audit $audit, bool $old = false): Auditable;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
interface Auditor
|
||||
{
|
||||
/**
|
||||
* Get an audit driver instance.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return AuditDriver
|
||||
*/
|
||||
public function auditDriver(Auditable $model): AuditDriver;
|
||||
|
||||
/**
|
||||
* Perform an audit.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute(Auditable $model);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see Resolver
|
||||
*/
|
||||
interface IpAddressResolver
|
||||
{
|
||||
/**
|
||||
* Resolve the IP Address.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function resolve(): string;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
interface Resolver
|
||||
{
|
||||
public static function resolve(Auditable $auditable);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see Resolver
|
||||
*/
|
||||
interface UrlResolver
|
||||
{
|
||||
/**
|
||||
* Resolve the URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function resolve(): string;
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see Resolver
|
||||
*/
|
||||
interface UserAgentResolver
|
||||
{
|
||||
/**
|
||||
* Resolve the User Agent.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function resolve();
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Contracts;
|
||||
|
||||
interface UserResolver
|
||||
{
|
||||
/**
|
||||
* Resolve the User.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Auth\Authenticatable|null
|
||||
*/
|
||||
public static function resolve();
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Drivers;
|
||||
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use OwenIt\Auditing\Contracts\Audit;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
use OwenIt\Auditing\Contracts\AuditDriver;
|
||||
|
||||
class Database implements AuditDriver
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function audit(Auditable $model): ?Audit
|
||||
{
|
||||
$implementation = Config::get('audit.implementation', \OwenIt\Auditing\Models\Audit::class);
|
||||
|
||||
return call_user_func([$implementation, 'create'], $model->toAudit());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune(Auditable $model): bool
|
||||
{
|
||||
if (($threshold = $model->getAuditThreshold()) > 0) {
|
||||
$forRemoval = $model->audits()
|
||||
->latest()
|
||||
->get()
|
||||
->slice($threshold)
|
||||
->pluck('id');
|
||||
|
||||
if (!$forRemoval->isEmpty()) {
|
||||
return $model->audits()
|
||||
->whereIn('id', $forRemoval)
|
||||
->delete() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Encoders;
|
||||
|
||||
class Base64Encoder implements \OwenIt\Auditing\Contracts\AttributeEncoder
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function encode($value)
|
||||
{
|
||||
return base64_encode($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function decode($value)
|
||||
{
|
||||
return base64_decode($value);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Events;
|
||||
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
|
||||
class AuditCustom
|
||||
{
|
||||
/**
|
||||
* The Auditable model.
|
||||
*
|
||||
* @var \OwenIt\Auditing\Contracts\Auditable
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Create a new Auditing event instance.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
*/
|
||||
public function __construct(Auditable $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Events;
|
||||
|
||||
use OwenIt\Auditing\Contracts\Audit;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
use OwenIt\Auditing\Contracts\AuditDriver;
|
||||
|
||||
class Audited
|
||||
{
|
||||
/**
|
||||
* The Auditable model.
|
||||
*
|
||||
* @var \OwenIt\Auditing\Contracts\Auditable
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Audit driver.
|
||||
*
|
||||
* @var \OwenIt\Auditing\Contracts\AuditDriver
|
||||
*/
|
||||
public $driver;
|
||||
|
||||
/**
|
||||
* The Audit model.
|
||||
*
|
||||
* @var \OwenIt\Auditing\Contracts\Audit|null
|
||||
*/
|
||||
public $audit;
|
||||
|
||||
/**
|
||||
* Create a new Audited event instance.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
* @param \OwenIt\Auditing\Contracts\AuditDriver $driver
|
||||
* @param \OwenIt\Auditing\Contracts\Audit $audit
|
||||
*/
|
||||
public function __construct(Auditable $model, AuditDriver $driver, Audit $audit = null)
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->driver = $driver;
|
||||
$this->audit = $audit;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Events;
|
||||
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
use OwenIt\Auditing\Contracts\AuditDriver;
|
||||
|
||||
class Auditing
|
||||
{
|
||||
/**
|
||||
* The Auditable model.
|
||||
*
|
||||
* @var \OwenIt\Auditing\Contracts\Auditable
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Audit driver.
|
||||
*
|
||||
* @var \OwenIt\Auditing\Contracts\AuditDriver
|
||||
*/
|
||||
public $driver;
|
||||
|
||||
/**
|
||||
* Create a new Auditing event instance.
|
||||
*
|
||||
* @param \OwenIt\Auditing\Contracts\Auditable $model
|
||||
* @param \OwenIt\Auditing\Contracts\AuditDriver $driver
|
||||
*/
|
||||
public function __construct(Auditable $model, AuditDriver $driver)
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->driver = $driver;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Events;
|
||||
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
|
||||
class DispatchAudit
|
||||
{
|
||||
/**
|
||||
* The Auditable model.
|
||||
*
|
||||
* @var Auditable
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Create a new DispatchAudit event instance.
|
||||
*
|
||||
* @param Auditable $model
|
||||
*/
|
||||
public function __construct(Auditable $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Events;
|
||||
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
|
||||
class DispatchingAudit
|
||||
{
|
||||
/**
|
||||
* The Auditable model.
|
||||
*
|
||||
* @var Auditable
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Create a new DispatchingAudit event instance.
|
||||
*
|
||||
* @param Auditable $model
|
||||
*/
|
||||
public function __construct(Auditable $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class AuditableTransitionException extends AuditingException
|
||||
{
|
||||
/**
|
||||
* Attribute incompatibilities.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $incompatibilities = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($message = '', array $incompatibilities = [], $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
|
||||
$this->incompatibilities = $incompatibilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attribute incompatibilities.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIncompatibilities(): array
|
||||
{
|
||||
return $this->incompatibilities;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class AuditingException extends Exception
|
||||
{
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
/**
|
||||
* @method static \OwenIt\Auditing\Contracts\AuditDriver auditDriver(\OwenIt\Auditing\Contracts\Auditable $model);
|
||||
* @method static void execute(\OwenIt\Auditing\Contracts\Auditable $model);
|
||||
*/
|
||||
class Auditor extends Facade
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return \OwenIt\Auditing\Contracts\Auditor::class;
|
||||
}
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Listeners;
|
||||
|
||||
use OwenIt\Auditing\Facades\Auditor;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use OwenIt\Auditing\Events\DispatchAudit;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class ProcessDispatchAudit implements ShouldQueue
|
||||
{
|
||||
public function viaConnection(): string
|
||||
{
|
||||
return Config::get('audit.queue.connection', 'sync');
|
||||
}
|
||||
|
||||
public function viaQueue(): string
|
||||
{
|
||||
return Config::get('audit.queue.queue', 'default');
|
||||
}
|
||||
|
||||
public function withDelay(DispatchAudit $event): int
|
||||
{
|
||||
return Config::get('audit.queue.delay', 0);
|
||||
}
|
||||
|
||||
public function handle(DispatchAudit $event)
|
||||
{
|
||||
Auditor::execute($event->model);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Listeners;
|
||||
|
||||
use OwenIt\Auditing\Facades\Auditor;
|
||||
|
||||
class RecordCustomAudit
|
||||
{
|
||||
public function handle(\OwenIt\Auditing\Contracts\Auditable $model)
|
||||
{
|
||||
Auditor::execute($model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property string $tags
|
||||
* @property string $event
|
||||
* @property array $new_values
|
||||
* @property array $old_values
|
||||
* @property mixed $user
|
||||
* @property mixed $auditable.
|
||||
*/
|
||||
class Audit extends Model implements \OwenIt\Auditing\Contracts\Audit
|
||||
{
|
||||
use \OwenIt\Auditing\Audit;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $casts = [
|
||||
'old_values' => 'json',
|
||||
'new_values' => 'json',
|
||||
// Note: Please do not add 'auditable_id' in here, as it will break non-integer PK models
|
||||
];
|
||||
|
||||
public function getSerializedDate($date)
|
||||
{
|
||||
return $this->serializeDate($date);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Redactors;
|
||||
|
||||
class LeftRedactor implements \OwenIt\Auditing\Contracts\AttributeRedactor
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function redact($value): string
|
||||
{
|
||||
$total = strlen($value);
|
||||
$tenth = ceil($total / 10);
|
||||
|
||||
// Make sure single character strings get redacted
|
||||
$length = ($total > $tenth) ? ($total - $tenth) : 1;
|
||||
|
||||
return str_pad(substr($value, $length), $total, '#', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Redactors;
|
||||
|
||||
class RightRedactor implements \OwenIt\Auditing\Contracts\AttributeRedactor
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function redact($value): string
|
||||
{
|
||||
$total = strlen($value);
|
||||
$tenth = ceil($total / 10);
|
||||
|
||||
// Make sure single character strings get redacted
|
||||
$length = ($total > $tenth) ? ($total - $tenth) : 1;
|
||||
|
||||
return str_pad(substr($value, 0, -$length), $total, '#', STR_PAD_RIGHT);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Resolvers;
|
||||
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
use OwenIt\Auditing\Contracts\Resolver;
|
||||
|
||||
class DumpResolver implements Resolver
|
||||
{
|
||||
public static function resolve(Auditable $auditable): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Resolvers;
|
||||
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
use OwenIt\Auditing\Contracts\Resolver;
|
||||
|
||||
class IpAddressResolver implements Resolver
|
||||
{
|
||||
public static function resolve(Auditable $auditable): string
|
||||
{
|
||||
return $auditable->preloadedResolverData['ip_address'] ?? Request::ip();
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Resolvers;
|
||||
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
|
||||
class UrlResolver implements \OwenIt\Auditing\Contracts\Resolver
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function resolve(Auditable $auditable): string
|
||||
{
|
||||
if (! empty($auditable->preloadedResolverData['url'] ?? null)) {
|
||||
return $auditable->preloadedResolverData['url'];
|
||||
}
|
||||
|
||||
if (App::runningInConsole()) {
|
||||
return self::resolveCommandLine();
|
||||
}
|
||||
|
||||
return Request::fullUrl();
|
||||
}
|
||||
|
||||
public static function resolveCommandLine(): string
|
||||
{
|
||||
$command = Request::server('argv', null);
|
||||
if (is_array($command)) {
|
||||
return implode(' ', $command);
|
||||
}
|
||||
|
||||
return 'console';
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Resolvers;
|
||||
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
use OwenIt\Auditing\Contracts\Resolver;
|
||||
|
||||
class UserAgentResolver implements Resolver
|
||||
{
|
||||
public static function resolve(Auditable $auditable)
|
||||
{
|
||||
return $auditable->preloadedResolverData['user_agent'] ?? Request::header('User-Agent');
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace OwenIt\Auditing\Resolvers;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use OwenIt\Auditing\Contracts\Auditable;
|
||||
|
||||
class UserResolver implements \OwenIt\Auditing\Contracts\UserResolver
|
||||
{
|
||||
/**
|
||||
* @return \Illuminate\Contracts\Auth\Authenticatable|null
|
||||
*/
|
||||
public static function resolve()
|
||||
{
|
||||
$guards = Config::get('audit.user.guards', [
|
||||
\config('auth.defaults.guard')
|
||||
]);
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
try {
|
||||
$authenticated = Auth::guard($guard)->check();
|
||||
} catch (\Exception $exception) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (true === $authenticated) {
|
||||
return Auth::guard($guard)->user();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user