[RAILS] So autorisieren Sie mit graphql-ruby

Wenn Sie GraphQL verwenden, möchten Sie es möglicherweise in verschiedenen Prozessen autorisieren.

Anfangs hatte ich wenig Kenntnisse über graphql-ruby, daher habe ich den Prozess zum Autorisieren im Erfassungs- und Aktualisierungsprozess aufgerufen. Wenn ich das graphql-ruby-Dokument jedoch erneut lese, wird die Autorisierungsmethode (autorisiert?) Ich fand heraus, dass es etwas gab, und schrieb einen Artikel, um den Vorgang zu überprüfen.

Über Graphql-Ruby

Es ist ein Juwel, das die Verwendung von GraphQL in Ruby (Rails) vereinfacht. https://github.com/rmosolgo/graphql-ruby

Die Details sind oft erst bekannt, wenn Sie sie tatsächlich ausprobieren, aber die Dokumentation ist großartig. https://graphql-ruby.org/guides

Zum Zeitpunkt des Schreibens dieses Artikels verwende ich graphql: 1.11.1. Bitte beachten Sie, dass der Betrieb von Gem noch aktualisiert wird. Wenn sich die Version unterscheidet, hat sich der Betrieb möglicherweise erheblich geändert.

Beispiel für die Implementierung der Autorisierung

Hier sind einige Beispiele für die Implementierung der ersten vier Muster.

Voraussetzungen

Es wird davon ausgegangen, dass die für die Autorisierung erforderlichen Login-Benutzerinformationen im Kontext gespeichert sind. Die Erklärung der Authentifizierung wird weggelassen, da sie vom Hauptpunkt dieses Artikels abweicht.

app/controllers/graphql_controller.rb


#Login-Benutzerinformationen sind Kontext[:current_user]Speichern in
#Null, wenn nicht angemeldet
context = { current_user: current_user }

Ich möchte, dass diese Abfrage nur vom angemeldeten Benutzer ausgeführt wird

Hier wird "eine Abfrage implementiert, die review_id angibt und den entsprechenden ReviewType zurückgibt".

Bevor Sie die Genehmigung erhalten

Implementieren Sie eine Abfrage, die einen ReviewType erhält, bevor Sie die Autorisierung implementieren.

app/graphql/types/query_type.rb


module Types
  class QueryType < Types::BaseObject
    field :review, resolver: Resolvers::ReviewResolver
  end
end

app/graphql/resolvers/review_resolver.rb


module Resolvers
  class ReviewResolver < BaseResolver
    type Types::ReviewType, null: true

    argument :review_id, Int, required: true

    def resolve(review_id:)
      Review.find_by(id: review_id)
    end
  end
end

app/graphql/types/review_type.rb


module Types
  class ReviewType < BaseObject
    field :id, ID, null: false
    field :title, String, null: true
    field :body, String, null: true
    field :secret, String, null: true
    field :user, Types::UserType, null: false
  end
end

app/graphql/types/user_type.rb


module Types
  class UserType < BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    field :email, String, null: false
  end
end

In GraphiQL sieht es folgendermaßen aus: スクリーンショット 2020-07-24 13.51.33.png

Berechtigung implementieren

Fügen wir nun die Einschränkung hinzu, die "nur der angemeldete Benutzer ausführen kann", zu dem zuvor implementierten Prozess.

Implementierung ohne Genehmigung?

Früher hatte ich eine Implementierung, die die Anmeldung überprüft, bevor ich eine Überprüfung mit der Auflösungsmethode erhalte.

Implementieren Sie zunächst eine Anmeldeprüfmethode in BaseResolver, damit sie von verschiedenen Resolvern verwendet werden kann. Wenn der Kontext [: current_user] nicht enthalten ist, tritt ein Fehler auf. Übrigens, wenn Sie GraphQL :: ExecutionError verwenden, wird die Antwort nur durch Auslösen in das GraphQL-Fehlerformat konvertiert.

app/graphql/resolvers/base_resolver.rb


 module Resolvers
   class BaseResolver < GraphQL::Schema::Resolver
     def login_required!
       #Erhöhen Sie, wenn Sie nicht angemeldet sind
       raise GraphQL::ExecutionError, 'login required!!' unless context[:current_user]
     end
   end
 end

Rufen Sie dann zu Beginn des Vorgangs die BaseResolver-Anmeldeprüfung auf.

