Skip to content

Commit

Permalink
Internal: Add token validation system initial implementation - refs #…
Browse files Browse the repository at this point in the history
  • Loading branch information
christianbeeznest committed Dec 12, 2024
1 parent db594c2 commit 0018aea
Show file tree
Hide file tree
Showing 8 changed files with 369 additions and 2 deletions.
119 changes: 119 additions & 0 deletions src/CoreBundle/Controller/ValidationTokenController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

declare(strict_types=1);

/* For licensing terms, see /license.txt */

namespace Chamilo\CoreBundle\Controller;

use Chamilo\CoreBundle\Entity\ValidationToken;
use Chamilo\CoreBundle\Repository\TrackEDefaultRepository;
use Chamilo\CoreBundle\Repository\ValidationTokenRepository;
use Chamilo\CoreBundle\ServiceHelper\ValidationTokenHelper;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;

#[Route('/validate')]
class ValidationTokenController extends AbstractController
{
public function __construct(
private readonly ValidationTokenHelper $validationTokenHelper,
private readonly ValidationTokenRepository $tokenRepository,
private readonly TrackEDefaultRepository $trackEDefaultRepository,
private readonly Security $security
) {}

#[Route('/{type}/{hash}', name: 'validate_token')]
public function validate(string $type, string $hash): Response
{
$token = $this->tokenRepository->findOneBy([
'type' => $this->validationTokenHelper->getTypeId($type),
'hash' => $hash
]);

if (!$token) {
throw $this->createNotFoundException('Invalid token.');
}

// Process the action related to the token type
$this->processAction($token);

// Remove the used token
$this->tokenRepository->remove($token, true);

// Register the token usage event
$this->registerTokenUsedEvent($token);

return $this->render('@ChamiloCore/Validation/success.html.twig', [
'type' => $type,
]);
}

#[Route('/test/generate-token/{type}/{resourceId}', name: 'test_generate_token')]
public function testGenerateToken(string $type, int $resourceId): Response
{
$typeId = $this->validationTokenHelper->getTypeId($type);
$token = new ValidationToken($typeId, $resourceId);
$this->tokenRepository->save($token, true);

$validationLink = $this->generateUrl('validate_token', [
'type' => $type,
'hash' => $token->getHash(),
], \Symfony\Component\Routing\Generator\UrlGeneratorInterface::ABSOLUTE_URL);

return new Response("Generated token: {$token->getHash()}<br>Validation link: <a href='{$validationLink}'>{$validationLink}</a>");
}

private function processAction(ValidationToken $token): void
{
switch ($token->getType()) {
case 1: // Assuming 1 is for 'ticket'
$this->processTicketValidation($token);
break;
case 2: // Assuming 2 is for 'user'
// Implement user validation logic here
break;
default:
throw new \InvalidArgumentException('Unrecognized token type');
}
}

private function processTicketValidation(ValidationToken $token): void
{
$ticketId = $token->getResourceId();

// Simulate ticket validation logic
// Here you would typically check if the ticket exists and is valid
// For now, we'll just print a message to simulate this
// Replace this with your actual ticket validation logic
$ticketValid = $this->validateTicket($ticketId);

if (!$ticketValid) {
throw new \RuntimeException('Invalid ticket.');
}

// If the ticket is valid, you can mark it as used or perform other actions
// For example, update the ticket status in the database
// $this->ticketRepository->markAsUsed($ticketId);
}

private function validateTicket(int $ticketId): bool
{
// Here you would implement the logic to check if the ticket is valid.
// This is a placeholder function to simulate validation.

// For testing purposes, let's assume all tickets are valid.
// In a real implementation, you would query your database or service.

return true; // Assume the ticket is valid for now
}

private function registerTokenUsedEvent(ValidationToken $token): void
{
$user = $this->security->getUser();
$userId = $user?->getId();
$this->trackEDefaultRepository->registerTokenUsedEvent($token, $userId);
}
}
4 changes: 2 additions & 2 deletions src/CoreBundle/Entity/TrackEDefault.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function getDefaultUserId()
return $this->defaultUserId;
}

public function setCId(int $cId): self
public function setCId(?int $cId): self
{
$this->cId = $cId;

Expand Down Expand Up @@ -153,7 +153,7 @@ public function getDefaultValue()
return $this->defaultValue;
}

