some progress in login forms

This commit is contained in:
Hesabix 2023-11-04 04:39:35 -04:00
parent 3a43694864
commit e7569b0fe9
14 changed files with 187 additions and 34 deletions

View file

@ -14,6 +14,7 @@ security:
pattern: ^/(_(profiler|wdt)|css|images|js)/ pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false security: false
main: main:
stateless: true
lazy: true lazy: true
provider: app_user_provider provider: app_user_provider
json_login: json_login:
@ -31,7 +32,6 @@ security:
# switch_user: true # switch_user: true
logout: logout:
path: front_user_logout path: front_user_logout
# where to redirect after logout # where to redirect after logout
target: general_home target: general_home
form_login: form_login:
@ -39,6 +39,11 @@ security:
login_path: front_user_login login_path: front_user_login
check_path: front_user_login check_path: front_user_login
enable_csrf: true enable_csrf: true
custom_authenticator: App\Security\BackAuthAuthenticator
remember_me:
secret: '%kernel.secret%' # required
lifetime: 116121600 # 4 years in seconds
always_remember_me: true
# Easy way to control access for large sections of your site # Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used # Note: Only the *first* access control that matches will be used

View file

@ -5,7 +5,9 @@ use App\Controller\CustomUserMessageAuthenticationException;
use App\Entity\Business; use App\Entity\Business;
use App\Entity\Permission; use App\Entity\Permission;
use App\Form\UserRegisterType; use App\Form\UserRegisterType;
use App\Security\BackAuthAuthenticator;
use App\Service\Provider; use App\Service\Provider;
use App\Service\twigFunctions;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use App\Entity\UserToken; use App\Entity\UserToken;
use Exception; use Exception;
@ -23,6 +25,7 @@ use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Address;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface; use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface;
use Symfony\Component\EventDispatcher\EventDispatcher, use Symfony\Component\EventDispatcher\EventDispatcher,
@ -75,10 +78,10 @@ class UserController extends AbstractController
} }
#[Route('/register', name: 'front_user_register')] #[Route('/register', name: 'front_user_register')]
public function front_user_register(Request $request,TranslatorInterface $translator, UserPasswordHasherInterface $userPasswordHasher, EntityManagerInterface $entityManager): Response public function front_user_register(twigFunctions $functions,Request $request,TranslatorInterface $translator, UserPasswordHasherInterface $userPasswordHasher, EntityManagerInterface $entityManager): Response
{ {
//redirect to hesabix app register page //redirect to hesabix app register page
return $this->redirect('https://app.hesabix.ir/user/register'); return $this->redirect($functions->systemSettings()->getAppSite() . '/user/register');
$user = new User(); $user = new User();
$form = $this->createForm(UserRegisterType::class, $user); $form = $this->createForm(UserRegisterType::class, $user);
$form->handleRequest($request); $form->handleRequest($request);
@ -148,27 +151,33 @@ class UserController extends AbstractController
return $this->redirectToRoute('app_register'); return $this->redirectToRoute('app_register');
} }
#[Route('/login/by/token', name: 'app_login_by_token')] #[Route('/login/by/token{route}', name: 'app_login_by_token' , requirements: ['route' => '.+'])]
public function app_login_by_token(Request $request): Response public function app_login_by_token(string $route,AuthenticationUtils $authenticationUtils,twigFunctions $functions,Request $request, UserPasswordHasherInterface $userPasswordHasher, UserAuthenticatorInterface $userAuthenticator, BackAuthAuthenticator $authenticator, EntityManagerInterface $entityManager): Response
{ {
$token = $entityManager->getRepository(UserToken::class)->findOneBy(['tokenID'=>$request->get('tokenID')]);
if(!$token){
$token = $entityManager->getRepository(UserToken::class)->findOneBy(['token'=>$request->get('tokenID')]);
if(!$token)
throw $this->createNotFoundException('توکن معتبر نیست');
}
// log the user in on the current firewall $userAuthenticator->authenticateUser(
$security->login($user); $token->getUser(),
$authenticator,
$request
);
return $this->redirect($functions->systemSettings()->getAppSite() . $route);
}
// if the firewall has more than one authenticator, you must pass it explicitly /**
// by using the name of built-in authenticators... * @throws Exception
$security->login($user, 'form_login'); */
// ...or the service id of custom authenticators #[Route('/logout/by/token{route}', name: 'app_logout_by_token' , requirements: ['route' => '.+'])]
$security->login($user, ExampleAuthenticator::class); public function app_logout_by_token(string $route,twigFunctions $functions,Request $request,Security $security, EntityManagerInterface $entityManager): Response
{
// you can also log in on a different firewall try {
$security->login($user, 'form_login', 'other_firewall'); $security->logout(false);
} catch (Exception $e){}
// use the redirection logic applied to regular login return $this->redirect($functions->systemSettings()->getAppSite() . $route);
$redirectResponse = $security->login($user);
return $redirectResponse;
// or use a custom redirection logic (e.g. redirect users to their account page)
// return new RedirectResponse('...');
} }
} }