app/graphql/resolvers/review_resolver.rb


 def resolve(review_id:)
+  #Führen Sie zu Beginn des Vorgangs eine Anmeldeprüfung durch
+  login_required!

   Review.find_by(id: review_id)
 end

Wenn Sie es mit GraphiQL ausführen, ohne sich anzumelden, sieht es wie folgt aus. スクリーンショット 2020-07-25 12.53.22.png

Ich habe mit dieser Methode erreicht, was ich tun möchte, aber Resolver, die eine Anmeldung erfordern, müssen zu Beginn des Prozesses immer "login_required!" Schreiben. Ich habe nach einer Möglichkeit gesucht, diesen Prozess automatisch zu autorisieren, bevor er aufgerufen wird, wie z. B. before_action im Controller.

Implementierung mit autorisierten?

Als ich die Anleitung von graphql-ruby noch einmal las, bemerkte ich, dass es eine Methode namens autorisiert gibt? Es scheint, dass Sie dies verwenden können, um die Autorisierung vor der Auflösungsmethode durchzuführen und zu steuern, ob sie ausgeführt werden kann oder nicht. Unten finden Sie eine Anleitung zum Hinzufügen zur Mutation, aber Sie können sie auch zu Resolver hinzufügen. https://graphql-ruby.org/mutations/mutation_authorization.html

Da Resolver, für den eine Anmeldung erforderlich ist, für allgemeine Zwecke verwendet werden kann, habe ich login_required_resolver erstellt, den Resolver, für den eine Anmeldung erforderlich ist, erbt. Die autorisierten Parameter (args) enthalten dieselben Parameter wie die Auflösung.

app/graphql/resolvers/login_required_resolver.rb


module Resolvers
  class LoginRequiredResolver < BaseResolver
    def authorized?(args)
      context[:current_user].present?
    end
  end
end

Ändern Sie review_resolver, um login_required_resolver zu erben. Andere Implementierungen sind dieselben wie vor dem Hinzufügen der Autorisierung.

app/graphql/resolvers/review_resolver.rb


- class ReviewResolver < BaseResolver
+ class ReviewResolver < LoginRequiredResolver

Wenn Sie es mit GraphiQL ausführen, ohne sich anzumelden, sieht es wie folgt aus. スクリーンショット 2020-07-25 13.01.45.png

Wenn das Ergebnis von autorisiert? Falsch ist, gibt es keine Fehlerinformationen und nur "data: null" wird zurückgegeben. Wie im Handbuch beschrieben, scheint das Standardverhalten darin zu bestehen, nur "data: null" zurückzugeben, wenn dies autorisiert ist. Ist false. Wenn es kein Problem mit der Angabe der Rückgabe von null gibt, können Sie es so lassen, wie es ist. Wenn es jedoch nicht autorisiert ist, versuchen Sie es zu ändern, damit auch Fehlerinformationen zurückgegeben werden.

Das Hinzufügen von Fehlerinformationen ist einfach und kann durch Auslösen von GraphQL :: ExecutionError in autorisiertem? Übrigens, wenn Sie erfolgreich sind, müssen Sie vorsichtig sein, da dies nicht als Erfolg erkannt wird, es sei denn, Sie geben ausdrücklich true zurück.

app/graphql/resolvers/login_required_resolver.rb


module Resolvers
  class LoginRequiredResolver < BaseResolver
    def authorized?(args)
      #GraphQL, wenn nicht autorisiert::ExecutionError auslösen
      raise GraphQL::ExecutionError, 'login required!!' unless context[:current_user]

      true
    end
  end
end

Wenn Sie es mit GraphiQL ausführen, ohne sich anzumelden, sieht es wie folgt aus. Jetzt können Sie die Fehlerinformationen zurückgeben, auch wenn Sie autorisierte? Verwenden. スクリーンショット 2020-07-25 13.25.54.png

Wenn Sie autorisiert? Verwenden, können Sie es einfach schreiben, weil Sie den Autorisierungsprozess nicht in der Auflösungsmethode schreiben müssen. (Dieses Beispiel ist eine ziemlich einfache Implementierung, daher gibt es keinen großen Unterschied ...)

Ich möchte, dass diese Mutation nur vom Administrator ausgeführt wird

Hier wird "Mutation implementiert, die den Titel und den Text der entsprechenden Überprüfung durch Angabe von review_id aktualisiert".

