Automatically merge session user in Symfony2 - security

I have written my own User-class extending Symfony's UserInterface and EquatableInterface which gets authenticated nice on logins and is correctly retrievable using $this->getUser(); in a controller.
Now, my question is, if it is possible to write something (dunno, a listener for example) that can automatically merge the user coming from the open session (so the one you retrieve with $user = $this->getUser();), so I dont have to call stuff like:
/**
* #Route("/url/to/route")
*/
public function myAction()
{
$user = $this->getUser();
$mergedUser = $this->getDoctrine()->getManager()->merge($user);
return array('user' => $mergedUser);
}
But instead:
/**
* #Route("/url/to/route")
*/
public function myAction()
{
/* User gets merged automatically! */
$mergedUser = $this->getUser();
return array('user' => $mergedUser);
}

Related

Symfony4. Force change password. Redirect to change-password site

I build login logic myself and i don't want use FosUserBundle.
I check Entity User date of last change password and if is more than 30 days i want that user will not fully authenticated only redirect to sites that they will must change password.
I try to use SecurityListener and onSecurityInteractiveLogin method.
Checking date works, but user is authenticated and redirect to sites what i configured in case after correct login (not to route change-password).
class SecurityListener
{
private $tokenStorage;
private $em;
private $session;
private $urlGenerator;
public function __construct(TokenStorage $tokenStorage, EntityManager $doctrine, Session $session, UrlGeneratorInterface $urlGenerator)
{
$this->tokenStorage = $tokenStorage;
$this->em = $doctrine;
$this->session = $session;
$this->urlGenerator = $urlGenerator;
}
/**
* #param InteractiveLoginEvent $event
* #return RedirectResponse
*/
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$lastPasswordAt = $this->tokenStorage->getToken()->getUser()->getlastPasswordAt();
$data = new \DateTime("now");
$data->modify('-30 days');
if ($lastPasswordAt < $data) {
$this->session->invalidate();
return $this->urlGenerator->generate('app_change_password');
}
}
}
Please give me some hints how to handle this issue.

Logout doesn't logout in laravel socialite

I am unable to logout the google user in my application,when i logout it was redirecting login page saying it is successfully logged out but when i click login with google button it is redirecting to previous logged in user i.e., it is not logged out
my controller code:
class LoginController extends Controller {
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct() {
$this->middleware('guest')->except('logout','getLogout');
}
/**
* Redirect the user to the GitHub authentication page.
*
* #return \Illuminate\Http\Response
*/
public function redirectToProvider() {
return Socialite::driver('google')->redirect();
}
/**
* Obtain the user information from GitHub.
*
* #return \Illuminate\Http\Response
*/
public function handleProviderCallback() {
$user = Socialite::driver('google')->stateless()->user();
if($user) {
$authUser = $this->findOrCreateUser($user);
Auth::login($authUser, true);
}
return view ( 'home' )->withDetails ( $user )->withService ( 'google' );
// $user->token;
}
/**
* Return user if exists; create and return if doesn't
*
* #param $githubUser
* #return User
*/
private function findOrCreateUser($googleUser) {
if ($authUser = User::where('email', $googleUser->email)->first()) {
return $authUser;
}
return User::create([
'name' => $googleUser->name,
'email' => $googleUser->email,
]);
}}
I have followed the laravel documentation steps for this social login but i am unable to logout from my application..can anyone explain why this is going to happen??
Well, finally i found solution to my question.Actually, there is nothing to worry about it.Logout code is working pretty good.Google account ask for permission for first time login after getting grant access it won't ask for permission it directly continues to login.If we logout the google account then it will ask for permission otherwise it won't

How to render twig templates into a service. Is it Best practice?

