How to limit who can call a method in ruby - ruby-on-rails

I'm looking for a good ruby way to limit who can call a method. The example occurs within a rails environment but its not specific to rails.
I have a Model called Document that handles finding documents within folders. Folders are permissioned so I have a class that handles Permissions. In heavily simplified form Permissions exposes the interface to retrieve Documents:
class Permissions
def documents(folder_list)
#strip folders from folder_list user doesn't have permissions for
Document.where("folder.name in (?)", permissioned_folder_list)
end
end
Functionally this works fine but rspec testing is a nightmare when the querying is much more complex than this simplified example. We end up with lots of expectations that are to do with the mechanics of how Documents are stored. Really I want to have something more like this:
class Permissions
def documents(folder_list)
#strip folders from folder_list user doesn't have permissions for
Document.documents(permissioned_folder_list)
end
end
class Document
self.documents(folder_list)
Document.where("folder.name in (?)", folder_list)
end
end
Which would be nicely factored and easy to test. The trouble is this now provides an interface on Document which looks like a nice domain-level interface but completely bypasses permissions. Its very easy for someone to come along and use this and get reasonable-looking results which are wholly incorrect.
What I'd like to do is prevent the Document::documents method from being called by anything other than an instance of Permissions. Its not foolproof as you can still call all or where and bypass permissions but then you have to recreate all the complex query logic when there is a method that clearly handles all that for you, but you can't call it unless you go through Permissions.
Whats the idiomatic ruby way of doing something like this? BTW, because permissions are stored in the user's sesion session, calling Document and having it call Permissions rather than calling Permissions and having it call Document is pretty messy.

Is there some reason the private keyword is not sufficient?
You seem to not want another class for the private methods. Why not just make them instance methods that follow the private declaration?
If you need to use session from somewhere other than a controller, pass it as an argument.
class Permissions
def documents(folder_list, session)
_documents(folder_list, session)
end
private
def _documents(folder_list, session)
Document.where("folder.name in (?)", folder_list)
end
end
Not sure if I'm missing some part of your question.

Related

How to get Pundit to apply a scope to a model with a different name than the policy?

