Redirect works incorrectly - security

I try to reidrect user to login page if he not authenticated, but redirect work in both cases. Here is my code:
$securityContext = $this->get('security.context');
if ($securityContext->isGranted('IS_AUTHENTICATED_ANONYMOUSLY')) {
return $this->redirect($this->generateUrl('fos_user_security_login'));
} else {
$currentUser = $securityContext->getToken()->getUser()->getProfile()->getId();
$pathToImages = $this->get('kernel')->getRootDir().'/../web';
$userCars = $this->getDoctrine()->getRepository('VputiUserBundle:Car')
->findBy(array('profile' => $currentUser));
return $this->render('VputiUserBundle:Car:index.html.twig',
array('userCars' =>$userCars,
'pathToImages' => $pathToImages));
}
Where I am wrong? Thanks)

Every user is granted IS_AUTHENTICATED_ANONYMOUSLY, so that will always resolve to true. What you want is to check that the user wasn't granted IS_AUTHENTICATED_REMEMBERED, like this:
if ( ! $securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return $this->redirect($this->generateUrl('fos_user_security_login'));
} else {
...
}
I recommend reading this fantastic answer about roles for more information.
Anyway, in this case you don't need to do that. By default, when you send someone to a page that's behind a firewall it will automatically redirect them to the login page if they're not authenticated.

In Symfony2 are three level of authentication
IS_AUTHENTICATED_ANONYMOUSLY
IS_AUTHENTICATED_REMEMBERED
IS_AUTHENTICATED_FULLY
If a user have IS_AUTHENTICATED_FULLY role has also IS_AUTHENTICATED_REMEMBERED and IS_AUTHENTICATED_ANONYMOUSLY.
If a user have IS_AUTHENTICATED_REMEMBERED role has also IS_AUTHENTICATED_ANONYMOUSLY
So, everytime you check, you will pass that control. Try to "invert" logic: check for IS_AUTHENTICATED_FULLY and render the page, redirect otherwise
Working code for your case
$securityContext = $this->get('security.context');
if ($securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
$currentUser = $securityContext->getToken()->getUser()->getProfile()->getId();
$pathToImages = $this->get('kernel')->getRootDir().'/../web';
$userCars = $this->getDoctrine()->getRepository('VputiUserBundle:Car')
->findBy(array('profile' => $currentUser));
return $this->render('VputiUserBundle:Car:index.html.twig',
array('userCars' =>$userCars,
'pathToImages' => $pathToImages));
} else {
return $this->redirect($this->generateUrl('fos_user_security_login'));
}

Your problem is that all users who have been authenticated at all, either anonymously, via the "remember me" function, or fully using a username and password, have the "IS_AUTHENTICATED_ANONYMOUSLY" role; they stack. So your test always returns true, and the user is always redirected.
In this case, it may be better to do the test the other way around - redirect if the user doesn't have "IS_AUTHENTICATED_FULLY" or "IS_AUTHENTICATED_REMEMBERED".
See this related answer: https://stackoverflow.com/a/12984413/84649

Related

app.getDeviceLocation() always returning null

I have successfully asked the user for permission to get their coarse device and I have checked that app.isPermissionGranted() transitions from false to true after the user allows permission, but whenever I use app.getDeviceLocation(), it simply returns null. My Google Home that I test on gives my correct location when I ask, "Where am I?"
Here's a snippet of my code:
function askDate(app) {
console.log('Permission:', app.isPermissionGranted()); //says true
if (app.isPermissionGranted()) {
//this always says null
console.log('User device location:', JSON.stringify(app.getDeviceLocation()));
app.ask('thanks for the location!');
} else {
app.tell('You need to allow me to access your location to give an accurate recommendation');
}
}
function askForLocPermission(app) {
console.log('Permission:', app.isPermissionGranted()); //says false
let permission = app.SupportedPermissions.DEVICE_COARSE_LOCATION;
app.data.permission = permission;
app.askForPermission('To get weather near you', permission); //asks user for permission to get their zip code
}
I ended up testing it with app.SupportedPermissions.DEVICE_PRECISE_LOCATION and that worked gave me my coordinates no problem. I hope Google fixes the bug for coarse locations if there is one.

