Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/saterday-night-fixes'
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenvdlinde committed Oct 23, 2024
2 parents 26534e4 + 1fb5425 commit 0b94685
Show file tree
Hide file tree
Showing 15 changed files with 550 additions and 135 deletions.
35 changes: 29 additions & 6 deletions lib/Controller/ObjectsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@
use OCA\OpenRegister\Service\SearchService;
use OCA\OpenRegister\Db\ObjectEntity;
use OCA\OpenRegister\Db\ObjectEntityMapper;
use OCA\OpenRegister\Db\AuditTrail;
use OCA\OpenRegister\Db\AuditTrailMapper;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IAppConfig;
use OCP\IRequest;
use Symfony\Component\Uid\Uuid;

class ObjectsController extends Controller
{


/**
* Constructor for the ObjectsController
*
Expand Down Expand Up @@ -79,7 +83,7 @@ public function index(ObjectService $objectService, SearchService $searchService
foreach ($results as $key => $result) {
$results[$key] = $result->getObjectArray();
}

return new JSONResponse(['results' => $results]);
}

Expand All @@ -97,7 +101,7 @@ public function index(ObjectService $objectService, SearchService $searchService
public function show(string $id): JSONResponse
{
try {
return new JSONResponse($this->objectEntityMapper->find(id: (int) $id));
return new JSONResponse($this->objectEntityMapper->find(idOrUuid: (int) $id)->getObjectArray());
} catch (DoesNotExistException $exception) {
return new JSONResponse(data: ['error' => 'Not Found'], statusCode: 404);
}
Expand All @@ -116,6 +120,7 @@ public function show(string $id): JSONResponse
public function create(): JSONResponse
{
$data = $this->request->getParams();
$object = $data['object'];

foreach ($data as $key => $value) {
if (str_starts_with($key, '_')) {
Expand All @@ -125,13 +130,19 @@ public function create(): JSONResponse

if (isset($data['id'])) {
unset($data['id']);

}

// save it
$objectEntity = $this->objectEntityMapper->createFromArray(object: $data);

$this->auditTrailMapper->createAuditTrail(new: $objectEntity);

return new JSONResponse($this->objectEntityMapper->createFromArray(object: $data));
return new JSONResponse($objectEntity->getObjectArray());
}

/**
* Updates an existing object
* Updates an existing object
*
* This method updates an existing object based on its ID.
*
Expand All @@ -144,6 +155,7 @@ public function create(): JSONResponse
public function update(int $id): JSONResponse
{
$data = $this->request->getParams();
$object = $data['object'];

foreach ($data as $key => $value) {
if (str_starts_with($key, '_')) {
Expand All @@ -153,7 +165,14 @@ public function update(int $id): JSONResponse
if (isset($data['id'])) {
unset($data['id']);
}
return new JSONResponse($this->objectEntityMapper->updateFromArray(id: (int) $id, object: $data));

// save it
$oldObject = $this->objectEntityMapper->find($id);
$objectEntity = $this->objectEntityMapper->updateFromArray(id: $id, object: $data);

$this->auditTrailMapper->createAuditTrail(new: $objectEntity, old: $oldObject);

return new JSONResponse($objectEntity->getOBjectArray());
}

/**
Expand All @@ -169,6 +188,10 @@ public function update(int $id): JSONResponse
*/
public function destroy(int $id): JSONResponse
{
// Create a log entry
$oldObject = $this->objectEntityMapper->find($id);
$this->auditTrailMapper->createAuditTrail(old: $oldObject);

$this->objectEntityMapper->delete($this->objectEntityMapper->find((int) $id));

return new JSONResponse([]);
Expand All @@ -178,7 +201,7 @@ public function destroy(int $id): JSONResponse
* Retrieves a list of logs for an object
*
* This method returns a JSON response containing the logs for a specific object.
*
*
* @NoAdminRequired
* @NoCSRFRequired
*
Expand Down
90 changes: 89 additions & 1 deletion lib/Db/AuditTrailMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use Symfony\Component\Uid\Uuid;
use OCA\OpenRegister\Db\ObjectEntityMapper;

class AuditTrailMapper extends QBMapper
{
public function __construct(IDBConnection $db)
private $objectEntityMapper;

public function __construct(IDBConnection $db, ObjectEntityMapper $objectEntityMapper)
{
parent::__construct($db, 'openregister_audit_trails');
$this->objectEntityMapper = $objectEntityMapper;
}

public function find(int $id): Log
Expand Down Expand Up @@ -58,6 +62,18 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters
return $this->findEntities(query: $qb);
}

public function findAllUuid(string $idOrUuid, ?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array
{
try {
$object = $this->objectEntityMapper->find(idOrUuid: $idOrUuid);
$objectId = $object->getId();
$filters['object'] = $objectId;
return $this->findAll($limit, $offset, $filters, $searchConditions, $searchParams);
} catch (\OCP\AppFramework\Db\DoesNotExistException $e) {
return [];
}
}

public function createFromArray(array $object): Log
{
$log = new Log();
Expand All @@ -70,6 +86,78 @@ public function createFromArray(array $object): Log

return $this->insert(entity: $log);
}

/**
* Creates an audit trail for object changes
*
* @param ObjectEntity|null $old The old state of the object
* @param ObjectEntity|null $new The new state of the object
* @return AuditTrail The created audit trail
*/
public function createAuditTrail(?ObjectEntity $old = null, ?ObjectEntity $new = null): AuditTrail
{
// Determine the action based on the presence of old and new objects
$action = 'update';
if ($new === null) {
$action = 'delete';
$objectEntity = $old;
} elseif ($old === null) {
$action = 'create';
$objectEntity = $new;
} else {
$objectEntity = $new;
}

// Initialize an array to store changed fields
$changed = [];
if ($action !== 'delete') {
$oldArray = $old ? $old->jsonSerialize() : [];
$newArray = $new->jsonSerialize();

// Compare old and new values to detect changes
foreach ($newArray as $key => $value) {
if (!isset($oldArray[$key]) || $oldArray[$key] !== $value) {
$changed[$key] = [
'old' => $oldArray[$key] ?? null,
'new' => $value
];
}
}

// For updates, check for removed fields
if ($action === 'update') {
foreach ($oldArray as $key => $value) {
if (!isset($newArray[$key])) {
$changed[$key] = [
'old' => $value,
'new' => null
];
}
}
}
}

// Get the current user
$user = \OC::$server->getUserSession()->getUser();

// Create and populate a new AuditTrail object
$auditTrail = new AuditTrail();
$auditTrail->setUuid(Uuid::v4());
$auditTrail->setObject($objectEntity->getId());
$auditTrail->setAction($action);
$auditTrail->setChanged($changed);
$auditTrail->setUser($user->getUID());
$auditTrail->setUserName($user->getDisplayName());
$auditTrail->setSession(session_id());
$auditTrail->setRequest(\OC::$server->getRequest()->getId());
$auditTrail->setIpAddress(\OC::$server->getRequest()->getRemoteAddress());
$auditTrail->setCreated(new \DateTime());
$auditTrail->setRegister($objectEntity->getRegister());
$auditTrail->setSchema($objectEntity->getSchema());

// Insert the new AuditTrail into the database and return it
return $this->insert(entity: $auditTrail);
}

// We dont need update as we dont change the log
}
57 changes: 57 additions & 0 deletions lib/Migration/Version1Date20241022135300.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\OpenRegister\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version1Date20241022135300 extends SimpleMigrationStep {

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

/// rename the register column to regsiter
$table = $schema->getTable('openregister_audit_trails');
if (!$table->hasColumn('register')) {
$table->addColumn('register', Types::INTEGER, ['notnull' => false]);
}
if ($table->hasColumn('regsiter')) {
$table->dropColumn('regsiter', 'register');
}

return $schema;
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}
}
67 changes: 23 additions & 44 deletions lib/Service/ObjectService.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,63 +204,23 @@ public function saveObject(int $register, int $schema, array $object): ObjectEnt
$object['id'] = $objectEntity->getUuid();
}

