Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Controller\Api;
|
||||
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\ApiBundle\Controller\CommonApiController;
|
||||
use Mautic\ApiBundle\Helper\EntityResultHelper;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Helper\AppVersion;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\UserBundle\Entity\Role;
|
||||
use Mautic\UserBundle\Model\RoleModel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
/**
|
||||
* @extends CommonApiController<Role>
|
||||
*/
|
||||
class RoleApiController extends CommonApiController
|
||||
{
|
||||
/**
|
||||
* @var RoleModel|null
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
public function __construct(CorePermissions $security, Translator $translator, EntityResultHelper $entityResultHelper, RouterInterface $router, FormFactoryInterface $formFactory, AppVersion $appVersion, RequestStack $requestStack, ManagerRegistry $doctrine, ModelFactory $modelFactory, EventDispatcherInterface $dispatcher, CoreParametersHelper $coreParametersHelper)
|
||||
{
|
||||
$roleModel = $modelFactory->getModel('user.role');
|
||||
\assert($roleModel instanceof RoleModel);
|
||||
|
||||
$this->model = $roleModel;
|
||||
$this->entityClass = Role::class;
|
||||
$this->entityNameOne = 'role';
|
||||
$this->entityNameMulti = 'roles';
|
||||
$this->serializerGroups = ['roleDetails', 'publishDetails'];
|
||||
|
||||
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Role &$entity
|
||||
* @param string $action
|
||||
*/
|
||||
protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit')
|
||||
{
|
||||
if (isset($parameters['rawPermissions'])) {
|
||||
$this->model->setRolePermissions($entity, $parameters['rawPermissions']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Controller\Api;
|
||||
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\ApiBundle\Controller\CommonApiController;
|
||||
use Mautic\ApiBundle\Helper\EntityResultHelper;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Helper\AppVersion;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Model\UserModel;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
|
||||
/**
|
||||
* @extends CommonApiController<User>
|
||||
*/
|
||||
class UserApiController extends CommonApiController
|
||||
{
|
||||
/**
|
||||
* @var UserModel|null
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
public function __construct(
|
||||
CorePermissions $security,
|
||||
Translator $translator,
|
||||
EntityResultHelper $entityResultHelper,
|
||||
RouterInterface $router,
|
||||
FormFactoryInterface $formFactory,
|
||||
AppVersion $appVersion,
|
||||
private UserPasswordHasherInterface $hasher,
|
||||
RequestStack $requestStack,
|
||||
ManagerRegistry $doctrine,
|
||||
ModelFactory $modelFactory,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
$userModel = $modelFactory->getModel('user.user');
|
||||
\assert($userModel instanceof UserModel);
|
||||
|
||||
$this->model = $userModel;
|
||||
$this->entityClass = User::class;
|
||||
$this->entityNameOne = 'user';
|
||||
$this->entityNameMulti = 'users';
|
||||
$this->serializerGroups = ['userDetails', 'roleList', 'publishDetails'];
|
||||
$this->dataInputMasks = ['signature' => 'html'];
|
||||
|
||||
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the logged in user's data.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function getSelfAction(TokenStorageInterface $tokenStorage)
|
||||
{
|
||||
$currentUser = $tokenStorage->getToken()->getUser();
|
||||
$view = $this->view($currentUser, Response::HTTP_OK);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user.
|
||||
*/
|
||||
public function newEntityAction(Request $request)
|
||||
{
|
||||
$entity = $this->model->getEntity();
|
||||
|
||||
if (!$this->security->isGranted('user:users:create')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$parameters = $request->request->all();
|
||||
|
||||
if (isset($parameters['plainPassword']['password'])) {
|
||||
$submittedPassword = $parameters['plainPassword']['password'];
|
||||
$entity->setPassword($this->model->checkNewPassword($entity, $this->hasher, $submittedPassword));
|
||||
}
|
||||
|
||||
return $this->processForm($request, $entity, $parameters, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits an existing user or creates a new one on PUT if not found.
|
||||
*
|
||||
* @param int $id User ID
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function editEntityAction(Request $request, $id)
|
||||
{
|
||||
$entity = $this->model->getEntity($id);
|
||||
$parameters = $request->request->all();
|
||||
$method = $request->getMethod();
|
||||
|
||||
if (!$this->security->isGranted('user:users:edit')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
if (null === $entity) {
|
||||
if ('PATCH' === $method
|
||||
|| ('PUT' === $method && !$this->security->isGranted('user:users:create'))
|
||||
) {
|
||||
// PATCH requires that an entity exists or must have create access for PUT
|
||||
return $this->notFound();
|
||||
} else {
|
||||
$entity = $this->model->getEntity();
|
||||
if (isset($parameters['plainPassword']['password'])) {
|
||||
$submittedPassword = $parameters['plainPassword']['password'];
|
||||
$entity->setPassword($this->model->checkNewPassword($entity, $this->hasher, $submittedPassword));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Changing passwords via API is forbidden
|
||||
if (!empty($parameters['plainPassword'])) {
|
||||
unset($parameters['plainPassword']);
|
||||
}
|
||||
if ('PATCH' == $method) {
|
||||
// PATCH will accept a diff so just remove the entities
|
||||
|
||||
// Changing username via API is forbidden
|
||||
if (!empty($parameters['username'])) {
|
||||
unset($parameters['username']);
|
||||
}
|
||||
} else {
|
||||
// PUT requires the entire entity so overwrite the username with the original
|
||||
$parameters['username'] = $entity->getUsername();
|
||||
$parameters['role'] = $entity->getRole()->getId();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->processForm($request, $entity, $parameters, $method);
|
||||
}
|
||||
|
||||
protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit')
|
||||
{
|
||||
switch ($action) {
|
||||
case 'new':
|
||||
$submittedPassword = null;
|
||||
if (isset($parameters['plainPassword'])) {
|
||||
if (is_array($parameters['plainPassword']) && isset($parameters['plainPassword']['password'])) {
|
||||
$submittedPassword = $parameters['plainPassword']['password'];
|
||||
} else {
|
||||
$submittedPassword = $parameters['plainPassword'];
|
||||
}
|
||||
}
|
||||
|
||||
$entity->setPassword($this->model->checkNewPassword($entity, $this->hasher, $submittedPassword, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if a user has permission(s) to a action.
|
||||
*
|
||||
* @param int $id User ID
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function isGrantedAction(Request $request, $id)
|
||||
{
|
||||
$entity = $this->model->getEntity($id);
|
||||
if (!$entity instanceof $this->entityClass) {
|
||||
return $this->notFound();
|
||||
}
|
||||
|
||||
$permissions = $request->request->all()['permissions'] ?? [];
|
||||
|
||||
if (empty($permissions)) {
|
||||
return $this->badRequest('mautic.api.call.permissionempty');
|
||||
} elseif (!is_array($permissions)) {
|
||||
$permissions = [$permissions];
|
||||
}
|
||||
|
||||
$return = $this->security->isGranted($permissions, 'RETURN_ARRAY', $entity);
|
||||
$view = $this->view($return, Response::HTTP_OK);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a list of roles for user edits.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getRolesAction(Request $request)
|
||||
{
|
||||
if (!$this->security->isGranted(
|
||||
['user:users:create', 'user:users:edit'],
|
||||
'MATCH_ONE'
|
||||
)
|
||||
) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$filter = $request->query->get('filter', null);
|
||||
$limit = (int) $request->query->get('limit', null);
|
||||
$roles = $this->model->getLookupResults('role', $filter, $limit);
|
||||
|
||||
$view = $this->view($roles, Response::HTTP_OK);
|
||||
$context = $view->getContext()->setGroups(['roleList']);
|
||||
$view->setContext($context);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Controller\FormController;
|
||||
use Mautic\CoreBundle\Helper\LanguageHelper;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Model\UserModel;
|
||||
use Mautic\UserBundle\Security\SAML\Helper as SAMLHelper;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
|
||||
class ProfileController extends FormController
|
||||
{
|
||||
/**
|
||||
* Generate's account profile.
|
||||
*/
|
||||
public function indexAction(Request $request, LanguageHelper $languageHelper, UserPasswordHasherInterface $hasher,
|
||||
TokenStorageInterface $tokenStorage, SAMLHelper $samlHelper): Response|RedirectResponse
|
||||
{
|
||||
// get current user
|
||||
$me = $tokenStorage->getToken()->getUser();
|
||||
\assert($me instanceof User);
|
||||
/** @var UserModel $model */
|
||||
$model = $this->getModel('user');
|
||||
|
||||
// set some permissions
|
||||
$permissions = [
|
||||
'apiAccess' => ($this->coreParametersHelper->get('api_enabled')) ?
|
||||
$this->security->isGranted('api:access:full')
|
||||
: 0,
|
||||
'editName' => $this->security->isGranted('user:profile:editname'),
|
||||
'editUsername' => $this->security->isGranted('user:profile:editusername'),
|
||||
'editPosition' => $this->security->isGranted('user:profile:editposition'),
|
||||
'editEmail' => $this->security->isGranted('user:profile:editemail'),
|
||||
];
|
||||
|
||||
$action = $this->generateUrl('mautic_user_account');
|
||||
$form = $model->createForm($me, $this->formFactory, $action, ['in_profile' => true]);
|
||||
|
||||
$overrides = [];
|
||||
|
||||
// make sure this user has access to edit privileged fields
|
||||
foreach ($permissions as $permName => $hasAccess) {
|
||||
if ('apiAccess' == $permName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$hasAccess) {
|
||||
// set the value to its original
|
||||
switch ($permName) {
|
||||
case 'editName':
|
||||
$overrides['firstName'] = $me->getFirstName();
|
||||
$overrides['lastName'] = $me->getLastName();
|
||||
$form->remove('firstName');
|
||||
$form->add(
|
||||
'firstName_unbound',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.firstname',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'mapped' => false,
|
||||
'disabled' => true,
|
||||
'data' => $me->getFirstName(),
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$form->remove('lastName');
|
||||
$form->add(
|
||||
'lastName_unbound',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.lastname',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'mapped' => false,
|
||||
'disabled' => true,
|
||||
'data' => $me->getLastName(),
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
break;
|
||||
|
||||
case 'editUsername':
|
||||
$overrides['username'] = $me->getUserIdentifier();
|
||||
$form->remove('username');
|
||||
$form->add(
|
||||
'username_unbound',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.username',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'mapped' => false,
|
||||
'disabled' => true,
|
||||
'data' => $me->getUserIdentifier(),
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'editPosition':
|
||||
$overrides['position'] = $me->getPosition();
|
||||
$form->remove('position');
|
||||
$form->add(
|
||||
'position_unbound',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.position',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'mapped' => false,
|
||||
'disabled' => true,
|
||||
'data' => $me->getPosition(),
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
break;
|
||||
case 'editEmail':
|
||||
$overrides['email'] = $me->getEmail();
|
||||
$form->remove('email');
|
||||
$form->add(
|
||||
'email_unbound',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.type.email',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'mapped' => false,
|
||||
'disabled' => true,
|
||||
'data' => $me->getEmail(),
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a submitted form and process it
|
||||
$submitted = $request->getSession()->get('formProcessed', 0);
|
||||
if ('POST' === $request->getMethod() && !$submitted) {
|
||||
$request->getSession()->set('formProcessed', 1);
|
||||
|
||||
// check to see if the password needs to be rehashed
|
||||
$formUser = $request->request->all()['user'] ?? [];
|
||||
$submittedPassword = $formUser['plainPassword']['password'] ?? null;
|
||||
$overrides['password'] = $model->checkNewPassword($me, $hasher, $submittedPassword);
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
if ($this->isFormValid($form)) {
|
||||
foreach ($overrides as $k => $v) {
|
||||
$func = 'set'.ucfirst($k);
|
||||
$me->$func($v);
|
||||
}
|
||||
|
||||
// form is valid so process the data
|
||||
$model->saveEntity($me);
|
||||
|
||||
// check if the user's locale has been downloaded already, fetch it if not
|
||||
$installedLanguages = $languageHelper->getSupportedLanguages();
|
||||
|
||||
if ($me->getLocale() && !array_key_exists($me->getLocale(), $installedLanguages)) {
|
||||
$fetchLanguage = $languageHelper->extractLanguagePackage($me->getLocale());
|
||||
|
||||
// If there is an error, we need to reset the user's locale to the default
|
||||
if ($fetchLanguage['error']) {
|
||||
$me->setLocale(null);
|
||||
$model->saveEntity($me);
|
||||
$message = 'mautic.core.could.not.set.language';
|
||||
$messageVars = [];
|
||||
|
||||
if (isset($fetchLanguage['message'])) {
|
||||
$message = $fetchLanguage['message'];
|
||||
}
|
||||
|
||||
if (isset($fetchLanguage['vars'])) {
|
||||
$messageVars = $fetchLanguage['vars'];
|
||||
}
|
||||
|
||||
$this->addFlashMessage($message, $messageVars);
|
||||
}
|
||||
}
|
||||
|
||||
// Update timezone and locale
|
||||
$tz = $me->getTimezone();
|
||||
if (empty($tz)) {
|
||||
$tz = $this->coreParametersHelper->getDefaultTimezone();
|
||||
}
|
||||
$request->getSession()->set('_timezone', $tz);
|
||||
|
||||
$locale = $me->getLocale();
|
||||
if (empty($locale)) {
|
||||
$locale = $this->coreParametersHelper->get('locale');
|
||||
}
|
||||
$request->getSession()->set('_locale', $locale);
|
||||
|
||||
$returnUrl = $this->generateUrl('mautic_user_account');
|
||||
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'returnUrl' => $returnUrl,
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\ProfileController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
'flashes' => [ // success
|
||||
[
|
||||
'type' => 'notice',
|
||||
'msg' => 'mautic.user.account.notice.updated',
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return $this->redirectToRoute('mautic_dashboard_index');
|
||||
}
|
||||
}
|
||||
$request->getSession()->set('formProcessed', 0);
|
||||
|
||||
$isSamlUser = $samlHelper->isSamlSession();
|
||||
if ($isSamlUser) {
|
||||
$form->remove('plainPassword');
|
||||
}
|
||||
|
||||
$parameters = [
|
||||
'permissions' => $permissions,
|
||||
'me' => $me,
|
||||
'userForm' => $form->createView(),
|
||||
'isSamlUser' => $isSamlUser,
|
||||
'authorizedClients' => $this->forward('Mautic\ApiBundle\Controller\ClientController::authorizedClientsAction')->getContent(),
|
||||
];
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => $parameters,
|
||||
'contentTemplate' => '@MauticUser/Profile/index.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $this->generateUrl('mautic_user_account'),
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Controller\FormController;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Form\Type\PasswordResetConfirmType;
|
||||
use Mautic\UserBundle\Form\Type\PasswordResetType;
|
||||
use Mautic\UserBundle\Model\UserModel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
class PublicController extends FormController
|
||||
{
|
||||
/**
|
||||
* Generates a new password for the user and emails it to them.
|
||||
*/
|
||||
public function passwordResetAction(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
|
||||
{
|
||||
/** @var UserModel $model */
|
||||
$model = $this->getModel('user');
|
||||
|
||||
$data = ['identifier' => ''];
|
||||
$action = $this->generateUrl('mautic_user_passwordreset');
|
||||
$form = $this->formFactory->create(PasswordResetType::class, $data, ['action' => $action]);
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if ('POST' === $request->getMethod()) {
|
||||
if ($isValid = $this->isFormValid($form)) {
|
||||
// find the user
|
||||
$data = $form->getData();
|
||||
$user = $model->getRepository()->findByIdentifier($data['identifier']);
|
||||
|
||||
/**
|
||||
* Calculation of time to standardize fix response for vulnerability
|
||||
* Users enumeration - forgot password. Constant response time is 1s.
|
||||
*/
|
||||
$desiredTime = 1.0;
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
if (null !== $user) {
|
||||
$model->sendResetEmail($user);
|
||||
}
|
||||
$this->addFlashMessage('mautic.user.user.notice.passwordreset');
|
||||
} catch (\Exception) {
|
||||
$this->addFlashMessage('mautic.user.user.notice.passwordreset.error', [], 'error');
|
||||
}
|
||||
|
||||
$endTime = microtime(true);
|
||||
$executionTime = $endTime - $startTime;
|
||||
|
||||
if ($executionTime < $desiredTime) {
|
||||
usleep((int) (($desiredTime - $executionTime) * 1000000));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('login');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/Security/reset.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $action,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function passwordResetConfirmAction(Request $request, UserPasswordHasherInterface $hasher): mixed
|
||||
{
|
||||
/** @var UserModel $model */
|
||||
$model = $this->getModel('user');
|
||||
|
||||
$data = ['identifier' => '', 'password' => '', 'password_confirm' => ''];
|
||||
$action = $this->generateUrl('mautic_user_passwordresetconfirm');
|
||||
$form = $this->formFactory->create(PasswordResetConfirmType::class, [], ['action' => $action]);
|
||||
$token = $request->query->get('token');
|
||||
|
||||
if ($token) {
|
||||
$request->getSession()->set('resetToken', $token);
|
||||
}
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if ('POST' === $request->getMethod()) {
|
||||
if ($isValid = $this->isFormValid($form)) {
|
||||
// find the user
|
||||
$data = $form->getData();
|
||||
/** @var User $user */
|
||||
$user = $model->getRepository()->findByIdentifier($data['identifier']);
|
||||
|
||||
if (null == $user) {
|
||||
$this->addFlashMessage('mautic.user.user.notice.passwordreset.success');
|
||||
|
||||
return $this->redirectToRoute('login');
|
||||
} else {
|
||||
if ($request->getSession()->has('resetToken')) {
|
||||
$resetToken = $request->getSession()->get('resetToken');
|
||||
|
||||
if ($model->confirmResetToken($user, $resetToken)) {
|
||||
$encodedPassword = $model->checkNewPassword($user, $hasher, $data['plainPassword']);
|
||||
$user->setPassword($encodedPassword);
|
||||
$model->saveEntity($user);
|
||||
|
||||
$this->addFlashMessage('mautic.user.user.notice.passwordreset.success');
|
||||
|
||||
$request->getSession()->remove('resetToken');
|
||||
|
||||
return $this->redirectToRoute('login');
|
||||
}
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/Security/resetconfirm.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $action,
|
||||
],
|
||||
]);
|
||||
} else {
|
||||
$this->addFlashMessage('mautic.user.user.notice.passwordreset.missingtoken');
|
||||
|
||||
return $this->redirectToRoute('mautic_user_passwordresetconfirm');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/Security/resetconfirm.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $action,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\UserBundle\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Controller\FormController;
|
||||
use Mautic\CoreBundle\Factory\PageHelperFactoryInterface;
|
||||
use Mautic\UserBundle\Entity;
|
||||
use Mautic\UserBundle\Entity\PermissionRepository;
|
||||
use Mautic\UserBundle\Entity\UserRepository;
|
||||
use Mautic\UserBundle\Model\RoleModel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException;
|
||||
|
||||
class RoleController extends FormController
|
||||
{
|
||||
/**
|
||||
* Generate's default role list view.
|
||||
*
|
||||
* @param int $page
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function indexAction(Request $request, PageHelperFactoryInterface $pageHelperFactory, $page = 1)
|
||||
{
|
||||
if (!$this->security->isGranted('user:roles:view')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$this->setListFilters();
|
||||
|
||||
$pageHelper = $pageHelperFactory->make('mautic.role', $page);
|
||||
|
||||
$limit = $pageHelper->getLimit();
|
||||
$start = $pageHelper->getStart();
|
||||
$orderBy = $request->getSession()->get('mautic.role.orderby', 'r.name');
|
||||
$orderByDir = $request->getSession()->get('mautic.role.orderbydir', 'ASC');
|
||||
$filter = $request->get('search', $request->getSession()->get('mautic.role.filter', ''));
|
||||
$tmpl = $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index';
|
||||
$model = $this->getModel('user.role');
|
||||
\assert($model instanceof RoleModel);
|
||||
$items = $model->getEntities(
|
||||
[
|
||||
'start' => $start,
|
||||
'limit' => $limit,
|
||||
'filter' => $filter,
|
||||
'orderBy' => $orderBy,
|
||||
'orderByDir' => $orderByDir,
|
||||
]
|
||||
);
|
||||
|
||||
$request->getSession()->set('mautic.role.filter', $filter);
|
||||
|
||||
$count = count($items);
|
||||
if ($count && $count < ($start + 1)) {
|
||||
$lastPage = $pageHelper->countPage($count);
|
||||
$returnUrl = $this->generateUrl('mautic_role_index', ['page' => $lastPage]);
|
||||
$pageHelper->rememberPage($lastPage);
|
||||
|
||||
return $this->postActionRedirect([
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => [
|
||||
'page' => $lastPage,
|
||||
'tmpl' => $tmpl,
|
||||
],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\RoleController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_role_index',
|
||||
'mauticContent' => 'role',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$roleIds = [];
|
||||
|
||||
foreach ($items as $role) {
|
||||
$roleIds[] = $role->getId();
|
||||
}
|
||||
|
||||
$pageHelper->rememberPage($page);
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'items' => $items,
|
||||
'userCounts' => (!empty($roleIds)) ? $model->getRepository()->getUserCount($roleIds) : [],
|
||||
'searchValue' => $filter,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'tmpl' => $tmpl,
|
||||
'permissions' => [
|
||||
'create' => $this->security->isGranted('user:roles:create'),
|
||||
'edit' => $this->security->isGranted('user:roles:edit'),
|
||||
'delete' => $this->security->isGranted('user:roles:delete'),
|
||||
],
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/Role/list.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $this->generateUrl('mautic_role_index', ['page' => $page]),
|
||||
'mauticContent' => 'role',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate's new role form and processes post data.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response
|
||||
*/
|
||||
public function newAction(Request $request)
|
||||
{
|
||||
if (!$this->security->isGranted('user:roles:create')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
// retrieve the entity
|
||||
$entity = new Entity\Role();
|
||||
$model = $this->getModel('user.role');
|
||||
\assert($model instanceof RoleModel);
|
||||
|
||||
// set the return URL for post actions
|
||||
$returnUrl = $this->generateUrl('mautic_role_index');
|
||||
|
||||
// set the page we came from
|
||||
$page = $request->getSession()->get('mautic.role.page', 1);
|
||||
$action = $this->generateUrl('mautic_role_action', ['objectAction' => 'new']);
|
||||
|
||||
// get the user form factory
|
||||
$permissionsConfig = $this->getPermissionsConfig($entity);
|
||||
$form = $model->createForm($entity, $this->formFactory, $action, ['permissionsConfig' => $permissionsConfig['config']]);
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$valid = false;
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
// set the permissions
|
||||
$role = $request->request->all()['role'] ?? [];
|
||||
$permissions = $role['permissions'] ?? null;
|
||||
$model->setRolePermissions($entity, $permissions);
|
||||
|
||||
// form is valid so process the data
|
||||
$model->saveEntity($entity);
|
||||
|
||||
$this->addFlashMessage('mautic.core.notice.created', [
|
||||
'%name%' => $entity->getName(),
|
||||
'%menu_link%' => 'mautic_role_index',
|
||||
'%url%' => $this->generateUrl('mautic_role_action', [
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $entity->getId(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cancelled || ($valid && $this->getFormButton($form, ['buttons', 'save'])->isClicked())) {
|
||||
return $this->postActionRedirect([
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\RoleController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_role_index',
|
||||
'mauticContent' => 'role',
|
||||
],
|
||||
]);
|
||||
} elseif ($valid) {
|
||||
return $this->editAction($request, $entity->getId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
'permissionsConfig' => $permissionsConfig,
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/Role/form.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_role_new',
|
||||
'route' => $this->generateUrl('mautic_role_action', ['objectAction' => 'new']),
|
||||
'mauticContent' => 'role',
|
||||
'permissionList' => $permissionsConfig['list'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate's role edit form and processes post data.
|
||||
*
|
||||
* @param int $objectId
|
||||
* @param bool $ignorePost
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse|Response
|
||||
*/
|
||||
public function editAction(Request $request, $objectId, $ignorePost = false)
|
||||
{
|
||||
if (!$this->security->isGranted('user:roles:edit')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
/** @var RoleModel $model */
|
||||
$model = $this->getModel('user.role');
|
||||
$entity = $model->getEntity($objectId);
|
||||
|
||||
// set the page we came from
|
||||
$page = $request->getSession()->get('mautic.role.page', 1);
|
||||
|
||||
// set the return URL
|
||||
$returnUrl = $this->generateUrl('mautic_role_index', ['page' => $page]);
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\RoleController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_role_index',
|
||||
'mauticContent' => 'role',
|
||||
],
|
||||
];
|
||||
|
||||
// user not found
|
||||
if (null === $entity) {
|
||||
return $this->postActionRedirect(
|
||||
array_merge($postActionVars, [
|
||||
'flashes' => [
|
||||
[
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.role.error.notfound',
|
||||
'msgVars' => ['%id' => $objectId],
|
||||
],
|
||||
],
|
||||
])
|
||||
);
|
||||
} elseif ($model->isLocked($entity)) {
|
||||
// deny access if the entity is locked
|
||||
return $this->isLocked($postActionVars, $entity, 'user.role');
|
||||
}
|
||||
|
||||
$permissionsConfig = $this->getPermissionsConfig($entity);
|
||||
$action = $this->generateUrl('mautic_role_action', ['objectAction' => 'edit', 'objectId' => $objectId]);
|
||||
$form = $model->createForm($entity, $this->formFactory, $action, ['permissionsConfig' => $permissionsConfig['config']]);
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if (!$ignorePost && 'POST' === $request->getMethod()) {
|
||||
$valid = false;
|
||||
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
// set the permissions
|
||||
$role = $request->request->all()['role'] ?? [];
|
||||
$permissions = $role['permissions'] ?? null;
|
||||
$model->setRolePermissions($entity, $permissions);
|
||||
|
||||
// form is valid so process the data
|
||||
$model->saveEntity($entity, $this->getFormButton($form, ['buttons', 'save'])->isClicked());
|
||||
|
||||
$this->addFlashMessage('mautic.core.notice.updated', [
|
||||
'%name%' => $entity->getName(),
|
||||
'%menu_link%' => 'mautic_role_index',
|
||||
'%url%' => $this->generateUrl('mautic_role_action', [
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $entity->getId(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
// unlock the entity
|
||||
$model->unlockEntity($entity);
|
||||
}
|
||||
|
||||
if ($cancelled || ($valid && $this->getFormButton($form, ['buttons', 'save'])->isClicked())) {
|
||||
return $this->postActionRedirect($postActionVars);
|
||||
} else {
|
||||
// the form has to be rebuilt because the permissions were updated
|
||||
$permissionsConfig = $this->getPermissionsConfig($entity);
|
||||
$form = $model->createForm($entity, $this->formFactory, $action, ['permissionsConfig' => $permissionsConfig['config']]);
|
||||
}
|
||||
} else {
|
||||
// lock the entity
|
||||
$model->lockEntity($entity);
|
||||
}
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
'permissionsConfig' => $permissionsConfig,
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/Role/form.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_role_index',
|
||||
'route' => $action,
|
||||
'mauticContent' => 'role',
|
||||
'permissionList' => $permissionsConfig['list'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function getPermissionsConfig(Entity\Role $role): array
|
||||
{
|
||||
$permissionObjects = $this->security->getPermissionObjects();
|
||||
$translator = $this->translator;
|
||||
$permissionRepo = $this->doctrine->getRepository(Entity\Permission::class);
|
||||
\assert($permissionRepo instanceof PermissionRepository);
|
||||
|
||||
$permissionsArray = ($role->getId()) ? $permissionRepo->getPermissionsByRole($role, true) : [];
|
||||
|
||||
$permissions = [];
|
||||
$permissionsList = [];
|
||||
/** @var \Mautic\CoreBundle\Security\Permissions\AbstractPermissions $object */
|
||||
foreach ($permissionObjects as $object) {
|
||||
if (!is_object($object)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($object->isEnabled()) {
|
||||
$bundle = $object->getName();
|
||||
$label = $translator->trans("mautic.{$bundle}.permissions.header");
|
||||
|
||||
// convert the permission bits from the db into readable names
|
||||
$data = $object->convertBitsToPermissionNames($permissionsArray);
|
||||
|
||||
// get the ratio of granted/total
|
||||
[$granted, $total] = $object->getPermissionRatio($data);
|
||||
|
||||
$permissions[$bundle] = [
|
||||
'label' => $label,
|
||||
'permissionObject' => $object,
|
||||
'ratio' => [$granted, $total],
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
$perms = $object->getPermissions();
|
||||
foreach ($perms as $level => $perm) {
|
||||
$levelPerms = array_keys($perm);
|
||||
$object->parseForJavascript($levelPerms);
|
||||
$permissionsList[$bundle][$level] = $levelPerms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// order permissions by label
|
||||
uasort($permissions, fn ($a, $b): int => strnatcmp($a['label'], $b['label']));
|
||||
|
||||
return ['config' => $permissions, 'list' => $permissionsList];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete's a role.
|
||||
*
|
||||
* @param int $objectId
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function deleteAction(Request $request, $objectId)
|
||||
{
|
||||
if (!$this->security->isGranted('user:roles:delete')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$page = $request->getSession()->get('mautic.role.page', 1);
|
||||
$returnUrl = $this->generateUrl('mautic_role_index', ['page' => $page]);
|
||||
$success = 0;
|
||||
$flashes = [];
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\RoleController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_role_index',
|
||||
'success' => $success,
|
||||
'mauticContent' => 'role',
|
||||
],
|
||||
];
|
||||
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
try {
|
||||
$model = $this->getModel('user.role');
|
||||
\assert($model instanceof RoleModel);
|
||||
$entity = $model->getEntity($objectId);
|
||||
|
||||
if (null === $entity) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.role.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
];
|
||||
} elseif ($model->isLocked($entity)) {
|
||||
return $this->isLocked($postActionVars, $entity, 'user.role');
|
||||
} else {
|
||||
$model->deleteEntity($entity);
|
||||
$name = $entity->getName();
|
||||
$flashes[] = [
|
||||
'type' => 'notice',
|
||||
'msg' => 'mautic.core.notice.deleted',
|
||||
'msgVars' => [
|
||||
'%name%' => $name,
|
||||
'%id%' => $objectId,
|
||||
],
|
||||
];
|
||||
}
|
||||
} catch (PreconditionRequiredHttpException $e) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
} // else don't do anything
|
||||
|
||||
return $this->postActionRedirect(
|
||||
array_merge($postActionVars, [
|
||||
'flashes' => $flashes,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a group of entities.
|
||||
*/
|
||||
public function batchDeleteAction(Request $request, RoleModel $model): Response
|
||||
{
|
||||
$page = $request->getSession()->get('mautic.role.page', 1);
|
||||
$returnUrl = $this->generateUrl('mautic_role_index', ['page' => $page]);
|
||||
$flashes = [];
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\RoleController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_role_index',
|
||||
'mauticContent' => 'role',
|
||||
],
|
||||
];
|
||||
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
$ids = json_decode($request->query->get('ids', ''));
|
||||
$deleteIds = [];
|
||||
$userRepo = $this->doctrine->getRepository(Entity\User::class);
|
||||
\assert($userRepo instanceof UserRepository);
|
||||
|
||||
// Loop over the IDs to perform access checks pre-delete
|
||||
foreach ($ids as $objectId) {
|
||||
$entity = $model->getEntity($objectId);
|
||||
$users = $userRepo->findByRole($entity);
|
||||
|
||||
if (null === $entity) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.role.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
];
|
||||
} elseif (count($users)) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.role.error.deletenotallowed',
|
||||
'msgVars' => ['%name%' => $entity->getName()],
|
||||
];
|
||||
} elseif (!$this->security->isGranted('user:roles:delete')) {
|
||||
$flashes[] = $this->accessDenied(true);
|
||||
} elseif ($model->isLocked($entity)) {
|
||||
$flashes[] = $this->isLocked($postActionVars, $entity, 'user.role', true);
|
||||
} else {
|
||||
$deleteIds[] = $objectId;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete everything we are able to
|
||||
if (!empty($deleteIds)) {
|
||||
$entities = $model->deleteEntities($deleteIds);
|
||||
|
||||
$flashes[] = [
|
||||
'type' => 'notice',
|
||||
'msg' => 'mautic.user.role.notice.batch_deleted',
|
||||
'msgVars' => [
|
||||
'%count%' => count($entities),
|
||||
],
|
||||
];
|
||||
}
|
||||
} // else don't do anything
|
||||
|
||||
return $this->postActionRedirect(
|
||||
array_merge($postActionVars, [
|
||||
'flashes' => $flashes,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Controller;
|
||||
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\CoreBundle\Controller\CommonController;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Service\FlashBag;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use Mautic\UserBundle\Exception\WeakPasswordException;
|
||||
use Mautic\UserBundle\Security\SAML\Helper as SAMLHelper;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Security\Core\Exception;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class SecurityController extends CommonController implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
ManagerRegistry $doctrine,
|
||||
ModelFactory $modelFactory,
|
||||
UserHelper $userHelper,
|
||||
CoreParametersHelper $coreParametersHelper,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
Translator $translator,
|
||||
FlashBag $flashBag,
|
||||
?RequestStack $requestStack,
|
||||
?CorePermissions $security,
|
||||
private AuthorizationCheckerInterface $authorizationChecker,
|
||||
) {
|
||||
parent::__construct($doctrine, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security);
|
||||
}
|
||||
|
||||
public function onRequest(RequestEvent $event): void
|
||||
{
|
||||
$controller = $event->getRequest()->attributes->get('_controller');
|
||||
\assert(is_string($controller));
|
||||
|
||||
if (!str_contains($controller, self::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// redirect user if they are already authenticated
|
||||
if ($this->authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY')
|
||||
|| $this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')
|
||||
) {
|
||||
$redirectUrl = $this->generateUrl('mautic_dashboard_index');
|
||||
$event->setResponse(new RedirectResponse($redirectUrl));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates login form and processes login.
|
||||
*/
|
||||
public function loginAction(Request $request, AuthenticationUtils $authenticationUtils, IntegrationHelper $integrationHelper, TranslatorInterface $translator): Response
|
||||
{
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
|
||||
if (null !== $error) {
|
||||
if ($error instanceof WeakPasswordException) {
|
||||
$this->addFlash(FlashBag::LEVEL_ERROR, $translator->trans('mautic.user.auth.error.weakpassword', [], 'flashes'));
|
||||
|
||||
return $this->forward('Mautic\UserBundle\Controller\PublicController::passwordResetAction');
|
||||
} elseif ($error instanceof Exception\BadCredentialsException) {
|
||||
$msg = 'mautic.user.auth.error.invalidlogin';
|
||||
} elseif ($error instanceof Exception\DisabledException) {
|
||||
$msg = 'mautic.user.auth.error.disabledaccount';
|
||||
} else {
|
||||
$msg = $error->getMessage();
|
||||
}
|
||||
|
||||
$this->addFlashMessage($msg, [], FlashBag::LEVEL_ERROR, null, false);
|
||||
}
|
||||
$request->query->set('tmpl', 'login');
|
||||
|
||||
// Get a list of SSO integrations
|
||||
$integrations = $integrationHelper->getIntegrationObjects(null, ['sso_service'], true, null, true);
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'last_username' => $authenticationUtils->getLastUsername(),
|
||||
'integrations' => $integrations,
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/Security/login.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $this->generateUrl('login'),
|
||||
'mauticContent' => 'user',
|
||||
'sessionExpired' => true,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing.
|
||||
*/
|
||||
public function loginCheckAction(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* The plugin should be handling this in it's listener.
|
||||
*/
|
||||
public function ssoLoginAction($integration): RedirectResponse
|
||||
{
|
||||
return new RedirectResponse($this->generateUrl('login'));
|
||||
}
|
||||
|
||||
/**
|
||||
* The plugin should be handling this in it's listener.
|
||||
*/
|
||||
public function ssoLoginCheckAction($integration): RedirectResponse
|
||||
{
|
||||
// The plugin should be handling this in it's listener
|
||||
|
||||
return new RedirectResponse($this->generateUrl('login'));
|
||||
}
|
||||
|
||||
public function samlLoginRetryAction(Request $request, SAMLHelper $samlHelper, SessionInterface $session): Response
|
||||
{
|
||||
if (!$samlHelper->isSamlEnabled()) {
|
||||
return new RedirectResponse($this->generateUrl('login'));
|
||||
}
|
||||
|
||||
$session->invalidate();
|
||||
|
||||
$this->addFlashMessage('mautic.user.security.saml.clearsession', [], FlashBag::LEVEL_ERROR);
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'loginRoute' => $this->generateUrl('lightsaml_sp.discovery'),
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/Security/saml_login_retry.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $this->generateUrl('mautic_base_index'),
|
||||
'mauticContent' => 'user',
|
||||
'sessionExpired' => true,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
KernelEvents::REQUEST => 'onRequest',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,628 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\UserBundle\Controller;
|
||||
|
||||
use JMS\Serializer\SerializerInterface;
|
||||
use Mautic\CoreBundle\Controller\FormController;
|
||||
use Mautic\CoreBundle\Factory\PageHelperFactoryInterface;
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\CoreBundle\Helper\IpLookupHelper;
|
||||
use Mautic\CoreBundle\Helper\LanguageHelper;
|
||||
use Mautic\CoreBundle\Model\AuditLogModel;
|
||||
use Mautic\CoreBundle\Model\FormModel;
|
||||
use Mautic\EmailBundle\Helper\MailHelper;
|
||||
use Mautic\UserBundle\Form\Type\ContactType;
|
||||
use Mautic\UserBundle\Model\RoleModel;
|
||||
use Mautic\UserBundle\Model\UserModel;
|
||||
use Mautic\UserBundle\Security\SAML\Helper as SAMLHelper;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
class UserController extends FormController
|
||||
{
|
||||
/**
|
||||
* Generate's default user list.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse|Response
|
||||
*/
|
||||
public function indexAction(Request $request, PageHelperFactoryInterface $pageHelperFactory, int $page = 1)
|
||||
{
|
||||
if (!$this->security->isGranted('user:users:view')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
$pageHelper = $pageHelperFactory->make('mautic.user', $page);
|
||||
|
||||
$this->setListFilters();
|
||||
|
||||
$currentUserId = $this->user->getId();
|
||||
$limit = $pageHelper->getLimit();
|
||||
$start = $pageHelper->getStart();
|
||||
$orderBy = $request->getSession()->get('mautic.user.orderby', 'u.lastName, u.firstName, u.username');
|
||||
$orderByDir = $request->getSession()->get('mautic.user.orderbydir', 'ASC');
|
||||
$search = $request->get('search', $request->getSession()->get('mautic.user.filter', ''));
|
||||
$search = html_entity_decode($search);
|
||||
$request->getSession()->set('mautic.user.filter', $search);
|
||||
|
||||
// do some default filtering
|
||||
$filter = ['string' => $search, 'force' => ''];
|
||||
$tmpl = $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index';
|
||||
$users = $this->getModel('user.user')->getEntities(
|
||||
[
|
||||
'start' => $start,
|
||||
'limit' => $limit,
|
||||
'filter' => $filter,
|
||||
'orderBy' => $orderBy,
|
||||
'orderByDir' => $orderByDir,
|
||||
]);
|
||||
|
||||
// Check to see if the number of pages match the number of users
|
||||
$count = count($users);
|
||||
if ($count && $count < ($start + 1)) {
|
||||
// the number of entities are now less then the current page so redirect to the last page
|
||||
$lastPage = $pageHelper->countPage($count);
|
||||
$pageHelper->rememberPage($lastPage);
|
||||
$returnUrl = $this->generateUrl('mautic_user_index', ['page' => $lastPage]);
|
||||
|
||||
return $this->postActionRedirect([
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => [
|
||||
'page' => $lastPage,
|
||||
'tmpl' => $tmpl,
|
||||
],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\UserController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_user_index',
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$pageHelper->rememberPage($page);
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'items' => $users,
|
||||
'searchValue' => $search,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'tmpl' => $tmpl,
|
||||
'currentUserId' => $currentUserId,
|
||||
'permissions' => [
|
||||
'create' => $this->security->isGranted('user:users:create'),
|
||||
'edit' => $this->security->isGranted('user:users:editother'),
|
||||
'delete' => $this->security->isGranted('user:users:deleteother'),
|
||||
],
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/User/list.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $this->generateUrl('mautic_user_index', ['page' => $page]),
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate's form and processes new post data.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse|Response
|
||||
*/
|
||||
public function newAction(Request $request, LanguageHelper $languageHelper, UserPasswordHasherInterface $hasher, SAMLHelper $samlHelper)
|
||||
{
|
||||
if (!$this->security->isGranted('user:users:create')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
/** @var UserModel $model */
|
||||
$model = $this->getModel('user.user');
|
||||
|
||||
// retrieve the user entity
|
||||
$user = $model->getEntity();
|
||||
|
||||
// set the return URL for post actions
|
||||
$returnUrl = $this->generateUrl('mautic_user_index');
|
||||
|
||||
// set the page we came from
|
||||
$page = $request->getSession()->get('mautic.user.page', 1);
|
||||
|
||||
// get the user form factory
|
||||
$action = $this->generateUrl('mautic_user_action', ['objectAction' => 'new']);
|
||||
$form = $model->createForm($user, $this->formFactory, $action);
|
||||
|
||||
// Check for a submitted form and process it
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$valid = false;
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
// check to see if the password needs to be rehashed
|
||||
$formUser = $request->request->all()['user'] ?? [];
|
||||
$submittedPassword = $formUser['plainPassword']['password'] ?? null;
|
||||
$password = $model->checkNewPassword($user, $hasher, $submittedPassword);
|
||||
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
// form is valid so process the data
|
||||
$user->setPassword($password);
|
||||
$model->saveEntity($user);
|
||||
|
||||
// check if the user's locale has been downloaded already, fetch it if not
|
||||
$installedLanguages = $languageHelper->getSupportedLanguages();
|
||||
|
||||
if ($user->getLocale() && !array_key_exists($user->getLocale(), $installedLanguages)) {
|
||||
$fetchLanguage = $languageHelper->extractLanguagePackage($user->getLocale());
|
||||
|
||||
// If there is an error, we need to reset the user's locale to the default
|
||||
if ($fetchLanguage['error']) {
|
||||
$user->setLocale(null);
|
||||
$model->saveEntity($user);
|
||||
$message = 'mautic.core.could.not.set.language';
|
||||
$messageVars = [];
|
||||
|
||||
if (isset($fetchLanguage['message'])) {
|
||||
$message = $fetchLanguage['message'];
|
||||
}
|
||||
|
||||
if (isset($fetchLanguage['vars'])) {
|
||||
$messageVars = $fetchLanguage['vars'];
|
||||
}
|
||||
|
||||
$this->addFlashMessage($message, $messageVars);
|
||||
}
|
||||
}
|
||||
|
||||
$this->addFlashMessage('mautic.core.notice.created', [
|
||||
'%name%' => $user->getName(),
|
||||
'%menu_link%' => 'mautic_user_index',
|
||||
'%url%' => $this->generateUrl('mautic_user_action', [
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $user->getId(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cancelled || ($valid && $this->getFormButton($form, ['buttons', 'save'])->isClicked())) {
|
||||
return $this->postActionRedirect([
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page, 'isSamlUser' => false],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\UserController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_user_index',
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
]);
|
||||
} elseif ($valid && !$cancelled) {
|
||||
return $this->editAction($request, $languageHelper, $hasher, $samlHelper, $user->getId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => ['form' => $form->createView(), 'isSamlUser' => false],
|
||||
'contentTemplate' => '@MauticUser/User/form.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_user_new',
|
||||
'route' => $action,
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates edit form and processes post data.
|
||||
*
|
||||
* @param int $objectId
|
||||
* @param bool $ignorePost
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse|Response
|
||||
*/
|
||||
public function editAction(Request $request, LanguageHelper $languageHelper, UserPasswordHasherInterface $hasher, SAMLHelper $samlHelper, $objectId, $ignorePost = false)
|
||||
{
|
||||
if (!$this->security->isGranted('user:users:edit')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
$model = $this->getModel('user.user');
|
||||
\assert($model instanceof UserModel);
|
||||
$user = $model->getEntity($objectId);
|
||||
if (null === $user) {
|
||||
return $this->postActionRedirect([
|
||||
'returnUrl' => $this->generateUrl('mautic_user_index'),
|
||||
'flashes' => [
|
||||
[
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.user.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
$oldEmail = $user->getEmail();
|
||||
|
||||
/** @var AuditLogModel $auditLogModel */
|
||||
$auditLogModel = $this->getModel('core.auditlog');
|
||||
$auditLogRepository = $auditLogModel->getRepository();
|
||||
$userActivity = $auditLogRepository->getLogsForUser($user);
|
||||
$users = $model->getEntities();
|
||||
|
||||
$roleModel = $this->getModel('user.role');
|
||||
\assert($roleModel instanceof RoleModel);
|
||||
$roleRepository = $roleModel->getRepository();
|
||||
$roles = $roleRepository->getEntities();
|
||||
|
||||
// set the page we came from
|
||||
$page = $request->getSession()->get('mautic.user.page', 1);
|
||||
|
||||
// set the return URL
|
||||
$returnUrl = $this->generateUrl('mautic_user_index', ['page' => $page]);
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\UserController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_user_index',
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
];
|
||||
|
||||
if ($model->isLocked($user)) {
|
||||
// deny access if the entity is locked
|
||||
return $this->isLocked($postActionVars, $user, 'user.user');
|
||||
}
|
||||
|
||||
$action = $this->generateUrl('mautic_user_action', ['objectAction' => 'edit', 'objectId' => $objectId]);
|
||||
$form = $model->createForm($user, $this->formFactory, $action);
|
||||
|
||||
$isSamlUser = $samlHelper->isSamlSession();
|
||||
if ($isSamlUser) {
|
||||
$form->remove('plainPassword');
|
||||
}
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if (!$ignorePost && 'POST' === $request->getMethod()) {
|
||||
$valid = false;
|
||||
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
// check to see if the password needs to be rehashed
|
||||
$formUser = $request->request->all()['user'] ?? [];
|
||||
$submittedPassword = $formUser['plainPassword']['password'] ?? null;
|
||||
$password = $model->checkNewPassword($user, $hasher, $submittedPassword);
|
||||
$newEmail = $formUser['email'] ?? null;
|
||||
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
// form is valid so process the data
|
||||
$user->setPassword($password);
|
||||
$model->saveEntity($user, $this->getFormButton($form, ['buttons', 'save'])->isClicked());
|
||||
if (!empty($submittedPassword)) {
|
||||
$model->sendChangePasswordInfo($user);
|
||||
}
|
||||
|
||||
if ($newEmail !== $oldEmail) {
|
||||
$model->sendChangeEmailInfo($oldEmail, $user);
|
||||
}
|
||||
|
||||
// check if the user's locale has been downloaded already, fetch it if not
|
||||
$installedLanguages = $languageHelper->getSupportedLanguages();
|
||||
|
||||
if ($user->getLocale() && !array_key_exists($user->getLocale(), $installedLanguages)) {
|
||||
$fetchLanguage = $languageHelper->extractLanguagePackage($user->getLocale());
|
||||
|
||||
// If there is an error, we need to reset the user's locale to the default
|
||||
if ($fetchLanguage['error']) {
|
||||
$user->setLocale(null);
|
||||
$model->saveEntity($user);
|
||||
$message = 'mautic.core.could.not.set.language';
|
||||
$messageVars = [];
|
||||
|
||||
if (isset($fetchLanguage['message'])) {
|
||||
$message = $fetchLanguage['message'];
|
||||
}
|
||||
|
||||
if (isset($fetchLanguage['vars'])) {
|
||||
$messageVars = $fetchLanguage['vars'];
|
||||
}
|
||||
|
||||
$this->addFlashMessage($message, $messageVars);
|
||||
}
|
||||
}
|
||||
|
||||
$this->addFlashMessage('mautic.core.notice.updated', [
|
||||
'%name%' => $user->getName(),
|
||||
'%menu_link%' => 'mautic_user_index',
|
||||
'%url%' => $this->generateUrl('mautic_user_action', [
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $user->getId(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
// unlock the entity
|
||||
$model->unlockEntity($user);
|
||||
}
|
||||
|
||||
if ($cancelled || ($valid && $this->getFormButton($form, ['buttons', 'save'])->isClicked())) {
|
||||
return $this->postActionRedirect($postActionVars);
|
||||
}
|
||||
} else {
|
||||
// lock the entity
|
||||
$model->lockEntity($user);
|
||||
}
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
'logs' => $userActivity,
|
||||
'users' => $users,
|
||||
'roles' => $roles,
|
||||
'editAction' => true,
|
||||
'isSamlUser' => $isSamlUser,
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/User/form.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_user_index',
|
||||
'route' => $action,
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a user object.
|
||||
*
|
||||
* @param int $objectId
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function deleteAction(Request $request, $objectId)
|
||||
{
|
||||
if (!$this->security->isGranted('user:users:delete')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$currentUser = $this->user;
|
||||
$page = $request->getSession()->get('mautic.user.page', 1);
|
||||
$returnUrl = $this->generateUrl('mautic_user_index', ['page' => $page]);
|
||||
$success = 0;
|
||||
$flashes = [];
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\UserController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_user_index',
|
||||
'route' => $returnUrl,
|
||||
'success' => $success,
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
];
|
||||
if ('POST' === $request->getMethod()) {
|
||||
// ensure the user logged in is not getting deleted
|
||||
if ((int) $currentUser->getId() !== (int) $objectId) {
|
||||
$model = $this->getModel('user.user');
|
||||
\assert($model instanceof UserModel);
|
||||
$entity = $model->getEntity($objectId);
|
||||
|
||||
if (null === $entity) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.user.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
];
|
||||
} elseif ($model->isLocked($entity)) {
|
||||
return $this->isLocked($postActionVars, $entity, 'user.user');
|
||||
} else {
|
||||
$model->deleteEntity($entity);
|
||||
$name = $entity->getName();
|
||||
$flashes[] = [
|
||||
'type' => 'notice',
|
||||
'msg' => 'mautic.core.notice.deleted',
|
||||
'msgVars' => [
|
||||
'%name%' => $name,
|
||||
'%id%' => $objectId,
|
||||
],
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.user.error.cannotdeleteself',
|
||||
];
|
||||
}
|
||||
} // else don't do anything
|
||||
|
||||
return $this->postActionRedirect(
|
||||
array_merge($postActionVars, [
|
||||
'flashes' => $flashes,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts a user.
|
||||
*
|
||||
* @param int $objectId
|
||||
*/
|
||||
public function contactAction(Request $request, SerializerInterface $serializer, MailHelper $mailer, IpLookupHelper $ipLookupHelper, $objectId): Response|\Symfony\Component\HttpFoundation\RedirectResponse
|
||||
{
|
||||
$model = $this->getModel('user.user');
|
||||
$user = $model->getEntity($objectId);
|
||||
|
||||
// user not found
|
||||
if (null === $user) {
|
||||
return $this->postActionRedirect([
|
||||
'returnUrl' => $this->generateUrl('mautic_dashboard_index'),
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\UserController::contactAction',
|
||||
'flashes' => [
|
||||
[
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.user.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$action = $this->generateUrl('mautic_user_action', ['objectAction' => 'contact', 'objectId' => $objectId]);
|
||||
$form = $this->createForm(ContactType::class, [], ['action' => $action]);
|
||||
|
||||
$currentUser = $this->user;
|
||||
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$contact = $request->request->all()['contact'] ?? [];
|
||||
$formUrl = $contact['returnUrl'] ?? '';
|
||||
$returnUrl = $formUrl ? urldecode($formUrl) : $this->generateUrl('mautic_dashboard_index');
|
||||
$valid = false;
|
||||
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
$subject = InputHelper::clean($form->get('msg_subject')->getData());
|
||||
$body = InputHelper::clean($form->get('msg_body')->getData());
|
||||
|
||||
$mailer->setFrom($currentUser->getEmail(), $currentUser->getName());
|
||||
$mailer->setSubject($subject);
|
||||
$mailer->setTo($user->getEmail(), $user->getName());
|
||||
$mailer->setBody($body);
|
||||
$mailer->send();
|
||||
|
||||
$reEntity = $form->get('entity')->getData();
|
||||
if (empty($reEntity)) {
|
||||
$bundle = $object = 'user';
|
||||
$entityId = $user->getId();
|
||||
} else {
|
||||
$bundle = $object = $reEntity;
|
||||
if (strpos($reEntity, ':')) {
|
||||
[$bundle, $object] = explode(':', $reEntity);
|
||||
}
|
||||
$entityId = $form->get('id')->getData();
|
||||
}
|
||||
|
||||
$details = $serializer->serialize([
|
||||
'from' => $currentUser->getName(),
|
||||
'to' => $user->getName(),
|
||||
'subject' => $subject,
|
||||
'message' => $body,
|
||||
], 'json');
|
||||
|
||||
$log = [
|
||||
'bundle' => $bundle,
|
||||
'object' => $object,
|
||||
'objectId' => $entityId,
|
||||
'action' => 'communication',
|
||||
'details' => $details,
|
||||
'ipAddress' => $ipLookupHelper->getIpAddressFromRequest(),
|
||||
];
|
||||
$auditLogModel = $this->getModel('core.auditlog');
|
||||
\assert($auditLogModel instanceof AuditLogModel);
|
||||
$auditLogModel->writeToLog($log);
|
||||
|
||||
$this->addFlashMessage('mautic.user.user.notice.messagesent', ['%name%' => $user->getName()]);
|
||||
}
|
||||
}
|
||||
if ($cancelled || $valid) {
|
||||
return $this->redirect($returnUrl);
|
||||
}
|
||||
} else {
|
||||
$reEntityId = (int) $request->get('id');
|
||||
$reSubject = InputHelper::clean($request->get('subject'));
|
||||
$returnUrl = InputHelper::clean($request->get('returnUrl', $this->generateUrl('mautic_dashboard_index')));
|
||||
$reEntity = InputHelper::clean($request->get('entity'));
|
||||
|
||||
$form->get('entity')->setData($reEntity);
|
||||
$form->get('id')->setData($reEntityId);
|
||||
$form->get('returnUrl')->setData($returnUrl);
|
||||
|
||||
if (!empty($reEntity) && !empty($reEntityId)) {
|
||||
/** @var FormModel<object> $model */
|
||||
$model = $this->getModel($reEntity);
|
||||
$entity = $model->getEntity($reEntityId);
|
||||
|
||||
if (null !== $entity) {
|
||||
$subject = $model->getUserContactSubject($reSubject, $entity);
|
||||
$form->get('msg_subject')->setData($subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delegateView([
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
'user' => $user,
|
||||
],
|
||||
'contentTemplate' => '@MauticUser/User/contact.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $action,
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a group of entities.
|
||||
*/
|
||||
public function batchDeleteAction(Request $request): Response
|
||||
{
|
||||
$page = $request->getSession()->get('mautic.user.page', 1);
|
||||
$returnUrl = $this->generateUrl('mautic_user_index', ['page' => $page]);
|
||||
$flashes = [];
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $page],
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\UserController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_user_index',
|
||||
'mauticContent' => 'user',
|
||||
],
|
||||
];
|
||||
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
$model = $this->getModel('user');
|
||||
\assert($model instanceof UserModel);
|
||||
$ids = json_decode($request->query->get('ids', ''));
|
||||
$deleteIds = [];
|
||||
$currentUser = $this->user;
|
||||
|
||||
// Loop over the IDs to perform access checks pre-delete
|
||||
foreach ($ids as $objectId) {
|
||||
$entity = $model->getEntity($objectId);
|
||||
|
||||
if ((int) $currentUser->getId() === (int) $objectId) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.user.error.cannotdeleteself',
|
||||
];
|
||||
} elseif (null === $entity) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.user.user.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
];
|
||||
} elseif (!$this->security->isGranted('user:users:delete')) {
|
||||
$flashes[] = $this->accessDenied(true);
|
||||
} elseif ($model->isLocked($entity)) {
|
||||
$flashes[] = $this->isLocked($postActionVars, $entity, 'user', true);
|
||||
} else {
|
||||
$deleteIds[] = $objectId;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete everything we are able to
|
||||
if (!empty($deleteIds)) {
|
||||
$entities = $model->deleteEntities($deleteIds);
|
||||
|
||||
$flashes[] = [
|
||||
'type' => 'notice',
|
||||
'msg' => 'mautic.user.user.notice.batch_deleted',
|
||||
'msgVars' => [
|
||||
'%count%' => count($entities),
|
||||
],
|
||||
];
|
||||
}
|
||||
} // else don't do anything
|
||||
|
||||
return $this->postActionRedirect(
|
||||
array_merge($postActionVars, [
|
||||
'flashes' => $flashes,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user