rails cancan access is denied - ruby-on-rails

I have combined custom authentication with CanCan and to my frustration it is not exactly working as expected. So, here is the snippet of code in my controller
class Profile < ActiveRecord::Base
belongs_to :user, :inverse_of => :profile
delegate :email, to: :user
validates_presence_of :user_id
end
class Api::V1::ProfileController < Api::V1::UserActivityController
load_and_authorize_resource :user
load_and_authorize_resource :profile, :through => :user, :class => "Profile"
def index
#profile = subject_user.profile
authorize! :index, #profile
end
end
Here is the Ability class
class Ability
include CanCan::Ability
def initialize(user)
alias_action :index, :show, :to => :read
can :read, Profile
end
end
If I try can :read, :all it works fine but with can :read, Profile it fails. I had the initial hypothesis that maybe the delagate in the Profile class causes the denial but that is not the case. any thoughts?
The page is supposed to render into JSON using rabl
object #profile
attributes :first_name, :last_name, :gender
-- Update --
I figured that namespacing and nesting causes the issues with my code. My controlled is in fact namespaced (I updated the controller code) and I am trying to access Profile throught he following routing:
namespace :user
namespace :profile
end
I tried the following code for load_and_authorize_resource in ProfileController (also updated the controller code above) but it is not working. Any thoughts on how it should be modified? Does anything need to be changed in the Ability class?
load_and_authorize_resource :user
load_and_authorize_resource :profile, :through => :user, :class => "Profile"

Related

has_many :through broke some code

So i'm relatively new to RoR, and am having some issues in trying to get my code back up and working. So previously I had users, and wikis that users could create. I've set up so that users can subscribe and get premium status to make wikis private. Now I'm in the process of making it so that Premium users can add standard users as collaborators to the wiki. I've decided to got about associating them through has_many :through relationships.
The issue I'm running into so that some of my buttons have started making errors that I don't understand. The one I'm stuck on right now is when showing the page that has a create new wiki button on it.
This is the error I am getting when I added the has_many through: relationship
No route matches {:action=>"new", :controller=>"wikis", :format=>nil, :user_id=>nil} missing required keys: [:user_id]
Here are the models:
collaborator.rb
class Collaborator < ActiveRecord::Base
belongs_to :wiki
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
...
has_many :collaborators
has_many :wikis, :through => :collaborators
end
wiki.rb
class Wiki < ActiveRecord::Base
belongs_to :user
has_many :collaborators
has_many :users, :through => :collaborators
end
The important bits of the wiki_controller.rb
def new
#user = User.find(params[:user_id])
#wiki = Wiki.new
authorize #wiki
end
def create
#user = current_user
#wiki = #user.wikis.create(wiki_params)
authorize #wiki
if #wiki.save
flash[:notice] = "Wiki was saved"
redirect_to #wiki
else
flash[:error] = "There was an error saving the Wiki. Please try again"
render :new
end
end
And finally the show.html.erb file the button is located in.
<div class="center-align">
<%= link_to "New Wiki", new_user_wiki_path(#user, #wiki), class: 'btn grey darken-1' %>
</div>
If I'm missing any files or relevant info please let me know. This may be a simple stupid answer but I'm stuck for the life of me.
Thanks in advance.
Edit:
Here is the requested added info, first up the show info in the users_controllers.rb
def show
#wikis = policy_scope(Wiki)
end
the corresponding policy scope I'm using in the user_policy.rb
class UserPolicy < ApplicationPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
#user = user
#scope = scope
end
def resolve
wikis = []
all_wikis = scope.all
all_wikis.each do |wiki|
if wiki.user == user || wiki.users.include?(user)
wikis << wiki
end
end
end
wikis
end
end
and the route.rb file
Rails.application.routes.draw do
devise_for :users
resources :users, only: [:update, :show] do
resources :wikis, shallow: true
end
resources :wikis, only: [:index]
resources :charges, only: [:new, :create]
delete '/downgrade', to: 'charges#downgrade'
authenticated do
root to: "users#show", as: :authenticated
end
root to: 'welcome#index'
end
Hope it helps
I found out the problem. I set up the migrate file wrong when originally creating the collaboration model.
Thanks for all of your help.

Rails 3 CanCan nested Resources

Have a nested resource as such
class Dealer < ActiveRecord::Base
has_many :vehicles
end
and
class Vehicle < ActiveRecord::Base
belongs_to :dealer
end
below are my routes.
resources :dealers do
resources :vehicles, :except => [:index]
end
resources :vehicles, :only => [:index]
looking at the wiki at the github page for cancan I did the following:
class VehiclesController < ApplicationController
load_and_authorize_resource :dealer
load_and_authorize_resource :vehicle, :through => :dealer
def index
#vehicles = Vehicle.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #vehicles }
end
end
end
but now when the admin tries to go to the index page with the abilities:
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
end
end
I get
Couldn't find Dealer with id=
what do i need to change for admin to still be able to do all the actions and yet have others be checked before they can do any action.
The problem is not that he is not authorized to this action. The problem is that CanCan tries to fetch an instance of dealer to load all its vehicles and you have not provided a :dealer_id within params[:dealer_id]. Cancan assumes you would be loading only dealer's vehicles in this controller because you used an load_and_authorieze :through. This authorization should be used within Dealers::VehiclesController.
As long as you only loading vehicles just use load_and_authorize_resource :vehicle. And because load_and_authorize will set #vehicles for you within the before filter there is also no need to load the vehicles explicitly with Vehicle.all.
load_and_authorize is just a convenient method and it assumes some defaults. Once you will come to a point where you have some more complex use case. It will be time to throw away this method and just use CanCan's authorize!, can? and a properly configured Vehicle .accessible_by (for listing) methods.
When using load_and_authorize_resource :vehicle, through: :dealer it expects to receive a dealer_id in the request in order to authorize the dealer.
Since you use except: :index in your routes dealer_id will not be automatically included in the request.
If you don't want to authorize the dealer in the index action you can do something like this (taken from Can Can wiki)
class CommentsController < ApplicationController
load_and_authorize_resource :post
load_and_authorize_resource :through => :post
skip_authorize_resource :only => :show
skip_authorize_resource :post, :only => :show
end

set current user when creating a comment with inherited_resource rails

I am using Rails Inherited_resource gem in my comments controller, and comments is a nested resource so:
resources :projects do
resources :comments do
end
I also have a belongs_to in the comments controller:
belongs_to :project, :finder => :find_by_project_uuid!, :class_name => "Thfz::Project", :polymorphic => true
How can I set the comment's user association to the current_user(user_id) when its created? As user_id is not suppose to be massive assigned.
I tried following:
def begin_of_association_chain
current_user
end
This does set the user id correctly, but I cannot get nested resource working for Project with this.
Same question come when destroy a comment, I will need to find the comment through current_user, so how to achieve this?
So do I have to write my own create and destroy actions?
Thanks :)
Have you tried the following inside comments_controller?
class CommentsController < InheritedResources::Base
before_filter :authenticate_user! # Assuming you are using Devise for authentication
respond_to :html, :xml, :json
belongs_to :project, :finder => :find_by_project_uuid!, :class_name => "Thfz::Project"
def create
#comment = build_resource
#comment.author = current_user
create!
end
end