View file

@ -56,16 +56,16 @@ class UserController extends AbstractController
'message' => 'missing credentials', 'message' => 'missing credentials',
], Response::HTTP_UNAUTHORIZED); ], Response::HTTP_UNAUTHORIZED);
} }
$tokenString = $this->RandomString(254); // somehow create an API token for $user
$token = new UserToken(); $token = new UserToken();
$token->setUser($user); $token->setUser($user);
$token->setToken($tokenString); $token->setToken($this->RandomString(254));
$token->setTokenID($this->RandomString(254));
$entityManager->persist($token); $entityManager->persist($token);
$entityManager->flush(); $entityManager->flush();
return $this->json([ return $this->json([
'user' => $user->getUserIdentifier(), 'user' => $user->getUserIdentifier(),
'token' => $tokenString, 'token' => $token->getToken(),
'tokenID'=> $token->getTokenID()
]); ]);
} }

View file

@ -25,6 +25,9 @@ class Settings
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
private ?string $zarinpalMerchant = null; private ?string $zarinpalMerchant = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $appSite = null;
public function getId(): ?int public function getId(): ?int
{ {
return $this->id; return $this->id;
@ -77,4 +80,16 @@ class Settings
return $this; return $this;
} }
public function getAppSite(): ?string
{
return $this->appSite;
}
public function setAppSite(?string $appSite): static
{
$this->appSite = $appSite;
return $this;
}
} }

View file

@ -20,6 +20,9 @@ class UserToken
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private ?User $user = null; private ?User $user = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $tokenID = null;
public function getId(): ?int public function getId(): ?int
{ {
return $this->id; return $this->id;
@ -48,4 +51,16 @@ class UserToken
return $this; return $this;
} }
public function getTokenID(): ?string
{
return $this->tokenID;
}
public function setTokenID(?string $tokenID): static
{
$this->tokenID = $tokenID;
return $this;
}
} }

View file

@ -4,6 +4,7 @@ namespace App\Repository;
use App\Entity\UserToken; use App\Entity\UserToken;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
/** /**
@ -40,7 +41,7 @@ class UserTokenRepository extends ServiceEntityRepository
} }
/** /**
* @throws \Doctrine\ORM\NonUniqueResultException * @throws NonUniqueResultException
*/ */
public function findByApiToken($value): UserToken | null public function findByApiToken($value): UserToken | null
{ {
@ -54,6 +55,18 @@ class UserTokenRepository extends ServiceEntityRepository
; ;
} }
/**
* @throws NonUniqueResultException
*/
public function findByApiTokenID($value): UserToken | null
{
return $this->createQueryBuilder('u')
->andWhere('u.tokenID = :val')
->setParameter('val', $value)
->orderBy('u.id', 'ASC')
->getQuery()
->getOneOrNullResult();
}
// public function findOneBySomeField($value): ?UserToken // public function findOneBySomeField($value): ?UserToken
// { // {
// return $this->createQueryBuilder('u') // return $this->createQueryBuilder('u')

View file

@ -0,0 +1,92 @@
<?php
namespace App\Security;
use App\Entity\User;
use App\Repository\UserTokenRepository;
use Doctrine\ORM\NonUniqueResultException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
class BackAuthAuthenticator extends AbstractAuthenticator
{
/**
* @var UserTokenRepository
*/
private UserTokenRepository $userTokenRepository;
public function __construct(UserTokenRepository $userRepository)
{
$this->userTokenRepository = $userRepository;
}
public function supports(Request $request): ?bool
{
// TODO: Implement supports() method.
}
/**
* @throws NonUniqueResultException
*/
public function authenticate(Request $request): Passport
{
$apiToken = $request->get('tokenID');
if (null == $apiToken) {
// The token header was empty, authentication fails with HTTP Status
// Code 401 "Unauthorized"
throw new CustomUserMessageAuthenticationException('No API token provided');
}
$tk = $this->userTokenRepository->findByApiTokenID($apiToken);
if (! $tk) {
throw new UserNotFoundException();
}
// TODO: Implement authenticate() method.
return new Passport(
new UserBadge($apiToken, function() use ($apiToken) {
$tk = $this->userTokenRepository->findByApiTokenID($apiToken);
if (! $tk) {
throw new UserNotFoundException();
}
return $tk->getUser();
}),
new PasswordCredentials($tk->getUser()->getPassword()),
[
new RememberMeBadge(),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
// TODO: Implement onAuthenticationSuccess() method.
return new Response('1');
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
// TODO: Implement onAuthenticationFailure() method.
return new Response('0');
}
// public function start(Request $request, AuthenticationException $authException = null): Response
// {
// /*
// * If you would like this class to control what happens when an anonymous user accesses a
// * protected page (e.g. redirect to /login), uncomment this method and make this class
// * implement Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface.
// *
// * For more details, see https://symfony.com/doc/current/security/experimental_authenticators.html#configuring-the-authentication-entry-point
// */
// }
}

View file

@ -5,6 +5,7 @@ namespace App\Service;
use App\Entity\ChangeReport; use App\Entity\ChangeReport;
use App\Entity\Settings;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
class twigFunctions class twigFunctions
@ -84,5 +85,8 @@ class twigFunctions
return '0.0.1'; return '0.0.1';
} }
public function systemSettings(){
return $this->em->getRepository(Settings::class)->findAll()[0];
}
} }

