Initial commit

This commit is contained in:
Yaser
2026-08-13 19:56:46 +03:30
commit de1d57a67b
474 changed files with 43185 additions and 0 deletions

View File

@@ -0,0 +1,307 @@
# frozen_string_literal: true
require 'spec_helper'
describe '/api/v2/auth functionality test' do
include_context 'geoip mock'
before do
end
before do
create :permission, role: 'admin', action: 'AUDIT', verb: 'put', path: 'api/v2/admin/users'
create :permission, role: 'member'
create :permission, role: 'technical', action: 'AUDIT', verb: 'post', path: 'api/v2/admin/wallets'
create :permission, role: 'accountant', action: 'AUDIT', verb: 'delete', path: 'api/v2/admin/markets'
@user = create(:user)
@accountant_user = create(:user, role: 'accountant')
@admin_user = create(:user, role: 'admin')
@technical_user = create(:user, role: 'technical')
Permission.create(role: 'admin', action: 'ACCEPT', verb: 'get', path: 'api/v2/admin/users/list')
Permission.create(role: 'accountant', action: 'ACCEPT', verb: 'post', path: 'api/v2/accountant/documents')
end
let(:uri) { '/api/v2/identity/sessions' }
let(:do_create_session_request_acc) { post uri, params: { email: @accountant_user.email, password: @accountant_user.password }, headers: { 'HTTP_USER_AGENT' => 'random-browser' }}
let(:do_create_session_request_adm) { post uri, params: { email: @admin_user.email, password: @admin_user.password, otp_code: '1357' }, headers: { 'HTTP_USER_AGENT' => 'random-browser' } }
let(:do_create_session_request_tech) { post uri, params: { email: @technical_user.email, password: @technical_user.password }, headers: { 'HTTP_USER_AGENT' => 'random-browser' } }
let(:do_create_session_request) { post uri, params: { email: @user.email, password: @user.password }, headers: { 'HTTP_USER_AGENT' => 'random-browser' } }
let(:auth_request) { '/api/v2/auth/not_in_the_rules_path' }
let(:protected_request) { '/api/v2/resource/users/me' }
describe 'audit permissions testing' do
let!(:turn_off_2fa) do
User.all.each { |u| u.update(otp: false) }
end
let(:do_some_requests) do
delete '/api/v2/auth/api/v2/admin', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
post '/api/v2/auth/api/v2/admin', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
put '/api/v2/auth/api/v2/admin', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
end
context 'records activity if path doesnt match with AUDIT permission path but matches with DROP or BLANK' do
context 'acts as expected with different roles with another permission type match' do
before do
Permission.delete_all
Rails.cache.delete('permissions')
end
it 'for accountant' do
expect(Permission.all.count).to eq(0)
# accountant
do_create_session_request_acc
do_some_requests
expect(Activity.where(category: 'admin', result: 'denied').count).to eq(3)
end
it 'for admin' do
expect(Permission.all.count).to eq(0)
# admin
do_create_session_request_adm
do_some_requests
expect(Activity.where(category: 'admin', result: 'denied').count).to eq(3)
end
end
context 'acts as expected with different roles without any permissions' do
before do
Permission.delete_all
Rails.cache.delete('permissions')
expect(Permission.all.count).to eq(0)
end
it 'for accountant' do
# accountant
do_create_session_request_acc
do_some_requests
expect(response.status).to eq(401)
expect(Activity.where(category: 'admin', result: 'denied').count).to eq(3)
end
it 'for admin' do
# admin
do_create_session_request_adm
do_some_requests
expect(response.status).to eq(401)
expect(Activity.where(category: 'admin', result: 'denied').count).to eq(3)
end
end
end
context 'records failed activity if path matches with AUDIT permission path' do
context 'creates only one thread' do
before do
Permission.delete_all
Permission.create(role: 'technical', action: 'AUDIT', verb: 'post', path: 'api/v2/admin/wallets')
Permission.create(role: 'admin', action: 'AUDIT', verb: 'put', path: 'api/v2/admin/users')
Permission.create(role: 'accountant', action: 'AUDIT', verb: 'delete', path: 'api/v2/admin/markets')
Rails.cache.delete('permissions')
end
end
context 'without topic specified and without user_uid in params' do
let!(:create_audit_permissions) do
Permission.create(role: 'technical', action: 'AUDIT', verb: 'post', path: 'api/v2/admin/wallets')
Permission.create(role: 'admin', action: 'AUDIT', verb: 'put', path: 'api/v2/admin/users')
Permission.create(role: 'accountant', action: 'AUDIT', verb: 'delete', path: 'api/v2/admin/markets')
Rails.cache.delete('permissions')
end
it 'works for general topic' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
get '/api/v2/auth/api/v2/admin', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('general')
expect(Activity.last.result).to eq('denied')
end
it 'works for patch request' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
patch '/api/v2/auth/api/v2/admin', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('general')
expect(Activity.last.action).to eq('update')
expect(Activity.last.result).to eq('denied')
end
it 'works for non - put post patch get delete requests' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
head '/api/v2/auth/api/v2/admin', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('general')
expect(Activity.last.action).to eq('system')
expect(Activity.last.result).to eq('denied')
end
it 'for account role' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
delete '/api/v2/auth/api/v2/admin/markets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('markets')
expect(Activity.last.result).to eq('denied')
end
it 'for admin role' do
do_create_session_request_adm
expect(Activity.where(category: 'admin').count).to eq(0)
put '/api/v2/auth/api/v2/admin/users', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('users')
expect(Activity.last.result).to eq('denied')
end
it 'for technical role' do
do_create_session_request_tech
expect(Activity.where(category: 'admin').count).to eq(0)
post '/api/v2/auth/api/v2/admin/wallets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('wallets')
expect(Activity.last.result).to eq('denied')
end
end
context 'with specified topic and without user_uid' do
let!(:create_audit_permissions) do
Permission.delete_all
Permission.create(role: 'technical', action: 'ACCEPT', verb: 'post', path: 'api/v2/admin/wallets', topic: 'tech_support')
Permission.create(role: 'admin', action: 'ACCEPT', verb: 'put', path: 'api/v2/admin/users', topic: 'administrating')
Permission.create(role: 'accountant', action: 'ACCEPT', verb: 'delete', path: 'api/v2/admin/markets', topic: 'accounting')
Permission.create(role: 'technical', action: 'AUDIT', verb: 'post', path: 'api/v2/admin/wallets', topic: 'tech_support')
Permission.create(role: 'admin', action: 'AUDIT', verb: 'put', path: 'api/v2/admin/users', topic: 'administrating')
Permission.create(role: 'accountant', action: 'AUDIT', verb: 'delete', path: 'api/v2/admin/markets', topic: 'accounting')
Rails.cache.delete('permissions')
end
context 'for different roles' do
it 'accountant' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
delete '/api/v2/auth/api/v2/admin/markets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('accounting')
expect(Activity.last.result).to eq('succeed')
end
it 'technical' do
do_create_session_request_tech
expect(Activity.where(category: 'admin').count).to eq(0)
post '/api/v2/auth/api/v2/admin/wallets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('tech_support')
expect(Activity.last.result).to eq('succeed')
end
it 'admin' do
do_create_session_request_adm
expect(Activity.where(category: 'admin').count).to eq(0)
put '/api/v2/auth/api/v2/admin/users', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('administrating')
expect(Activity.last.result).to eq('succeed')
end
end
end
context 'with specified topic and user_uid' do
let!(:create_audit_permissions) do
Permission.create(role: 'technical', action: 'AUDIT', verb: 'post', path: 'api/v2/admin/wallets', topic: 'tech_support')
Permission.create(role: 'admin', action: 'AUDIT', verb: 'put', path: 'api/v2/admin/users', topic: 'administrating')
Permission.create(role: 'accountant', action: 'AUDIT', verb: 'delete', path: 'api/v2/admin/markets', topic: 'accounting')
Rails.cache.delete('permissions')
end
it 'for different roles' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
delete '/api/v2/auth/api/v2/admin/markets', params: { uid: @user.uid } ,headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('markets')
expect(Activity.last.result).to eq('denied')
end
it 'includes data in denied activity' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
delete '/api/v2/auth/api/v2/admin/markets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }, params: { user_uid: @user.uid }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('markets')
expect(Activity.last.result).to eq('denied')
expect(Activity.last.target_uid).to eq(@user.uid)
expect(JSON.parse(Activity.last.data).keys).to include('user_uid', 'path', 'note')
end
end
end
context 'records succesfull activity if path matches with AUDIT permission path' do
context 'with different params combination' do
let!(:create_audit_permissions) do
Permission.create(role: 'accountant', action: 'ACCEPT', verb: 'delete', path: 'api/v2/admin/markets')
Permission.create(role: 'admin', action: 'ACCEPT', verb: 'put', path: 'api/v2/admin/users')
Permission.create(role: 'technical', action: 'ACCEPT', verb: 'post', path: 'api/v2/admin/wallets')
Permission.create(role: 'technical', action: 'AUDIT', verb: 'post', path: 'api/v2/admin/wallets')
Permission.create(role: 'admin', action: 'AUDIT', verb: 'put', path: 'api/v2/admin/users')
Permission.create(role: 'accountant', action: 'AUDIT', verb: 'delete', path: 'api/v2/admin/markets')
Rails.cache.delete('permissions')
end
context 'for different roles without params' do
it 'accountant' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
delete '/api/v2/auth/api/v2/admin/markets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('markets')
expect(Activity.last.result).to eq('succeed')
end
it 'technical' do
do_create_session_request_tech
expect(Activity.where(category: 'admin').count).to eq(0)
post '/api/v2/auth/api/v2/admin/wallets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('wallets')
expect(Activity.last.result).to eq('succeed')
end
it 'admin' do
do_create_session_request_adm
expect(Activity.where(category: 'admin').count).to eq(0)
put '/api/v2/auth/api/v2/admin/users', headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('users')
expect(Activity.last.result).to eq('succeed')
end
end
context 'for different roles with user_uid in params' do
it 'includes data in succesfull activity' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
delete '/api/v2/auth/api/v2/admin/markets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }, params: { user_uid: @user.uid }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('markets')
expect(Activity.last.result).to eq('succeed')
expect(Activity.last.target_uid).to eq(@user.uid)
expect(JSON.parse(Activity.last.data).keys).to include('user_uid', 'path', 'note')
end
it 'create activity and save target_uid for accountant if user with this uid exists' do
do_create_session_request_acc
expect(Activity.where(category: 'admin').count).to eq(0)
delete '/api/v2/auth/api/v2/admin/markets', headers: { 'HTTP_USER_AGENT' => 'random-browser' }, params: { user_uid: @user.uid }
expect(Activity.where(category: 'admin').count).to eq(1)
expect(Activity.last.topic).to eq('markets')
expect(Activity.last.result).to eq('succeed')
expect(Activity.last.target_uid).to eq(@user.uid)
end
end
end
end
end
end

