Question

J'ai mis tout mon code d'authentification d'utilisateur au même endroit, à savoir lib / auth.rb. Cela ressemble à ceci:

lib / auth.rb

module Admin

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

end

J'inclus ce module dans l'assistant d'application. Ces fonctions sont donc disponibles dans toutes les vues:

application_helper.rb

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

Et je l'inclue également dans le contrôleur d'application, de sorte que les contrôleurs puissent également appeler les fonctions:

application.rb

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

Jusqu'ici, tout va bien.

Le problème est que mon application ne ressemble pas à une application Web normale. Plus précisément, plusieurs utilisateurs peuvent être connectés au système à partir du même ordinateur en même temps (en utilisant le même navigateur). Je fais une authentification pour des actions en regardant toutes les personnes qui sont connectées à partir de cette adresse IP et si elles peuvent toutes le faire, cela passe.

Cela signifie que si un administrateur veut faire quelque chose, il doit d'abord connecter tout le monde à l'autre, ce qui est ennuyeux. Mais nous voulons le sceau d'approbation de l'administrateur sur tout ce que fait l'administrateur. Donc, la suggestion qui m’était donnée était de permettre à l’administrateur de fournir un combo nom d'utilisateur / mot de passe sur toute page à laquelle ils n'auraient normalement pas accès (par exemple, une page "modifier l'utilisateur" aurait ces champs de saisie supplémentaires) et les routines d'authentification vérifier pour cela. Cela signifie

Admin::do_i_have_permission_to?(permission)

doit obtenir les paramètres de la requête en cours. Je ne peux pas simplement utiliser params [: toto] comme dans un contrôleur, car params n'est pas défini; De même, request.parameters [: foo] ne fonctionnera pas non plus. Ma recherche a révélé:

  • Les paramètres de recherche actuels sont dans la requête en cours,
  • La demande actuelle est dans le contrôleur actuel,
  • Le contrôleur actuel est dans le répartiteur actuel et
  • Je ne suis pas sûr que le répartiteur actuel soit conservé quelque part.

Cela dit, l’expérience m’a appris que, lorsque je saute dans tous ces obstacles, je suis très probablement mal à l'aise. Alors, quelle est la bonne façon de le faire? Les options que j'ai envisagées sont:

  • Il suffit de déplacer toutes les fonctions actuellement dans auth.rb dans ApplicationHelper où (je pense) elles auront accès à la requête et autres. Fonctionne, mais encombre l’assistant.
  • Déplacez toutes les fonctions ailleurs, ils verront ces méthodes (je ne sais pas où)
  • Il me manque simplement quelque chose.
Était-ce utile?

La solution

Dans une application Rails typique, les informations d'authentification sont stockées dans la session active, pas dans les paramètres. En tant que tel, il est assez simple d’écrire un assistant qui fait ce que vous voulez.

Il semble assez peu orthodoxe de créer un module qui est ensuite inclus dans ApplicationHelper. L'approche traditionnelle consiste à créer un assistant distinct qui, dans ce cas, serait probablement appelé AuthenticationHelper. Cela peut ensuite être inclus dans tous les contrôleurs requis ou, si vous préférez, chargé dans ApplicationController pour le rendre disponible universellement.

De manière générale, les assistants ne doivent pas inclure d’autres assistants. Il est préférable de simplement charger plusieurs assistants dans un contrôleur donné.

Les méthodes d'assistance ont un accès complet à toutes les variables d'instance déclarées dans le contexte du contrôleur à partir duquel elles fonctionnent. Pour être précis, il s’agit de variables d’instance uniquement (@name) et non de variables locales (nom). Des méthodes auxiliaires sont également exécutées pour une vue particulière.

De plus, je ne sais pas pourquoi un utilisateur fournirait des informations d'identification et effectuerait une opération dans la même étape, du moins pour les applications Web traditionnelles. Généralement, le processus consiste à se connecter, puis à exécuter une action séparément.

Toutefois, dans le cas d’une API où chaque transaction est une opération indépendante, l’approche la plus simple consiste à extraire les paramètres de requête pertinents relatifs à l’authentification, à établir des variables d’instance de contrôleur, puis à exécuter la procédure suivante. demande particulière compte tenu des contraintes imposées par les informations d'identification.

L’approche que j’adopte habituellement pour ce genre de choses consiste à superposer une structure d’authentification dans ApplicationController lui-même, qui peut effectuer les contrôles requis. Ce sont des méthodes protégées.

Alors qu’il est tentant d’en ajouter tout un tas, comme can_edit_user? et can_create_group? ceux-ci deviennent très vite incontrôlables. C'est une conception plus simple à mettre en crochet pour un can_perform polyvalent? ou has_authority_to? méthode qui passe une opération et tous les paramètres requis.

Par exemple, une implémentation très approximative:

  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

Évidemment, vous voudriez transférer beaucoup de contrôles d’autorité dans des blocs before_filter pour éviter les répétitions et améliorer la cohérence.

Un exemple de structure complète peut être plus utile, tel que le système d'authentification d'utilisateur Wristband:

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top