From 6c6e8883f703efe8e05d893a94c169ac90733d42 Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Fri, 30 Jul 2021 22:24:51 +0100 Subject: [PATCH] Introduce privileged scopes that only an administrator can enable --- .../oauth2_applications_controller.rb | 4 +-- app/models/oauth2_application.rb | 13 ++++++++++ app/views/oauth2_applications/_form.html.erb | 2 +- config/initializers/doorkeeper.rb | 4 ++- lib/oauth.rb | 7 +++-- .../oauth2_applications_controller_test.rb | 26 +++++++++---------- test/factories/oauth_applications.rb | 2 +- 7 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 app/models/oauth2_application.rb diff --git a/app/controllers/oauth2_applications_controller.rb b/app/controllers/oauth2_applications_controller.rb index 63b77be4b..97d84b173 100644 --- a/app/controllers/oauth2_applications_controller.rb +++ b/app/controllers/oauth2_applications_controller.rb @@ -20,8 +20,8 @@ class Oauth2ApplicationsController < Doorkeeper::ApplicationsController end def application_params - params[:doorkeeper_application][:scopes]&.delete("") - params.require(:doorkeeper_application) + params[:oauth2_application][:scopes]&.delete("") + params.require(:oauth2_application) .permit(:name, :redirect_uri, :confidential, :scopes => []) .merge(:owner => current_resource_owner) end diff --git a/app/models/oauth2_application.rb b/app/models/oauth2_application.rb new file mode 100644 index 000000000..165761520 --- /dev/null +++ b/app/models/oauth2_application.rb @@ -0,0 +1,13 @@ +class Oauth2Application < Doorkeeper::Application + belongs_to :owner, :polymorphic => true + + validate :allowed_scopes + + private + + def allowed_scopes + return if owner.administrator? + + errors.add(:scopes) if scopes.any? { |scope| Oauth::PRIVILEGED_SCOPES.include?(scope) } + end +end diff --git a/app/views/oauth2_applications/_form.html.erb b/app/views/oauth2_applications/_form.html.erb index d69536c6b..7fde3e0e7 100644 --- a/app/views/oauth2_applications/_form.html.erb +++ b/app/views/oauth2_applications/_form.html.erb @@ -3,5 +3,5 @@ <%= f.form_group :confidential do %> <%= f.check_box :confidential %> <% end %> -<%= f.collection_check_boxes :scopes, Oauth.scopes, :name, :description %> +<%= f.collection_check_boxes :scopes, Oauth.scopes(:privileged => current_user.administrator?), :name, :description %> <%= f.primary %> diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 549138b33..32156b821 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -48,6 +48,8 @@ Doorkeeper.configure do # end # end + application_class "Oauth2Application" + # Enables polymorphic Resource Owner association for Access Tokens and Access Grants. # By default this option is disabled. # @@ -221,7 +223,7 @@ Doorkeeper.configure do # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes # default_scopes :public - optional_scopes(*Oauth::SCOPES) + optional_scopes(*Oauth::SCOPES, *Oauth::PRIVILEGED_SCOPES) # Allows to restrict only certain scopes for grant_type. # By default, all the scopes will be available for all the grant types. diff --git a/lib/oauth.rb b/lib/oauth.rb index 8f45a3b4b..6980447f9 100644 --- a/lib/oauth.rb +++ b/lib/oauth.rb @@ -1,5 +1,6 @@ module Oauth SCOPES = %w[read_prefs write_prefs write_diary write_api read_gpx write_gpx write_notes].freeze + PRIVILEGED_SCOPES = %w[].freeze class Scope attr_reader :name @@ -13,7 +14,9 @@ module Oauth end end - def self.scopes - SCOPES.collect { |s| Scope.new(s) } + def self.scopes(privileged: false) + scopes = SCOPES + scopes += PRIVILEGED_SCOPES if privileged + scopes.collect { |s| Scope.new(s) } end end diff --git a/test/controllers/oauth2_applications_controller_test.rb b/test/controllers/oauth2_applications_controller_test.rb index eec5e02ec..338144a95 100644 --- a/test/controllers/oauth2_applications_controller_test.rb +++ b/test/controllers/oauth2_applications_controller_test.rb @@ -67,11 +67,11 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest assert_response :success assert_template "oauth2_applications/new" assert_select "form", 1 do - assert_select "input#doorkeeper_application_name", 1 - assert_select "textarea#doorkeeper_application_redirect_uri", 1 - assert_select "input#doorkeeper_application_confidential", 1 + assert_select "input#oauth2_application_name", 1 + assert_select "textarea#oauth2_application_redirect_uri", 1 + assert_select "input#oauth2_application_confidential", 1 Oauth.scopes.each do |scope| - assert_select "input#doorkeeper_application_scopes_#{scope.name}", 1 + assert_select "input#oauth2_application_scopes_#{scope.name}", 1 end end end @@ -87,7 +87,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest session_for(user) assert_difference "Doorkeeper::Application.count", 0 do - post oauth_applications_path(:doorkeeper_application => { + post oauth_applications_path(:oauth2_application => { :name => "Test Application" }) end @@ -95,7 +95,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest assert_template "oauth2_applications/new" assert_difference "Doorkeeper::Application.count", 0 do - post oauth_applications_path(:doorkeeper_application => { + post oauth_applications_path(:oauth2_application => { :name => "Test Application", :redirect_uri => "https://test.example.com/", :scopes => ["bad_scope"] @@ -105,7 +105,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest assert_template "oauth2_applications/new" assert_difference "Doorkeeper::Application.count", 1 do - post oauth_applications_path(:doorkeeper_application => { + post oauth_applications_path(:oauth2_application => { :name => "Test Application", :redirect_uri => "https://test.example.com/", :scopes => ["read_prefs"] @@ -154,11 +154,11 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest assert_response :success assert_template "oauth2_applications/edit" assert_select "form", 1 do - assert_select "input#doorkeeper_application_name", 1 - assert_select "textarea#doorkeeper_application_redirect_uri", 1 - assert_select "input#doorkeeper_application_confidential", 1 + assert_select "input#oauth2_application_name", 1 + assert_select "textarea#oauth2_application_redirect_uri", 1 + assert_select "input#oauth2_application_confidential", 1 Oauth.scopes.each do |scope| - assert_select "input#doorkeeper_application_scopes_#{scope.name}", 1 + assert_select "input#oauth2_application_scopes_#{scope.name}", 1 end end end @@ -178,7 +178,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest assert_template "oauth2_applications/not_found" put oauth_application_path(:id => client, - :doorkeeper_application => { + :oauth2_application => { :name => "New Name", :redirect_uri => nil }) @@ -186,7 +186,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest assert_template "oauth2_applications/edit" put oauth_application_path(:id => client, - :doorkeeper_application => { + :oauth2_application => { :name => "New Name", :redirect_uri => "https://new.example.com/url" }) diff --git a/test/factories/oauth_applications.rb b/test/factories/oauth_applications.rb index a9b3b875d..1e62d2c97 100644 --- a/test/factories/oauth_applications.rb +++ b/test/factories/oauth_applications.rb @@ -1,5 +1,5 @@ FactoryBot.define do - factory :oauth_application, :class => "Doorkeeper::Application" do + factory :oauth_application, :class => "Oauth2Application" do sequence(:name) { |n| "OAuth application #{n}" } sequence(:redirect_uri) { |n| "https://example.com/app/#{n}" } -- 2.39.5