Yii file permission verify doesn't work

I have a question about yii.
I have some videos and the view action is performed based on some privileges. Some videos may be hidden, just for some user categories or for all. I can handle this from yii filter or something, but the real problem is what happen if someone knows the video file path, i can't handle the permissions in this case.
My solution is ok, but I have one problem - when someone access direct a file I redirect it (from htaccess) to a page, when I verify the user permission based on it's id. When I try to access the file from url everything is ok, the permission filter works fine, but when I the file is called from the player something it's wrong. From what I saw, the Yii::app()->user->id is empty.
Any recommendation?
This is my UserIdentity class:
class UserIdentity extends CUserIdentity
{
private $_id;
/**
* Authenticates a user.
* #return boolean whether authentication succeeds.
*/
public function authenticate()
{
$user=Utilizator::model()->findByAttributes(array('username'=>$this->username));
if($user===null)
{
$this->errorCode=self::ERROR_USERNAME_INVALID;
}
else
{
if($user->password!==$user->encrypt($this->password))
{
$this->errorCode=self::ERROR_PASSWORD_INVALID;
}
else
{
$this->_id = $user->id;
$this->errorCode=self::ERROR_NONE;
}
}
return !$this->errorCode;
}
public function getId()
{
return $this->username;
}
}

Sailsjs policies

I am having trouble figuring out sails policies, I follow the tutorial but still can't make it work.
In my policies.js file:
module.exports.policies = {
'*':true,
UsersController:{
'*':false,
signIn: 'skipAuthenticated'
}
}
And in my authenticated.js file:
module.exports = function skipAuthenticated(req, res, ok){
console.log("testing");
if (req.session.authenticated){
console.log("testing");
return ok();
}
else {
return res.send("You are not permitted to perform this action.", 403);
}
}
But the policy does not trigger. Any help would be really appreciated.
Suppose you have ProductsController and LoginController.
In policies.js :
module.exports.policies = {
'*': true,
login: {
'*': true
},
products: {
'*': 'isAuth',
feed: true
}
}
In Above Example:
Actions inside "login" controller can be accessed by any one. However, in products controller any other actions except for "feed" need to pass the "isAuth" policy.
If you take a look at the section entitled How do I protect my controllers with policies? it describes that the policy name matches the name of the file in api/policies. So your problem is that the actual policy name is "authenticated" (you said authenticated.js) and the policy name you're trying to use in your ACL is "skipAuthenticated."
So you can either change the policy file name to skipAuthenticated.js or you can change your ACL to reflect the actual policy name.
http://sailsjs.org/#!documentation/policies
You can apply one or more policies to a given controller or action. Any file in your /policies folder (e.g. authenticated.js) is referable in your ACL (config/policies.js) by its filename minus the extension, (e.g. 'authenticated').
Check out my completed gist. It should work with Sails 0.98 using passportjs and mysql
https://gist.github.com/anhnt/8297229
i'm guessing the filename is wrong try renaming authenticated.js to skipAuthenticated.js if that doesn't help remove the camelcase name... i had some issues with camelcase but can't remember if it was with policies

When are user roles refreshed and how to force it?