Vor der Eingabe der Autorisierung

Implementieren Sie Mutation, um Review zu aktualisieren, bevor Sie die Autorisierung implementieren. Klassen, die so verwendet werden, wie sie sind, wie z. B. ReviewType, die im vorherigen Beispiel verwendet wurden, werden weggelassen.

app/graphql/types/mutation_type.rb


module Types
  class MutationType < Types::BaseObject
    field :update_review, mutation: Mutations::UpdateReview
  end
end

app/graphql/mutations/update_review.rb


module Mutations
  class UpdateReview < BaseMutation
    argument :review_id, Int, required: true
    argument :title, String, required: false
    argument :body, String, required: false

    type Types::ReviewType

    def resolve(review_id:, title: nil, body: nil)
      review = Review.find review_id
      review.title = title if title
      review.body = body if body
      review.save!

      review
    end
  end
end

Bei der Ausführung in GraphiQL werden die Überprüfungsdaten wie folgt aktualisiert. スクリーンショット 2020-07-27 11.34.17.png

Berechtigung implementieren

Sie können autorisierte? In Mutation wie im vorherigen Beispiel verwenden. Es ist in der folgenden Anleitung aufgeführt. https://graphql-ruby.org/mutations/mutation_authorization.html

Erstellen Sie eine übergeordnete Klasse, die Mutation erbt, die nur Administratoren zur Verfügung steht, und erben Sie sie.

app/graphql/mutations/base_admin_mutation.rb


module Mutations
  class BaseAdminMutation < BaseMutation
    def authorized?(args)
      raise GraphQL::ExecutionError, 'login required!!' unless context[:current_user]
      raise GraphQL::ExecutionError, 'permission denied!!' unless context[:current_user].admin?

      super
    end
  end
end

app/graphql/mutations/update_review.rb


- class UpdateReview < BaseMutation
+ class UpdateReview < BaseAdminMutation

Wenn Mutation autorisiert ist? Gibt auch nur false zurück, Fehlerinformationen werden nicht zurückgegeben, Daten sind null und die Aktualisierungsverarbeitung wird nicht ausgeführt. Resolver sieht immer noch gut aus, aber Mutation versteht es nur, wenn es Fehlerinformationen zurückgibt. Deshalb habe ich es implementiert, um auch GraphQL :: ExecutionError auszulösen. Übrigens, wenn Sie die Anleitung lesen, scheint es eine Möglichkeit zu geben, Fehlerinformationen zurückzugeben, indem Sie Fehler als Rückgabewert zurückgeben, wie unten gezeigt. Ich habe es versucht, aber die folgende Methode hat die fehlerhaften Speicherorte und Pfade nicht zurückgegeben, aber ich konnte die Fehlermeldungen zurückgeben. Wenn Sie die Nachricht nur zurückgeben müssen, können Sie sie mit beiden Methoden implementieren.

def authorized?(employee:)
  if context[:current_user]&.admin?
    true
  else
    return false, { errors: ["permission denied!!"] }
  end
end

Wenn es von einem Benutzer ausgeführt wird, der keine Administratorrechte in GraphiQL hat, sieht es wie folgt aus. Im Fehlerfall wird der Update-Vorgang natürlich nicht ausgeführt. スクリーンショット 2020-07-27 11.48.29.png

Ich möchte, dass diese Abfrage nur zurückgegeben wird, wenn die Daten, die ich besitze

Hier ändern wir es basierend auf der ersten "Abfrage, die review_id angibt und den entsprechenden ReviewType zurückgibt". Das erste, was ich gemacht habe, war, nur den Anmeldestatus zu überprüfen, aber diesmal ist Meine Eigenschaft überprüfen? Fügen Sie einen Scheck für hinzu.

Versuchen Sie es in der gleichen autorisierten zu implementieren?

Es wäre schön, wenn ich dem gleichen autorisierten einen Scheck hinzufügen könnte? Wie der Login-Check, aber dieser Check kann erst durchgeführt werden, nachdem ich Revew erhalten habe. Selbst mit autorisiertem? Wird review_id als Argument empfangen, sodass es möglich ist, eine Überprüfung zu erhalten, dies verdeckt jedoch die Rolle der Auflösung. Ich werde es tatsächlich implementieren.

app/graphql/resolvers/login_required_resolver.rb


 def authorized?(args)
   raise GraphQL::ExecutionError, 'login required!!' if context[:current_user].blank?

