I have an application with the framework Symfony, I have users with particular rights, if they don't have the right to access into a page, I must block them, but the users still can access into page with modifying URLs. For example I have this URL that they have the right to access in it:
dialog/campany/sms/fid/setting/new
and they don't have the right to access to this URL:
dialog/campany/mail/fid/setting/new
but they can by remplacing sms by mail.
You have to checkout how your security.yml is configured and control the access from there. If you need information about it you can visit: http://symfony.com/doc/current/security/access_control.html.
Also you could control the access making a function that checks the user's credentials before given access to a given page.
Supposing that ROLE_SMS can only access to dialog/campany/sms/ and ROLE_MAIL can only access to dialog/campany/mail/
In app/config/security.yml , you should have:
security:
access_control:
- { path: ^/dialog/campany/sms/, roles: ROLE_SMS }
- { path: ^/dialog/campany/mail/, roles: ROLE_MAIL }
If you only want to limit the /new URL, you can change the path to:
- { path: ^/dialog/campany/sms/fid/setting/new$, roles: ROLE_SMS }
- { path: ^/dialog/campany/mail/fid/setting/new$, roles: ROLE_MAIL }
Related
# set hierarchy for roles?
role_hierarchy:
# give admin also the roles inside the array.
ROLE_ADMIN: [ROLE_USER]
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
#- { path: ^/admin, roles: ROLE_ADMIN }
# Unless the path is login, user must be authenticated anonymously.
# This means only page accessible anonymously is login page.
- { path: ^(/(login|register)), roles: IS_AUTHENTICATED_ANONYMOUSLY }
# can visit any other path if authenticated fully
- { path: ^/, roles: IS_AUTHENTICATED_FULLY }
This simple code doesn't seem to work. I cannot visit login or register anonymously. I know the IS_AUTHENTICATED_FULLY part is working as when I comment it out (and I am signed out, aka authenticated anonymously) I can visit other paths other than login and register.
Even when I simply do:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
It doesn't work and I cannot visit /login. What am I doing wrong?
I have been using this video as a guide: https://youtu.be/XjbIDOIoXTo?t=4211
Symfony 5.3 has deprecated the old authentication mechanism along with the Guard Component see https://symfony.com/blog/new-in-symfony-5-3-guard-component-deprecation. The new system doesn't "authenticate" user by default with IS_AUTHENTICATED_ANONYMOUSLY.
Anonymous users no longer exist
You now must use the PUBLIC_ACCESS as #Bossman specified in a comment. https://symfony.com/doc/current/security.html#allowing-unsecured-access-i-e-anonymous-users
The video in your link clearly states that the video has been recorded using Symfony 5.2
NOTICE - THIS SERIES WAS RECORDED USING SYMFONY 5.2. THERE HAVE BEEN SOME MINOR CHANGES AND SOME CLASSES HAVE SINCE BEEN REMOVED. YOU WILL STILL BE ABLE TO FOLLOW THIS TUTORIAL BUT YOU WILL NEED TO COMBINE IT THE DOCUMENTATION IN SOME PARTS.
I try to set up security per folder under src/ in Symfony. But I want a different set of security rules per main folder "General" and "Intranet" without having to prefix the routes... So I only have to prefix "Extranet"
Is that possible? I know that with a prefix in routing.yml it is very easy to do but that is not an option since the visible urls will suddenly change
The problem arises when we have to allow External users to our platform. For years it was only available for the companies' employees only but now external people must have access to certain pages. And some general routes (ajax calls etc) must be available for all
src/
Intranet/ => Open routes for internal users
SomeBundle
...
General/ => Open routes for all users
AnotherBundle
...
Extranet/ => Open routes for external users
TheBestBundle
...
Then the Extranet routes all get an extra prefix /extranet/. But I would like to have the other 2 (General and Intranet) without any prefix
# routing.yml
extranet:
resource: "#ExtranetBundle/Controller/"
prefix: /extranet/
Then with access control I take care of the /extranet routes
access_control:
# Login and the base_route "/" is always available
- { path: ^/$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
# External users + Super admins + server IP's can only access urls starting with /extranet/
-
path: ^/extranet/*
roles: [ROLE_EXTERNAL_USER, ROLE_SUPER_ADMIN]
ips: !php/const:SomeBundle\SomeClass\ConstantProvider::ALLOWED_SERVER_IPS
# Some routes need to be available for both internal and external users
# but hopefully without having to prefix them
...
# All other routes are only for internal users and the right ip addresses
-
path: ^/*
role: ROLE_INTERNAL_USER
ips: !php/const:SomeBundle\SomeClass\ConstantProvider::ALLOWED_SERVER_IPS
Or maybe an idea of approaching this problem differently?
I Think the best approche is to use voter external user need to have a unique ROLE (EXTERNAL_ROLE) then you can use voter to deney resources you want to protect https://symfony.com/doc/current/security/voters.html so this way you can protect a resource based on a the logic not on the folder
I am using fos bundle for Symfony and it's working fine but now I want to restrict all pages for not logged user except (login and register). How can I achieve that?
security.yml need to look like this:
access_control:
- { path: ^/(register|login)?$, roles: IS_AUTHENTICATED_ANONYMOUSLY}
- { path: ^/?$, roles: ROLE_USER}
Check Documentation for more Security Symfony !
Greetings Violence
I assume, that you use FosUserBundle and as stated in the comments, it's all explained in the Docs.
Have a look at "Step 4: Configure your application's security.yml"
I come to you because I have a little concern about the management of users and their Permissions with Symfony2.
Let me explain:
I set up the FOSUserBundle:
What I'd like to do now is a rights management. I have an entity 'Post'.
I have users with roles specified below.
ROLE_GUEST - VIEW,RATE
ROLE_USER - VIEW,CREATE,RATE,EDIT_OWN
ROLE_EDITOR - VIEW,CREATE,RATE,EDIT,DELETE
I want to set permission to each roles for performing certain actions.
Thank you :)
If i understand your necessity correctly you want to have a security layer based on those roles. You can do this in may ways:
The symfony default way -
you can configure the security layer of symfony like in the example below
# app/config/security.yml
security:
# ...
access_control:
- { path: ^/post/view, roles: VIEW }
- { path: ^/post/rate, roles: RATE }
# etc
This will take care of route access control. More info on http://symfony.com/doc/current/cookbook/security/access_control.html
For more complex roles like EDIT_OWN, you can take the direct approach
if (!$post->isAuthor($this->getUser())) {
$this->denyAccessUnlessGranted('EDIT', $post);
// or without the shortcut:
//
// use Symfony\Component\Security\Core\Exception\AccessDeniedException;
// ...
//
// if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) {
// throw $this->createAccessDeniedException();
// }
} else {
$this->denyAccessUnlessGranted('EDIT_OWN', $post);
}
For all this and more you can check the symfony site http://symfony.com/doc/current/best_practices/security.html
For even more advanced roles or ACL requirement also take a look here https://symfony.com/doc/current/components/security/authorization.html and at the authorization voters https://symfony.com/doc/current/components/security/authorization.html#voters
In the 4 links I provided in this post you should find all you need to implement RBAC as well as ACL. You can also find information about some annotations you may want to use. Also there are some extensions to the symfony security layer that may come in handy depending on the symfony version you are working on like JMS\SecurityExtraBundle.
Hope this help,
Alexandru Cosoi
I am setting up a website which I want to use separate firewalls and authentication systems for frontend and backend. So my security.yml is configured as below. I am using in_memory user provider in early development phase.
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
backend_in_memory:
memory:
users:
admin: { password: admin, roles: [ 'ROLE_ADMIN' ] }
frontend_in_memory:
memory:
users:
user: { password: 12345, roles: [ 'ROLE_USER' ] }
firewalls:
# (Configuration for backend omitted)
frontend_login_page:
pattern: ^/login$
security: false
frontend:
pattern: ^/
provider: frontend_in_memory
anonymous: ~
form_login:
check_path: login_check_route # http://example.com/login_check
login_path: login_route # http://example.com/login
access_control:
# (Configuration for backend omitted)
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
I have omitted the backend part because it doesn't matter. The problem is still there when the omitted part is commented out.
The problem is that frontend authentication won't work with the above configuration. Here's what I did:
Visit http://example.com/login
Enter the credential (user:12345), click login
http://example.com/login_check authenticates the user
The authentication service redirects user back to http://example.com/. No error is thrown. In fact, when I turned on the debug_redirects option, it clearly shows that "user" is authenticated on the redirect page.
Expected behavior: The security token should show that I'm logged in as "user" after following the redirect and go back to the index page.
Actual behavior: The security token still shows "anonymous" login after following the redirect and go back to the index page.
But with nearly identical settings (paths and route names aren't the same), the backend part works correctly.
After some investigation I found that the cause is the way user providers is currently written. Notice that frontend_in_memory section is placed below backend_in_memory that is used for backend authentication. So I explicitly specify the frontend_in_memory provider for the frontend firewall. And it kind of works - I must login with "user:12345" in the frontend login page. Logging in with "admin" won't work. So it must be using the correct user provider. But I suspect that the framework cannot update the security token correctly because it is still searching the "user" account from the first user provider which is backend_in_memory. In fact I can make the above config work with either one of the following changes:
add "user" login to the backend_in_memory provider's user list (password needn't be the same), or
swap frontend_in_memory with backend_in_memory so that frontend_in_memory becomes the first user provider.
Of course they are not the correct way of solving this problem. Adding "user" account to the backend makes no sense at all; swapping the order of two user providers fixes the frontend but breaks the backend.
I would like to know what's wrong and how to fix this. Thank you!
I was stuck when I posted the question, but after a sleep the answer is found ;)
Turns out I came across an issue reported long ago:
https://github.com/symfony/symfony/issues/4498
In short,
The problem isn't about the configuration.
And it isn't about authentication neither.
It actually relates to how an authenticated user is refreshed after redirection. That's why the app is correctly authenticated as "user" on the redirect page, but not after that.
Here is the code when the framework refreshes the user (can be found in \Symfony\Component\Security\Http\Firewall\ContextListener):
foreach ($this->userProviders as $provider) {
try {
$refreshedUser = $provider->refreshUser($user);
$token->setUser($refreshedUser);
if (null !== $this->logger) {
$this->logger->debug(sprintf('Username "%s" was reloaded from user provider.', $refreshedUser->getUsername()));
}
return $token;
} catch (UnsupportedUserException $unsupported) {
// let's try the next user provider // *1
} catch (UsernameNotFoundException $notFound) {
if (null !== $this->logger) {
$this->logger->warning(sprintf('Username "%s" could not be found.', $notFound->getUsername()));
}
return; // *2
}
}
The above code shows how the framework loops through the user providers to find the particular user (refreshUser()). *1 and *2 are added by me. If a user provider throws an UnsupportedUserException, this means that the provider isn't responsible for the supplied UserInterface. The listener will then iterate to the next user provider (*1).
However, if what the user provider thrown is a UsernameNotFoundException, this means that the provider is responsible for the supplied UserInterface, but the corresponding account could not be found. The loop will then stop immediately. (*2)
In my question, the same user provider, \Symfony\Component\Security\Core\User\InMemoryUserProvider, is used in both frontend and backend environment. And InMemoryUserProvider is responsible for the UserInterface implemented by Symfony\Component\Security\Core\User\User.
In the frontend, "user" is in fact authenticated successfully. However, in the user refresh attempt,
The order of the user providers will be like this: backend in-memory provider, frontend in-memory provider.
So, backend in-memory provider will run first.
The backend in-memory provider believes it is responsible for the supplied UserInterface because it is also an instance of Symfony\Component\Security\Core\User\User.
But it fails to locate the "user" account (it only has the "admin" account).
It then throws a UsernameNotFoundException.
The refreshUser() routine won't bother to try with next provider because UsernameNotFoundException means that the responsible user provider is already found. Instead it stops trying and removes the authentication token.
This explains why the configuration won't work. Despite using a different user provider, the only way to work around this is to copy the framework's InMemoryUserProvider and User classes and change the refreshUser() method to check against the copied User class, so that the frontend and backend user provider uses different user classes and won't clash.