# frozen_string_literal: true module Decidim module DecidimAwesome # A middleware that stores the current awesome context by parsing the request class CurrentConfig # Initializes the Rack Middleware. # # app - The Rack application def initialize(app) @app = app end # Main entry point for a Rack Middleware. # # env - A Hash. def call(env) @request = Rack::Request.new(env) if @request.env["decidim.current_organization"] && processable_path? @config = awesome_config_instance env["decidim_awesome.current_config"] = @config tamper_user_model add_flash_message_from_request(env) # puts "requested path: #{env["PATH_INFO"]}" # puts "current_organization: #{@request.env["decidim.current_organization"]&.id}" # puts "potential_admins: #{Decidim::User.awesome_potential_admins}" # puts "scoped admins: #{Decidim::User.awesome_admins_for_current_scope}" else reset_user_model end @app.call(env) end private # a workaround to set a flash message if comming from the error controller (route not found) def add_flash_message_from_request(env) return unless scoped_admins_active? return unless @request.params.has_key? "unauthorized" env["rack.session"]["flash"] = ActionDispatch::Flash::FlashHash.new(alert: I18n.t("decidim.core.actions.unauthorized")).to_session_value end def awesome_config_instance @awesome_config_instance = Config.new @request.env["decidim.current_organization"] @awesome_config_instance.context_from_request @request @awesome_config_instance end def reset_user_model return unless scoped_admins_active? Decidim::User.awesome_potential_admins = [] Decidim::User.awesome_admins_for_current_scope = [] end def tamper_user_model return unless scoped_admins_active? Decidim::User.awesome_potential_admins = potential_admins Decidim::User.awesome_admins_for_current_scope = if safe_get_route? || safe_post_route? Decidim::User.awesome_potential_admins else valid_admins end end def potential_admins @config.collect_sub_configs_values("scoped_admin") do |subconfig| subconfig&.constraints&.detect { |c| c.settings["participatory_space_manifest"] == "none" } ? false : true end.flatten.uniq.map(&:to_i) end def valid_admins @config.collect_sub_configs_values("scoped_admin") do |subconfig| # allow index controllers if scoped to a subspace/component constraints = subconfig&.constraints || [] additional_constraints = additional_get_constraints(constraints) + additional_post_constraints(constraints) # inject additional constraints here for further use @config.inject_sub_config_constraints("scoped_admin", subconfig.var[13..], additional_constraints) if subconfig @config.valid_in_context?(constraints + additional_constraints) end.flatten.uniq.map(&:to_i) end # rubocop:disable Lint/DuplicateBranch # avoid unnecessary processing for non-user routes def processable_path? return true if safe_get_route? spaces = ContextAnalyzers::RequestAnalyzer.participatory_spaces_routes.keys.join("|^(/admin){0,1}/") case @request.path when %r{"|^(/admin){0,1}/#{spaces}} true when %r{^/admin/} true end end def safe_get_route? return false unless @request.get? case @request.path when "/" true when "/admin/" true when %r{^/admin/admin_terms} true when %r{^/profiles/|^/notifications/|^/conversations/|^/pages/} true end end def safe_post_route? return false unless @request.post? || @request.put? || @request.patch? case @request.path when %r{^/admin/admin_terms} true end end # rubocop:enable Lint/DuplicateBranch # to access certain deeper routes it requires first to click on a parent route, even without Post permissions in there # this adds this additional routes to these cases # For instance: # accessing /admin/participatory_processes/som-procress requires access first to /admin/participatory_processes def additional_get_constraints(constraints) return [] unless @request.get? additions = [] constraints.each do |constraint| next if constraint.settings["participatory_space_manifest"].blank? # processes groups must give access to processes generic url if constraint.settings["participatory_space_manifest"] == "process_groups" additions << OpenStruct.new(settings: { "participatory_space_manifest" => "participatory_processes", "match" => "exclusive" }) end # adds a exclusive constraint to the parent participatory space (so index page can be accessed) next unless constraint.settings.size > 1 additions << OpenStruct.new(settings: { "participatory_space_manifest" => constraint.settings["participatory_space_manifest"], "match" => "exclusive" }) end additions end # adds access to REST routes with id instead of the slug ot allow editing # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity def additional_post_constraints(constraints) return [] unless @request.post? || @request.patch? constraints.filter_map do |constraint| settings = constraint.settings.dup next unless settings["participatory_space_manifest"].present? && settings["participatory_space_slug"].present? # replicate the constraint with the id of the participatory space manifest = Decidim.participatory_space_manifests.find { |s| s.name.to_s == settings["participatory_space_manifest"] } next unless manifest model = manifest.model_class_name.try(:constantize) next unless model settings["participatory_space_slug"] = model.find_by(slug: settings["participatory_space_slug"])&.id OpenStruct.new(settings:) if settings["participatory_space_slug"] end end # rubocop:enable Metrics/CyclomaticComplexity # rubocop:enable Metrics/PerceivedComplexity def scoped_admins_active? Decidim::User.respond_to?(:awesome_admins_for_current_scope) && Decidim::User.respond_to?(:awesome_potential_admins) end end end end