Git Product home page Git Product logo

access-granted's People

Contributors

agferrier avatar badlamer avatar crisfole avatar erlingur avatar farnoy avatar jjbohn avatar jrochkind avatar lokeshdevnani avatar marckysharky avatar pokonski avatar trevorhinesley avatar vinbarnes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

access-granted's Issues

Higher level roles do not seem to inherit from lower level roles

Looking at this line from documentation at https://blog.chaps.io/2015/11/13/role-based-authorization-in-rails.html
Roles inherit from less important roles below them, but only from roles which apply to the given user. So we only need to add additional permissions on top of them. In this case, we will let members create posts and topics:

I have not been able to get this backward inheritance to work. I have something similar to this in my config.

class AccessPolicy
  include AccessGranted::Policy

  def configure
    role :admin, { role: 'admin' } do
       # can :view, Admin::DashboardController
     end

    role :staff, { role: 'staff' } do
      can :view, Admin::DashboardController
    end

    role :view_only, { role: 'view_only' } do

    end
  end
end

However calling can? :view, Admin::DashboardController only returns true for the :staff role, and returns false for the higher level :admin role. Unless I uncomment the line in the :admin block.

Error when there is no user logged in

I'm using an access policy in my rails 4 app that checks the user role, very similar to the example here.

role :moderator, proc { |u| u.moderator? } do
  can [:update, :destroy], Post
  can :update, User
end

role :guest do
  can :read, :all
end

I got an error when there are no user logged in, because the call u.moderator? expects an user and u is nil.

To fix this error I needed to override the initialize method:

def initialize(user)
  user ||= User.new
  super user
end

My question is: do I need to check when there is an @user in my policy or do I did something wrong applying the gem?

If I need to check the @user i think that would be great update the readme with this info.

Support for authorize_resource!

It would be nice not having to call authorize! in every controller method. Do you have any plans to include this functionality?

I'm curious, how much work/how complicated is it?

I might be out of my depth, but I'm willing to give it a crack if you think it's useful, and if I can get some guidance.

Caching accesses.

Hi.

We've been using this gem with rolify, and encountered a problem with performance. The thing was, we were doing a lot "can?" on one page. And by a lot I mean a LOT. It took 7 seconds to load everything every time we were reloading the page. The thing is, most of the queries where duplicated, so we could cache them, instead of loading them from DB (DB caching seems not very efficient). We have solved the problem by overriding the "can?" method like this:

def can?(action, subject = nil)     
    @user.can = {} unless @user.can
    key = "#{action}_#{subject.try(:to_s)}".to_sym
    return @user.can[key] unless @user.can[key].nil?
    roles.each do |role|
      next unless role.applies_to?(@user)
      permission = role.find_permission(action, subject)
      if permission
        @user.can[key] = true
        return permission.granted
      end
    end
    @user.can[key] = false
    false
  end

Caching feature would be a great thing. In our case, the code above increased performance on the given page from 7 sec load time, to 2 sec load time. Of course it depends on the amount of data, more data, bigger improvement, on the other pages it is not noticeable at all. It's downside is the fact, that we are caching it inside a user, which doesn't seem like the best idea. Changing accesses while user is logged in will have no effect, unless we force him to relog. Also it is a security danger, if user finds a way to somehow write something to the "can" attribute, he will gain access, and we will not be checking again if he really does have an access.

Anyways, that's what happened, I just wanted to share what we did. Great work by the way.

Permissions with blocks work too persmissive (block is ignored)

Hello.

I have such policy:

can [:read, :update, :destroy], User do |user, merchant_admin|
  user.merchant.present? && created_user_is_of_creator_merchant?(merchant_admin, user)
end

and check

<% if can? :read, User %>
  <li><%= link_to 'Users', users_path %></li>
<% end %>

When I add byebug to policy block the execution even doesn't get stopped. So, looks like it looks at User at decides that it's enough.

Only when I comment out the policy definition, the check works.

How to use with rails-api

Hi,

What would be the correct approach to use access-granted with the rails-api gem? In rails-api the ActionController::Base is replaced with a scaled-down version so the controller methods won't be applied automatically by access-granted in https://github.com/chaps-io/access-granted/blob/master/lib/access-granted.rb.

