Having this code in a Mojolicious App:
my $svc = $authorized->under('/cleaning')->to('Login#has_role', roles_allowed => ['office', 'booking', 'reception']);
$svc->post('/new') ->name('create_svc')->to('Cleaning#create');
$svc->get ('/edit/:id') ->name('edit_svc') ->to('Cleaning#edit');
$svc->post('/edit/:id') ->name('update_svc')->to('Cleaning#update');
What is the most fundamental and simple way to restrict the update_svc route to office and booking? In other words: All users having the office or booking role should be able to send the changing post requests whereas the cleaning users should only be able to view the form.
I have found this solution myself:
my $svc_read = $authorized->under('/cleaning')->to('Login#has_role', roles_allowed => ['office', 'booking', 'reception']);
my $svc_write = $authorized->under('/cleaning')->to('Login#has_role', roles_allowed => ['office', 'booking']);
$svc_write->post('/new') ->name('create_svc')->to('Cleaning#create');
$svc_read ->get('/edit/:id') ->name('edit_svc') ->to('Cleaning#edit');
$svc_write->post('/edit/:id')->name('update_svc')->to('Cleaning#update');
But may be there are more generic solutions which work better, if you have many get/post routes already available in your app.
Related
I want to provide a me endpoint because not everyone should have access to all resources. The current logged in user should only have access to his own resources. So let's assume you would have a simple Todo REST API with an endpoint returning all tasks from a single user
GET /users/{username}/tasks
The current logged in user should not be able to get information about other users. A possible solution for this would be
GET /users/me/tasks
This has already been discussed here
Designing URI for current logged in user in REST applications
The problem is that I also want to keep the endpoint from above for development purposes (a private/hidden endpoint). Unfortunately both endpoints match the same route. So me could be the username.
I don't want to prevent a username called me with an if-statement like
if(username == "me")
throw new ConflictException("Username 'me' is forbidden");
I think this would be bad design. A possible solution would be to avoid embedding me in a resource and instead embed the resources in me endpoints. So instead of
GET /users/me/tasks
GET /users/me/orders
POST /users/me/tasks
PATCH /users/me/username
DELETE /users/me/tasks/{taskName}
I could remove the users resource and make me the initial base resource. As you might guess /users/:username extracts the username from the url parameter and /me extracts the username from the json web token but both endpoints run the same logic.
So when I want to keep private/hidden endpoints to fetch a user by username do I have to make me a separate resource? Or is there a way to keep me only as a replacement for the username parameter?
EDIT
I tried to create a simple route example. As you can see most of the endpoints of users and me are mirrored and only differ by the user identification (username as parameter or jwt payload). And for this sample only the me endpoints are accessible to everyone. Other endpoints are private / hidden.
users
GET / => get users => private
GET /:username => get user => private
GET /:username/tasks => get tasks from user => private
GET /:username/tasks/:taskName => get task from user => private
POST /:username/tasks => create user task => private
PATCH /:username/username => update username => private
DELETE /:username => delete user => private
DELETE /:username/tasks/:taskName => delete user task => private
tasks
GET / => get tasks => private
me
GET / => get current logged in user => public
GET /tasks => get tasks from current logged in user => public
GET /tasks/:taskName => get task from current logged in user => public
POST /tasks => create task for current logged in user => public
PATCH /username => update username => public
DELETE / => delete current logged in user => public
DELETE /tasks/:taskName => delete task from current logged in user => public
I think you are getting confused because your definitions are a bit tangled.
Here's the good news: REST doesn't care about the spelling conventions you use for your identifiers. So if it makes your life easy to have
GET /users/12345/tasks
GET /me/tasks
Then you can implement both of those, make sure that the access restrictions are correct, and off you go.
There's nothing wrong, from a REST perspective, about using
GET /users/12345/tasks
GET /users/me/tasks
The spellings of the target-uri are different, which means that from the perspective of a general-purpose client they identify different resources.
BUT... the routing implementation that you are using to implement your API may not allow you to easily distinguish these two cases. What you have effectively got here are two different matches for the same pattern, which requires some extra clever to remove the ambiguity; if your routing library/framework doesn't offer that, then you are going to have to shim it in yourself.
And, as you note, you've got a huge mess if the token "me" itself could match a real identifier.
REST doesn't have a concept of "base resource" - at least, not one that lines up with what you are thinking about. There's no inherent relationship between /users and /users/12345 from the REST perspective. Many server frameworks do treat request handling as a hierarchy of "resources", but again: that's an implementation detail of your particular server.
REST really only cares about your interface - that you understand HTTP requests in the standard way.
Which all brings us back to the good news; since REST doesn't care what spelling conventions you use, you are welcome to make any arbitrary choices you like... including whatever spelling convention makes your internal routing framework easy to work with.
So if your routing framework is telling you to create a different "initial base resource" to support your family of "me" endpoints, then just do that.
Hi I'm building REST api for an app, I have a requirement in URL
such that url should be something like this e.g
www.abc.com/api/param1/value1/param2/value2/param3/value3.... and so on
There are cases
case: The number of params are not limited it can change frequent
if today it is something like this
www.abc.com/api/param1/value1/param2/value2/param3/value3
tomorrow it can be like this
www.abc.com/api/param1/value1/param2/value2/param3/value3/param4/value4
Is there a configuration where once you configure the url pattern
and every thing go smooth
and in conrtoller params should contain this kind of key-value pair
{ "param1" => "value1","param2" => "value2","param3" => "value3"...and so on }
any suggestion !! how to achieve this ??
If your params are not fixed you can use wildcard in routing
for e.g
get 'items/list/*specs', controller: 'items', action: 'list'
def list
specs = params[:specs] # e.g, "base/books/fiction/dickens" #split it and place in a hash
end
Rails routing provides a way to specify fully custom routes with static and dynamic segments as explained in the Rails Routing Guide.
Your requirement should be achievable with
get '/api/param1/:param1/param2/:param2/...', to: 'controller#action'
You can use route scoping for this particular kind of problem . In other way it is nested routes
More details : http://guides.rubyonrails.org/routing.html#nested-resources
This is a example,
GET /magazines/:magazine_id/ads/:id/edit ads#edit
return an HTML form for editing an ad belonging to a specific magazine
I think this would be helpful for you.
I want to create some routes with restrictions by role. Something like this:
my $auth = $app->routes->under->to('auth#check');
$auth->get('/list')->to('some#list')->name('list');
$auth->get('/add')->to('some#add', roles => ['user', 'admin'])->name('add');
I don't have any problem with checking roles in after_dispatch hook. But I cannot access this data when I'm trying to create a links for this routes.
For example, I'm a guest on /list route and want to form menu with available links. So, I have to check roles from route /add to decide to show this link or not.
For this moment I found only one way to get default data from route with name:
app->routes->lookup('add')->pattern->defaults->{roles}
And it looks like a hack. How I can do this in a right way?
You are right. This method to get defaults of the route is documented here
my $defaults = $pattern->defaults;
$pattern = $pattern->defaults({foo => 'bar'});
I'm trying to use the vBulletin REST Mobile API to simply register.
The sourced are installed on my local machine and according the documentation https://www.vbulletin.com/forum/content.php/393-User-Registration-Process-Mobile-API
This procedure should not be so hard, especially without humanity and COPPA authentication.
However I've stacked!
The method definition describes "addnewmember" clear, so I've generated a test link, which should do the job.
https://www.vbulletin.com/forum/content.php/365-User-Related-Methods
The link is:
.../forum/api.php?&api_m=register_addmember&api_c=1&api_s=76ec9eec61e7fdfef2f3feee28d5f392&api_sig=8fe54313b333cc0fef4ddd8e398b5c80&api_v=6&agree=1&username=testuser&email=XXXXXX%40gmail.com&emailconfirm=XXXXX%40gmail.com&password=12345678&passwordconfirm=12345678
As a response I get: register_not_agreed
The Docs: register_not_agreed
The agree parameter should be set to 1.
Which is also clear - agree parameter was not there.
Here comes the funny part - In the API-Log I can see that the 'agree' parameter is correctly passed
*1 test_client Gast 13:23, 18.06.2012 register_addmember Array ( [api_m] => register_addmember [api_c] => 1 [api_s] => 76ec9eec61e7fdfef2f3feee28d5f392 [api_sig] => 8fe54313b333cc0fef4ddd8e398b5c80 [api_v] => 6 [agree] => 1 [username] => testuser [email] => ....*
Is there anybody with experience with the Mobile API that could help?
I don't know why it does not work with a pure GET call but I'm sure it will work (because I'm working on a vBulletin API client in Python and I did it this way) if you:
use GET parameters to send api_c, api_sm, api_m, and api_sig
use POST data for all the rest (username, email, agree, etc)
I develop and maintain a customer portal, written in Perl/Catalyst. We make use of the Catalyst authentication plugins (w/ an LDAP storage backend, coupled with a few deny_unless rules to ensure the right people have the right group membership).
It's often that in managing a customer's permissions, we have the need to test out a user's settings before we hand things over. Currently, our only recourse is to reset a user's password and log in ourselves, but this is less than ideal, particularly if the user has already set their own passwords, etc.
My question is this: for Catalyst, has anyone come across a method of impersonating a user account such that, given the correct super-admin privileges, one could impersonate another account temporarily while testing out a setting, and then back out once done?
If not in Catalyst, then how have people approached this in other frameworks, or their own custom solutions? Admittedly, this is something that introduces a potentially egregious attack vector for a web application, but if forced to implement, how have people approached design for this? Perhaps some serious cookie-session-fu? Or possibly an actualID/effectiveID system?
We use a custom authenticator controller, a custom user class (MyApp::Core::User) and several realms:
package MyApp::Controller::Auth;
...
sub surrogate : Local {
my ( $self, $c ) = #_;
my $p = $c->req->params;
my $actual_user = $c->user; # save it for later
try {
$c->authenticate({ id=>$p->{surrogate_id} }, 'none');
$c->session->{user} = new MyApp::Core::User(
active_user => $actual_user,
effective_user => $c->user );
$c->stash->{json} = { success => \1, msg => "Login Ok" };
} catch {
$c->stash->{json} = { success => \0, msg => "Invalid User" };
};
$c->forward('View::JSON');
}
In myapp.conf I use something like this:
<authentication>
default_realm ldap
<realms>
<ldap>
# ldap realm config stuff here
</local>
<none>
<credential>
class Password
password_field password
password_type none
</credential>
<store>
class Null
</store>
</none>
</realms>
</authentication>
That way we're creating a normal Catalyst user object, but wrapping it around our custom user class for more control. I probably could have created an specialized realm for surrogating, but I've chosen using my own user class instead. It was done a while back and I can recall why we did it that way.