Structuring Zend_Acl and CRUD with Parent/Child Relationships - zend-framework

I am wondering how should I structure my ACL for CRUD with Parent/Child Relations.
Eg. Projects have TodoLists. TodoLists have Todos
There are various controller actions for project
/projects/add
/projects/edit/{projId}
/projects/delete/{projId}
/todo-lists/add/{projId}
/todo-lists/edit/{todoListId}
...
As you can see going down the hierarchy, some actions have ids that refer not to themselves (eg. todo-lists controller -> todo-list resource) but to their parent
So with with I have setup (generally), it looks like this
ACL Controller Plugin (preDispatch)
Set role to loggedin user or 'unauthenticated'
Set resource to controller name
Set privilege to action name
if request param 'id' is set, get the actual entity (I am using Doctrine ORM) that implements Zend_Acl_Resource_Interface. Here is where the complication arises. I will usually get the resource from the controller name, but for eg. with /todo-lists/add I must know to get the parent entity instead (Project). With this setup, I will have to chage the privilege to to something like 'addTodoList'. This way, the project acl assertion class will have to TodoLists stuff. There will also be a disconnect between Controller Actions & ACL Logic. Is that ok?
Maybe I should have addTodoListAction in ProjectsController instead of TodoListsController? This will simplify my ACL code, I won't need to check and modify resource/privileges? I can just take these directly from the request params (Controller & Action names).
How do you setup ACL's like this?

use Zend_Acl_Assertion , create your assertion for projectid and todoId. At the time of giving permission do
$myAcl->allow($role,'projects','edits',new My_Project_Assertion());
and you cannot use action "addTodoListAction" because of captial letters (or define your own dispatcher) addtodolistAction wd work;

Related

How to implement dynamic creation of permission groups with different set of endpoints Django Rest Framework

In my project I have lot of endpoint views (APIViews, ViewSets). For all of them now I set permissions, some of them are default (e.g. AllowAny) and some are custom created:
permission_classes = (IsUserHaveSomePermission,)
Now I want to implement some flexible system, that will allow me to specify set of allowed endpoints for each user, for example:
On front-end I want to select some user and have a list of checkboxes that correspond to project's endpoints.
This is just an utopian solution, some details may be changed, but the main question is to how make something similar so that admins can basically dynamically change list of allowed endpoints/views for user?
thanks in advance
This solution can be implemented by storing if the user has permission to access the current request method and request path.
Create a new db model for storing the user, request method and request path. Lets say the name of the model is RequestPermission
Instead of the path you can store a constant representing the url so that you have the flexibility of editing the path later on. This constant can be the url name which is supported by django.
class RequestPermission(models.Model):
user = user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='request_permissions')
method = models.CharField(max_length=10)
path_name = models.CharField(max_length=200)
create a custom permission class:
class IsUserResuestAllowed(permissions.BasePermission):
def has_permission(self, request, view):
user = request.user
# you can choose how to get the path_name from the path
path_name = get_path_name(request.path)
return RequestPermission.objects.filter(user=user, method=request.method, path_name=path_name).exists()
Now you can use this class as the default permission class in rest framework settings or use it per view.

How to use DiscoveredResource to traverse to a single entity resource exposed by a RepositoryRestResource

