Puis-je exposer une route sans authentification ?

Existe-t-il un moyen de créer une route qui contourne l’authentification ?

Cela est utilisé pour l’intégration avec des services tiers.
La nécessité est de fournir au service une URL (les en-têtes ou les charges utiles de tout type ne sont pas pris en charge).
Auparavant, cela était possible en ajoutant « ?api_key=…&api_username= », mais cette option n’existe plus.

L’instance Discourse est hébergée par nos soins.

Vous aurez besoin d’un plugin personnalisé qui crée une route capable d’accepter un contenu et d’agir en conséquence. Sachez que, puisque vous ne pouvez pas authentifier l’utilisateur, vous devrez peut-être mettre en place un moyen d’authentifier le contenu.

Ma règle est de ne pas autoriser les comptes anonymes. Je ne peux donc pas accéder aux routes du plugin. Ce dont j’aurais besoin, c’est d’un moyen de créer une route adaptée à ce type de scénario.

Par exemple,

  # Rails 3.2 par défaut laisse passer la requête avec une session vide
  # Nous sommes ici plus rigoristes en annulant la session / current_user
  # puis en levant une exception CSRF
  def handle_unverified_request
    # NOTE : La clé API est secrète ; son utilisation rend inutile le jeton CSRF
    unless is_api? || is_user_api?
      super
      clear_current_user
      render plain: "[\"BAD CSRF\"]", status: 403
    end
  end

Une façon de notifier, durant ce flux ou tout le flux d’authentification, qu’un contrôleur et une action reçus via des paramètres (définis automatiquement lors de l’appel d’une route) peuvent contourner toutes les vérifications et exécuter la route.

Je ne maîtrise pas parfaitement le fonctionnement global, mais puisqu’il existe une option d’accès anonyme, je suis certain qu’une solution est possible.

Cela permettrait aux utilisateurs de chiffrer la charge utile si nécessaire, mais pour des mises à jour génériques du système, cela serait très utile.

Via un plugin, vous pouvez ajouter votre nouvelle route à DiscoursePluginRegistry.api_parameter_routes afin de continuer à transmettre les identifiants API via des paramètres de requête :

Vous aurez également besoin de la ligne suivante

skip_before_action :redirect_to_login_if_required

dans votre contrôleur personnalisé, si le site nécessite une connexion.

D’accord. Alors, surchargez cette méthode et passez l’action. Je vais essayer et vous tiendrai au courant si ça marche.

# Par défaut, nous autorisons uniquement les en-têtes pour l'envoi des identifiants API
  # Cependant, dans certains scénarios, il est essentiel de les envoyer via des paramètres d'URL
  # nous devons donc ajouter quelques exceptions
  def api_parameter_allowed?
    request_method = @env["REQUEST_METHOD"]&.downcase&.to_sym
    request_format = @env['action_dispatch.request.formats']&.first&.symbol

    path_params = @env['action_dispatch.request.path_parameters']
    request_route = "#{path_params[:controller]}##{path_params[:action]}" if path_params

    if 'm_exposed#endpoint' == request_route && request_method == 'get'
      puts 'Autorisé'
      return true
    end

    PARAMETER_API_PATTERNS.any? do |p|
      (p[:method] == "*" || Array(p[:method]).include?(request_method)) &&
      (p[:format] == "*" || Array(p[:format]).include?(request_format)) &&
      (p[:route] == "*" || Array(p[:route]).include?(request_route))
    end
  end

J’ai fait cela simplement pour minimiser les différences dans git à l’avenir.

Aussi, mon contrôleur :

# frozen_string_literal: true

class MExposedController < ::ApplicationController

  skip_before_action :redirect_to_login_if_required, raise: false

  def endpoint
    render json: { state: "hello world" }
  end

end

Donc maintenant, le point de terminaison ne vérifie plus les paramètres ou les identifiants des en-têtes.

Comment puis-je faire en sorte qu’il vérifie la clé API fournie et n’accorde l’accès que si elle est valide ? (Comme cela fonctionnait auparavant)

@riking @blake ?

Je pense que l’ajout de requires_login à votre contrôleur fera l’affaire. Jetez un œil à certains des autres contrôleurs dans app/controllers pour des exemples.

Eh bien, ça n’a pas marché :slight_smile:

class MExposedController < ::ApplicationController

  requires_login

  skip_before_action :redirect_to_login_if_required, raise: false

Terminé avec 403 Interdit

{"errors":["Vous devez être connecté pour faire cela."],"error_type":"not_logged_in"}

Ceci est déclaré à l’intérieur d’un plugin.

Et vous avez transmis vos identifiants API via des paramètres de requête ? Si c’est le cas, cela signifie que votre route n’a probablement pas été ajoutée correctement à la liste de contournement pour autoriser les identifiants API dans les paramètres de requête. Vous devrez peut-être déboguer la logique de contournement et vérifier si votre route a bien été ajoutée.

Bon, ça fonctionne et contourne tout sans requires_login. J’ai posté comment je l’ai fait. Hmm..

Il semble que je ne puisse le faire fonctionner qu’avec des identifiants. Eh bien, au moins j’ai résolu mon problème initial.