Merge pull request #5908 from opf/housekeeping/session-authentication-header

Restrict session API authentication to X-Requested-With
pull/5925/head
Markus Kahl 7 years ago committed by GitHub
commit 34eef79ef5
  1. 4
      lib/api/root.rb
  2. 6
      lib/open_project/authentication/strategies/warden/session.rb
  3. 14
      spec/features/api/authentication_spec.rb
  4. 1
      spec/features/security/session_ttl_spec.rb
  5. 6
      spec/rails_helper.rb
  6. 12
      spec/requests/api/v3/activities_by_work_package_resource_spec.rb
  7. 65
      spec/requests/api/v3/authentication_spec.rb
  8. 8
      spec/requests/api/v3/queries/create_form_api_spec.rb
  9. 22
      spec/requests/api/v3/queries/create_query_spec.rb
  10. 9
      spec/requests/api/v3/queries/update_form_api_spec.rb
  11. 24
      spec/requests/api/v3/queries/update_query_spec.rb
  12. 17
      spec/requests/api/v3/rack_deflater_spec.rb
  13. 53
      spec/requests/api/v3/relations/relations_api_spec.rb
  14. 5
      spec/requests/api/v3/relations/relations_index_spec.rb
  15. 7
      spec/requests/api/v3/root_resource_spec.rb
  16. 8
      spec/requests/api/v3/support/api_helper.rb
  17. 4
      spec/requests/api/v3/support/api_v3_collection_response.rb
  18. 35
      spec/requests/api/v3/user/create_user_resource_spec.rb
  19. 4
      spec/requests/api/v3/user/filters_spec.rb
  20. 21
      spec/requests/api/v3/user/update_user_resource_spec.rb
  21. 4
      spec/requests/api/v3/work_packages/available_projects_on_create_api_spec.rb
  22. 6
      spec/requests/api/v3/work_packages/available_projects_on_edit_api_spec.rb
  23. 2
      spec/requests/api/v3/work_packages/available_relation_candidates_spec.rb
  24. 6
      spec/requests/api/v3/work_packages/form/work_package_form_resource_spec.rb
  25. 6
      spec/requests/attachments_routes_spec.rb
  26. 13
      spec/requests/auth/api_v2_spec.rb
  27. 2
      spec/requests/current_user_spec.rb
  28. 10
      spec/requests/old_issue_2_wp_spec.rb
  29. 10
      spec/requests/short_uri_wp_spec.rb
  30. 12
      spec/support/request_with_session_header.rb
  31. 16
      spec/support/rspec_request_specs.rb

@ -122,10 +122,6 @@ module API
false
end
def running_in_test_env?
Rails.env.test? && ENV['CAPYBARA_DISABLE_TEST_AUTH_PROTECTION'] != 'true'
end
# checks whether the user has
# any of the provided permission in any of the provided
# projects

@ -12,7 +12,7 @@ module OpenProject
include ::OpenProject::Authentication::SessionExpiry
def valid?
session && !session_ttl_expired?
session && !session_ttl_expired? && xml_request_header_set?
end
def authenticate!
@ -21,6 +21,10 @@ module OpenProject
success! user
end
def xml_request_header_set?
request.env['HTTP_X_REQUESTED_WITH'.freeze] == 'XMLHttpRequest'.freeze
end
def user_id
Hash(session)['user_id']
end

@ -58,7 +58,6 @@ require 'spec_helper'
describe 'Login', type: :feature do
after do
User.current = nil
enable_test_auth_protection
end
let(:user_password) { 'bob1!' * 4 }
@ -90,20 +89,9 @@ describe 'Login', type: :feature do
# which would cause User.current to be set
User.current = other_user
# disable a hack in the API's authenticate method
# which would cause authentication to not work
disable_test_auth_protection
# taking /api/v3 as it does not run any authorization
page.driver.header('X-Requested-With', 'XMLHttpRequest')
visit '/api/v3'
expect(User.current).to eql(user)
end
def disable_test_auth_protection
ENV['CAPYBARA_DISABLE_TEST_AUTH_PROTECTION'] = 'true'
end
def enable_test_auth_protection
ENV.delete 'CAPYBARA_DISABLE_TEST_AUTH_PROTECTION'
end
end

@ -57,6 +57,7 @@ describe 'Session TTL',
describe 'outdated TTL on API request' do
it 'expires on the next APIv3 request' do
page.driver.header('X-Requested-With', 'XMLHttpRequest')
visit "/api/v3/work_packages/#{work_package.id}"
body = JSON.parse(page.body)