View 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

View File

@@ -0,0 +1,138 @@
# frozen_string_literal: true
require 'spec_helper'
describe '/api/v2/auth CSRF functionality test' do
include_context 'geoip mock'
before do
allow_any_instance_of(Barong::Authorize).to receive(:validate_csrf!).and_call_original
end
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
context 'without CSRF token' do
before do
Rails.cache.delete('permissions')
do_create_session_request
end
it 'doesnt return bearer token on valid session without CSRF' do
post auth_request
expect(response.status).to eq(401)
expect(response.headers['Authorization']).to be_nil
expect(response.body).to eq("{\"errors\":[\"authz.missing_csrf_token\"]}")
end
it 'works with any type of changing state verb request' do
available_types = %w[post put delete patch]
available_types.each do |ping|
method("#{ping}").call auth_request
expect(response.status).to eq(401)
expect(response.headers['Authorization']).to be_nil
expect(response.body).to eq("{\"errors\":[\"authz.missing_csrf_token\"]}")
get protected_request, headers: { 'Authorization' => response.headers['Authorization'] }
expect(response.status).to eq(401)
end
end
it 'works without CSRF token on any type of safe verb request' do
available_types = %w[get head]
available_types.each do |ping|
method("#{ping}").call auth_request
expect(response.status).to eq(200)
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 'with CSRF token' do
before do
Rails.cache.delete('permissions')
do_create_session_request
@csrf = json_body[:csrf_token]
end
it 'doesnt return bearer token on valid session without CSRF' do
get auth_request, headers: { 'X-CSRF-Token': @csrf }
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
end
it 'doesnt work on any type of request without csrf' do
available_types = %w[post get put delete patch]
available_types.each do |ping|
method("#{ping}").call auth_request, headers: { 'X-CSRF-Token': @csrf }
expect(response.status).to eq(200)
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
end
describe 'testing workability with api keys' do
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 'with valid api keys' do
it 'works without CSRF' 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
end
end