cancan load_resource for nested resources don't build has_one association

I have an Owner model wich has_one Address, and accepts_nested_attributes for it. When loading a Owner, for the :new action, I expected the :load_resource method to build the association like #owner.build_address, but this don't happen with the code below:
class OnwersController < ApplicationController
load_and_authorize_resource
load_resource :address, :through => :owner, :singleton => true, :parent => false
Is this the expected behaviour and I have to do #owner.address = #address by my own ?
Thank you
You may refer to the answer. https://stackoverflow.com/a/7015900/950843
It works for me except I have to ignore both :new and :create actions.

Inherited Resources and CanCan 3 levels nesting

I have a problem with 3 levels nesting of models in CanCan combined with Inherited Resources. I've read that we should nest everything up to 2 levels, but I had to put everything under account model and now I've tried doing this in CanCan:
load_and_authorize_resource :account
load_and_authorize_resource :project, :through => :account
load_and_authorize_resource :model, :through => :project
That gives me #account variable that has a value of #project, like it is overwriting that. #project is what is supposed to be and #model too. Is that fault of mine, CanCan's, Inherited Resources or just CanCan isn't supporting 3 levels nesting? Also, I do this in IR for the ModelsController.
belongs_to :account, :finder => :find_by_name! do
belongs_to :project, :finder => :find_by_name!
end
Another strange thing is when i remove the part load_and_ from CanCan's definition. It works then, but I've read that it can be dangerous not to use the load part.
Can I use only the authorize_resource or should I do something with CanCan?
Your authorizations have been correct as far as I can say.
The developer of the CanCan gem ryan posted how this should behave: https://github.com/ryanb/cancan/issues/127#issuecomment-364475
That means that your
load_and_authorize_resource :account
load_and_authorize_resource :project, :through => :account
load_and_authorize_resource :model, :through => :project
will end up in an block like this (here: create action. For other actions should the last authorize! and the #model change):
#account = Account.find(params[:account_id])
authorize! :read, #account
#project = #account.projects.find(params[:project_id])
authorize! :read, #project
#model = #project.models.build
authorize! :new, #model
I hope that this answer can help developers looking for nested cancan authorization :-) .
source: https://github.com/ryanb/cancan/issues/127#issuecomment-364475
ps: wrong behavior for /accounts/1/projects/2/models/new:
load_and_authorize_resource :project
load_and_authorize_resource :model, :through => :project
This is kind of a security issue, because this will do
#project = Project.find(params[:project_id])
[...]
, and does not check if the current account is allowed to read the linked account '1'.
And it does not check, if the project '2' is really a project of account '1'.

Resources