Skip to content

Commit

Permalink
feat(TwoFactorBundle): Added TwoFactor bundle to login to Roadiz with…
Browse files Browse the repository at this point in the history
… TOTP application
  • Loading branch information
ambroisemaupate committed Apr 13, 2023
1 parent 2cea745 commit 0953b00
Show file tree
Hide file tree
Showing 30 changed files with 931 additions and 15 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/split.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ jobs:
local_path: 'Rozier'
split_repository: 'rozier'
default_branch: 'main'
-
local_path: 'RoadizTwoFactorBundle'
split_repository: 'two-factor-bundle'
default_branch: 'main'


steps:
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ test:
php -d "memory_limit=-1" vendor/bin/phpcbf -p ./lib/RoadizFontBundle/src
php -d "memory_limit=-1" vendor/bin/phpcbf -p ./lib/RoadizRozierBundle/src
php -d "memory_limit=-1" vendor/bin/phpcbf -p ./lib/RoadizUserBundle/src
php -d "memory_limit=-1" vendor/bin/phpcbf -p ./lib/RoadizTwoFactorBundle/src
php -d "memory_limit=-1" vendor/bin/phpcbf -p ./lib/Rozier/src
php -d "memory_limit=-1" vendor/bin/phpstan analyse -c phpstan.neon
php -d "memory_limit=-1" bin/console lint:twig ./lib/Rozier/src/Resources/views
Expand All @@ -27,6 +28,7 @@ test:
php -d "memory_limit=-1" bin/console lint:twig ./lib/RoadizRozierBundle/templates
php -d "memory_limit=-1" bin/console lint:twig ./lib/RoadizFontBundle/templates
php -d "memory_limit=-1" bin/console lint:twig ./lib/RoadizCoreBundle/templates
php -d "memory_limit=-1" bin/console lint:twig ./lib/RoadizTwoFactorBundle/templates

requirements:
vendor/bin/requirements-checker
Expand Down
11 changes: 10 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"doctrine/doctrine-migrations-bundle": "^3.1",
"doctrine/migrations": "^3.1.1",
"doctrine/orm": "^2.14.1",
"endroid/qr-code": "^4.0",
"enshrined/svg-sanitize": "^0.15",
"gedmo/doctrine-extensions": "~3.10.0",
"guzzlehttp/guzzle": "^7.2.0",
Expand Down Expand Up @@ -78,9 +79,15 @@
"roadiz/openid": "^2.1",
"roadiz/random": "^2.1",
"roadiz/rozier-bundle": "^2.1",
"roadiz/two-factor-bundle": "^2.1",
"roadiz/user-bundle": "^2.1",
"rollerworks/password-common-list": "^0.2.0",
"rollerworks/password-strength-bundle": "^2.2",
"scheb/2fa-backup-code": "^6.8",
"scheb/2fa-bundle": "^6.8",
"scheb/2fa-google-authenticator": "^6.8",
"scheb/2fa-totp": "^6.8",
"scheb/2fa-trusted-device": "^6.8",
"scienta/doctrine-json-functions": "^4.2",
"sensio/framework-extra-bundle": "^6.1",
"sentry/sentry-symfony": "^4.2",
Expand Down Expand Up @@ -156,7 +163,7 @@
"symfony/browser-kit": "5.4.*",
"symfony/css-selector": "5.4.*",
"symfony/debug-bundle": "5.4.*",
"symfony/maker-bundle": "^1.0",
"symfony/maker-bundle": "^1.48",
"symfony/phpunit-bridge": "5.4.*",
"symfony/web-profiler-bundle": "5.4.*",
"symplify/monorepo-builder": "11.2.2.72"
Expand Down Expand Up @@ -195,6 +202,7 @@
"RZ\\Roadiz\\OpenId\\": "lib/OpenId/src/",
"RZ\\Roadiz\\Random\\": "lib/Random/src/",
"RZ\\Roadiz\\RozierBundle\\": "lib/RoadizRozierBundle/src/",
"RZ\\Roadiz\\TwoFactorBundle\\": "lib/RoadizTwoFactorBundle/src/",
"RZ\\Roadiz\\Typescript\\Declaration\\": "lib/DtsGenerator/src/",
"RZ\\Roadiz\\UserBundle\\": "lib/RoadizUserBundle/src/",
"RZ\\Roadiz\\Utils\\": "lib/Models/src/Roadiz/Utils/",
Expand Down Expand Up @@ -224,6 +232,7 @@
"roadiz/roadiz": "*",
"roadiz/rozier": "*",
"roadiz/rozier-bundle": "*",
"roadiz/two-factor-bundle": "*",
"roadiz/user-bundle": "*"
},
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions config/bundles.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
RZ\Roadiz\CompatBundle\RoadizCompatBundle::class => ['all' => true],
RZ\Roadiz\RozierBundle\RoadizRozierBundle::class => ['all' => true],
RZ\Roadiz\UserBundle\RoadizUserBundle::class => ['all' => true],
RZ\Roadiz\TwoFactorBundle\RoadizTwoFactorBundle::class => ['all' => true],
JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
Symfony\Cmf\Bundle\RoutingBundle\CmfRoutingBundle::class => ['all' => true],
Rollerworks\Bundle\PasswordStrengthBundle\RollerworksPasswordStrengthBundle::class => ['all' => true],
Expand All @@ -28,4 +29,5 @@
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
Rollerworks\Bundle\PasswordCommonListBundle\RollerworksPasswordCommonListBundle::class => ['all' => true],
Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
];
26 changes: 26 additions & 0 deletions config/packages/scheb_2fa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# See the configuration reference at https://symfony.com/bundles/SchebTwoFactorBundle/6.x/configuration.html
scheb_two_factor:
security_tokens:
- Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken
- Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken

