I'd like to ask other opinions about code structuring of business logic on Laravel applications, mainly regarding permissions at the row level.
For those that don't know it, Laravel is a MVC framework for PHP, much like Rails.
For the sake of understanding, let's suppose a multi-tenant application where each user has his own albums and pictures, so far so good.
Now, each user can invite others to collaborate (by uploading photos) into his album.
Both, the album's owner and collaborator that uploaded the picture may be able to delete or update information about that picture.
Only the owner may edit the album and invite new collaborators.
Collaborators can remove themselves of the album if they want so.
Pinterest should be a nice example of something similar, but our application is probably 3 or 4 times more complex.
The question is: where should I handle that kind of logic?
Laravel proposes the approach of having repositories, entities and services, which I don't fully understand, probably because of the lack of good examples. So the obvious first choice to meet those deadlines was to put it all on controllers (ew!). Now, digging into refactoring, there are many possible ways to un'spaghettize our code:
I've seen people implement ACL at row level (looks kinda dumb and overkill)
It would be possible to turn models into behavior aware objects and not only data containers, something like $album->add_photo($photo) and check permissions at that function
It would also be possible to override model's save method and do there those checks
Or, follow the Laravel proposed road of having separate layers of concern
I suppose that having methods like $album->can_be_edited_by($user) may simplify the displaying of 404 erros on routes not allowed, hiding view's links as well as validating before saving the models
Which would you recommend, and does anyone know any simple, but understandable, example of repositories, entities and services not using .NET?
Thanks!
Edit: I guess that a full ACL system would cause excessive overhead, since there may be thousands of resources associated with each user, but only one role per kind of association. For instance, pictures will have an uploader_id and albums will have an owner_id.
I could be wrong but I think ACLs are OBJECT based permissions (i.e., a user can or can't delete photos in GENERAL). What you want is more custom MODEL based permissions (row level like you said), i.e., a user can delete photos that they themselves created (SPECIFIC ones).
Most Laravel packages are designed for object based permissions I think, but not https://github.com/deefour/authorizer - this one is a great hidden gem. We don't use it in our project but I found that it really covers all the bases we'd need.
We have really advanced model permissions on our app, I have them scattered throughout my models, but I take a very model centric approach, which isn't necessarily very "laravel-esque". In your example with delete, I would override the delete method in your model or listen for the eloquent event and prevent it there. If you have to prevent read/write on certain attributes you could even do that by extending your validator or using custom mutators/getters, serializers or listening on events. More on where to add business logic in my question/answer here: https://stackoverflow.com/a/27804817/796437
I'm still trying to find the best approach, if I do I'll update this - but thought I'd post.
In Laravel you can use Policies or use solutions, like Symfony Voters.
For Laravel exists same package - Laravel Simple Voters.
Using this, you can check access to custom objects, looks like this:
Access::isGranted('edit', $post) // current user can edit this post?
You can put this logic, to example, into middleware, if you wish check requests to controllers.
Related
In my Laravel application, I have a Model called Project which has, among others, a property called approved_at. There is also a ProjectController, which contains the usual methods *(index, show, create, update, edit...)
Now, the user that created the Project can edit it (modifying its other attributes), but only some "staff" members have the right to approve one.
From what I understand, there can be two ways to handle the approval operation:
Both users, from their respective views (let's call them edit and approve) fire the ProjectController#update method, which will internally distinguish who-can-do-what by means of authorization or policies;
I create a new ProjectApprovalController, with its own update method, and check the authorization at the endpoint (eg, /projects/{id}/approve).
What is the best approach to tackle this behaviour?
It depends on what do you want to do with this in the future. If there would be some kind of extra steps to do behind the approve method for example: connection to external micro service to check if project exists in external database with subsidies then you should definitely split it.
If you don’t mind I would suggest you to not focus so much on the implementation. Your application should be removable as fast as you build it. There is a great presentation about this from Greg Young called ‘The Art of
Destroying Software’. Be more focus to build your solution with SOLID principles and test the behaviour of this method to make it easier to replace in the future.
to answer your question, second option is more restful approach, but I don’t know if that is not shooting to fly with a cannon
I am currently building a website with the following user types:
customers
caregivers
partners
agent/mediator
system user (<- special case, I'll talk to you in a moment)
Each user type should only be allowed to do certain things.
However, the system users are somewhat different.
These should be able to be divided into groups, and these groups should also have different rights.
My customer should be able to create/edit these groups including the assignment of given rights. However, it should not be possible to edit the other user types.
I have already seen this ACL packages (https://github.com/spatie/laravel-permission and https://github.com/JosephSilber/bouncer).
But I don't think they support that.
I have also thought of the following approach: There is no user type "system user", but the users are assigned the type of group, and in the code, I check if this type is "customer","caregiver","partner" and "agent". If this is not the case, the user is automatically a system user.
Is there perhaps an open source project that has such a similar approach? I'm currently having a hard time with all the controller classes at the moment (have now merged them into a single user controller and have the $type in the route and a factory class return the right model to me, and then call up a factory again to return the respective service class <- feels totally wrong and badly implemented).
Currently, Joseph Silber is building multi-tenancy for Bouncer. I think that will fit your needs, but it's not released yet for the time I'm writing this.
Also, it's practically 100% sure that you will need to extend a package like that or fork it to adapt to your needs. Looks like a very specific code that will need modifications.
I'm starting with Symfony 3, and I need some guidance about how to proceed with the process of implementing dynamic authorization and authentication.
First of all, the authentication bit is mostly done, I've done it according to this link from symfony docs: http://symfony.com/doc/current/security/entity_provider.html. What I'm yet to understand is how to implement the interface function getRoles() in order to return a value from the database (I have a table Role related to the User table).
Secondly, the authorization part. My application will require the end user to create his own access mechanism, in other words, I have an interface where the user creates a Role, then defines what pages that Role will be able to access and what privileges it'll have (create, read, update, delete, and so on). Afterwards the roles are attributed to the application users.
All in all it is pretty standard stuff so Symfony must have a clean way to do it. What I figured out so far is that I'll have to use ACLs, so I did as in the docs: http://symfony.com/doc/current/security/acl.html
My honest question here is: What now? What steps do I take to fully implement the authentication mechanism? What should I do now in order to persist and retrieve the access rules? How do I relate them with the user Roles?
P.S.: This question might be sort of a duplicate of some other questions here, but truth being told, those questions did not help me either, nor my scavenging in the docs did.
So, your question is very broad. Anyway it is a good question, so I'm going to try to reply.
THE AUTHENTICATION
Nothing to say here, I simply hope you used FOSUserBundle as suggested by the article itself: is the best way to implement a registration/login system in Symfony and it will give you an idea of how the entire process works. Starting from scratch if you are not a Symfony experienced developer seems to not be the best idea.
Give FOSUserBundle a try!
THE AUTHORIZATION PROCESS
About authorization you have basically two options: the use of Voters and the use of ACL.
In my experience the best option is ever the use of Voters.
In most cases, in fact, you will have a bidirectional reference (see Doctrine's documentation about this) in your entities between the User and the object on which (s)he have rights. In this case the ACL is not required and even discouraged.
In fact, the ACL does nothing more than creating a relationship between two objects (the User and the Article in your use case). To manage this relationship it uses a table in the database and so it has to query it to get the relationship and check for the authorization rights.
But if you already have a bidirectional reference between the User and the Article/Group directly in your entities, then you already have this relationship in place and so you can use Voters and the use of the ACL is superfluous and even, as said, discouraged as it is a useless duplication.
If you don't have this bidirectional relationship in your entities, then create it: it will be certainly useful in the future for other things and, anyway, you will have ever the ability to access your linked entities directly from your entities tree!
More, in your scenario, you cannot use ACL as you will have custom rights/privileges on your objects: the voters, again, are the best option to build this kind of things.
Don't use ACL, but Voters instead
HOW TO PROCEED
The first thing I would do, is to list in an interface all the available privileges: after all, they are strictly related to your application business logic, as it is not possible for a user to make someone able to do something that your app cannot do: if your application doesn't implement an editing flow, then it is not possible for a user to give someone the ability to edit an article. It's obvious.
So, something like this may be good:
interface PrivilegesEnum
{
const CREATE = 1;
const EDIT = 2;
const DELETE = 4;
const READ = 8;
const OTHER = 16;
// ... Other privileges
}
As you can see, I've given a numeric value to each privilege: this will give you the ability to use bitmasks that is a really powerful mechanism to manage this kind of things: it allows you to use only one field in the database to list all the privileges.
You can read more about bitmasks here:
https://www.google.it/search?q=bitmask+php+example
How to implement a bitmask in php?
Why should I use bitwise/bitmask in PHP?
http://alanhollis.com/a-quick-guide-to-using-bitmasks-for-permissions-in-php/
https://codereview.stackexchange.com/questions/1509/php-bitmask-class
https://www.google.it/search?q=php+bitmask+theory
How to implement a bitmask in php?
http://php.net/manual/en/language.operators.bitwise.php
http://php.net/manual/it/language.operators.bitwise.php
https://code.tutsplus.com/articles/understanding-bitwise-operators--active-11301
https://code.tutsplus.com/articles/number-systems-an-introduction-to-binary-hexadecimal-and-more--active-10848
I used this system in the past and those are some useful links I collected. They might help you!
BUILD A FORM TO LIST PRIVILEGES
Another thing you may find useful is a FormType to list your available privileges: you can do this writing a simple custom FormType.
HOW TO MANAGE ROLES
To manage roles read how Roles are managed by the Security Component and in FOSUserBundle (on Stackoverflow)
THE RELATIONSHIP BETWEEN USERS AND GROUPS AND ARTICLES
Once you reached this point you should have some more entities, read more about Doctrine relationship mechanisms knowing it better and you should be able to relate your users with their role, their group and the articles.
Anyway you will have all the required conceptual and practical tools needed to think better at your concrete implementation.
FINAL NOTE
As you can see, implement such type how authorization process isn't so simple.
I suggest you to think very carefully if it is really required in this stage of the development of your app, because if you can defer it in the future, then I suggest you to do it.
If you want to go online as fast as possible, implementing this system will require a lot of time to learn, implement, debug and refact the code (I'm speaking of weeks, not days!).
So, if you have all this time, then, go to implement this system. But if you feel you haven't all this time, then go with a more "static" system, go online, and then make it more "dynamic".
After all, this is the Lean Startup era!
Good luck!
I need help with something I can’t get my head wrapped around regarding the Repository and Service/Use-case pattern (part of DDD design) I want to implement in my next (Laravel PHP) project.
All seems clear. Just one part of DDD that is confusing is the data structures from repositories. People seem to choose data structures the Repository should return (arrays or entities) but it all has disadvantages. One of which is performance looking at my experiences in the past. And one is which you don’t have interfaces for simple data structures (array or simple object attributes).
I’ll start with explaining the experience I have with a previous project. This project had flaws but some good strengths I learned from and like to see in my new project but with solving some design mistakes.
Previous experience
In the past I’ve build a website that was API Centric using the Kohana framework and Doctrine 2 ORM (data mapper pattern). The flow looked like this:
Website controller → API client (HMVC calls) → API controller → Custom Repository → Doctrine 2 ORM native Repository/Entity-manager
My custom Repository returned plain arrays using Doctrine2 DQL. Doctrine2 recommends array result data for read only operations. And yes, it made my site nice and light. The API controller just converted the array data to JSON. Simple as that.
In the past my company created projects relying fully on loaded Doctrine2 entities and it’s something we regretted due to performance.
My REST API supported queries like
/api/users?include_latest_adverts=2&include_location=true
on the users resource. The API controller passed include_location to the repository which directly included the location relation. The controller read latest_adverts=2 and called the adverts repository to get the latest 2 adverts of each user. Arrays were returned.
For example first user array:
[
name
avatar
adverts [
advert 1 [
name
price
]
advert 2 [
….
]
]
]
This proved to be very successful. My whole website was using the API. It would be very easy to to add a new client because the API was perfectly in production already using oauth. The whole website runs on it.
But this design had flaws too. My controller still contained A LOT of logic for validation, mailing, params or filters like has_adverts=true to get users with adverts only. It would mean that if I created a new port, like a total new CLI interface, I would have to duplicate alot of these controllers due to all the validation etc. But no duplication if I would create a new client. So at least one problem was solved :-)
My admin panels were completely coupled to the doctrine2 repository/entity-manager to speed up development (sort of). Why? Because my API had fat controllers with special functionality for the website only (special validation, mailing for registering etc). I would have to redo work or refactor a lot. So decided to use the Entities directly to still have some sort clear way of writing code instead of rewriting all my API controllers and move them to Services (for site & admin) for instance. Time was an issue in fixing my design mistakes.
For my next project I want all code to go through my own custom repositories and services. One flow for good separation.
New project (using DDD ideas) and dilemma with data structures
While I like the idea of being API centric, I don’t want my next project to be API centric in core because I think the same functionality should be available without the HTTP protocol in between. I want to design the core using DDD ideas.
But I liked the idea using a layer that just talked as a API and returns simple arrays. The perfect base for any new port, including my own frontend. My idea is to consider my Service classes as the API interface (return the array data), do the validation etc. I could have Services specially for the website (registering) and plain services used by the Admin or background processes. In some admin cases a Service would not be required anyway for simple CRUD editing, I could just use Repositories directly. Controllers would be very thin. With this creating a real REST API would just be a matter to create new controllers using the same Services my frontend controller classes do.
For internal logic like business rules it would be useful to have Entities (clear interfaces) instead of arrays from repositories. This way I could benefit from defining some methods that did some logic based on attributes. BUT If I would be using Doctrine2 and my repositories would always return Entities my application would suffer a big performance hit!!
One data structure ensures performance but no clear interfaces, the other ensures clear interfaces but bad performance when using a Data Pattern pattern like Doctrine 2 (now or in the future). Also I could end up with two data types which would be confusing.
I was thinking something similar to this flow:
Controller (thin) → UserService (incl. validation) → UserRepository (just storage) → Eloquent ORM
Why Eloquent instead of Doctrine2? Because I want to stick a bit to what’s common within the Laravel framework and community. So I could benefit from third party modules, for example to generate admin interfaces or similar based on models (bypassing my DDD rules). Other than using third party modules, I would design my core stuff so switching should always be easy and not affect data structure choices or performance.
Eloquent is an activerecord pattern. So I would be tempted to convert this data to POPO’s like Doctrine2 entities are. But nope... as said above, with doctrine2 real models would make the system very fat. So I fall back to simple arrays again. Knowing this would work for both and any other implementation in the future.
But it feels bad always rely on arrays. Especially when creating internal business rules. A developer would have to guess values on arrays, have no autocompletion in his IDE, could not have special methods like in Entity classes. But making two ways of dealing with data feels bad too. Or I am just too perfectionist ;) I want ONE clear data structure for all!
Building interfaces and POPO’s would mean a lot of duplicate work. I would need to convert an Eloquent model (just a table mapper, not entity) to an entity object implementing this interface. All is extra work. And eventually my last layer would be just like a API, thus converting it to arrays again. Which is extra work too. Arrays seem the deal again.
It seemed so easy reading up into DDD and Hexagonal. It seems so logic! But in reality I struggle with this one simple issue trying to stick to OOP principles. I want to use arrays because it’s the only way to be 100% sure I am not depended on any model choice and querying choice from my ORM regarding performance etc and don't have duplicate work in converting to arrays for views or an API. But there's no clear contract on how a user array could look. I want to speed up my project using these patterns, not slow them down :-) So not an option to have many converters.
Now I read a lot of topics. One makes POPO’s & interfaces that conform proper entities like Doctrine2 could return, but with all the extra work for Eloquent. Switching to Doctrine2 should be fairly easy, but would impact performance so bad or one would need to convert Doctrine2 array data to these own entity interfaces. Others choose to return simple arrays.
One convinces people to use Doctrine2 instead of Eloquent, but they leave out the fact that Doctrine2 is heavy and you really need to use array results for read only operations.
We design repositories to be changeable right? Not because it’s “nice” by design only. So how could we rely on full Entities if it has such big impact on performance or duplicate work? Even when using Doctrine2 only (coupled) this same issue would arise due to its performance!
All ORM implementations would be able to return arrays, thus no duplicate work there. Good performance. But we miss clear contracts. And we don’t have interfaces for arrays or class attributes (as a workaround)... Ugh ;)
Do I just miss a missing block in our programming languages? Interfaces on simple data structures??
Is it wise to make all arrays and have advanced business logic talk to these arrays? Thus no classes with clear interfaces. Any precalculated data (normally would be returned by an Entity method) would be within an array key defined the Service class. if not wise, what’s the alternative considering all of the above?
I would really appreciate if someone with great experience in this “domain” considering performance, different ORM implementations, etc could tell me how he/she has dealt with this?
Thanks in advance!
I think what you are dealing with is something similiar I'm struggling with. The solution I'm thinking works best is:
Entities/Repositories
Use and pass around Entities always when performing Write operations (Creating things, Updating things, Deleting things, and complex combinations thereof).
Sometimes you may use Entities when doing Read operations (when you anticipate the Read might need to be used for a Write soon after...ie. ->findById is soon followed by ->save).
Anytime you are working with an Entity (whether it be Write or Read), the Repositories need to be the place to go. You should be able to tell new developers that they can only persist to the database through Entities and the Repository.
The Entities will have properties that represent some Domain Object (many times they represent a database table with the fields of a table, but not always). They will also contain the domain logic/rules with them (ie. validation, calculations) so they are not anemic. You may additionally have some domain services if your Entities need help interacting with other Entities (need to trigger other events), or you just need an additional place to handle some extra domain logic (perform Repository calls to check for some unique conditions).
Your Repositories will solely be for working with Entities. The Repositories could accept Entities and do some persistence work with them. Or they could accept just some parameters, and do some reading/fetching into full Entities.
Some Repositories will know how to save some Domain Objects that are more complex than others. Perhaps an Entity that has a property which contains a list of other Entities that need to be saved along side the main entity (you can dive deeper into learning about Aggregate roots if you want).
The interfaces to Repositories rest in your Domain layer, but not the actual implementations of those Repositories. That way you can have an Eloquent version or whatever.
Other Queries (Table Data Gateway)
These queries won't work with Entities. They'll just be accepting parameters and returning things like Arrays or POPO's (Plain Old PHP Objects).
Many times you will need to perform Reads that do not return nicely into a single Entity. These Reads are typically more for reporting (not for CRUD-like operations, like Reading a user into an edit form that is eventually submitted and saved). For example, you might have a report that is 200 rows of JOINed data. If you used the Repositiory and tried to return large deep objects (with all the relationships populated, or even lazy-loaded) then you are going to have performance issues. Instead, use the Table Data Gatway pattern. You are just displaying data and not really needing OOP power here. The outputted data could however contain ID's, which through the UI could be used to initiate calls to Repository persistence methods.
As you are developing your app, when you come across the need for a new Read/Report query, create a new method in some class somewhere in your Table Data Gatway folder. You may find you have already created a similar query, so see how you can consolidate the other query. Use some parameters if necessary to make the gateway method's queries more flexible in particular ways (ie. columns to select, sort order, pagination, etc.). Don't make your queries too flexible though, this is where query builders/ORMs go wrong! You need to constrain your queries to a certain extent to where if you need to replace them (perhaps a different database engine) then you can easily perceive what the allowed variations are and aren't. It's up to you to find the right balance between flexibility (so you have more DRY code) and constraints (so you can optimize/replace queries later).
You can create services in your Domain to handle receiving parameters, then passing them to the Table Data Gateway, and then receiving back arrays to do some more mutating on. This will keep your Domain logic in the domain (and out of the infrastructure/persistence layer of the Repository & Table Data Gateway).
Again, just like the Repository, use interfaces in your domain services so that the implementation details stay out of your Domain layer, and resides in the actual Table Data Gateway folder.
I'm currently creating my own PHP mvc for a site that I run so that it will include just the code needed yo be as light and fast as possible.
The site has quite a large range of functions and user features so working out which actions go where in which models controlled by which controllers is starting to get quite complex.
Example
Say I have the following member features
Favourites
Friends
History
Each of those can be controlled by the membercontroller but then my question is whether to have them all inside one model or a model for each.
Each of those three has sub many actions such as:
Add to favourites
Remove favourites
Show favourites
Add to history
Remove history
Show history
Add as friend
Remove friend
Message friend
...etc
At the moment I'm thinking a model for each (favourite, friends, history) is probably the best way, but can it get to a point where you have too many models?
At the moment the whole site has 6 controllers, 17 models and 25 views
Yes you can technically have too many models, there is a limit (as always) of how many classes can exist in PHP. But it's pretty large, so keep on going. You can not only have many but also different kind of models at once. So keep on going, don't restrain your coding by thinking there might be a limit you don't see so far.
So not the count of files, but how nicely written your code is, e.g. is everything grouped properly that belongs together? See as well Domain Model.
I suggest you let ModelController deal with actions that somehow modify Model.
I.e. FavoritesController deals with adding, removing and showing favorites (stored on FavoritesModel). Keeps your controllers lean/slim, is a lot easier to test (less methods) and keeps logical app parts together.
Also, you could divide the application into smaller apps that deal each with:
Auth/Login
Social/Sharing
add/read/show articles (main app)
In such scenarios there is no "right" answer, so all I can give you is my own interpretation. I would use a service to do bind one or more models together. So, a User service would use the User model and the Favourite model to manipulate and display user favourites.