+   #Sie müssen an dieser Stelle eine Bewertung erhalten
+   review = Review.find_by(id: args[:review_id])
+   return false unless review
+   raise GraphQL::ExecutionError, 'permission denied!!' if context[:current_user].id != review.user_id

   true
 end

Sie benötigen eine Überprüfung mit autorisierten ?. Es wird auch durch die Auflösungsmethode erfasst, so dass es ineffizient erscheint, es auch hier zu erfassen. Wie wäre es also mit einer Überprüfung auf der Auflösungsseite?

app/graphql/resolvers/review_resolver.rb


 def resolve(review_id:)
-   Review.find_by(id: review_id)
+   review = Review.find_by(id: review_id)
+   raise GraphQL::ExecutionError, 'permission denied!!' if context[:current_user].id != review.user_id

+   review
 end

Dies scheint effizienter zu sein als die Implementierung mit autorisiertem ", aber durch Ausschneiden des Prüfprozesses in" autorisiert "wurde der Prüfprozess zur Lösung hinzugefügt, der nur den Datenerfassungsprozess beschreibt.

Zuerst dachte ich, dass das einzige, was erst nach der Datenerfassung überprüft werden kann, die Überprüfung mit Entschlossenheit ist, aber ich habe erfahren, dass autorisiert? Kann auch in ReviewType definiert werden, also werde ich es in ReviewType definieren.

Überprüfen Sie mit Überprüfungstyp

Was bedeutet es, mit ReviewType zu überprüfen? Ich werde es tatsächlich implementieren.

Ich möchte ReviewType für jedermann verfügbar machen, daher erstelle ich einen ReviewType mit dem Namen MyReviewType mit Einschränkungen, die nur ich anzeigen kann.

app/graphql/types/my_review_type.rb


module Types
  class MyReviewType < ReviewType
    def self.authorized?(object, context)
      raise GraphQL::ExecutionError, 'permission denied!!' if context[:current_user].id != object.user_id

      true
    end
  end
end

Wie im Handbuch erwähnt, verwendet autorisiert? Verwendet in Typ Objekt und Kontext als Argumente. Da es sich um eine Klassenmethode handelt, müssen Sie vorsichtig sein. https://graphql-ruby.org/authorization/authorization.html

Sie müssen lediglich den Antworttyp auf MyReviewType setzen. Es sind keine weiteren Änderungen erforderlich.

app/graphql/resolvers/review_resolver.rb


- type Types::ReviewType, null: true
+ type Types::MyReviewType, null: true

Wenn Sie in GraphiQL eine andere als Ihre eigene Überprüfung angeben, lautet diese wie folgt. スクリーンショット 2020-07-27 22.40.15.png

Jetzt, da Sie den Autorisierungsprozess nicht mehr in der Auflösungsmethode schreiben müssen, können Sie ihn einfach schreiben. Wenn Sie die Antwort auf MyReviewType festlegen, wird durch einfaches Lesen der Schemadefinition deutlich, dass diese Abfrage MyReviewType = "Nur Sie können sie anzeigen" zurückgibt.

Dieses Feld wird nur zurückgegeben, wenn die Daten des angemeldeten Benutzers vorhanden sind

Im vorherigen Beispiel habe ich MyReviewType so definiert, dass ich nur die gesamte Antwort für meine Daten sehen kann. Es kann jedoch vorkommen, dass Sie nur bestimmte Felder ausblenden möchten, nicht alle.

Ich werde den Überprüfungstyp erneut veröffentlichen. Hier soll die geheime Spalte nur meine Daten sehen.

app/graphql/types/review_type.rb


module Types
  class ReviewType < BaseObject
    field :id, ID, null: false
    field :title, String, null: true
    field :body, String, null: true
    field :secret, String, null: true # <-Machen Sie dies nur für Sie sichtbar
    field :user, Types::UserType, null: false
  end
end

Es scheint autorisiert zu sein. Kann auch vor Ort implementiert werden, aber es scheint schwierig zu sein, nur ein Feld anzupassen. Deshalb habe ich beschlossen, es ohne Verwendung von autorisiert zu implementieren. Hier. https://graphql-ruby.org/authorization/authorization.html Klicken Sie hier für eine Feldanleitung https://graphql-ruby.org/fields/introduction.html#field-parameter-default-values