View File

@@ -0,0 +1,144 @@
# frozen_string_literal: true
require 'spec_helper'
describe '/api/v2/auth functionality test' do
include_context 'geoip mock'
let(:do_protected_request) { get '/api/v2/auth/api/v2/resource/users/me' }
describe 'testing rbac workability' do
before(:example) do
Thread.list.last.kill if Thread.list.last.to_s.include?('activity')
end
let!(:create_permissions) do
Permission.create(role: 'superadmin', action: 'ACCEPT', verb: 'get', path: 'api/v2/admin/users/list')
Rails.cache.delete('permissions')
@admin = User.create(email: 'superadmin@admin.io', password: 'Tecohvi0', role: 'superadmin', state: 'active')
end
context 'with cookies' do
let(:do_create_session_request_superadm) { post '/api/v2/identity/sessions', params: { email: 'superadmin@admin.io', password: 'Tecohvi0' }}
context 'not enough permissions' do
it 'denies access for user with missing cookies' do
do_protected_request
expect(response.status).to eq(401)
expect(response.body).to eq("{\"errors\":[\"authz.invalid_session\"]}")
end
it 'denies access for non-accountant user with valid cookies trying to GET accountant api' do
do_create_session_request_superadm
get '/api/v2/auth/api/v2/accountant/documents'
expect(response.status).to eq(401)
expect(response.body).to eq("{\"errors\":[\"authz.invalid_permission\"]}")
expect(response.headers['Authorization']).to be_nil
end
it 'denies POST for endpoint but allowing GET for admin user according to permissions' do
do_create_session_request_superadm
post '/api/v2/auth/api/v2/admin/users/list'
expect(response.status).to eq(401)
expect(response.body).to eq("{\"errors\":[\"authz.invalid_permission\"]}")
expect(response.headers['Authorization']).to be_nil
get '/api/v2/auth/api/v2/admin/users/list'
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 because of the typo in the path' do
do_create_session_request_superadm
get '/api/v2/auth/api/v2/admon/users/list'
expect(response.status).to eq(401)
expect(response.body).to eq("{\"errors\":[\"authz.invalid_permission\"]}")
end
end
context 'enough permissions' do
it 'allowes access with for user with valid cookies, verb, role and path' do
do_create_session_request_superadm
get '/api/v2/auth/api/v2/admin/users/list'
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
expect(response.headers['Authorization']).to include "Bearer"
end
end
end
context 'with api_keys' do
let!(:admin_api_key) { create :api_key, key_holder_account: @admin }
let(:nonce) { (Time.now.to_f * 1000).to_i }
let(:secret) { admin_api_key.secret }
let(:signature) { OpenSSL::HMAC.hexdigest('SHA256', secret, nonce.to_s + admin_api_key.kid ) }
let!(:turn_on_2fa) { @admin.update(otp: true) }
context 'with valid api key headers' do
context 'enough permissions' do
it 'allowes access with for api key owner for valid verb, owner role and path' do
allow(TOTPService).to receive(:validate?)
.with(@admin.uid, '1357') { true }
get '/api/v2/auth/api/v2/admin/users/list', headers: {
'X-Auth-Apikey' => admin_api_key.kid,
'X-Auth-Nonce' => nonce,
'X-Auth-Signature' => signature
}
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
expect(response.headers['Authorization']).to include "Bearer"
end
end
context 'not enough permissions' do
it 'denies access for non-accountant api key owner with valid api_key trying to POST accountant api' do
allow(TOTPService).to receive(:validate?)
.with(@admin.uid, '1357') { true }
allow(SecretStorage).to receive(:get_secret)
.with(admin_api_key.kid) { Vault::Secret.new(data: { value: secret }) }
post '/api/v2/auth/api/v2/accountant/documents', headers: {
'X-Auth-Apikey' => admin_api_key.kid,
'X-Auth-Nonce' => nonce,
'X-Auth-Signature' => signature
}
expect(response.status).to eq(401)
expect(response.body).to eq("{\"errors\":[\"authz.invalid_permission\"]}")
end
it 'denies POST for endpoint but allowing GET for api key owner according to permissions' do
allow(TOTPService).to receive(:validate?)
.with(@admin.uid, '1357') { true }
allow(SecretStorage).to receive(:get_secret)
.with(admin_api_key.kid) { Vault::Secret.new(data: { value: secret }) }
post '/api/v2/auth/api/v2/accountant/documents', headers: {
'X-Auth-Apikey' => admin_api_key.kid,
'X-Auth-Nonce' => nonce,
'X-Auth-Signature' => signature
}
Rails.cache.delete(admin_api_key.kid)
expect(response.status).to eq(401)
expect(response.body).to eq("{\"errors\":[\"authz.invalid_permission\"]}")
get '/api/v2/auth/api/v2/admin/users/list', headers: {
'X-Auth-Apikey' => admin_api_key.kid,
'X-Auth-Nonce' => nonce,
'X-Auth-Signature' => signature
}
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
expect(response.headers['Authorization']).to include "Bearer"
end
end
end
end
end
end

