Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 99 additions & 95 deletions Gemfile.lock

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,21 @@ Decidim::DecidimAwesome.configure do |config|
config.force_authorization_allowed_controller_names = %w(account pages homepage)
```

#### 21. Manual verifications

The admin will be allowed to manually authorize users using the methods specified in the `/system` admin section.
Currently, only form based handlers are supported (Direct methods).
Admins can manually override or verify users in the participants list but they still have to fulfill the requirements of the verifier (although they will be allowed to force the authorization even if some of them fails).

Admin logs are also created in each action for accountability.

System configuration:

![System manual authorization config](examples/manual_verifications_system.png)
![List of authorizations](examples/manual_verifications_1.png)
![Removing an authorization](examples/manual_verifications_2.png)
![Creating an authorization](examples/manual_verifications_3.png)

#### To be continued...

We're not done! Please check the [issues](/decidim-ice/decidim-module-decidim_awesome/issues) (and participate) to see what's on our mind
Expand Down
4 changes: 0 additions & 4 deletions app/cells/decidim/decidim_awesome/content_blocks/map_cell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,7 @@ def global_map_components
true
when :proposals
component.settings.geocoding_enabled
else
false
end
else
false
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module Decidim
module DecidimAwesome
module System
module RegisterOrganizationOverride
extend ActiveSupport::Concern

included do
private

alias_method :decidim_create_organization, :create_organization

def create_organization
@organization = decidim_create_organization
if form.clean_awesome_admins_available_authorizations.present?
AwesomeConfig.create!(
var: :admins_available_authorizations,
organization: @organization,
value: form.clean_awesome_admins_available_authorizations
)
end
@organization
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Decidim
module DecidimAwesome
module System
module UpdateOrganizationOverride
extend ActiveSupport::Concern

included do
private

alias_method :decidim_original_save_organization, :save_organization

def save_organization
decidim_original_save_organization
if form.clean_awesome_admins_available_authorizations.present?
add_awesome_configs!
elsif awesome_config&.persisted?
awesome_config.destroy!
end
end

def add_awesome_configs!
awesome_config.value = form.clean_awesome_admins_available_authorizations
awesome_config.save!
end

def awesome_config
@awesome_config ||= AwesomeConfig.find_or_initialize_by(var: :admins_available_authorizations, organization: @organization)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def current_authorizations
end

def allowed_controllers
%w(required_authorizations authorizations upload_validations timeouts editor_images) + awesome_config[:force_authorization_allowed_controller_names].to_a
%w(required_authorizations authorizations upload_validations timeouts editor_images locales) + awesome_config[:force_authorization_allowed_controller_names].to_a
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# frozen_string_literal: true

module Decidim
module DecidimAwesome
module Admin
class AdminAuthorizationsController < DecidimAwesome::Admin::ApplicationController
include NeedsAwesomeConfig

layout false
helper_method :user, :authorization, :workflow, :handler, :conflict
# overwrite original rescue_from to ensure we print messages from ajax methods
rescue_from Decidim::ActionForbidden, with: :json_error

before_action do
enforce_permission_to :edit_config, :admins_available_authorizations, handler: workflow.name
end

def edit
render "authorization" if authorization
end

def update
if conflict
message = render_to_string("conflict")
else
message = render_to_string(partial: "callout", locals: { i18n_key: "user_authorized", klass: "success" })
Decidim::Verifications::AuthorizeUser.call(handler, current_organization) do
on(:transferred) do |transfer|
message += render_to_string(partial: "callout", locals: { i18n_key: "authorization_transferred", klass: "success" }) if transfer.records.any?
end
on(:invalid) do
if force_verification.present?
create_forced_authorization
else
message = render_to_string(partial: "callout", locals: { i18n_key: "user_not_authorized", klass: "alert" })
message += render_to_string("edit", locals: { with_override: true })
end
end
on(:ok) do
Decidim::ActionLogger.log("admin_creates_authorization", current_user, user, nil, user_id: user.id, handler: workflow.name, handler_name: workflow.fullname)
end
end
end

render json: {
message:,
granted: granted?,
userId: user.id,
handler: workflow.name
}
end

def destroy
message = if destroy_authorization
render_to_string(partial: "callout", locals: { i18n_key: "authorization_destroyed", klass: "success" })
else
render_to_string(partial: "callout", locals: { i18n_key: "authorization_not_destroyed", klass: "alert" })
end

render json: {
message:,
granted: granted?,
userId: user.id,
handler: workflow.name
}
end

private

def create_forced_authorization
Decidim::Authorization.create_or_update_from(handler)
Decidim::ActionLogger.log("admin_forces_authorization", current_user, user, nil, handler: workflow.name, user_id: user.id, handler_name: workflow.fullname,
reason: force_verification)
end

def destroy_authorization
if authorization&.destroy
Decidim::ActionLogger.log("admin_destroys_authorization", current_user, user, nil, user_id: user.id, handler: workflow.name, handler_name: workflow.fullname)
end
end

def json_error(exception)
render json: render_to_string(partial: "callout", locals: { message: exception.message, klass: "alert" }), status: :unprocessable_entity
end

def user
@user ||= Decidim::User.find(params[:id])
end

def authorization
@authorization ||= Decidim::Authorization.where.not(granted_at: nil).find_by(user:, name: workflow.name)
end

def granted?
authorization&.reload.present?
rescue ActiveRecord::RecordNotFound
false
end

def workflow
@workflow ||= Decidim::Verifications.find_workflow_manifest(params[:handler])
end

def handler
@handler ||= Decidim::AuthorizationHandler.handler_for(params[:handler], handler_params)
end

def conflict
@conflict ||= Decidim::Authorization.find_by(unique_id: handler.unique_id)
end

def handler_params
(params[:authorization_handler] || {}).merge(user:)
end

def force_verification
@force_verification ||= params[:force_verification].to_s.strip.presence
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module Decidim
module DecidimAwesome
module System
module OrganizationFormOverride
extend ActiveSupport::Concern

included do
alias_method :decidim_original_map_model, :map_model

attribute :awesome_admins_available_authorizations, Array[String]

def map_model(model)
decidim_original_map_model(model)
map_awesome_configs(model)
end

def clean_awesome_admins_available_authorizations
return unless awesome_admins_available_authorizations

awesome_admins_available_authorizations.select(&:present?)
end

private

def map_awesome_configs(organization)
self.awesome_admins_available_authorizations = Decidim::DecidimAwesome::AwesomeConfig.find_by(var: :admins_available_authorizations, organization:)&.value
end
end
end
end
end
end
15 changes: 12 additions & 3 deletions app/forms/decidim/decidim_awesome/admin/config_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ class ConfigForm < Decidim::Form
validates :validate_body_min_length, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :validate_body_max_caps_percent, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }
validates :validate_body_max_marks_together, presence: true, numericality: { greater_than_or_equal_to: 1 }
validates :force_authorization_after_login, inclusion: { in: lambda { |form|
form.current_organization.available_authorizations & Decidim.authorization_workflows.map(&:name)
} }
validate :force_authorization_after_login_is_valid
# TODO: validate non general admins are here

def self.from_params(params, additional_params = {})
Expand Down Expand Up @@ -136,6 +134,17 @@ def sanitize_labels!
code
end
end

private

def force_authorization_after_login_is_valid
return if force_authorization_after_login.blank?

invalid = force_authorization_after_login - (current_organization.available_authorizations & Decidim.authorization_workflows.map(&:name))
return if invalid.empty?

errors.add(:force_authorization_after_login, :invalid)
end
end
end
end
Expand Down
54 changes: 28 additions & 26 deletions app/helpers/decidim/decidim_awesome/map_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,34 @@ def awesome_map_for(components, &)
end

html_options = {
"class" => "awesome-map",
"id" => "awesome-map",
"data-components" => components.map do |component|
{
id: component.id,
type: component.manifest.name,
name: translated_attribute(component.name),
url: Decidim::EngineRouter.main_proxy(component).root_path,
amendments: component.manifest.name == :proposals ? Decidim::Proposals::Proposal.where(component:).only_emendations.count : 0
}
end.to_json,
"data-hide-controls" => settings_source.try(:hide_controls),
"data-collapsed" => global_settings.collapse,
"data-truncate" => global_settings.truncate || 255,
"data-map-center" => global_settings.map_center,
"data-map-zoom" => global_settings.map_zoom || 8,
"data-menu-merge-components" => global_settings.menu_merge_components,
"data-menu-amendments" => global_settings.menu_amendments,
"data-menu-meetings" => global_settings.menu_meetings,
"data-menu-categories" => global_settings.menu_categories,
"data-menu-hashtags" => global_settings.menu_hashtags,
"data-show-not-answered" => step_settings&.show_not_answered,
"data-show-accepted" => step_settings&.show_accepted,
"data-show-withdrawn" => step_settings&.show_withdrawn,
"data-show-evaluating" => step_settings&.show_evaluating,
"data-show-rejected" => step_settings&.show_rejected
class: "awesome-map",
id: "awesome-map",
data: {
"components" => components.map do |component|
{
id: component.id,
type: component.manifest.name,
name: translated_attribute(component.name),
url: Decidim::EngineRouter.main_proxy(component).root_path,
amendments: component.manifest.name == :proposals ? Decidim::Proposals::Proposal.where(component:).only_emendations.count : 0
}
end.to_json,
"hide-controls" => settings_source.try(:hide_controls),
"collapsed" => global_settings.collapse,
"truncate" => global_settings.truncate || 255,
"map-center" => global_settings.map_center,
"map-zoom" => global_settings.map_zoom || 8,
"menu-merge-components" => global_settings.menu_merge_components,
"menu-amendments" => global_settings.menu_amendments,
"menu-meetings" => global_settings.menu_meetings,
"menu-categories" => global_settings.menu_categories,
"menu-hashtags" => global_settings.menu_hashtags,
"show-not-answered" => step_settings&.show_not_answered,
"show-accepted" => step_settings&.show_accepted,
"show-withdrawn" => step_settings&.show_withdrawn,
"show-evaluating" => step_settings&.show_evaluating,
"show-rejected" => step_settings&.show_rejected
}
}

content_tag(:div, html_options) do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- insert_after "erb[loud]:contains('show_email_modal')" -->

<%= render "decidim/decidim_awesome/admin/officializations/verification_modal" %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- insert_before "td.table-list__actions" -->

<% if awesome_config[:admins_available_authorizations] %>
<%= render "decidim/decidim_awesome/admin/officializations/participants_td", user: %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- insert_before "th:last" -->

<% if awesome_config[:admins_available_authorizations] %>
<%= render "decidim/decidim_awesome/admin/officializations/participants_th" %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!-- insert_top '#advanced-settings-panel' -->

<% if Decidim::DecidimAwesome.enabled?(:admins_available_authorizations) %>
<div class="awesome_available_authorizations border-2 rounded border-background p-4 form__wrapper mt-8 first:mt-0 last:pb-4">
<h3 class="h4"><%= t "decidim.decidim_awesome.system.organizations.awesome_tweaks" %></h3>

<%= render partial: "decidim/decidim_awesome/system/organizations/admin_allowed_authorizations", locals: { f: f } %>
</div>
<% end %>
Loading
Loading