Initial commit
This commit is contained in:
366
spec/api/v2/auth/auth_spec.rb
Normal file
366
spec/api/v2/auth/auth_spec.rb
Normal file
@@ -0,0 +1,366 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe '/api/v2/auth functionality test' do
|
||||
include_context 'geoip mock'
|
||||
|
||||
let(:uri) { '/api/v2/identity/sessions' }
|
||||
let!(:create_permissions) do
|
||||
create :permission, role: 'admin'
|
||||
create :permission, role: 'member', action: 'ACCEPT', verb: 'all', path: 'not_in_the_rules_path'
|
||||
create :permission, role: 'member', action: 'ACCEPT', verb: 'get', path: '/api/v2/resource/users/me'
|
||||
create :permission, role: 'accountant'
|
||||
end
|
||||
let!(:user) { create(:user) }
|
||||
let(:params) do
|
||||
{
|
||||
email: user.email,
|
||||
password: user.password
|
||||
}
|
||||
end
|
||||
|
||||
let(:do_create_session_request) { post uri, params: params }
|
||||
let(:auth_request) { '/api/v2/auth/not_in_the_rules_path' }
|
||||
let(:protected_request) { '/api/v2/resource/users/me' }
|
||||
|
||||
describe 'testing workability with session' do
|
||||
before do
|
||||
Rails.cache.delete('permissions')
|
||||
end
|
||||
|
||||
context 'with valid session' do
|
||||
before do
|
||||
do_create_session_request
|
||||
end
|
||||
|
||||
it 'returns bearer token on valid session' do
|
||||
get auth_request
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'allows any type of request' do
|
||||
available_types = %w[post get put head delete patch]
|
||||
available_types.each do |ping|
|
||||
method("#{ping}").call auth_request
|
||||
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
|
||||
get protected_request, headers: { 'Authorization' => response.headers['Authorization'] }
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ip related' do
|
||||
let(:create_session_uri) { uri }
|
||||
let(:auth_uri) { '/api/v2/auth/not_in_the_rules_path' }
|
||||
let(:ip_to_be_equal) { '168.238.57.64' }
|
||||
let(:ip_that_differs) { '168.27.3.225' }
|
||||
|
||||
context 'default ip behaviour from HTTP_X_FORWARDED_FOR via action_dispatch.remote_ip' do
|
||||
it 'works if ip from session matches request ip' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'denies access if ip from session doesnt match request ip' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_that_differs }
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'ip behaviour from TRUE_CLIENT_IP header' do
|
||||
before do
|
||||
allow(Barong::App.config).to receive_messages(gateway: 'akamai')
|
||||
end
|
||||
|
||||
it 'works if ip from session matches request ip' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_that_differs, 'HTTP_TRUE_CLIENT_IP' => ip_to_be_equal }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'uses HTTP_X_FORWARDED_FOR if TRUE_CLIENT_IP missing and works' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'uses HTTP_X_FORWARDED_FOR if TRUE_CLIENT_IP empty and works' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => '' }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => '' }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'denies access if ip from session doesnt match request ip in HTTP_TRUE_CLIENT_IP' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_that_differs, 'HTTP_TRUE_CLIENT_IP' => ip_that_differs }
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'uses HTTP_X_FORWARDED_FOR if TRUE_CLIENT_IP empty or missing and denies access' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => '' }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_that_differs }
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'testing session related errors' do
|
||||
it 'renders error if no session or api key headers provided' do
|
||||
get auth_request
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.invalid_session\"]}")
|
||||
end
|
||||
|
||||
it 'renders error if session belongs to non-active user' do
|
||||
do_create_session_request
|
||||
expect(response.status).to eq(200)
|
||||
user.update(state: 'banned')
|
||||
|
||||
get auth_request
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.user_not_active\"]}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'testing restrictions' do
|
||||
before do
|
||||
Rails.cache.delete('permissions')
|
||||
do_create_session_request
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
let(:do_restricted_request) { put '/api/v2/auth/api/v2/peatio/management/ping' }
|
||||
|
||||
it 'receives access error if path is blacklisted' do
|
||||
do_restricted_request
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.permission_denied\"]}")
|
||||
end
|
||||
|
||||
let(:do_whitelisted_request) { put '/api/v2/auth/api/v2/peatio/public/ping' }
|
||||
|
||||
it 'receives access error if path is blacklisted' do
|
||||
do_whitelisted_request
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to be_empty
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'testing workability with api keys' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:test_user) { create(:user, otp: otp_enabled) }
|
||||
let(:otp_enabled) { true }
|
||||
let!(:api_key) { create :api_key, key_holder_account: test_user }
|
||||
let(:otp_code) { '1357' }
|
||||
let(:nonce) { (Time.now.to_f * 1000).to_i }
|
||||
let(:kid) { api_key.kid }
|
||||
let(:secret) { api_key.secret }
|
||||
let(:data) { nonce.to_s + kid }
|
||||
let(:algorithm) { 'SHA' + api_key.algorithm[2..4]}
|
||||
let(:signature) { OpenSSL::HMAC.hexdigest(algorithm, secret, data) }
|
||||
|
||||
before do
|
||||
Rails.cache.delete('permissions')
|
||||
SecretStorage.store_secret(secret, api_key.kid)
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, otp_code) { true }
|
||||
end
|
||||
|
||||
context 'testing api key related errors' do
|
||||
it 'catches api key headers and renders error on missing header' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
}
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.invalid_api_key_headers\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error if nonce is string' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => 'nonce',
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.nonce_not_valid_timestamp\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error if nonce is zero' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => 0,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.nonce_not_valid_timestamp\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error if nonce is older than default 5 seconds in milliseconds' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => ((Time.now - 10.seconds).to_f * 1000).to_i,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.nonce_expired\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error if nonce is from future with milisecond' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => ((Time.now + 10.seconds).to_f * 1000).to_i,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.nonce_expired\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error on blank header' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => ' '
|
||||
}
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.invalid_api_key_headers\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'renders error when signature is invalid' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => 'some-random-signature'
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.invalid_signature\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
let(:ban_api_key) { APIKey.last.update(state: 'banned') }
|
||||
|
||||
it 'renders error when api key state is not active' do
|
||||
ban_api_key
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.apikey_not_active\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
let(:ban_user) { test_user.update(state: 'banned') }
|
||||
|
||||
it 'renders error when api key state is not active' do
|
||||
ban_user
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.apikey_not_active\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
let(:disable_user_2fa) { test_user.update(otp: false) }
|
||||
|
||||
it 'renders error when api key is valid but user have disabled 2fa' do
|
||||
disable_user_2fa
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.apikey_not_active\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'testing api key with valid params' do
|
||||
it 'catches api key headers and works as expected with milisecond nonce' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to be_empty
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
|
||||
get protected_request, headers: { 'Authorization' => response.headers['Authorization'] }
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'testing restrictions' do
|
||||
let(:do_restricted_request) {
|
||||
put '/api/v2/auth/api/v2/peatio/management/ping', headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
}
|
||||
|
||||
it 'receives access error if path is blacklisted' do
|
||||
do_restricted_request
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.permission_denied\"]}")
|
||||
end
|
||||
|
||||
let(:do_whitelisted_request) {
|
||||
put '/api/v2/auth/api/v2/peatio/public/ping', headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
}
|
||||
|
||||
it 'receives access error if path is whitelisted' do
|
||||
do_whitelisted_request
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to be_empty
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user