<?php
namespace App\Controller;
use App\Repository\UserRepository;
use App\Services\OktaApiService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use App\Entity\User;
use App\Form\UserType;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class SecurityController extends AbstractController
{
private $session;
private $okta;
private $userRepository;
public function __construct(
SessionInterface $session,
OktaApiService $okta,
UserRepository $UserRepository)
{
$this->session = $session;
$this->okta = $okta;
$this->userRepository = $UserRepository;
}
/**
* @Route("/login", name="login", methods={"GET", "POST"})
*/
public function login(AuthenticationUtils $authenticationUtils)
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
/**
* @Route("/sso", name="sso", methods={"GET", "POST"})
*/
public function sso(
AuthenticationUtils $authenticationUtils,
Request $request,
LoggerInterface $logger
)
{
// $logger->debug(
// sprintf(
// "%s",
// $request->attributes->get('_route')
// ), $request->query->all()
// );
return $this->redirect($this->okta->buildAuthorizeUrl());
}
/**
* @Route("/authorization-code/callback", name="callback")
*/
public function callback(
Request $request,
LoggerInterface $logger
)
{
$token = $this->okta->authorizeUser();
if (!$token) {
return $this->redirectToRoute('homepage');
}
$email = $token->email;
$user = $this->userRepository->findOneByEmail($email);
if (! $user) {
$user = new User();
}
// Manually authenticate the user
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
$this->get('session')->set('_security_main', serialize($token));
$user->setEmail($email);
$user->setToken($token);
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$logger->debug(
sprintf(
"%s by %s",
$request->attributes->get('_route'),
$this->getUser()->getUsername()
)
);
return $this->redirectToRoute('homepage');
}
/**
* @Route("/logout", name="app_logout", methods={"GET"})
*/
public function logout() {
// Build logout url
$url = $this->okta->logout();
// Empty sessions
$this->session->clear();
$this->session->invalidate();
// Empty security token. Without this the user can go back end browse NJ without beeing authenticated on Okta
$this->get('security.token_storage')->setToken(NULL);
return new RedirectResponse($url);
// controller can be blank: it will never be executed!
// throw new \Exception('Don\'t forget to activate logout in security.yaml');
}
/**
* @Route("/users", name="users", methods={"GET"})
*/
public function users() {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$entityManager = $this->getDoctrine()->getManager();
$users = $entityManager->getRepository(User::class)->findAll();
return $this->render('security/users.html.twig', [
'users' => $users
]);
}
/**
* @Route("/users/edit/{id}", name="users_edit", methods={"GET", "POST"})
*/
public function edit($id, Request $request, UserPasswordEncoderInterface $passwordEncoder) {
$this->denyAccessUnlessGranted('ROLE_ADMIN');
// 1) build the form
if( $id == 0 ) {
$user = new User();
} else {
$entityManager = $this->getDoctrine()->getManager();
$user = $entityManager->getRepository(User::class)->find($id);
}
$form = $this->createForm(UserType::class, $user);
if( $id != 0 ) {
$form->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
'required' => false,
));
}
// 2) handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if( $id == 0 || !empty($form['plainPassword']->getData()) ) {
// 3) Encode the password (you could also do this via Doctrine listener)
$password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
}
// 4) save the User!
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
// ... do any other work - like sending them an email, etc
// maybe set a "flash" success message for the user
return $this->redirectToRoute('users');
}
return $this->render(
'security/edit.html.twig',
array('form' => $form->createView())
);
}
}