Chapters
33 Chapters
|
3:34:13
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
13.
access_control Authorization & Roles
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.10.2
"doctrine/doctrine-bundle": "^1.6.10", // 1.10.2
"doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
"doctrine/orm": "^2.5.11", // v2.7.2
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.8.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.0
"sensio/framework-extra-bundle": "^5.1", // v5.2.0
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.1.4
"symfony/cache": "^3.3|^4.0", // v4.1.4
"symfony/console": "^4.0", // v4.1.4
"symfony/flex": "^1.0", // v1.21.6
"symfony/framework-bundle": "^4.0", // v4.1.4
"symfony/lts": "^4@dev", // dev-master
"symfony/property-access": "^3.3|^4.0", // v4.1.4
"symfony/property-info": "^3.3|^4.0", // v4.1.4
"symfony/security-bundle": "^4.0", // v4.1.4
"symfony/serializer": "^3.3|^4.0", // v4.1.4
"symfony/twig-bundle": "^4.0", // v4.1.4
"symfony/web-server-bundle": "^4.0", // v4.1.4
"symfony/yaml": "^4.0", // v4.1.4
"twig/extensions": "^1.5" // v1.5.2
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.1.4
"symfony/dotenv": "^4.0", // v4.1.4
"symfony/maker-bundle": "^1.0", // v1.7.0
"symfony/monolog-bundle": "^3.0", // v3.3.0
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.1.4
"symfony/stopwatch": "^3.3|^4.0", // v4.1.4
"symfony/var-dumper": "^3.3|^4.0", // v4.1.4
"symfony/web-profiler-bundle": "^3.3|^4.0" // v4.1.4
}
}
39 Comments
I faced the problem with the roles. I am using Php8 and Symfony 5.3. In security.yml file I have decalred:
` access_control:
Hey Mr_T
What happens if you remove only the
requires_channelpart? Do you get any logs in the browser's console?Cheers!
Hey MolloKhan ,
I am sorry for my late response. Indeed, if I remove requires_channel part, then it works properly. In the browser's console I only get this message "Failed to load resource: the server responded with a status of 404 (Not Found)". And I also noticed that the tab's title changes to "Valet - Not found" when I get 404 error page
I believe, I know what's causing such a problem. I initiated symfony server as follows: 'symfony serve -d' and I didn't add flag '--no-tls' at the end and I think, that's why it is not loaded when 'requires_channel: http'
Hey Mr_T
Oh, nice catch! By default Symfony CLI spin up a webserver which should be accessed via HTTPS, you have to specify a flag to allow non-secure traffic
Cheers!
What if you want pages to be accessed interchangebly by multiple Roles?
for example: admin / accecable by: Admins
customer-support / accecable by: Admin, CustomerSupport
webshop / accecable by: Admins, Moderators, but not CustomerSupport
Is this possible? Are you going to learn this in a future episode?
Hey Farry7,
Yes, it's possible. And actually access_control that we're talking about in this video is exactly what you need. Just pass the array of roles, it should work, e.g:
Also, see docs for more detailed info about it: https://symfony.com/doc/current/security/access_control.html
I hope this helps!
Cheers!
Hi there,
IMHO, it's a risk the following behaviour:
- when I set this url /XXXXX as my real entry point to my dashboard and somebody writes /admin, my website automatically shows the /XXXXX, my real entry point to my dashboard. The security of my website could be compromised. Bad choice of design from my viewpoint.
When somebody writes /admin or whatever, I wouldn't show my real entry point. I'm going to know how to do this change.
Best regards.
Hey Abelardo,
I'm sorry, I'm not sure I completely understand your problem. Could you explain it a bit more? Probably some good real example? Do you mean when someone is try to access your /admin entry point - they see 403 Forbidden access code but you would rather prefer to return 404 instead?
Cheers!
Yes, sure! :)
When somebody goes to /admin entry point, you encourage us to show the login screen.🔒🔑 They could crack your system. 👹
For me, this behaviour is riskly since you are showing the entry point of your dashboard or admin zone. 🔐
I think a good behaviour of the application is to show a screen where you inform about an error (with no more information).
With this approach, you:
1) don't give them clues about how to enter into your dashboard; a security break!
2) don't give them clues about why they were wrong.
Best regards.
Hey AbelardoLG,
Yes, I see your point... but if someone may break your website knowing the admin page route - then you probably have problems with the website security :) Let's think about it... First of all, if you're not logged in - the system may not know WHO you are, and so it may not know if the admin page should be shown to you or not. That's where it redirects you to login page so you may tell the system who you are. That's just a good UX. Another point - nobody will hack your admin route, usually bad guys hack your login form :) That means that it does not really matter if users know your admin route or no, most probably they know your login page route, and it's enough for start cracking your system. And even if they cracked your credentials and logged in to your website as admin - the fact they don't know the your admin route is really a little problem, as your credentials were leaked anyway, and you definitely have problems in case case as well. And if someone could break your website admin credentials - probably the easiest he could do is to guess the admin route, I don't think it would be difficult for them to do so, even if you will make up /aLongAndSuperSecureAdminRoutePath :) So, I don't think that knowing the admin route is a security break really. Well, if you add a public link to your admin page for all users - it's not a good idea of course, but if your security system works well - it's not a security break. And if you really care about not making the admin route easy guessable - I'd recommend you to not call it as /admin that almost everyone might guess. Go with some more long and secure path name.
So, as far as you use long password with special chars in it and your system security works well and does not have bugs - knowing your admin route is not a problem at all. But if you're looking for a more security - look into 2 factor authentication, or at least get rid of username/password standard login form in favor of social login buttons. And use 2-factor authentication there :) But of course, then you're responsive for securing your laptop and log out from your socials, if you don't want someone pick your laptop and auto sign in with your logged in Facebook account :)
I hope it makes sense for you.
Cheers!
Interesting discussion here. 🤗
You said :
the system may not know WHO you are, and so he may not know if the admin page should be shown to you or not. That's where it redirects you to login page so you may tell the system who you are.
Yes, it's obviously my webpage is not a fortress :D but a simple web easily cracked but if you show the login page to anybody, you are showing the door to your fortress (a step more and that unknown will be inside it). IMHO, the less weak points you show the entire world, the less oportunity they have to harm you.
Go with some more long and secure path name.: Yes, it was made. But it's not enough since if they fail while they try to guess the admin panel, your approach will show the door to your control panel; so, double security: 1) a hard admin path and 2) when it is guessed, or it is failed, don't show the way to enter your control panel. I could fail on purpose to show your control panel login and...I'll try to break your security system. :)
Also, I don't use an email as part of the credentials. I use an identifier (it may be a long string, a special text, the name of the owner's pet name...and so on) and a password, indeed. Again, 2 factor authentication is applied here.
Obviously, I don't think that a robust security system exists (not at least in this world xD); but I should think faster than anybody who attempts to enter to my system.
As forementioned, interesting discussion here. 🤗
Best regards and take care. Cheers.
Hey AbelardoLG,
> Yes, it's obviously my webpage is not a fortress :D but a simple web easily cracked but if you show the login page to anybody, you are showing the door to your fortress (a step more and that unknown will be inside it). IMHO, the less weak points you show the entire world, the less opportunity they have to harm you.
Agree, but once again it's not about your admin page route path, it's more about your login page route path. In this case, I agree. The less people know your login page address - the less harm they may do. But, it's "relative". I bet you are not using crazy long route path with special chars in it. And if so, it's easy to write a simple script that will find your login page... it's totally easier than write a script that will hack your credentials :)
The most common solution to avoid bad guys cracking your credentials with login page - two factor authentication. Or, you can add a more custom code and allow like only 3 invalid password failures. If someone hit this - just block the username for some time, like an hour or so. It will increase your security a lot.
> but I should think faster than anybody who attempts to enter to my system.
Yes, agree :) And those are good questions to think about, really. It always depends on your project. Well, if you allow your users to login/register on the website - your login form should be public. If it's only for admins and users should not even know about it - all you said have much more sense of course. the current implementation of login process in Symfony is probably more user friendly, as it has wider use I think. Usually, if you want to make something more project specific - you would probably to add more custom code.
Yes, interesting discussion! And good questions! :)
Cheers!
Admin page route path = login page route path since the latter allows the access to the former. My web is about recipes. Any user is not intended to have credentials to access to any profile. Only the admin people have them.
Or, you can add a more custom code and allow like only 3 invalid password failures: I'll add this step to my control panel login page. :)
I couldn't block any anonymous user: how to do this when the ip is dinamically associated. 🤔
Have a nice thirsty day (sorry, Thursday) Take 🍻
Hey AbelardoLG,
> I couldn't block any anonymous user: how to do this when the ip is dynamically associated. 🤔
You don't need the IP :) Well, let's take a standard login form with username and password. Usually, intruders know the username, and crack only password just literally iterating over some common words, etc. In this case, you just need to block the access by the username and that's it. If someone enter his password incorrectly 3 times for the same username - block that username. And you don't care about IP here. Well, you can also add an IP check and then no matter what username/password pair was inputted - if it was made from the same IP 3 times and failed - block the IP. Well, doing both blocks (by IP *and* by username) you solve dynamic IPs for intruders.
Well, instead username it might be email or identifier as in your case - the strategy is the same. Username is just a general name for everything: identifier, email, username, token, etc.
If you afraid of blocking good users - I'd recommend to make the system obvious. Like saying how much failure attempts user has, and say that current username/IP is blocked. And that they have to wait some time before trying to login again or write to the support and ask to unblock them. And yeah, you don't need to block users forever. Doing it at least for 1 hour will already be a good enough thing.
I hope this helps!
Cheers!
Hi everybody.
I face an issue regarding the CRUD of a user and the roles.
In my User entity, the property <b>roles</b> is a type JSON:
In my database, the field is a LONGTEXT, with this content for example: ["ROLE_SUPER_ADMIN"]
I don't have any issue with all of this.
What I'm now trying to do, is to give the opportunity to EDIT a user, with a classic form.
Here, the code for the choice of the role:
It's working! Except that I don't want to give the chance to the user to choose multiple roles.
But.. If I change the "multiple" to false, I have this error: Notice: Array to string conversion.
I understand what it means, but what I don't get is how to manage this differently.
Can somebody help with this and some explanations ? Thanks!
Hey Christina,
Basically, you need to use a data transformer that will convert a string value selected in the form into an array. See the docs for more info: https://symfony.com/doc/cur...
Or, I think as an option, you can allow setting string value as an argument to the User::setRoles() method, but convert it into an array inside, because getRoles() should return an array IIRC. This trick should also work. But technically, as I understand your implementation, it's still possible to set multiple roles to one user, so probably it's not a big deal to have multiple=true for that form as it's exactly how your system works?
I hope this helps!
Cheers!
Hi, I have a question about the way of manage user roles in a large project. I have seen in many projects that a common way of manage roles is creating a new table "Role" and make a many to many relation to the "User" table. The way symfony works propose to have the roles inside the User table. Is that the best option? If not, I still want to take advantage of the symfony access control and authorization system, I think is very powerful. But how do I do to adapt that with that new "Role" table?
The project I am developing will have a lot of Roles related to user according to the modules that they decide to acquire and some administrator roles. That's the reason I am wondering using instead another table of roles.
Thanks!
Hey acuedd!
Excellent question :).
It's not the best option, it's just the "simplest" option - and one that works for most use-cases. But what you're thinking is a completely valid way to go.
At the end of the day, the only thing that Symfony cares about is that it can call
$user->getRoles()on yourUserobject and that this returns an array of the role strings. You can do that by having a simple property directly on theUserlike we do in this tutorial or by using some relations and looping over those relations inside ofgetRoles()to get an array of all of the strings.There is one point I want to challenge you on... to make sure you need the complexity before you add it ;). Personally, I would not create a
Roleentity. Why? Well, suppose you have some module and you want to only allow people with ROLE_EMPLOYEE_ADMIN to access that section. This will mean that (somewhere in your code, likeaccess_controlor in your controller), you will literally have the stringROLE_EMPLOYEE_ADMINwritten. What I'm trying to say is: you will truly have a finite set of actual "roles" in your system. If you have aRoleentity... and someone uses an admin interface to create a new role calledROLE_I_LIKE_SKIINGand then assign it to some users... what will happen? Well, unless a programmer has already added some code that protects a module/section with this role... nothing will happen. Does that make sense? I don't like having a database table full of values that are not truly dynamic.But, I could be missing some important use-case for you :). In general, I think there are two main options:
A) Simple: roles field on User.
B) More complex: a Group entity with a roles field. And then, each User is ManyToMany to Groups.
Option (B) is nice when you want your admins to be able to add people to "groups" (e.g. Human resources) and automatically get whatever roles are given to that group. But the roles are still just a property on that Group. Regardless of which option you choose, if you have a lot of roles, I'd recommend adding each of them as constants to some class (e.g.
RoleManager) along with apublic static getAllRoles()method that returns the whole list. You can use this in your code to, for example, create a drop-down menu with a list of all the roles.Let me know if this makes sense!
Cheers!
Hi! Yes it makes sense. I understood your point.
The roles in my project will be finite, but they will be related to the modules of the project mostly, and only some administrator roles that also will be finite, I think with an example it will be more understandable:
Supose the project will have a module to have control of the assistance of students to some class and other to create plans of classes and sell them to other people. When a user subscribes to the site, he will have the option to select wich modules he want to buy, and depending on that he will or not have access to that modules. So, the assign of roles will be when the user buys a module.
The reason I thought it will be better to have another entity with all the roles is because a role will be created when another module is created, and many roles will be assigned to many users according to what they bought, so, the roles will repeat a lot between each user.
But I still don't know if I'm exaggerating and complicating the work needlessly. With the whole scenario, I will be very grateful if you give me your opinion haha. Please.
In case create an entity for roles is the best for my project, if I understood you, the method getRoles() only need to return an array of strings full of roles, so the only extra thing I have to do is fetch all the roles related to the user and insert them in a string array and return that to still be able to use the access_controll and authorization system of symfony?
Thanks a lot! Sorry for the inconvenience.
Hey acuedd!
An example is indeed the best way! :)
Here's my answer I think I might not use roles for this! Here's the important point: if you forget about security for a minute and pretend that we're not storing any roles anywhere in the database, you already have enough information to know which user should have access to which module. You know this because, when a user subscribes to the site and selects the modules he wants to buy, you certainly store that information somewhere in the database. What you really want then, is a way for Symfony's security system to look at that existing data and use it to determine access. If you also stored all this info as roles related to users (however you structure it), you will be effectively duplication that info in the database.
So, how do we do this? Voters: https://symfonycasts.com/screencast/symfony-security/custom-voter
For example, above a controller for a module (or wherever you want to put security config), you might have:
Your custom voter would handle this
MODULE_ACCESS"attribute". The "subject" variable that the voter will receive will be the string module name (foo_barin this case). You will then check the database to see if the user should have access to the foo_bar module and grant/deny access.Let me know how this sounds!
Cheers!
Hi!
Oh thanks! I hadn't thought about that, you are totally right. Yes, I will fetch the sale information for the users and use voters instead of creating another entity.
Thanks for the help!
Hi. Tried to enable Tree on User entity with materialised path behaviour. Done everything from docs and make UserRepository extended from MaterializedPathRepository to have all its methods. Got eror from UserRepository:
Can you show me the rigth direction where to dig?
Have you checked at the docs? https://github.com/Atlantic...
I believe you didn't set up correctly your entity configuration
Of course i read it, and setup entity correctly. At now time it works well as a tree entity, but with standard repository without MaterializedPathRepository's methods.
Oh I see your problem, the autowire functionality can't just autowire your repository because of unknown dependencies but I found a possible solution. Just inject the "ManagerRegistry" into your entity's repository and add a little bit of logic as shown here: https://github.com/Atlantic...
I hope it helps. Cheers!
Oh, its so simple and straight, that i didn`t guess by myself)) Thank you wery much!
Hello,
I have a small question, i have this error " Could not convert database value "ROLE_ADMIN" to Doctrine Type json ", i don't understand why i have this error, is it the version of MySQL?
Hey @nico
That's because you are storing a string instead of an array in your $user->roles property. Just change that and everything should work fine :)
Cheers!
Thank you for your help, like you said i had to change from json to json_array and it worked.
Thank you again
Sorry, i talked too fast now i have this error "Uncaught PHP Exception Doctrine\DBAL\Types\ConversionException: "Could not convert database value to 'array' as an error was triggered by the unserialization: 'unserialize(): Error at offset 0 of 10 bytes'" do you have an idea
Oh, I think you are storing a string instead of an array in your $roles property. I think you can use the DB type of "json, json_array, or array" and it should work (but I might be wrong). Anyway, I drop you the docs about roles so you can get a better idea of how they work: https://symfony.com/doc/cur...
If you still have problems, let me know. Cheers!
Good day,
I have an issue regarding functional testing on an assertion regarding the security @isGranted("ROLE_USER") at the top of the controller and I am looking for help :) .
Here is the code I have :
<b>In User class : </b>
<b>In Controller : </b>
<b>In TestController</b>
The execution of that test case results in a response with status code 301.
If I change the assert at the top of the controller to "IS_AUTHENTICATED_REMEMBERED" or "ROLE_RECRUITER" (The role tested here.) the result is correct.
Any idea of what I am missing?
Regards,
Hey J-D
What's going on is that Symfony is redirecting you automatically to
/requestsbecause you are hitting/requests/(ending with slash), just adjust your route and you won't get a 301 response anymore. And those both roles are being by your fake "login" processCheers!
Hello Diego!
Thanks for the swift answer.
I already tested this before and I believe I had a double issue. You solution indeed solves the 301... But replace it with a 403. And i failed to notice the status error that changed during my debugging.
Additionally, if change the trailing / and adapt the the code at the top of the controller to align with my role hierarchy definition, the test works. It's just when I use the ROLE_USER that is only present in getRoles() that the test returns a 403.
This status code makes more sense. I'll use "IS_AUTHENTICATED_REMEMBERED" for my purposes.
I'm still curious if I missed anything else to make the "ROLE_USER" work with the tests.
Oh, I think you have to add the "ROLE_USER" role as well when creating the "fake" Token. Try it out and let me know if it works :)
Exactly on point!
Thank you again : with your guidance I now understand better the usage of the fake token. I assumed the fake token used the security configuration of the app, but it was a mistake. It only contains the information I provided to it.
Yep :) Since you manually created and set the token into the session (like short-circuiting), then the security component will just use it, so it doesn't matter if after that you change a role on your User object
Cheers!
"Houston: no signs of life"
Start the conversation!