$oldObject = $objectEntity->getObject();
$oldObject = clone $objectEntity;
$objectEntity->setObject($object);
$changed = [];

foreach ($object as $key => $value) {
if (!isset($oldObject[$key]) || $oldObject[$key] !== $value) {
$changed[$key] = [
'old' => $oldObject[$key] ?? null,
'new' => $value
];
}
}

// Check for removed properties
foreach ($oldObject as $key => $value) {
if (!isset($object[$key])) {
$changed[$key] = [
'old' => $value,
'new' => null
];
}
}

// Normal loging
//$changed = $objectEntity->getUpdatedFields();



// If the object has no uuid, create a new one
if (empty($objectEntity->getUuid())) {
$objectEntity->setUuid(Uuid::v4());
}

if($objectEntity->getId()){
$objectEntity = $this->objectEntityMapper->update($objectEntity);
$action = 'update';
$this->auditTrailMapper->createAuditTrail(new: $objectEntity, old: $oldObject);
}
else {
$objectEntity = $this->objectEntityMapper->insert($objectEntity);
$action = 'create';
$this->auditTrailMapper->createAuditTrail(new: $objectEntity);
}

// Create a log entry
$user = \OC::$server->getUserSession()->getUser();

$log = new AuditTrail();
$log->setUuid(Uuid::v4());
$log->setObject($objectEntity->getId());
$log->setAction($action);
$log->setChanged($changed);
$log->setUser($user->getUID());
$log->setUserName($user->getDisplayName());
$log->setSession(session_id());
$log->setRequest(\OC::$server->getRequest()->getId());
$log->setIpAddress(\OC::$server->getRequest()->getRemoteAddress());
$log->setCreated(new \DateTime());
$this->auditTrailMapper->insert($log);

return $objectEntity;
}

Expand Down Expand Up @@ -491,4 +451,23 @@ public function setSchema(int $schema): void
{
$this->schema = $schema;
}

/**
* Get the audit trail for a specific object
*
* @param int $register The register ID
* @param int $schema The schema ID
* @param string $id The object ID
* @return array The audit trail for the object
*/
public function getAuditTrail(int $register, int $schema, string $id): array
{
$filters = [
//'register' => $register,
//'schema' => $schema,
'object' => $id
];

return $this->auditTrailMapper->findAllUuid(idOrUuid: $id);
}
}
Loading

0 comments on commit 0b94685

Please sign in to comment.