google:
enabled: true
server_name: "%env(string:APP_NAMESPACE)%"
issuer: "%env(string:APP_TITLE)%"
totp:
enabled: true # If TOTP authentication should be enabled, default false
server_name: "%env(string:APP_NAMESPACE)%"
issuer: "%env(string:APP_TITLE)%"

# Trusted device feature
trusted_device:
enabled: true # If the trusted device feature should be enabled
lifetime: 5184000 # Lifetime of the trusted device cookie
extend_lifetime: true
key: "%env(string:APP_SECRET)%"
cookie_name: trusted_device # Name of the trusted device cookie
cookie_secure: false # Set the 'Secure' (HTTPS Only) flag on the trusted device cookie
cookie_same_site: "lax" # The same-site option of the cookie, can be "lax" or "strict"
backup_codes:
enabled: true # If the backup code feature should be enabled
20 changes: 13 additions & 7 deletions config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ security:
main:
lazy: true
provider: all_users
two_factor:
auth_form_path: 2fa_login # The route name you have used in the routes.yaml
check_path: 2fa_login_check # The route name you have used in the routes.yaml

# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
Expand All @@ -73,14 +76,17 @@ security:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/rz-admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/rz-admin/login, roles: PUBLIC_ACCESS }
- { path: ^/rz-admin/logout, roles: PUBLIC_ACCESS }
- { path: ^/rz-admin, roles: ROLE_BACKEND_USER }
- { path: ^/api/token, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: "^/api/custom_forms/(?:[0-9]+)/post", methods: [ POST ], roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: "^/api/contact_form/post", methods: [ POST ], roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: "^/api/users/signup", methods: [ POST ], roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: "^/api/users/password_request", methods: [ POST ], roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: "^/api/users/password_reset", methods: [ PUT ], roles: IS_AUTHENTICATED_ANONYMOUSLY }
# This ensures that the form can only be accessed when two-factor authentication is in progress.
- { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
- { path: ^/api/token, roles: PUBLIC_ACCESS }
- { path: "^/api/custom_forms/(?:[0-9]+)/post", methods: [ POST ], roles: PUBLIC_ACCESS }
- { path: "^/api/contact_form/post", methods: [ POST ], roles: PUBLIC_ACCESS }
- { path: "^/api/users/signup", methods: [ POST ], roles: PUBLIC_ACCESS }
- { path: "^/api/users/password_request", methods: [ POST ], roles: PUBLIC_ACCESS }
- { path: "^/api/users/password_reset", methods: [ PUT ], roles: PUBLIC_ACCESS }
- { path: "^/api/users", methods: [ GET, PUT, PATCH, POST ], roles: ROLE_USER }
- { path: ^/api, roles: ROLE_BACKEND_USER, methods: [ POST, PUT, PATCH, DELETE ] }
# - { path: ^/profile, roles: ROLE_USER }
3 changes: 3 additions & 0 deletions config/routes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ roadiz_rozier:
roadiz_font:
resource: "@RoadizFontBundle/config/routing.yaml"

roadiz_two_factor:
resource: "@RoadizTwoFactorBundle/config/routing.yaml"

rz_intervention_request:
resource: "@RZInterventionRequestBundle/Resources/config/routing.yml"
prefix: /
Expand Down
7 changes: 7 additions & 0 deletions config/routes/scheb_2fa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
2fa_login:
path: /2fa
defaults:
_controller: "scheb_two_factor.form_controller::form"

2fa_login_check:
path: /2fa_check
7 changes: 4 additions & 3 deletions lib/RoadizCoreBundle/config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ security:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/rz-admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/rz-admin/login, roles: PUBLIC_ACCESS }
- { path: ^/rz-admin/logout, roles: PUBLIC_ACCESS }
- { path: ^/rz-admin, roles: ROLE_BACKEND_USER }
- { path: ^/api/token, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: "^/api/custom_forms/(?:[0-9]+)/post", methods: [ POST ], roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/token, roles: PUBLIC_ACCESS }
- { path: "^/api/custom_forms/(?:[0-9]+)/post", methods: [ POST ], roles: PUBLIC_ACCESS }
- { path: ^/api, roles: ROLE_BACKEND_USER, methods: [ POST, PUT, PATCH, DELETE ] }
# - { path: ^/profile, roles: ROLE_USER }
2 changes: 1 addition & 1 deletion lib/RoadizRozierBundle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ security:
custom_authenticator:
- RZ\Roadiz\RozierBundle\Security\RozierAuthenticator
access_control:
- { path: ^/rz-admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/rz-admin/login, roles: PUBLIC_ACCESS }
- { path: ^/rz-admin, roles: ROLE_BACKEND_USER }
```
- Add custom routes:
Expand Down
58 changes: 58 additions & 0 deletions lib/RoadizTwoFactorBundle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Roadiz Font bundle

![Run test status](https://github.com/roadiz/two-factor-bundle/actions/workflows/run-test.yml/badge.svg?branch=develop)

Installation
============

Make sure Composer is installed globally, as explained in the
[installation chapter](https://getcomposer.org/doc/00-intro.md)
of the Composer documentation.

Applications that use Symfony Flex
----------------------------------

Open a command console, enter your project directory and execute:

```console
$ composer require roadiz/two-factor-bundle
```

Applications that don't use Symfony Flex
----------------------------------------

### Step 1: Download the Bundle

Open a command console, enter your project directory and execute the
following command to download the latest stable version of this bundle:

```console
$ composer require roadiz/two-factor-bundle
```

### Step 2: Enable the Bundle

Then, enable the bundle by adding it to the list of registered bundles
in the `config/bundles.php` file of your project:

```php
// config/bundles.php

