Domanda

Ho messo tutto il mio codice di autenticazione dell'utente in un unico posto, vale a dire lib / auth.rb. Sembra così:

lib / auth.rb

module Admin

  def do_i_have_permission_to?(permission)
    # Code to check all of this goes here
  end

end

Includo questo modulo come parte dell'helper dell'applicazione, quindi queste funzioni sono disponibili in tutte le visualizzazioni:

application_helper.rb

require 'auth'
module ApplicationHelper
  include Admin
  # other stuff here
end

E lo includo anche come parte del controller dell'applicazione, così anche i controller possono chiamare le funzioni:

application.rb

require 'auth'
class ApplicationController < ActionController::Base
  include Admin
end

Finora tutto bene.

Il problema è che la mia applicazione non è come una normale app web. In particolare, più di un utente può accedere al sistema dallo stesso computer contemporaneamente (utilizzando lo stesso browser). Faccio l'autenticazione per le azioni guardando tutte le persone che hanno effettuato l'accesso da quell'IP e se possono farlo tutte, passa.

Ciò significa che, se un amministratore vuole fare qualcosa, quell'amministratore deve prima disconnettere tutti gli altri, il che è fastidioso. Vogliamo il sigillo di approvazione dell'amministratore su tutto ciò che fa l'amministratore. Quindi il suggerimento che mi è stato dato era di averlo in modo che l'amministratore potesse fornire una combinazione nome utente / password su qualsiasi pagina a cui normalmente non avrebbero accesso (ad esempio una pagina "modifica utente" avrebbe questi campi di input extra) e le routine di autenticazione controlla quello. Questo significa

Admin::do_i_have_permission_to?(permission)

deve ottenere i parametri della richiesta corrente. Non posso semplicemente usare params [: foo] come farei in un controller, perché params non è definito; allo stesso modo request.parameters [: foo] non funzionerà. La mia ricerca ha rivelato:

  • I parametri di ricerca correnti si trovano nella richiesta corrente,
  • La richiesta corrente si trova nel controller corrente,
  • Il controller corrente si trova nel dispatcher corrente e
  • Non sono sicuro che l'attuale dispatcher sia tenuto da nessuna parte.

Detto questo, l'esperienza mi dice che quando salto attraverso questi tanti cerchi, molto probabilmente lo sto facendo nel modo sbagliato. Allora, qual è il modo giusto per farlo? Le opzioni che ho considerato sono:

  • Basta spostare tutte le funzioni attualmente in auth.rb in ApplicationHelper dove (penso) avranno accesso alla richiesta e così via. Funziona, ma ingombra l'inferno dall'aiutante.
  • Spostando tutte le funzioni da qualche altra parte vedranno quei metodi (non so dove)
  • Mi manca semplicemente qualcosa.
È stato utile?

Soluzione

In una tipica applicazione Rails, le informazioni di autenticazione vengono archiviate nella sessione attiva, non i parametri. Come tale, è abbastanza semplice scrivere un aiuto che fa quello che vuoi.

Sembra piuttosto poco ortodosso creare un modulo che viene poi incluso in ApplicationHelper. L'approccio tradizionale è quello di creare un helper separato che in questo caso verrebbe probabilmente chiamato AuthenticationHelper. Questo può quindi essere incluso in qualsiasi controller richiesto o, se preferisci, caricato in ApplicationController per renderlo disponibile universalmente.

In termini generali, gli Helpers non dovrebbero includere altri Helpers. È meglio semplicemente caricare più helper in un determinato controller.

I metodi di supporto hanno pieno accesso a tutte le variabili di istanza dichiarate nel contesto del controller da cui operano. Per essere specifici, si tratta solo di variabili di istanza (@name) e non di variabili locali (name). I metodi di supporto vengono eseguiti anche per una vista particolare.

Inoltre, non sono sicuro del motivo per cui un utente dovrebbe fornire credenziali ed eseguire un'operazione nello stesso passaggio, almeno per le app tradizionali basate sul Web. Di solito il processo consiste nell'accedere e quindi eseguire un'azione separatamente.

Tuttavia, nel caso di un'API in cui ogni transazione è un'operazione indipendente, l'approccio più semplice da fare è estrarre i parametri di richiesta pertinenti che si occupano di autenticazione, stabilire alcune variabili di istanza del controller e quindi procedere all'esecuzione del richiesta particolare dati i vincoli imposti dalle credenziali.

L'approccio che di solito seguo per questo genere di cose è quello di sovrapporre una struttura di autenticazione nello stesso ApplicationController che può eseguire i controlli richiesti. Questi sono metodi protetti.

Mentre sei tentato di farne un mucchio intero come can_edit_user? e can_create_group? questi molto rapidamente sfuggono di mano. È un design più semplice da mettere in un gancio per un can_perform generico? o has_authority_to? metodo a cui viene passata un'operazione e tutti i parametri richiesti.

Ad esempio, un'implementazione molto approssimativa:

  class ApplicationController < ActionController::Base
  protected
    def has_authority_to?(operation, conditions = { })
      AuthenticationCheck.send(operation, conditions)
    rescue
      false
    end
  end

  module AuthenticationCheck
    def self.edit_user?(conditions)
      session_user == conditions[:user]
    end
  end

  class UserController
    # ...

    def edit
      @user = User.find(params[:id])

      unless (has_authority_to?(:edit_user, :user => @user))
        render(:partial => 'common/access_denied', :status => :forbidden)
      end
    rescue ActiveRecord::RecordNotFound
      render(:partial => 'users/not_found')
    end
  end

Ovviamente dovresti effettuare molti controlli dell'autorità in blocchi before_filter per evitare la ripetizione e promuovere la coerenza.

Un esempio completo di framework potrebbe essere di maggiore aiuto, come il sistema di autenticazione dell'utente Wristband:

http://github.com/theworkinggroup/wristband/tree/master

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top