@ -74,15 +74,9 @@ RSpec.configure do |config|
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# include spec/api for API request specs
config.include RSpec::Rails::RequestExampleGroup, type: :request
# Add helpers to parse json-responses
config.include JsonSpec::Helpers
# include spec/api for API request specs
config.include RSpec::Rails::RequestExampleGroup, type: :request
# TODO test if we can remove this
config.include ::Angular::DSL
OpenProject::Configuration['attachments_storage_path'] = 'tmp/files'

@ -52,14 +52,14 @@ describe API::V3::Activities::ActivitiesByWorkPackageAPI, type: :request do
end
it 'succeeds' do
expect(response.status).to eql 200
expect(last_response.status).to eql 200
end
context 'not allowed to see work package' do
let(:current_user) { FactoryGirl.create(:user) }
it 'fails with HTTP Not Found' do
expect(response.status).to eql 404
expect(last_response.status).to eql 404
end
end
end
@ -69,9 +69,9 @@ describe API::V3::Activities::ActivitiesByWorkPackageAPI, type: :request do
shared_context 'create activity' do
before do
header "Content-Type", "application/json"
post api_v3_paths.work_package_activities(work_package.id),
params: { comment: { raw: comment } }.to_json,
headers: { 'CONTENT_TYPE' => 'application/json' }
{ comment: { raw: comment } }.to_json
end
end
@ -96,11 +96,11 @@ describe API::V3::Activities::ActivitiesByWorkPackageAPI, type: :request do
include_context 'create activity'
it 'responds with error' do
expect(response.status).to eql 422
expect(last_response.status).to eql 422
end
it 'notes the error' do
expect(response.body)
expect(last_response.body)
.to be_json_eql("Subject can't be blank.".to_json)
.at_path('message')
end

@ -45,20 +45,21 @@ describe API::V3, type: :request do
strategies = OpenProject::Authentication::Strategies::Warden
def basic_auth(user, password)
def set_basic_auth_header(user, password)
credentials = ActionController::HttpAuthentication::Basic.encode_credentials user, password
{ 'Authorization' => credentials }
header 'Authorization', credentials
end
shared_examples 'it is basic auth protected' do
context 'when not allowed', with_config: { apiv3_enable_basic_auth: false } do
context 'with valid credentials' do
before do
get resource, params: {}, headers: basic_auth(username, password)
set_basic_auth_header(username, password)
get resource
end
it 'should return 401 unauthorized' do
expect(response.status).to eq 401
expect(last_response.status).to eq 401
end
end
end
@ -69,15 +70,15 @@ describe API::V3, type: :request do
end
it 'should return 401 unauthorized' do
expect(response.status).to eq 401
expect(last_response.status).to eq 401
end
it 'should return the correct JSON response' do
expect(JSON.parse(response.body)).to eq response_401
expect(JSON.parse(last_response.body)).to eq response_401
end
it 'should return the WWW-Authenticate header' do
expect(response.header['WWW-Authenticate'])
expect(last_response.header['WWW-Authenticate'])
.to include 'Basic realm="OpenProject API"'
end
end
@ -86,64 +87,63 @@ describe API::V3, type: :request do
let(:expected_message) { 'You did not provide the correct credentials.' }
before do
get resource, params: {}, headers: basic_auth(username, password.reverse)
set_basic_auth_header(username, password.reverse)
get resource
end
it 'should return 401 unauthorized' do
expect(response.status).to eq 401
expect(last_response.status).to eq 401
end
it 'should return the correct JSON response' do
expect(JSON.parse(response.body)).to eq response_401
expect(JSON.parse(last_response.body)).to eq response_401
end
it 'should return the correct content type header' do
expect(response.headers['Content-Type']).to eq 'application/hal+json; charset=utf-8'
expect(last_response.headers['Content-Type']).to eq 'application/hal+json; charset=utf-8'
end
it 'should return the WWW-Authenticate header' do
expect(response.header['WWW-Authenticate'])
expect(last_response.header['WWW-Authenticate'])
.to include 'Basic realm="OpenProject API"'
end
end
context 'with invalid credentials an X-Authentication-Scheme "Session"' do
let(:expected_message) { 'You did not provide the correct credentials.' }
let(:headers) do
auth = basic_auth(username, password.reverse)
auth.merge('X-Authentication-Scheme' => 'Session')
end
before do
get resource, params: {}, headers: headers
set_basic_auth_header(username, password.reverse)
header 'X-Authentication-Scheme', 'Session'
get resource
end
it 'should return 401 unauthorized' do
expect(response.status).to eq 401
expect(last_response.status).to eq 401
end
it 'should return the correct JSON response' do
expect(JSON.parse(response.body)).to eq response_401
expect(JSON.parse(last_response.body)).to eq response_401
end
it 'should return the correct content type header' do
expect(response.headers['Content-Type']).to eq 'application/hal+json; charset=utf-8'
expect(last_response.headers['Content-Type']).to eq 'application/hal+json; charset=utf-8'
end
it 'should return the WWW-Authenticate header' do
expect(response.header['WWW-Authenticate'])
expect(last_response.header['WWW-Authenticate'])
.to include 'Session realm="OpenProject API"'
end
end
context 'with valid credentials' do
before do
get resource, params: {}, headers: basic_auth(username, password)
set_basic_auth_header(username, password)
get resource
end
it 'should return 200 OK' do
expect(response.status).to eq 200
expect(last_response.status).to eq 200
end
end
end
@ -212,7 +212,7 @@ describe API::V3, type: :request do
end
it 'should return 200 OK' do
expect(response.status).to eq 200
expect(last_response.status).to eq 200
end
it 'should "login" the anonymous user' do
@ -222,21 +222,23 @@ describe API::V3, type: :request do
context 'with invalid credentials' do
before do
get resource, params: {}, headers: basic_auth(username, password)
set_basic_auth_header(username, password)
get resource
end
it 'should return 401 unauthorized' do
expect(response.status).to eq 401
expect(last_response.status).to eq 401
end
end
context 'with valid global credentials' do
before do
get resource, params: {}, headers: basic_auth('global_account', 'global_password')
set_basic_auth_header('global_account', 'global_password')
get resource
end
it 'should return 200 OK' do
expect(response.status).to eq 200
expect(last_response.status).to eq 200
end
it 'should login an admin system user' do
@ -247,11 +249,12 @@ describe API::V3, type: :request do
context 'with valid user credentials' do
before do
get resource, params: {}, headers: basic_auth('apikey', api_key.value)
set_basic_auth_header('apikey', api_key.value)
get resource
end
it 'should return 200 OK' do
expect(response.status).to eq 200
expect(last_response.status).to eq 200
end
it 'should login user' do

