aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/policies/application_policy.rb5
-rw-r--r--app/policies/boiv_policy.rb2
-rw-r--r--app/policies/chain.rb57
-rw-r--r--app/policies/line_policy.rb7
-rw-r--r--app/policies/route_policy.rb3
-rw-r--r--app/policies/routing_constraint_zone_policy.rb3
-rw-r--r--app/policies/time_table_policy.rb12
-rw-r--r--app/views/referential_lines/_filters.html.slim2
-rw-r--r--spec/features/line_footnotes_spec.rb3
-rw-r--r--spec/policies/boiv_policy_spec.rb7
-rw-r--r--spec/policies/line_policy_spec.rb18
-rw-r--r--spec/policies/route_policy_spec.rb22
-rw-r--r--spec/policies/routing_constraint_zone_policy_spec.rb22
-rw-r--r--spec/policies/time_table_policy_spec.rb13
-rw-r--r--spec/support/pundit/shared_examples.rb45
15 files changed, 199 insertions, 22 deletions
diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb
index de8a23344..a863404ae 100644
--- a/app/policies/application_policy.rb
+++ b/app/policies/application_policy.rb
@@ -7,7 +7,10 @@ class ApplicationPolicy
@record = record
end
- attr_accessor :referential
+ def archived?
+ !!referential.try(:archived_at)
+ end
+
def referential
@referential ||= record_referential
end
diff --git a/app/policies/boiv_policy.rb b/app/policies/boiv_policy.rb
index e29a2e6de..4270dc686 100644
--- a/app/policies/boiv_policy.rb
+++ b/app/policies/boiv_policy.rb
@@ -1,3 +1,4 @@
+require_relative 'chain'
class BoivPolicy < ApplicationPolicy
def boiv_read_offer?
@@ -11,5 +12,4 @@ class BoivPolicy < ApplicationPolicy
def show?
boiv_read_offer?
end
-
end
diff --git a/app/policies/chain.rb b/app/policies/chain.rb
new file mode 100644
index 000000000..4bf96bd84
--- /dev/null
+++ b/app/policies/chain.rb
@@ -0,0 +1,57 @@
+module Policies
+ # Implements the `chain_policies` macro as follows
+ #
+ # chain_policies <method_chain>, policies:
+ #
+ # e.g.
+ #
+ # chain_policies [:archived?, :!], policies: %i{create? edit?}
+ #
+ # which would establish a precondition `not archived` for the `create?` and `edit?`
+ # method, it is semantically identical to instrumenting both methods
+ # as follows:
+ #
+ # def create? # or edit?
+ # archived?.! && <original code of method>
+ # end
+ module Chain
+
+ # A local chain store implemented to avoid any possible side effect on client policies.
+ defined_chains = {}
+
+ # Using `define_method` in order to close over `defined_chains`
+ # We need to store the chains because the methods they will apply to
+ # are not defined yet.
+ define_method :chain_policies do |*conditions, policies:|
+ policies.each do | meth_name |
+ # self represents the client Policy
+ defined_chains[[self, meth_name]] = conditions
+ end
+ end
+ # Intercept method definition and check if a policy_chain has been registered for it‥.
+ define_method :method_added do |meth_name, *args, &blk|
+ # Delete potentially registered criteria conditions to‥.
+ # (i) protect against endless recursion via (:merthod_added → :define_method → :method_added → ‥.
+ # (ii) get the condition
+ conditions = defined_chains.delete([self, meth_name])
+ return unless conditions
+
+ instrument_method(meth_name, conditions)
+ end
+
+ private
+
+ # Access to the closure is not necessary anymore, normal metaprogramming can take place :)
+ def instrument_method(meth_name, conditions)
+ orig_method = instance_method(meth_name)
+ # In case of warnings remove original method here, depends on Ruby Version, ok in 2.3.1
+ define_method meth_name do |*a, &b|
+ # Method chain describing the chained policy precondition.
+ conditions.inject(self) do | result, msg |
+ result.send msg
+ end &&
+ orig_method.bind(self).(*a, &b)
+ end
+ end
+ end
+end
diff --git a/app/policies/line_policy.rb b/app/policies/line_policy.rb
index 443e3e22f..c3e0051c8 100644
--- a/app/policies/line_policy.rb
+++ b/app/policies/line_policy.rb
@@ -1,4 +1,9 @@
+require_relative 'chain'
class LinePolicy < BoivPolicy
+ extend Policies::Chain
+
+ chain_policies :archived?, :!, policies: %i{create_footnote? destroy_footnote? edit_footnote?}
+
class Scope < Scope
def resolve
scope
@@ -22,7 +27,7 @@ class LinePolicy < BoivPolicy
end
def destroy_footnote?
- user.has_permission?('routes.destroy')
+ user.has_permission?('footnotes.destroy')
end
def update_footnote? ; edit_footnote? end
diff --git a/app/policies/route_policy.rb b/app/policies/route_policy.rb
index ff13d3163..dba3a27da 100644
--- a/app/policies/route_policy.rb
+++ b/app/policies/route_policy.rb
@@ -1,10 +1,13 @@
class RoutePolicy < BoivPolicy
+ extend Policies::Chain
class Scope < Scope
def resolve
scope
end
end
+ chain_policies :archived?, :!, policies: %i{create? destroy? edit?}
+
def create?
user.has_permission?('routes.create') # organisation match via referential is checked in the view
end
diff --git a/app/policies/routing_constraint_zone_policy.rb b/app/policies/routing_constraint_zone_policy.rb
index 58cbaa9e1..abba5639c 100644
--- a/app/policies/routing_constraint_zone_policy.rb
+++ b/app/policies/routing_constraint_zone_policy.rb
@@ -1,10 +1,13 @@
class RoutingConstraintZonePolicy < BoivPolicy
+ extend Policies::Chain
class Scope < Scope
def resolve
scope
end
end
+ chain_policies :archived?, :!, policies: %i{create? destroy? edit?}
+
def create?
user.has_permission?('routing_constraint_zones.create') # organisation match via referential is checked in the view
end
diff --git a/app/policies/time_table_policy.rb b/app/policies/time_table_policy.rb
index 1bb2add15..efab6ac00 100644
--- a/app/policies/time_table_policy.rb
+++ b/app/policies/time_table_policy.rb
@@ -1,24 +1,28 @@
class TimeTablePolicy < BoivPolicy
+ extend Policies::Chain
+
class Scope < Scope
def resolve
scope
end
end
+ chain_policies :archived?, :!, policies: %i{create? destroy? duplicate? edit?}
+
def create?
- user.has_permission?('time_tables.create') # organisation match via referential is checked in the view
+ user.has_permission?('time_tables.create') # organisation match via referential is checked in the view
end
def edit?
- organisation_match? && user.has_permission?('time_tables.edit')
+ organisation_match? && user.has_permission?('time_tables.edit')
end
def destroy?
- organisation_match? && user.has_permission?('time_tables.destroy')
+ organisation_match? && user.has_permission?('time_tables.destroy')
end
def duplicate?
- organisation_match? && create?
+ organisation_match? && create?
end
def update? ; edit? end
diff --git a/app/views/referential_lines/_filters.html.slim b/app/views/referential_lines/_filters.html.slim
index aa355884b..93d449507 100644
--- a/app/views/referential_lines/_filters.html.slim
+++ b/app/views/referential_lines/_filters.html.slim
@@ -9,7 +9,7 @@
.ffg-row
.form-group.togglable
= f.label Chouette::Route.human_attribute_name(:wayback), required: false, class: 'control-label'
- = f.input :wayback_eq_any, as: :checkboxes, class: 'form-control', collection: Chouette::Route.wayback.values, as: :check_boxes, label: false, required: false, wrapper_html: { class: 'checkbox_list'}, label_method: lambda{|l| ("<span>" + t("enumerize.route.wayback.#{l}") + "</span>").html_safe}
+ = f.input :wayback_eq_any, class: 'form-control', collection: Chouette::Route.wayback.values, as: :check_boxes, label: false, required: false, wrapper_html: { class: 'checkbox_list'}, label_method: lambda{|l| ("<span>" + t("enumerize.route.wayback.#{l}") + "</span>").html_safe}
.actions
= link_to 'Effacer', referential_line_path(@referential, @line), class: 'btn btn-link'
diff --git a/spec/features/line_footnotes_spec.rb b/spec/features/line_footnotes_spec.rb
index 4d77cba41..6a359ad50 100644
--- a/spec/features/line_footnotes_spec.rb
+++ b/spec/features/line_footnotes_spec.rb
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-require 'spec_helper'
-
describe 'Line Footnotes', type: :feature do
login_user
diff --git a/spec/policies/boiv_policy_spec.rb b/spec/policies/boiv_policy_spec.rb
index 3af82ddfe..bf09cdcd9 100644
--- a/spec/policies/boiv_policy_spec.rb
+++ b/spec/policies/boiv_policy_spec.rb
@@ -1,16 +1,15 @@
RSpec.describe BoivPolicy, type: :policy do
-
permissions :index? do
- it_behaves_like 'permitted and same organisation', 'boiv:read-offer'
+ it_behaves_like 'permitted policy and same organisation', 'boiv:read-offer'
end
permissions :boiv_read_offer? do
- it_behaves_like 'permitted and same organisation', 'boiv:read-offer'
+ it_behaves_like 'permitted policy and same organisation', 'boiv:read-offer'
end
permissions :show? do
- it_behaves_like 'permitted and same organisation', 'boiv:read-offer'
+ it_behaves_like 'permitted policy and same organisation', 'boiv:read-offer'
end
end
diff --git a/spec/policies/line_policy_spec.rb b/spec/policies/line_policy_spec.rb
new file mode 100644
index 000000000..ead5918aa
--- /dev/null
+++ b/spec/policies/line_policy_spec.rb
@@ -0,0 +1,18 @@
+RSpec.describe LinePolicy, type: :policy do
+
+ %w{create destroy edit}.each do | permission |
+ footnote_permission = "#{permission}_footnote"
+ permissions "#{footnote_permission}?".to_sym do
+ it_behaves_like 'permitted policy', "footnotes.#{permission}", archived: true
+ end
+ end
+
+ permissions :new_footnote? do
+ it_behaves_like 'permitted policy', 'footnotes.create', archived: true
+ end
+
+ permissions :update_footnote? do
+ it_behaves_like 'permitted policy', 'footnotes.edit', archived: true
+ end
+
+end
diff --git a/spec/policies/route_policy_spec.rb b/spec/policies/route_policy_spec.rb
new file mode 100644
index 000000000..baf14c9fc
--- /dev/null
+++ b/spec/policies/route_policy_spec.rb
@@ -0,0 +1,22 @@
+RSpec.describe RoutePolicy, type: :policy do
+
+ permissions :create? do
+ it_behaves_like 'permitted policy', 'routes.create', archived: true
+ end
+
+ permissions :destroy? do
+ it_behaves_like 'permitted policy and same organisation', 'routes.destroy', archived: true
+ end
+
+ permissions :edit? do
+ it_behaves_like 'permitted policy and same organisation', 'routes.edit', archived: true
+ end
+
+ permissions :new? do
+ it_behaves_like 'permitted policy', 'routes.create', archived: true
+ end
+
+ permissions :update? do
+ it_behaves_like 'permitted policy and same organisation', 'routes.edit', archived: true
+ end
+end
diff --git a/spec/policies/routing_constraint_zone_policy_spec.rb b/spec/policies/routing_constraint_zone_policy_spec.rb
new file mode 100644
index 000000000..4b0f2cafe
--- /dev/null
+++ b/spec/policies/routing_constraint_zone_policy_spec.rb
@@ -0,0 +1,22 @@
+RSpec.describe RoutingConstraintZonePolicy, type: :policy do
+
+ permissions :create? do
+ it_behaves_like 'permitted policy', 'routing_constraint_zones.create', archived: true
+ end
+
+ permissions :destroy? do
+ it_behaves_like 'permitted policy and same organisation', 'routing_constraint_zones.destroy', archived: true
+ end
+
+ permissions :edit? do
+ it_behaves_like 'permitted policy and same organisation', 'routing_constraint_zones.edit', archived: true
+ end
+
+ permissions :new? do
+ it_behaves_like 'permitted policy', 'routing_constraint_zones.create', archived: true
+ end
+
+ permissions :update? do
+ it_behaves_like 'permitted policy and same organisation', 'routing_constraint_zones.edit', archived: true
+ end
+end
diff --git a/spec/policies/time_table_policy_spec.rb b/spec/policies/time_table_policy_spec.rb
index 48beea75d..1283a9fcf 100644
--- a/spec/policies/time_table_policy_spec.rb
+++ b/spec/policies/time_table_policy_spec.rb
@@ -1,7 +1,18 @@
RSpec.describe TimeTablePolicy, type: :policy do
permissions :duplicate? do
- it_behaves_like 'permitted and same organisation', 'time_tables.create'
+ it_behaves_like 'permitted policy and same organisation', 'time_tables.create', archived: true
end
+ %w{destroy edit}.each do | permission |
+ permissions "#{permission}?".to_sym do
+ it_behaves_like 'permitted policy and same organisation', "time_tables.#{permission}", archived: true
+ end
+ end
+
+ permissions :create? do
+ it_behaves_like 'permitted policy', 'time_tables.create', archived: true
+ end
+
+
end
diff --git a/spec/support/pundit/shared_examples.rb b/spec/support/pundit/shared_examples.rb
index 9583ab30c..4d14c46da 100644
--- a/spec/support/pundit/shared_examples.rb
+++ b/spec/support/pundit/shared_examples.rb
@@ -1,27 +1,60 @@
-RSpec.shared_examples "permitted and same organisation" do |permission|
+RSpec.shared_examples 'permitted policy and same organisation' do
+ | permission, archived: false|
- context "permission absent → " do
+ context 'permission absent → ' do
it "denies a user with a different organisation" do
expect_it.not_to permit(user_context, referential)
end
- it "and also a user with the same organisation" do
+ it 'and also a user with the same organisation' do
user.update_attribute :organisation, referential.organisation
expect_it.not_to permit(user_context, referential)
end
end
- context "permission present → " do
+ context 'permission present → ' do
before do
add_permissions(permission, for_user: user)
end
- it "denies a user with a different organisation" do
+ it 'denies a user with a different organisation' do
expect_it.not_to permit(user_context, referential)
end
- it "but allows it for a user with the same organisation" do
+ it 'but allows it for a user with the same organisation' do
user.update_attribute :organisation, referential.organisation
expect_it.to permit(user_context, referential)
end
+
+ if archived
+ it 'removes the permission for archived referentials' do
+ user.update_attribute :organisation, referential.organisation
+ referential.update_attribute :archived_at, 42.seconds.ago
+ expect_it.not_to permit(user_context, referential)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'permitted policy' do
+ | permission, archived: false|
+ context 'permission absent → ' do
+ it "denies a user with a different organisation" do
+ expect_it.not_to permit(user_context, referential)
+ end
+ end
+ context 'permission present → ' do
+ before do
+ add_permissions(permission, for_user: user)
+ end
+ it 'allows a user with a different organisation' do
+ expect_it.to permit(user_context, referential)
+ end
+
+ if archived
+ it 'removes the permission for archived referentials' do
+ referential.update_attribute :archived_at, 42.seconds.ago
+ expect_it.not_to permit(user_context, referential)
+ end
+ end
end
end