Skip to content

Commit

Permalink
Merge a143b71 into addf91f
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasGuilloux committed Jul 28, 2021
2 parents addf91f + a143b71 commit ead65be
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 8 deletions.
27 changes: 27 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ Quick showcase
<<1. Installation>> +
<<2. Documentation>> +
- link:docs/Configuration.adoc[Configuration] +
- link:docs/Creation.adoc[Creation] +
- link:docs/Signatory.adoc[Signatory] +
- link:docs/Security.adoc[Security] +
- link:docs/BackOffice.adoc[BackOffice] +
- link:docs/Events.adoc[Events] +
<<3. Versioning>> +
<<4. Contributing>> +
<<5. License>> +
Expand All @@ -49,11 +54,33 @@ This version of the bundle requires Symfony 4.4+ and PHP 7.3+.
composer require rich-id/terms-module-bundle
----

You must add the routes to your application by adding the following content to the file `config/routes/rich_id_terms_module.yaml`:

[source, yaml]
----
rich_id_terms_module:
resource: "@RichIdTermsModuleBundle/Resources/config/routing/routing.xml"
# # Edit the path of the routes
# module_terms_sign:
# path: /cgu/{termsSlug}/signature
# methods: [GET|POST]
# defaults: { _controller: RichId\TermsModuleBundle\UserInterface\Controller\SignRoute }
#
# module_terms_terms:
# path: /cgu/{termsSlug}
# methods: GET
# defaults: { _controller: RichId\TermsModuleBundle\UserInterface\Controller\TermsRoute }
----

== 2. Documentation

* link:docs/Configuration.adoc[Configuration]
* link:docs/Creation.adoc[Creation]
* link:docs/Signatory.adoc[Signatory]
* link:docs/Security.adoc[Security]
* link:docs/BackOffice.adoc[BackOffice]
* link:docs/Events.adoc[Events]


== 3. Versioning
Expand Down
30 changes: 25 additions & 5 deletions docs/Creation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The only way to create a new term is to make a migration. Since a term should always have at least one version, the migration will also create a version.

Hopefully, there is trait that might help you to do so. Here is an example of the usage of this trait:
Hopefully, this is quite straight forward since it just requires to insert 2 entries. The following example shoes how to insert terms with the slug `my_slug` and the title `My Terms`.