Wenn Sie dieselbe Methode wie den unten gezeigten Feldnamen definieren, wird diese Methode aufgerufen. Ich habe die Autorisierung innerhalb dieser Methode implementiert.

app/graphql/types/review_type.rb


module Types
  class ReviewType < BaseObject
    field :id, ID, null: false
    field :title, String, null: true
    field :body, String, null: true
    field :secret, String, null: true
    field :user, Types::UserType, null: false

    #Wird beim Definieren einer Methode mit Feldname aufgerufen
    def secret
      #Wenn der angemeldete Benutzer und der Benutzer, der die Bewertung verfasst hat, unterschiedlich sind, geben Sie nil zurück
      return if object.user_id != context[:current_user].id

      object.secret
    end
  end
end

Wenn Sie in GraphiQL eine andere als Ihre eigene Überprüfung angeben, lautet diese wie folgt. Das Geheimnis wurde null zurückgegeben. スクリーンショット 2020-07-28 22.51.28.png

Wenn Sie diese Prüfung in Resolver implementieren, müssen alle Resolver, die ReviewType verwenden, geheim sein. Wenn Sie sie jedoch in ReviewType implementieren, müssen einzelne Resolver nicht an die Zugriffskontrolle von secret denken.

Schließlich

Ich wollte die Anleitung durchlesen, bevor ich anfing, graphql-ruby zu verwenden, aber ich habe die Existenz von autorisierten? ... Es scheint, dass es andere nützliche Funktionen als autorisierte gibt, die Sie noch nicht bemerkt haben. Auch wenn es jetzt nicht existiert, wurde die Version aktualisiert und es besteht eine hohe Wahrscheinlichkeit, dass in Zukunft neue Funktionen hinzugefügt werden. Daher möchte ich weiterhin die Trends von graphql-ruby überprüfen.

Recommended Posts

So autorisieren Sie mit graphql-ruby
So erstellen Sie CloudStack mit Docker
So führen Sie einen Vertrag mit web3j aus
So sortieren Sie eine Liste mit Comparator
[Rails] So laden Sie Bilder mit Carrierwave hoch
[Java] So berechnen Sie das Alter mit LocalDate
[Swift5] So implementieren Sie Animationen mit "lottie-ios"
So implementieren Sie die Image-Veröffentlichung mithilfe von Schienen
So fügen Sie Symbole mit Font awesome ein
So geben Sie Excel und PDF mit Excella aus
[Rails] So erstellen Sie ein Diagramm mit lazy_high_charts
So löschen Sie einen Controller usw. mit einem Befehl
Wie man Stimme oder Musik mit Javascript spielt
[Ethereum] So führen Sie einen Vertrag mit web3j-Teil 2 aus
So implementieren Sie die Brotkrumenfunktion mit gretel
[Rails] So laden Sie mehrere Bilder mit Carrierwave hoch
So generieren Sie einen Primärschlüssel mit @GeneratedValue
So verknüpfen Sie Bilder mit FactoryBot Active Storage
So entwickeln Sie OpenSPIFe
So rufen Sie AmazonSQSAsync auf
Verwendung von Map
Wie benutzt man rbenv?
Verwendung mit_option
Verwendung von fields_for
Verwendung von java.util.logging
Verwendung von collection_select
Wie benutzt man Twitter4J
Wie benutzt man active_hash! !!
So installieren Sie Docker
Verwendung von MapStruct
Verwendung von TreeSet
So deinstallieren Sie Rails
So installieren Sie Docker-Maschine
[Verwendung des Etiketts]
Wie man ein schattiertes Glas macht
Wie schreibe ich Docker-Compose
Wie man Identität benutzt
Wie man Hash benutzt
Wie schreibe ich Mockito
So erstellen Sie Docker-Compose
So installieren Sie MySQL
So schreiben Sie eine Migrationsdatei
Wie man android-midi-lib baut
Verwendung von Dozer.mapper
Wie benutzt man Gradle?
Verwendung von org.immutables
Verwendung von VisualVM
Verwendung von Map
Wie man einen Schrägstrich zurückschlägt \
So verketten Sie Zeichenfolgen
So ermitteln Sie, wie viel Festplatte Docker verwendet
So führen Sie mit RxAndroid einen Komponententest mit JVM an einer Quelle durch
Wie man einen Oleore-Generator mit Swagger Codegen herstellt