In my application I have permissions set up like so for an admin:
can :read, Journey
can :destroy, Journey
can :update, Journey
And I have controllers like so:
class JourneyController < ApplicationController
authorize_resource class: :journey
def index; end
def show; end
end
module Journeys
class VoidJourneyController < ApplicationController
authorize_resource class: :journey
def show; end
def destroy; end
end
end
This is based on how DHH does his controllers: http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/
Now the issue I have is that because I have a show method inside the VoidJourney controller (this is to show the user some additional information as we talk to an API) it means a user who doesn't have permission to destroy a journey can access it because show is aliased to read and only the destroy is protected in that controller.
CanCanCan has the alias_action method, but that only allows aliasing a method to another for all controllers, not just one.
The only way I could think to handle this was to do:
def show
authorize! :destroy, :journey
end
So that it checks that method against a different permission. But I'd like to avoid having to do that if possible.
Is it possible to alias a method in only one controller to another? And not alias for all controllers. Looking at the docs I can't see this.
Related
I have come across a bit of confusion regarding the use of view helpers controllers. The kind of scenario I have is:
session_helper.rb:
module SessionsHelper
# logs in the given user.
def log_in(user)
session[:user_id]=user.id
end
sessions_controller.rb:
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
end
end
def destroy
log_out
redirect_to root_url()
end
Now, as per the documentation that I have read, it mentions that helpers are used in views, to reduce the amount of coding to be done there.
My question is: how I am able to use the log_in and log_out methods defined in the session_helper in my controller?
If anyone can clear me on this concept would be very much helpful.
Answer on your question:
ActionController::Base.helpers.log_in(user)
But, it's better to place those methods in the controller.
I am working on a application where Users can list their in-game items to trade with other Users. A user's profile url would be something like this:
/users/1/index
And their user listings profile would be something like
/users/1/listings/1
All other resources nested under users would be the same as the latter.
I am trying to implement a method that is called by a before_filter callback that checks to see if a user has blocked or is blocked by the user who owns the profile and respective nested resources such as ability to message them, view their listings etc. If either has blocked each other, then they redirected to the root page of the application. This is the method that I use for the before_filter:
def blocked_relationships
if blocked?
redirect_to :root
end
end
I used another method that checks the state of the relationships between the two users.
This is the method I found and worked on after some research courtesy of the Rails Recipes book:
def blocked?
Relationship.exists?(user_id: current_user.id, other_user_id: params[:user_id], status: "blocked") ||
Relationship.exists?(user_id: params[:user_id], other_user_id: current_user.id, status: "blocked")
end
The problem I have is that this method only works, for example, when User 1 is looking at User 2's items, messages, listings etc. because the url:
/users/2/listings [or items or etc]
will contain a params that makes reference to the user as params[:user_id]. params[:id] in this case and context will refer to the listings id.
BUT, if I am User 1 and I have blocked User 2 and visit User 2's profile, this method will not work because the url /users/2/index will use params[:id] to instead of params[:user_id].
I've been thinking about how to implement this in a DRY way but I can't seem to solve my problem other than doing something like this:
def blocked?
if params[:user_id].blank?
Relationship.exists?(user_id: current_user.id, other_user_id: params[:id], status: "blocked") ||
Relationship.exists?(user_id: params[:id], other_user_id: current_user.id, status: "blocked")
else
Relationship.exists?(user_id: current_user.id, other_user_id: params[:user_id], status: "blocked") ||
Relationship.exists?(user_id: params[:user_id], other_user_id: current_user.id, status: "blocked")
end
end
I also considered the possibility that I'm not even implementing my blocking feature correctly, but before I address that issue, I was wondering if anyone had any ideas on how to solve this problem. Any help or feedback would be greatly appreciated and I would be happy to add anymore information for clarification. Thanks!
Why not other_id = params[:user_id] || params[:id]? This is a way to override :id when :user_id is present.
About your blocking feature though, to me I'd like to see a user even if I've blocked them. I'd create a blocked_by_user_id field on the Relationship to see who did the blocking and only disallow the blocked party from seeing the user's profile.
You'd probably want to checkout authorization gems for rails like cancan or related (it's not my favorite but the most popular). However, you could handle it like this:
class User
has_many :relationships,
scope :accessible_by,
->(user) { where.not id: user.relationships.where(status: :blocked).pluck(:other_user_id) }
end
Then use the relationship User.accessible_by(current_user) on your controller instead of plainly User to retrieve resources. For example:
class UsersController < ApplicationController
def index
#users = User.accessible_by(current_user)
# bleh
end
def show
#user = User.accessible_by(current_user).find(params[:id])
# etc
end
end
When the resource is nested under a user you could do this:
class Users::PicturesController < UsersController
def index
#pictures = User.accessible_by(current_user)
.find(params[:user_id]).pictures
end
def show
#picture = User.accessible_by(current_user)
.find(params[:user_id]).pictures.find(params[:id])
end
end
When a user tries to access a resource that can't view, ActiveRecord::RecordNotFound will be raised, so you should handle it:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNorFound, with: :rescue_not_found
private
def rescue_not_found
redirect_to root_path,
notice: 'You can\'t access that with your current priveleges. '
end
end
I want to make an admin controller, it has following 3 views index, members(list of users) and commet(all the feedback from the user) for the following i made following actions in controllers
class AdminController < ApplicationController
before_filter :authenticate_user!
def index
#members = User.all
#comment = Feedback.all
end
def member
#members = User.all
end
def comment
#comment = Feedback.all
end
end
Now I want to make comment and member view such that the admin can delete or edit the feedback or user, now how should write the methods for that?, since all this is happening in the same page?
I did not know what to search for this problem. So I am posting it, it might be repeated
I've been trying Padrino framework in one of my project, and there is one thing that really annoys me. I want to implement just for instance a user registration process using OmniAuth and want to break my request handler (controller's action) to separate methods, like this:
get ":provider/callback" do
#user = find_the_user_by_oauth(request)
create_user unless #user
store_user_in_session
end
def find_the_user_by_oauth(request)
#...
end
def store_user_in_session
session[:user_id] = #user.id
end
I know it would be nicer to push the logic to the model layer, but my question is, how could I break a controller logic to separated methods and share information among them (like using instance variables). In Rails I created these methods in the private scope of my controller, but here I should extend the Application class because it throws Undefined method exception for the previous code. I tried Helpers, but helpers don't know the instance variables, so you should pass the variables every time.
What is the good way to make my controller actions clean in Padrino?
To define a method inside an Padrino Controller you can use define_method instead of def.
For your example, do something like this:
Admin.controllers :dummy do
define_method :find_the_user_by_oauth do |request|
request.params["username"]
# ...
end
define_method :store_user_in_session do
session[:user_id] = #user
end
get :test do
#user = find_the_user_by_oauth(request)
create_user unless #user
store_user_in_session()
session.inspect
end
end
Padrino runs the block sent to Admin.controllers using instance_eval.
See this answer for the differences https://stackoverflow.com/a/3171649 between define_method and def
possible offtopic, but would you consider to use Espresso Framework instead.
then you'll can solve your issue as simple as:
class App < E
def index provider, action = 'callback'
#user = find_the_user_by_oauth
create_user unless #user
store_user_in_session
end
private
def find_the_user_by_oauth
# provider, action are accessed via `action_params`
# action_params[:provider]
# action_params[:action]
end
def store_user_in_session
session[:user_id] = #user.id
end
end
My foo controller is defined as such:
class FooController < ApplicationController
before_filter :authenticate, :only => :some_action
def show
send(some_action)
end
def some_action
end
private
def authenticate
# redirect user if not authenticated
end
end
Given I am not logged in, when I navigate directly to the some_action action, I am redirected to the front page. When I navigate to the show action, however, I am not.
The before_filter doesn't run when I call the action through the send.
Right now, my workaround is to call authenticate directly from my some_action action.
Any ideas about why the before_filter doesn't get called through send and how to fix it? Is this intended behavior for send being called through a controller?
Thanks!
send is a ruby metaprogramming method. You should only be using it in special circumstances, rather than all the time.
How about this?
class FooController < ApplicationController
before_filter :authenticate, :only => [:show, :some_action]
def show
...
end
def some_action
...
end
end