I am currently trying to create my own authorization system with Symfony (version 2.3). And I am not sure if I am overcomplicating things, I am pretty new to Symfony and SOA. I am creating my own authorization system because I have the need to restrict rights according to current user, its role and a current customer (current user can change customer) and its products.
What I currently have and I think may be too complicated is the following:
Foreach entity I have a repository for read operations only:
MyBundle/Entities/
Customer.php/CustomerRepository.php
Product.php/ProductRepository.php
...
To abstract the usage of the repositories and also the handling the create/update/delete. I have a Manager class per entity which are using the repositories (proxying to the repositores and usage of combination of different repos).
MyBundle/Manager/
CustomerManager.php
ProductManager.php
...
Then I have my classes which are are getting the authorization according to current user, its role and a current selected customer and its products. They are using the Managers for loading all the acl data.
MyBundle/Authorization/
AuthCustomers.php //Allowed customers to which user is assigned
AuthRights.php //Rights according to user and customer
The services look like this:
auth.manager.product:
class: MyBundle\Manager\ProductManager
arguments: ["#doctrine.odm.mongodb.document_manager"]
auth.manager.customer:
class: MyBundle\Manager\CustomerManager
arguments: ["#doctrine.odm.mongodb.document_manager"]
auth.authorization.authorization_rights_factory:
class: MyBundle\Authorization\AuthorizationRightsFactory
arguments: ["#auth.manager.role", "#auth.manager.customer"]
auth.authorization.authorization_customers_factory:
class: MyBundle\Authorization\AuthorizationCustomersFactory
arguments: ["#auth.manager.group", "#auth.manager.customer"]
Until this point I actually think it is ok. But what I would need now and what concerns me is, when doing some action on any of these entities I would actually need to do a check with my AuthorizationXXX services if I am allowed to do it. So the Authorization class is dependant on the Manager and the other way round.
To abstract this (to prevent infinite reference and I can reuse this in a controller and command script) I would now create another service where I inject the Authorization and Manager services and do these checks. So I would actually have another manager for my services.
In the end I would then have 7 entities, with 7 repositories and 7 Managers + a few authorization classes + 7 services to combine the managers and the authorization classes.
The question now is, is this the right way? I really feel like I am overcomplicating things. Any simpler suggestions are welcome :)
Voters seems to fit with your need, check out the doc: http://symfony.com/doc/current/components/security/authorization.html#voters
You could create a Voter which take as argument the current user, his roles and the current customer and return true if your business rules are respected and false if not.
In the end I now just kept the structure and just changed the meaning of the layers a little bit.
What I now have and feel comfortable with is:
Repositories for read only operations, are aware of other repositories (here I overdid it it in the beginning by wanting to keep the repos unaware of each other).
Manager which are handling persistence of the entities and proxying through to the read operations from the Repositories. They don't care about Authorization.
Authorization services which just handles my authorization things, so they can be reused anywhere without having to do Symfony specific stuff.
Model service which combines the Manager and Authorization and generally all depencies. Actually the Symfony controllers would just call the model services' methods. But with this I can also reuse it for Command line scripts etc.
I still have a lot of classes, but they are now nicely separated and only do their stuff and could be replaced easily.
In general I also looked at the FOSUserBundle in detail to get some inspiration. I think they solved it pretty well in general.
Related
Our team is having a bit of a headache while modifying our core model.
Our problem :
We have a bundle, let's name it COREBundle. We load it via composer on every app to get the very basic required to boot our system.
The COREBundle provides a Customer Entity.
Each app has some special customer properties that we want to add : how to proceed ?
Of course the complexity here is to make everything work with the COREBundle provided.
We are exploring the idea of having some MappedSuperClasses but it means we will need to modify all our entities in ALL our apps, which will be extremely time-consuming.
Help? Thank you!
This question already has answers here:
How can I implement an Access Control List in my Web MVC application?
(3 answers)
Closed 6 years ago.
I'm working on an MVC application with this structure:
Request
V
FrontController <-> Router
V
Controller <-> Model
V
View
I have two other components that I need to place in this structure:
Authentification: Logs the user in using the $_SESSION global variable;
RBAC: Role Based Access Control that can check if a role has access granted to a "ressource" (Controller method).
Every users can have any given number of roles (they can also have none).
Now, I need to place those two components in my applications, I need them to be able to:
If the User isn't authed and that the Request requires a authed User to be executed, the client should be redirected to a login page;
If the RBAC sees that the authed User doesn't have a role that has access granted to the required "ressource" to execute the Controller's method, the Controller's method should still be executed but with knowledge that the User did not have the permission to do so (Example: A User writes an article but doesn't have the right to publish it, so the article is saved as a draft and the User is told that a Moderator will have to publish it).
I already have a few ideas where to locate the Authentification and RBAC but I'm not sure:
Authentification could go in the FrontController or the Router;
RBAC could go in the FrontController or the Controller.
I saw someone putting the RBAC in the model but I don't understand why.
I'd like to have some insight on the subject please. Where should I put the Authentification and RBAC components?
Thank you!
In my experience, access control business logic changes as new features are added, so it pays to design flexibility and motility into your access control system. Thus, I would engineer Authentication and RBAC as separate traits, then incorporate those traits into the controller space as necessary.
As described, it sounds like the authentication trait would best be incorporated into your front controller: you know that all dependent controllers require authentication, so incorporate that check early in the life cycle to free up request sockets. If your requirements ever change to need some controllers to be ungated, you can push the trait down into specific controllers or into a base controller class.
As for RBAC, that may apply globally to all controllers as well as locally to some controllers. For example, your FrontController may query the RBAC to build the routing table, while dependent controllers would use the RBAC for their specific needs.
One thing to consider, though: you may also have some RBAC needs in the model. That is, some roles may have limited access to some fields in some models: role A can access all of model X, but role B can only read fields 1, 2, and 3 of model X. (Trust me, I have seen very, very complicated rules around roles that can see and act on what fields.)
Engineering RBAC as a controller trait may make porting to model space difficult. Thus, you may find it better to engineer RBAC as a service delegate and inject it on demand. With a well-provisioned IoC container, a service delegate is just as easy as compile-time traiting.
Finally, I'll add that you're going to want both of these under heavy test (they are important, after all). So whatever you choose, engineer so they can be tested. In my opinion, both traits and delegates are easy to test in isolation and either would be a good choice for implementing the necessary logic.
In a typical MVC application the authentication check (i.e. "if not auth, then stop and render the login page instead") is done very early in processing the request, while the business logic (i.e. "if user has this permission then this happens, otherwise that happens") is handled within the "C" (the controller).
Most frameworks have a mechanism in place for tests like the authentication check your are describing - names vary but I have often seen it called "middleware".
The role based access control is purely your implementation.
I am developing an app with Symfony2 that has 3 main parts called frontoffice, backoffice, admin. I was thinking to create three separate bundles: FrontOfficeBundle, BackOfficeBundle, AdminBundle but Symfony's docs says, each bundle should not have any relation with each other. Entity is already a shared property and probably some models. I could create a SharedBundle but it does not make sense. I remember when I created an app 2 years go when I had like 15 bundles and all connected each other and I know from experience that it's a nightmare.
Should I have just one bundle AppBundle and logic split in the folders, eg. Controller/Admin; Controller/FrontOffice, Controller/BackOffice?
What's the best approach?
It's all about SRP and DRY
It doesn't hurt to create a bundle, so make a separate bundle for stuff you need in multiple bundles, e.g. I tend to create an EntityBundle which contains entities and their repositories (as service).
Of course you can just use a single AppBundle too but please don't put your logic into the controllers -> create reusable services! Inject the services you need into your controller (which should themselves be services too).
Alternatives to base Controller Methods
No such thing as best approach.
However, instead of grouping by class type (Controller,Command,Form,Templates) within a directory, I like to create a single "component" directory for each request action. So I would have
Action
FrontOffice
BackOffics
Admin
User
UserController.php
UserFormType.php
UserTemplate.html.twig
Grouping files in this fashion really cuts down on figuring out where the various files live. Is it the "best" approach? Nope. But it works for me.
I am quiet new to the symfony framework and took some lessons and purchased the 'Starting in Symfony2' tutorial from knpuniverse. I want to be sure that I use the correct setup for my application. My question is, How do you call your first central bundle? eg. FrontendBundle? I want to make the next structure in my application:
FrontendBundle
The front where people get a landing site where they can also login
from there, when they login, they get into the next bundle:
CustomerBundle
Backend app where customers get their invoices and pay them and edit their information we stored in the database
And at least:
AdminBundle
Another backend app where I can edit customers, make invoices for customers and edit the app information
Is this the correct way and is FOSUserBundle a good bundle for this kind of application?
Bundles have sense only if they can be used in "multiple projects". I mean: if you write code, make it as a bundle and you can't reuse that bundle (for example because bundles, as you described above, are strictly and logically connected to your project) this three bundle separations is totally useless.
So, final answer (that is also a question) is:
Could you reuse FrontendBundle in other projects?
Could you reuse CustomBundle in other projects?
Could you reuse AdminBundle in other porjects?
If you notice that you can't use any or all of these bundles for other project, maybe this separations isn't good.
Why I say that?
Because if they can't be used in separated ways is likely that they should be used togheter so you should keep them togheter.
So I advice to keep them into a single bundle (YourNameYourBundleNameBundle (in that notation, for example)) and to separate the single "areas":
FrontEnd controllers/entities/views
Custom controllers/entities/views
Admin controllers/entities/views
This problem has been discussed at length already. For application specific code either do a single AppBundle or don't use bundles at all.
Recently i moved to Symfony 2 and i have a litte question.
Let's say i have the following Model:
"catalog" which contains "catalogs". The model gets its data from files but also needs a database connection to verify things.
In the Zend framework or other past projects i loaded the dependencies by a static object which forms a kind of "registry".
As i understand, Symfony 2 uses their service pattern (dependencie injection) instead. But how does this apply to my case.
Must i create a service for every model class i use to auto inject all dependencies? Or is it perfectly valid when i create a instance from my object myself and set for example the database connection in my constructor?
To create a service for every class which needs dependencies, seems a little bit overkill to me.
You can certainly create classes and inject dependencies the old-fashion way but take the time to learn the details of creating services. I think you will find:
Adding a new service is trivial. Copy/paste a few lines of configuration, adjust the class, id and maybe some parameters and you are done. Takes much less time than creating the actual class.
Very soon you will progress from just injecting a database connection to injecting other services as well as perhaps some parameters. Do you really want to have to remember to do all that stuff each time you need to new an object?
Using service id's can divorce your controllers from the exact location/name of a class. The first time you need to do some refactoring and maybe move some services into their own bundle or perhaps swap out a service with another you will be glad that you won't need to hunt down all your code and make changes.
S2 is not really focused on "Models". Instead, think in terms of a service named CatalogManager which wraps access to assorted catalog functionality.