2013-03-29 20 views
5

Sto usando cancan per autorizzare le azioni del mio controller. Una delle classi in cui l'accesso è autorizzato da cancan è un albero, implementato con acts_as_ancestry. Sto riscontrando problemi nell'uso di load_and_authorize_resource quando l'utente non è autorizzato ad accedere al livello di root, ma è consentito l'accesso a partire da un nodo interno.Come posso impostare una relazione ancestrale con cancan su un nodo interno dell'albero?

Ecco alcune definizioni di classe relavant:

class User < ActiveRecord::Base 
    belongs_to :organization, :inverse_of => :users 
end 

class Post < ActiveRecord::Base 
    belongs_to :organization, :inverse_of => :posts 
end 

class Organization < ActiveRecord::Base 
    has_ancestry :cache_depth => true 
    has_many :users, :inverse_of => :organization 
    has_many :posts, :inverse_of => :organization 
end 

Le regole per la gestione dei messaggi sono "È possibile gestire i messaggi in qualsiasi organizzazione sotto la vostra". Il mio cancan definizione abilità è questa:

class Ability 
    include CanCan::Ability 

    def initialize(user) 
    user ||= User.new 

    # subtree_ids is added by acts_as_ancestry 
    can :manage, Post, {:organization_id => user.organization.subtree_ids} 
    end 
end 

Nel controllore, ho questo (altre azioni omesso)

class PostsController < ApplicationController 
    load_and_authorize_resource :post 

    def index 
    end 

    def new 
    end 
end 

Tutto funziona bene quando l'utente autorizzato appartiene all'organizzazione root. Tuttavia, quando accedo come utente autorizzato su un nodo interno, l'azione index funziona correttamente, ma quando viene richiamata la nuova azione, ottengo un errore di autorizzazione can can.

Ecco quello che vedo nel registro:

Access denied on new #<Post id: nil, organization_id: 1> 

Il organization_id 1 (la radice) è venuta dallo schema:

create_table "posts", :force => true do |t| 
    t.integer "organization_id", :default => 1 
end 

Con Cancan, la nuova azione costruirà un nuovo Post e assegnarlo a @post. Quando esegue questa operazione, inizializzerà tutti gli attributi con i valori presi dalla definizione can in Abilities.rb. Tuttavia, non farà nulla se quegli attributi sono Array, Hash o Ranges e il valore predefinito finisce sullo schema.

Come autorizzare gli utenti a gestire i post nella loro sottostruttura, ma quando creano un nuovo post, lo impostano automaticamente sulla loro organizzazione?

+0

Così ho fatto il seguente progetto per testare il tuo problema ed è qui: https://github.com/robmathews/cancan_test.git – Rob

risposta

3

In Cancan, se la variabile @post è già inizializzato da voi, non chiamerà load_resource su di esso , solo la parte autorizza. Vedi questa parte della documentazione: https://github.com/ryanb/cancan/wiki/Authorizing-controller-actions, "Sovrascrivi caricamento".

Quindi la soluzione più semplice è quello di prendere il controllo dell'inizializzazione te stesso e renderlo quello che ti serve, come qui:

class PostsController < ApplicationController 
    before_filter :initialize_post, :only => [:new, :create] 

    def initialize_post 
    @post = current_user.organization.posts.build(params[:post]||{:name=>'Smashing Kittens'}) 
    end 

    load_and_authorize_resource :post 
    def index 
    end 

    def new 
    end 

    def create 
    end 
end 

Potete vederlo lavorare in questo progetto di test che ho creato dal tuo post: https://github.com/robmathews/cancan_test .

1

Ho avuto un problema simile e ha finito per scrivere i permessi relativi Antenati in blocchi in questo modo:

can :manage, Post do |post| 
    post.organization.subtree_ids.include?(user.organization_id) 
end