@ -39,7 +39,7 @@ describe "POST /api/v3/queries/form", type: :request do
let(:parameters) { {} }
let(:override_params) { {} }
let(:form) { JSON.parse response.body }
let(:form) { JSON.parse last_response.body }
let(:static_columns_json) do
%w(id project assignee author
@ -97,13 +97,13 @@ describe "POST /api/v3/queries/form", type: :request do
additional_setup
header "Content-Type", "application/json"
post path,
params: parameters.merge(override_params).to_json,
headers: { 'CONTENT_TYPE' => 'application/json' }
parameters.merge(override_params).to_json
end
it 'should return 200(OK)' do
expect(response.status).to eq(200)
expect(last_response.status).to eq(200)
end
it 'should be of type form' do

@ -97,17 +97,16 @@ describe "POST /api/v3/queries", type: :request do
describe "creating a query" do
before do
post "/api/v3/queries",
params: params.to_json,
headers: { "Content-Type": "application/json" }
header "Content-Type", "application/json"
post "/api/v3/queries", params.to_json
end
it 'should return 201 (created)' do
expect(response.status).to eq(201)
expect(last_response.status).to eq(201)
end
it 'should render the created query' do
json = JSON.parse(response.body)
json = JSON.parse(last_response.body)
expect(json["_type"]).to eq "Query"
expect(json["name"]).to eq "Dummy Query"
@ -133,13 +132,12 @@ describe "POST /api/v3/queries", type: :request do
context "with invalid parameters" do
def post!
post "/api/v3/queries",
params: params.to_json,
headers: { "Content-Type": "application/json" }
header "Content-Type", "application/json"
post "/api/v3/queries", params.to_json
end
def json
JSON.parse response.body
JSON.parse last_response.body
end
it "yields a 422 error given an unknown project" do
@ -147,7 +145,7 @@ describe "POST /api/v3/queries", type: :request do
post!
expect(response.status).to eq 422
expect(last_response.status).to eq 422
expect(json["message"]).to eq "Project not found"
end
@ -156,7 +154,7 @@ describe "POST /api/v3/queries", type: :request do
post!
expect(response.status).to eq 422
expect(last_response.status).to eq 422
expect(json["message"]).to eq "Status Operator is not included in the list"
end
@ -165,7 +163,7 @@ describe "POST /api/v3/queries", type: :request do
post!
expect(response.status).to eq 422
expect(last_response.status).to eq 422
expect(json["message"]).to eq "Statuz does not exist."
end
end

@ -53,7 +53,7 @@ describe "POST /api/v3/queries/form", type: :request do
let(:parameters) { {} }
let(:override_params) { {} }
let(:form) { JSON.parse response.body }
let(:form) { JSON.parse last_response.body }
before do
project.add_member! user, role
@ -62,13 +62,12 @@ describe "POST /api/v3/queries/form", type: :request do
additional_setup
post path,
params: parameters.merge(override_params).to_json,
headers: { 'CONTENT_TYPE' => 'application/json' }
header 'CONTENT_TYPE', 'application/json'
post path, parameters.merge(override_params).to_json
end
it 'should return 200(OK)' do
expect(response.status).to eq(200)
expect(last_response.status).to eq(200)
end
it 'should be of type form' do

@ -34,7 +34,7 @@ describe "PATCH /api/v3/queries/:id", type: :request do
let(:project) { FactoryGirl.create :project }
def json
JSON.parse response.body
JSON.parse last_response.body
end
let!(:query) do
@ -113,17 +113,16 @@ describe "PATCH /api/v3/queries/:id", type: :request do
describe "updating a query" do
before do
patch "/api/v3/queries/#{query.id}",
params: params.to_json,
headers: { "Content-Type": "application/json" }
header "Content-Type", "application/json"
patch "/api/v3/queries/#{query.id}", params.to_json
end
it 'should return 200 (ok)' do
expect(response.status).to eq(200)
expect(last_response.status).to eq(200)
end
it 'should render the updated query' do
json = JSON.parse(response.body)
json = JSON.parse(last_response.body)
expect(json["_type"]).to eq "Query"
expect(json["name"]).to eq "Dummy Query"
@ -151,7 +150,7 @@ describe "PATCH /api/v3/queries/:id", type: :request do
let(:params) { {} }
it "should not change anything" do
json = JSON.parse(response.body)
json = JSON.parse(last_response.body)
expect(json["_type"]).to eq "Query"
expect(json["name"]).to eq "A Query"
@ -161,9 +160,8 @@ describe "PATCH /api/v3/queries/:id", type: :request do
context "with invalid parameters" do
def post!
patch "/api/v3/queries/#{query.id}",
params: params.to_json,
headers: { "Content-Type": "application/json" }
header "Content-Type", "application/json"
patch "/api/v3/queries/#{query.id}", params.to_json
end
it "yields a 422 error given an unknown project" do
@ -171,7 +169,7 @@ describe "PATCH /api/v3/queries/:id", type: :request do
post!
expect(response.status).to eq 422
expect(last_response.status).to eq 422
expect(json["message"]).to eq "Project not found"
end
@ -180,7 +178,7 @@ describe "PATCH /api/v3/queries/:id", type: :request do
post!
expect(response.status).to eq 422
expect(last_response.status).to eq 422
expect(json["message"]).to eq "Status Operator is not included in the list"
end
@ -189,7 +187,7 @@ describe "PATCH /api/v3/queries/:id", type: :request do
post!
expect(response.status).to eq 422
expect(last_response.status).to eq 422
expect(json["message"]).to eq "Statuz does not exist."
end
end

@ -38,17 +38,16 @@ describe Rack::Deflater, type: :request do
# It could be any endpoint really.
get api_v3_paths.configuration
expect(response.headers['Content-Encoding']).to be_nil
expect(last_response.headers['Content-Encoding']).to be_nil
etag = response.headers['Etag']
content_length = response.headers['Content-Length'].to_i
etag = last_response.headers['Etag']
content_length = last_response.headers['Content-Length'].to_i
get api_v3_paths.configuration,
params: {},
headers: { 'Accept-Encoding' => 'gzip' }
header "Accept-Encoding", "gzip"
get api_v3_paths.configuration
expect(response.headers['Etag']).to eql etag
expect(response.headers['Content-Length'].to_i).to_not eql content_length
expect(response.headers['Content-Encoding']).to eql 'gzip'
expect(last_response.headers['Etag']).to eql etag
expect(last_response.headers['Content-Length'].to_i).to_not eql content_length
expect(last_response.headers['Content-Encoding']).to eql 'gzip'
end
end

@ -72,13 +72,12 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
before do
expect(Relation.count).to eq 0
post "/api/v3/work_packages/#{from.id}/relations",
params: params.to_json,
headers: { "Content-Type": "application/json" }
header "Content-Type", "application/json"
post "/api/v3/work_packages/#{from.id}/relations", params.to_json
end
it 'should return 201 (created)' do
expect(response.status).to eq(201)
expect(last_response.status).to eq(201)
end
it 'should have created a new relation' do
@ -86,7 +85,7 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
end
it 'should have created the relation correctly' do
rel = described_class.new(Relation.new, current_user: user).from_json response.body
rel = described_class.new(Relation.new, current_user: user).from_json last_response.body
expect(rel.from).to eq from
expect(rel.to).to eq to
@ -110,13 +109,12 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
before do
relation
patch "/api/v3/relations/#{relation.id}",
params: update.to_json,
headers: { "Content-Type": "application/json" }
header "Content-Type", "application/json"
patch "/api/v3/relations/#{relation.id}", update.to_json
end
it "should return 200 (ok)" do
expect(response.status).to eq 200
expect(last_response.status).to eq 200
end
it "updates the relation's description" do
@ -128,7 +126,7 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
end
it "should return the updated relation" do
rel = described_class.new(Relation.new, current_user: user).from_json response.body
rel = described_class.new(Relation.new, current_user: user).from_json last_response.body
expect(rel).to eq relation.reload
end
@ -141,11 +139,11 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
end
it "should return 422" do
expect(response.status).to eq 422
expect(last_response.status).to eq 422
end
it "should indicate an error with the type attribute" do
attr = JSON.parse(response.body).dig "_embedded", "details", "attribute"
attr = JSON.parse(last_response.body).dig "_embedded", "details", "attribute"
expect(attr).to eq "type"
end
@ -165,17 +163,17 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
end
it "should return 422" do
expect(response.status).to eq 422
expect(last_response.status).to eq 422
end
it "should indicate an error with the `from` attribute" do
attr = JSON.parse(response.body).dig "_embedded", "details", "attribute"
attr = JSON.parse(last_response.body).dig "_embedded", "details", "attribute"
expect(attr).to eq "from"
end
it "should let the user know the attribute is read-only" do
msg = JSON.parse(response.body)["message"]
msg = JSON.parse(last_response.body)["message"]
expect(msg).to include 'read-only'
end
@ -199,14 +197,13 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
before do
project.add_member! user, role
post "/api/v3/work_packages/#{from.id}/relations",
params: params.to_json,
headers: { "Content-Type": "application/json" }
header "Content-Type", "application/json"
post "/api/v3/work_packages/#{from.id}/relations", params.to_json
end
context "with the required permissions" do
it "works" do
expect(response.status).to eq 201
expect(last_response.status).to eq 201
end
end
@ -214,7 +211,7 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
let(:permissions) { [:view_work_packages] }
it "is forbidden" do
expect(response.status).to eq 403
expect(last_response.status).to eq 403
end
end
@ -226,17 +223,17 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
let!(:to) { FactoryGirl.create :work_package }
it "should return 422" do
expect(response.status).to eq 422
expect(last_response.status).to eq 422
end
it "should indicate an error with the `to` attribute" do
attr = JSON.parse(response.body).dig "_embedded", "details", "attribute"
attr = JSON.parse(last_response.body).dig "_embedded", "details", "attribute"
expect(attr).to eq "to"
end
it "should have a localized error message" do
message = JSON.parse(response.body)["message"]
message = JSON.parse(last_response.body)["message"]
expect(message).not_to include "translation missing"
end
@ -251,7 +248,7 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
end
it "should return 204 and destroy the relation" do
expect(response.status).to eq 204
expect(last_response.status).to eq 204
expect(Relation.exists?(relation.id)).to eq false
end
end
@ -305,19 +302,19 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
end
it 'returns 200' do
expect(response.status).to eql 200
expect(last_response.status).to eql 200
end
it 'returns the visible relation (and only the visible one) satisfying the filter' do
expect(response.body)
expect(last_response.body)
.to be_json_eql('1')
.at_path('total')
expect(response.body)
expect(last_response.body)
.to be_json_eql('1')
.at_path('count')
expect(response.body)
expect(last_response.body)
.to be_json_eql(relation.id.to_json)
.at_path('_embedded/elements/0/id')
end

@ -69,9 +69,10 @@ describe 'GET /api/v3/relations', type: :request do
filters: [filter].to_json
}
get "/api/v3/relations", params: params
header "Content-Type", "application/json"
get "/api/v3/relations", params
json = JSON.parse response.body
json = JSON.parse last_response.body
Array(Hash(json).dig("_embedded", "elements")).map { |e| e["id"] }
end

