I'm trying to add an action link to an active scaffold controller using
if current_user.can? :update, Post
config.action_links.add 'export', :label => 'Export', :page => true
end
but whatever controller I try to use, I always get undefined method current_user
So, how can I check if the logged user can/cannot do something?
I've even tried with
def ability
#ability ||= Ability.new(self)
end
delegate :can?, :cannot?, :to => :ability
as suggested here, but without success.
What's wrong with this?
Note that in the normal case ActiveScaffold config is done in the class not the instance so there is no current_user set because there is no request yet.
current_user is a typically defined method, but it is one that you must add. it is not supplied by rails. So, you need a current_user method in your ApplicationController. There are tons of examples but I would look at the authlogic example app on github
Related
I'm using Devise along with CanCan/Rolify for authentication for my rails app and I'm having a hard time understanding how to pass Devise methods to namespaced controllers.
I'm using a standard users model and controller for the main part of my app, everything on that side works well and good. However, I have set it up that if an account with a specific role (which is determined after registration) logs in using the main login page, it will redirect them to namespaced views and controllers.
Lets say that user_id: 3 logs in and is redirected to 0.0.0.0/external/dashboard, I can call Devise's current_user method on the dashboard page after redirection and returns the correct information. However, when I set a link_to for that user to be able to edit his profile at 0.0.0.0/external/users/3/edit I receive the following error:
Started GET "/external/users/3/edit" for 127.0.0.1 at 2017-08-31 21:40:59 -0400
ActionController::RoutingError (undefined local variable or method `current_user' for External::UsersController:Class)
routes
devise_for :users, controllers: { confirmations: 'confirmations', registrations: 'registrations' }
# External routes
namespace :external do
resources :properties, :clients, :orders, :deeds, :mortgages, :users, :owners, :dashboard
end
external/dashboard view
<%= user_name_helper(current_user) %>
user_name_helper (produces user edit link)
def user_name_helper(user)
if user.name.blank?
link_to "Update your profile here.", edit_external_user_path(user)
else
"Hello, #{user.name}"
end
end
Error happens when I click the link produced by this helper.
But <%= link_to fa_icon("sign-out", text: "Logout"), destroy_user_session_path, method: :delete %> works fine from 0.0.0.0/external/dashboard.
So I am confused as to why I can call current_user within the view for the External::DashboardController but can't call it within the External::UsersController. All namespaced controllers inherit directly from ApplicationController and I want to keep only using a single users model. I've searched through a bunch of different articles but can't seem to find an answer with a configuration similar to mine, so can anyone explain to me what I might be doing wrong and how to get this to work as well as allowing me to have the ability to use any other Devise methods in my namespaced views? Thanks in advance.
Welp, I figured it out. The issue was that I was calling a conditional before_filter in my external/ controllers as follows:
before_filter :client_only unless current_user.has_role? :admin
Once I removed that everything worked as intended. I still have the following before_filter in place which works.
before_filter :client_only
...
private
def client_only
redirect_to root_path unless current_user.is_client?
end
Not entirely sure why the conditional filter wouldn't work the way I originally had it so if anyone has any insight and feels like sharing then I'm all ears, thanks.
I am a bit confused regarding CanCan Gem. I basically understand how to set up abillity.rb. For example lest say we have the following code:
// in abillity.rb
user ||= User.new
can [:update, :destroy, :edit, :read], Book do |book|
book.dashboard.user_id == user.id
end
And then lets say we have the following books controller:
// books_controller.rb
load_and_authorize_resource
def destroy
if can?(:destroy, #book)
#book.destroy!
redirect_to happy_world_path
else
redirect_to not_happy
end
end
My question is: Do we need to check 'can?(:destroy, #book)'?
From my understanding 'load_and_authorize_resource' will not even allow access to this method if we don't have abillity to destroy it.
Yo do not need to add if can?(:destroy, #book) in your action if you use load_and_authorize_resource
Like the README say
Setting this for every action can be tedious, therefore the load_and_authorize_resource method is provided to automatically authorize all actions in a RESTful style resource controller.
If an user without authorization try to destroy, he get a unauthorized response ( not remember if is a 401 code)
Maybe you can use if can?(:destroy, #book) in your views, to do no show thte destroy button. Like also in Check Abilities & Authorization section
What is the correct way to authorize and check abilities for a namespaced, model-less controller using CanCanCan?
After much googling and reading the wiki, I currently have
#controllers/namespaces/unattacheds_controller.rb
def Namespaces::UnattachedsController
authorize_resource class: false
def create
# does some stuff
end
end
#models/ability.rb
def admin
can [:create], :namespaces_unattacheds
end
#view/
<%= if can? :create, :namespaces_unattacheds %>
# show a create form to authorized users
<% end %>
This is not correctly authorizing the controller. Admins can see the conditional create form, but are not authorized to post to the create action.
post :create, valid_params
Failure/Error: { it { expect( flash ).to have_content "Successfully created" }
expected to find text "Successfully created"
got: "You are not authorized to access this page."
In one example, the wiki suggests creating a separate Ability class for a namespaced controller. https://github.com/CanCanCommunity/cancancan/wiki/Admin-Namespace
Is there a simpler way to achieve this? This app uses many namespaced controllers, I don't really want to create an ability class for each one.
Is there correct syntax to refer to the namespaced controller in the Ability class?
can [:create], Namespaces::Unattacheds
can [:create], :namespaces_unattacheds
can [:create], namespaces/unattacheds
????
It sounds like you are setting permissions on the Namespaces::Unattacheds model, which means your controller doesn't need to do:
authorize_resource class: false
Your controller does have a model. Maybe it also inherits from ApplicationController? (That would be a logical thing to do.)
If you are trying to avoid affecting certain controller methods, use only/except clauses, as described here:
https://github.com/CanCanCommunity/cancancan/wiki/Authorizing-controller-actions#choosing-actions
I don't think the namespace depth is an issue IF it matches between your model and your controller. You just need load_and_authorize_resource and the proper form in ability.rb:
can [:create], Namespaces::Unattacheds
Maybe not the prettiest solution but I managed to achive this by adding
skip_authorization_check
before_action { raise CanCan::AccessDenied unless current_user.can?(params[:action].to_sym, ::namespaces_unattacheds) }
If you do it like this, you can pass whatever you want from this controller to the ability class.
You need to add the can? method first to be able to use this https://github.com/CanCanCommunity/cancancan/wiki/Ability-for-Other-Users
I'm trying to override the index action of the ActiveAdmin controller for it to display results for the current_user instead of all results.
controller do
def index
#user_tasks = UserTask.where(:user_id => current_user.id).page(params[:page])
end
end
When accessing ActiveAdmin, an exception in thrown:
ActionView::Template::Error (undefined method `base' for nil:NilClass):
1: render renderer_for(:index)
I'm using rails 3.1 and the latest ActiveAdmin version. gem "activeadmin", :git => 'https://github.com/gregbell/active_admin.git'.
I don't know why but
controller do
def index
index! do |format|
#user_tasks = UserTask.where(:user_id => current_user.id).page(params[:page])
format.html
end
end
end
did the trick.
This is not required any more.
ActiveAdmin 0.4.4 now supports scoping queries without overriding this method.
please see here: http://activeadmin.info/docs/2-resource-customization.html#scoping_the_queries
If your administrators have different access levels, you may sometimes
want to scope what they have access to. Assuming your User model has
the proper has_many relationships, you can simply scope the listings
and finders like so:
ActiveAdmin.register Post do
scope_to :current_user
# or if the association doesn't have the default name.
# scope_to :current_user, :association_method => :blog_posts
end
Let override the action like this:
controller do
def scoped_collection
# some stuffs
super.where("type = ?", "good")
end
# other stuffs
end
By this way, you also can run the export functions (to xml, csv, ...) normally with new collection that you have overridden.
In my test, it just works for where condition and scope, not for limit.
Refer from this: https://github.com/activeadmin/activeadmin/issues/642
Trying to redirect users to their associated 'home' page after successful login w/out nil'ing out stored_location_for(resource_or_scope)...which is giving me some endless redirect loops (pretty sure I've set it up wrong).
Regardless, I'm looking for a better approach...
Devise's docs state: After
signing in a user, confirming the
account or updating the password,
Devise will look for a scoped root
path to redirect. Example: For a
:user resource, it will use
user_root_path if it exists,
otherwise default root_path will be
used. This means that you need to set
the root inside your routes: root :to => "home"
I'm sorta a newbie...how does one go about generating this home_root_path for each user?
rDocs also mention:
-- (Object) after_sign_in_path_for(resource_or_scope)
The default url to be used after
signing in. This is used by all Devise
controllers and you can overwrite it
in your ApplicationController to
provide a custom hook for a custom
resource.
By default, it first tries to find a resource_root_path, otherwise
it uses the root path. For a user
scope, you can define the default url
in the following way:
map.user_root '/users', :controller => 'users' # creates user_root_path
map.namespace :user do |user|
user.root :controller => 'users' # creates user_root_path
end
but these just gives me undefined local variable or methodmap' for #ActionDispatch::Routing::Mapper:…` errors.
If you would like to redirect using a route in answer to your question below:
how does one go about generating this home_root_path for each user?
This will work if you place it in your config/routes file. It will redirect a user to articles#index after (for example) a successful confirmation.
# Rails 4+
get 'user_root' => 'articles#index', as: :user_root
# Rails 3
match 'user_root' => 'articles#index', as: :user_root
See Devise: Controllers Filters and Helpers
You could try something like this:
application_controller.rb:
def after_sign_in_path_for(resource_or_scope)
# return home_page_path for user using current_user method
end
Dug around a bit to figure out the same thing. #polarblau's answer is correct,
def after_sign_in_path_for(resource_or_scope)
user_info_path(current_user)
end
where user_info_path is the path to the page you wish to display.
Also, I would allow this to fall back to super just in case, although I'm not entirely sure if that is necessary...
def after_sign_in_path_for(resource)
if resource.is_a(User)
user_info_path(resource)
else
super
end
end
I spent several hours trying to get the same functionality, and this is the code that ended up working for me:
def after_sign_in_path_for(resource)
current_user
end
If I ever tried current_user_path, I always got undefined local variable or method current_user_path errors.
Also, I'm using Rails 3.2.8 and Devise 2.1.2.
Hope that helps.
Based on #SnapShot answer, this worked for me. I'm using multiple devise models, trying to redirect back to the users profile edit page.
get 'user_root', to: redirect('/users/edit'), as: :user_root
ROR 7 answer
get '/users/home' => 'application#test', as: :user_root