Explicitly allow Doorkeeper scope in controller - ruby-on-rails

In my current application I have two doorkeeper scopes, user and admin. In the doorkeeper documentation for setting scopes in an API it shows
class Api::V1::ProductsController < Api::V1::ApiController
before_action -> { doorkeeper_authorize! :public }, only: :index
before_action only: [:create, :update, :destroy] do
doorkeeper_authorize! :admin, :write
end
...
end
I don't want to call doorkeeper in every controller, so in my ApplicationController I have
module API
module V1
class ApplicationController < ActionController::API
before_action { doorkeeper_authorize! :user, :project }
...
end
end
end
but I don't want to give :project access to every controller. Is there a way for me to allow user in our application controller before_action { doorkeeper_authorize! :user } and on a per-controller basis allow project? ie:
module API
module V1
class SomeController < ApplicationController
before_action only: [:index, :show] { doorkeeper_authorize! :project }
...
end
end
end

Use a conditional with controller_name - smith like this:
before_action { doorkeeper_authorize! :project },
if: -> { controller_name == 'some' }
Check if maybe you should pass a param to lambda like:
if: ->(instance) { instance.controller_name == 'some' }

I was able to solve this by doing the following in my API::V1::ApplicationController
module API
module V1
class ApplicationController < ActionController::API
WHITELISTED_PROJECT_CONTROLLERS = %w( projects pre_task_plans
job_hazard_analyses ).freeze
before_action :authorize!
def authorize!
if project_scope?
if !WHITELISTED_PROJECT_CONTROLLERS.include?(controller_name)
return user_not_authorized
end
end
doorkeeper_authorize! :user, :project
end
def project_scope?
doorkeeper_token&.scopes&.any? { |s| s == 'project' }
end
...
end
end
end

maybe creating your own filter might be an option
before_action :doorkeeper_user_authorize!, only: [:create, :update, :destroy]
protected
def doorkeeper_user_authorize!
doorkeeper_authorize!( :user )
end
def doorkeeper_project_authorize!
doorkeeper_authorize!( :user, :project )
end
then in controller where project should be allowed
skip_before_action :doorkeeper_user_authorize!
before_action :doorkeeper_project_authorize!

Related

How I can skip callback in action

I have controller looks like
class BarsController < ApplicationController
after_action :some_method, only: [:index]
def index
get_cache = $redis.get('some_key')
if get_cache.present?
# i want to skip after_action callback in here
else
# other stuff
end
end
end
How can I skip after_action :some_method if get_cache is present? I know I can do this with conditional like this
class BarsController < ApplicationController
after_action :some_method, only: [:index], unless: :check_redis
def index
get_cache = $redis.get('some_key')
if get_cache.present?
# i want to skip after_action callback in here
else
# other stuff
end
end
private
def check_redis
$redis.get('some_key')
end
end
But I think that is redundant, because should multiple get to redis.
This should work:
class BarsController < ApplicationController
after_action :some_method, only: [:index], unless: :skip_action?
def index
get_cache = $redis.get('some_key')
if get_cache.present?
#skip_action = true
# i want to skip after_action callback in here
else
# other stuff
end
end
private
def skip_action?
#skip_action
end
end
You can also use attr_accessor :skip_action instead of private method because controller is just object.

before_action extend from superclass

I have next definitions:
ApplicationController:
before_action :set_resource, only: [:edit, :update, :destroy]
...
private
def set_resource
...
OtherController < ApplicationController
before_action :set_resource, only: [:new_action1, :new_action2]
...
def new_action1
....
def new_action2
I expect set_resource method will be called before actions edit, update, destroy, new_action1, new_action2, but it right only for methods: edit, update, destroy
I faced almost the same problem today while writing plugin for Redmine.
There is an issues_controller with callbacks in Redmine:
OtherController < ApplicationController
before_filter :find_issue, :only => [:show, :edit, :update]
before_filter :authorize, :except => [:index]
end
And I added another action in module that is being included in issues_controller:
module IssuesControllerPatch
def self.included(base)
before_filter :find_issue, :only => [:show, :edit, :update, :merge]
end
end
What happened here is newly added filter for merge action gets called after authorize method, thus failing authorization.
To solve the problem, I overrode authorize method like that:
module IssuesControllerPatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
alias_method :default_authorize, :authorize
alias_method :authorize, :authorize_with_merge
end
end
module InstanceMethods
def authorize_with_merge
find_issue if params[:action] == "merge" && params[:controller] == "issues"
default_authorize
end
end
end
Not quite elegant, but works like a charm. This should help you, as well.

before_action for specific controller

class ApplicationController < ActionController::Base
before_action :test, only: [:index]
def test
ap 'test'
end
end
The above is run before every single index action, be it dogs#index or cats#index or rabbits#index. How should I get it to execute just before cats#index and rabbits#index?
I want to test to be exectuted before actions in many controllers.
You can skip this method:
class ApplicationController < ActionController::Base
before_action :test, only: [:index]
def test
p 'test'
end
end
class DogsController < ApplicationController
skip_before_action :test
end
Just move your call into the controller you want it to run in.
class ApplicationController < ActionController::Base
# nothing here!
def test
# ...
end
end
class CatsController < ApplicationController
before_action :test, only: [:index]
end
class RabbitsController < ApplicationController
before_action :test, only: [:index]
end
It is pretty simple actually
Create before_action in application_controller and check if: that incoming request is for concerned_controller.
class ApplicationController < ActionController::Base
before_create :assign_setting, only: :create, if: :registration_controller?
def registration_controller?
params["controller"] == "registrations"
end
def assign_settings
# your code
puts "settings applied"
end
end