@ -72,6 +72,13 @@ describe 'API v3 Root resource' do
it 'should respond with a root representer' do
expect(subject).to have_json_path('instanceName')
end
context 'without the X-requested-with header', skip_xhr_header: true do
it 'returns unauthorized regardless of the session validity' do
expect(response.status).to eq(401)
expect(response.body).to eq('unauthorized')
end
end
end
end
end

@ -27,7 +27,7 @@
#++
shared_examples_for 'safeguarded API' do
it { expect(response.response_code).to eq(404) }
it { expect(last_response.status).to eq(404) }
end
shared_examples_for 'valid activity request' do
@ -38,10 +38,10 @@ shared_examples_for 'valid activity request' do
allow(User).to receive(:current).and_return(admin)
end
it { expect(response.response_code).to eq(status_code) }
it { expect(last_response.status).to eq(status_code) }
describe 'response body' do
subject { response.body }
subject { last_response.body }
it { is_expected.to be_json_eql('Activity::Comment'.to_json).at_path('_type') }
@ -55,5 +55,5 @@ shared_examples_for 'invalid activity request' do
allow(User).to receive(:current).and_return(admin)
end
it { expect(response.response_code).to eq(422) }
it { expect(last_response.status).to eq(422) }
end

@ -29,9 +29,9 @@
require 'spec_helper'
shared_examples_for 'API V3 collection response' do |total, count, type|
subject { response.body }
subject { last_response.body }
it { expect(response.status).to eql(200) }
it { expect(last_response.status).to eql(200) }
it { is_expected.to be_json_eql('Collection'.to_json).at_path('_type') }