public function setSessionId(int $sessionId): self
public function setSessionId(?int $sessionId): self
{
$this->sessionId = $sessionId;

Expand Down
96 changes: 96 additions & 0 deletions src/CoreBundle/Entity/ValidationToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

declare(strict_types=1);

/* For licensing terms, see /license.txt */

namespace Chamilo\CoreBundle\Entity;

use Chamilo\CoreBundle\Repository\ValidationTokenRepository;
use Doctrine\ORM\Mapping as ORM;

/**
* ValidationToken entity.
*/
#[ORM\Table(name: 'validation_token')]
#[ORM\Index(columns: ['type', 'hash'], name: 'idx_type_hash')]
#[ORM\Entity(repositoryClass: ValidationTokenRepository::class)]
class ValidationToken
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: 'integer')]
protected ?int $id = null;

#[ORM\Column(type: 'integer')]
protected int $type;

#[ORM\Column(type: 'bigint')]
protected int $resourceId;

#[ORM\Column(type: 'string', length: 64)]
protected string $hash;

#[ORM\Column(type: 'datetime')]
protected \DateTime $createdAt;

public function __construct(int $type, int $resourceId)
{
$this->type = $type;
$this->resourceId = $resourceId;
$this->hash = hash('sha256', uniqid((string) rand(), true));
$this->createdAt = new \DateTime();
}

public function getId(): ?int
{
return $this->id;
}

public function getType(): int
{
return $this->type;
}

public function setType(int $type): self
{
$this->type = $type;
return $this;
}

public function getResourceId(): int
{
return $this->resourceId;
}

public function setResourceId(int $resourceId): self
{
$this->resourceId = $resourceId;
return $this;
}

public function getHash(): string
{
return $this->hash;
}

public function getCreatedAt(): \DateTime
{
return $this->createdAt;
}

public function setCreatedAt(\DateTime $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}

/**
* Genera un enlace de validación.
*/
public static function generateLink(int $type, int $resourceId): string
{
$token = new self($type, $resourceId);
return '/validate/' . $type . '/' . $token->getHash();
}
}
42 changes: 42 additions & 0 deletions src/CoreBundle/Migrations/Schema/V200/Version20241211183300.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

/* For licensing terms, see /license.txt */

namespace Chamilo\CoreBundle\Migrations\Schema\V200;

use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;

final class Version20241211183300 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'Migration for creating the validation_token table';
}

public function up(Schema $schema): void
{
if (!$schema->hasTable('validation_token')) {
$this->addSql("
CREATE TABLE validation_token (
id INT AUTO_INCREMENT NOT NULL,
type INT NOT NULL,
resource_id BIGINT NOT NULL,
hash CHAR(64) NOT NULL,
created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime)',
INDEX idx_type_hash (type, hash),
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC
");
}
}

public function down(Schema $schema): void
{
if ($schema->hasTable('validation_token')) {
$this->addSql('DROP TABLE validation_token');
}
}
}
19 changes: 19 additions & 0 deletions src/CoreBundle/Repository/TrackEDefaultRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace Chamilo\CoreBundle\Repository;

use Chamilo\CoreBundle\Entity\TrackEDefault;
use Chamilo\CoreBundle\Entity\ValidationToken;
use DateTime;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
Expand Down Expand Up @@ -65,4 +66,22 @@ public function getUserCourseRegistrationAt(int $courseId, int $userId, ?int $se

return null;
}

/**
* Registers an event when a validation token is used.
*/
public function registerTokenUsedEvent(ValidationToken $token, ?int $userId = null): void
{
$event = new TrackEDefault();
$event->setDefaultUserId($userId ?? 0);
$event->setCId(null);
$event->setDefaultDate(new \DateTime());
$event->setDefaultEventType('VALIDATION_TOKEN_USED');
$event->setDefaultValueType('validation_token');
$event->setDefaultValue(\json_encode(['hash' => $token->getHash()]));
$event->setSessionId(null);

$this->_em->persist($event);
$this->_em->flush();
}
}
37 changes: 37 additions & 0 deletions src/CoreBundle/Repository/ValidationTokenRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

/* For licensing terms, see /license.txt */

namespace Chamilo\CoreBundle\Repository;

use Chamilo\CoreBundle\Entity\ValidationToken;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

class ValidationTokenRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ValidationToken::class);
}

public function save(ValidationToken $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);

if ($flush) {
$this->getEntityManager()->flush();
}
}

public function remove(ValidationToken $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);

if ($flush) {
$this->getEntityManager()->flush();
}
}
}
6 changes: 6 additions & 0 deletions src/CoreBundle/Resources/views/Validation/success.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% extends "@ChamiloCore/Layout/layout_one_col.html.twig" %}

{% block content %}
<h1>Validation Successful</h1>
<p>The token for {{ type }} has been successfully validated.</p>
{% endblock %}
Loading

0 comments on commit 0018aea

Please sign in to comment.