First off, I'm not using FOSUserBundle and I can't because I'm porting a legacy system which has its own Model layer (no Doctrine/Mongo/whatsoever here) and other very custom behavior.
I'm trying to connect my legacy role system with Symfony's so I can use native symfony security in controllers and views.
My first attempt was to load and return all of the user's roles in the getRoles() method from the Symfony\Component\Security\Core\User\UserInterface. At first, it looked like that worked. But after taking a deeper look, I noticed that these roles are only refreshed when the user logs in. This means that if I grant or revoke roles from a user, he will have to log out and back in for the changes to take effect. However, if I revoke security roles from a user, I want that to be applied immediately, so that behavior isn't acceptable to me.
What I want Symfony to do is to reload a user's roles on every request to make sure they're up-to-date. I have implemented a custom user provider and its refreshUser(UserInterface $user) method is being called on every request but the roles somehow aren't being refreshed.
The code to load / refresh the user in my UserProvider looks something like this:
public function loadUserByUsername($username) {
$user = UserModel::loadByUsername($username); // Loads a fresh user object including roles!
if (!$user) {
throw new UsernameNotFoundException("User not found");
}
return $user;
}
(refreshUser looks similar)
Is there a way to make Symfony refresh user roles on each request?
So after a couple of days trying to find a viable solution and contributing to the Symfony2 user mailing list, I finally found it. The following has been derived from the discussion at https://groups.google.com/d/topic/symfony2/NDBb4JN3mNc/discussion
It turns out that there's an interface Symfony\Component\Security\Core\User\EquatableInterface that is not intended for comparing object identity but precisely to
test if two objects are equal in security and re-authentication context
Implement that interface in your user class (the one already implementing UserInterface). Implement the only required method isEqualTo(UserInterface $user) so that it returns false if the current user's roles differ from those of the passed user.
Note: The User object is serialized in the session. Because of the way serialization works, make sure to store the roles in a field of your user object, and do not retrieve them directly in the getRoles() Method, otherwise all of that won't work!
Here's an example of how the specific methods might look like:
protected $roles = null;
public function getRoles() {
if ($this->roles == null) {
$this->roles = ...; // Retrieve the fresh list of roles
// from wherever they are stored here
}
return $this->roles;
}
public function isEqualTo(UserInterface $user) {
if ($user instanceof YourUserClass) {
// Check that the roles are the same, in any order
$isEqual = count($this->getRoles()) == count($user->getRoles());
if ($isEqual) {
foreach($this->getRoles() as $role) {
$isEqual = $isEqual && in_array($role, $user->getRoles());
}
}
return $isEqual;
}
return false;
}
Also, note that when the roles actually change and you reload the page, the profiler toolbar might tell you that your user is not authenticated. Plus, looking into the profiler, you might find that the roles didn't actually get refreshed.
I found out that the role refreshing actually does work. It's just that if no authorization constraints are hit (no #Secure annotations, no required roles in the firewall etc.), the refreshing is not actually done and the user is kept in the "unauthenticated" state.
As soon as you hit a page that performs any kind of authorization check, the user roles are being refreshed and the profiler toolbar displays the user with a green dot and "Authenticated: yes" again.
That's an acceptable behavior for me - hope it was helpful :)
In your security.yml (or the alternatives):
security:
always_authenticate_before_granting: true
Easiest game of my life.
From a Controller, after adding roles to a user, and saving to the database, simply call:
// Force refresh of user roles
$token = $this->get('security.context')->getToken()->setAuthenticated(false);
Take a look here, set always_authenticate_before_granting to true at security.yml.
I achieve this behaviour by implementing my own EntityUserProvider and overriding loadByUsername($username) method :
/**
* Load an user from its username
* #param string $username
* #return UserInterface
*/
public function loadUserByUsername($username)
{
$user = $this->repository->findOneByEmailJoinedToCustomerAccount($username);
if (null === $user)
{
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
}
//Custom function to definassigned roles to an user
$roles = $this->loadRolesForUser($user);
//Set roles to the user entity
$user->setRoles($roles);
return $user;
}
The trick is to call setRoles each time you call loadByUsername ... Hope it helps
Solution is to hang a subscriber on a Doctrine postUpdate event. If updated entity is User, same user as logged, then I do authenticate using AuthenticationManager service. You have to inject service container (or related services) to subscriber, of course. I prefer to inject whole container to prevent a circular references issue.
public function postUpdate(LifecycleEventArgs $ev) {
$entity = $ev->getEntity();
if ($entity instanceof User) {
$sc = $this->container->get('security.context');
$user = $sc->getToken()->getUser();
if ($user === $entity) {
$token = $this->container->get('security.authentication.manager')->authenticate($sc->getToken());
if ($token instanceof TokenInterface) {
$sc->setToken($token);
}
}
}
}
Sorry i cant reply in comment so i replay to question. If someone new in symfony security try to get role refresh work in Custom Password Authentication then inside function authenticateToken :
if(count($token->getRoles()) > 0 ){
if ($token->getUser() == $user ){
$passwordValid=true;
}
}
And do not check for passwords from DB/LDAP or anywhere. If user come in system then in $token are just username and had no roles.
I've been battling this for Symfony4, and I think I've finally settled down to a solution.
The thing is that in my case, the roles depend on the "company" the user is working with. It may be a CEO in one company, but an operator in another one, and the menus, permissions, etc. depend on the company. When switching companies, the user must not re-login.
Finally I've done the following:
Set the firewall to stateless.
In the FormAuthentication class, I set an attribute in the session explicitely, with the username.
I set up another Guard, which essentially take this attribute and loads the user for it from the database, for every single request.
class FormAuthenticator extends AbstractFormLoginAuthenticator
{
/** Constructor omitted */
public function supports(Request $request)
{
return 'app_login' === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'nomusuari' => $request->request->get('nomusuari'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['nomusuari']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $userProvider->loadUserByUsername($credentials['nomusuari']);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Invalid user/password');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
$valid = $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
return $valid;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$request->getSession()->set("user_username",$token->getUsername());
return new RedirectResponse(
$this->urlGenerator->generate("main")
);
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate('app_login');
}
}
The SessionAuthenticator (returns JSON, you may have to adapt it):
class SessionAuthenticator extends AbstractGuardAuthenticator
{
/**
* Called on every request to decide if this authenticator should be
* used for the request. Returning `false` will cause this authenticator
* to be skipped.
*/
public function supports(Request $request)
{
return $request->getSession()->has("user_username");
}
/**
* Called on every request. Return whatever credentials you want to
* be passed to getUser() as $credentials.
*/
public function getCredentials(Request $request)
{
return $request->getSession()->get("user_username","");
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
if (null === $credentials) {
// The token header was empty, authentication fails with HTTP Status
// Code 401 "Unauthorized"
return null;
}
// if a User is returned, checkCredentials() is called
/*return $this->em->getRepository(User::class)
->findOneBy(['apiToken' => $credentials])
;*/
return $userProvider->loadUserByUsername($credentials);
}
public function checkCredentials($credentials, UserInterface $user)
{
// Check credentials - e.g. make sure the password is valid.
// In case of an API token, no credential check is needed.
// Return `true` to cause authentication success
return true;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$data = [
// you may want to customize or obfuscate the message first
'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
// or to translate this message
// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
];
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
/**
* Called when authentication is needed, but it's not sent
*/
public function start(Request $request, AuthenticationException $authException = null)
{
$data = [
// you might translate this message
'message' => 'Authentication Required'
];
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
public function supportsRememberMe()
{
return false;
}
}
Finally, my security.yaml:
main:
anonymous:
stateless: true
guard:
entry_point: App\Security\FormAuthenticator
authenticators:
- App\Security\SessionAuthenticator
- App\Security\FormAuthenticator
Working fine. I can see the changes in the toolbar, and the Roles are refreshed.
HTH,
Esteve

Groovy help... About def edit and controllers

What does def edit = {} contain by default? You see, I was following a book but it turns out to be using an older version that's why some of the code don't work. I have this piece of code:
def edit= {
def user = User.get(params.id)
if (session?.user?.id == null){
flash.message = "You have to login first before editting your stuff."
redirect(action:'login')
return
}else if(session?.user?.id != params.id) {
flash.message = "You can only edit yourself."
redirect(action:list)
return
}else{
//What should I put here?
}
}
It's already functional. If the user clicks on edit without logging in, then he's redirected to a login page. Otherwise, if he did login, then he's only allowed to edit himself. What should I put on the "else" clause? It should already should already allow the user to edit his stuff, but I don't really know how to implement what I want. :(
It would be great if someone could share the default edit snippet.
I'm a bit new to all these, so go easy on me.
If you're talking about Grails, back up your UserController and try grails generate-controller - it will give you the complete text of default actions.
I also suggest that you look through scaffolding chapter - it's a great point to start.
the default edit action should look like this (pseudo-code, it depends on the actual domain class you create the code upon):
def edit = {
redirect(action: "show", id: params.id)
return true
def <domain>Instance = <DomainClass>.get(params.id)
if (!<domain>Instance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: '<DomainClass>.label', default: '<DomainClass>'), params.id])}"
redirect(action: "list")
}
else {
return [<domain>Instance: <domain>Instance]
}
}
btw: most of the time you don't have to do the security checks by programming these explicitly in the controller code, check out the Grails Spring Security Plugin for that purpose.

Resources