Skip to content

Commit

Permalink
feat: Write critical operations to the audit log
Browse files Browse the repository at this point in the history
  • Loading branch information
hweihwang committed Apr 1, 2024
1 parent b881e80 commit 21885c2
Show file tree
Hide file tree
Showing 14 changed files with 368 additions and 9 deletions.
16 changes: 16 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@
use Exception;
use OCA\Analytics\Datasource\DatasourceEvent;
use OCA\Tables\Capabilities;
use OCA\Tables\Event\RowDeletedEvent;
use OCA\Tables\Event\TableDeletedEvent;
use OCA\Tables\Event\TableOwnershipTransferredEvent;
use OCA\Tables\Event\ViewDeletedEvent;
use OCA\Tables\Listener\AnalyticsDatasourceListener;
use OCA\Tables\Listener\BeforeTemplateRenderedListener;
use OCA\Tables\Listener\LoadAdditionalListener;
use OCA\Tables\Listener\TablesReferenceListener;
use OCA\Tables\Listener\UserDeletedListener;
use OCA\Tables\Listener\WhenRowDeletedAuditLogListener;
use OCA\Tables\Listener\WhenTableDeletedAuditLogListener;
use OCA\Tables\Listener\WhenTableTransferredAuditLogListener;
use OCA\Tables\Listener\WhenViewDeletedAuditLogListener;
use OCA\Tables\Middleware\PermissionMiddleware;
use OCA\Tables\Reference\ContentReferenceProvider;
use OCA\Tables\Reference\ReferenceProvider;
use OCA\Tables\Search\SearchTablesProvider;
use OCA\Tables\Service\Support\AuditLogServiceInterface;
use OCA\Tables\Service\Support\DefaultAuditLogService;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
Expand Down Expand Up @@ -47,11 +57,17 @@ public function register(IRegistrationContext $context): void {
throw new Exception('Cannot include autoload. Did you run install dependencies using composer?');
}

$context->registerService(AuditLogServiceInterface::class, fn($c) => $c->query(DefaultAuditLogService::class));

Check failure on line 60 in lib/AppInfo/Application.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

UndefinedInterfaceMethod