@ -41,18 +41,19 @@ describe ::API::V3::Users::UsersAPI, type: :request do
end
def send_request
post path, params: parameters.to_json, headers: { 'Content-Type' => 'application/json' }
header "Content-Type", "application/json"
post path, parameters.to_json
end
let(:errors) { parse_json(response.body)['_embedded']['errors'] }
let(:errors) { parse_json(last_response.body)['_embedded']['errors'] }
shared_context 'represents the created user' do |expected_attributes|
it 'returns the represented user' do
send_request
expect(response.status).to eq(201)
expect(response.body).to have_json_type(Object).at_path('_links')
expect(response.body)
expect(last_response.status).to eq(201)
expect(last_response.body).to have_json_type(Object).at_path('_links')
expect(last_response.body)
.to be_json_eql('User'.to_json)
.at_path('_type')
@ -69,13 +70,13 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'returns an erroneous response' do
send_request
expect(response.status).to eq(422)
expect(last_response.status).to eq(422)
expect(errors.count).to eq(5)
expect(errors.collect { |el| el['_embedded']['details']['attribute'] })
.to contain_exactly('password', 'login', 'firstname', 'lastname', 'email')
expect(response.body)
expect(last_response.body)
.to be_json_eql('urn:openproject-org:api:v3:errors:MultipleErrors'.to_json)
.at_path('errorIdentifier')
end
@ -150,9 +151,9 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'returns an error for the authSource attribute' do
send_request
attr = JSON.parse(response.body).dig "_embedded", "details", "attribute"
attr = JSON.parse(last_response.body).dig "_embedded", "details", "attribute"
expect(response.status).to eq 422
expect(last_response.status).to eq 422
expect(attr).to eq "authSource"
end
end
@ -186,9 +187,9 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'returns the represented user' do
send_request
expect(response.body).not_to have_json_path("_embedded/errors")
expect(response.body).to have_json_type(Object).at_path('_links')
expect(response.body)
expect(last_response.body).not_to have_json_path("_embedded/errors")
expect(last_response.body).to have_json_type(Object).at_path('_links')
expect(last_response.body)
.to be_json_eql('User'.to_json)
.at_path('_type')
end
@ -255,10 +256,10 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'marks the mail as missing' do
send_request
expect(response.body)
expect(last_response.body)
.to be_json_eql('urn:openproject-org:api:v3:errors:PropertyConstraintViolation'.to_json)
.at_path('errorIdentifier')
expect(response.body)
expect(last_response.body)
.to be_json_eql('email'.to_json)
.at_path('_embedded/details/attribute')
end
@ -271,10 +272,10 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'returns an erroneous response' do
send_request
expect(response.status).to eq(422)
expect(last_response.status).to eq(422)
expect(errors).not_to be_empty
expect(response.body)
expect(last_response.body)
.to be_json_eql('urn:openproject-org:api:v3:errors:MultipleErrors'.to_json)
.at_path('errorIdentifier')
@ -289,7 +290,7 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'returns an erroneous response' do
send_request
expect(response.status).to eq(403)
expect(last_response.status).to eq(403)
end
end
end

