I have several Rails models that I'm trying to expose via a REST api. I'm looking for a simple way to allow mass assignment in certain contexts (through the api or admin interface) but to disallow when populating from user based forms.
There are a few catches as well. First, I'm populating a bunch of child objects using accepts_nested_attributes_for. Second, I'm using resource_controller plugin which automatically applies params correctly for you in standard update case. The api controllers are in their own "namespace" so I'm open to something DRY that could be implemented in a base controller.
Several solutions come to mind but no clean solution presents itself at the moment. Any suggestions?
You can use a mixed solution.
That said, you can tweak ActiveRecord in order to allow mass assignment depending on user role.
You can implement the system yourself or use an existing plugin, such as safe_mass_assignment.
Related
I'm looking for the best way to solve a problem.
At this moment I have a site for a customer, example.domain.com
My customer ask to create another website with some changes in design, but the contents are the same of the first website. I don't want to duplicate the website, because every feature I add to the site A must be deployed also to site B, and I'm looking a smart way to handle the situation.
I need to keep two different domains and I need also custom mailers and other small tweaks in the controllers (and maybe in some models).
My idea is to put in application controller a before filter like this
before_action :detect_domain
private
def detect_domain
case request.env['HTTP_HOST']
when "example.domain.com"
request.variant = :host1
when "example1.domain.com"
request.variant = :host2
end
end
Then I use the variant with some conditional to choose the mailer, to customize the views and to apply some code changes.
Any other idea?
Using a before filter and a per-request variable like your proposal will work, with a couple caveats that I'll mention below. I'd recommend a tool like the request_store gem to actually store the per-request value of which "skin" is selected.
Now, for the caveats. First, the main problem with per-request variables is that your Rails app does not always exist in the context of a request. Background jobs and console sessions operate outside of the usual request/response flow of your app. You will need to think about what happens when your models or other non-controller/view code is executed when that variable isn't set. I would suggest simply not having your models depend on RequestStore at all -- have the controllers pass any request-specific information down into the models, if needed.
Secondly, it's not clear from your description if you want any data or logical separation between the two domains, or if you just want different look-and-feels. If the former, you might consider the apartment gem, which aims to make database multi-tenancy easier.
EDIT: I also want to mention that, as an alternative to the multi-tenant solution above, you also have the option of a multi-instance solution. Wherein, you use an environment variable to indicate which version of the site should be displayed, and spin up multiple instances of your app (either on the same server with a reverse proxy, or on separate servers with separate DNS entries or a reverse proxy). The downside is increased infrastructure costs, but the context problem I mentioned above no longer exists (everything always has access to environment variables).
If I want to build a Rails app that has two different types of users, let's say one type is called players and the other one is owners, what is the best and most efficient approach to modeling the app?
Things to take into account:
There should only be one Login, but different Registration forms that Owners/Players can use.
Owners can have access to a control panel but Players cannot.
Owners cannot share any of Players capabilities, but both need to be able to perform Login/Registration.
I am not using Devise, so please do not suggest it.
Different Approaches I've considered:
Using cancancan gem, but it does not really seem to meet my needs in the sense that I am not looking to create a user/admin hierarchical approach but rather a if you're a Player, then you can see these pages and perform these actions but Owners cannot and vice versa. Almost like splitting the app in two. cancancan seems that it would treat Owners as "Players with extra privileges", not different privileges entirely.
Creating separate models with separate login and registration forms, which seems like a disaster waiting to happen. One small mixup between a Players table and the Owners table, especially with the primary keys, and that will be a world of trouble where people could end up logging in to the wrong accounts.
Creating a polymorphic or has_one relation toward an Account model, which so far, seems like the best way to probably go about it. If I created a polymorphic Account model, I can store different types of Players/Owners, but how could I compare login credentials against all types?
I had been trying to find something on this matter regarding how to map this out and was surprised to not find an information on how to do this without using Devise. If anyone has any good links they can point me to that also address this matter (without Devise), please leave them in your answer! Thanks.
I'd suggest one User class with a type attribute that determines whether the user is a Player or an Owner (single table inheritance). This way you keep the registration logic in one place but can customize the forms depending on the user's class.
There must be alternatives to cancancan that help with what you want to do, or you can implement helpers yourself:
def can_access_control_panel?
current_user.is_a?(Owner)
end
You have to have a way to separate one user from another. One way is to add an attribute to the User table so you can call current_user.role and it will return "owner" or return "player".
I have used Pundit gem in the past. It lets you define which controller actions the current user is allowed to access. So as you create resources for your application, you can add a policy that specifies who is allowed to that given resource. This is the repo to the application.
This answer might help you.
So, I've implemented some permissions between my users and the objects the users modify.. and I would like to lessen the coupling between the views/controllers with the models (calling said permissions). To do that, I had an idea: Implementing some of the permission functionality in the before_save / before_create / before_destroy callbacks. But since the permissions are tied to users (current_user.can_do_whatever?), I didn't know what to do.
This idea may even increase coupling, as current_user is specifically controller-level.
The reason why I initially wanted to do this is:
All over my controllers, I'm having to check if a user has the ability to save / create / destroy. So, why not just return false upon save / create / destroy like rails' .save already does, and add an error to the model object and return false, just like rails' validations?
Idk, is this good or bad? is there a better way to do this?
Let the controller check the user's privileges. To have the model handle authorization logic would lead to more coupling (just in different places, and there'd still be coupling to the controller to get the current user). Checking privileges isn't really internal logic to a model.
Simile: Imagine if it was a file's responsibility to check whether you can read/write to it, instead of having the OS (which is the mother of all controllers, really) handle the access.
If you want cleaner controllers you can (for instance) make some generalized before_filters that restrict access to CRUD actions based on the current user.
Flambino gave you the party line :) but let me add my 2 cents.
It is certainly possible to consider access control logic as part of models.
Model validations is a way of checking the manipulating user's rights on crud actions. A downside of this is exactly the fact that access logic usually generalizes across models, therefore we like to abstract them out like in a neat cancan ability file.
You can actually have your cake and eat it by making your manipulation itself a polymorphic association on models. This is how most audit/version control systems are implemented.
https://github.com/collectiveidea/audited
You could extend it and put your access control logic in audit class validations, so it is in one place.
Audited gem also offers a cunning way to pass the current user to the model level by using an observer as around filter on a controller.
https://github.com/collectiveidea/audited/blob/master/lib/audited/sweeper.rb
but there are other methods too https://github.com/bokmann/sentient_user often considered hacky.
Caveats
In either case you will have to guard against situations when models
are manipulated outside the request cycle (in a background routine,
cronjob, console) when current_user is ill-defined.
You usually do not want to treat access violations with validation
errors but use standard http status code responses.
Third, in a web app setting, you usually manipulate objects via forms
and typically you can already controll access to say an update form
in fact usually coupled with the same access rights as update itself
(cancan even have general alias for new/create and edit/update) so if
the latter is handled with validations, this generalized access logic
will be lost. Not to mention that read-access logic is impossible to
handle with model validations.
hope this helps
I'm looking for a plugin that provides a role based authorization mechanism for securing read/write access to attributes. I'm picturing something along the lines of declarative_authorization for white listing attributes of model objects. I've spent some time looking around but have come up short, does anyone know of anything?
EDIT: I'm using declarative_authorization to control which users have access to what actions in the controller, but I need something similar that provides access control to the attributes of each model object. I'm trying to prevent information leakage through the web API or users from crafting malicious posts. I can do this through the mass_assignment_authorizer but I was hoping some plugin did this already.
CanCan Branch 2.0
https://github.com/ryanb/cancan/tree/2.0
Edit:
The continuation of CanCan is called CanCanCan.
See https://github.com/bryanrite/cancancan
What about creating a model / controller pair for each controller, and then allowing each role only to access methods in its controller(s)? Then you can make a before_filter in each controller that makes sure each user has permission to use methods in that controller.
Perhaps you can help me think this through to greater detail.
I need to build or make available a uri for a model instance that can be referenced or used by another application which may or may not be a rails application.
e.g.
I create a standard Post with content; I want to build a URL for that post another application can consume or reference by looking at the model in the database (or another less sticky fashion). Datamapper has a URI field, I want to build a canonical uri, store it there and have another application be able to access, announce, manipulate, etc.
Basically, I have several applications that may be in different places, that need to access the same model, to do differing things with the model. I need a way to make that happen clearly without putting them all in one monster application.
I've looked at Pubsubhub, RSS, etc. but haven't found any concrete examples of what I'm trying to do. Do I need to create an common API for the applications, etc?
DataMapper is very flexible about using existing databases.
Many people come to DataMapper because it can create and tear down the database structures without migrations. However, you do not have to work with it in that way.
I have had good success with using a large set of models owned by a central 'housekeeping' app and then declaring a small subset of the same models in separate 'interface' apps.
Some trial and error is required to figure out what works but it can certainly be done. I'd suggest putting your models in modules and including them across apps if possible.
A final point it sounds like you want URIs/URLs to be the primary interface. If that is the case I strongly suggest you look at Sinatra. It is entirely oriented around URLs (and I find Rails routes very obtuse).