
Libreria di autorizzazione dichiarativa con un DSL per scrivere regole di policy, condizioni e caching. Consente una gestione delle autorizzazioni scalabile e DRY per applicazioni Ruby.
DeclarativePolicy: Una libreria di autorizzazione dichiarativaQuesta libreria fornisce un DSL per scrivere politiche di autorizzazione.
Può essere usata per separare la logica dalle autorizzazioni, ed è stata usata su larga scala in produzione su GitLab.com.
L'autrice originale di questa libreria è Jeanine Adkisson, e il copyright è detenuto da GitLab.
Aggiungi questa riga al Gemfile della tua applicazione:
gem 'declarative_policy'
E poi esegui:
$ bundle install
Oppure installala manualmente con:
$ gem install declarative_policy
require 'declarative_policy'
class User
attr_reader :name
def initialize(name:)
@name = name
end
end
class Vehicle
def initialize(owner:, trusted: [])
@owner = owner
@trusted = trusted
end
def owner?(user)
@owner.name == user.name
end
def trusted?(user)
@owner.name == user.name || @trusted.detect { |t| t.name == user.name }
end
end
class VehiclePolicy < DeclarativePolicy::Base
condition(:owns) { @subject.owner?(@user) }
condition(:trusted) { @subject.trusted?(@user) }
rule { owns }.enable :sell_vehicle
rule { trusted }.enable :drive_vehicle
end
jack = User.new(name: 'jack')
jill = User.new(name: 'jill')
jacks_vehicle = Vehicle.new(owner: jack, trusted: [jill])
jills_vehicle = Vehicle.new(owner: jill, trusted: [jack])
puts "Jack can drive Jack's vehicle? -> #{DeclarativePolicy.policy_for(jack, jacks_vehicle).can?(:drive_vehicle)}"
puts "Jack can drive Jill's vehicle? -> #{DeclarativePolicy.policy_for(jack, jills_vehicle).can?(:drive_vehicle)}"
puts "Jack can sell Jack's vehicle? -> #{DeclarativePolicy.policy_for(jack, jacks_vehicle).can?(:sell_vehicle)}"
puts "Jack can sell Jill's vehicle? -> #{DeclarativePolicy.policy_for(jack, jills_vehicle).can?(:sell_vehicle)}"
$ ruby example.rb
Jack can drive Jack's vehicle? -> true
Jack can drive Jill's vehicle? -> true
Jack can sell Jack's vehicle? -> true
Jack can sell Jill's vehicle? -> false
L'astrazione principale di questa libreria è una Policy. Le Policy combinano:
conditions) sullo stato del mondorules)Questa libreria esiste per determinare il valore di verità di affermazioni della forma:
User Predicate [Subject]
La rinomina di User in Actor e Subject in Resource è discussa in questo issue.
Per esempio:
user :is_aliveuser :can_drive caruser :can_sell carFa questo permettendoci di associare una Policy (un insieme di regole su quali affermazioni sono vere) con gli oggetti delle frasi. Un'affermazione è considerata valida se nessuna regola la prevents, e almeno una regola la enables.
Per esempio, immaginiamo di avere un modello dati contenente veicoli e utenti, e vogliamo sapere se un utente può guidare un veicolo. Abbiamo bisogno di una VehiclePolicy:
class VehiclePolicy < DeclarativePolicy::Base
# relevant facts
condition(:owns) { @subject.owner == @user }
condition(:has_access_to) { @subject.owner.trusts?(@user) }
condition(:old_enough_to_drive) { @user.age >= laws.minimum_age }
condition(:has_driving_license) { @user.driving_license&.valid? }
# expensive rules can have 'score'. Higher scores are 'more expensive' to calculate
condition(:owns, score: 0) { @subject.owner == @user }
condition(:has_access_to, score: 3) { @subject.owner.trusts?(@user) }
condition(:intoxicated, score: 5) { @user.blood_alcohol > laws.max_blood_alcohol }
# conclusions we can draw:
rule { owns }.enable :drive_vehicle
rule { has_access_to }.enable :drive_vehicle
rule { ~old_enough_to_drive }.prevent :drive_vehicle
rule { intoxicated }.prevent :drive_vehicle
rule { ~has_driving_license }.prevent :drive_vehicle
# we can use methods to abstract common logic
def laws
@subject.registration.country.driving_laws
end
end
Alcuni punti da notare: avremmo potuto scrivere tutto come un'unica grande regola ((owns | has_access_to) & old_enough_to_drive & ~intoxicated & has_driving_license) ma possiamo vedere alcune delle caratteristiche che rendono le politiche dichiarative scalabili per grandi sistemi: le regole possono essere suddivise in piccoli elementi, e composte in regole più grandi. Nuove condizioni e regole possono essere aggiunte in qualsiasi momento.
Ciò che è più difficile da vedere è che molte ottimizzazioni delle prestazioni sono gestite per noi in modo trasparente:
In breve questa libreria mira ad essere dichiarativa: dichiariamo le regole che sono importanti, e la libreria organizza come valutarle.
La memorizzazione nella cache è una caratteristica particolarmente preziosa delle policy. Se aggiungiamo nuove regole sulla vendita di un veicolo, per esempio:
rule { owns }.enable :sell_vehicle
Quindi il fatto di proprietà può essere condiviso tra diverse chiamate alla policy, risparmiando chiamate al database e altre costose operazioni di I/O.
Possiamo verificare la determinazione di una policy con:
cache = Session.current_session
policy = DeclarativePolicy.policy_for(user, car, cache: cache)
policy.can?(:drive_vehicle)
Per maggiori dettagli sull'uso, consulta la documentazione.
Dopo aver clonato il repository, esegui bundle install per installare le dipendenze. Poi, esegui rake spec per eseguire i test. Puoi anche eseguire bin/console per un prompt interattivo che ti permetterà di sperimentare.
Per installare questa gemma sulla tua macchina locale, esegui bundle exec rake install. Per rilasciare una nuova versione, aggiorna il numero di versione in version.rb, e poi esegui bundle exec rake release, che creerà un tag git per la versione, effettuerà il push dei commit e dei tag git, e caricherà il file .gem su rubygems.org.
Maggiori dettagli su policy e ruoli personalizzati si trovano nelle seguenti pagine:
Segnalazioni di bug e richieste di merge sono benvenute su GitLab all'indirizzo https://gitlab.com/gitlab-org/ruby/gems/declarative-policy. Questo progetto è concepito per essere uno spazio sicuro e accogliente per la collaborazione, e ci si aspetta che i contributori aderiscano al codice di condotta di GitLab.
Rilasciamo declarative_policy su base ad-hoc. Non c'è regolarità nei rilasci,
rilasciamo quando facciamo una modifica - indipendentemente dalla dimensione della modifica.
Per rilasciare una nuova versione:
Gemfile di GitLab per aggiornare anche la gemma Ruby declarative_policy alla nuova versione.La gemma è disponibile come open source secondo i termini della Licenza MIT.
Chiunque interagisca con il codice del progetto DeclarativePolicy, i tracker delle issue,
le chat room e le mailing list è tenuto a seguire
il codice di condotta.