Upgrade 1-11.38

This commit is contained in:
xesmyd
2026-03-30 14:10:30 +02:00
parent f2a7e6d1fc
commit ac648ef29d
24665 changed files with 69682 additions and 2205004 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2010-2016 Thomas Rabaix
Copyright (c) 2010 Thomas Rabaix
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+17 -3
View File
@@ -14,7 +14,7 @@ lint-composer:
.PHONY: lint-composer
lint-yaml:
find . -name '*.yml' -not -path './vendor/*' -not -path './src/Resources/public/vendor/*' | xargs yaml-lint
yaml-lint --ignore-non-yaml-files --quiet --exclude vendor .
.PHONY: lint-yaml
@@ -51,8 +51,22 @@ cs-fix-xml:
done
.PHONY: cs-fix-xml
test:
phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml
build:
mkdir $@
HAS_XDEBUG=$(shell php --modules|grep --quiet xdebug;echo $$?)
build/xdebug-filter.php: phpunit.xml.dist build
ifeq ($(HAS_XDEBUG), 0)
phpunit --dump-xdebug-filter $@
endif
test: build/xdebug-filter.php
ifeq ($(HAS_XDEBUG), 0)
phpunit --prepend build/xdebug-filter.php -c phpunit.xml.dist --coverage-clover build/logs/clover.xml
else
phpunit -c phpunit.xml.dist
endif
.PHONY: test
docs:
+38 -25
View File
@@ -2,7 +2,11 @@
"name": "sonata-project/user-bundle",
"type": "symfony-bundle",
"description": "Symfony SonataUserBundle",
"keywords": ["user", "sonata", "google authenticator"],
"keywords": [
"user",
"sonata",
"google authenticator"
],
"homepage": "http://sonata-project.org/bundles/user",
"license": "MIT",
"authors": [
@@ -19,30 +23,44 @@
"require": {
"php": "^7.1",
"friendsofsymfony/user-bundle": "^2.0",
"sonata-project/admin-bundle": "^3.1",
"sonata-project/core-bundle": "^3.2",
"sonata-project/admin-bundle": "^3.34",
"sonata-project/core-bundle": "^3.12",
"sonata-project/datagrid-bundle": "^2.2.1",
"sonata-project/doctrine-extensions": "^1.0",
"sonata-project/easy-extends-bundle": "^2.2",
"swiftmailer/swiftmailer": "^4.3 || ^5.0 || ^6.0",
"symfony/config": "^2.8 || ^3.2 || ^4.0",
"symfony/console": "^2.8 || ^3.2 || ^4.0",
"symfony/form": "^2.8 || ^3.2 || ^4.0",
"symfony/dependency-injection": "^2.8 || ^3.2 || ^4.0",
"symfony/form": "^2.8.18 || ^3.2.5 || ^4.0",
"symfony/framework-bundle": "^2.8 || ^3.2 || ^4.0",
"symfony/http-foundation": "^2.8 || ^3.2 || ^4.0",
"symfony/http-kernel": "^2.8 || ^3.2 || ^4.0",
"symfony/options-resolver": "^2.8 || ^3.2 || ^4.0",
"symfony/security-acl": "^2.8 || ^3.0",
"symfony/security-core": "^2.8 || ^3.2 || ^4.0",
"symfony/translation": "^2.8 || ^3.2 || ^4.0"
},
"conflict": {
"friendsofsymfony/rest-bundle": "<2.1 || >=3.0",
"jms/serializer": "<0.13 || >=3.0",
"nelmio/api-doc-bundle": "<2.4",
"sonata-project/block-bundle": "<3.11",
"sonata-project/doctrine-orm-admin-bundle": "<3.0",
"sonata-project/google-authenticator": "<1.0",
"sonata-project/seo-bundle": "<2.0"
},
"require-dev": {
"doctrine/orm": "^2.0",
"friendsofsymfony/rest-bundle": "^1.5 || ^2.0",
"jms/serializer-bundle": "^0.13 || ^1.0",
"matthiasnoback/symfony-config-test": "^2.1",
"matthiasnoback/symfony-dependency-injection-test": "^1.1",
"friendsofsymfony/rest-bundle": "^2.1",
"jms/serializer-bundle": "^1.0 || ^2.0 || ^3.0",
"matthiasnoback/symfony-config-test": "^4.0",
"matthiasnoback/symfony-dependency-injection-test": "^3.0",
"nelmio/api-doc-bundle": "^2.4",
"sonata-project/block-bundle": "^3.2",
"sonata-project/block-bundle": "^3.11",
"sonata-project/google-authenticator": "^1.0 || ^2.0",
"sonata-project/seo-bundle": "^2.0",
"symfony/phpunit-bridge": "^4.0"
"symfony/phpunit-bridge": "^4.3"
},
"suggest": {
"friendsofsymfony/rest-bundle": "For using the public API methods.",
@@ -52,21 +70,6 @@
"sonata-project/google-authenticator": "For google auth user login",
"sonata-project/seo-bundle": "For SEO breadcrumb block service usage"
},
"conflict": {
"friendsofsymfony/rest-bundle": "<1.1",
"jms/serializer": "<0.13 || >=2.0",
"nelmio/api-doc-bundle": "<2.4",
"sonata-project/block-bundle": "<3.2",
"sonata-project/doctrine-orm-admin-bundle": "<3.0",
"sonata-project/google-authenticator": "<1.0",
"sonata-project/seo-bundle": "<2.0"
},
"autoload": {
"psr-4": { "Sonata\\UserBundle\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Sonata\\UserBundle\\Tests\\": "tests/" }
},
"config": {
"sort-packages": true
},
@@ -74,5 +77,15 @@
"branch-alias": {
"dev-master": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"Sonata\\UserBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Sonata\\UserBundle\\Tests\\": "tests/"
}
}
}
+11 -2
View File
@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
DO NOT EDIT THIS FILE!
It's auto-generated by sonata-project/dev-kit package.
-->
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
@@ -8,15 +14,18 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="SonataUserBundle Test Suite">
<testsuite name="Sonata User Bundle Test Suite">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
</listeners>
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
@@ -22,11 +22,12 @@ use Sonata\AdminBundle\Form\Type\ModelType;
use Sonata\AdminBundle\Show\ShowMapper;
use Sonata\CoreBundle\Form\Type\DatePickerType;
use Sonata\UserBundle\Form\Type\SecurityRolesType;
use Sonata\UserBundle\Form\Type\UserGenderListType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\LocaleType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TimezoneType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Form\FormTypeInterface;
class UserAdmin extends AbstractAdmin
{
@@ -58,8 +59,8 @@ class UserAdmin extends AbstractAdmin
public function getExportFields()
{
// avoid security field to be exported
return array_filter(parent::getExportFields(), function ($v) {
return !in_array($v, ['password', 'salt']);
return array_filter(parent::getExportFields(), static function ($v) {
return !\in_array($v, ['password', 'salt'], true);
});
}
@@ -103,7 +104,7 @@ class UserAdmin extends AbstractAdmin
if ($this->isGranted('ROLE_ALLOWED_TO_SWITCH')) {
$listMapper
->add('impersonating', 'string', ['template' => 'SonataUserBundle:Admin:Field/impersonating.html.twig'])
->add('impersonating', 'string', ['template' => '@SonataUser/Admin/Field/impersonating.html.twig'])
;
}
}
@@ -182,6 +183,17 @@ class UserAdmin extends AbstractAdmin
$now = new \DateTime();
$genderOptions = [
'choices' => \call_user_func([$this->getUserManager()->getClass(), 'getGenderList']),
'required' => true,
'translation_domain' => $this->getTranslationDomain(),
];
// NEXT_MAJOR: Remove this when dropping support for SF 2.8
if (method_exists(FormTypeInterface::class, 'setDefaultOptions')) {
$genderOptions['choices_as_values'] = true;
}
$formMapper
->tab('User')
->with('General')
@@ -202,10 +214,7 @@ class UserAdmin extends AbstractAdmin
->add('lastname', null, ['required' => false])
->add('website', UrlType::class, ['required' => false])
->add('biography', TextType::class, ['required' => false])
->add('gender', UserGenderListType::class, [
'required' => true,
'translation_domain' => $this->getTranslationDomain(),
])
->add('gender', ChoiceType::class, $genderOptions)
->add('locale', LocaleType::class, ['required' => false])
->add('timezone', TimezoneType::class, ['required' => false])
->add('phone', null, ['required' => false])
@@ -13,23 +13,59 @@ declare(strict_types=1);
namespace Sonata\UserBundle\Command;
use FOS\UserBundle\Model\UserManagerInterface;
use Sonata\UserBundle\GoogleAuthenticator\Helper;
use Sonata\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* NEXT_MAJOR: stop extending ContainerAwareCommand.
*/
class TwoStepVerificationCommand extends ContainerAwareCommand
{
/**
* @var ?Helper
*/
private $helper;
/**
* @var ?UserManagerInterface
*/
private $userManager;
/**
* NEXT_MAJOR: make $helper and $userManager mandatory (but still nullable).
*/
public function __construct(
?string $name,
?Helper $helper = null,
?UserManagerInterface $userManager = null
) {
parent::__construct($name);
$this->helper = $helper;
$this->userManager = $userManager;
}
/**
* {@inheritdoc}
*/
public function configure(): void
{
$this->setName('sonata:user:two-step-verification');
$this->addArgument('username', InputArgument::REQUIRED, 'The username to protect with a two step verification process');
$this->addArgument(
'username',
InputArgument::REQUIRED,
'The username to protect with a two step verification process'
);
$this->addOption('reset', null, InputOption::VALUE_NONE, 'Reset the current two step verification token');
$this->setDescription('Generate a two step verification process to secure an access (Ideal for super admin protection)');
$this->setDescription(
'Generate a two step verification process to secure an access (Ideal for super admin protection)'
);
}
/**
@@ -37,28 +73,46 @@ class TwoStepVerificationCommand extends ContainerAwareCommand
*/
public function execute(InputInterface $input, OutputInterface $output): void
{
if (!$this->getContainer()->has('sonata.user.google.authenticator.provider')) {
if (null === $this->helper && !$this->getContainer()->has('sonata.user.google.authenticator.provider')) {
throw new \RuntimeException('Two Step Verification process is not enabled');
}
$helper = $this->getContainer()->get('sonata.user.google.authenticator.provider');
$manager = $this->getContainer()->get('fos_user.user_manager');
if (null === $this->helper) {
@trigger_error(sprintf(
'Not providing the $helper argument of "%s::__construct()" is deprecated since 4.3.0 and will no longer be possible in 5.0',
__CLASS__
), E_USER_DEPRECATED);
$helper = $this->getContainer()->get('sonata.user.google.authenticator.provider');
\assert($helper instanceof Helper);
$this->helper = $helper;
}
$user = $manager->findUserByUsernameOrEmail($input->getArgument('username'));
if (null === $this->userManager) {
@trigger_error(sprintf(
'Not providing the $userManager argument of "%s::__construct()" is deprecated since 4.3.0 and will no longer be possible in 5.0',
__CLASS__
), E_USER_DEPRECATED);
$manager = $this->getContainer()->get('fos_user.user_manager');
\assert($manager instanceof UserManagerInterface);
$this->userManager = $manager;
}
$user = $this->userManager->findUserByUsernameOrEmail($input->getArgument('username'));
\assert($user instanceof UserInterface);
if (!$user) {
throw new \RuntimeException(sprintf('Unable to find the username : %s', $input->getArgument('username')));
}
if (!$user->getTwoStepVerificationCode() || $input->getOption('reset')) {
$user->setTwoStepVerificationCode($helper->generateSecret());
$manager->updateUser($user);
$user->setTwoStepVerificationCode($this->helper->generateSecret());
$this->userManager->updateUser($user);
}
$output->writeln([
sprintf('<info>Username</info> : %s', $input->getArgument('username')),
sprintf('<info>Secret</info> : %s', $user->getTwoStepVerificationCode()),
sprintf('<info>Url</info> : %s', $helper->getUrl($user)),
sprintf('<info>Url</info> : %s', $this->helper->getUrl($user)),
]);
}
}
@@ -13,15 +13,20 @@ declare(strict_types=1);
namespace Sonata\UserBundle\Controller;
use FOS\UserBundle\Model\UserInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
// NEXT_MAJOR: remove this file
@trigger_error(
'The '.__NAMESPACE__.'\AdminResettingController class is deprecated since version 4.3.0 and will be removed in 5.0.'
.' Use '.__NAMESPACE__.'\RequestAction, '.__NAMESPACE__.'\CheckEmailAction, '.__NAMESPACE__.'\ResetAction or '.__NAMESPACE__.'\SendEmailAction instead.',
E_USER_DEPRECATED
);
use Sonata\UserBundle\Action\CheckEmailAction;
use Sonata\UserBundle\Action\RequestAction;
use Sonata\UserBundle\Action\ResetAction;
use Sonata\UserBundle\Action\SendEmailAction;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
class AdminResettingController extends Controller
{
@@ -30,171 +35,47 @@ class AdminResettingController extends Controller
*/
public function requestAction()
{
if ($this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
return new RedirectResponse($this->get('router')->generate('sonata_admin_dashboard'));
}
/** @var RequestAction $requestAction */
$requestAction = $this->container->get(RequestAction::class);
return $this->render('@SonataUser/Admin/Security/Resetting/request.html.twig', [
'base_template' => $this->get('sonata.admin.pool')->getTemplate('layout'),
'admin_pool' => $this->get('sonata.admin.pool'),
]);
return $requestAction($this->getCurrentRequest());
}
/**
* @param Request $request
*
* @return Response
*/
public function sendEmailAction(Request $request)
{
$username = $request->request->get('username');
/** @var SendEmailAction $sendEmailAction */
$sendEmailAction = $this->container->get(SendEmailAction::class);
/** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserByUsernameOrEmail($username);
$ttl = $this->container->getParameter('fos_user.resetting.retry_ttl');
if (null !== $user && !$user->isPasswordRequestNonExpired($ttl)) {
if (!$user->isAccountNonLocked()) {
return new RedirectResponse($this->get('router')->generate('sonata_user_admin_resetting_request'));
}
if (null === $user->getConfirmationToken()) {
/** @var $tokenGenerator TokenGeneratorInterface */
$tokenGenerator = $this->get('fos_user.util.token_generator');
$user->setConfirmationToken($tokenGenerator->generateToken());
}
$this->sendResettingEmailMessage($user);
$user->setPasswordRequestedAt(new \DateTime());
$userManager->updateUser($user);
}
return new RedirectResponse($this->generateUrl('sonata_user_admin_resetting_check_email', [
'username' => $username,
]));
return $sendEmailAction($this->getCurrentRequest());
}
/**
* @param Request $request
*
* @return Response
*/
public function checkEmailAction(Request $request)
{
$username = $request->query->get('username');
/** @var CheckEmailAction $checkEmailAction */
$checkEmailAction = $this->container->get(CheckEmailAction::class);
if (empty($username)) {
// the user does not come from the sendEmail action
return new RedirectResponse($this->generateUrl('sonata_user_admin_resetting_request'));
}
return $this->render('@SonataUser/Admin/Security/Resetting/checkEmail.html.twig', [
'base_template' => $this->get('sonata.admin.pool')->getTemplate('layout'),
'admin_pool' => $this->get('sonata.admin.pool'),
'tokenLifetime' => ceil($this->container->getParameter('fos_user.resetting.retry_ttl') / 3600),
]);
return $checkEmailAction($this->getCurrentRequest());
}
/**
* @param Request $request
* @param string $token
*
* @return Response
*/
public function resetAction(Request $request, $token)
public function resetAction(Request $request, string $token)
{
if ($this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
return new RedirectResponse($this->get('router')->generate('sonata_admin_dashboard'));
}
/** @var ResetAction $resetAction */
$resetAction = $this->container->get(ResetAction::class);
/** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.resetting.form.factory');
/** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** @var $loginManager \FOS\UserBundle\Security\LoginManagerInterface */
$loginManager = $this->get('fos_user.security.login_manager');
$user = $userManager->findUserByConfirmationToken($token);
$firewallName = $this->container->getParameter('fos_user.firewall_name');
if (null === $user) {
throw new NotFoundHttpException(sprintf('The user with "confirmation token" does not exist for value "%s"', $token));
}
if (!$user->isPasswordRequestNonExpired($this->container->getParameter('fos_user.resetting.token_ttl'))) {
return new RedirectResponse($this->generateUrl('sonata_user_admin_resetting_request'));
}
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->setConfirmationToken(null);
$user->setPasswordRequestedAt(null);
$user->setEnabled(true);
$message = $this->get('translator')->trans('resetting.flash.success', [], 'FOSUserBundle');
$this->addFlash('success', $message);
$response = new RedirectResponse($this->generateUrl('sonata_admin_dashboard'));
try {
$loginManager->logInUser($firewallName, $user, $response);
$user->setLastLogin(new \DateTime());
} catch (AccountStatusException $ex) {
// We simply do not authenticate users which do not pass the user
// checker (not enabled, expired, etc.).
if ($this->has('logger')) {
$this->get('logger')->warning(sprintf(
'Unable to login user %d after password reset',
$user->getId())
);
}
}
$userManager->updateUser($user);
return $response;
}
return $this->render('@SonataUser/Admin/Security/Resetting/reset.html.twig', [
'token' => $token,
'form' => $form->createView(),
'base_template' => $this->get('sonata.admin.pool')->getTemplate('layout'),
'admin_pool' => $this->get('sonata.admin.pool'),
]);
return $resetAction($this->getCurrentRequest(), $token);
}
/**
* Send an email to a user to confirm the password reset.
*
* @param UserInterface $user
*/
private function sendResettingEmailMessage(UserInterface $user): void
private function getCurrentRequest(): Request
{
$url = $this->generateUrl('sonata_user_admin_resetting_reset', [
'token' => $user->getConfirmationToken(),
], UrlGeneratorInterface::ABSOLUTE_URL);
$rendered = $this->renderView($this->container->getParameter('fos_user.resetting.email.template'), [
'user' => $user,
'confirmationUrl' => $url,
]);
// Render the email, use the first line as the subject, and the rest as the body
$renderedLines = explode(PHP_EOL, trim($rendered));
$subject = array_shift($renderedLines);
$body = implode(PHP_EOL, $renderedLines);
$message = (new \Swift_Message())
->setSubject($subject)
->setFrom($this->container->getParameter('fos_user.resetting.email.from_email'))
->setTo((string) $user->getEmail())
->setBody($body);
$this->get('mailer')->send($message);
return $this->container->get('request_stack')->getCurrentRequest();
}
}
@@ -13,75 +13,43 @@ declare(strict_types=1);
namespace Sonata\UserBundle\Controller;
use Sonata\UserBundle\Model\UserInterface;
// NEXT_MAJOR: remove this file
@trigger_error(
'The '.__NAMESPACE__.'\AdminSecurityController class is deprecated since version 4.3.0 and will be removed in 5.0.'
.' Use '.__NAMESPACE__.'\CheckLoginAction, '.__NAMESPACE__.'\LoginAction or '.__NAMESPACE__.'\LogoutAction instead.',
E_USER_DEPRECATED
);
use Sonata\UserBundle\Action\CheckLoginAction;
use Sonata\UserBundle\Action\LoginAction;
use Sonata\UserBundle\Action\LogoutAction;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
class AdminSecurityController extends Controller
{
/**
* @param Request $request
*
* @return Response|RedirectResponse
*/
public function loginAction(Request $request)
public function loginAction(Request $request): Response
{
if ($this->getUser() instanceof UserInterface) {
$this->addFlash('sonata_user_error', 'sonata_user_already_authenticated');
$url = $this->generateUrl('sonata_admin_dashboard');
/** @var LoginAction $loginAction */
$loginAction = $this->container->get(LoginAction::class);
return $this->redirect($url);
}
$session = $request->getSession();
$authErrorKey = Security::AUTHENTICATION_ERROR;
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
if ($this->isGranted('ROLE_ADMIN')) {
$refererUri = $request->server->get('HTTP_REFERER');
return $this->redirect($refererUri && $refererUri != $request->getUri() ? $refererUri : $this->generateUrl('sonata_admin_dashboard'));
}
$csrfToken = $this->has('security.csrf.token_manager')
? $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue()
: null;
return $this->render('@SonataUser/Admin/Security/login.html.twig', [
'admin_pool' => $this->get('sonata.admin.pool'),
'base_template' => $this->get('sonata.admin.pool')->getTemplate('layout'),
'csrf_token' => $csrfToken,
'error' => $error,
'last_username' => (null === $session) ? '' : $session->get(Security::LAST_USERNAME),
'reset_route' => $this->generateUrl('sonata_user_admin_resetting_request'),
]);
return $loginAction($request);
}
public function checkAction(): void
{
throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
/** @var CheckLoginAction $checkLoginAction */
$checkLoginAction = $this->container->get(CheckLoginAction::class);
$checkLoginAction();
}
public function logoutAction(): void
{
throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
/** @var LogoutAction $logoutAction */
$logoutAction = $this->container->get(LogoutAction::class);
$logoutAction();
}
}
@@ -19,7 +19,6 @@ use FOS\RestBundle\Controller\Annotations\View;
use FOS\RestBundle\Request\ParamFetcherInterface;
use FOS\RestBundle\View\View as FOSRestView;
use FOS\UserBundle\Model\GroupInterface;
use JMS\Serializer\SerializationContext;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sonata\DatagridBundle\Pager\PagerInterface;
use Sonata\UserBundle\Model\GroupManagerInterface;
@@ -63,6 +62,7 @@ class GroupController
*
* @QueryParam(name="page", requirements="\d+", default="1", description="Page for groups list pagination (1-indexed)")
* @QueryParam(name="count", requirements="\d+", default="10", description="Number of groups by page")
* @QueryParam(name="orderBy", map=true, requirements="ASC|DESC", nullable=true, strict=true, description="Query groups order by clause (key is field, value is direction")
* @QueryParam(name="enabled", requirements="0|1", nullable=true, strict=true, description="Enabled/disabled groups only?")
*
* @View(serializerGroups={"sonata_api_read"}, serializerEnableMaxDepthChecks=true)
@@ -73,20 +73,6 @@ class GroupController
*/
public function getGroupsAction(ParamFetcherInterface $paramFetcher)
{
$orderByQueryParam = new QueryParam();
$orderByQueryParam->name = 'orderBy';
$orderByQueryParam->requirements = 'ASC|DESC';
$orderByQueryParam->nullable = true;
$orderByQueryParam->strict = true;
$orderByQueryParam->description = 'Query groups order by clause (key is field, value is direction)';
if (property_exists($orderByQueryParam, 'map')) {
$orderByQueryParam->map = true;
} else {
$orderByQueryParam->array = true;
}
$paramFetcher->addParam($orderByQueryParam);
$supportedFilters = [
'enabled' => '',
];
@@ -104,7 +90,7 @@ class GroupController
if (!$sort) {
$sort = [];
} elseif (!is_array($sort)) {
} elseif (!\is_array($sort)) {
$sort = [$sort, 'asc'];
}
@@ -239,18 +225,12 @@ class GroupController
$group = $form->getData();
$this->groupManager->updateGroup($group);
$view = FOSRestView::create($group);
$context = new Context();
$context->setGroups(['sonata_api_read']);
$context->enableMaxDepth();
if (class_exists('FOS\RestBundle\Context\Context')) {
$context = new Context();
$context->setGroups(['sonata_api_read']);
$view->setContext($context);
} else {
$serializationContext = SerializationContext::create();
$serializationContext->setGroups(['sonata_api_read']);
$serializationContext->enableMaxDepthChecks();
$view->setSerializationContext($serializationContext);
}
$view = FOSRestView::create($group);
$view->setContext($context);
return $view;
}
@@ -19,7 +19,6 @@ use FOS\RestBundle\Controller\Annotations\View;
use FOS\RestBundle\Request\ParamFetcherInterface;
use FOS\RestBundle\View\View as FOSRestView;
use FOS\UserBundle\Model\GroupInterface;
use JMS\Serializer\SerializationContext;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sonata\DatagridBundle\Pager\PagerInterface;
use Sonata\UserBundle\Model\GroupManagerInterface;
@@ -72,6 +71,7 @@ class UserController
*
* @QueryParam(name="page", requirements="\d+", default="1", description="Page for users list pagination (1-indexed)")
* @QueryParam(name="count", requirements="\d+", default="10", description="Number of users by page")
* @QueryParam(name="orderBy", map=true, requirements="ASC|DESC", nullable=true, strict=true, description="Query users order by clause (key is field, value is direction")
* @QueryParam(name="enabled", requirements="0|1", nullable=true, strict=true, description="Enabled/disabled users only?")
*
* @View(serializerGroups={"sonata_api_read"}, serializerEnableMaxDepthChecks=true)
@@ -82,20 +82,6 @@ class UserController
*/
public function getUsersAction(ParamFetcherInterface $paramFetcher)
{
$orderByQueryParam = new QueryParam();
$orderByQueryParam->name = 'orderBy';
$orderByQueryParam->requirements = 'ASC|DESC';
$orderByQueryParam->nullable = true;
$orderByQueryParam->strict = true;
$orderByQueryParam->description = 'Query users order by clause (key is field, value is direction)';
if (property_exists($orderByQueryParam, 'map')) {
$orderByQueryParam->map = true;
} else {
$orderByQueryParam->array = true;
}
$paramFetcher->addParam($orderByQueryParam);
$supporedCriteria = [
'enabled' => '',
];
@@ -113,7 +99,7 @@ class UserController
if (!$sort) {
$sort = [];
} elseif (!is_array($sort)) {
} elseif (!\is_array($sort)) {
$sort = [$sort, 'asc'];
}
@@ -369,18 +355,12 @@ class UserController
$user = $form->getData();
$this->userManager->updateUser($user);
$view = FOSRestView::create($user);
$context = new Context();
$context->setGroups(['sonata_api_read']);
$context->enableMaxDepth();
if (class_exists('FOS\RestBundle\Context\Context')) {
$context = new Context();
$context->setGroups(['sonata_api_read']);
$view->setContext($context);
} else {
$serializationContext = SerializationContext::create();
$serializationContext->setGroups(['sonata_api_read']);
$serializationContext->enableMaxDepthChecks();
$view->setSerializationContext($serializationContext);
}
$view = FOSRestView::create($user);
$view->setContext($context);
return $view;
}
@@ -13,6 +13,7 @@ declare(strict_types=1);
namespace Sonata\UserBundle\DependencyInjection;
use Sonata\AdminBundle\Controller\CRUDController;
use Sonata\UserBundle\Admin\Entity\GroupAdmin;
use Sonata\UserBundle\Admin\Entity\UserAdmin;
use Sonata\UserBundle\Entity\BaseGroup;
@@ -32,8 +33,14 @@ class Configuration implements ConfigurationInterface
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('sonata_user');
$treeBuilder = new TreeBuilder('sonata_user');
// Keep compatibility with symfony/config < 4.2
if (!method_exists($treeBuilder, 'getRootNode')) {
$rootNode = $treeBuilder->root('sonata_user');
} else {
$rootNode = $treeBuilder->getRootNode();
}
$supportedManagerTypes = ['orm', 'mongodb'];
@@ -94,7 +101,7 @@ class Configuration implements ConfigurationInterface
->addDefaultsIfNotSet()
->children()
->scalarNode('class')->cannotBeEmpty()->defaultValue(GroupAdmin::class)->end()
->scalarNode('controller')->cannotBeEmpty()->defaultValue('SonataAdminBundle:CRUD')->end()
->scalarNode('controller')->cannotBeEmpty()->defaultValue(CRUDController::class)->end()
->scalarNode('translation')->cannotBeEmpty()->defaultValue('SonataUserBundle')->end()
->end()
->end()
@@ -102,7 +109,7 @@ class Configuration implements ConfigurationInterface
->addDefaultsIfNotSet()
->children()
->scalarNode('class')->cannotBeEmpty()->defaultValue(UserAdmin::class)->end()
->scalarNode('controller')->cannotBeEmpty()->defaultValue('SonataAdminBundle:CRUD')->end()
->scalarNode('controller')->cannotBeEmpty()->defaultValue(CRUDController::class)->end()
->scalarNode('translation')->cannotBeEmpty()->defaultValue('SonataUserBundle')->end()
->end()
->end()
@@ -115,6 +122,7 @@ class Configuration implements ConfigurationInterface
->scalarNode('default_avatar')->defaultValue('bundles/sonatauser/default_avatar.png')->end()
->end()
->end()
->scalarNode('mailer')->defaultValue('sonata.user.mailer.default')->info('Custom mailer used to send reset password emails')->end()
->end()
;
@@ -37,7 +37,7 @@ class SonataUserExtension extends Extension implements PrependExtensionInterface
{
if ($container->hasExtension('twig')) {
// add custom form widgets
$container->prependExtensionConfig('twig', ['form_themes' => ['SonataUserBundle:Form:form_admin_fields.html.twig']]);
$container->prependExtensionConfig('twig', ['form_themes' => ['@SonataUser/Form/form_admin_fields.html.twig']]);
}
}
@@ -67,10 +67,21 @@ class SonataUserExtension extends Extension implements PrependExtensionInterface
$loader->load('form.xml');
if (class_exists('Google\Authenticator\GoogleAuthenticator')) {
@trigger_error(
'The \'Google\Authenticator\' namespace is deprecated in sonata-project/GoogleAuthenticator since version 2.1 and will be removed in 3.0.',
E_USER_DEPRECATED
);
}
if (class_exists('Google\Authenticator\GoogleAuthenticator') ||
class_exists('Sonata\GoogleAuthenticator\GoogleAuthenticator')) {
$loader->load('google_authenticator.xml');
}
$loader->load('twig.xml');
$loader->load('command.xml');
$loader->load('actions.xml');
$loader->load('mailer.xml');
if ('orm' === $config['manager_type'] && isset(
$bundles['FOSRestBundle'],
@@ -87,7 +98,7 @@ class SonataUserExtension extends Extension implements PrependExtensionInterface
$loader->load('security_acl.xml');
}
$this->checkManagerTypeToModelTypeMapping($config);
$this->checkManagerTypeToModelTypesMapping($config);
$this->registerDoctrineMapping($config);
$this->configureAdminClass($config, $container);
@@ -95,6 +106,7 @@ class SonataUserExtension extends Extension implements PrependExtensionInterface
$this->configureTranslationDomain($config, $container);
$this->configureController($config, $container);
$this->configureMailer($config, $container);
$container->setParameter('sonata.user.default_avatar', $config['profile']['default_avatar']);
@@ -104,8 +116,6 @@ class SonataUserExtension extends Extension implements PrependExtensionInterface
}
/**
* @param array $config
*
* @throws \RuntimeException
*
* @return array
@@ -155,7 +165,8 @@ class SonataUserExtension extends Extension implements PrependExtensionInterface
return;
}
if (!class_exists('Google\Authenticator\GoogleAuthenticator')) {
if (!class_exists('Google\Authenticator\GoogleAuthenticator')
&& !class_exists('Sonata\GoogleAuthenticator\GoogleAuthenticator')) {
throw new \RuntimeException('Please add ``sonata-project/google-authenticator`` package');
}
@@ -253,56 +264,64 @@ class SonataUserExtension extends Extension implements PrependExtensionInterface
* Adds aliases for user & group managers depending on $managerType.
*
* @param ContainerBuilder $container
* @param $managerType
* @param string $managerType
*/
protected function aliasManagers(ContainerBuilder $container, $managerType): void
{
$container->setAlias('sonata.user.user_manager', sprintf('sonata.user.%s.user_manager', $managerType));
$container->setAlias('sonata.user.group_manager', sprintf('sonata.user.%s.group_manager', $managerType));
// NEXT_MAJOR: call setPublic(true) directly, when dropping support for Sf 3.3
$container->getAlias('sonata.user.user_manager')->setPublic(true);
$container->getAlias('sonata.user.group_manager')->setPublic(true);
}
/**
* @param array $config
*/
private function checkManagerTypeToModelTypeMapping(array $config): void
private function checkManagerTypeToModelTypesMapping(array $config): void
{
$managerType = $config['manager_type'];
$actualModelClasses = [
$config['class']['user'],
$config['class']['group'],
];
if ('orm' === $managerType) {
$expectedModelClasses = [
EntityUser::class,
EntityGroup::class,
];
} elseif ('mongodb' === $managerType) {
$expectedModelClasses = [
DocumentUser::class,
DocumentGroup::class,
];
} else {
if (!\in_array($managerType, ['orm', 'mongodb'], true)) {
throw new \InvalidArgumentException(sprintf('Invalid manager type "%s".', $managerType));
}
foreach ($actualModelClasses as $index => $actualModelClass) {
if ('\\' === substr($actualModelClass, 0, 1)) {
$actualModelClass = substr($actualModelClass, 1);
}
$this->prohibitModelTypeMapping(
$config['class']['user'],
'orm' === $managerType ? DocumentUser::class : EntityUser::class,
$managerType
);
$expectedModelClass = $expectedModelClasses[$index];
$this->prohibitModelTypeMapping(
$config['class']['group'],
'orm' === $managerType ? DocumentGroup::class : EntityGroup::class,
$managerType
);
}
if ($actualModelClass !== $expectedModelClass && !is_subclass_of($actualModelClass, $expectedModelClass)) {
throw new \InvalidArgumentException(
sprintf(
'Model class "%s" does not correspond to manager type "%s".',
$actualModelClass,
$managerType
)
);
}
/**
* Prohibit using wrong model type mapping.
*
* @param string $actualModelClass
* @param string $prohibitedModelClass
* @param string $managerType
*/
private function prohibitModelTypeMapping(
string $actualModelClass,
string $prohibitedModelClass,
string $managerType
): void {
if (is_a($actualModelClass, $prohibitedModelClass, true)) {
throw new \InvalidArgumentException(
sprintf(
'Model class "%s" does not correspond to manager type "%s".',
$actualModelClass,
$managerType
)
);
}
}
private function configureMailer(array $config, ContainerBuilder $container): void
{
$container->setAlias('sonata.user.mailer', $config['mailer']);
}
}
@@ -14,7 +14,6 @@ declare(strict_types=1);
namespace Sonata\UserBundle\Entity;
use Sonata\UserBundle\Model\User as AbstractedUser;
use Sonata\UserBundle\Model\UserInterface;
/**
* Represents a Base User Entity.
@@ -37,18 +36,4 @@ class BaseUser extends AbstractedUser
{
$this->updatedAt = new \DateTime();
}
/**
* Returns the gender list.
*
* @return array
*/
public static function getGenderList()
{
return [
'gender_unknown' => UserInterface::GENDER_UNKNOWN,
'gender_female' => UserInterface::GENDER_FEMALE,
'gender_male' => UserInterface::GENDER_MALE,
];
}
}
@@ -42,12 +42,12 @@ class GroupManager extends BaseGroupManager implements GroupManagerInterface
$fields = $this->objectManager->getClassMetadata($this->class)->getFieldNames();
foreach ($sort as $field => $direction) {
if (!in_array($field, $fields)) {
if (!\in_array($field, $fields, true)) {
throw new \RuntimeException(sprintf("Invalid sort field '%s' in '%s' class", $field, $this->class));
}
}
if (0 == count($sort)) {
if (0 === \count($sort)) {
$sort = ['name' => 'ASC'];
}
@@ -124,11 +124,11 @@ class UserManager extends BaseUserManager implements UserManagerInterface, Manag
$fields = $this->objectManager->getClassMetadata($this->getClass())->getFieldNames();
foreach ($sort as $field => $direction) {
if (!in_array($field, $fields)) {
if (!\in_array($field, $fields, true)) {
throw new \RuntimeException(sprintf("Invalid sort field '%s' in '%s' class", $field, $this->getClass()));
}
}
if (0 == count($sort)) {
if (0 === \count($sort)) {
$sort = ['username' => 'ASC'];
}
foreach ($sort as $field => $direction) {
@@ -13,13 +13,14 @@ declare(strict_types=1);
namespace Sonata\UserBundle\EventListener;
use FOS\UserBundle\Model\UserManagerInterface;
use Sonata\UserBundle\GoogleAuthenticator\Helper;
use Sonata\UserBundle\Model\User;
use Sonata\UserBundle\Model\UserManagerInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
@@ -45,14 +46,21 @@ final class TwoFactorLoginSuccessHandler implements AuthenticationSuccessHandler
*/
private $userManager;
/**
* @var UrlGeneratorInterface
*/
private $urlGenerator;
public function __construct(
EngineInterface $engine,
Helper $helper,
UserManagerInterface $userManager
UserManagerInterface $userManager,
UrlGeneratorInterface $urlGenerator = null // NEXT_MAJOR: make it mandatory.
) {
$this->engine = $engine;
$this->googleAuthenticator = $helper;
$this->userManager = $userManager;
$this->urlGenerator = $urlGenerator;
}
/**
@@ -62,8 +70,6 @@ final class TwoFactorLoginSuccessHandler implements AuthenticationSuccessHandler
{
/** @var $user User */
$user = $token->getUser();
$redirectResponse = new RedirectResponse('/admin');
$needToHave2FA = $this->googleAuthenticator->needToHaveGoogle2FACode($request);
if ($needToHave2FA && !$user->getTwoStepVerificationCode()) {
@@ -86,6 +92,12 @@ final class TwoFactorLoginSuccessHandler implements AuthenticationSuccessHandler
$request->getSession()->set($this->googleAuthenticator->getSessionKey($token), null);
}
return $redirectResponse;
// NEXT_MAJOR: remove hardcoded url.
$url = $this->urlGenerator
? $this->urlGenerator->generate('sonata_admin_dashboard')
: '/admin'
;
return new RedirectResponse($url);
}
}
@@ -16,10 +16,12 @@ namespace Sonata\UserBundle\Form\Type;
use Sonata\UserBundle\Form\Transformer\RestoreRolesTransformer;
use Sonata\UserBundle\Security\EditableRolesBuilder;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -54,12 +56,12 @@ class SecurityRolesType extends AbstractType
$transformer = new RestoreRolesTransformer($this->rolesBuilder);
// GET METHOD
$formBuilder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($transformer): void {
$formBuilder->addEventListener(FormEvents::PRE_SET_DATA, static function (FormEvent $event) use ($transformer): void {
$transformer->setOriginalRoles($event->getData());
});
// POST METHOD
$formBuilder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($transformer): void {
$formBuilder->addEventListener(FormEvents::PRE_SUBMIT, static function (FormEvent $event) use ($transformer): void {
$transformer->setOriginalRoles($event->getForm()->getData());
});
@@ -109,7 +111,7 @@ class SecurityRolesType extends AbstractType
return $this->rolesBuilder->getRolesReadOnly($options['choice_translation_domain']);
},
'choice_translation_domain' => function (Options $options, $value) {
'choice_translation_domain' => static function (Options $options, $value) {
// if choice_translation_domain is true, then it's the same as translation_domain
if (true === $value) {
$value = $options['translation_domain'];
@@ -135,7 +137,7 @@ class SecurityRolesType extends AbstractType
]);
// Symfony 2.8 BC
if ($resolver->isDefined('choices_as_values')) {
if (method_exists(FormTypeInterface::class, 'setDefaultOptions')) {
$resolver->setDefault('choices_as_values', true);
}
}
@@ -145,7 +147,7 @@ class SecurityRolesType extends AbstractType
*/
public function getParent()
{
return 'Symfony\Component\Form\Extension\Core\Type\ChoiceType';
return ChoiceType::class;
}
/**
@@ -15,6 +15,18 @@ namespace Sonata\UserBundle\Form\Type;
use Sonata\CoreBundle\Form\Type\BaseStatusType;
@trigger_error(
'The '.__NAMESPACE__.'\UserGenderListType class is deprecated since version 4.1 and will be removed in 5.0.'
.' Use Symfony\Component\Form\Extension\Core\Type\ChoiceType instead.',
E_USER_DEPRECATED
);
/**
* NEXT_MAJOR: remove this class.
*
* @deprecated since version 4.1, to be removed in 5.0.
* Use Symfony\Component\Form\Extension\Core\Type\ChoiceType instead
*/
class UserGenderListType extends BaseStatusType
{
}
@@ -109,7 +109,7 @@ class Helper
public function needToHaveGoogle2FACode(Request $request): bool
{
$ip = $request->server->get('HTTP_X_FORWARDED_FOR', $request->server->get('REMOTE_ADDR'));
if (in_array($ip, $this->ipWhiteList)) {
if (\in_array($ip, $this->ipWhiteList, true)) {
return false;
}
@@ -53,7 +53,7 @@ class RequestListener
*/
public function onCoreRequest(GetResponseEvent $event): void
{
if (HttpKernel::MASTER_REQUEST != $event->getRequestType()) {
if (HttpKernel::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
@@ -81,8 +81,8 @@ class RequestListener
}
$state = 'init';
if ('POST' == $request->getMethod()) {
if (true == $this->helper->checkCode($user, $request->get('_code'))) {
if ('POST' === $request->getMethod()) {
if (true === $this->helper->checkCode($user, $request->get('_code'))) {
$session->set($key, true);
return;
+17 -3
View File
@@ -21,12 +21,12 @@ use FOS\UserBundle\Model\User as AbstractedUser;
abstract class User extends AbstractedUser implements UserInterface
{
/**
* @var \DateTime
* @var \DateTime|null
*/
protected $createdAt;
/**
* @var \DateTime
* @var \DateTime|null
*/
protected $updatedAt;
@@ -36,7 +36,7 @@ abstract class User extends AbstractedUser implements UserInterface
protected $twoStepVerificationCode;
/**
* @var \DateTime
* @var \DateTime|null
*/
protected $dateOfBirth;
@@ -573,4 +573,18 @@ abstract class User extends AbstractedUser implements UserInterface
return $this;
}
/**
* Returns the gender list.
*
* @return array
*/
public static function getGenderList()
{
return [
'gender_unknown' => UserInterface::GENDER_UNKNOWN,
'gender_female' => UserInterface::GENDER_FEMALE,
'gender_male' => UserInterface::GENDER_MALE,
];
}
}
@@ -96,7 +96,7 @@ interface UserInterface extends \FOS\UserBundle\Model\UserInterface
public function setDateOfBirth($dateOfBirth);
/**
* @return \DateTime
* @return \DateTime|null
*/
public function getDateOfBirth();
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.user.editable_role_builder" class="Sonata\UserBundle\Security\EditableRolesBuilder">
<service id="sonata.user.editable_role_builder" class="Sonata\UserBundle\Security\EditableRolesBuilder" public="false">
<argument type="service" id="security.token_storage"/>
<argument type="service" id="security.authorization_checker"/>
<argument type="service" id="sonata.admin.pool"/>
@@ -10,9 +10,33 @@
<argument type="service" id="translator"/>
</call>
</service>
<service id="sonata.user.form.type.security_roles" class="Sonata\UserBundle\Form\Type\SecurityRolesType">
<service id="sonata.user.form.type.security_roles" class="Sonata\UserBundle\Form\Type\SecurityRolesType" public="true">
<tag name="form.type" alias="sonata_security_roles"/>
<argument type="service" id="sonata.user.editable_role_builder"/>
</service>
<service id="sonata.user.matrix_roles_builder" class="Sonata\UserBundle\Security\RolesBuilder\MatrixRolesBuilder" public="false">
<argument type="service" id="security.token_storage"/>
<argument type="service" id="sonata.user.admin_roles_builder"/>
<argument type="service" id="sonata.user.security_roles_builder"/>
</service>
<service id="sonata.user.admin_roles_builder" class="Sonata\UserBundle\Security\RolesBuilder\AdminRolesBuilder" public="false">
<argument type="service" id="security.authorization_checker"/>
<argument type="service" id="sonata.admin.pool"/>
<argument type="service" id="translator"/>
</service>
<service id="sonata.user.security_roles_builder" class="Sonata\UserBundle\Security\RolesBuilder\SecurityRolesBuilder" public="false">
<argument type="service" id="security.authorization_checker"/>
<argument type="service" id="sonata.admin.pool"/>
<argument type="service" id="translator"/>
<argument>%security.role_hierarchy.roles%</argument>
</service>
<service id="sonata.user.form.roles_matrix_type" class="Sonata\UserBundle\Form\Type\RolesMatrixType" public="true">
<tag name="form.type"/>
<argument type="service" id="sonata.user.matrix_roles_builder"/>
</service>
<service id="sonata.user.roles_matrix_extension" class="Sonata\UserBundle\Twig\RolesMatrixExtension" public="false">
<argument type="service" id="sonata.user.matrix_roles_builder"/>
<tag name="twig.extension"/>
</service>
</services>
</container>
@@ -2,6 +2,7 @@
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.user.form.gender_list" class="Sonata\UserBundle\Form\Type\UserGenderListType">
<deprecated>The "%service_id%" service is deprecated since 4.1 and will be removed in 5.0.</deprecated>
<argument>%fos_user.model.user.class%</argument>
<argument>getGenderList</argument>
<argument>Sonata\UserBundle\Form\Type\UserGenderListType</argument>
@@ -19,12 +19,13 @@
<tag name="kernel.event_listener" event="kernel.request" method="onCoreRequest" priority="-1"/>
<argument type="service" id="sonata.user.google.authenticator.provider"/>
<argument type="service" id="security.token_storage"/>
<argument type="service" id="templating"/>
<argument type="service" id="sonata.templating"/>
</service>
<service id="sonata.user.google.authenticator.success_handler" class="Sonata\UserBundle\EventListener\TwoFactorLoginSuccessHandler" public="false">
<argument type="service" id="templating.engine.twig"/>
<argument type="service" id="sonata.templating"/>
<argument type="service" id="sonata.user.google.authenticator.provider"/>
<argument type="service" id="FOS\UserBundle\Model\UserManagerInterface"/>
<argument type="service" id="fos_user.user_manager.default"/>
<argument type="service" id="router"/>
</service>
</services>
</container>
@@ -7,7 +7,7 @@
<argument type="service" id="fos_user.object_manager"/>
<argument>%fos_user.model.user.class%</argument>
</service>
<service id="sonata.user.manager.user" class="Sonata\UserBundle\Entity\UserManagerProxy">
<service id="sonata.user.manager.user" class="Sonata\UserBundle\Entity\UserManagerProxy" public="true">
<argument>%fos_user.model.user.class%</argument>
<argument type="service" id="doctrine"/>
<argument type="service" id="sonata.user.orm.user_manager"/>
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<routes xmlns="http://symfony.com/schema/routing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="sonata_user_admin_resetting_request" path="/request" methods="GET">
<default key="_controller">SonataUserBundle:AdminResetting:request</default>
<default key="_controller">Sonata\UserBundle\Action\RequestAction</default>
</route>
<route id="sonata_user_admin_resetting_send_email" path="/send-email" methods="POST">
<default key="_controller">SonataUserBundle:AdminResetting:sendEmail</default>
<default key="_controller">Sonata\UserBundle\Action\SendEmailAction</default>
</route>
<route id="sonata_user_admin_resetting_check_email" path="/check-email" methods="GET">
<default key="_controller">SonataUserBundle:AdminResetting:checkEmail</default>
<default key="_controller">Sonata\UserBundle\Action\CheckEmailAction</default>
</route>
<route id="sonata_user_admin_resetting_reset" path="/reset/{token}" methods="GET POST">
<default key="_controller">SonataUserBundle:AdminResetting:reset</default>
<default key="_controller">Sonata\UserBundle\Action\ResetAction</default>
</route>
</routes>
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<routes xmlns="http://symfony.com/schema/routing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="sonata_user_admin_security_login" path="/login">
<default key="_controller">SonataUserBundle:AdminSecurity:login</default>
<default key="_controller">Sonata\UserBundle\Action\LoginAction</default>
</route>
<route id="sonata_user_admin_security_check" path="/login_check" methods="POST">
<default key="_controller">SonataUserBundle:AdminSecurity:check</default>
<default key="_controller">Sonata\UserBundle\Action\CheckLoginAction</default>
</route>
<route id="sonata_user_admin_security_logout" path="/logout">
<default key="_controller">SonataUserBundle:AdminSecurity:logout</default>
<default key="_controller">Sonata\UserBundle\Action\LogoutAction</default>
</route>
</routes>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.user.twig.global" class="Sonata\UserBundle\Twig\GlobalVariables">
<service id="sonata.user.twig.global" class="Sonata\UserBundle\Twig\GlobalVariables" public="false">
<argument type="service" id="service_container"/>
</service>
</services>
@@ -246,6 +246,10 @@
<source>Management</source>
<target>Management</target>
</trans-unit>
<trans-unit id="form_status">
<source>Status</source>
<target>Status</target>
</trans-unit>
<trans-unit id="field.label_roles_editable">
<source>field.label_roles_editable</source>
<target>Roles</target>
@@ -250,6 +250,10 @@
<source>Security</source>
<target>Sécurité</target>
</trans-unit>
<trans-unit id="form_status">
<source>Status</source>
<target>Statut</target>
</trans-unit>
<trans-unit id="field.label_roles_editable">
<source>field.label_roles_editable</source>
<target>Rôles</target>
@@ -558,6 +562,10 @@
<source>title_user_resetting_reset</source>
<target>title_user_resetting_reset</target>
</trans-unit>
<trans-unit id="qr_go_next">
<source>qr_go_next</source>
<target>Je l'ai scanné. Continuer.</target>
</trans-unit>
</body>
</file>
</xliff>
@@ -496,7 +496,7 @@
</trans-unit>
<trans-unit id="forgotten_password">
<source>forgotten_password</source>
<target>Elfelejtettet jelszó</target>
<target>Elfelejtett jelszó</target>
</trans-unit>
<trans-unit id="sonata_user_profile_breadcrumb_index">
<source>sonata_user_profile_breadcrumb_index</source>
@@ -246,6 +246,10 @@
<source>Management</source>
<target>Gestione</target>
</trans-unit>
<trans-unit id="form_status">
<source>Status</source>
<target>Stato</target>
</trans-unit>
<trans-unit id="field.label_roles_editable">
<source>field.label_roles_editable</source>
<target>Ruoli</target>
@@ -554,6 +558,14 @@
<source>title_user_resetting_reset</source>
<target>Reset password</target>
</trans-unit>
<trans-unit id="qr_code_secret">
<source>qr_code_secret</source>
<target>Segreto QR-code</target>
</trans-unit>
<trans-unit id="qr_go_next">
<source>qr_go_next</source>
<target>Scansionato. Procedi.</target>
</trans-unit>
<trans-unit id="general">
<source>General</source>
<target>Generale</target>
@@ -582,10 +594,6 @@
<source>Social</source>
<target>Social</target>
</trans-unit>
<trans-unit id="status">
<source>Status</source>
<target>Stato</target>
</trans-unit>
<trans-unit id="user">
<source>User</source>
<target>Utente</target>
@@ -9,7 +9,7 @@ file that was distributed with this source code.
#}
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% extends '@SonataAdmin/CRUD/base_list_field.html.twig' %}
{% block field %}
{% if app.user and object.username != app.user.username and sonata_user.impersonating %}
@@ -27,3 +27,12 @@ file that was distributed with this source code.
{% endif %}
{% endspaceless %}
{% endblock sonata_security_roles_widget %}
{% block sonata_roles_matrix_widget %}
{% spaceless %}
{{ renderMatrix(form)|raw }}
<ul class="list-unstyled">
{{ renderRolesList(form)|raw }}
</ul>
{% endspaceless %}
{% endblock sonata_roles_matrix_widget %}
@@ -41,7 +41,7 @@ class UserAclVoter extends AclVoter
*/
public function vote(TokenInterface $token, $subject, array $attributes)
{
if (!is_object($subject) || !$this->supportsClass(get_class($subject))) {
if (!\is_object($subject) || !$this->supportsClass(\get_class($subject))) {
return self::ACCESS_ABSTAIN;
}
@@ -97,7 +97,7 @@ class EditableRolesBuilder
if ($this->authorizationChecker->isGranted($name) || $isMaster) {
$roles[$name] = $this->translateRole($name, $domain);
if ($expanded) {
$result = array_map([$this, 'translateRole'], $rolesHierarchy, array_fill(0, count($rolesHierarchy), $domain));
$result = array_map([$this, 'translateRole'], $rolesHierarchy, array_fill(0, \count($rolesHierarchy), $domain));
$roles[$name] .= ': '.implode(', ', $result);
}
foreach ($rolesHierarchy as $role) {
@@ -149,13 +149,13 @@ class EditableRolesBuilder
// TODO get the base role from the admin or security handler
$baseRole = $securityHandler->getBaseRole($admin);
if (0 == strlen($baseRole)) { // the security handler related to the admin does not provide a valid string
if (0 === \strlen($baseRole)) { // the security handler related to the admin does not provide a valid string
continue;
}
foreach ($admin->getSecurityInformation() as $role => $permissions) {
$role = sprintf($baseRole, $role);
call_user_func($func, $role, $isMaster, $permissions);
\call_user_func($func, $role, $isMaster, $permissions);
}
}
}
@@ -15,6 +15,7 @@ namespace Sonata\UserBundle;
use Sonata\CoreBundle\Form\FormHelper;
use Sonata\UserBundle\DependencyInjection\Compiler\GlobalVariablesCompilerPass;
use Sonata\UserBundle\DependencyInjection\Compiler\RolesMatrixCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -26,6 +27,7 @@ class SonataUserBundle extends Bundle
public function build(ContainerBuilder $container): void
{
$container->addCompilerPass(new GlobalVariablesCompilerPass());
$container->addCompilerPass(new RolesMatrixCompilerPass());
$this->registerFormMapping();
}