return [
// ...
\RZ\Roadiz\TwoFactor\RoadizTwoFactor::class => ['all' => true],
];
```

## Configuration

- Copy and merge `@RoadizTwoFactor/config/packages/*` files into your project `config/packages` folder
```yaml
# config/routes.yaml
roadiz_two_bundle:
resource: "@RoadizTwoFactor/config/routing.yaml"
```
## Contributing
Report [issues](https://github.com/roadiz/core-bundle-dev-app/issues) and send [Pull Requests](https://github.com/roadiz/core-bundle-dev-app/pulls) in the [main Roadiz repository](https://github.com/roadiz/core-bundle-dev-app)
69 changes: 69 additions & 0 deletions lib/RoadizTwoFactorBundle/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"name": "roadiz/two-factor-bundle",
"license": "MIT",
"keywords": [
"cms",
"backoffice",
"rezo zero"
],
"authors": [
{
"name": "Ambroise Maupate",
"email": "ambroise@roadiz.io",
"homepage": "https://www.roadiz.io",
"role": "Lead developer"
}
],
"type": "symfony-bundle",
"require": {
"php": ">=8.0",
"doctrine/orm": "^2.14.1",
"endroid/qr-code": "^4.0",
"roadiz/core-bundle": "^2.1",
"roadiz/rozier-bundle": "^2.1",
"scheb/2fa-backup-code": "^6.8",
"scheb/2fa-bundle": "^6.8",
"scheb/2fa-totp": "^6.8",
"scheb/2fa-google-authenticator": "^6.8",
"scheb/2fa-trusted-device": "^6.8",
"sensio/framework-extra-bundle": "^6.1",
"symfony/framework-bundle": "5.4.*"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.4",
"phpstan/phpstan": "^1.5.3",
"phpstan/phpstan-doctrine": "^1.3",
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.5",
"symfony/stopwatch": "5.4.*"
},
"config": {
"optimize-autoloader": true,
"preferred-install": {
"*": "dist"
},
"sort-packages": true,
"allow-plugins": {
"symfony/flex": false,
"symfony/runtime": false,
"php-http/discovery": false
}
},
"autoload": {
"psr-4": {
"RZ\\Roadiz\\TwoFactorBundle\\": "src/"
}
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
}
},
"extra": {
"branch-alias": {
"dev-main": "2.1.x-dev",
"dev-develop": "2.2.x-dev"
}
}
}
11 changes: 11 additions & 0 deletions lib/RoadizTwoFactorBundle/config/routing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

2fa_admin_two_factor:
path: /rz-admin/two-factor
defaults:
_controller: RZ\Roadiz\TwoFactorBundle\Controller\TwoFactorAdminController::twoFactorAdminAction


2fa_qr_code_totp:
path: /rz-admin/two-factor/qr/totp
defaults:
_controller: RZ\Roadiz\TwoFactorBundle\Controller\QrCodeController::totpQrCodeAction
26 changes: 26 additions & 0 deletions lib/RoadizTwoFactorBundle/config/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind: {}

RZ\Roadiz\TwoFactorBundle\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Traits/'
- '../src/Kernel.php'
- '../src/Tests/'
- '../src/Event/'

RZ\Roadiz\TwoFactorBundle\Controller\:
resource: '../src/Controller'
tags: [ 'controller.service_arguments' ]

roadiz_two_factor.security.totp.provider:
class: RZ\Roadiz\TwoFactorBundle\Security\Provider\AuthenticatorTwoFactorProvider
tags:
- { name: scheb_two_factor.provider, alias: 'roadiz_totp' }
36 changes: 36 additions & 0 deletions lib/RoadizTwoFactorBundle/migrations/Version20230413154052.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace RZ\Roadiz\TwoFactorBundle\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20230413154052 extends AbstractMigration
{
public function getDescription(): string
{
return 'Added TwoFactorUser entity';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE two_factor_users (user_id INT NOT NULL, secret VARCHAR(255) DEFAULT NULL, backup_codes JSON DEFAULT NULL, trusted_version INT DEFAULT 1 NOT NULL, algorithm VARCHAR(6) DEFAULT NULL, period SMALLINT DEFAULT NULL, digits SMALLINT DEFAULT NULL, UNIQUE INDEX UNIQ_12ED8E9FA76ED395 (user_id), PRIMARY KEY(user_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE two_factor_users ADD CONSTRAINT FK_12ED8E9FA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE');
$this->addSql('DROP TABLE untitled_folder');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE untitled_folder (untitled_id INT NOT NULL, folder_id INT NOT NULL, INDEX IDX_257063F7162CB942 (folder_id), INDEX IDX_257063F752B0ED85 (untitled_id), PRIMARY KEY(untitled_id, folder_id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB COMMENT = \'\' ');
$this->addSql('ALTER TABLE untitled_folder ADD CONSTRAINT FK_257063F7162CB942 FOREIGN KEY (folder_id) REFERENCES folders (id) ON UPDATE NO ACTION ON DELETE CASCADE');
$this->addSql('ALTER TABLE untitled_folder ADD CONSTRAINT FK_257063F752B0ED85 FOREIGN KEY (untitled_id) REFERENCES nodes_sources (id) ON UPDATE NO ACTION ON DELETE CASCADE');
$this->addSql('DROP TABLE two_factor_users');
}
}
Loading

0 comments on commit 0953b00

Please sign in to comment.