View File

@@ -0,0 +1,196 @@
# 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: 'member', action: 'ACCEPT', verb: 'all', path: 'tasty_endpoint'
Rails.cache.delete('permissions')
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/tasty_endpoint' }
let(:auth_session_create_request) { '/api/v2/auth/api/v2/barong/identity/sessions' }
describe 'test blocklogin restriction' do
before do
allow(Rails.cache).to receive(:delete_matched).and_return(nil)
Rails.cache.delete('restrictions')
end
context 'block session creation' do
let!(:restriction) { create(:restriction, value: 'EUROPE', scope: 'continent', category: 'blocklogin', code: 425) }
before do
allow_any_instance_of(Barong::Authorize).to receive(:validate_session!).and_return(true)
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(london_ip)
end
it do
post auth_session_create_request, params: params
expect(response.status).to eq(restriction.code)
end
it do
do_create_session_request # This request will be successful because path doesn't include 'api/v2/auth'
get auth_request
expect(response.status).to eq(200)
end
end
end
describe 'test blacklist restrictions' do
before do
allow_any_instance_of(Barong::Authorize).to receive(:validate_session!).and_return(true)
Rails.cache.delete('restrictions')
do_create_session_request
end
context 'restrict by ip' do
let!(:restriction) { create(:restriction, value: '192.168.0.1', scope: 'ip', category: 'blacklist') }
let!(:disabled) { create(:restriction, value: '192.168.0.3', scope: 'ip', state: 'disabled', category: 'blacklist') }
it 'request with restricted ip' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return('192.168.0.1')
get auth_request
expect(response.status).to eq(401)
expect(response.headers['Authorization']).to be_nil
end
it 'request with non-restricted ip' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return('192.168.0.2')
get auth_request
expect(response.status).to eq(200)
end
it 'request with disabled restriction' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return('192.168.0.3')
get auth_request
expect(response.status).to eq(200)
end
end
context 'restricts with ip subnet' do
let!(:restriction) { create(:restriction, value: '192.168.32.0/24', scope: 'ip_subnet', category: 'blacklist') }
it 'request with restricted ip' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return('192.168.32.42')
get auth_request
expect(response.status).to eq(403)
expect(response.headers['Authorization']).to be_nil
expect(response.body).to eq("{\"errors\":[\"authz.restrict.blacklist\"]}")
end
it 'request with non-restricted ip' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return('192.168.33.3')
get auth_request
expect(response.status).to eq(200)
end
end
context 'geoip' do
context 'restricts with country' do
let!(:restriction) { create(:restriction, value: 'japan', scope: 'country', category: 'blacklist') }
it 'with restricted ip' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(tokyo_ip)
get auth_request
expect(response.status).to eq(423)
expect(response.headers['Authorization']).to be_nil
expect(response.body).to eq("{\"errors\":[\"authz.restrict.blacklist\"]}")
end
it 'with non-restricted ip' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(london_ip)
get auth_request
expect(response.status).to eq(200)
end
end
context 'restricts with continent' do
let!(:restriction) { create(:restriction, value: 'EUROPE', scope: 'continent', category: 'blacklist') }
it 'with restricted ip' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(london_ip)
get auth_request
expect(response.status).to eq(423)
expect(response.headers['Authorization']).to be_nil
expect(response.body).to eq("{\"errors\":[\"authz.restrict.blacklist\"]}")
end
it 'with non-restricted ip' do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(tokyo_ip)
get auth_request
expect(response.status).to eq(200)
end
end
end
end
describe 'test restriction ierarchy' do
before do
allow_any_instance_of(Barong::Authorize).to receive(:validate_session!).and_return(true)
Rails.cache.delete('restrictions')
do_create_session_request
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return('192.168.0.1')
end
context 'whitelist -> maintenance' do
let!(:maintenance_restriction) { create(:restriction, value: '192.168.0.1', scope: 'ip', category: 'maintenance') }
let!(:whitelist_restriction) { create(:restriction, value: '192.168.0.1', scope: 'ip', category: 'whitelist') }
it '200' do
get auth_request
expect(response.status).to eq(200)
end
end
context 'maintenance -> blacklist' do
let!(:blacklist_restriction) { create(:restriction, value: '192.168.0.1', scope: 'ip', category: 'blacklist') }
let!(:maintenance_restriction) { create(:restriction, value: '192.168.0.1', scope: 'ip', category: 'maintenance') }
context 'standard code error' do
it '471' do
get auth_request
expect(response.status).to eq(471)
end
end
end
context 'blacklist' do
context 'standard code error' do
let!(:blacklist_restriction) { create(:restriction, value: '192.168.0.1', scope: 'ip', category: 'blacklist') }
it '401' do
get auth_request
expect(response.status).to eq(401)
end
end
context 'custom code error' do
let!(:blacklist_restriction) { create(:restriction, value: '192.168.0.1', scope: 'ip', category: 'blacklist', code: 403) }
it '403' do
get auth_request
expect(response.status).to eq(403)
end
end
end
end
end