Cannot expire cached fragment by default methods in Ruby on Rails + Devise

I'm using Devise as authenticating solution in Rails and I have a cached fragment :recent_users.
I want this fragment to expire when a new user is registered, changed or removed, so I put in my(manually created) users_controller.rb
class UsersController < ApplicationController
cache_sweeper :user_sweeper, :only => [:create, :update, :destroy]
...
But my fragment does not expire when new creates or changes.
My user_sweeper contains basic prescriptions
class UserSweeper < ActionController::Caching::Sweeper
observe User
def after_save(user)
expire_cache(user)
end
def after_destroy(user)
expire_cache(user)
end
private
def expire_cache(user)
expire_fragment :recent_users
end
end
What am I doing wrong?
Problem solved!
I followed this steps and everything works:
$ mkdir app/controllers/users
$ touch app/controllers/users/registrations_controller.rb
In registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
cache_sweeper :user_sweeper, :only => [:create, :update, :destroy]
end
Problem was that Registrations in Devise is a separate controller.
Put this in applications_controller.rb
class ApplicationController < ActionController::Base
cache_sweeper :user_sweeper, :only => [:create, :update, :destroy]
...

before_filter :require_owner

I have a number of resources (Trips, Schedules, etc) with actions that should be limited to just the resource's owner.
How do you implement code with a #require_owner method defined in ApplicationController to achieve this? Ideally, the code will look up the inheritance chain for the owner so the before_filter will work on a :comment that belongs_to :trip that belongs_to :user.
class TripsController < ApplicationController
belongs_to :member
before_filter :require_owner
...
end
I don't fully follow the description (would a comment really be owned by the trip owner?), but expanding slightly on jonnii's answer, here is an example that restricts the trip controller:
class ApplicationController < ActionController::Base
...
protected
# relies on the presence of an instance variable named after the controller
def require_owner
object = instance_variable_get("##{self.controller_name.singularize}")
unless current_user && object.is_owned_by?(current_user)
resond_to do |format|
format.html { render :text => "Not Allowed", :status => :forbidden }
end
end
end
end
class TripsController < ApplicationController
before_filter :login_required # using restful_authentication, for example
# only require these filters for actions that act on single resources
before_filter :get_trip, :only => [:show, :edit, :update, :destroy]
before_filter :require_owner, :only => [:show, :edit, :update, :destroy]
...
protected
def get_trip
#trip = Trip.find(params[:id])
end
end
Assuming the model looks like this:
class Trip < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
...
def is_owned_by?(agent)
self.owner == agent
# or, if you can safely assume the agent is always a User, you can
# avoid the additional user query:
# self.owner_id == agent.id
end
end
The login_required method (provided by or relying on an auth plugin like restful_authentication or authlogic) makes sure that the user is logged in and provides the user with a current_user method, get_trip sets the trip instance variable which is then checked in require_owner.
This same pattern can be adapted to just about any other resource, provided the model has implemented the is_owned_by? method. If you are trying to check it when the resource is a comment, then you'd be in the CommentsController:
class CommentsController < ApplicationController
before_filter :login_required # using restful_authentication, for example
before_filter :get_comment, :only => [:show, :edit, :update, :destroy]
before_filter :require_owner, :only => [:show, :edit, :update, :destroy]
...
protected
def get_comment
#comment = Comment.find(params[:id])
end
end
with a Comment model that looks like:
class Comment < ActiveRecord::Base
belongs_to :trip
# either
# delegate :is_owned_by?, :to => :trip
# or the long way:
def is_owned_by?(agent)
self.trip.is_owned_by?(agent)
end
end
Make sure to check the logs as you are doing this since association-dependent checks can balloon into a lot of queries if you aren't careful.
There's a few different ways to do this. You should definitely check out the acl9 plugin (https://github.com/be9/acl9/wiki/tutorial:-securing-a-controller).
If you decide you want to do this yourself, I'd suggest doing something like:
class Trip < ...
def owned_by?(user)
self.user == user
end
end
class Comment < ...
delegate :owned_by?, :to => :trip
end
# in your comment controller, for example
before_filter :find_comment
before_filter :require_owner
def require_owner
redirect_unless_owner_of(#commemt)
end
# in your application controller
def redirect_unless_owner_of(model)
redirect_to root_url unless model.owned_by?(current_user)
end
Forgive me if there are any syntax errors =) I hope this helps!
Acl9 is a authorization plugin. I'd give you the link, but I don't have cut and paste on my iPhone. If no one else provides the link by the time I get to a computer, I'll get it for you. Or you can google. Whichever. :)
I have only just started using it, but it has an extremely simple interface. You just have to create a roles table and a roles_user. Let me know how it goes if you decide to use it.
Or just use inherited resources:
InheritedResources also introduces another method called begin_of_association_chain. It’s mostly used when you want to create resources based on the #current_user and you have urls like “account/projects”. In such cases you have to do #current_user.projects.find or #current_user.projects.build in your actions.
You can deal with it just by doing:
class ProjectsController < InheritedResources::Base
protected
def begin_of_association_chain
#current_user
end
end

Resources