lib/AppInfo/Application.php:60:82: UndefinedInterfaceMethod: Method Psr\Container\ContainerInterface::query does not exist (see https://psalm.dev/181)

Check failure on line 60 in lib/AppInfo/Application.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

UndefinedInterfaceMethod

lib/AppInfo/Application.php:60:82: UndefinedInterfaceMethod: Method Psr\Container\ContainerInterface::query does not exist (see https://psalm.dev/181)

$context->registerEventListener(BeforeUserDeletedEvent::class, UserDeletedListener::class);
$context->registerEventListener(DatasourceEvent::class, AnalyticsDatasourceListener::class);
$context->registerEventListener(RenderReferenceEvent::class, TablesReferenceListener::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalListener::class);
$context->registerEventListener(TableDeletedEvent::class, WhenTableDeletedAuditLogListener::class);
$context->registerEventListener(ViewDeletedEvent::class, WhenViewDeletedAuditLogListener::class);
$context->registerEventListener(RowDeletedEvent::class, WhenRowDeletedAuditLogListener::class);
$context->registerEventListener(TableOwnershipTransferredEvent::class, WhenTableTransferredAuditLogListener::class);

$context->registerSearchProvider(SearchTablesProvider::class);

Expand Down
26 changes: 26 additions & 0 deletions lib/Event/RowDeletedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Event;

use OCA\Tables\Db\Row2;
use OCP\EventDispatcher\Event;

final class RowDeletedEvent extends Event
{
public function __construct(protected Row2 $row, protected string $userId)
{
parent::__construct();
}

public function getRow(): Row2
{
return $this->row;
}

public function getUserId(): string
{
return $this->userId;
}
}
26 changes: 26 additions & 0 deletions lib/Event/TableDeletedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Event;

use OCA\Tables\Db\Table;
use OCP\EventDispatcher\Event;

final class TableDeletedEvent extends Event
{
public function __construct(protected Table $table, protected string $userId)
{
parent::__construct();
}

public function getTable(): Table
{
return $this->table;
}

public function getUserId(): string
{
return $this->userId;
}
}
31 changes: 31 additions & 0 deletions lib/Event/TableOwnershipTransferredEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Event;

use OCA\Tables\Db\Table;
use OCP\EventDispatcher\Event;

final class TableOwnershipTransferredEvent extends Event
{
public function __construct(protected Table $table, protected string $toUserId, protected ?string $fromUserId = null)
{
parent::__construct();
}

public function getTable(): Table
{
return $this->table;
}

public function getFromUserId(): string

Check failure on line 22 in lib/Event/TableOwnershipTransferredEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

InvalidNullableReturnType

lib/Event/TableOwnershipTransferredEvent.php:22:38: InvalidNullableReturnType: The declared return type 'string' for OCA\Tables\Event\TableOwnershipTransferredEvent::getFromUserId is not nullable, but 'null|string' contains null (see https://psalm.dev/144)

Check failure on line 22 in lib/Event/TableOwnershipTransferredEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

InvalidNullableReturnType

lib/Event/TableOwnershipTransferredEvent.php:22:38: InvalidNullableReturnType: The declared return type 'string' for OCA\Tables\Event\TableOwnershipTransferredEvent::getFromUserId is not nullable, but 'null|string' contains null (see https://psalm.dev/144)
{
return $this->fromUserId;

Check failure on line 24 in lib/Event/TableOwnershipTransferredEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

NullableReturnStatement

lib/Event/TableOwnershipTransferredEvent.php:24:16: NullableReturnStatement: The declared return type 'string' for OCA\Tables\Event\TableOwnershipTransferredEvent::getFromUserId is not nullable, but the function returns 'null|string' (see https://psalm.dev/139)

Check failure on line 24 in lib/Event/TableOwnershipTransferredEvent.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

NullableReturnStatement

lib/Event/TableOwnershipTransferredEvent.php:24:16: NullableReturnStatement: The declared return type 'string' for OCA\Tables\Event\TableOwnershipTransferredEvent::getFromUserId is not nullable, but the function returns 'null|string' (see https://psalm.dev/139)
}

public function getToUserId(): string
{
return $this->toUserId;
}
}
26 changes: 26 additions & 0 deletions lib/Event/ViewDeletedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Event;

use OCA\Tables\Db\View;
use OCP\EventDispatcher\Event;

final class ViewDeletedEvent extends Event
{
public function __construct(protected View $view, protected string $userId)
{
parent::__construct();
}

public function getView(): View
{
return $this->view;
}

public function getUserId(): string
{
return $this->userId;
}
}
33 changes: 33 additions & 0 deletions lib/Listener/WhenRowDeletedAuditLogListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Listener;

use OCA\Tables\Event\RowDeletedEvent;
use OCA\Tables\Service\Support\AuditLogServiceInterface;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

final class WhenRowDeletedAuditLogListener implements IEventListener

Check failure on line 12 in lib/Listener/WhenRowDeletedAuditLogListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

MissingTemplateParam

lib/Listener/WhenRowDeletedAuditLogListener.php:12:55: MissingTemplateParam: OCA\Tables\Listener\WhenRowDeletedAuditLogListener has missing template params when extending OCP\EventDispatcher\IEventListener, expecting 1 (see https://psalm.dev/182)

Check failure on line 12 in lib/Listener/WhenRowDeletedAuditLogListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

MissingTemplateParam

lib/Listener/WhenRowDeletedAuditLogListener.php:12:55: MissingTemplateParam: OCA\Tables\Listener\WhenRowDeletedAuditLogListener has missing template params when extending OCP\EventDispatcher\IEventListener, expecting 1 (see https://psalm.dev/182)
{
public function __construct(protected AuditLogServiceInterface $auditLogService)
{
}

public function handle(Event $event): void
{
if (!($event instanceof RowDeletedEvent)) {
return;
}

$row = $event->getRow();
$userId = $event->getUserId();
$rowId = $row->getId();

$this->auditLogService->log("Row with ID: $rowId was deleted by user with ID: $userId", [
'row' => $row->jsonSerialize(),
'userId' => $userId,
]);
}
}
32 changes: 32 additions & 0 deletions lib/Listener/WhenTableDeletedAuditLogListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Listener;

use OCA\Tables\Event\TableDeletedEvent;
use OCA\Tables\Service\Support\AuditLogServiceInterface;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

final class WhenTableDeletedAuditLogListener implements IEventListener

Check failure on line 12 in lib/Listener/WhenTableDeletedAuditLogListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

MissingTemplateParam

lib/Listener/WhenTableDeletedAuditLogListener.php:12:57: MissingTemplateParam: OCA\Tables\Listener\WhenTableDeletedAuditLogListener has missing template params when extending OCP\EventDispatcher\IEventListener, expecting 1 (see https://psalm.dev/182)

Check failure on line 12 in lib/Listener/WhenTableDeletedAuditLogListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

MissingTemplateParam

lib/Listener/WhenTableDeletedAuditLogListener.php:12:57: MissingTemplateParam: OCA\Tables\Listener\WhenTableDeletedAuditLogListener has missing template params when extending OCP\EventDispatcher\IEventListener, expecting 1 (see https://psalm.dev/182)
{
public function __construct(protected AuditLogServiceInterface $auditLogService)
{
}

public function handle(Event $event): void
{
if (!($event instanceof TableDeletedEvent)) {
return;
}

$table = $event->getTable();
$userId = $event->getUserId();

$this->auditLogService->log("Table with ID: $table->id was deleted by user with ID: $userId", [
'table' => $table->jsonSerialize(),
'userId' => $userId,
]);
}
}
34 changes: 34 additions & 0 deletions lib/Listener/WhenTableTransferredAuditLogListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Listener;

use OCA\Tables\Event\TableOwnershipTransferredEvent;
use OCA\Tables\Service\Support\AuditLogServiceInterface;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

final class WhenTableTransferredAuditLogListener implements IEventListener

Check failure on line 12 in lib/Listener/WhenTableTransferredAuditLogListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

MissingTemplateParam

lib/Listener/WhenTableTransferredAuditLogListener.php:12:61: MissingTemplateParam: OCA\Tables\Listener\WhenTableTransferredAuditLogListener has missing template params when extending OCP\EventDispatcher\IEventListener, expecting 1 (see https://psalm.dev/182)

Check failure on line 12 in lib/Listener/WhenTableTransferredAuditLogListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

MissingTemplateParam

lib/Listener/WhenTableTransferredAuditLogListener.php:12:61: MissingTemplateParam: OCA\Tables\Listener\WhenTableTransferredAuditLogListener has missing template params when extending OCP\EventDispatcher\IEventListener, expecting 1 (see https://psalm.dev/182)
{
public function __construct(protected AuditLogServiceInterface $auditLogService)
{
}

public function handle(Event $event): void
{
if (!($event instanceof TableOwnershipTransferredEvent)) {
return;
}

$table = $event->getTable();
$fromUserId = $event->getFromUserId();
$toUserId = $event->getToUserId();

$this->auditLogService->log("Table with ID: $table->id was transferred from user with ID: $fromUserId to user with ID: $toUserId", [
'table' => $table->jsonSerialize(),
'fromUserId' => $fromUserId,
'toUserId' => $toUserId,
]);
}
}
32 changes: 32 additions & 0 deletions lib/Listener/WhenViewDeletedAuditLogListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Listener;

use OCA\Tables\Event\ViewDeletedEvent;
use OCA\Tables\Service\Support\AuditLogServiceInterface;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

final class WhenViewDeletedAuditLogListener implements IEventListener

Check failure on line 12 in lib/Listener/WhenViewDeletedAuditLogListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

MissingTemplateParam

lib/Listener/WhenViewDeletedAuditLogListener.php:12:56: MissingTemplateParam: OCA\Tables\Listener\WhenViewDeletedAuditLogListener has missing template params when extending OCP\EventDispatcher\IEventListener, expecting 1 (see https://psalm.dev/182)

Check failure on line 12 in lib/Listener/WhenViewDeletedAuditLogListener.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

MissingTemplateParam

lib/Listener/WhenViewDeletedAuditLogListener.php:12:56: MissingTemplateParam: OCA\Tables\Listener\WhenViewDeletedAuditLogListener has missing template params when extending OCP\EventDispatcher\IEventListener, expecting 1 (see https://psalm.dev/182)
{
public function __construct(protected AuditLogServiceInterface $auditLogService)
{
}

public function handle(Event $event): void
{
if (!($event instanceof ViewDeletedEvent)) {
return;
}

$view = $event->getView();
$userId = $event->getUserId();

$this->auditLogService->log("View with ID: $view->id was deleted by user with ID: $userId", [
'view' => $view->jsonSerialize(),
'userId' => $userId,
]);
}
}
29 changes: 26 additions & 3 deletions lib/Service/RowService.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\Event\RowDeletedEvent;
use OCA\Tables\ResponseDefinitions;
use OCA\Tables\Service\ColumnTypes\IColumnTypeBusiness;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\Exception;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Server;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
Expand All @@ -33,13 +35,25 @@ class RowService extends SuperService {
private Row2Mapper $row2Mapper;
private array $tmpRows = []; // holds already loaded rows as a small cache

public function __construct(PermissionsService $permissionsService, LoggerInterface $logger, ?string $userId,
ColumnMapper $columnMapper, ViewMapper $viewMapper, TableMapper $tableMapper, Row2Mapper $row2Mapper) {
protected IEventDispatcher $eventDispatcher;

public function __construct(
PermissionsService $permissionsService,
LoggerInterface $logger,
?string $userId,
ColumnMapper $columnMapper,
ViewMapper $viewMapper,
TableMapper $tableMapper,
Row2Mapper $row2Mapper,
IEventDispatcher $eventDispatcher
)
{
parent::__construct($logger, $userId, $permissionsService);
$this->columnMapper = $columnMapper;
$this->viewMapper = $viewMapper;
$this->tableMapper = $tableMapper;
$this->row2Mapper = $row2Mapper;
$this->eventDispatcher = $eventDispatcher;
}

/**
Expand Down Expand Up @@ -450,7 +464,16 @@ public function delete(int $id, ?int $viewId, string $userId): Row2 {
}

try {
return $this->filterRowResult($view ?? null, $this->row2Mapper->delete($item));
$deletedRow = $this->row2Mapper->delete($item);

$event = new RowDeletedEvent(
row: $item,
userId: $userId
);

$this->eventDispatcher->dispatchTyped($event);

return $this->filterRowResult($view ?? null, $deletedRow);
} catch (Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new InternalError(get_class($this) . ' - ' . __FUNCTION__ . ': '.$e->getMessage());
Expand Down
10 changes: 10 additions & 0 deletions lib/Service/Support/AuditLogServiceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Service\Support;

interface AuditLogServiceInterface
{
public function log(string $message, array $context): void;
}
22 changes: 22 additions & 0 deletions lib/Service/Support/DefaultAuditLogService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace OCA\Tables\Service\Support;

use OCP\EventDispatcher\IEventDispatcher;
use OCP\Log\Audit\CriticalActionPerformedEvent;

final class DefaultAuditLogService implements AuditLogServiceInterface
{
public function __construct(private IEventDispatcher $eventDispatcher)
{
}

public function log(string $message, array $context): void
{
$auditEvent = new CriticalActionPerformedEvent($message, $context);

$this->eventDispatcher->dispatchTyped($auditEvent);
}
}
Loading

0 comments on commit 21885c2

Please sign in to comment.