View File

@@ -0,0 +1,158 @@
# frozen_string_literal: true
require 'spec_helper'
include ActiveSupport::Testing::TimeHelpers
describe '/api/v2/auth functionality test' do
include_context 'geoip mock'
let(:session_expire_time) do
Barong::App.config.session_expire_time.to_i.seconds
end
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_destroy_session_request) { delete '/api/v2/identity/sessions', headers: { 'HTTP_USER_AGENT': 'legacy-browser' } }
let(:do_create_session_request) { post '/api/v2/identity/sessions', params: params, headers: { 'HTTP_USER_AGENT': 'legacy-browser' } }
let(:auth_request) { '/api/v2/auth/not_in_the_rules_path' }
describe 'testing session hash validations' do
before do
Rails.cache.delete('permissions')
end
context 'with valid ip, browser' do
it 'authorize traffic' do
do_create_session_request
expect(response.status).to eq(200)
post auth_request, headers: { 'HTTP_USER_AGENT': 'legacy-browser' }
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
expect(response.headers['Authorization']).to include "Bearer"
end
end
context 'when session params has changed after session opening' do
it 'return error if USER_AGENT changes' do
do_create_session_request
expect(response.status).to eq(200)
post auth_request, headers: { 'HTTP_USER_AGENT': 'new-browser' }
expect(response.status).to eq(401)
expect(response.headers['Authorization']).to be_nil
end
it 'return error if IP changes' do
do_create_session_request
expect(response.status).to eq(200)
post auth_request, headers: { 'REMOTE_ADDR': '128.0.0.12' }
expect(response.status).to eq(401)
expect(response.headers['Authorization']).to be_nil
end
it 'return error if everything changes' do
do_create_session_request
expect(response.status).to eq(200)
post auth_request, headers: { 'REMOTE_ADDR': '128.0.0.12', 'HTTP_USER_AGENT': 'new-browser' }
expect(response.status).to eq(401)
expect(response.headers['Authorization']).to be_nil
end
end
end
describe 'testing session renewal' do
before do
Rails.cache.delete('permissions')
end
context 'with valid session' do
it 'authorize traffic and renew session every request' do
do_create_session_request
expect(response.status).to eq(200)
post auth_request, headers: { 'HTTP_USER_AGENT': 'legacy-browser' }
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
expect(response.headers['Authorization']).to include "Bearer"
start_time = Time.current
30.times do
# 5 minute before session will expire
travel session_expire_time - 5.minutes
# renew session with private request
post auth_request, headers: { 'HTTP_USER_AGENT': 'legacy-browser' }
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
expect(response.headers['Authorization']).to include "Bearer"
end
expect(Time.current).to be > (start_time + session_expire_time)
travel session_expire_time + 10.minutes
post auth_request, headers: { 'HTTP_USER_AGENT': 'legacy-browser' }
expect(response.status).to eq(401)
end
end
end
describe 'testing session destroy' do
before do
Rails.cache.delete('permissions')
end
context 'with valid session' do
it 'deletes session from cache on #logout' do
do_create_session_request
expect(response.status).to eq(200)
post auth_request, headers: { 'HTTP_USER_AGENT': 'legacy-browser' }
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
expect(response.headers['Authorization']).to include "Bearer"
do_destroy_session_request
expect(response.status).to eq(200)
post auth_request, headers: { 'HTTP_USER_AGENT': 'legacy-browser' }
expect(response.status).to eq(401)
end
end
context 'with invalid session params' do
it 'deletes session from cache on auth if params is wrong' do
do_create_session_request
expect(response.status).to eq(200)
post auth_request, headers: { 'HTTP_USER_AGENT': 'legacy-browser' }
expect(response.status).to eq(200)
expect(response.headers['Authorization']).not_to be_nil
expect(response.headers['Authorization']).to include "Bearer"
post auth_request, headers: { 'HTTP_USER_AGENT': 'new-browser' }
expect(response.status).to eq(401)
post auth_request, headers: { 'HTTP_USER_AGENT': 'legacy-browser' }
expect(response.status).to eq(401)
end
end
end
end