I'm trying to set up a system with multiple applications connecting by use of a discovery server. I can traverse the hal responses to a specific resource, but I'm looking for a solution to get from a collection resource to a single resource and find the data for a specific entity.
In 1 application I have a RepositoryRestResource exposing some object:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {}
In some other application, I would like to get to a single thing. I have the id (let's say it's 1) and have the relation name of the collection and the single resource.
I would like to use a DiscoveredResource to get a link to this single item resource, or to the collection resource which I can then somehow expand using the ID (which would require a templated resource).
If at all possible I would not like to just add "/1" at the end of the URL.
this is how I currently create a DiscoveredResource to point to the collection resource:
new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things"));
Should I and is it possible to add a templated link on a collection resource created by a #RepositoryRestResource. Or is there some other trick I am missing?
The solution here is to add a custom method as a #RestResource which exposes a relation with a templates URL you can then follow to.
Repo:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {
#RestResource(rel = "thing")
Thing findOneById(#Param("id") Long id);
}
Discovery + traverson:
DiscoveredResource resource = new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things","search","thing"));
Link link = resource.getLink().expand(id);

REST - Updating partial data

I am currently programming a REST service and a website that mostly uses this REST service.
Model:
public class User {
private String realname;
private String username;
private String emailAddress;
private String password;
private Role role;
..
}
View:
One form to update
realname
email address
username
Another form to update the role
And a third form to change the password
.
Focussing on the first view, which pattern would be a good practice?
PUT /user/{userId}
imho not because the form contains only partial data (not role, not password). So it cannot send a whole user object.
PATCH /user/{userId}
may be ok. Is a good way to implement it like:
1) read current user entity
2)
if(source.getRealname() != null) // Check if field was set (partial update)
dest.setRealname(source.getRealname());
.. for all available fields
3) save dest
POST /user/{userId}/generalInformation
as summary for realname, email, username
.
Thank you!
One problem with this approach is that user cannot nullify optional fields since code is not applying the value if (input is empty and value) is null.
This might be ok for password or other required entity field but for example if you have an optional Note field then the user cannot "clean" the field.
Also, if you are using a plain FORM you cannot use PATCH method, only GET or POST.
If you are using Ajax you might be interested in JSON Merge Patch (easier) and/or JavaScript Object Notation (JSON) Patch (most complete); for an overview of the problems that one can find in partial updates and in using PATCH see also this page.
A point is that a form can only send empty or filled value, while a JSON object property can have three states: value (update), null (set null) and no-property (ignore).
An implementation I used with success is ZJSONPATCH
Focussing on the first view, which pattern would be a good practice?
My suggestion starts from a simple idea: how would you do this as web pages in HTML?
You probably start from a page that offers a view of the user, with hyperlinks like "Update profile", "Update role", "Change password". Clicking on update profile would load an html form, maybe with a bunch of default values already filled in. The operator would make changes, then submit the form, which would send a message to an endpoint that knows how to decode the message body and update the model.
The first two steps are "safe" -- the operator isn't proposing any changes. In the last step, the operator is proposing a change, so safe methods would not be appropriate.
HTML, as a hypermedia format, is limited to two methods (GET, POST), so we might see the browser do something like
GET /user/:id
GET /forms/updateGeneralInformation?:id
POST /updates/generalInformation/:id
There are lots of different spellings you can use, depending on how to prefer to organize your resources. The browser doesn't care, because it's just following links.
You have that same flexibility in your API. The first trick in the kit should always be "can I solve this with a new resource?".
Ian S Robinson observed: specialization and innovation depend on an open set. If you restrict yourself to a closed vocabulary of HTTP methods, then the open set you need to innovate needs to lie elsewhere: the RESTful approach is to use an open set of resources.
Update of a profile really does sound like an operation that should be idempotent, so you'd like to use PUT if you can. Is there anything wrong with:
GET /user/:id/generalInformation
PUT /user/:id/generalInformation
It's a write, it's idempotent, it's a complete replacement of the generalInformation resource, so the HTTP spec is happy.
Yes, changing the current representation of multiple resources with a single request is valid HTTP. In fact, this is one of the approaches described by RFC 7231
Partial content updates are possible by targeting a separately identified resource with state that overlaps a portion of the larger resource
If you don't like supporting multiple views of a resource and supporting PUT on each, you can apply the same heuristic ("add more resources") by introducing a command queue to handle changes to the underlying model.
GET /user/:id/generalInformation
PUT /changeRequests/:uuid
Up to you whether you want to represent all change requests as entries in the same collection, or having specialized collections of change requests for subsets of operations. Tomato, tomahto.