View file

@ -48,8 +48,8 @@
<!-- Right Section --> <!-- Right Section -->
<div> <div>
<a href="https://app.hesabix.ir/user/login" class="btn btn-sm btn-primary me-1">ورود</a> <a href="{{ twigFunctions.systemSettings().appSite }}/user/login" class="btn btn-sm btn-primary me-1">ورود</a>
<a href="https://app.hesabix.ir/user/register" class="btn btn-sm btn-success">عضویت</a> <a href="{{ twigFunctions.systemSettings().appSite }}/user/register" class="btn btn-sm btn-success">عضویت</a>
</div> </div>
<!-- END Right Section --> <!-- END Right Section -->

View file

@ -52,7 +52,7 @@
<div class="alert alert-info"> <div class="alert alert-info">
برای ارسال دیدگاه به حساب خود وارد شوید. برای ارسال دیدگاه به حساب خود وارد شوید.
<br> <br>
<a href="/login">ورود</a> | <a href="https://app.hesabix.ir/register">عضویت</a> <a href="/login">ورود</a> | <a href="{{ twigFunctions.systemSettings().appSite }}/register">عضویت</a>
</div> </div>
{% else %} {% else %}
<div class="rounded bg-light px-2 pt-2 pb-1"> <div class="rounded bg-light px-2 pt-2 pb-1">

View file

@ -35,7 +35,7 @@
<!-- Footer --> <!-- Footer -->
<ul class="list-inline flex-gow-1 p-5 fs-sm fw-medium mb-0"> <ul class="list-inline flex-gow-1 p-5 fs-sm fw-medium mb-0">
<li class="list-inline-item"> <li class="list-inline-item">
<a class="text-muted" href="https://app.hesabix.ir/"> ورود به حسابیکس </a> <a class="text-muted" href="{{ twigFunctions.systemSettings().appSite }}/"> ورود به حسابیکس </a>
</li> </li>
</ul> </ul>
<!-- END Footer --> <!-- END Footer -->

View file

@ -10,7 +10,7 @@
<h3 class="mx-5"> <h3 class="mx-5">
متاسفانه پرداخت ناموفق بود! متاسفانه پرداخت ناموفق بود!
</h3> </h3>
<a class="btn btn-lg btn-success mt-4" href="https://app.hesabix.ir/profile/dashboard"> <a class="btn btn-lg btn-success mt-4" href="{{ twigFunctions.systemSettings().appSite }}/profile/dashboard">
<i class="bi bi-buy"></i> <i class="bi bi-buy"></i>
بازگشت به پروفایل کاربری بازگشت به پروفایل کاربری
</a> </a>

View file

@ -10,7 +10,7 @@
<h3 class="mx-5"> <h3 class="mx-5">
با تشکر از پرداخت شما. سفارش شما اعمال شد. با تشکر از پرداخت شما. سفارش شما اعمال شد.
</h3> </h3>
<a class="btn btn-lg btn-success mt-4" href="https://app.hesabix.ir/profile/dashboard"> <a class="btn btn-lg btn-success mt-4" href="{{ twigFunctions.systemSettings().appSite }}/profile/dashboard">
<i class="bi bi-buy"></i> <i class="bi bi-buy"></i>
بازگشت به پروفایل کاربری بازگشت به پروفایل کاربری
</a> </a>

View file

@ -12,7 +12,7 @@
نیاز نیست که هیچگونه نرم افزاری را نصب کنید. کافیست در سایت ثبت نام کرده و بلافاصله کسب و کار خود را مدیریت کنید. نیاز نیست که هیچگونه نرم افزاری را نصب کنید. کافیست در سایت ثبت نام کرده و بلافاصله کسب و کار خود را مدیریت کنید.
</p> </p>
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center"> <div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
<a href="https://app.hesabix.ir/user/login" class="btn btn-primary btn-lg px-4 gap-3"> <a href="{{ twigFunctions.systemSettings().appSite }}/user/login" class="btn btn-primary btn-lg px-4 gap-3">
<i class="fa fa-door-open"></i> <i class="fa fa-door-open"></i>
ورود | عضویت رایگان ورود | عضویت رایگان
</a> </a>