diff --git a/Gemfile.lock b/Gemfile.lock
index 5899690..fa85f88 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -3,13 +3,13 @@ PATH
specs:
decidim-rest_full (0.0.2)
api-pagination (~> 6.0)
+ cancancan
decidim-admin (>= 0.28, < 0.30)
decidim-comments (>= 0.28, < 0.30)
decidim-core (>= 0.28, < 0.30)
doorkeeper
jsonapi-serializer
rswag-api
- rswag-ui
GEM
remote: https://rubygems.org/
@@ -108,6 +108,7 @@ GEM
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
byebug (11.1.3)
+ cancancan (3.6.1)
capybara (3.40.0)
addressable
matrix
@@ -677,9 +678,6 @@ GEM
json-schema (>= 2.2, < 6.0)
railties (>= 5.2, < 8.0)
rspec-core (>= 2.14)
- rswag-ui (2.15.0)
- actionpack (>= 5.2, < 8.0)
- railties (>= 5.2, < 8.0)
rubocop (1.65.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
diff --git a/Rakefile b/Rakefile
index 2d9116c..472c000 100644
--- a/Rakefile
+++ b/Rakefile
@@ -6,7 +6,7 @@ require "rspec/core/rake_task"
def install_module(path)
Dir.chdir(path) do
system("bundle check || bundle install")
- # system("bundle exec rake decidim_rest_full:install:migrations")
+ system("bundle exec rake decidim_rest_full:install:migrations")
system("bundle exec rails db:migrate")
end
end
diff --git a/app/controllers/decidim/api/rest_full/application_controller.rb b/app/controllers/decidim/api/rest_full/application_controller.rb
index 4cf0b5b..d4a006e 100644
--- a/app/controllers/decidim/api/rest_full/application_controller.rb
+++ b/app/controllers/decidim/api/rest_full/application_controller.rb
@@ -5,6 +5,7 @@ module Api
module RestFull
class ApplicationController < ActionController::API
include Decidim::RestFull::ApiException::Handler
+ delegate :can?, :cannot?, :authorize!, to: :ability
protected
@@ -59,6 +60,10 @@ def available_locales
private
+ def ability
+ @ability ||= Decidim::RestFull::Ability.from_doorkeeper_token(doorkeeper_token)
+ end
+
def populate_params
@populate_params ||= if params[:populate].is_a?(String)
params[:populate].split(",").map(&:to_sym)
diff --git a/app/controllers/decidim/api/rest_full/system/organizations_controller.rb b/app/controllers/decidim/api/rest_full/system/organizations_controller.rb
index 22d6884..7fd9662 100644
--- a/app/controllers/decidim/api/rest_full/system/organizations_controller.rb
+++ b/app/controllers/decidim/api/rest_full/system/organizations_controller.rb
@@ -6,39 +6,46 @@ module Api
module RestFull
module System
class OrganizationsController < ApplicationController
- before_action { doorkeeper_authorize! :system }
+ before_action do
+ doorkeeper_authorize! :system
+ authorize! :read, ::Decidim::Organization
+ end
# List all organizations
def index
- # Extract only the populated fields
- allowed_fields = OrganizationSerializer.db_fields
- only_fields = populated_fields([], allowed_fields)
-
# Fetch organizations and paginate
- organizations = paginate(Decidim::Organization.select(*only_fields))
+ organizations = paginate(collection)
# Render the response
- render json: OrganizationSerializer.new(
- organizations,
- params: { only: only_fields, locales: available_locales },
- fields: { organization: only_fields.push(:meta) }
- ).serializable_hash
+ render json: serializable_hash(organizations)
end
# Show a single organization
def show
- # Extract only the populated fields
- only_fields = populated_fields(OrganizationSerializer.db_fields, OrganizationSerializer.db_fields)
-
# Find the organization by ID
- organization = Decidim::Organization.find(params[:id])
-
+ organization = collection.find(params[:id])
# Render the response
- render json: OrganizationSerializer.new(
- organization,
- params: { only: only_fields, locales: available_locales },
- fields: { organization: only_fields.map(&:to_sym) }
+ render json: serializable_hash(organization)
+ end
+
+ private
+
+ def serializable_hash(resource)
+ OrganizationSerializer.new(
+ resource,
+ params: { locales: available_locales }
).serializable_hash
end
+
+ def collection
+ Decidim::Organization.select(
+ :id,
+ :name,
+ :secondary_hosts,
+ :host,
+ :created_at,
+ :updated_at
+ )
+ end
end
end
end
diff --git a/app/controllers/decidim/api/rest_full/system/users_controller.rb b/app/controllers/decidim/api/rest_full/system/users_controller.rb
index 4dd620f..52fe190 100644
--- a/app/controllers/decidim/api/rest_full/system/users_controller.rb
+++ b/app/controllers/decidim/api/rest_full/system/users_controller.rb
@@ -5,7 +5,10 @@ module Api
module RestFull
module System
class UsersController < ApplicationController
- before_action { doorkeeper_authorize! :system }
+ before_action do
+ doorkeeper_authorize! :system
+ authorize! :read, ::Decidim::User
+ end
# List all users
def index
diff --git a/app/controllers/decidim/rest_full/system/api_clients_controller.rb b/app/controllers/decidim/rest_full/system/api_clients_controller.rb
index 8b17250..7c40625 100644
--- a/app/controllers/decidim/rest_full/system/api_clients_controller.rb
+++ b/app/controllers/decidim/rest_full/system/api_clients_controller.rb
@@ -31,6 +31,7 @@ def new
def edit
@api_client = collection.find(params[:id])
@form = form(ApiClientForm).from_model(@api_client)
+ @perm_form = form(ApiPermissions).from_model(@api_client)
end
def create
diff --git a/app/controllers/decidim/rest_full/system/permissions_controller.rb b/app/controllers/decidim/rest_full/system/permissions_controller.rb
new file mode 100644
index 0000000..0842dde
--- /dev/null
+++ b/app/controllers/decidim/rest_full/system/permissions_controller.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Decidim
+ module RestFull
+ module System
+ class PermissionsController < Decidim::System::ApplicationController
+ helper Decidim::Admin::AttributesDisplayHelper
+ helper Decidim::Core::Engine.routes.url_helpers
+ helper_method :destroy_admin_session_path
+
+ def core_engine_routes
+ Decidim::Core::Engine.routes.url_helpers
+ end
+
+ def create
+ @form = form(ApiPermissions).from_params(params)
+ api_client = Decidim::RestFull::ApiClient.find(@form.api_client_id)
+ api_client.permissions = @form.permissions.map do |perm_string|
+ api_client.permissions.build(permission: perm_string)
+ end
+ api_client.save!
+ redirect_to core_engine_routes.edit_system_api_client_path(api_client)
+ end
+ end
+ end
+ end
+end
diff --git a/app/forms/decidim/rest_full/api_permissions.rb b/app/forms/decidim/rest_full/api_permissions.rb
new file mode 100644
index 0000000..c3cfc9a
--- /dev/null
+++ b/app/forms/decidim/rest_full/api_permissions.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Decidim
+ module RestFull
+ # The form that validates the data to construct a valid OAuthApplication.
+ class ApiPermissions < Decidim::Form
+ mimic :system_api_client
+ attribute :permissions, [String]
+ attribute :api_client_id, Integer
+
+ def organization
+ current_organization || Decidim::Organization.find_by(id: decidim_organization_id)
+ end
+ end
+ end
+end
diff --git a/app/models/decidim/rest_full/ability.rb b/app/models/decidim/rest_full/ability.rb
new file mode 100644
index 0000000..2358ed1
--- /dev/null
+++ b/app/models/decidim/rest_full/ability.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require "cancan"
+
+module Decidim
+ module RestFull
+ class Ability
+ include ::CanCan::Ability
+ attr_reader :api_client, :permissions
+
+ def initialize(api_client)
+ return unless api_client
+
+ @api_client = api_client
+ @permissions = api_client.permission_strings
+
+ can :impersonate, Decidim::RestFull::ApiClient if permissions.include? "oauth.impersonate"
+ can :login, Decidim::RestFull::ApiClient if permissions.include? "oauth.login"
+ # Switch scopes and compose permissions
+ scopes = api_client.scopes.to_a
+ perms_for_public if scopes.include? "public"
+ perms_for_system if scopes.include? "system"
+ perms_for_proposals if scopes.include? "proposals"
+ end
+
+ def self.from_doorkeeper_token(doorkeeper_token)
+ return Decidim::RestFull::Ability.new(nil) unless doorkeeper_token && doorkeeper_token.valid?
+ return Decidim::RestFull::Ability.new(nil) unless doorkeeper_token.application.is_a? Decidim::RestFull::ApiClient
+
+ Decidim::RestFull::Ability.new(doorkeeper_token.application)
+ end
+
+ private
+
+ def perms_for_public; end
+
+ def perms_for_system
+ can :read, ::Decidim::Organization if permissions.include? "system.organizations.read"
+ can :read, ::Decidim::User if permissions.include? "system.users.read"
+ end
+
+ def perms_for_proposals; end
+ end
+ end
+end
diff --git a/app/models/decidim/rest_full/api_client.rb b/app/models/decidim/rest_full/api_client.rb
index 2e36d47..325dc04 100644
--- a/app/models/decidim/rest_full/api_client.rb
+++ b/app/models/decidim/rest_full/api_client.rb
@@ -7,10 +7,15 @@ class ApiClient < ::Doorkeeper::Application
include Decidim::Loggable
belongs_to :organization, foreign_key: "decidim_organization_id", class_name: "Decidim::Organization", inverse_of: :api_clients
+ has_many :permissions, class_name: "Decidim::RestFull::Permission", dependent: :destroy
validates :scopes, presence: true
before_validation :dummy_attributes
+ def permission_strings
+ @permission_strings ||= permissions.pluck(:permission)
+ end
+
def owner
organization
end
diff --git a/app/models/decidim/rest_full/permission.rb b/app/models/decidim/rest_full/permission.rb
new file mode 100644
index 0000000..24a029d
--- /dev/null
+++ b/app/models/decidim/rest_full/permission.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Decidim
+ module RestFull
+ class Permission < ::ApplicationRecord
+ self.table_name = "decidim_rest_full_api_client_permissions"
+
+ belongs_to :api_client, class_name: "Decidim::RestFull::ApiClient"
+
+ validates :permission, presence: true
+ end
+ end
+end
diff --git a/app/views/decidim/rest_full/system/api_clients/edit.html.erb b/app/views/decidim/rest_full/system/api_clients/edit.html.erb
index c061a10..c92abcc 100644
--- a/app/views/decidim/rest_full/system/api_clients/edit.html.erb
+++ b/app/views/decidim/rest_full/system/api_clients/edit.html.erb
@@ -22,59 +22,92 @@
<%= t(".permissions_scopes") %><%= @api_client.scopes.to_a %>
+<%= form_for(@perm_form, method: :post, url: system_api_permissions_path ) do |form| %>
+<%= form.hidden_field :api_client_id, value: @api_client.id %>
+
<% if @api_client.scopes.to_a.include?("system") %>