Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 74 additions & 35 deletions docs/book/v5/how-to/authorization.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,93 @@
# Authorization Guards

## Summary

This page explains how Dotkernel Frontend restricts access: roles and their permissions are defined in `authorization.global.php`, and `authorization-guards.global.php` requires those permissions for specific routes and controller actions.

## Details

The packages responsible for restricting access to certain parts of the application are [dot-rbac-guard](https://github.com/dotkernel/dot-rbac-guard) and [dot-rbac](https://github.com/dotkernel/dot-rbac).
These packages work together to create an infrastructure that is customizable and diversified to manage user access to the platform by specifying the type of role the user has.

The `authorization.global.php` file provides multiple configurations specifying multiple roles as well as the types of permissions to which these roles have access.

```php
//example of a flat RBAC model that specifies two types of roles as well as their permission
'roles' => [
'admin' => [
// config/autoload/authorization.global.php - flat RBAC model shipped with Frontend
'dot_authorization' => [
'guest_role' => 'guest',
'role_provider' => [
'type' => 'InMemory',
'options' => [
'roles' => [
'user' => [
'permissions' => [
'authenticated',
'edit',
'delete',
//etc..
]
'premium',
],
],
'user' => [
'guest' => [
'permissions' => [
'authenticated',
//etc..
]
]
]
'unauthenticated',
],
],
],
],
],
],
```

The `authorization-guards.global.php` file provides configuration to restrict access to certain actions based on the permissions defined in `authorization.global.php` so basically we have to add the permissions in the dot-rbac configuration file first to specify the action restriction permissions.

```php
// configuration example to restrict certain actions of some routes based on the permissions specified in the dot-rbac configuration file
// config/autoload/authorization-guards.global.php - ControllerPermission guard shipped with Frontend
'type' => 'ControllerPermission',
'options' => [
'rules' => [
[
'route' => 'account',
'actions' => [//list of actions to apply , or empty array for all actions
'unregister',
'avatar',
'details',
'changePassword'
],
'permissions' => ['authenticated']
],
[
'route' => 'admin',
'actions' => [
'deleteAccount'
],
'permissions' => [
'delete'
//list of roles to allow
]
]
]
[
'route' => 'account',
'actions' => [ // list of actions to apply, or empty array for all actions
'avatar',
'details',
'changePassword',
'deleteAccount',
],
'permissions' => ['authenticated'],
],
[
'route' => 'page',
'actions' => [
'premium-content',
],
'permissions' => ['premium'], // list of permissions required
],
],
],
```

> The default `protection_policy` is `GuardInterface::POLICY_ALLOW`: routes and actions without a rule are accessible to everyone.

## FAQ

### **Q: What happens to routes and actions that have no rule?**

A: They are open to everyone.
The shipped `protection_policy` is `GuardInterface::POLICY_ALLOW`.

### **Q: Which role does a visitor who is not logged in have?**

A: `guest`, as set by `guest_role` in `authorization.global.php`.
It has only the `unauthenticated` permission.

### **Q: How do I protect every action of a route?**

A: Add a rule for the route with an empty `actions` array.

### **Q: What is the `premium` permission for?**

A: It protects the `premium-content` action of the `page` route (`/page/premium-content`).
The shipped `user` role has it, so any logged-in user can open the page.

### **Q: Where are a user's roles stored?**

A: In the `user_role` table, linked to users through `user_roles`.
The default roles are created by the `RoleLoader` fixture.
21 changes: 21 additions & 0 deletions docs/book/v5/how-to/creating-fixtures.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Fixtures

## Summary

This page lists and runs the Doctrine data fixtures that seed the database, either all at once or one class at a time.

## Details

> Fixtures are used to seed the database with initial values and should only be executed ONCE each, after migrating the database.

Seeding the database is done with the help of our custom package `dotkernel/dot-data-fixtures` built on top of `doctrine/data-fixtures`.
Expand Down Expand Up @@ -30,3 +36,18 @@ php bin/doctrine fixtures:execute --class=RoleLoader
Fixtures can and should be ordered to ensure database consistency.
More on ordering fixtures can be found here :
https://www.doctrine-project.org/projects/doctrine-data-fixtures/en/latest/how-to/fixture-ordering.html#fixture-ordering

## FAQ

### **Q: Where do fixture classes go?**

A: In `data/doctrine/fixtures`, the path set by `doctrine` => `fixtures` in `config/autoload/doctrine.global.php`.

### **Q: What value does `--class` take?**

A: The class's short name, which must match its file name without `.php`, for example `--class=RoleLoader` for `RoleLoader.php`.

### **Q: Why should each fixture run only once?**

A: `fixtures:execute` appends to the existing data instead of purging it.
Running a fixture twice inserts its rows again, which fails on unique columns such as `user_role.name`.
34 changes: 30 additions & 4 deletions docs/book/v5/how-to/creating-migrations.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Creating migrations

## Summary

This page generates a new Doctrine migration file and shows how to write its `up` and `down` methods.

## Details

Migrations are used to create and/or edit the database structure.
To generate a new migration file, use this command:

Expand All @@ -18,12 +24,32 @@ You can add new queries in:
This example creates a new column named `test`.
Add this in `public function up`:

```shell
$this->addSql('ALTER TABLE users ADD test VARCHAR(255) NOT NULL');
```php
$this->addSql('ALTER TABLE user ADD test VARCHAR(255) NOT NULL');
```

And its opposite in `public function down`:

```shell
$this->addSql('ALTER TABLE users DROP test');
```php
$this->addSql('ALTER TABLE user DROP test');
```

## FAQ

### **Q: Where are new migrations saved?**

A: In `data/doctrine/migrations`, in the `Frontend\Migrations` namespace, as set in `config/migrations.php`.

### **Q: How do I undo the last migration?**

A: Run `php vendor/bin/doctrine-migrations migrate prev`, which runs the `down` method of the latest executed migration.

### **Q: Can Doctrine write the migration for me?**

A: Yes. After changing your entities, run `php vendor/bin/doctrine-migrations diff`, which generates a migration from the difference between the entity mapping and the database.
Review the generated SQL before you run it.

### **Q: Is a failed migration rolled back?**

A: `config/migrations.php` enables `transactional` and `all_or_nothing`.
MySQL and MariaDB commit schema changes implicitly, so a failed `ALTER TABLE` may still leave earlier statements applied.
38 changes: 36 additions & 2 deletions docs/book/v5/how-to/csrf.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CSRF protection in forms

## Summary

This page adds CSRF protection to a form in three steps: a `Csrf` form element, a CSRF validator in the input filter, and the rendered hidden field in the template.
It also shows how to test the protection and how the token timeout works.

## Details

A Cross-Site Request Forgery (CSRF) attack is a type of security vulnerability that tricks a user into performing actions on a web application in which they are authenticated, without their knowledge or consent.

Web applications can protect users against these types of attacks by implementing CSRF tokens in their forms which are known only to the application that generated them and must be included when submitting forms.
Expand Down Expand Up @@ -33,7 +40,8 @@ $this->add(new \Laminas\Form\Element\Csrf('exampleCsrf', [
### Validate field

Open the InputFilter that validates the form fields and append the following code to the method that initializes the
fields (usually `init`):
fields (usually `init`).
If the form builds its input filter itself (for example `ProfileDetailsForm`), add it there instead:

```php
$csrf = new \Laminas\InputFilter\Input('exampleCsrf');
Expand Down Expand Up @@ -76,6 +84,12 @@ After filling out the form, submitting it should work as before.
In order to make sure that the new CSRF field works as expected, you can inspect the form using your browser's `Developer tools` and modify its value.
Submitting a filled out form should result in a validation error:

```text
CSRF is invalid
```

Clearing the value instead results in:

```text
CSRF is required and cannot be empty
```
Expand All @@ -87,7 +101,27 @@ This represents the value in seconds for how long the token is valid.
Submitting a form that has been rendered for longer than this value will result in a validation error:

```text
**CSRF** is invalid
CSRF is invalid
```

> You can modify the value of `timeout` in each form, but the default value should work in most cases.

## FAQ

### **Q: Which forms are already protected?**

A: Every shipped form: login, register, request password reset, reset password, profile details, avatar upload, change password, delete account and contact.
Their fields are named after the form, for example `userLoginCsrf` and `contactCsrf`.

### **Q: How long is a token valid?**

A: 3600 seconds (one hour), set by the `timeout` option of each form's `Csrf` element.

### **Q: Can two forms use the same CSRF field name?**

A: Give each form its own name.
Each name keeps its own token in the session, so forms that share a name also share, and overwrite, the same token.

### **Q: Which error appears when the token is wrong?**

A: A missing token gives "CSRF is required and cannot be empty"; a modified or expired token gives "CSRF is invalid".
39 changes: 33 additions & 6 deletions docs/book/v5/how-to/dependency-injection.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Dependency Injection

## Summary

This page explains how Dotkernel Frontend injects constructor dependencies with the `#[Inject]` attribute from `dot-dependency-injection`, and how to register a class with `AttributedServiceFactory` so that no factory has to be written.

## Details

Dependency injection is a design pattern used in software development to implement inversion of control.
In simpler terms, it's the act of providing dependencies for an object during instantiation.

Expand All @@ -14,20 +20,21 @@ Dotkernel Frontend, through its [dot-dependency-injection](https://github.com/do
`dot-dependency-injection` determines the dependencies by looking at the `#[Inject]` attribute, added to the constructor of a class.
Each dependency is specified as a separate parameter of the `#[Inject]` attribute.

For our example we will inject `UserService` and `config` dependencies into a `UserHandler`.
For our example we will inject `UserServiceInterface` and `config` dependencies into a `UserController`.

```php
use Dot\Controller\AbstractActionController;
use Dot\DependencyInjection\Attribute\Inject;

class UserHandler implements RequestHandlerInterface
class UserController extends AbstractActionController
{
#[Inject(
UserService::class,
UserServiceInterface::class,
"config",
)]
public function __construct(
protected UserServiceInterface $userService,
protected array $config,
protected array $config = [],
) {
}
}
Expand All @@ -43,7 +50,7 @@ public function getDependencies(): array
{
return [
'factories' => [
UserHandler::class => AttributedServiceFactory::class
UserController::class => AttributedServiceFactory::class
]
];
}
Expand All @@ -53,4 +60,24 @@ That's it.
When your object is instantiated from the container, it will automatically have its dependencies resolved.

> Dependencies injection is available to any object within Dotkernel Frontend.
> For example, you can inject dependencies in a service, a handler and so on, simply by registering them in the `ConfigProvider`.
> For example, you can inject dependencies in a service, a controller and so on, simply by registering them in the `ConfigProvider`.

## FAQ

### **Q: Do I still need to write factories?**

A: Not for classes that use `#[Inject]`.
Register services and controllers with `AttributedServiceFactory`, and Doctrine repositories with `AttributedRepositoryFactory`, as `src/User/src/ConfigProvider.php` does.

### **Q: How do I inject a single configuration key?**

A: Use dot notation in the attribute.
For example, `RecaptchaService` uses `#[Inject("config.recaptcha")]` to receive only the `recaptcha` array.

### **Q: Does the order of the `#[Inject]` arguments matter?**

A: Yes. The dependencies are passed to the constructor in the order they are listed, so the list must match the order of the constructor parameters.

### **Q: Does the package support setter or property injection?**

A: No. `dot-dependency-injection` supports constructor injection only.
32 changes: 31 additions & 1 deletion docs/book/v5/how-to/npm_commands.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# NPM Commands

## Summary

This page installs the frontend build dependencies with npm and compiles the assets, either continuously while you develop or once, minified, for production.

## Details

> The Frontend interface has been tested with npm v10.0.4 and Node.js v20.11.0.

To install dependencies into the `node_modules` directory run this command.

```shell
Expand All @@ -13,10 +21,32 @@ The watch command compiles the components then monitors the files for changes an

```shell
npm run watch
```
```

After all updates are done, this command compiles the assets locally, minifies them and makes them ready for production.

```shell
npm run prod
```

To compile the assets once in development mode, without watching for changes, run:

```shell
npm run dev
```

## FAQ

### **Q: Where are the source assets, and where does the build write them?**

A: The sources are in `src/App/assets`.
Webpack writes the compiled files to `public/css`, `public/js`, `public/fonts` and `public/images`.

### **Q: Which Node.js and npm versions are supported?**

A: The interface has been tested with npm v10.0.4 and Node.js v20.11.0.

### **Q: Should I commit the compiled assets?**

A: The repository tracks the compiled `public/css/app.css` and `public/js/app.js`.
Run `npm run prod` before you commit asset changes so that the committed files are minified.
Loading
Loading