How do you deal with a :create permission in cancan that's defined by the parent object? - cancan

Let's say you're writing the software for Blogger.
Each user can create a blog post only if they are the owner of the blog. CanCan would normally define an ability check in this circumstance as:
user.can? :create, Post
However the user can only create the post if they are the owner of the current blog and there's no way to reference the current blog using only its classname. What I really need to be able to do is:
user.can? :create, Post, #current_blog
such that in the cancan definitions I can say
can :create, Post do |post, blog|
user == blog.owner
end
Is that possible or am I confused in how I'm approaching this?

Define an ability based on the parent of the current object:
can :create, Post, :blog => { :user => user }
This will make sure the current user can only create a new Post record for a blog that they are an owner of.
uses Post.blog_id to find parent Blog record
compares Blog.user_id field to current_user.id field
Also make sure that you are using a :through declaration when loading and authorizing your Post resource, so CanCan knows who the parent record is.
PostsController:
load_and_authorize_resource :through => :blog
See the CanCan wiki on GitHub for more detail.

You can use
user.can? :create_posts, #current_blog
or
user.can?:update, #current_blog
instead of
user.can? :create, Post, #current_blog
. Of course, you probably need to take out new and create action from load_and_authorize_resource. And test "authorization" by yourself
def new
unauthorized! if cannot? :create_posts, #current_blog
end

Related

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.

What's the correct URI for a PUT REST request?

What's the correct URI for a PUT REST request if i want to edit/update only one article?
Is it:
// PUT api/articles
or:
// PUT api/articles/id
UPDATE
I don't know if i can send the article id from the body with all other attributes (title, etc.) and emit the id from the URI or should i add it to the URI as well. But i guess that i got my answer already. Will have the id in the body and URI i guess.
If you're following REST principles, then the correct answer is, "Whatever you got back in the Location header from the POST request that created the resource". The basic idea is that the server assigns a URI path as the resource identifier when the resource is created. That path is what you then use in subsequent requests, including GET, PUT and DELETE, when you want to refer to the same resource.
If the resource you want to update is retrieved by doing a GET to /api/articles, then use /api/articles for your PUT request too. If you GET /api/articles/id, then use PUT /api/articles/id to make changes to it.
Based on what you are saying, I'm guessing you want to update or create a new article, not replace all articles.
PUT is used to replace the resource that you are targeting. This means that:
PUT api/articles
creates or replaces all your articles, and:
PUT api/articles/id
Creates or replaces the article 'id'.

Best approach for updating a relation to another resource in a REST API

Let's say I have a REST API adhering to basic HATEOAS principles. Items belong to a User.
GET /item/13
{
id: 13,
name: 'someItem',
type: 'someType',
_links: [
{
rel: 'user',
href: '/user/42'
}
]
}
Now I need a way to change the user for a given item. Using either a PUT or a PATCH, which is the preferable way of performing that modification?
Establish the new relation by setting the id of the new linked resource as a simple property in the JSON body
PATCH /item/13
{
userId: 43
}
Establish the new relation by having the client pass the link itself as the input
PATCH /item/13
{
_links: [
rel: 'user',
href: '/user/43'
]
}
I usually think of links as read-only representations of relations stored in other formats (such as id:s to other resources), returned from GET calls. It doesn't feel very natural to me to have links as input to POST/PUT/PATCH calls, and the fact that links is an array makes it even stranger (should you be able to update all links? One single link?), but I have seen it suggested in various articles. Is there a best practice? What would be the benefits of using the links approach?
The point of REST is (at least one of them) is to make everything visible through a standard interface. In other words, if the 'relations' are a thing, than it too should have its own resource.
The API should also be more descriptive. This might be subjective, and I don't know all the details of your model/design, but 'items' don't have 'links'. 'Items' might instead have a single 'owner'. If this is the case, it might look something like:
GET /item/123/owner
So POSTing or PUTing an URL of a user (or some simple representation) would 'change' the owner of the item. It might be not allowed to DELETE the owner, depending on if the model allows unowned items.
Note, that the representation under "/item/123" would in this case have to link to "/item/123/owner", since the client only follows links it gets from the server.
So, think about what are important 'things', all of those should have a resource. Also, try to add as much 'meaning'/semantics as you can. The relation should not be called 'user', it should be called 'owner' (or whatever the meaning should be in your model).

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.

How to display just students in users page(Moodle:/admin/user.php)

I want to display just students in moodle users page without manually adding a filter using the web interface. Could anybody help me on how to do?
You can create a link that shows only students but you'll need to post the data. Here's how I did it.
If you install WebDeveloper - http://chrispederick.com/work/web-developer/
Then go to /admin/user.php and click WebDeveloper, choose the "Forms" tab and "convert POSTS to GETS".
Choose the filter and click add filter. This will display a url with all the form fields. Moodle is expecting a POST rather than a GET though.
So copy and paste the url into a variable and use the single_button function with the post method eg:
$url = '/admin/user.php?sesskey=xxxxxxx&_qf__user_add_filter_form=1&mform_showmore_id_newfilter=1&mform_isexpanded_id_newfilter=1&realname_op=0&realname=&lastname_op=0&lastname=&firstname_op=0&firstname=&email_op=0&email=&city_op=0&city=&country_op=0&confirmed=&suspended=&profile_fld=0&profile_op=0&profile=&courserole_rl=5&courserole_ct=0&courserole=&systemrole=0&cohort_op=2&cohort=&username_op=0&username=&auth=&deleted=&addfilter=Add+filter';
echo $OUTPUT->single_button($url, get_string('student'), 'post');
Copy and paste your own url rather than the above.
You can probably remove a lot of the parameters. You should also get the roleid for the student and use the moodle_url function, eg:
$studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
$params = array('courserole_rl' => $studentroleid, 'courserole_ct' => 0, ... );
$url = new moodle_url('/admin/user.php', $params);
echo $OUTPUT->single_button($url, get_string('students'), 'post');
This is not possible, as, in Moodle, it is rare for someone to be assigned the role of 'student' at the system level (and if you did, they would have access to every course on the site).
The concept of 'student' in Moodle only makes sense at the course level.