-
Notifications
You must be signed in to change notification settings - Fork 487
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Internal: Add token validation system initial implementation - refs #5959 #5963
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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')] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead. |
||
class ValidationTokenController extends AbstractController | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You must use "/**" style comments for a class comment |
||
{ | ||
public function __construct( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing function doc comment |
||
private readonly ValidationTokenHelper $validationTokenHelper, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line indented incorrectly; expected 4 spaces, found 8 |
||
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); | ||
} | ||
} |
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; | ||
} | ||
|
||
/** | ||
* Generates a validation link. | ||
*/ | ||
public static function generateLink(int $type, int $resourceId): string | ||
{ | ||
$token = new self($type, $resourceId); | ||
return '/validate/' . $type . '/' . $token->getHash(); | ||
} | ||
} |
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'); | ||
} | ||
} | ||
} |
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(); | ||
} | ||
} | ||
} |
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 %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a single space around assignment operators