Would a solution be to manually include the module like with cancan (https://github.com/ryanb/cancan/wiki/Rails-API-Gem) or is there any cleaner ways to solve this?

permission always uses conditions hash when passed a Class as subject

What is the thinking behind ignoring a block and always attempting to evaluate the conditions hash when passed a Class as a subject.

    def matches_conditions?(subject)
      if @block && !subject.is_a?(Class)
        @block.call(subject, @user)
      else
        matches_hash_conditions?(subject)
      end
    end

I would always like to evaluate a block if it is present (I've got some user related logic I need to execute to determine whether to allow access)
Would it be acceptable to change the logic so that blocks are always evaluated if present?

Feature request: special handling for nil current_user?

This relates to #32, but since it's been a couple years I thought it was worth opening a new issue.

I would LOVE it if access-granted could have a special "role" that is checked for nil current_user.

With the advice in #32, you can create a special NullUser, which is not so so bad, but you do have to keep it in sync with any possible methods on your real user you may reference in a role condition lambda.

I think there could be a pretty simple implementation in access-control that would save you from boilerplate code requiring ongoing maintenance -- for what I'd think would be a fairly common use case, I'm kind of surprised the authors and current users of access-granted haven't needed it.

If current_user is nil, assume no roles match, for present roles, short circuit it and don't try on nil.

But add some way to declare a "role" that looks just like current role definitions, but applies always and only when current_user is nil. It might be easiest to simply add new syntax:

nil_user_role do
  can :read, Post do |post|
      post.public?
  end
end

Object and Subobject

How can I define rule for a subobject extends object rule.
es.

can :delete, Object do |obj, user|
  post.author_id == user.id
end
can :delete, Subobject do |sub, user|
  can? :delete, sub.parent && sub.id>3
end

errors with non-logged in users

I've got a configure block defined in a pretty standard way:

    role :admin, proc { |user| user.is_admin } do
       can :manage, Post
    end

When I view the page as a non logged in user though I get an error:

undefined method `is_admin' for nil:NilClass

I must be missing something pretty simple -- unless it depends on authentication before authorization? Little help please?

Version 0.2 not published to rubygems

Hi,

Had some issues yesterday setting up this gem in my rails app. Seems like the latest version that is published on rubygems (0.1.1) doesn't include the rails lib. However I managed to solve this by fetching the directly from the git repo so maybe the docs needs to be updated or 0.2 should be published to rubygems.

Thanks!

ArgumentError when Permission checking in view

I'm doing a simple permission check in my view file like <% if can? :create, Team %> and I'm getting an ArgumentError for 'wrong number of arguments (0 for 1)'

here is the full block:

<% if can? :create, Team %>
  <%= link_to 'New Team', new_team_path %>
<% end %>

Support for :all

CanCanCan has a nice shortcut for managing all models: can :manage, :all and it would be great if this gem had that too, or something similar. I noticed a quick fix that would allow something like this to work:
can :read, ApplicationRecord

In permission.rb:

def matches_subject?(subject)
    subject == @subject || subject.class <= @subject
end

Adding .class onto the end of @subject allows the second condition to work successfully. Otherwise, it evaluates to nil.

def matches_subject?(subject)
    subject == @subject || subject.class <= @subject.class
end

Weird bug in 1.0.4: permissions don't seem to be inherited from lower-privileged users

@pokonski Hello, Piotr. Something wrong goes with v1.0.4.

Policy file:

role :admin, proc { |user| user.admin? } do
  can :access, :direct_user_creation

  can :read, User
  can :create, User
  can :destroy, User
end

role :planner, proc { |user| user.planner? } do
  can :manage, Merchant
end

This way, admin cannot :edit a particular merchant (checking with authorize! :edit, @merchant) of even :read Merchants.

I change it to

# ...
role :planner, proc { |user| user.planner? } do
  can :read, Merchant
  can :edit, Merchant
end
# ...

and still no way for :admin to :read or :edit Merchants

Then I go with:

role :admin, proc { |user| user.admin? } do
  can :access, :direct_user_creation

  can :read, User
  can :create, User
  can :destroy, User

  can :read, Merchant
  can :edit, Merchant
end

role :planner, proc { |user| user.planner? } do
  can :read, Merchant
  can :edit, Merchant
end

And only this setup allows my :admin to both :read and :edit Merchants.

Can you please check what could be the reason? I can provide any additional details that you may require. Thanks.

Doesn't work with ActiveRecord relations

For instance

@artists = Artist.all
authorize! :read, @artists

Will not work, no matter if can :read, Artist is specified in the policy. This does work:

@artists = Artist.all
authorize! :read, Artist

But is insufficient for obvious reasons. Each one needs to be checked individually to make sure this collection is viable for this user in this case.

Also, something like this seems like it may work:

@artists = Artist.all
authorize! :read, @artists.first

But if @artists.first == nil in this case of an empty collection, this will result in access denied.

Can we use with `gem 'role_model'`

First Congratulation on your work.

In our application One admin can have multiple roles as listed below. We use role_model gem.

Roles are defined in admin.rb
roles :account_manager, :customer_support, :data_entry, :sales_rep, :advertising_rep, :management, :tech_support, :super_admin

It works fine with cancan but I liked your gem a lot. Can this ability.rb of cancan be replaced in access_policy.rb . If yes please help me out


class Ability
  include CanCan::Ability

  def initialize(user)
    if user.admin?
      can :manage, :all
    else
      cannot :manage, OperatingExpense
      cannot :manage, AttendanceRecord
    end

    can :read, Admin, group: user.group

    can :manage, Admin, id: user.id

    if user.manager?
      can :manage, Admin, group: user.group
      cannot :manage, BlockedLocation
    end

    can :manage, Shop, admin_group: user.group # renders the account_manager role moot
    can :manage, Order do |order|
      order.shop.admin_group == user.group
    end

    # # role-based abilities
    if user.has_role? :account_manager, :customer_support, :data_entry, :sales_rep, :advertising_rep, :management
      can :manage, Admin do |admin|
        admin.id == user.id
      end
      can :manage, Shop
      can :manage, Coupon
      can :manage, Comment
      can :manage, Order
      can :manage, OrderAdjustment
      can :manage, ReceivedCall
      can :manage, Ledger
      can :manage, Payout
      can :manage, Ticket
      can :manage, Chain
      can :manage, User
      can :manage, BlockedIpAddress
      can :manage, BlockedLocation
    end

    if user.has_role? :super_admin, :tech_support
      can :manage, :all?
    end
  end
end

README should provide promised "cannot" example.

README says:

Note: cannot is still available, but has a very specifc [sic] use. See Usage below.

However, no example is actually given of cannot in Usage or elsewhere. Controller/view method cannot? is described, but not the quite different cannot method in permission definitions.

At first I thought cannot was maybe not actually there, the README reference was wrong or I misunderstood it.

But then I saw it in the specs. Apparently it does exist, hooray!

But the README needs a short explained example as promised. :)

Is this project dead?

Asking because last update was 7 months ago, last release almost a year ago and some outstanding issues.

class inheritance, and class as test subject

I found what I think is an inconvenient inconsistency around inheritance; I also have a simple PR to fix it.

access_granted normally works great with inheritance. Imagine we have a Vehicle and a Bicycle < Vehicle. And we have a policy:

    class AccessPolicy
      include AccessGranted::Policy
      
      role :user do
        can :read, Vehicle
      end
    end

AccessPolicy.new(user).can? :read, Vehicle.new # true, of course
AccessPolicy.new(user).can? :read, Bicycle.new # still true, it works with the sub-class

But access_granted also has a convenient feature where you can check on the class instead of an instance, intended to be used with the meaning of sort of generic/any/all objects, like often for create, but you can use it for anything:

AccessPolicy.new(user).can? :read, Vehicle # this is true too, okay

That one does NOT work with inheritance:

AccessPolicy.new(user).can? :read, Bicycle # FALSE
# I believe it should be true

I think this is a bug, or would be an improvement if above were true also. I think I have a simple PR to make it so.

Thoughts?

Bug related to :manage and using multiple operations in permission definition

Hey there. I've spotted a couple of cases where behavior doesn't seem logical:

role :admin, proc { |user| user.admin? } do
  can :manage, Merchant # can? :edit @merchant is AccessDenied, :read is OK
end
role :admin, proc { |user| user.admin? } do
  can [:manage], Merchant # can? :edit @merchant is AccessDenied, :read is AccessDenied
end
role :admin, proc { |user| user.admin? } do
  can [:manage, :edit], Merchant # can? :edit @merchant is AccessDenied, :read is AccessDenied
end

❓ ❓ ❓

Support for introspection

I propose to add an interface for introspection uses. It can help in different situations. For example, if you need to serialize some object along with current_user's privileges on it now I should carefully copy it from policy one by one. Getting this list dynamically in run-time make things a lot of easier and less error-prone.

Please don't use manage

Hey Piotr, I read your blog post http://blog.chaps.io/2015/11/13/role-based-authorization-in-rails.html a few minutes ago. Because im not unhappy with CanCan too, I was exited to read how your library works. It looks good, it looks better than CanCan! But one thing I read in your blog post confused me:

:manage is a meta-action borrowed from CanCan, which is just a shortcut for defining all default CRUD actions ([:read, :create, :update, :destroy]).

Because I have had some issues with the :manage action, I already had to look a little bit deeper onto it. A must say that :mage isn't ":read, :create, :update, :destroy". It's more than that. Please take a look at this lines of code:

I have thought that :mange means only ":read, :create, :update, :destroy" for a long time. But it means "all".I can image that there are many other people, who are thinking that. Therefore I wish you wouldn't use :manage. The idea of an shortcut for ":read, :create, :update, :destroy" is great, but maybe with an other name. Maybe with a simple :crud :) How do you think about that point?

Best regards, Michał

Able to access protected controller actions

This means that you define what the user can do, which results in clean, readable policies regardless of application complexity. You don't have to worry about juggling cans and cannots in a very convoluted way!

Based on the README, my understanding is that access to controller actions should raise an error if not specified in access_policy.rb. That doesn't seem to be the case in my example. I am setting current_user when User has not logged in yet and assigning a role = 'locked'.

Based on access_policy.rb, a user with a role of 'locked' should only be able to read from one specific action, however, in my example the user is allowed to perform any action, it appears that the application_policy is not being applied to this new user, not sure why.

Yes, I am specifying authorize! in each controller action.

application_controller.rb

def current_user
  super || User.new
end

user.rb

after_initialize :set_default_role, :if => :new_record?

def set_default_role   
  self.role ||= 'locked'
end

access_policy.rb

class AccessPolicy
  include AccessGranted::Policy

  def configure

    role :superhero do
      can :manage, Company
      can :index, Company
      can :manage, Event
    end

    role :admin do
      can :manage, User
      can :manage, App
      can :manage, Event
    end

    role :member do
      can :index, App
      can :index, User
      can :index, Event
    end

    role :locked do
      can :read, @page
    end

  end
end

Block in role always evaluating true

This is my policy

# The most important admin role, gets checked first
    role :admin, proc {|u| u.admin? } do
      can [:new, :create, :edit, :update, :destroy], Project
    end
# Less privileged moderator role
    role :editor, proc {|u| u.editor? } do
      can [:new, :create], Project
      can [:edit, :update], Project do |project, user|
        project.user_id == user.id
      end
    end
...

In Project controller:

def edit
    authorize! :edit, Project
...

There is no restriction, my editor role is able to edit any project whether project.user_id matches user.id or not.

Replacement for accessible_by

Since AccessGranted does not support CanCan's accessible_by, how can I achieve something similar to it? It's handy to have such a scope to retrieve only permitted objects.

Tutorial for beginners

Are you considering to create a tutorial for beginners ? I mean sth which will show how to start or example based on simple rails application. I asked because doc, wiki is poor.

How to setup access_policy.rb when roles are not part of a user model?

I'm pretty new to Rails, however, I work on the app where I have this trio: users, wikis, wiki_users. Many users can be assigned to many wikis and vice versa.

I struggle with connecting my access_policy.rb file with Rails. How could I let the gem know that it should look for user, assigned to a specific wiki with a specific role on that specific wiki?

# wiki_user.rb
  belongs_to :user
  belongs_to :wiki

  enum role: { owner: 1, administrator: 2, moderator: 3, contributor: 4, reader: 5 }
# user.rb
...
  has_many :wiki_users, dependent: :destroy
  has_many :wikis, through: :wiki_users
...
  def wiki_user_role(role)
    wiki = Wiki.find_by_subdomain request.subdomain
    wiki.users.include?(self) && wiki.wiki_users.find_by_user_id(self.id).role == role
  end
# project.rb
...
  has_many :wiki_users, dependent: :destroy
  has_many :users, through: :wiki_users
...

My project_users table contains columns id (of the relationship), user_id, project_id, and role. My access_policy.rb file looks like this so far, with some effort to make it work in role :reader block. However, it all feels just like a workaround and I am wondering whether this scenario can't be handeled in easier manner?

  def configure

    # ROLES
    # Owner — a person who handles payments, e.g. owner of the company
    # Administrator — administrator who does not care ab payments only ab the wiki itself
    # Moderator — can create and edit other people articles
    # Contributor — can only create and edit his own articles
    # Reader (guest) — only the permission to read

    role :owner, proc { |user| user.present? } do
      can [:edit, :update, :destroy], Article
      can [:edit, :update, :destroy], Comment
      can [:edit, :update, :destroy], User
      can [:edit, :update, :destroy], Wiki

    end

    role :administrator, proc { |user| user.present? } do
      can [:edit, :update, :destroy], Article
      can [:edit, :update, :destroy], Comment
      can [:edit, :update, :destroy], User

    end

    role :moderator, proc { |user| user.present? } do
      can [:edit, :update, :destroy], Article
      can [:edit, :update, :destroy], Comment

    end

    role :contributor, proc { |user| user.present? } do
      can [:edit, :create, :destroy], Article do |article, user|
        article.user_id == user.id
      end

      can [:edit, :create, :destroy], Comment do |comment, user|
        comment.user_id == user.id
      end
    end

    role :reader, proc { |user| user.present? && user.wiki_user_role("reader") } do
      can :read, Article do |article, user|
        article.wiki.users.include? user
      end

      can :read, User do |selected_user_and_wiki, user|
        selected_user = selected_user_and_wiki.first
        wiki = selected_user_and_wiki.second

        wiki.users.include?(selected_user) && wiki.users.include?(user)
      end

      can [:edit, :destroy], User do |edited_user, user|
        edited_user == user
      end
    end

  end

Class vs Instance subjects

As I'm trying to do something a bit more convoluted in my app, I'm realizing access-granted doesn't work quite as I thought for being able to kind of interchangeably use a Class (with the semantics "all of them", as in the can? :create, Post example) and an instance of a class interchangeably.

I was imagining on the same permission you could kind of use both interchangeably. But if we try say:

    role :admin, { role: "admin" } do
      can :manage, Post
    end

    role :user do
      can :manage, Post, { published: true }
    end

and then we try can? :create, Post, then of course you get undefined method published' for Post:Class`.

Because it's trying to call published on the specific Post object you passed in, of course it is, you did specify that condition naturally. And Post.published is not a thing.

So I guess any given permission, you need to decide you are going to only use with a class object (like in README example for :create), or only use it with an instance (like in README examples with :update or :make_manager).

In my example above, I shouldn't say can :manage, Post, but instead maybe:

can [:read, :update, :destroy], Post, { published: false }

and then maybe only can :create, Post only on admin role.

My actual use case, I was hoping to mix and match. I had a read role defined like above, where admin's can read everything, everyone else can only read published things.

So when I have a specific item, of course I can just ask can? :read, specific_post,

But I want to know if I should show a UI widget, say, "include unpublished posts", and I should only show that widget to those who can see even unpublished posts, and I was hoping to be able to do can? :read, Post and have it be only true for Post.

But that won't work.

Curious if you have any advice. Should I just define a new permission :can_see_unpublished_posts which I give only to admin, so I can check that to decide whether to show the "include unpublished posts" button? It seems duplicative, but...

I suppose we could do another PR where access_granted, for hash conditions, first checks to make sure the method exists (with respond_to?), and if it doesn't, that's just false?

Or I guess I could write the condition long-hand:

    can :read, Post do |post, user|
       post.respond_to?(:published) && post.published
    end

And now I guess it'll work if I ask can? :read, Post (answer is no if they can only read published posts, because Post.respond_to?(:published) is false.... but still return properly for Post instances (based on published).

I'm not super happy with any of these solutions, I am curious your feedback!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.