I have the following Symfony 3 controller:
public function register(Request $request)
{
$username=$request->get('username');
$password=$request->get('password');
$email=$request->get('email');
$captchaResponse=$request->get('g-recaptcha-response');
$session =$request->getSession();
$res1 = new Response();
$response_data=array('status'=>0);
if($session->get('captcha')===$captchaResponse)
{
$en = $this->container->get('user_model');
$data=$en->register($username,$password,$email);
$res1->headers->set('Content-Type','text/json');
if($data['status']===false)
{
$response_data['data']="An Internal error Happened";
error_log($data['data']);
}
else if($data['status']===-1)
{
$response_data['data']=$data['data'];
}
else
{
$response_data['status']=1;
$response_data['data']="Please check your mail to confirm your registration.";
/*Send Email*/
$message = \Swift_Message::newInstance()
->setSubject('Please confirm your registration')
->setFrom('symphotest#openmailbox.org')
->setTo($email)
->setBody($this->renderView('emails/confirm.html.twig',array('token'=>$data['data'])))
->addPart(
$this->renderView('emails/registration.txt.twig',array('token'=>$data['data'])),
'text/plain'
);
$this->get('mailer')->send($message);
}
}
else
{
$response_data['data']="You have not given a correct captcha";
}
$res1->setContent(json_encode($response_data));
$session->set('captcha',uniqid());//Generate gibberish in order not to reuse the very same captcha again
return $res1;
}
And I have made the following service:
namespace AppBundle\Models;
use Doctrine\ORM\EntityManager;
use AppBundle\Util\ModelStatus;
use AppBundle\Exceptions\InvalidArgumentException;
use \SwiftMessage;
class UserModel
{
/** #var EntityManager */
private $em;
/** #var \Swift_Mailer */
private $mailer;
/** #var \Twig_Environment */
private $twig;
/**
*
* #param EntityManager $em
* #param \Swift_Mailer $mailer
* #param \Twig_Environment $twig
*/
public function construct(EntityManager $em, \Swift_Mailer $mailer,\Twig_Environment $twig)
{
$this->em=$em;
$this->$mailer=$mailer;
}
/**
* Performs the actions needed for Registration
*
* #param unknown $username
* #param unknown $password
* #param unknown $email
* #param \Swift_Message $registrationMessage
*
* #return ModelStatus
*/
public function register($username,$password,$email)
{
$modelStatus=new ModelStatus();
try
{
/** #var \AppBundle\Entity\UserRepository */
$repository=$this->em->getRepository('AppBundle::Users');
$token=$repository->register($username,$password,$email);
$modelStatus->setData($token);
$modelStatus->setStatus(ModelStatus::STATUS_SUCCESS);
$this->mailer->send($registrationMessage);
$message = Swift_Message::newInstance()
->setSubject('Please confirm your registration')
->setFrom('symphotest#openmailbox.org')
->setTo($email)
->setBody($this->twig->//->renderView('emails/confirm.html.twig',array('token'=>$data['data'])))
->addPart(
$this->renderView('emails/registration.txt.twig',array('token'=>$data['data'])),
'text/plain'
);
$this->get('mailer')->send($message);
}
catch(InvalidArgumentException $arg)
{
$modelStatus->setStatus(ModelStatus::STATUS_FAILURE);
$modelStatus->setMessage($arg->getMessage());
}
catch (\Exception $e)
{
$modelStatus->setStatus(ModelStatus::STATUS_FAILURE);
$modelStatus->setMessage($e->getMessage());
}
return $modelStatus;
}
}
And I am refactoring the following section:
$message = \Swift_Message::newInstance()
->setSubject('Please confirm your registration')
->setFrom('symphotest#openmailbox.org')
->setTo($email)
->setBody($this->renderView('emails/confirm.html.twig',array('token'=>$data['data'])))
->addPart(
$this->renderView('emails/registration.txt.twig',array('token'=>$data['data'])),
'text/plain'
);
$this->get('mailer')->send($message);
Into the Model method register.
But As you can see I render some twig templates and I do know the best Option on how to do it. So far I thought the following options:
To render the templates as string and pass them to the the register method, create and send the email there.
Load the twig rendering service into the model and the render into the model. If not exists create one.
In the second bullet I may need to load the Twig rendering engine into a service. How can I do that?
In the end having the constructor like this:
public function __construct(EntityManager $em, \Swift_Mailer $mailer,\Twig_Environment $twig)
{
$this->em=$em;
$this->mailer=$mailer;
$this->twig=$twig;
}
And loading the service like this:
user_model:
class: AppBundle\Models\UserModel
arguments: ['#doctrine.orm.entity_manager','#mailer','#twig']
Seems that solved the problem.

ASP.net Identity Disable User

Using the new ASP.net Identity in MVC 5, How do we disable a user from logging in? I don't want to delete them, maybe just disable their account for a time period.
Does anyone have any ideas on this as I don't see a status column or anything on the ASPNetUsers table.
await userManager.SetLockoutEnabledAsync(applicationUser.Id, true);
await userManager.SetLockoutEndDateAsync(DateTime.Today.AddYears(10));
Update: As CountZero points out, if you're using v2.1+, then you should try and use the lockout functionality they added first, before trying the solution below. See their blog post for a full sample: http://blogs.msdn.com/b/webdev/archive/2014/08/05/announcing-rtm-of-asp-net-identity-2-1-0.aspx
Version 2.0 has the IUserLockoutStore interface that you can use to lockout users, but the downside is that there is no OOB functionality to actually leverage it beyond the pass-through methods exposed by the UserManager class. For instance, it would be nice if it would actually increment the lockout count as a part of the standard username/password verification process. However, it's fairly trivial to implement yourself.
Step #1: Create a custom user store that implements IUserLockoutStore.
// I'm specifying the TKey generic param here since we use int's for our DB keys
// you may need to customize this for your environment
public class MyUserStore : IUserLockoutStore<MyUser, int>
{
// IUserStore implementation here
public Task<DateTimeOffset> GetLockoutEndDateAsync(MyUser user)
{
//..
}
public Task SetLockoutEndDateAsync(MyUser user, DateTimeOffset lockoutEnd)
{
//..
}
public Task<int> IncrementAccessFailedCountAsync(MyUser user)
{
//..
}
public Task ResetAccessFailedCountAsync(MyUser user)
{
//..
}
public Task<int> GetAccessFailedCountAsync(MyUser user)
{
//..
}
public Task<bool> GetLockoutEnabledAsync(MyUser user)
{
//..
}
public Task SetLockoutEnabledAsync(MyUser user, bool enabled)
{
//..
}
}
Step #2: Instead of UserManager, use the following class in your login/logout actions, passing it an instance of your custom user store.
public class LockingUserManager<TUser, TKey> : UserManager<TUser, TKey>
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
{
private readonly IUserLockoutStore<TUser, TKey> _userLockoutStore;
public LockingUserManager(IUserLockoutStore<TUser, TKey> store)
: base(store)
{
if (store == null) throw new ArgumentNullException("store");
_userLockoutStore = store;
}
public override async Task<TUser> FindAsync(string userName, string password)
{
var user = await FindByNameAsync(userName);
if (user == null) return null;
var isUserLockedOut = await GetLockoutEnabled(user);
if (isUserLockedOut) return user;
var isPasswordValid = await CheckPasswordAsync(user, password);
if (isPasswordValid)
{
await _userLockoutStore.ResetAccessFailedCountAsync(user);
}
else
{
await IncrementAccessFailedCount(user);
user = null;
}
return user;
}
private async Task<bool> GetLockoutEnabled(TUser user)
{
var isLockoutEnabled = await _userLockoutStore.GetLockoutEnabledAsync(user);
if (isLockoutEnabled == false) return false;
var shouldRemoveLockout = DateTime.Now >= await _userLockoutStore.GetLockoutEndDateAsync(user);
if (shouldRemoveLockout)
{
await _userLockoutStore.ResetAccessFailedCountAsync(user);
await _userLockoutStore.SetLockoutEnabledAsync(user, false);
return false;
}
return true;
}
private async Task IncrementAccessFailedCount(TUser user)
{
var accessFailedCount = await _userLockoutStore.IncrementAccessFailedCountAsync(user);
var shouldLockoutUser = accessFailedCount > MaxFailedAccessAttemptsBeforeLockout;
if (shouldLockoutUser)
{
await _userLockoutStore.SetLockoutEnabledAsync(user, true);
var lockoutEndDate = new DateTimeOffset(DateTime.Now + DefaultAccountLockoutTimeSpan);
await _userLockoutStore.SetLockoutEndDateAsync(user, lockoutEndDate);
}
}
}
Example:
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult> Login(string userName, string password)
{
var userManager = new LockingUserManager<MyUser, int>(new MyUserStore())
{
DefaultAccountLockoutTimeSpan = /* get from appSettings */,
MaxFailedAccessAttemptsBeforeLockout = /* get from appSettings */
};
var user = await userManager.FindAsync(userName, password);
if (user == null)
{
// bad username or password; take appropriate action
}
if (await _userManager.GetLockoutEnabledAsync(user.Id))
{
// user is locked out; take appropriate action
}
// username and password are good
// mark user as authenticated and redirect to post-login landing page
}
If you want to manually lock someone out, you can set whatever flag you're checking in MyUserStore.GetLockoutEnabledAsync().
You can have a new class, which should be derived from IdentityUser class. YOu can add a boolean property in the new class and can use this new property of take care per check for login process. I also done it pretty well. I might wanna take a look at : blog
UserManager.RemovePasswordAsync("userId") will effectively disable a user. If the user has no password he will not be able to log in. You will need to set a new password to enable the user again.

GWT cross-domain rpc

I need to call a GWT application service from javascript which is running on different domain.
How could this be done? How do I point from my applicaton to the javascript url?
Thank you.
The idea behind a cross domain request is the your java script creats a script tag which loads a generated java script from the forgein url. When loaded the generated java script is evaluated and calls a callback function you created.
The following code ist not testet and shows the idea:
public class CrossSiteDomainRequest {
/** Counter to create unique ids for callback function. */
private static int idCounter = 0;
/** Url to load the javascript from. */
private String url;
/**
* Creates a new loader with the given <code>url</code>.
* #param url to load the java script from {#link #url}.
*/
public CrossSiteDomainRequest(String url) {
this.url = url;
}
/**
* Uses the {#link #url} to load the data from another url.
*/
public void load() {
String callbackId = "callbackId" + idCounter++;
String prepend = url.indexOf("?") != -1 ? "&" : "?";
String u = url + prepend + "callback=" + callbackId// Add more Parameters
createCallback(this, transId);
Element script = DOM.createElement("script");
script.setAttribute("src", u);
script.setAttribute("id", callbackId);
script.setAttribute("type", "text/javascript");
script.setAttribute("language", "JavaScript");
getHead().appendChild(script);
}
/**
* Destroys the callback with the given <code>id</code>.
* #param id of the script tag and native javascript callback function which should be destroyed.
*/
protected void destroyCallbackmethod(String id) {
getHead().removeChild(DOM.getElementById(id));
removeCallback(id);
}
/**
* This method is invoked by the callback to handel the loaded data.
* #param callbackId DOM-Id of the callback whick invoked this method.
* #param jso Object that encapsultes the loaded data.
*/
#SuppressWarnings("unchecked")
protected void onReceivedData(String callbackId, JavaScriptObject jso) {
try {
// Read data
} catch (Exception e) {
// Handle Error
}
destroyCallbackmethod(callbackId);
}
/**
* Creates a native javascript callback.
* #param cscr to invoke the {#link #onReceivedData(String, com.google.gwt.core.client.JavaScriptObject)} on when the data has been loaded.
* #param callbackId DOM-Id to create the callback back.
*/
private native void createCallback(CrossSiteDomainRequest cscr, String callbackId) /*-{
$wnd[callbackId] = function(j) {
proxy.#com.test.package.client.CrossSiteDomainRequest::onReceivedData(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(callbackId, j);
};
}-*/;
private native void removeCallback(String callbackId) /*-{
$wnd[callbackId] = null;
}-*/;
public static native Element getHead() /*-{
return $doc.getElementsByTagName('head')[0];
}-*/;
}
If you create a CrossSiteDomainRequest Object for the URL http://www.test.com/loadXDR.js
you have to evaluate the callbackId parameter and generate a java script which may looks like this:
callbackId({"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}});
The callbackId has to be replaced accordingly.

Resources