8000 Add support for dynamic scopes by stanhu · Pull Request #1739 · doorkeeper-gem/doorkeeper · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add support for dynamic scopes #1739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 30, 2024
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
3 changes: 3 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
exclude_patterns:
- "lib/doorkeeper/config.rb"
- "spec/"
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ User-visible changes worth mentioning.

Add your entry here.

- [#1739] Add support for dynamic scopes
- [#1715] Fix token introspection invalid request reason
- [#1714] Fix `Doorkeeper::AccessToken.find_or_create_for` with empty scopes which raises NoMethodError
- [#1712] Add `Pragma: no-cache` to token response
Expand Down
18 changes: 18 additions & 0 deletions lib/doorkeeper/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ def confirm_application_owner
@config.instance_variable_set(:@confirm_application_owner, true)
end

# Provide support for dynamic scopes (e.g. user:*) (disabled by default)
# Optional parameter delimiter (default ":") if you want to customize
# the delimiter separating the scope name and matching value.
#
# @param opts [Hash] the options to configure dynamic scopes
def enable_dynamic_scopes(opts = {})
@config.instance_variable_set(:@enable_dynamic_scopes, true)
@config.instance_variable_set(:@dynamic_scopes_delimiter, opts[:delimiter] || ':')
end

# Define default access token scopes for your provider
#
# @param scopes [Array] Default set of access (OAuth::Scopes.new)
Expand Down Expand Up @@ -511,6 +521,14 @@ def enable_application_owner?
option_set? :enable_application_owner
end

def enable_dynamic_scopes?
option_set? :enable_dynamic_scopes
end

def dynamic_scopes_delimiter
@dynamic_scopes_delimiter
end

def polymorphic_resource_owner?
option_set? :polymorphic_resource_owner
end
Expand Down
38 changes: 37 additions & 1 deletion lib/doorkeeper/oauth/scopes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class Scopes
include Enumerable
include Comparable

DYNAMIC_SCOPE_WILDCARD = "*"

def self.from_string(string)
string ||= ""
new.tap do |scope|
Expand All @@ -26,7 +28,15 @@ def initialize
end

def exists?(scope)
@scopes.include? scope.to_s
scope = scope.to_s

@scopes.any? do |allowed_scope|
if dynamic_scopes_enabled? && dynamic_scopes_present?(allowed_scope, scope)
dynamic_scope_match?(allowed_scope, scope)
else
allowed_scope == scope
end
end
end

def add(*scopes)
Expand Down Expand Up @@ -66,6 +76,32 @@ def &(other)

private

def dynamic_scopes_enabled?
Doorkeeper.config.enable_dynamic_scopes?
end

def dynamic_scope_delimiter
return unless dynamic_scopes_enabled?

@dynamic_scope_delimiter ||= Doorkeeper.config.dynamic_scopes_delimiter
end

def dynamic_scopes_present?(allowed, requested)
allowed.include?(dynamic_scope_delimiter) && requested.include?(dynamic_scope_delimiter)
end

def dynamic_scope_match?(allowed, requested)
allowed_pattern = allowed.split(dynamic_scope_delimiter, 2)
request_pattern = requested.split(dynamic_scope_delimiter, 2)

return false if allowed_pattern[0] != request_pattern[0]
return false if allowed_pattern[1].blank?
return false if request_pattern[1].blank?
return true if allowed_pattern[1] == DYNAMIC_SCOPE_WILDCARD && allowed_pattern[1].present?

allowed_pattern[1] == request_pattern[1]
end

def to_array(other)
case other
when Scopes
Expand Down
32 changes: 32 additions & 0 deletions spec/lib/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,38 @@
end
end

describe "enable_dynamic_scopes" do
it "is disabled by default" do
expect(Doorkeeper.config.enable_dynamic_scopes?).not_to be(true)
end

context "when enabled with default delimiter" do
before do
Doorkeeper.configure do
enable_dynamic_scopes
end
end

it 'returns true' do
expect(Doorkeeper.config.enable_dynamic_scopes?).to be(true)
expect(Doorkeeper.config.dynamic_scopes_delimiter).to eq(":")
end
end

context "when enabled with custom delimiter" do
before do
Doorkeeper.configure do
enable_dynamic_scopes(delimiter: "-")
end
end

it 'returns true' do
expect(Doorkeeper.config.enable_dynamic_scopes?).to be(true)
expect(Doorkeeper.config.dynamic_scopes_delimiter).to eq("-")
end
end
end

describe "enable_application_owner" do
it "is disabled by default" do
expect(Doorkeeper.config.enable_application_owner?).not_to be(true)
Expand Down
90 changes: 90 additions & 0 deletions spec/lib/oauth/scopes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,95 @@
it "is false if no scopes are included even for existing ones" do
expect(scopes).not_to have_scopes(described_class.from_string("public admin notexistent"))
end

context "with dynamic scopes disabled" do
context "with wildcard dynamic scope" do
before do
scopes.add "user:*"
end

it "returns false with specific user" do
expect(scopes).not_to have_scopes(described_class.from_string("public user:1"))
end

it "returns true with wildcard user" do
expect(scopes).to have_scopes(described_class.from_string("public user:*"))
end

it "returns false if requested scope missing parameter" do
expect(scopes).not_to have_scopes(described_class.from_string("public user:"))
end
end
end

context "with dynamic scopes enabled" do
before do
Doorkeeper.configure do
enable_dynamic_scopes
end
end

context "with wildcard dynamic scope" do
before do
scopes.add "user:*"
end

it "returns true with specific user" do
expect(scopes).to have_scopes(described_class.from_string("public user:1"))
end

it "returns true with wildcard user" do
expect(scopes).to have_scopes(described_class.from_string("public user:*"))
end

it "returns false if requested scope missing parameter" do
expect(scopes).not_to have_scopes(described_class.from_string("public user:"))
end

it "returns false if dynamic scope does not match" do
expect(scopes).not_to have_scopes(described_class.from_string("public userA:1"))
end
end

context "with specific dynamic scope" do
before do
scopes.add "user:1"
end

it "returns true with specific user" do
expect(scopes).to have_scopes(described_class.from_string("public user:1"))
end

it "returns false with wildcard user" do
expect(scopes).not_to have_scopes(described_class.from_string("public user:*"))
end

it "returns false for disallowed user" do
expect(scopes).not_to have_scopes(described_class.from_string("public user:2"))
end

context "with custom delimiter" do
before do
Doorkeeper.configure do
enable_dynamic_scopes(delimiter: "-")
end

scopes.add "user-1"
end

it "returns true with specific user" do
expect(scopes).to have_scopes(described_class.from_string("public user-1"))
end

it "returns false with wildcard user" do
expect(scopes).not_to have_scopes(described_class.from_string("public user-*"))
end

it "returns false for disallowed user" do
expect(scopes).not_to have_scopes(described_class.from_string("public user-2"))
end
end
end
end
end
end
0