SPRA-2 add admin; add user list; add user; change permissions

This commit is contained in:
2024-07-30 11:54:52 +02:00
parent f54b6db693
commit 3d72531a48
19 changed files with 582 additions and 5 deletions
@@ -0,0 +1,15 @@
<?php
namespace App\Controller\Admin;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BaseAdminController extends AbstractController
{
#[Route('/admin', name: 'app_admin')]
public function indexAction(): Response
{
return $this->render('admin/index.html.twig');
}
}
@@ -0,0 +1,119 @@
<?php
namespace App\Controller\Admin;
use App\Entity\User;
use App\Enum\UserPermission;
use App\Helper\HashGenerator;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class UsersController extends AbstractController
{
/**
* Expire in hours
*/
private const _ACTIVATION_HASH_EXPIRE = 6;
#[Route('/admin/users', name: 'app_admin_users')]
public function indexAction(EntityManagerInterface $entityManager): Response
{
$allUsers = $entityManager->getRepository(User::class)->findAll();
return $this->render('admin/users/list.html.twig', [
'users' => $allUsers,
]);
}
#[Route('/admin/users/edit/{id}', name: 'app_admin_user_edit')]
public function editPermission(EntityManagerInterface $entityManager, User $user, Request $request): Response
{
if ($request->getMethod() === 'POST' &&
$this->isCsrfTokenValid('admin-user-edit-permission', $request->get('token'))) {
foreach (UserPermission::cases() as $case) {
if ($case->value === "ROLE_ADMIN") {
continue;
}
$isPermission = false;
if ($request->get($case->value)) {
$isPermission = true;
}
if ($isPermission) {
$user->addRoles($case->value);
} else {
$user->removeRoles($case->value);
}
}
$entityManager->persist($user);
$entityManager->flush();
}
return $this->render('admin/users/edit.html.twig', [
'user' => $user,
'permissionAll' => UserPermission::casesGroup()
]);
}
#[Route('/admin/users/add', name: 'app_admin_user_add')]
public function addUser(Request $request, EntityManagerInterface $entityManager, UserPasswordHasherInterface $hasher) : Response
{
$user = new User();
$form = $this->createFormBuilder($user)
->add('username',TextType::class, ['label'=>'Nazwa użytkownika'])
->add('submit',SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var User $newUser */
$newUser = $form->getData();
$hash = $hasher->hashPassword($newUser, HashGenerator::generateHash(32));
$newUser->setPassword($hash);
$entityManager->persist($newUser);
$entityManager->flush();
return $this->redirectToRoute('app_admin_user_add_success',['id'=>$newUser->getId()]);
}
return $this->render('admin/users/add.html.twig',[
'form' => $form->createView(),
'plainPassword' => null
]);
}
#[Route('/admin/users/add/{id}', name: 'app_admin_user_add_success')]
public function addUserSuccess(User $user, EntityManagerInterface $entityManager) : Response
{
$newUser = false;
if (!$user->getActivationHash() || $user->getDateActivationHash() < new \DateTime()) {
$user->setActivationHash(HashGenerator::generateHash(64));
$user->setDateActivationHash(new \DateTime("+ ".self::_ACTIVATION_HASH_EXPIRE." hours"));
$entityManager->persist($user);
$entityManager->flush();
$newUser = true;
}
return $this->render('admin/users/userAddSuccess.html.twig', [
'user' => $user,
'activationLink' => $this->generateUrl('app_user_activate', ['id'=>$user->getId(),'hash'=>$user->getActivationHash()], UrlGeneratorInterface::ABSOLUTE_URL),
'newUser' => $newUser
]);
}
#[Route('/admin/users/delete/{id}', name: 'app_admin_user_remove', methods: ['POST'])]
public function deleteUser(User $user, EntityManagerInterface $entityManager, Request $request) : Response
{
if ($this->isCsrfTokenValid('admin-remove-user',$request->get('token'))){
$entityManager->remove($user);
$entityManager->flush();
}
return $this->redirectToRoute('app_admin_users');
}
}
@@ -2,12 +2,18 @@
namespace App\Controller\User;
use App\Entity\User;
use App\Helper\HashGenerator;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\Entity;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use function Symfony\Component\String\u;
class SecurityController extends AbstractController
{
@@ -35,4 +41,27 @@ class SecurityController extends AbstractController
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
#[Route(path: '/user/activate/{id}/{hash}', name: 'app_user_activate')]
public function activate(User $user, string $hash, EntityManagerInterface $entityManager, UserPasswordHasherInterface $hasher) : Response
{
$dateNow = new \DateTime();
if ($user->getActivationHash() !== $hash || $user->getDateActivationHash() < $dateNow) {
return $this->render('security/passwordGenerate.html.twig', [
'user' => null,
]);
}
$newPassword = HashGenerator::generateHash(12);
$user->setPassword($hasher->hashPassword($user, $newPassword));
$user->setActivationHash(null);
$user->setDateActivationHash(null);
$entityManager->persist($user);
$entityManager->flush();
return $this->render('security/passwordGenerate.html.twig', [
'user' => $user,
'newPassword' => $newPassword,
]);
// return new Response("Twój login: `{$user->getUsername()}`. Twoje hasło: `{$newPassword}`");
}
}
+73
View File
@@ -2,7 +2,9 @@
namespace App\Entity;
use App\Enum\UserPermission;
use App\Repository\UserRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
@@ -31,6 +33,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\Column]
private ?string $password = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $activationHash = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $dateActivationHash = null;
public function getId(): ?int
{
return $this->id;
@@ -82,6 +90,33 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return $this;
}
public function addRoles(string $role): void
{
if ($role === 'ROLE_USER') {
return;
}
$roles = $this->getRoles();
$roles[] = $role;
$this->roles = $roles;
}
public function removeRoles(string $roleRemove): void
{
if ($roleRemove === 'ROLE_USER') {
return;
}
$roles = $this->getRoles();
foreach ($roles as $key => $role) {
if ($role === $roleRemove) {
unset($roles[$key]);
break;
}
}
$roles = array_values($roles);
$this->setRoles($roles);
}
/**
* @see PasswordAuthenticatedUserInterface
*/
@@ -105,4 +140,42 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getEnumRoles(): array
{
$tmp=[];
foreach ($this->getRoles() as $role) {
$tmp[]=UserPermission::from($role);
}
return $tmp;
}
public function isGranted(UserPermission $permission): bool
{
return in_array($permission, $this->getEnumRoles(), true);
}
public function getActivationHash(): ?string
{
return $this->activationHash;
}
public function setActivationHash(?string $activationHash): static
{
$this->activationHash = $activationHash;
return $this;
}
public function getDateActivationHash(): ?\DateTimeInterface
{
return $this->dateActivationHash;
}
public function setDateActivationHash(?\DateTimeInterface $dateActivationHash): static
{
$this->dateActivationHash = $dateActivationHash;
return $this;
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace App\Enum;
enum UserPermission : string
{
case ADMIN = 'ROLE_ADMIN';
case USER = 'ROLE_USER';
case ALL = 'ROLE_ACCESS_ALL';
case CLIENT_ADD = 'ROLE_CLIENT_ADD';
case CLIENT_VIEW = 'ROLE_CLIENT_VIEW';
case CLIENT_DELETE = 'ROLE_CLIENT_DELETE';
case REPORT_VIEW = 'ROLE_REPORT_VIEW';
case REPORT_EDIT = 'ROLE_REPORT_EDIT';
public function takeLang(): string
{
return match ($this) {
self::ADMIN => 'Admin',
self::USER => 'Zalogowany',
self::ALL => 'Pełny dostęp',
self::CLIENT_ADD => 'Dodawanie klienta',
self::CLIENT_VIEW => 'Wyświetlanie klienta',
self::CLIENT_DELETE => 'Usuwanie klientów',
self::REPORT_VIEW => 'Wyświetlanie sprawozdań',
self::REPORT_EDIT => 'Edycja sprawozdań',
};
}
public static function casesGroup() : array
{
return [
'case' => UserPermission::ADMIN,
'children' => [
[
'case' => UserPermission::ALL,
'children' => [
UserPermission::CLIENT_ADD,
UserPermission::CLIENT_VIEW,
UserPermission::CLIENT_DELETE,
UserPermission::REPORT_VIEW,
UserPermission::REPORT_EDIT,
]
]
]
];
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace App\Helper;
class HashGenerator
{
public static function generateHash(int $length): string
{
$bytes = openssl_random_pseudo_bytes($length);
return substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $length);
}
}
+1 -2
View File
@@ -72,5 +72,4 @@ class ClientsVoter extends Voter
{
return $this->security->isGranted('ROLE_ACCESS_ALL');
}
}
//HqmN6Egol5o_K5nY
}