[source, php]
----
Expand All @@ -12,16 +12,36 @@ use RichId\TermsModuleBundle\Infrastructure\Migrations\TermsMigrationTrait;
final class RandomMigration extends AbstractMigration
{
use TermsMigrationTrait;
public function up(Schema $schema): void
{
$this->createTerms('my_slug', 'My Terms');
$this->addSQL("
INSERT INTO module_terms_terms
(slug, name, is_published, is_depublication_locked)
VALUES ('my_slug', 'My Terms', 0, 0)
");
$this->addSQL("
INSERT INTO module_terms_terms_version
(version, is_enabled, title, content, publication_date, terms_id)
VALUES (
1,
0,
'My title',
'My content',
CURRENT_TIME,
(SELECT id FROM module_terms_terms WHERE slug = 'my_slug')
)
");
}
public function down(Schema $schema): void
{
$this->deleteTerms('my_slug');
$this->addSQL("
DELETE FROM module_terms_terms_version AS version WHERE
version.terms_id = (SELECT terms.id FROM module_terms_terms WHERE terms.slug = 'my_slug')
");
$this->addSQL("DELETE FROM module_terms_terms WHERE slug = 'my_slug'");
}
}
----
16 changes: 16 additions & 0 deletions docs/Events.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
== Events

Various events are available to let you react to every action. Every event is prefixed with the namespace `RichId\TermsModuleBundle\Domain\Event\`.

[cols="1,1"]
|===
| Event | Description

| TermsPublishedEvent | Triggered on publication
| TermsSignedEvent | Triggered when a version is signed/not signed/skipped by a subject
| TermsUnpublishedEvent | Triggered on un-publication
| TermsVersionCreatedEvent | Triggered when a new version is created
| TermsVersionDeletedEvent | Triggered when a draft version is deleted
| TermsVersionEnabledEvent | Triggered when a version is published
| TermsVersionUpdatedEvent | Triggered when a version is updated
|===
108 changes: 108 additions & 0 deletions docs/Security.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
== Security

=== Guards

You may want to control who can sign the terms. This can be easily done using the `TermsGuardInterface` that will previously check if the subject can sign it.

The following example protect the terms with the slug `your_slug` to be signed by anybody but a User.

[source,php]
----
final class RandomGuard implements TermsGuardInterface
{
public function supports(string $slug, TermsSubjectInterface $subject): bool
{
return $slug === 'your_slug';
}
public function check(string $slug, TermsSubjectInterface $subject): bool
{
return $subject instanceof User;
}
}
----


=== Protecting a route

Since it can be hard to create a generic voter to check if a subject has sign the terms, the best way is to create a custom voter for each case.

The following example exposes a voter that checks if the User has signed a route.

[source, php]
----
final class UserTermsVoter extends Voter
{
/** @var HasSignedLastTermsVersion **/
protected $hasSignedLastTermsVersion;
/** @var Security
protected $security;
// ...
protected function supports($attribute, $subject): bool
{
$user = $this->security->getUser();
return $user instanceof User
&& $user instanceof TermsSubjectInterface
&& $attribute === 'HAS_USER_SIGNED_TERMS';
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
return ($this->hasSignedLastTermsVersion)('your_slug', $subject);
}
}
----

Using it in a controller is now straight forward !

[source, php]
----
final class RandomRoute extends AbstractController
{
/**
* @IsGranted("HAS_USER_SIGNED_TERMS")
*/
public function __invoke(): Response
{
return new Response('Yay!');
}
}
----

=== Redirection after a voter

Since the voter blocks the access of the route, this may be a brutal behaviour. A redirection to the signing page of the appropriate terms would be smoother. For this, a listener that catches when an `AccessDeniedException` is thrown is the best approach.

The following example redirects the User if the voter denies the access.

[source, php]
----
final class RedirectUserWhenTermsNotSignedListener
{
/** @var Security */
private $security;
/** @var GenerateSigningRoute */
private $generateSigningRoute;
// ...
public function __invoke(ExceptionEvent $event): void
{
$user = $this->security->getUserCompany();
$exception = $event->getThrowable();
if (!$exception instanceof AccessDeniedException || !user instanceof User) {
return;
}
$route = ($this->generateSigningRoute)('your_slug', $user);
$response = new RedirectResposne($route);
$event->setResponse($response);
}
}
----
8 changes: 5 additions & 3 deletions docs/Signatory.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ From the code, you can check if the use has signed using the use case `HasSignTe
----
final class ProtectedRoute extends AbstractController
{
/** @var GenerateSigningRedirectResponse */
protected $generateSigningRedirectResponse;
/** @var GenerateSigningRoute */
protected $generateSigningRoute;
/** @var HasSignedLastTermsVersion */
protected $hasSignedLastTermsVersion;
Expand All @@ -88,7 +88,9 @@ final class ProtectedRoute extends AbstractController
$isTermsSigned = ($this->hasSignedLastTermsVersion)($terms->getSlug(), $subject);
if (!$isTermsSigned) {
return ($this->redirectSigningRedirectResponse)($terms, $subject);
$route = ($this->redirectSigningRedirectResponse)($terms, $subject);
return new RedirectResponse($route);
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/Infrastructure/Migrations/TermsMigrationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@

namespace RichId\TermsModuleBundle\Infrastructure\Migrations;

/**
* @deprecated The migrations should be stateless, please copy the raw SQL instead
*/
trait TermsMigrationTrait
{
/** @deprecated The migrations should be stateless, please copy the raw SQL instead */
protected function createTerms(
string $slug,
string $name,
Expand All @@ -31,6 +35,7 @@ protected function createTerms(
");
}

/** @deprecated The migrations should be stateless, please copy the raw SQL instead */
protected function deleteTerms(string $slug): void
{
$this->addSQL("
Expand Down

0 comments on commit ead65be

Please sign in to comment.