Logging with Email and Password , Not with Username Yii 2

How I can manage to login with email address,not with the standart (username + password).I enter the website with my Users in my DataBase , but is there a way to change that to be with email address instead of that user name , because when I use Gii , I got a lot of errors , even I try to fix those errors
First, try to locate your SiteController or any other Controller you use for the index route. It should have an action function that corresponds to the login route; it is usually with signature public function actionLogin().
You should see the initialized model (usually, the LoginForm model). The model should have a function for login logic which is checked to determine user authenticity. You should find that this function invokes another login function which requires the User object as first argument/parameter. The function is usually the $this->getUser() function.
Looking into this will point to you a call to the actual data model that fetches user by whatever criteria/property you specify; this can be email or anything else that might not even need be unique but generally, you want to use a unique data property like username and email. This function relies on the User data model. It, by Gii default, calls the function User::findByUsername(search_property)
Yii2 provides a default User model that implements the Identity interface; that's where you want to make the adjustment you need. It should have the required static function findByUsername() or something similar. You would find that Yii2 default searches within static data to find user, you should link that to you (User) data model which I assume you generated using Gii.
My Gii sequence usually looks like such:
List item
Generate the yii2-basic/yii2-advanced using composer
Create Database (I have a user table in there) and set proper db credentials in config/db.php
Rename the default model/User.php to model/OldUser.php
Create Data Models using Gii
Make the newly generated User Model implement IdentityInterface to allow Yii2 freely-given session management by adding implements yii\web\IdentityInterface to the class declaration.
Implement all the required methods of the IdentityInterface. You can check in `model/OldUser.php' for guidance.
Create static functions to findUserByEmail($email) or findUserByUsername($username)
Mine usually look like this
I hope this helps.
I made it just change everywhere where must be email,instead of username,because of Yii default username loggin , thank u for the advices

How to represent a read-only property in a REST Api

if you have a REST API that is hypermedia-driven (HATEOAS) you can easily change a client's behavior by including or omitting links in the response (_links). That enables a client to completely forget about testing permissions for the operations that are possible in the current state of a resource (the link to the operation is present or not).
Additionally you can leave out properties in the response if the current user doesn't have permission to see it.
That way authorization is done entirely on the server (and controls actions and properties that are eligible to execute/view).
But what if I want to a have a read-only property? It is no problem for the REST API to ignore the property if it is present in the request (_POST_ OR _PUT_). it just won't get saved. But how can a client distinguish between write and read-only properties to present the user appropriate controls (like a disabled input field in HTML)?
The goal is to never ever have the client request a user's permissions, but to have a completely resource driven client/frontend.
Any help is greatly appreciated :-)
If I misunderstood your question, I apologize upfront. With that being said...
But how can a client distinguish between write and read-only
properties to present the user appropriate controls (like a disabled
input field in HTML)
Well, there are multiple solutions to this. The simplest one I can personally think of is to make each property an object having a simple structure of something like:
...
someProperty: {
value: 'some value',
access: 'read-only'
},
someOtherProperty: {
value: 'some value',
access: 'write'
}
...
You can obviously get as creative as you want with how you represent the "access" level of the property (using enums, booleans, changing access to be isReadOnly or whatever).
After that, the person using the API now knows they are read-only or not. If they submit a "write" value for a "read-only" property as part of the POST payload, then they should expect nothing less than a 403 response.
Edit:
In case you can't alter the properties in this manner, there are a number of other ways you can still achieve this:
write documentation that explains what access each property has
create a route that the user can submit 1 or more properties to in order to receive a response that indicates the access level of each property (response: { propName: 'read-only', propName2: 'write', etc.)
Return a propertyAccess map as part of the response (mapping properties to access levels).
end of the day, you just need a way to map a property with an access level. however that's done depends on what your restrictions and requirements are for the api, what changes you can make, and what is acceptable to both your client(s) and the business requirements.