@ -54,9 +54,9 @@ describe 'GET /api/v3/users', type: :request do
filters: [filter].to_json
}
get "/api/v3/users", params: params
get "/api/v3/users", params
json = JSON.parse response.body
json = JSON.parse last_response.body
Array(Hash(json).dig("_embedded", "elements")).map { |e| e["login"] }
end

@ -43,16 +43,17 @@ describe ::API::V3::Users::UsersAPI, type: :request do
end
def send_request
patch path, params: parameters.to_json, headers: { 'Content-Type' => 'application/json' }
header "Content-Type", "application/json"
patch path, parameters.to_json
end
shared_context 'successful update' do |expected_attributes|
it 'responds with the represented updated user' do
send_request
expect(response.status).to eq(200)
expect(response.body).to have_json_type(Object).at_path('_links')
expect(response.body)
expect(last_response.status).to eq(200)
expect(last_response.body).to have_json_type(Object).at_path('_links')
expect(last_response.body)
.to be_json_eql('User'.to_json)
.at_path('_type')
@ -78,7 +79,7 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'updates the users password correctly' do
send_request
expect(response.status).to eq(200)
expect(last_response.status).to eq(200)
updated_user = User.find(user.id)
matches = updated_user.check_password?(password)
@ -96,13 +97,13 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'returns an erroneous response' do
send_request
expect(response.status).to eq(422)
expect(last_response.status).to eq(422)
expect(response.body)
expect(last_response.body)
.to be_json_eql('email'.to_json)
.at_path('_embedded/details/attribute')
expect(response.body)
expect(last_response.body)
.to be_json_eql('urn:openproject-org:api:v3:errors:PropertyConstraintViolation'.to_json)
.at_path('errorIdentifier')
end
@ -114,7 +115,7 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'responds with 404' do
send_request
expect(response.status).to eql(404)
expect(last_response.status).to eql(404)
end
end
@ -124,7 +125,7 @@ describe ::API::V3::Users::UsersAPI, type: :request do
it 'returns an erroneous response' do
send_request
expect(response.status).to eq(403)
expect(last_response.status).to eq(403)
end
end
end

