Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Easier attribute-level authorization with ActiveSupport::CurrentAttributes #587

Closed
duffyjp opened this issue Apr 19, 2019 · 2 comments
Closed

Comments

@duffyjp
Copy link

duffyjp commented Apr 19, 2019

I've seen attribute level abilities within the upcoming 3.0.0 #474 but my understand is you'd still have to check the authorization at each use in your views and add extra effort to your Controller params with permitted_attributes.

I've been using ActiveSupport::CurrentAttributes lately with Rails 5.2, and by using it to expose the current_user to the model layer. I just set it as a before_action in my ApplicationController after my normal authentication steps.

After that it's easy. Just modify the setters and getters for the attributes you want to authorize.

Say we have a User model with sensitive data.

class User < ActiveRecord::Base
  def api_key
    return nil unless Current.user && Ability.new(Current.user).can?(:sensitive, self)
    read_attribute(:api_key)
  end

  def api_key=(value)
    return nil unless Current.user && Ability.new(Current.user).can?(:sensitive, self)
    write_attribute(:api_key, value)
  end
end

I made up the User model here to match the OP's example, but where I'm using this in practice it's been great, with no effort sensitive fields just show blank. SomeModel.as_json also doesn't expose the sensitive fields by accident if you weren't careful in an API or a scaffold you didn't fix yet.

I guess I don't have a question, other than to ask if this is a bad idea for some reason I'm not thinking of?

System configuration

Rails version:
5.2.2.1

Ruby version:
ruby 2.6.2p47

CanCanCan version
2.3.0

@stalniy
Copy link

stalniy commented Jun 27, 2019

It may work good for some apps but generally you are mixing 2 layers: model and authorization. This may cause issues in futures:

For example, in blog app, you can prevent users to get information about each other but when you show blog post, you need to show info about author of that post.

In this case model getter will return nil

@duffyjp
Copy link
Author

duffyjp commented Sep 5, 2019

I know this is quite stale, but I'd love to offer a little follow up for anyone that stumbles on this. I've been using this technique now since writing the OP and it's working fantastically. Specifically I have some Excel files being generated, with a single template and no apparent logic in the view (excel template, but same idea) a user without explicit access to a field setup as above will just get nil in that cell. I used to have to write a different template for each level of access.

It's a bit of untraditional layer mixing to be sure, but it works very well and gives good peace of mind that a sensitive field will not be revealed accidentally.

@duffyjp duffyjp closed this as completed Sep 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants