<?php
namespace App\Controller\Dashboard\Settings;
use App\Component\ViewLayer\AbstractView;
use App\Component\ViewLayer\Views\AbstractHtmlView;
use App\Component\ViewLayer\Views\AjaxHtmlView;
use App\Component\ViewLayer\Views\JsonView;
use App\Controller\PaginationTrait;
use App\Form\Forms\DummyForm;
use App\Form\Forms\Searching\AccountSearchForm;
use App\Form\Forms\Security\AccountType;
use App\Form\Forms\Security\Profile\ContainerRoleType;
use App\Form\Forms\Security\Profile\GroupType;
use App\Form\Forms\Security\Profile\ListRoleType;
use App\Form\Forms\Security\Profile\SchoolRoleType;
use App\Model\Searching\AccountRoleAssociationSearch;
use App\Model\Searching\AccountSearch;
use App\Model\Searching\GroupAccountSearch;
use App\Model\Searching\RoleAssociationSearch;
use Cms\BulletinBundle\Model\Bulletins\AccountCreationBulletin;
use Cms\BulletinBundle\Model\Bulletins\PasswordResetBulletin;
use Cms\BulletinBundle\Service\BulletinService;
use Cms\TenantBundle\Model\ProductsBitwise;
use Platform\SecurityBundle\Entity\Access\GroupAccount;
use Platform\SecurityBundle\Entity\Access\RoleAssociation;
use Platform\SecurityBundle\Entity\Access\RoleAssociation\AccountRoleAssociation;
use Platform\SecurityBundle\Entity\Identity\Account;
use Platform\SecurityBundle\Service\PasswordService;
use Products\NotificationsBundle\Controller\AbstractDashboardController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route(
* "/accounts",
* )
*/
final class AccountController extends AbstractDashboardController
{
use PaginationTrait;
public const ROUTES__MAIN = 'app.app.dashboard.settings.accounts.main';
public const ROUTES__CREATE = 'app.app.dashboard.settings.accounts.create';
public const ROUTES__UPDATE = 'app.app.dashboard.settings.accounts.update';
public const ROUTES__ROLES_MAIN = 'app.app.dashboard.settings.accounts.roles.main';
public const ROUTES__ROLES_DELETE = 'app.app.dashboard.settings.accounts.roles.delete';
public const ROUTES__ROLES_PERMISSIONS_INHERITANCE_TOGGLE = 'app.app.dashboard.settings.accounts.roles.inheritance_toggle';
public const ROUTES__GROUPS_MAIN = 'app.app.dashboard.settings.accounts.groups.main';
public const ROUTES__GROUPS_DELETE = 'app.app.dashboard.settings.accounts.groups.delete';
public const ROUTES__REACTIVATE = 'app.app.dashboard.settings.accounts.reactivate';
public const ROUTES__DEACTIVATE = 'app.app.dashboard.settings.accounts.deactivate';
public const ROUTES__MANAGE_PASSWORD = 'app.app.dashboard.settings.accounts.manage_password';
public const ROUTES__ROLES_PERMISSIONS_SUPER_USER_TOGGLE = 'app.app.dashboard.settings.accounts.roles.suer_user_toggle';
public const ROUTES__ROLES_PERMISSIONS_IMPERSONATION_TOGGLE = 'app.app.dashboard.settings.accounts.roles.impersonation_toggle';
private const MANAGE_PASSWORD_TYPE__RESET = 'reset';
private const MANAGE_PASSWORD_TYPE__CLEAR = 'clear';
private const MAX_ASSOCIATION = 10000;
/**
* @param int $pagination
* @return AbstractHtmlView|Response
*
* @Route(
* "/list/{pagination}",
* name = self::ROUTES__MAIN,
* requirements = {
* "pagination" = "[1-9]\d*",
* },
* defaults = {
* "pagination" = 0,
* },
* )
*/
public function mainAction(int $pagination = 0)
{
// AUDIT
$this->denyAccessUnlessGranted('app.security.admin');
$result = $this->doSearch(
Account::class,
'accounts',
AccountSearch::class,
AccountSearchForm::class,
$pagination,
);
return ($result instanceof Response) ? $result : $this->html($result);
}
/**
* @param Request $request
* @return AbstractView|RedirectResponse
*
* @Route(
* "/create",
* name = self::ROUTES__CREATE,
* )
* @throws \Exception
*/
public function createAction(Request $request)
{
// AUDIT
$this->denyAccessUnlessGranted('app.security.admin');
$account = new Account();
$form = $this->createForm(
AccountType::class,
$account,
[
'password' => false,
'notify' => ($this->getGlobalContext()->getTenant()->getProducts(
)->getMask() !== ProductsBitwise::SMM__BASE),
]
);
if ($this->handleForm($form, $request)) {
if ( ! empty($password = $form->get('password')->getData())) {
$account->setPasswordRaw($password);
}
$this->getEntityManager()->save($account);
if ($form->get('notify')->getData() === true) {
$this->sendCreateEmail($account, $password);
}
$this->getLoggingService()->createLog($account);
return $this->jsonView([
'redirect' => true,
]);
}
return $this->ajax([
'form' => $form->createView(),
'account' => $account,
])->unwrap();
}
/**
* @param Account $account
* @return AbstractHtmlView|JsonView
*
* @Route(
* "/{account}/update",
* name = self::ROUTES__UPDATE,
* requirements = {
* "account" = "[1-9]\d*",
* },
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
*/
public function updateAction(Account $account)
{
// AUDIT
$this->denyAccessUnlessGranted('app.security.admin');
$isSuperAdmin = $this->isGranted('campussuite.super_admin');
// only superadmins can edit special permissions nad onoster account has only special permissions section
if( ! $isSuperAdmin && $account->isOneRoster()) {
throw new \LogicException();
}
$form = $this->createForm(AccountType::class, $account, [
'password' => false,
]);
if ($this->handleForm($form)) {
$this->getEntityManager()->save($account);
if ( $form->has('password') && ! empty($password = $form->get('password')->getData())) {
$this->getEntityManager()->save($account->setPasswordRaw($password));
if ($form->get('notify')->getData() === true) {
$this->sendResetPasswordEmail($account, $password);
}
}
$this->getLoggingService()->createLog($account);
return $this->jsonView([
'redirect' => true,
]);
}
return $this->ajax([
'form' => $form->createView(),
'account' => $account,
])->unwrap();
}
/**
* @param Account $account
* @return AbstractHtmlView
*
* @Route(
* "/{account}/roles",
* name = self::ROUTES__ROLES_MAIN,
* requirements = {
* "account" = "[1-9]\d*",
* }
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
*/
public function rolesAction(Account $account): AbstractHtmlView
{
// AUDIT
$this->denyAccessUnlessGranted('app.security.admin');
$accountRoleAssociationRepository = $this->getEntityManager()->getRepository(AccountRoleAssociation::class);
$accountRoleAssociationSearch = (new AccountRoleAssociationSearch())->setAccount($account);
$containerRoleForm = $this->createForm(ContainerRoleType::class);
$schoolRoleForm = $this->createForm(SchoolRoleType::class);
$listRoleForm = $this->createForm(ListRoleType::class);
$isContainerRoleFormSubmitted = $this->handleForm($containerRoleForm);
$isSchoolRoleFormSubmitted = $this->handleForm($schoolRoleForm);
$isListRoleFormSubmitted = $this->handleForm($listRoleForm);
if ($isContainerRoleFormSubmitted || $isSchoolRoleFormSubmitted || $isListRoleFormSubmitted) {
$association = (new AccountRoleAssociation())
->setAccount($account)
->setInheritance(RoleAssociation::INHERITANCES__ME_ONLY);
switch (true) {
case $isContainerRoleFormSubmitted:
$data = $containerRoleForm->getData();
$association->setContainer($data['container']);
$containerRoleForm = $this->createForm(ContainerRoleType::class);
break;
case $isSchoolRoleFormSubmitted:
$data = $schoolRoleForm->getData();
$association->setSchool($data['school']);
$schoolRoleForm = $this->createForm(SchoolRoleType::class);
break;
case $isListRoleFormSubmitted:
$data = $listRoleForm->getData();
$association->setList($data['list']);
$listRoleForm = $this->createForm(ListRoleType::class);
break;
default:
throw new \LogicException();
}
$association
->setRole($data['role']);
$this->getEntityManager()->save($association);
$this->getLoggingService()->createLog($account);
}
return $this->ajax([
'account' => $account,
'websiteAssociations' => $accountRoleAssociationRepository->findBySearch(
$accountRoleAssociationSearch->setTypes([RoleAssociationSearch::TYPES__CONTAINER]),
self::MAX_ASSOCIATION
),
'messageAssociations' => $accountRoleAssociationRepository->findBySearch(
$accountRoleAssociationSearch->setTypes(
[RoleAssociationSearch::TYPES__SCHOOL, RoleAssociationSearch::TYPES__LIST]
),
self::MAX_ASSOCIATION
),
'containerRoleForm' => $containerRoleForm->createView(),
'schoolRoleForm' => $schoolRoleForm->createView(),
'listRoleForm' => $listRoleForm->createView(),
])->unwrap();
}
/**
* @param AccountRoleAssociation $accountRoleAssociation
* @return JsonView
*
* @Route(
* "/_inheritance_toggle/{accountRoleAssociation}",
* name = self::ROUTES__ROLES_PERMISSIONS_INHERITANCE_TOGGLE,
* methods = {"POST"},
* requirements = {
* "accountRoleAssociation" = "[1-9]\d*",
* },
* )
* @ParamConverter(
* "accountRoleAssociation",
* class = AccountRoleAssociation::class,
* )
*/
public function toggleInheritanceAction(
AccountRoleAssociation $accountRoleAssociation
): JsonView {
// AUDIT
$this->denyAccessUnlessGranted('app.security.admin');
$inheritance = $accountRoleAssociation->getInheritance();
$toggledInheritance = $inheritance === RoleAssociation::INHERITANCES__ME_ONLY ? RoleAssociation::INHERITANCES__ME_AND_ALL_DESCENDENTS : RoleAssociation::INHERITANCES__ME_ONLY;
$accountRoleAssociation->setInheritance($toggledInheritance);
$this->getLoggingService()->createLog($accountRoleAssociation->getAccount());
return $this->jsonView([
'value' => $inheritance === RoleAssociation::INHERITANCES__ME_ONLY,
]);
}
/**
* @param Account $account
* @param AccountRoleAssociation $association
* @return AbstractView|AjaxHtmlView|JsonView|RedirectResponse
*
* @Route(
* "/{account}/roles/{association}/delete",
* name = self::ROUTES__ROLES_DELETE,
* requirements = {
* "account" = "[1-9]\d*",
* "association" = "[1-9]\d*",
* },
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
* @ParamConverter(
* "association",
* class = AccountRoleAssociation::class,
* )
*/
public function roleDeleteAction(Account $account, AccountRoleAssociation $association)
{
$this->denyAccessUnlessGranted(
'app.security.admin',
[$account, null]
);
$this->denyAccessUnlessGranted(
'app.security.admin',
[$association, null]
);
$id = $association->getId();
$this->getEntityManager()->delete($association);
$this->getLoggingService()->createLog($association, $id);
return $this->redirectToRoute(self::ROUTES__ROLES_MAIN, ['account' => $account->getId()]);
}
/**
* @param Account $account
* @return AbstractHtmlView
*
* @Route(
* "/{account}/groups",
* name = self::ROUTES__GROUPS_MAIN,
* requirements = {
* "account" = "[1-9]\d*",
* }
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
*/
public function groupsAction(Account $account): AbstractHtmlView
{
// AUDIT
$this->denyAccessUnlessGranted('app.security.admin');
$groupAccountAssociationRepository = $this->getEntityManager()->getRepository(GroupAccount::class);
$groupRoleAssociationSearch = (new GroupAccountSearch())->setAccount($account);
$form = $this->createForm(GroupType::class, null, ['account' => $account]);
if ($this->handleForm($form)) {
$association = (new GroupAccount())
->setGroup($form->getData()['group'])
->setAccount($account);
$this->getEntityManager()->save($association);
$this->getLoggingService()->createLog($account);
// reset form
$form = $this->createForm(GroupType::class, null, ['account' => $account]);
}
return $this->ajax([
'account' => $account,
'associations' => $groupAccountAssociationRepository->findBySearch(
$groupRoleAssociationSearch,
self::MAX_ASSOCIATION
),
'form' => $form->createView(),
])->unwrap();
}
/**
* @param Account $account
* @param GroupAccount $association
* @return AbstractView|AjaxHtmlView|JsonView|RedirectResponse
*
* @Route(
* "/{account}/groups/{association}/delete",
* name = self::ROUTES__GROUPS_DELETE,
* requirements = {
* "account" = "[1-9]\d*",
* "association" = "[1-9]\d*",
* },
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
* @ParamConverter(
* "association",
* class = GroupAccount::class,
* )
*/
public function groupDeleteAction(Account $account, GroupAccount $association)
{
$this->denyAccessUnlessGranted(
'app.security.admin',
[$account, null]
);
$this->denyAccessUnlessGranted(
'app.security.admin',
[$association, null]
);
$id = $association->getId();
$this->getEntityManager()->delete($association);
$this->getLoggingService()->createLog($association, $id);
return $this->redirectToRoute(self::ROUTES__GROUPS_MAIN, ['account' => $account->getId()]);
}
/**
* @param Account $account
* @return AbstractView|AjaxHtmlView|JsonView
*
* @Route(
* "/{account}/reactivate",
* name = self::ROUTES__REACTIVATE,
* requirements = {
* "account" = "[1-9]\d*",
* },
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
*/
public function reactivateAction(Account $account): AbstractView
{
// AUDIT
$this->denyAccessUnlessGranted(
'app.security.admin',
[$account, null]
);
if ($account->isActive()) {
return $this->jsonView([
'redirect' => true,
]);
}
$form = $this->createForm(DummyForm::class);
if ($this->handleForm($form)) {
$id = $account->getId();
$account->setActive(true);
$this->getEntityManager()->save($account);
$this->getLoggingService()->createLog($account, $id);
return $this->jsonView([
'redirect' => true,
]);
}
return $this->ajax([
'account' => $account,
'form' => $form->createView(),
]);
}
/**
* @param Account $account
* @return AbstractView|AjaxHtmlView|JsonView
*
* @Route(
* "/{account}/deactivate",
* name = self::ROUTES__DEACTIVATE,
* requirements = {
* "account" = "[1-9]\d*",
* },
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
*/
public function deactivateAction(Account $account): AbstractView
{
// AUDIT
$this->denyAccessUnlessGranted(
'app.security.admin',
[$account, null]
);
if ( ! $account->isActive()) {
return $this->jsonView([
'redirect' => true,
]);
}
$form = $this->createForm(DummyForm::class);
if ($this->handleForm($form)) {
$id = $account->getId();
$account->setActive(false);
$this->getEntityManager()->save($account);
$this->getLoggingService()->createLog($account, $id);
return $this->jsonView([
'redirect' => true,
]);
}
return $this->ajax([
'account' => $account,
'form' => $form->createView(),
]);
}
/**
* @param Request $request
* @param Account $account
* @param string|null $type
* @return AbstractView|AjaxHtmlView|JsonView
*
* @Route(
* "/{account}/manage-password/{type}",
* name = self::ROUTES__MANAGE_PASSWORD,
* requirements = {
* "account" = "[1-9]\d*",
* "type" = "reset|clear"
* },
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
*/
public function managePasswordAction(Request $request, Account $account, string $type = null)
{
$this->denyAccessUnlessGranted(
'app.security.admin',
[$account, null]
);
if ( ! $account->canManagePassword()) {
return $this->jsonView([
'redirect' => true,
]);
}
$form = $this->createForm(DummyForm::class);
if ($this->handleForm($form) && $type != null) {
$id = $account->getId();
if ($type === self::MANAGE_PASSWORD_TYPE__RESET) {
$this->getPasswordService()->sendResetPasswordInitEmail(
$request,
$account
);
} elseif ($type === self::MANAGE_PASSWORD_TYPE__CLEAR) {
$this->getEntityManager()->save(
$account->setPassword(null)
);
}
$this->getEntityManager()->save($account);
$this->getLoggingService()->createLog($account, $id);
return $this->jsonView([
'redirect' => true,
]);
}
return $this->ajax([
'account' => $account,
'form' => $form->createView(),
]);
}
/**
* @param Account $account
* @return JsonView
*
* @Route(
* "/_super_user_toggle/{account}",
* name = self::ROUTES__ROLES_PERMISSIONS_SUPER_USER_TOGGLE,
* methods = {"POST"},
* requirements = {
* "account" = "[1-9]\d*",
* },
* )
* @ParamConverter(
* "account",
* class = Account::class,
* )
*/
public function toggleSuperUserAction(Account $account): JsonView {
// AUDIT
$this->denyAccessUnlessGranted('campussuite.super_admin');
$account->getSpecialPermissions()->setSuperUser( ! $account->getSpecialPermissions()->isSuperUser());
$this->getEntityManager()->save($account);
$this->getLoggingService()->createLog($account);
return $this->jsonView([
'value' => $account->getSpecialPermissions()->isSuperUser(),
]);
}
/**
* @param Account $account
* @param string $password
* @return void
*/
private function sendResetPasswordEmail(Account $account, string $password): void
{
$this->getBulletinService()->send(
(new PasswordResetBulletin())
->setTo($account)
->setAccount($account)
->setPassword($password)
->setDashboard($this->getGlobalContext()->getDashboardUrl())
);
}
/**
* @param Account $account
* @param string|null $password
* @return void
*/
private function sendCreateEmail(Account $account, ?string $password = null): void
{
$this->getBulletinService()->send(
(new AccountCreationBulletin())
->setTo($account)
->setAccount($account)
->setPassword($password)
->setDashboard($this->getGlobalContext()->getDashboardUrl())
);
}
/**
* @return PasswordService
*/
private function getPasswordService(): PasswordService
{
return $this->get(__METHOD__);
}
/**
* @return BulletinService
*/
private function getBulletinService(): BulletinService
{
return $this->get(__METHOD__);
}
}