mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-23 11:23:27 +08:00
Some checks are pending
Licenses / run (push) Waiting to run
Linting / run (push) Waiting to run
Publish Assets / publish-assets (push) Waiting to run
Tests / core backend (push) Waiting to run
Tests / plugins backend (push) Waiting to run
Tests / core frontend (Chrome) (push) Waiting to run
Tests / plugins frontend (push) Waiting to run
Tests / themes frontend (push) Waiting to run
Tests / core system (push) Waiting to run
Tests / plugins system (push) Waiting to run
Tests / themes system (push) Waiting to run
Tests / core frontend (Firefox ESR) (push) Waiting to run
Tests / core frontend (Firefox Evergreen) (push) Waiting to run
Tests / chat system (push) Waiting to run
Tests / merge (push) Blocked by required conditions
This will allow you to create an api key scope just for creating users.
212 lines
6.9 KiB
Ruby
Vendored
212 lines
6.9 KiB
Ruby
Vendored
# encoding: utf-8
|
|
# frozen_string_literal: true
|
|
|
|
RSpec.describe ApiKey do
|
|
fab!(:user)
|
|
|
|
it { is_expected.to belong_to :user }
|
|
it { is_expected.to belong_to :created_by }
|
|
it { is_expected.to validate_length_of(:description).is_at_most(255) }
|
|
it { is_expected.to define_enum_for(:scope_mode).with_values(%w[global read_only granular]) }
|
|
|
|
it "validates at least one scope for granular mode" do
|
|
api_key = ApiKey.new
|
|
api_key.scope_mode = "granular"
|
|
|
|
api_key.validate
|
|
|
|
expect(api_key.errors).to contain_exactly("Api key scopes at least one must be selected")
|
|
end
|
|
|
|
it "generates a key when saving" do
|
|
api_key = ApiKey.new
|
|
api_key.save!
|
|
initial_key = api_key.key
|
|
expect(initial_key.length).to eq(64)
|
|
|
|
# Does not overwrite key when saving again
|
|
api_key.description = "My description here"
|
|
api_key.save!
|
|
expect(api_key.reload.key).to eq(initial_key)
|
|
end
|
|
|
|
it "does not have the key when loading later from the database" do
|
|
api_key = ApiKey.create!
|
|
expect(api_key.key_available?).to eq(true)
|
|
expect(api_key.key.length).to eq(64)
|
|
|
|
api_key = ApiKey.find(api_key.id)
|
|
expect(api_key.key_available?).to eq(false)
|
|
expect { api_key.key }.to raise_error(ApiKey::KeyAccessError)
|
|
end
|
|
|
|
it "can lookup keys based on their hash" do
|
|
key = ApiKey.create!.key
|
|
expect(ApiKey.with_key(key).length).to eq(1)
|
|
end
|
|
|
|
it "can calculate the epoch correctly" do
|
|
expect(ApiKey.last_used_epoch.to_datetime).to be_a(DateTime)
|
|
|
|
SiteSetting.api_key_last_used_epoch = ""
|
|
expect(ApiKey.last_used_epoch).to eq(nil)
|
|
end
|
|
|
|
it "can automatically revoke unused keys" do
|
|
now = Time.now
|
|
|
|
SiteSetting.api_key_last_used_epoch = now - 2.years
|
|
SiteSetting.revoke_api_keys_unused_days = 180 # 6 months
|
|
|
|
freeze_time now - 1.year
|
|
never_used = Fabricate(:api_key)
|
|
used_previously = Fabricate(:api_key)
|
|
used_previously.update(last_used_at: Time.zone.now)
|
|
used_recently = Fabricate(:api_key)
|
|
|
|
freeze_time now - 3.months
|
|
used_recently.update(last_used_at: Time.zone.now)
|
|
|
|
freeze_time now
|
|
ApiKey.revoke_unused_keys!
|
|
|
|
[never_used, used_previously, used_recently].each(&:reload)
|
|
expect(never_used.revoked_at).to_not eq(nil)
|
|
expect(used_previously.revoked_at).to_not eq(nil)
|
|
expect(used_recently.revoked_at).to eq(nil)
|
|
|
|
# Restore them
|
|
[never_used, used_previously, used_recently].each { |a| a.update(revoked_at: nil) }
|
|
|
|
# Move the epoch to 1 month ago
|
|
SiteSetting.api_key_last_used_epoch = now - 1.month
|
|
ApiKey.revoke_unused_keys!
|
|
|
|
[never_used, used_previously, used_recently].each(&:reload)
|
|
expect(never_used.revoked_at).to eq(nil)
|
|
expect(used_previously.revoked_at).to eq(nil)
|
|
expect(used_recently.revoked_at).to eq(nil)
|
|
end
|
|
|
|
it "can automatically revoke keys by max life" do
|
|
freeze_time
|
|
|
|
SiteSetting.revoke_api_keys_maxlife_days = 2
|
|
|
|
older_key = Fabricate(:api_key, created_at: 3.days.ago)
|
|
newer_key = Fabricate(:api_key, created_at: 1.days.ago)
|
|
revoked_key = Fabricate(:api_key, created_at: 3.days.ago, revoked_at: 1.day.ago)
|
|
|
|
expect { ApiKey.revoke_max_life_keys! }.to change { older_key.reload.revoked_at }.from(nil).to(
|
|
be_within_one_second_of Time.current
|
|
).and not_change { newer_key.reload.revoked_at }.and not_change {
|
|
revoked_key.reload.revoked_at
|
|
}
|
|
end
|
|
|
|
describe "API Key scope mappings" do
|
|
it "maps api_key permissions" do
|
|
api_key_mappings = ApiKeyScope.scope_mappings[:topics]
|
|
|
|
assert_responds_to(api_key_mappings.dig(:write, :actions))
|
|
assert_responds_to(api_key_mappings.dig(:read, :actions))
|
|
assert_responds_to(api_key_mappings.dig(:read_lists, :actions))
|
|
end
|
|
|
|
def assert_responds_to(mappings)
|
|
mappings.each do |m|
|
|
controller, method = m.split("#")
|
|
controller_name = "#{controller.capitalize}Controller"
|
|
expect(controller_name.constantize.method_defined?(method)).to eq(true)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#request_allowed?" do
|
|
let(:request) do
|
|
ActionDispatch::TestRequest.create.tap do |request|
|
|
request.path_parameters = { controller: "topics", action: "show", topic_id: "3" }
|
|
request.remote_addr = "133.45.67.99"
|
|
end
|
|
end
|
|
|
|
let(:env) { request.env }
|
|
|
|
let(:key) { ApiKey.new(api_key_scopes: [scope]) }
|
|
|
|
context "with regular scopes" do
|
|
let(:scope) do
|
|
ApiKeyScope.new(resource: "topics", action: "read", allowed_parameters: { topic_id: "3" })
|
|
end
|
|
|
|
it "allows the request if there are no allowed IPs" do
|
|
key.allowed_ips = nil
|
|
key.api_key_scopes = []
|
|
expect(key.request_allowed?(env)).to eq(true)
|
|
end
|
|
|
|
it "rejects the request if the IP is not allowed" do
|
|
key.allowed_ips = %w[115.65.76.87]
|
|
expect(key.request_allowed?(env)).to eq(false)
|
|
end
|
|
|
|
it "allow the request if there are not allowed params" do
|
|
scope.allowed_parameters = nil
|
|
expect(key.request_allowed?(env)).to eq(true)
|
|
end
|
|
|
|
it "rejects the request when params are different" do
|
|
request.path_parameters = { controller: "topics", action: "show", topic_id: "4" }
|
|
expect(key.request_allowed?(env)).to eq(false)
|
|
end
|
|
|
|
it "accepts the request if one of the parameters match" do
|
|
request.path_parameters = { controller: "topics", action: "show", topic_id: "4" }
|
|
scope.allowed_parameters = { topic_id: %w[3 4] }
|
|
expect(key.request_allowed?(env)).to eq(true)
|
|
end
|
|
|
|
it "allow the request when the scope has an alias" do
|
|
request.path_parameters = { controller: "topics", action: "show", id: "3" }
|
|
expect(key.request_allowed?(env)).to eq(true)
|
|
end
|
|
|
|
it "rejects the request when the main parameter and the alias are both used" do
|
|
request.path_parameters = { controller: "topics", action: "show", topic_id: "3", id: "3" }
|
|
expect(key.request_allowed?(env)).to eq(false)
|
|
end
|
|
end
|
|
|
|
context "with users:create scope" do
|
|
let(:scope) { ApiKeyScope.new(resource: "users", action: "create") }
|
|
|
|
let(:request) do
|
|
ActionDispatch::TestRequest.create.tap do |request|
|
|
request.path_parameters = { controller: "users", action: "create" }
|
|
request.request_method = "POST"
|
|
end
|
|
end
|
|
|
|
it "allows user creation requests" do
|
|
expect(key.request_allowed?(env)).to eq(true)
|
|
end
|
|
|
|
it "rejects non-user creation requests" do
|
|
request.path_parameters = { controller: "topics", action: "create" }
|
|
expect(key.request_allowed?(env)).to eq(false)
|
|
end
|
|
end
|
|
|
|
context "with global:read scope" do
|
|
let(:scope) { ApiKeyScope.new(resource: "global", action: "read") }
|
|
|
|
it "allows only GET requests for global:read" do
|
|
request.request_method = "GET"
|
|
expect(key.request_allowed?(env)).to eq(true)
|
|
|
|
request.request_method = "POST"
|
|
expect(key.request_allowed?(env)).to eq(false)
|
|
end
|
|
end
|
|
end
|
|
end
|