jywarren / plots2

The Public Lab website!
http://publiclab.org
GNU General Public License v3.0
17 stars 2 forks source link

Role Based Access Control #160

Closed btbonval closed 9 years ago

btbonval commented 11 years ago

Find an implement a Rails-friendly RBAC system.

btbonval commented 11 years ago

Most of the info I'm finding for this is extremely outdated (e.g. stackoverflow answers from 2008). All references to the ActiveRBAC project are 2008 and earlier.

Here's an article from 2011 advising a combination of cancan and devise; it seems to support true RBAC but looks like an awful lot of steps and integration to get it working: http://felixhanley.info/articles/rails-role-based-access-with-cancan/

Here's a railscast for cancan (2009): http://railscasts.com/episodes/192-authorization-with-cancan?view=comments

CanCan on ohloh indicates it is still mildly developed: http://www.ohloh.net/p/cancan

Devise appears to be more supported and active: http://www.ohloh.net/p/devise

It's almost like Rails shuns standard web practices like RBAC >:-/

jywarren commented 11 years ago

Devise is fairly well-known in the railsverse; i think railscasts has covered it a bit. So that might be the biggest bandwagon out there right now. What are some RBAC solutions for other languages/frameworks -- we can search for "blabla for Rails" to cast a wider net.

On Wed, Oct 16, 2013 at 1:51 PM, Bryan Bonvallet notifications@github.comwrote:

Most of the info I'm finding for this is extremely outdated (e.g. stackoverflow answers from 2008). All references to the ActiveRBAC project are 2008 and earlier.

Here's an article from 2011 advising a combination of cancan and devise; it seems to support true RBAC but looks like an awful lot of steps and integration to get it working: http://felixhanley.info/articles/rails-role-based-access-with-cancan/

Here's a railscast for cancan (2009): http://railscasts.com/episodes/192-authorization-with-cancan?view=comments

CanCan on ohloh indicates it is still mildly developed: http://www.ohloh.net/p/cancan

Devise appears to be more supported and active: http://www.ohloh.net/p/devise

It's almost like Rails shuns standard web practices like RBAC >:-/

— Reply to this email directly or view it on GitHubhttps://github.com/jywarren/plots2/issues/160#issuecomment-26441489 .

btbonval commented 11 years ago

Most pythonic web platforms have RBAC support built in, so there isn't a name for external RBAC moduless/gems/plugins.

Maybe Django has an RBAC plugin that might help cast that wider net. I feel like "RBAC" should be a pretty wide net tho... On Oct 16, 2013 2:16 PM, "Jeffrey Warren" notifications@github.com wrote:

Devise is fairly well-known in the railsverse; i think railscasts has covered it a bit. So that might be the biggest bandwagon out there right now. What are some RBAC solutions for other languages/frameworks -- we can search for "blabla for Rails" to cast a wider net.

On Wed, Oct 16, 2013 at 1:51 PM, Bryan Bonvallet notifications@github.comwrote:

Most of the info I'm finding for this is extremely outdated (e.g. stackoverflow answers from 2008). All references to the ActiveRBAC project are 2008 and earlier.

Here's an article from 2011 advising a combination of cancan and devise; it seems to support true RBAC but looks like an awful lot of steps and integration to get it working: http://felixhanley.info/articles/rails-role-based-access-with-cancan/

Here's a railscast for cancan (2009):

http://railscasts.com/episodes/192-authorization-with-cancan?view=comments

CanCan on ohloh indicates it is still mildly developed: http://www.ohloh.net/p/cancan

Devise appears to be more supported and active: http://www.ohloh.net/p/devise

It's almost like Rails shuns standard web practices like RBAC >:-/

— Reply to this email directly or view it on GitHub< https://github.com/jywarren/plots2/issues/160#issuecomment-26441489> .

— Reply to this email directly or view it on GitHubhttps://github.com/jywarren/plots2/issues/160#issuecomment-26443354 .

btbonval commented 11 years ago

Since the system is not written in a resource-centric way (although CRUD is theoretically supported for resources routes), RBAC might be difficult to shoe-horn in even if there is found to be a useful Ruby gem.

It might be faster and easier to design the permission system by hand. First cut:

Role Table: id, name

UserRole Table: user_id, role_id

Summary:

Instead of permissions, implement minimum required role. Minimum required role should be defined in its own Controller or in a Helper. Minimum required roles are embedded directly into Controller functions and/or possibly mapped using routes.rb.

Details:

Since there are no decorators in Ruby, it might be painstaking to write clear, explicit permissions checks into each Controller function. ApplicationController would need to implement something fancy to inherit the Minimum Required Role capability throughout all the Controllers to ease burden. Then each Controller itself might have a default minimum required role (e.g. AdminController has an 'admin' minimum requirement, which would mean we want to break Moderator-based controls into ModeratorController). Functions would then need a way to override the minimum required role (to elevate or reduce required level).