@ -53,7 +53,7 @@ describe API::V3::WorkPackages::AvailableProjectsOnCreateAPI, type: :request do
it_behaves_like 'API V3 collection response', 1, 1, 'Project'
it 'has the project for which the add_work_packages permission exists' do
expect(response.body).to be_json_eql(project.id).at_path('_embedded/elements/0/id')
expect(last_response.body).to be_json_eql(project.id).at_path('_embedded/elements/0/id')
end
end
@ -62,6 +62,6 @@ describe API::V3::WorkPackages::AvailableProjectsOnCreateAPI, type: :request do
FactoryGirl.create(:role, permissions: [])
end
it { expect(response.status).to eq(403) }
it { expect(last_response.status).to eq(403) }
end
end

@ -64,7 +64,7 @@ describe 'API::V3::WorkPackages::AvailableProjectsOnEditAPI', type: :request do
it_behaves_like 'API V3 collection response', 1, 1, 'Project'
it 'has the project for which the move_work_packages permission exists' do
expect(response.body).to be_json_eql(target_project.id).at_path('_embedded/elements/0/id')
expect(last_response.body).to be_json_eql(target_project.id).at_path('_embedded/elements/0/id')
end
end
@ -73,7 +73,7 @@ describe 'API::V3::WorkPackages::AvailableProjectsOnEditAPI', type: :request do
FactoryGirl.create(:role, permissions: [:view_work_packages])
end
it { expect(response.status).to eq(403) }
it { expect(last_response.status).to eq(403) }
end
context 'w/o the view_work_packages permission' do
@ -81,6 +81,6 @@ describe 'API::V3::WorkPackages::AvailableProjectsOnEditAPI', type: :request do
FactoryGirl.create(:role, permissions: [:edit_work_packages])
end
it { expect(response.status).to eq(404) }
it { expect(last_response.status).to eq(404) }
end
end

@ -61,7 +61,7 @@ describe ::API::V3::Relations::RelationRepresenter, type: :request do
let(:request) { get href }
let(:result) do
request
JSON.parse response.body
JSON.parse last_response.body
end
let(:subjects) { work_packages.map { |e| e["subject"] } }

