CanCan abilities and additional rules by engine - ruby-on-rails

I have application and engines (gems, Rails::Engine).
CanCan used for authorization and I want to use it at engines.
Engines isolated with namespace, but for example:
module MyEngine
class ApplicationController < ::ApplicationController
end
end
So, I can use load_and_authorize_resource at controllers (must specify model class name) and 'can' helper also. All abilities I must write to Ability at main application (models from engines must be namespaced). It is not nice way. I want specify abilities for engine at this engine, but without create new ability object.
How can I do this? Any idea?

It wasn't super pretty, but I did something similar with a Rails application I'm writing currently. I have written my own engine inside this application called FormX, which is sort of a form CMS engine. I wanted users to have the ability to only edit certain responses to forms, and only some users to have the ability to create forms.
Given that I had a Form model in the Formx namespace, I was able to define abilities in my MainApp ability.rb file by doing using Formx::Form syntax.
Ex.
can :manage, Formx::Form
Then in my controller I had to manually place the authorize! calls in each action, as I couldn't get load_and_authorize_resource to work within the engine's namespace.

Related

pundit authorisation multiple-inheritence (scopes, modules)?

I'm using Pundit for authorisation, and want to share logic between the policy classes. So far I've been using plain ruby modules and include, but haven't found a good solution for pundit's Scope classes.
For example granting an admin access to records with a particular tag is quite separate from restricting them to only published (not draft or discontinued/deleted) records.
For example
class PagePolicy < ApplicationPolicy
# restrict access to current pages
include PublishedOnlySharedPolicy
# enable section editors to update their tagged content
include TagsAclSharedPolicy
end
module TagsAclSharedPolicy
def update?
admin.in_tag_acl?(record) || super
end
def show?
admin.in_tag_acl?(record) || super
end
def scope
# ... can't do this?
end
end
These modules work fine for the ordinary ACL methods create? update? etc, but scopes are a puzzle since they're class definitions instead of methods.
I'm expecting scope composition to let me take the base class's scope and restrict it by adding where statements as usual, or expand it by using super, pluck, and building a new scope using union.
Is there a clean way to do this without metaprogramming? Or does the library need changing to support dynamic scopes instead of using class definitions?
Why does Pundit use class definitions for scopes anyway?
There's an answer at https://github.com/elabs/pundit/issues/310 if anyone is curious.
Basically add a whitelist ids method to application policy's scope, then override that with normal ruby in each policy's scope class, with any shared scope code going in its own module and being included in the scope.
May be able to metaprogram something even dryer, but that works.

Using Pundit with namespace

In my project, i have pretty common namespace "admin".
namespace :admin do
resources :users, except: :show
end
I use Pundit gem to set proper authorization, but i found it difficult to use with controllers within namespace. my policies are organised as below
-policies
-admin
user_policy.rb
application_policy.rb
admin_policy.rb
awesome_policy.rb
very similar to controllers.
However, when inside the controller i use "authorize" method i get nothing but an error, informing that app is "unable to find UserPolicy". My UserPolicy looks like this:
class Admin::UserPolicy < AdminPolicy
end
So what is the problem, what should I do to make Pundit see those policies inside namespace?
Short answer: you can't make Pundit use controller namespace-specific policies.
Long answer: Pundit looks at the resource (model) to determine which Policy class to use, so any time you pass an instance of the User model in as the resource, it's going to look for UserPolicy, not Admin::UserPolicy
See here, here and here.
You could specify a policy class on your User model, but this doesn't really solve your namespaced controller issue since Pundit is going to derive the policy class from the model, regardless of where you're authorizing.
With the new merged commit you can do this.
It will work automatically.
UPDATE:
From 0.3 version is effectively removed without replacing feature. However namespace feature can be obtained in namespace branch on github.
You can see the discussion of feature in issue on github.
My suggestion for people who want use namespaces with pundit - don't use yet. Make before filter to access restricted area like admin dashboard and leave authorization model rules in pundit files. That's way you will be able to use pundit without hacks and problems.
While onemanstartup mentioned that is should work automatically now, I wasn't able to get the namespaced policy to work, but here's what I did find to be acceptable.
In your case, in the AdminPolicy, I added custom action names, like
def new_user?
some code
end
and then in my Admin::UserController#new action
def new
authorize #user, :new_user?
end
Just came here because of the same problem. Working with pundit v2.1.0, this is possible by overriding policy_scope and authorize in the controller. What I did for a similar setup:
module Admin
class ModuleController < ModuleController
private
def policy_scope(scope)
super([:admin, scope])
end
def authorize(record, query = nil)
super([:admin, record], query)
end
end
end
And then just use the usual ways of working with your policy in your controller, which will then take the policy from your Admin namespace.
This is also described in the README at https://github.com/varvet/pundit#policy-namespacing
In case you need more context than just the current user and the record, Pundit policies with two input parameters will be helpful.

Where should I put methods to be used by multiple controllers in rails?

I have an admin controller and view to manage admin tasks. Many of those tasks are very similar to tasks conducted in my two main model-backed controllers, Users and Materials. I'm trying to dry up my code so I want to put it somewhere, but where?
For example:
As an admin I can delete a Material from my admin view but so can a User from their material view. I have almost identical code for this in both the admin and material controllers with the only exception that the redirect goes to a different place.
The Rails4 way is to use Concerns, even though there is some discussion going on about it. Still, I like this approach, even though most of the material you find will be more about models than about controllers.
A simple example
If you are on Rails 3 (as your tag implies), just add a concerns-folder into your controllers-folder and add it to your autoload-path:
#config/application.rb
config.autoload_paths += %W(#{config.root}/app/controllers/concerns)
For instance, I have something like this in app/controllers/concerns/can_can_sanitizer.rb
module CanCanSanitizer
extend ActiveSupport::Concern
included do
before_filter do
resource = controller_path.singularize.gsub('/', '_').to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
end
end
I include this into my application_controller just like any other module:
include CanCanSanitizer
Admittedly, not the best use-case, but it should give you a headstart.
If the AdminsController is inherited from UsersController, you can put such methods in UsersController, judging the difference from method arguments or controller name or code before super.
If Admin and User has no inheritance, you can create a separate module and get both Admin and User to include it.

ROR creating standalone components

In my web application I have a model User. It's quite common that you need to select some users for many different purposes related to many different models. My aim is to make this component very easy and fast to attach in a new place. E.g. if a users wants to select his friends the result of selection should be handled by User controller, but if you want to assign some users to a task this should be handled by Task controller.
Do you have any concept how to do this? Should I make another controller for selecting? How should I pass the selection to the suitable controller? Maybe by session? Do you have any other suggestions?
I think what you are looking for is a module which has common methods. If so you can do something like:
Create a module called Users and add the methods to that, and keep it inside your lib folder
Ex:
<app root>/lib
module User
def friends
<returns the given users friends>
end
end
and then you can call this module in both your controllers and models
Ex:
Class Friend
include User
end
Class FriendsController < ApplicationController
include User
end

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