Instead of writing, within a function, "if this permission, do this code, else do that code", it might be possible to redirect functions from routes.rb based on role. Hard to say how doable this is. The current routes.rb file does not really follow standards laid out in this link: http://guides.rubyonrails.org/routing.html

btbonval commented 11 years ago

The more I think about it, the less I like using routes.rb.

The minimum required role needs to have some action to perform if the minimum is not met. This would probably end up forwarding to some other action. By default, this would be a 304 Unauthorized (or, since we don't use proper HTTP responses, forward to / with a flash[] message). But it could be overridden on a per-function basis.

This would allow fine-grained action-controls. Example:

class SpamController << ApplicationController
  minreq UserRole.verified # shortcut for UserRole.find_by_name('verified')

  def spam
    # require at least moderator for code herein
    minreq UserRole.moderator, :fallback => spam_verified
    # ... usual code ... maybe it deletes spam
  end

  def spam_verified
    # require at least verified for code herein. the following line would be redundant based on the Controller's minreq.
    minreq UserRole.verified # default :fallback is front page with a notice "you can't do that"
    # ... usual code ... maybe it marks the spam for moderators/admin to review
  end
jywarren commented 11 years ago

You can define filter functions to be run before each controller method on a whitelist/blacklist basis: https://github.com/jywarren/spectral-workbench/blob/master/webserver/app/controllers/spectrums_controller.rb#L3

On Wed, Nov 13, 2013 at 12:57 PM, Bryan Bonvallet notifications@github.comwrote:

Since the system is not written in a resource-centric way (although CRUD is theoretically supported for resources routes), RBAC might be difficult to shoe-horn in even if there is found to be a useful Ruby gem.

It might be faster and easier to design the permission system by hand. First cut:

Role Table: id, name

UserRole Table; user_id, role_id

Summary:

Instead of permissions, implement minimum required role. Minimum required role should be defined in its own Controller or in a Helper. Minimum required roles are embedded directly into Controller functions and/or possibly mapped using routes.rb.

Details:

Since there are no decorators in Ruby, it might be painstaking to write clear, explicit permissions checks into each Controller function. ApplicationController would need to implement something fancy to inherit the Minimum Required Role capability throughout all the Controllers to ease burden. Then each Controller itself might have a default minimum required role (e.g. AdminController has an 'admin' minimum requirement, which would mean we want to break Moderator-based controls into ModeratorController). Functions would then need a way to override the minimum required role (to elevate or reduce required level).

Instead of writing, within a function, "if this permission, do this code, else do that code", it might be possible to redirect functions from routes.rb based on role. Hard to say how doable this is. The current routes.rb file does not really follow standards laid out in this link: http://guides.rubyonrails.org/routing.html

— Reply to this email directly or view it on GitHubhttps://github.com/jywarren/plots2/issues/160#issuecomment-28417596 .

btbonval commented 11 years ago

Filter functions are probably the better way to work this than the example I wrote.

Define a filter function that applies minimum required role filtering to all functions in the controller, then override it specifically at the top of the controller where needed.

I like things that modify code to be close to where the original code is, but maybe it isn't as easy. The pasted link shows a bunch of functions being modified at the top of the file. If one were to look at some particular function definition, one could easily miss the filter function at the top that is also relevant to the definition being reviewed.

btbonval commented 11 years ago

http://guides.rubyonrails.org/action_controller_overview.html#filters

btbonval commented 11 years ago

Unsure sure how to specify parameters to the filter given the Rails syntax for overriding the fallback action.

There'd need to be require_basic, require_verified, require_moderator, and require_admin functions defined in ApplicationController, with no ability to dynamically override the default fallback location. If params could be passed, then require_role could be a single filter which takes parameters of minimum role and fallback.

btbonval commented 11 years ago
The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering.

Assuming the current user info can be snatched from the controller, this creates an object which takes parameters and then passes that into before_action:

class ApplicationController < ActionController::Base
  before_action RequireRole.new('admin') :only => [:some_action]
  before_action RequireRole.new('moderator', some_fallback_action)
end

class RequireRole
  def initialize(role, fallback)
    @role = role
    if fallback !== undefined
      @fallback = fallback
    else
      @fallback = some_action_with_warning_and_redirect
  end
  def filter(controller)
     # extract current user from controller?
     # run the "at least" logic against @role and possibly run action @fallback
  end
end

There's got to be a better way. This looks horrific.

btbonval commented 9 years ago

So this ticket was a lot of exploration with very little climactic discovery. Basically I was looking for a system to implement and support roles, not just user permissions or group permissions. My searches turned up nothing.

At this point, if you can find a gem that supports users belonging to groups, and either or both users and groups being allowed access to this table or that element id, it's a win.