@ -91,9 +91,9 @@ describe 'API v3 Work package form resource', type: :request do
context 'existing work package' do
shared_examples_for 'valid payload' do
subject { response.body }
subject { last_response.body }
it { expect(response.status).to eq(200) }
it { expect(last_response.status).to eq(200) }
it { is_expected.to have_json_path('_embedded/payload') }
@ -207,7 +207,7 @@ describe 'API v3 Work package form resource', type: :request do
include_context 'post request'
it { expect(response.status).to eq(409) }
it { expect(last_response.status).to eq(409) }
it_behaves_like 'update conflict'
end

@ -36,13 +36,15 @@ describe 'attachments routes', type: :request do
it 'redirects /attachments/download with filename to attachments#download' do
get '/attachments/download/42/foo.png'
expect(response).to redirect_to('/attachments/42/foo.png')
expect(last_response).to be_redirect
expect(last_response.location).to end_with '/attachments/42/foo.png'
end
it 'redirects /attachments/download without filename to attachments#download' do
get '/attachments/download/42'
expect(response).to redirect_to('/attachments/42')
expect(last_response).to be_redirect
expect(last_response.location).to end_with '/attachments/42'
end
end
end

@ -55,13 +55,13 @@ describe 'API v2', type: :request do
context 'invalid' do
before { get "#{request_url}?key=invalid_key" }
it { expect(response.status).to eq(401) }
it { expect(last_response.status).to eq(401) }
end
context 'valid' do
before { get "#{request_url}?key=#{api_key}" }
it { expect(response.status).to eq(200) }
it { expect(last_response.status).to eq(200) }
end
end
@ -73,25 +73,26 @@ describe 'API v2', type: :request do
before do
allow(OpenProject::Configuration).to receive(:apiv2_enable_basic_auth?).and_return(enabled)
get request_url, headers: { 'Authorization' => credentials }
header 'Authorization', credentials
get request_url
end
context 'when enabled' do
let(:enabled) { true }
context 'valid' do
it { expect(response.status).to eq(200) }
it { expect(last_response.status).to eq(200) }
end
context 'invalid' do
let(:used_password) { 'foobar' }
it { expect(response.status).to eq(401) }
it { expect(last_response.status).to eq(401) }
end
end
context 'when disabled' do
let(:enabled) { false }
it { expect(response.status).to eq(401) }
it { expect(last_response.status).to eq(401) }
end
end

@ -48,7 +48,7 @@ module ResetCurrentUserCallback
end
end
describe ResetCurrentUser, type: :request do
describe ResetCurrentUser, type: :rails_request do
let!(:user) { FactoryGirl.create :user }
before do

@ -39,7 +39,10 @@ describe 'routes for old issue uris', type: :request do
get('/issues')
end
it { expect(response).to redirect_to('/work_packages') }
it do
expect(last_response).to be_redirect
expect(last_response.location).to end_with '/work_packages'
end
end
describe 'with specific id' do
@ -47,6 +50,9 @@ describe 'routes for old issue uris', type: :request do
get('/issues/1234')
end
it { expect(response).to redirect_to('/work_packages/1234') }
it do
expect(last_response).to be_redirect
expect(last_response.location).to end_with '/work_packages/1234'
end
end
end

@ -39,7 +39,10 @@ describe 'routes for old issue uris', type: :request do
get('/wp')
end
it { expect(response).to redirect_to('/work_packages') }
it do
expect(last_response).to be_redirect
expect(last_response.location).to end_with '/work_packages'
end
end
describe 'with specific id' do
@ -47,6 +50,9 @@ describe 'routes for old issue uris', type: :request do
get('/wp/1234')
end
it { expect(response).to redirect_to('/work_packages/1234') }
it do
expect(last_response).to be_redirect
expect(last_response.location).to end_with '/work_packages/1234'
end
end
end

@ -0,0 +1,12 @@
RSpec.configure do |c|
##
# Session-based API authentication requires the X-requested-with header to be present.
c.before(:each, type: :request) do |ex|
unless ex.metadata[:skip_xhr_header]
header('X-Requested-With', 'XMLHttpRequest')
end
end
end

@ -0,0 +1,16 @@
module RackTestHelper
include Rack::Test::Methods
def app
Rails.application
end
end
RSpec.configure do |config|
# Use Rack::Test for regular request specs (esp. API requests)
config.include RackTestHelper, type: :request
# If desired, we can use the Rails IntegrationTest request spec
# (more like a feature spec) with this type.
config.include RSpec::Rails::RequestExampleGroup, type: :rails_request
end
Loading…
Cancel
Save