DEV: Add support for allowed parameters in user api key scopes
Initially, this feature is only intended for use in core/plugins, so there is no API for requesting a parameter-scoped key. That may change in future.
This commit is contained in:
parent
1cec333f48
commit
23e5c605f6
|
@ -12,7 +12,8 @@ class UserApiKeyScope < ActiveRecord::Base
|
||||||
RouteMatcher.new(methods: :get, actions: 'notifications#index'),
|
RouteMatcher.new(methods: :get, actions: 'notifications#index'),
|
||||||
RouteMatcher.new(methods: :put, actions: 'notifications#mark_read')
|
RouteMatcher.new(methods: :put, actions: 'notifications#mark_read')
|
||||||
],
|
],
|
||||||
session_info: [ RouteMatcher.new(methods: :get, actions: 'session#current') ]
|
session_info: [ RouteMatcher.new(methods: :get, actions: 'session#current') ],
|
||||||
|
bookmarks_calendar: [ RouteMatcher.new(methods: :get, actions: 'users#bookmarks', formats: :ics, params: %i[username]) ]
|
||||||
}
|
}
|
||||||
|
|
||||||
def self.all_scopes
|
def self.all_scopes
|
||||||
|
@ -20,7 +21,7 @@ class UserApiKeyScope < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def permits?(env)
|
def permits?(env)
|
||||||
matchers.any? { |m| m.match?(env: env) }
|
matchers.any? { |m| m.with_allowed_param_values(allowed_parameters).match?(env: env) }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -35,11 +36,12 @@ end
|
||||||
#
|
#
|
||||||
# Table name: user_api_key_scopes
|
# Table name: user_api_key_scopes
|
||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :bigint not null, primary key
|
||||||
# user_api_key_id :integer not null
|
# user_api_key_id :integer not null
|
||||||
# name :string not null
|
# name :string not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
# allowed_parameters :jsonb
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
|
|
@ -1049,6 +1049,7 @@ en:
|
||||||
read: "Read all"
|
read: "Read all"
|
||||||
write: "Write all"
|
write: "Write all"
|
||||||
one_time_password: "Create a one-time login token"
|
one_time_password: "Create a one-time login token"
|
||||||
|
bookmarks_calendar: "Read bookmark reminders"
|
||||||
invalid_public_key: "Sorry, the public key is invalid."
|
invalid_public_key: "Sorry, the public key is invalid."
|
||||||
invalid_auth_redirect: "Sorry, this auth_redirect host is not allowed."
|
invalid_auth_redirect: "Sorry, this auth_redirect host is not allowed."
|
||||||
invalid_token: "Missing, invalid or expired token."
|
invalid_token: "Missing, invalid or expired token."
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddAllowedParametersToUserApiKeyScope < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :user_api_key_scopes, :allowed_parameters, :jsonb
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,3 +8,10 @@ Fabricator(:readonly_user_api_key, from: :user_api_key) do
|
||||||
client_id { SecureRandom.hex }
|
client_id { SecureRandom.hex }
|
||||||
application_name 'some app'
|
application_name 'some app'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Fabricator(:bookmarks_calendar_user_api_key, from: :user_api_key) do
|
||||||
|
user
|
||||||
|
scopes { [Fabricate.build(:user_api_key_scope, name: 'bookmarks_calendar')] }
|
||||||
|
client_id { SecureRandom.hex }
|
||||||
|
application_name 'some app'
|
||||||
|
end
|
||||||
|
|
|
@ -99,4 +99,33 @@ describe 'user api keys' do
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can restrict scopes by parameters" do
|
||||||
|
admin = Fabricate(:admin)
|
||||||
|
|
||||||
|
calendar_key = Fabricate(:bookmarks_calendar_user_api_key, user: admin)
|
||||||
|
|
||||||
|
get "/u/#{user.username}/bookmarks.json", headers: {
|
||||||
|
HTTP_USER_API_KEY: calendar_key.key,
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(403) # Does not allow json
|
||||||
|
|
||||||
|
get "/u/#{user.username}/bookmarks.ics", headers: {
|
||||||
|
HTTP_USER_API_KEY: calendar_key.key,
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200) # Allows ICS
|
||||||
|
|
||||||
|
# Now restrict the key
|
||||||
|
calendar_key.scopes.first.update(allowed_parameters: { username: admin.username })
|
||||||
|
|
||||||
|
get "/u/#{user.username}/bookmarks.ics", headers: {
|
||||||
|
HTTP_USER_API_KEY: calendar_key.key,
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(403) # Cannot access another users calendar
|
||||||
|
|
||||||
|
get "/u/#{admin.username}/bookmarks.ics", headers: {
|
||||||
|
HTTP_USER_API_KEY: calendar_key.key,
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(200) # Can access own calendar
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue