Allow local webservice to update Rails data with CanCan and Devise - ruby-on-rails

I have a local webservice running which needs to POST data to Rails in order to update and add records. How should I work this into my CanCan/Devise authentication/authorization? Is there a way to allow localhost-only access around authorization, or to create a persistent session with the other service?
Thanks!

if you wanna grant access locally,
# application_controller.rb
def current_ability
#current_ability ||= Ability.new(current_user, request.local?)
end
# ability.rb
class Ability
include CanCan::Ability
def initialize(user, local)
can(:manage, :all) if local
...
end
end

Related

How do you get Sorcery working with Grape in Rails?

How can you use a cookies based Sorcery authenticated session in Grape under Rails?
I am interested in utilizing commands like session and current_user from a grape controller.
Add the following helpers to your root API mount point:
class API < Grape::API
..
helpers APIHelpers
..
end
# add this to app/api/api_helpers.rb
module APIHelpers
include Sorcery::Controller
def session
env['rack.session']
end
def reset_session
if env['rack.session']&.respond_to?(:destroy)
session.destroy
else
env['rack.session'] = {}
end
end
def form_authenticity_token
session[:_csrf_token] ||= SecureRandom.base64(32)
end
def current_user
#current_user = login_from_session || nil unless defined?(#current_user)
#current_user
end
end
Including Sorcery::Controller gave me all the sorcery methods (login, current_user, etc), but there were a few missing session methods that needed to be added via the helpers to make sure sorcery was happy. Note, Grape does not provide the same CookieJar as rails, so you won't be able to utilize cookies.signed. This was not an issue for me, but it may be for you. I worked around the sorcery functions that would call a signed cookie.

Rails user-role system - Same user with multiple Rights on different sites

I have a Rails-Application, that serves different sites. E.g.
www.example1.com
www.example2.com
These sites are stored in the Site-model. Also I have set up a User-Role system, using Devise, Rolify, and cancancan
Now one user can have different Roles on different site. E.g he can be an Administrator on www.example1.com, but only a simple user on www.example2.com
I am loading the users permissions in the ability-model.
Now my question is: Where is this "initialize"-function called?
I need to give this function an additional parameter site_id, so that only the appropriate rights of the site are loaded, not the one of the other side.
How can I do this?
models/ability.rb
class Ability
include CanCan::Ability
# From where is this function called, and how can I adjust this call?
def initialize(user, site_id)
return false unless user.present?
user_role = user.users_roles.find_by(site_id: site_id).try(:role)
user_role.permissions.each do |p|
if p.permission_subject_id.nil?
can p.permission_action.to_sym, p.permission_subject_class.constantize
else
can p.permission_action.to_sym, p.permission_subject_class.constantize, id: p.subject_id
end
end unless user_role.nil?
end
end
That's initialized using the current_ability method. So you need to overwrite that helper in the application_controller like so
class ApplicationController < ActionController::Base
#...
private
def current_ability
#current_ability ||= Ability.new(current_user, request. original_url)
end
end

Rails 4 with Devise & Pundit - #user or #current_user

I am trying to make an app with Rails 4 and devise and pundit (with rolify for roles)
In the:
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
#user = user
#record = record
end
According to this Go Rails video: https://gorails.com/episodes/authorization-with-pundit?autoplay=1
This attr_reader section in the application policy covers all of the rest of the policies - so you don't need to repeat in each one.
However, my current question is given that I use devise, should I change the application policy to use current_user instead of user? Eg:
class ApplicationPolicy
attr_reader :current_user, :record
def initialize(current_user, record)
#current_user = current_user
#record = record
end
None of the examples I've found do it this way, but I don't understand why not.
I was hoping to figure out if I'm off the the right start before I start writing rules for each policy. Does every controller action that i make a policy for need to refer to user or should I change all of them to current_user?
You can use whatever you want to do the authorization logic.
Pundit will send in current_user from your controller when you call something like authorize #object.
From in your class you will just have to do your logic with current_user instead of user.
Why would you want to change it? From your applications perspective, you are really authorizing a user to do something, not necessarily a current_user. So keeping it as user follows conventions

Rails - ActiveAdmin & CanCan custom override method for initialize_cancan_ability

I am attempting to pass thru request data to the Ability model as suggested here:
class ApplicationController < ActionController::Base
#...
private
def current_ability
#current_ability ||= Ability.new(current_user, request.remote_ip)
end
end
and here:
class Ability
include CanCan::Ability
def initialize(user, ip_address=nil)
can :create, Comment unless BLACKLIST_IPS.include? ip_address
end
end
See: https://github.com/ryanb/cancan/wiki/Accessing-request-data
However, I am using ActiveAdmin with the CancanAdapter, and it uses a separate initialize call via:
def initialize_cancan_ability
klass = resource.namespace.cancan_ability_class
klass = klass.constantize if klass.is_a? String
klass.new user
end
See: https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/cancan_adapter.rb
So how/where can I redefine initialize_cancan_ability so that I can pass in request data similar to the current_ability example?
Basically I'm hoping to just replace the last line as such:
klass.new user, request
Thanks.
You can create a file under lib/monkey_patches/active_admin.rb and put your overridden method there:
require 'cancan'
# Add a setting to the application to configure the ability
ActiveAdmin::Application.inheritable_setting :cancan_ability_class, "Ability"
module ActiveAdmin
private
def initialize_cancan_ability
klass = resource.namespace.cancan_ability_class
klass = klass.constantize if klass.is_a? String
klass.new user, request
end
end
end
If you use Devise, you can access the the Ip from the User model user.current_sign_in_ip

How to get User from CanCan Ability

In CanCan, an ability is assigned to an individual user. Is there a way to access that user when you have an ability record.
I'd like to be able to say:
#ability = Ability.new(User.find(3))
#ability.based_on_user # Does not exist, but would respond with the same thing as User.find(3)
In my app, users can impersonate other users, which involves resetting #current_ability = Ability.new(#impersonated_user) and I'd like to be able to quickly get that user for debugging purposes. The documentation doesn't give any such method, but I would be okay with a hack, since it's just for my own purposes in testing.
Just add that method yourself to your ability model. Something like this:
# in app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
#user = user
# the rest of your code
end
def based_on_user
#user
end
end

Resources