In my Symfony 4.4 application i create the UserLocaleSubscriber which works fine but when user change your locale must logout ang login for change transaltion language. I try to use EqutableInterface to update the user session without logout user.
public function isEqualTo(UserInterface $user)
{
if ($user instanceof self)
{
if ($user->getLocale() != $this->locale) {
return false;
}
}
return true;
}
but stiil when i change the user locale i must logoout and login for using new locale. Is there any option to change language without logout?
You don’t need to logout at all, just redirect to /en or /es depend on your language and routing.
Related
I'm using Symfony 3.4 and I'm working with the Impersonate user feature : https://symfony.com/doc/3.4/security/impersonating_user.html
I need when I impersonate an user to get the original user.. I don't know how can I do that.
During impersonation, the user is provided with a special role called ROLE_PREVIOUS_ADMIN, is there a way to change this role ?
For example if my original user is ROLE_ADMIN, the special role is ROLE_PREVIOUS_ADMIN, but if my original user is ROLE_SOMETHING, the custom role should be : ROLE_PREVIOUS_SOMETHING
I just need to have a way to get the original user or at least get his roles.
Thanks !
I found a solution :
public function isImpersonatorAdmin()
{
$impersonatorUser = false;
if ($this->security->isGranted('ROLE_PREVIOUS_ADMIN')) {
foreach ($this->security->getToken()->getRoles() as $role) {
if ($role instanceof SwitchUserRole) {
$impersonatorUser = $role->getSource()->getUser()->hasRole('ROLE_ADMIN');
break;
}
}
}
return $impersonatorUser;
}
This function return true if the impersonator is ROLE_ADMIN.
My ASP.NET project uses a login screen which allows the users to enter credentials in order to sign in to the web application. At that point, the User object is set in the Session object. The User object contains information about the role of the user logged in. Now, I want the hangfire dashboard to be a link inside the application visible once the user logs in. And upon clicking the link, based on if the user's role is appropriate, I want to show or restrict the dashboard. How do I do this? I am not able to access the session object in the OWIN startup.cs class.
You can restrict access to the dashboard using an IAuthorizationFilter defined like this:
public class DashboardAuthorizationFilter : IAuthorizationFilter
{
public bool Authorize(IDictionary<string, object> owinEnvironment)
{
// In case you need an OWIN context, use the next line,
// `OwinContext` class is the part of the `Microsoft.Owin` package.
//var context = new OwinContext(owinEnvironment);
var ok = false;
if (HttpContext.Current != null && HttpContext.Current.User != null)
{
ok = IsAuthorizedForDashboard(HttpContext.Current.User);
}
return ok;
}
}
Where IsAuthorizedForDashboard is a function you will need to create.
Then register the filter like this:
app.UseHangfireDashboard(DashboardPath,
new DashboardOptions {
AuthorizationFilters = new List<IAuthorizationFilter> {
new DashboardAuthorizationFilter() }
});
I created an action that handles redirection to respected areas depending on user's type and ROLE (trainee, company or university let's say). If user is not logged in, it redirects to homepage (anonymous area), and if logged in - to their profile pages. I use it in homepage and many other cases, for example, as sign up and login success redirection.
public function authorizationAction(Request $request)
{
$user = $this->getUser();
$authorizationChecker = $this->get('security.authorization_checker');
$request->cookies->remove('action');
if ($user) {
if ($user->getType() == 'company' && $authorizationChecker->isGranted('ROLE_COMPANY_GUEST')) {
/** #var Company $company */
$company = $user->getCompany();
if ($user->getState() == 'active' && $company->getState() == 'active') {
$response = $this->redirectToRoute('company');
} else {
$response = $this->redirectToRoute('company_profile');
}
} elseif ($user->getType() == 'university' && $authorizationChecker->isGranted('ROLE_UNIVERSITY_GUEST')) {
/** #var University $university */
$university = $user->getUniversity();
if ($user->getState() == 'active' && $university->getState() == 'active') {
$response = $this->redirectToRoute('university');
} else {
$response = $this->redirectToRoute('university_profile');
}
} elseif ($user->getType() == 'trainee' && $authorizationChecker->isGranted('ROLE_TRAINEE')) {
/** #var Trainee $trainee */
$trainee = $user->getTrainee();
if ($user->getState() == 'active' && $trainee->getState() == 'active') {
$response = $this->redirectToRoute('trainee');
} else {
$response = $this->redirectToRoute('trainee_profile');
}
} else {
$response = $this->redirectToRoute('homepage');
}
} else {
$response = $this->redirectToRoute('homepage');
}
return $response;
}
I have seen some examples recommending using symfony events (kernel.request) to handle it minimizing controller code. But in this case I will not be able to use this action as sign up and login success path.
I am not using FOS, because of lack of user customization. I prefer handling User my self.
Is my approach wrong and something to be worried about?
Some things that I am concerned:
Redirection count:
For example. I am logged in user and I go to homepage and am redirected to my action where I am checked whether I am logged in or not and depending on user type I am redirected to respected page.
public function indexAction(Request $request)
{
if ($this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
return $this->redirectToRoute('authorization');
}
// ...
}
Slowing website:
In the future I will be using this action in more pages and website will definatelly slow down everytime executing same code on each page.
I am not using FOS, because of lack of user customization. I prefer handling User my self.
Blockquote
You really shouldn't reinvent the wheel here but even if you want to your assessment that extending FOS is hard is wrong, take a look at this gist how many of us Symfony Devs extend FOS to enable social login via HWIOauthBundle. Extending it for other purposes is equally trivial.
Update Since OP's Comments Below
....
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use AppBundle\Form\Type\LoginType;
....
....
$login_form = $this->createForm(LoginType::class);
$login_form->handleRequest($request);
if (!$login_form->isValid()){
//throw error
}
$email = strip_tags($login_form->get('email')->getData());
$password = $login_form->get('password')->getData();
// Call the User Manager
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserByUsernameOrEmail($email);
if(!$user){
//throw error : User With Submitted Credentials Does Not Exist
}
$user_password = $user->getPassword();
$encoded_password = $this->get('security.password_encoder')->encodePassword($user, $password);
if($encoded_password != $user_password){
//throw error : Wrong Password, Please Check Your Details & Re-submit
}
// Successful query of username/email and password now we log in user
// Create new token
$token = new UsernamePasswordToken($user, $user->getPassword(), 'main', $user->getRoles());
// Login New User
$tokenStorage = $this->get('security.token_storage');
$tokenStorage->setToken($token);
// User now logged in
$user_id = $user->getId();
....
Redirection count:
In your case each redirection would cause at least 1 database query to verify user session, so yes many them could lead to a bad use and server experience. If you use FOSUserBundle with Doctrine's Second Level Cache, you can avoid this query everytime you call $this->getUser();
Slowing website:
This very small bit of code with hardly be you bottleneck at scale but for arguments sake let's assume it is. I would solve this problem by introducing client side sessions. A framework like Angular or my personal favorite Ember would allow you to store user sessions on the client so as not to even bother going back to your server all the time for such a menial task as checking authentication or roles. I would still advise you to keep some server side code for those cheeky users who want to poke holes in your code.
The Access Control section in the Symfony documentation might offer easier solutions to restrict access. In my time using Symfony I have always been able to use it for redirection and access control.
I am building an application in ASP.NET MVC with windows authentication.
I need a way to logout the logged in user such that a new user can log into the same application without having to close the browser.
For this, I found a neat solution which is as below:
public ActionResult LogOut()
{
HttpCookie cookie = Request.Cookies["TSWA-Last-User"];
if(User.Identity.IsAuthenticated == false || cookie == null || StringComparer.OrdinalIgnoreCase.Equals(User.Identity.Name, cookie.Value))
{
string name = string.Empty;
if(Request.IsAuthenticated)
{
name = User.Identity.Name;
}
cookie = new HttpCookie("TSWA-Last-User", name);
Response.Cookies.Set(cookie);
Response.AppendHeader("Connection", "close");
Response.StatusCode = 0x191;
Response.Clear();
//should probably do a redirect here to the unauthorized/failed login page
//if you know how to do this, please tap it on the comments below
Response.Write("Unauthorized. Reload the page to try again...");
Response.End();
return RedirectToAction("Index");
}
cookie = new HttpCookie("TSWA-Last-User", string.Empty)
{
Expires = DateTime.Now.AddYears(-5)
};
Response.Cookies.Set(cookie);
return RedirectToAction("Index");
}
The problem with this approach however is that the same user cannot login again. It always needs to be a different user to the current one.
I am thinking I should be able to do this this by changing the if clause.
I tried removing the StringComparer.OrdinalIgnoreCase.Equals(User.Identity.Name, cookie.Value) condition as well but it fails to work since cookie value could be not null.
Please, check this post! It worked for me!
https://stackoverflow.com/a/3889441
Just in case, i'm pasting here the code:
#Palantir said:
That's strange... I make one single call to:
FormsAuthentication.SignOut(); and it works...
public ActionResult Logout() {
FormsAuthentication.SignOut();
return Redirect("~/");
}
I just started using Symfony's ACL system and was wondering why UserSecurityIdentity uses the username instead of the id of a User object to determine it's identity?
$user = new User();
$user->setId(new \MongoId());
$user->setUsername("frodo");
$dm->persist($user);
$uid = UserSecurityIdentity::fromAccount($user); // uses "frodo"
Our system allows users to alter their username, so using something more permanent (like the ID) to determine a user's identity seems more appropriate to me. Why was the ACL system implemented to use the username and not the ID? Any security considerations here?
This issue has been discussed here:
https://github.com/symfony/symfony/issues/5787
And has been solved in this commit:
https://github.com/symfony/symfony/commit/8d39213f4cca19466f84a5656a199eee98602ab1
So, now, whenever a user alter it's username, you can update its security indentity. I use a listener to do this:
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
/** Update user security identity in case nick is changed * */
if ($entity instanceof \Acme\UserBundle\Entity\User && $eventArgs->hasChangedField('username')) {
$aclProvider = $this->container->get('security.acl.provider');
$securityId = UserSecurityIdentity::fromAccount($entity);
$aclProvider->updateUserSecurityIdentity($securityId, $eventArgs->getOldValue('username'));
}
}
This is implementation of fromAccount() method from Symfony\Component\Security\Acl\Domain\UserSecurityIdentity class:
public static function fromAccount(UserInterface $user)
{
return new self($user->getUsername(), ClassUtils::getRealClass($user));
}
I think it is answer on your question.