I have a DirectoryController, and -- deliberately -- no UsersController. UsersController will doubtless be added when I create the administration controls, and will have a totally difference scope & purpose.
When I call policy_scoped in the directory controller, I want to use the scope from DirectoryPolicy and not UserPolicy (which doesn't exist yet). I want to do it in a way that verify_policy_scoped recognizes, which means the obvious work around of DirectoryPolicy::Scope.new(current_user, User).resolve is not only a tad long but also actually doesn't work. (Which seems like a bug) This seems like such an obvious oversight that I'm sure there's something, somewhere, to make this work, I just don't know what.
How do I do this in Pundit?
When you do this
DirectoryPolicy::Scope.new(current_user, User).resolve
you're effectively bypassing pundit's policy_scoped? tracking.
My first thought is that if DirectoryPolicy::Scope is intended to scope your user data, maybe you need to create a Directory model as pundit expects. It could be as simple as this
class Directory < User; end
Now when you have something like this in your DirectoryController
#users = policy_scope(Directory)
pundit can properly infer you want the DirectoryPolicy::Scope, and your resolve method can treat the scope as if it were the User class because of the inheritance.
If you can't subclass User as I describe here you'll need to show some actual code from your app and provide more context as to what Directory is and what you're trying to do with it.

Dynamic roles and permissions system in Rails app

I need to create roles based permissions systems in my Rails app. I would be totally happy with CanCan, but the main problem - it has to be dynamic, so that Admin has to be able to assign permissions and to create new roles. The permissions can be simple controller/action restrictions, and can be data related, for example some users can edit only their own profiles, and some of them can edit the profiles of all the users in the particular group. And it would be really nice to allow Admin to create new permissions.
What I'm thinking about is to store in db a controller/action, and some data related restrictions (I'm really confused here about the way to define them). So could you please give me some advice, what would be the best way to organize permissions?
Any thoughts are much appreciated
If you like CanCan, then I think is best to use it. Here is a short tutorial about storing abilities in database so non-programmers can update them:
https://github.com/ryanb/cancan/wiki/Abilities-in-Database
If you really, really want to implement such system yourself. Depending on your needs, I will suggest for you to implement it as simple as possible.
In the case you need only users to have access to modules(certain controllers). You can do:
Store all users permissions in just like serialized fields -> http://apidock.com/rails/ActiveRecord/Base/serialize/class
class User
serialize :permissions, Array
def access_to?(module)
permissions.include? module.to_s
end
end
some check when setting this field would be nice.
Just make a check on top of every controller if the current user have access to this controller(section)
class ApplicationController
private
def self.require_access_to(module)
before_filter do |c|
unless c.send(:current_user).try :access_to?(module)
c.send :render_no_presmissions_page
end
end
end
end
class AdminNewsController
require_access_to :news
end
Of course this is just a start position, from where you can easily evolve.
[EDIT The link given by #RadoslavStankov is a better answer than this.]
You can do it with CanCan easily. Just make a model for permissions (which has_and_belongs_to_many :users), and in your Ability#initialize load the permissions appropriate to the user from the model and say that the user can do them. Then make appropriate user interface for administrating that model.
Something like this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
user.permissions.each do |permission|
can permission.symbol, permission.scope
end
user.prohibitions.each do |permission|
cannot permission.symbol, permission.scope
end
end
end
where scope returned something like :all for nil scope, or Object.const_get(#scope_name) otherwise... Play with it. Anyway, it's doable.
More complex CanCan things are likely to be more complex (duh) - for example conditional permissions - but that would be a bitch to administer as well, so I think this should be enough for most applications.

Read only mode for Ruby on Rails application

I have an interactive Ruby on Rails application which I would like to put into a "read only mode" during certain times. This will allow users to read the data they need but block them from actions that write to the database.
One way to do this would be to have a true/false variable located in the database that was checked before any write was made.
My question. Is there a more elegant solution for this problem out there?
If you really want to prevent any database write, the easiest way I can imagine would be to override the readonly? method of a model to always return true, either in selected models or maybe even for all ActiveRecord models. If a model is set to readonly (normally done by calling #readonly! on it), any try to save the record will raise an ActiveRecord::ReadOnlyRecord error.
module ActiveRecord
class Base
def readonly?
true
end
end
end
(actually untested code, but you get the idea…)
Zargony's solution seems to be the best one, but I would like to add to it a bit.
So, about his code:
This works nicely. A good solution is to add this in an initializer and run this code only if an env var is set, so that you can choose whether to run the app in read-only mode on launching the app.
if ENV['READ_ONLY'] == 'true'
module ActiveRecord
class Base
def readonly?
true
end
end
end
end
And then run the server from command prompt like READ_ONLY=true bin/rails s. Also, adding
rescue_from ActiveRecord::ReadOnlyRecord, with: ->() {
flash[:alert] = "The site is running in read-only mode. We are going to return to full operation soon. Thank you for your patience!"
redirect_to root_path
}
to the ApplicationController (that all of your controllers should inherit from) is a nice way to show the users what is going on.
Another good one which I liked a little better is Declarative Authorization
which is covered by Railscasts as well: Railscasts - Declarative Authorization
Permissions plugin? Something simple like cancan where you define what a user can do, when. It will allow you to display links, or not, and restrict access to controller actions. The railscast will explain better than I can.
http://github.com/ryanb/cancan
http://railscasts.com/episodes/192-authorization-with-cancan
The answer by Zargony will work well but raise an exception if your application is trying to write anything. If you want your application to fail silently on writes so that it doesn't show error pages on each operation (e.g. if you update a timestamp on login in your code, you will get an exception), you can use the approach below:
unless Rails.env.test?
class ActiveRecord::Base
before_save do
raise ActiveRecord::Rollback, "Read-only"
end
before_destroy do
raise ActiveRecord::Rollback, "Read-only"
end
end
end

Rails saving IP address with every create/update request

I'd like to do the following:
define a before_filter in application.rb that extracts the user's IP address and stores it anywhere, preferably in the session.
define two before filters in all my models as before_create and before_update that add the current user's IP to the object to be stored.
Problem: I cannot access session[] neither env[] in a model. Can anyone help with a standard solution that I don't know yet?
Regards
Jason
Try this. In your user model add a class attribute accessor
cattr_accessor :current_ip
In your application controller add:
before_filter :set_current_ip
protected
def set_current_ip
User.current_ip = request.env['REMOTE_ADDR']
end
Then in your model you should be able to just call User.current_ip
We do something similar to get the current_user object passed through.
You're having trouble doing what you want because Rails is designed not to allow you to have access to session information in your models. It's the classic separation of concerns with MVC. Models are meant to work independently of your other layers, and you'll be thankful they do when you start doing things with Rake or other system tasks where you won't have a session.
The
cattr_accessor :current_ip
is a horrible approach. It's a hack and it should be apparent why. Yes, it may work, but it's the wrong approach to this problem.
Since you're tracking "who" did "what" by their IP, the logical place for this to happen is in the controller layer. There are several approaches you can take, including using CacheSweepers as auditors, as outlined in the Rails Recipes book. CacheSweepers can observe models but also have access to all controller information. Using the ditry attributes in a rails model, you can see exactly what changed.
#user = User.find_by_login "bphogan"
#user.login = "Brian"
#user.save
#user.changed
=> ["login"]
#user.changes
=> {"login"=>["bphogan", "brian"]}
#user.login_was
=> "bphogan"
Combine this with the session info you have and you have a pretty awesome auditor.
Does that help?
If you want to save the IP in the session, you can create a before filter in the applicationController. Like this, for each action, the filter is called and the ip is stored.
authlogic is a plugin to manage users login/sessions etc, it has a built in option to track the users IP
What you really need is a versioning plugin - I suggest having a look at one of the fine solutions at http://ruby-toolbox.com/categories/activerecord_versioning.html
Edit: archived version of that link (was 404 since sometime in 2012): https://web.archive.org/web/20111004161536/http://ruby-toolbox.com:80/categories/activerecord_versioning.html

Getting the current request in rails from a file in lib/

I've put all of my user-authentication code in one place, namely lib/auth.rb. It looks like this:
lib/auth.rb
module Admin
def do_i_have_permission_to?(permission)
# Code to check all of this goes here
end
end
I include this module as part of the application helper, so these functions are available in all the views:
application_helper.rb
require 'auth'
module ApplicationHelper
include Admin
# other stuff here
end
And I also include it as part of the application controller, so the controllers likewise can call the functions:
application.rb
require 'auth'
class ApplicationController < ActionController::Base
include Admin
end
So far, so good.
The problem is that my application is not like a normal web app. Specifically, more than one user can be logged into the system from the same computer at the same time (using the same browser). I do authentication for actions by looking at all the people who are logged in from that IP and if they can all do it, it passes.
What this means is that, if an admin wants to do something, that admin has to log everyone else out first, which is annoying. But we want the admin seal of approval on everything the admin does. So the suggestion given to me was to have it so the admin can supply a username/password combo on any page they would not normally have access to (e.g. an 'edit user' page would have these extra input fields) and the authentication routines would check for that. This means
Admin::do_i_have_permission_to?(permission)
needs to get at the current request parameters. I can't just use params[:foo] like I would in a controller, because params isn't defined; similarly request.parameters[:foo] will also not work. My searching has revealed:
The current search parameters are in the current request,
The current request is in the current controller,
The current controller is in the current dispatcher, and
I'm not sure the current dispatcher is kept anywhere.
That said, experience tells me that when I'm jumping through this many hoops, I'm very probably Doing It Wrong. So what is the right way to do it? Options I've considered are:
Just move all the functions currently in auth.rb into the ApplicationHelper where (I think) they'll have access to the request and such. Works, but clutters the hell out of the helper.
Move all the functions somewhere else they'll see those methods (I don't know where)
I'm just plain missing something.
In a typical Rails application, authentication information is stored in the active session, not the parameters. As such, it's pretty straightforward to write a helper that does what you want.
It seems rather unorthodox to create a module that is then included in ApplicationHelper. The traditional approach is to create a separate helper which in this case would probably be called AuthenticationHelper. This can then be included in any required controllers, or if you prefer, loaded into ApplicationController to make it available universally.
In general terms, Helpers should not include other Helpers. It is better to simply load multiple helpers into a given Controller.
Helper methods have full access to any instance variables declared within the controller context they are operating from. To be specific, these are instance variables only (#name) and not local variables (name). Helper methods are executed for a particular view as well.
Further, I'm not sure why a user would be providing credentials and performing an operation in the same step, at least for traditional web-based apps. Usually the process is to log in and then perform an action separately.
However, in the case of an API where each transaction is an independent operation, the most straightforward approach is to do is pull out the relevant request parameters that deal with authentication, establish some controller instance variables, and then proceed to perform the particular request given the constraints that the credentials impose.
The approach I usually follow for this sort of thing is to layer in an authentication structure in the ApplicationController itself which can perform the required checks. These are protected methods.
While it's tempting to roll in a whole heap of them such as can_edit_user? and can_create_group? these very quickly get out of hand. It is a simpler design to put in a hook for a general-purpose can_perform? or has_authority_to? method that is passed an operation and any required parameters.
For example, a very rough implementation:
class ApplicationController < ActionController::Base
protected
def has_authority_to?(operation, conditions = { })
AuthenticationCheck.send(operation, conditions)
rescue
false
end
end
module AuthenticationCheck
def self.edit_user?(conditions)
session_user == conditions[:user]
end
end
class UserController
# ...
def edit
#user = User.find(params[:id])
unless (has_authority_to?(:edit_user, :user => #user))
render(:partial => 'common/access_denied', :status => :forbidden)
end
rescue ActiveRecord::RecordNotFound
render(:partial => 'users/not_found')
end
end
Obviously you'd want to roll a lot of the authority checks into before_filter blocks to avoid repetition and to promote consistency.
A full framework example might be of more help, such as the Wristband user authentication system:
http://github.com/theworkinggroup/wristband/tree/master

Resources