<?php
namespace Diplix\KMGBundle\Controller\Admin;
use Diplix\KMGBundle\Controller\BaseController;
use Diplix\KMGBundle\Entity\Accounting\CoopMember;
use Diplix\KMGBundle\Entity\Customer;
use Diplix\KMGBundle\Entity\Role;
use Diplix\KMGBundle\Entity\User;
use Diplix\KMGBundle\Form\Admin\CustomerForm;
use Diplix\KMGBundle\Form\Admin\WizardForm;
use Diplix\KMGBundle\Form\DefaultDeleteForm;
use Diplix\KMGBundle\Form\Admin\UserForm;
use Diplix\KMGBundle\Helper\ClientConfigProvider;
use Diplix\KMGBundle\Repository\UserRepository;
use Diplix\KMGBundle\Service\MailHelper;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class UserController extends BaseController
{
/**
* @var UserRepository;
*/
protected $rep;
public function __construct(
protected EventDispatcherInterface $dispatcher,
protected MailHelper $mailHelper,
protected UserPasswordHasherInterface $hasher,
protected ClientConfigProvider $clientConfigProvider
)
{
}
protected function init()
{
$this->rep = $this->getDoctrine()->getManager()->getRepository('Diplix\KMGBundle\Entity\User');
if (
(!$this->hasUserRole(Role::USER_ADMIN))
&&
(!$this->hasUserRole(Role::SUB_USER_ADMIN))
&&
(!$this->hasUserRole(Role::ADMIN1))
)
{
throw new AccessDeniedException();
}
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
}
/**
* Generate a new random password
* @return string
*/
protected function generatePw()
{
$out = "";
for ($i = 0; $i < 4;$i++)
{
$out .= chr( rand(97,122));
}
for ($i = 0; $i < 4;$i++)
{
$out .= rand(0,9);
}
return $out;
}
/**
* Generate a new random username
* @return string
*/
public static function generateUsername()
{
$out = "";
for ($i = 0; $i < 8;$i++)
{
$out .= chr( rand(97,122));
}
$out .= uniqid();
return $out;
}
/**
* Set encrypted pw using the default provider
* @param User $row
* @param string $newPw
* @return true
*/
protected function setEncryptedUserPw(User $row, $newPw)
{
$password = $this->hasher->hashPassword($row,$newPw);
$row->setPassword($password);
return true;
}
/**
* @return User
*/
protected function getNewUserObject()
{
$row = new User();
// since the username is currently not really used, except for the admin account, just use a random one
$row->setUsername($this->generateUsername());
$newPassword = $this->generatePw();
$this->setEncryptedUserPw($row, $newPassword);
$role = $this->rep->getRoleObjectByRoleName(Role::USER);
$row->addRole($role);
if (!$this->hasUserRole(Role::USER_ADMIN))
{
// when not being an global user admin, limit the user to our group
$row->setCustomer( $this->getCurrentUser()->getCustomer());
}
return $row;
}
public function indexAction(Request $request)
{
$this->init();
$f = $request->get('filter','customers');
// TODO : change to array hydration
// TODO: use iterator and paging as well as dynamic json loading for dataTable !
if ( ($this->hasUserRole(Role::USER_ADMIN)) || ($this->hasUserRole(Role::ADMIN1)) )
{
$list = $this->rep->findUserForList($f,null);
}
else
{
$list = $this->rep->findUserForList('customers',$this->getCurrentUser()->getCustomer());
}
return $this->render('@DiplixKMG/Admin/User/list.html.twig', array(
"dataList"=>$list, "filter"=>$f));
}
protected function removeRoleIfNotHavingItSelf($roleName, User $data)
{
if (!$this->hasUserRole($roleName))
{
$remove = null;
/** @var Role $r */
foreach ($data->getRoles(true) as $r)
{
if ($r->getRole() == $roleName) $remove = $r;
}
if (is_object($remove))
{
$data->removeRole($remove);
$this->addFlash("warning","Zuweisen der ".$roleName."-Rolle nicht zulässig.");
}
}
}
public function editAction(Request $request)
{
$newUser = false;
User::$roles_as_object = true;
$this->init();
$id = $request->query->get('id');
if ($id>0)
{
if ( ($this->hasUserRole(Role::USER_ADMIN)) || ($this->hasUserRole(Role::ADMIN1)) )
$row = $this->rep->findOneBy(array("id"=>$id,"hidden"=>false));
else // for SUB_USER_ADMIN
$row = $this->rep->findOneBy(array("id"=>$id,"hidden"=>false,"customer"=>$this->getCurrentUser()->getCustomer()));
if ($row===null)
{
$this->addFlash('warning','Invalid ID');
return $this->redirect($this->generateUrl('admin-users'));
}
}
else
{
$row = $this->getNewUserObject();
$newUser = true;
$newGeneratedPassword = $this->generatePw();
$this->setEncryptedUserPw($row,$newGeneratedPassword);
}
// Only sys_admin may edit sys_admin users
if ( ($row->hasRole(Role::SYS_ADMIN)) && (!$this->hasUserRole(Role::SYS_ADMIN,true)) )
{
throw new AccessDeniedException();
}
$originalPasswordHash = $row->getPassword(); // NOTE: not accessible anymore after used with createForm()!!!
// render form
$sameCustomer = false;
if ( ($row->getCustomer()!==null) && ($this->getCurrentUser()->getCustomer()!==null) )
{
$sameCustomer = $row->getCustomer()->getId() === $this->getCurrentUser()->getCustomer()->getId();
}
$opts = array(
UserForm::SUPER => $this->hasUserRole(Role::SUPER_ADMIN),
UserForm::ALLOW_PW_CHANGE => !$row->hasRole(Role::SYS_ADMIN) && ( $this->hasUserRole(Role::SUPER_ADMIN) || ($this->hasUserRole(Role::SUB_USER_ADMIN) && $sameCustomer) ),
UserForm::ALLOW_ROLE_CHANGE => !$row->hasRole(Role::SYS_ADMIN),
UserForm::DASHBOARDS => $this->clientConfigProvider->getDashboardCategories()
);
$form = $this->createForm(UserForm::class,$row,$opts);
$form->handleRequest($request);
if ($form->isSubmitted())
{
$valid = $form->isValid();
/** @var User $data */
$data = $form->getData();
// check email uniqueness
$ret = $this->rep->findBy(array('email'=>$data->getEmail()));
if (count($ret)>0)
{
if ($ret[0]->getId() != $data->getId())
{
/*
$valid = false;
$form->get('email')->addError(new FormError("Diese eMail-Adresse existiert bereits"));
*/
// eMail-Adressen dĂĽrfen mehrfach vorkommen
$this->addFlash('info',sprintf('Hinweis: Diese eMail-Adresse (%s) wird mehrfach verwendet. Ein Login/Passwortreset per eMail ist zukünftig nicht mehr möglich.',$data->getEmail()));
}
}
// check username uniqueness
$ret = $this->rep->findOneBy(array('username'=>$data->getUsername()));
if (is_object($ret))
{
if ($ret->getId() != $data->getId())
{
$valid = false;
$form->get('username')->addError(new FormError("Dieser Benutzername existiert bereits"));
}
}
if (!$valid)
{
$form->addError(new FormError("Bitte ĂĽberprĂĽfen Sie Ihre Eingaben"));
return $this->render('@DiplixKMG/Admin/User/edit.html.twig',array ( "form" => $form->createView(),
"row"=> $row,
));
}
if (strlen(trim($data->getPassword()))<1)
{
// no pw entered keep old one
$data->setPassword($originalPasswordHash);
}
else
{
if ($newUser)
{
$data->setPassword($originalPasswordHash);
}
else
{
// set new pw
$this->setEncryptedUserPw($data, $data->getPassword());
$data->setLastPasswordChange(new \DateTime());
}
}
// do not allow to set superadmin rights when not being a superadmin
$this->removeRoleIfNotHavingItSelf(Role::SUPER_ADMIN,$data);
$this->removeRoleIfNotHavingItSelf(Role::SYS_ADMIN,$data);
// if (!$this->hasUserRole(Role::SUPER_ADMIN))
// {
// $remove = null;
// /** @var User $data*/
// /** @var Role $r */
// foreach ($data->getRoles(true) as $r)
// {
// if ($r->getRole() == Role::SUPER_ADMIN) $remove = $r;
// }
// if (is_object($remove))
// {
// $data->removeRole($remove);
// $this->addFlash("warning","Zuweisen der SuperAdmin-Rolle nicht zulässig.");
// }
// }
$this->rep->persistFlush($data);
$this->addFlash('success','Record has been saved.');
if ($newUser)
{
$this->addFlash("info",sprintf("Neues Passwort: %s",$newGeneratedPassword));
}
return $this->redirect($this->generateUrl( 'admin-users', array("lastRecordId"=>$data->getId())));
}
else
{
return $this->render('@DiplixKMG/Admin/User/edit.html.twig',array ( "form" => $form->createView(),
"row"=> $row,
));
}
}
public function deleteAction(Request $request)
{
$this->init();
$id = $request->get('id'); // _GET or _POST
/** @var User $row */
if ($this->hasUserRole(Role::USER_ADMIN))
$row = $this->rep->findOneBy(array("id"=>$id,"hidden"=>false));
else // for SUB_USER_ADMIN
$row = $this->rep->findOneBy(array("id"=>$id,"hidden"=>false,"customer"=>$this->getCurrentUser()->getCustomer()));
if ($row===null)
{
$this->addFlash('warning','Invalid record ID');
return $this->redirect($this->generateUrl('admin-users'));
}
if ($row->getSysUser())
{
$this->addFlash('warning','This is a internal system user. Please contact support if you want to delete it.');
return $this->redirect($this->generateUrl('admin-users'));
}
// Only sys_admin may delete sys_admin users
if ( ($row->hasRole(Role::SYS_ADMIN)) && (!$this->hasUserRole(Role::SYS_ADMIN,true)) )
{
throw new AccessDeniedException();
}
$form = $this->createForm( DefaultDeleteForm::class,array("id"=>$id));
$form->handleRequest($request);
if ($form->isSubmitted())
{
if ($form->get('commit')->getData() == 1)
{
$row->eraseMyself();
$this->rep->persistFlush($row);
$this->addFlash('success','Record has been deleted.');
}
return $this->redirect($this->generateUrl('admin-users'));
}
else
{
return $this->render('@DiplixKMG/Admin/User/delete.html.twig',array ( "row"=> $row, "form"=>$form->createView()));
}
}
public function wizardAction(Request $request)
{
$this->init();
$this->ensureUserHasRole(Role::USER_ADMIN);
$form = $this->createForm(WizardForm::class,array());
$form->handleRequest($request);
if ($form->isSubmitted())
{
$valid = $form->isValid();
if ($valid)
{
// check email uniqueness
$ret = $this->rep->findOneBy(array('email' => $form->get("email")->getData()));
if (is_object($ret))
{
$valid = false;
$form->get('email')->addError(new FormError("Diese eMail-Adresse existiert bereits"));
}
if ($valid)
{
$U = $this->getNewUserObject();
$newPassword = $this->generatePw();
$this->setEncryptedUserPw($U, $newPassword);
$U->setEmail($form->get("email")->getData());
$U->setPhone($form->get('phone')->getData());
$U->setFirstName($form->get("firstName")->getData());
$U->setLastName($form->get("lastName")->getData());
// ensure unique username
$unameBase = substr(strtolower($U->getFirstName()), 0, 1) .".". strtolower($U->getLastName());
$idx = 0;
do
{
$uname = $unameBase . ($idx > 0 ? sprintf("%d", $idx) : "");
$U->setUsername($uname);
$ret = $this->rep->findOneBy(array('username' => $uname));
$idx++;
} while (is_object($ret));
// store
$this->rep->persistFlush($U);
if ($form->get('type')->getData()==WizardForm::CUSTOMER)
{
// create customer
$C = new Customer();
$C->setName($form->get("company")->getData());
$C->setCustomerNo($form->get("customerNo")->getData());
$this->getDoctrine()->getManager()->persist($C);
$U->setCustomer($C);
}
else
if ($form->get('type')->getData()==WizardForm::MEMBER)
{
$M = new CoopMember();
$M->setName($form->get("company")->getData());
$M->setNumber($form->get("memberNo")->getData());
$M->setShortCode($form->get('shortCode')->getData());
$this->getDoctrine()->getManager()->persist($M);
$U->setMember($M);
}
else
{
throw $this->createAccessDeniedException('Unbekannter Typ');
}
$this->rep->flush($U);
$this->addFlash('success', 'Benutzer/Kunde wurde angelegt.');
// inform user
try
{
$mh = $this->mailHelper;
$mh->NewAccountMail($U, $newPassword);
$this->addFlash('info', 'Zugangsdaten wurden per eMail versendet.');
}
catch (\Throwable $ex)
{
$this->addFlash('warning','Automatischer Versand der Zugangsdaten fehlgeschlagen !');
}
return $this->redirectToRoute("admin-users");
}
}
}
return $this->render('@DiplixKMG/Admin/User/wizard.html.twig',array ( "form"=>$form->createView()));
}
public function loginAsAction(Request $request)
{
$this->init();
$this->ensureUserHasRole(Role::SUPER_ADMIN);
$id = $request->get('id'); // _GET or _POST
/** @var User $user */
$user = $this->rep->findOneBy(array("id" => $id, "hidden" => false));
if ($user === null)
{
$this->addFlash('warning', 'Invalid record ID');
return $this->redirect($this->generateUrl('admin-users'));
}
if ( ($user->hasRole(Role::SYS_ADMIN)) && (!$this->hasUserRole(Role::SYS_ADMIN,true)) )
{
throw new AccessDeniedException();
}
// Impersonate
$firewall = "admin_area";
$session = $request->getSession();
// clean eventually existing session
$this->container->get('security.token_storage')->setToken(null);
// set new token
$token = new UsernamePasswordToken($user, "no-password", $firewall, $user->getRoles());
$this->container->get('security.token_storage')->setToken($token);
// save token in session
$session->set('_security_'.$firewall, serialize($token));
$session->save();
// Fire the login event
$this->dispatcher->dispatch(new InteractiveLoginEvent($request, $token));
$this->addFlash("info","Sie befinden sich nun im Benutzerkontext von ".$user->getUsername());
$home = $this->generateUrl("kmg_home");
if ($user->getCustomer() != null)
{
return $this->redirectToRoute("admin-switch-customer",
array( "customerId"=>$user->getCustomer()->getId(),
"referTo"=>$home));
}
return $this->redirect($home);
}
}