Introduce privileged scopes that only an administrator can enable

This commit is contained in:
Tom Hughes 2021-07-30 22:24:51 +01:00
parent 6a9ab7cb2d
commit 6c6e8883f7
7 changed files with 38 additions and 20 deletions

View file

@ -20,8 +20,8 @@ class Oauth2ApplicationsController < Doorkeeper::ApplicationsController
end end
def application_params def application_params
params[:doorkeeper_application][:scopes]&.delete("") params[:oauth2_application][:scopes]&.delete("")
params.require(:doorkeeper_application) params.require(:oauth2_application)
.permit(:name, :redirect_uri, :confidential, :scopes => []) .permit(:name, :redirect_uri, :confidential, :scopes => [])
.merge(:owner => current_resource_owner) .merge(:owner => current_resource_owner)
end end

View file

@ -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

View file

@ -3,5 +3,5 @@
<%= f.form_group :confidential do %> <%= f.form_group :confidential do %>
<%= f.check_box :confidential %> <%= f.check_box :confidential %>
<% end %> <% 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 %> <%= f.primary %>

View file

@ -48,6 +48,8 @@ Doorkeeper.configure do
# end # end
# end # end
application_class "Oauth2Application"
# Enables polymorphic Resource Owner association for Access Tokens and Access Grants. # Enables polymorphic Resource Owner association for Access Tokens and Access Grants.
# By default this option is disabled. # By default this option is disabled.
# #
@ -221,7 +223,7 @@ Doorkeeper.configure do
# https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes
# default_scopes :public # default_scopes :public
optional_scopes(*Oauth::SCOPES) optional_scopes(*Oauth::SCOPES, *Oauth::PRIVILEGED_SCOPES)
# Allows to restrict only certain scopes for grant_type. # Allows to restrict only certain scopes for grant_type.
# By default, all the scopes will be available for all the grant types. # By default, all the scopes will be available for all the grant types.

View file

@ -1,5 +1,6 @@
module Oauth module Oauth
SCOPES = %w[read_prefs write_prefs write_diary write_api read_gpx write_gpx write_notes].freeze SCOPES = %w[read_prefs write_prefs write_diary write_api read_gpx write_gpx write_notes].freeze
PRIVILEGED_SCOPES = %w[].freeze
class Scope class Scope
attr_reader :name attr_reader :name
@ -13,7 +14,9 @@ module Oauth
end end
end end
def self.scopes def self.scopes(privileged: false)
SCOPES.collect { |s| Scope.new(s) } scopes = SCOPES
scopes += PRIVILEGED_SCOPES if privileged
scopes.collect { |s| Scope.new(s) }
end end
end end

View file

@ -67,11 +67,11 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest
assert_response :success assert_response :success
assert_template "oauth2_applications/new" assert_template "oauth2_applications/new"
assert_select "form", 1 do assert_select "form", 1 do
assert_select "input#doorkeeper_application_name", 1 assert_select "input#oauth2_application_name", 1
assert_select "textarea#doorkeeper_application_redirect_uri", 1 assert_select "textarea#oauth2_application_redirect_uri", 1
assert_select "input#doorkeeper_application_confidential", 1 assert_select "input#oauth2_application_confidential", 1
Oauth.scopes.each do |scope| 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 end
end end
@ -87,7 +87,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest
session_for(user) session_for(user)
assert_difference "Doorkeeper::Application.count", 0 do assert_difference "Doorkeeper::Application.count", 0 do
post oauth_applications_path(:doorkeeper_application => { post oauth_applications_path(:oauth2_application => {
:name => "Test Application" :name => "Test Application"
}) })
end end
@ -95,7 +95,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest
assert_template "oauth2_applications/new" assert_template "oauth2_applications/new"
assert_difference "Doorkeeper::Application.count", 0 do assert_difference "Doorkeeper::Application.count", 0 do
post oauth_applications_path(:doorkeeper_application => { post oauth_applications_path(:oauth2_application => {
:name => "Test Application", :name => "Test Application",
:redirect_uri => "https://test.example.com/", :redirect_uri => "https://test.example.com/",
:scopes => ["bad_scope"] :scopes => ["bad_scope"]
@ -105,7 +105,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest
assert_template "oauth2_applications/new" assert_template "oauth2_applications/new"
assert_difference "Doorkeeper::Application.count", 1 do assert_difference "Doorkeeper::Application.count", 1 do
post oauth_applications_path(:doorkeeper_application => { post oauth_applications_path(:oauth2_application => {
:name => "Test Application", :name => "Test Application",
:redirect_uri => "https://test.example.com/", :redirect_uri => "https://test.example.com/",
:scopes => ["read_prefs"] :scopes => ["read_prefs"]
@ -154,11 +154,11 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest
assert_response :success assert_response :success
assert_template "oauth2_applications/edit" assert_template "oauth2_applications/edit"
assert_select "form", 1 do assert_select "form", 1 do
assert_select "input#doorkeeper_application_name", 1 assert_select "input#oauth2_application_name", 1
assert_select "textarea#doorkeeper_application_redirect_uri", 1 assert_select "textarea#oauth2_application_redirect_uri", 1
assert_select "input#doorkeeper_application_confidential", 1 assert_select "input#oauth2_application_confidential", 1
Oauth.scopes.each do |scope| 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 end
end end
@ -178,7 +178,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest
assert_template "oauth2_applications/not_found" assert_template "oauth2_applications/not_found"
put oauth_application_path(:id => client, put oauth_application_path(:id => client,
:doorkeeper_application => { :oauth2_application => {
:name => "New Name", :name => "New Name",
:redirect_uri => nil :redirect_uri => nil
}) })
@ -186,7 +186,7 @@ class Oauth2ApplicationsControllerTest < ActionDispatch::IntegrationTest
assert_template "oauth2_applications/edit" assert_template "oauth2_applications/edit"
put oauth_application_path(:id => client, put oauth_application_path(:id => client,
:doorkeeper_application => { :oauth2_application => {
:name => "New Name", :name => "New Name",
:redirect_uri => "https://new.example.com/url" :redirect_uri => "https://new.example.com/url"
}) })

View file

@ -1,5 +1,5 @@
FactoryBot.define do FactoryBot.define do
factory :oauth_application, :class => "Doorkeeper::Application" do factory :oauth_application, :class => "Oauth2Application" do
sequence(:name) { |n| "OAuth application #{n}" } sequence(:name) { |n| "OAuth application #{n}" }
sequence(:redirect_uri) { |n| "https://example.com/app/#{n}" } sequence(:redirect_uri) { |n| "https://example.com/app/#{n}" }