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,39 @@
# frozen_string_literal: true
describe API::V2::Admin::Abilities, type: :request do
include_context 'bearer authentication'
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
describe 'GET /api/v2/admin/abilities' do
context 'superadmin user' do
let(:test_user) { create(:user, email: 'example@gmail.com', role: 'admin') }
it 'get all roles and permissions' do
get '/api/v2/admin/abilities', headers: auth_header
result = JSON.parse(response.body)
expect(response).to be_successful
expect(result).to eq(
'read' => %w[Level APIKey Permission],
'manage' => %w[User Activity Profile Label]
)
end
end
context 'member user' do
let(:test_user) { create(:user, email: 'example@gmail.com', role: 'member') }
it 'get all roles and permissions' do
get '/api/v2/admin/abilities', headers: auth_header
result = JSON.parse(response.body)
expect(response).to be_successful
expect(result).to eq({})
end
end
end
end

View File

@@ -0,0 +1,145 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::V2::Admin::Activities do
include_context 'bearer authentication'
describe 'GET /api/v2/admin/activities' do
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:do_request) { get '/api/v2/admin/activities', headers: auth_header }
context 'admin user' do
let!(:test_user) { create(:user, email: 'testa@gmail.com', role: 'admin') }
let!(:first_user) { create(:user, email: 'test1@gmail.com') }
let!(:second_user) { create(:user, email: 'test2@gmail.com') }
context 'user activities' do
let!(:create_actitivites) do
create(:activity, category: 'user', topic: 'session', action: 'login', user: first_user)
create(:activity, category: 'user', topic: 'session', action: 'login', user: second_user)
create(:activity, category: 'user', topic: 'otp', action: 'otp::enable', user: second_user)
create(:activity, category: 'admin', topic: 'otp', action: 'otp::enable', user: second_user)
end
it 'doesnt return admin activities' do
get '/api/v2/admin/activities', headers: auth_header
activities = JSON.parse(response.body)
expect(Activity.where(category: 'user').count).to eq activities.count
expect(Activity.all.count).not_to eq activities.count
end
it 'returns list of activities' do
get '/api/v2/admin/activities', headers: auth_header
activities = JSON.parse(response.body)
user_activites = Activity.where(category: 'user')
expect(user_activites.count).to eq activities.count
expect(user_activites.last.user_ip).to eq activities[0]['user_ip']
expect(user_activites.last.user_agent).to eq activities[0]['user_agent']
expect(user_activites.last.topic).to eq activities[0]['topic']
expect(user_activites.last.action).to eq activities[0]['action']
expect(user_activites.last.result).to eq activities[0]['result']
expect(user_activites.last.user.email).to eq activities[0]['user']['email']
expect(first_user.email).to eq activities[2]['user']['email']
end
it 'returns list of activities filtered by action' do
get '/api/v2/admin/activities', headers: auth_header, params: {action: 'login'}
activities = JSON.parse(response.body)
expect(activities.count).to eq 2
end
it 'returns list of activities filtered by uid' do
get '/api/v2/admin/activities', headers: auth_header, params: {uid: first_user.uid}
activities = JSON.parse(response.body)
expect(activities.count).to eq 1
end
it 'returns list of activities filtered by email' do
get '/api/v2/admin/activities', headers: auth_header, params: {email: first_user.email}
activities = JSON.parse(response.body)
expect(activities.count).to eq 1
end
it 'returns list of activities filtered by topic' do
get '/api/v2/admin/activities', headers: auth_header, params: {topic: 'otp'}
activities = JSON.parse(response.body)
expect(activities.count).to eq 1
end
it 'returns list of activities filtered by action and uid' do
get '/api/v2/admin/activities', headers: auth_header, params: {topic: 'session', action: 'login', uid: second_user.uid}
activities = JSON.parse(response.body)
expect(activities.count).to eq 1
end
end
context 'admin activities' do
let!(:create_actitivites) do
create(:activity, topic: 'session', action: 'login', user: first_user, category: 'admin', target_uid: second_user.uid)
create(:activity, topic: 'session', action: 'login', user: second_user, category: 'admin')
create(:activity, topic: 'otp', action: 'otp::enable', user: second_user, category: 'admin', target_uid: second_user.uid)
end
it 'returns list of activities' do
get '/api/v2/admin/activities/admin', headers: auth_header
activities = JSON.parse(response.body)
expect(Activity.count).to eq activities.count
expect(Activity.third.user_ip).to eq activities[0]['user_ip']
expect(Activity.third.user_agent).to eq activities[0]['user_agent']
expect(Activity.third.topic).to eq activities[0]['topic']
expect(Activity.third.action).to eq activities[0]['action']
expect(Activity.third.result).to eq activities[0]['result']
expect(Activity.third.user.email).to eq activities[0]['admin']['email']
expect(Activity.third.target.email).to eq activities[0]['target']['email']
expect(Activity.first.user.email).to eq activities[2]['admin']['email']
expect(Activity.first.target.email).to eq activities[2]['target']['email']
end
it 'returns list of activities filtered by action' do
get '/api/v2/admin/activities/admin', headers: auth_header, params: { action: 'login' }
activities = JSON.parse(response.body)
expect(activities.count).to eq 2
end
it 'returns list of activities filtered by uid' do
get '/api/v2/admin/activities/admin', headers: auth_header, params: { uid: first_user.uid }
activities = JSON.parse(response.body)
expect(activities.count).to eq 1
end
it 'returns list of activities filtered by email' do
get '/api/v2/admin/activities/admin', headers: auth_header, params: { email: first_user.email }
activities = JSON.parse(response.body)
expect(activities.count).to eq 1
end
it 'returns list of activities filtered by topic' do
get '/api/v2/admin/activities/admin', headers: auth_header, params: { topic: 'otp' }
activities = JSON.parse(response.body)
expect(activities.count).to eq 1
end
it 'returns list of activities filtered by action and uid' do
get '/api/v2/admin/activities/admin', headers: auth_header, params: { topic: 'session', action: 'login', uid: second_user.uid }
activities = JSON.parse(response.body)
expect(activities.count).to eq 1
end
it 'returns list of activities filtered by affected user' do
get '/api/v2/admin/activities/admin', headers: auth_header, params: { target_uid: second_user.uid }
activities = JSON.parse(response.body)
expect(activities.count).to eq 2
end
end
end
end
end

View File

@@ -0,0 +1,105 @@
# frozen_string_literal: true
describe 'Api::V2::Admin::APIKeys' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:create_superadmin_permission) do
create :permission,
role: 'superadmin'
end
describe 'GET /api/v2/admin/api_keys' do
let!(:test_user) { create(:user, otp: otp_enabled, role: 'superadmin') }
let(:otp_enabled) { true }
let!(:first_api_key) { create :api_key, key_holder_account: test_user }
let!(:second_api_key) { create :api_key, key_holder_account: test_user }
let(:expected_fields) do
{
kid: first_api_key.kid,
state: first_api_key.state,
scope: %w[trade]
}
end
let(:params) do
{
uid: test_user.uid
}
end
let(:do_request) { get "/api/v2/admin/api_keys", params: params, headers: auth_header }
context 'successful' do
it 'Returns api keys for selected account' do
do_request
expect(response.status).to eq(200)
expect(json_body.first.keys).to match_array %i[kid algorithm scope state created_at updated_at]
expect(json_body.first).to include(expected_fields)
expect(json_body.first).not_to include(:secret)
end
it 'Returns api keys for selected account in ASC order' do
params[:ordering] = 'asc'
params[:order_by] = 'id'
do_request
expect(response.status).to eq(200)
expect(json_body.first.keys).to match_array %i[kid algorithm scope state created_at updated_at]
expect(json_body.first).to include(expected_fields)
expect(json_body.first).not_to include(:secret)
end
it 'Returns api keys for selected account in DESC order' do
params[:ordering] = 'desc'
params[:order_by] = 'id'
do_request
expect(response.status).to eq(200)
expect(json_body.first.keys).to match_array %i[kid algorithm scope state created_at updated_at]
expect(json_body.second).to include(expected_fields)
expect(json_body.second).not_to include(:secret)
end
it 'Returns empty array if no api keys exist for selected account' do
test_user.api_keys.delete_all
do_request
expect(response.status).to eq(200)
expect(json_body).to eq([])
end
end
context 'error' do
let(:params) do
{
uid: 'random'
}
end
it 'Returns error if user doesnt exist' do
do_request
expect(response.status).to eq(404)
expect(json_body.first).to include(:errors, ["admin.user.doesnt_exist"])
end
it 'Returns error if api key attribute doesnt exist' do
params[:uid] = test_user.uid
params[:order_by] = 'length'
do_request
expect(response.status).to eq(422)
expect(json_body.first).to include(:errors, ["api_keys.ordering.invalid_attribute"])
end
it 'Returns error if invalid ordering' do
params[:uid] = test_user.uid
params[:ordering] = 'res'
do_request
expect(response.status).to eq(422)
expect(json_body.first).to include(:errors, ["api_keys.ordering.invalid_ordering"])
end
end
end
end

View File

@@ -0,0 +1,39 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::V2::Admin::Levels do
include_context 'bearer authentication'
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
describe 'GET /api/v2/admin/levels' do
let(:do_request) { get '/api/v2/admin/levels', headers: auth_header }
context 'admin user' do
let(:test_user) { create(:user, role: 'admin') }
it 'returns levels' do
do_request
expect(response.status).to eq(200)
result = JSON.parse(response.body)
expect(result.length).to eq(Level.count)
result.each do |level|
expected_level = Level.find(level['id'])
expect(level['key']).to eq(expected_level.key)
expect(level['value']).to eq(expected_level.value)
end
end
end
end
end

View File

@@ -0,0 +1,49 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::V2::Admin::Metrics do
include_context 'bearer authentication'
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
describe 'GET /api/v2/admin/users' do
let(:do_request) { get '/api/v2/admin/metrics', headers: auth_header }
context 'admin user' do
let(:test_user) { create(:user, role: 'admin') }
it 'returns empty hashes if no activity in database' do
do_request
expect(response.status).to eq(200)
expect(response.body).to eq("{\"signups\":{},\"sucessful_logins\":{},\"failed_logins\":{},\"pending_applications\":0}")
result = JSON.parse(response.body)
expect(result['signups']).to eq({})
expect(result['sucessful_logins']).to eq({})
expect(result['failed_logins']).to eq({})
end
context 'with data' do
let!(:login_activity) { create(:activity, action: 'login', result: 'succeed', topic: 'session', user: test_user) }
let!(:signup_activity) { create(:activity, action: 'signup', result: 'succeed', topic: 'account', user: test_user) }
let!(:failed_login_activity) { create(:activity, topic: 'session', action: 'login', result: 'failed', user: test_user) }
it 'returns data as expected' do
do_request
expect(response.status).to eq(200)
expect(JSON.parse(response.body)['failed_logins']).not_to eq({})
expect(JSON.parse(response.body)['sucessful_logins']).not_to eq({})
expect(JSON.parse(response.body)['signups']).not_to eq({})
end
end
end
end
end

View File

@@ -0,0 +1,144 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::V2::Admin::Permissions do
include_context 'bearer authentication'
let!(:create_admin_permission) { create(:permission, role: 'superadmin', action: 'accept', verb: 'get') }
let!(:test_user) { create(:user, role: 'superadmin') }
let!(:create_member_permission) { create(:permission, role: 'member') }
describe 'GET /api/v2/admin/permissions' do
context 'successful response' do
it 'returns all permissions' do
get '/api/v2/admin/permissions', headers: auth_header
result = JSON.parse(response.body)
expect(response).to be_successful
expect(result.count).to eq(Permission.count)
end
it 'returns paginated permissions' do
get '/api/v2/admin/permissions', params: { limit: 1, page: 1 }, headers: auth_header
result = JSON.parse(response.body)
expect(response).to be_successful
expect(response.headers.fetch('Total')).to eq '2'
expect(result.size).to eq 1
expect(result.first['role']).to eq 'superadmin'
get '/api/v2/admin/permissions', params: { limit: 1, page: 2 }, headers: auth_header
result = JSON.parse(response.body)
expect(response).to be_successful
expect(response.headers.fetch('Total')).to eq '2'
expect(result.size).to eq 1
expect(result.first['role']).to eq 'member'
end
end
end
describe 'POST /api/v2/admin/permissions' do
context 'unsuccessful response' do
it 'returns error while invalid verb creating' do
post '/api/v2/admin/permissions', params: { role: 'admin', action: 'drop', verb: 'options', path: 'api/v2' }, headers: auth_header
result = JSON.parse(response.body)
expect(response.code).to eq '422'
expect(result['errors']).to eq(['admin.permissions.invalid_verb'])
end
it 'returns error while invalid action creating' do
post '/api/v2/admin/permissions', params: { role: 'admin', action: 'delete', verb: 'put', path: 'api/v2' }, headers: auth_header
result = JSON.parse(response.body)
expect(response.code).to eq '422'
expect(result['errors']).to eq(['admin.permissions.invalid_action'])
end
it 'returns error while invalid role creating' do
post '/api/v2/admin/permissions', params: { role: 'supertrader', action: 'accept', verb: 'put', path: 'api/v2' }, headers: auth_header
result = JSON.parse(response.body)
expect(response.code).to eq '422'
expect(result['errors']).to eq(['admin.permission.role_doesnt_exist'])
end
it 'returns error while empty action creating' do
post '/api/v2/admin/permissions', params: { role: 'supertrader', action: '', verb: 'put', path: 'api/v2' }, headers: auth_header
result = JSON.parse(response.body)
expect(response.code).to eq '422'
expect(result['errors']).to eq(['admin.permission.empty_action'])
end
end
context 'successful response' do
let(:do_request) { post '/api/v2/admin/permissions', params: { role: 'superadmin', action: 'accept', verb: 'put', path: 'api/v2/admin' }, headers: auth_header }
it 'creates new permission' do
expect { do_request }.to change { Permission.count }.by(1)
expect(response).to be_successful
end
end
end
describe 'DELETE /api/v2/admin/permissions' do
context 'successful response' do
let(:do_request) { delete '/api/v2/admin/permissions', params: { id: create_member_permission.id }, headers: auth_header }
it 'delete permission' do
expect { do_request }.to change { Permission.count }.by(-1)
expect(response).to be_successful
end
end
context 'unsuccessful response' do
it 'return error while permission doesnt exist' do
delete '/api/v2/admin/permissions', params: { id: 0 }, headers: auth_header
result = JSON.parse(response.body)
expect(response.code).to eq '404'
expect(result['errors']).to eq(['admin.permission.doesnt_exist'])
end
end
end
describe 'PUT /api/v2/admin/permissions' do
context 'successful response' do
let(:permission) { Permission.find_by(id: create_admin_permission.id) }
it 'returns updated path for permission' do
put '/api/v2/admin/permissions', params: { id: create_admin_permission.id , path: 'api/v2/admin' }, headers: auth_header
expect(response).to be_successful
expect(permission.path).to eq 'api/v2/admin'
end
it 'returns updated verb for permission' do
put '/api/v2/admin/permissions', params: { id: create_admin_permission.id , verb: 'PUT' }, headers: auth_header
expect(response).to be_successful
expect(permission.verb).to eq 'PUT'
end
it 'returns updated fileds for permission' do
put '/api/v2/admin/permissions', params: { id: create_admin_permission.id , verb: 'POST', action: 'all', path: 'api/v2' }, headers: auth_header
expect(response).to be_successful
expect(permission.verb).to eq 'POST'
expect(permission.action).to eq 'ALL'
expect(permission.path).to eq 'api/v2'
end
end
context 'unsuccessful response' do
it 'return error while permission doesnt exist' do
put '/api/v2/admin/permissions', params: { id: 0, action: 'accept' }, headers: auth_header
result = JSON.parse(response.body)
expect(response.code).to eq '404'
expect(result['errors']).to eq(['admin.permission.doesnt_exist'])
end
end
end
end

View File

@@ -0,0 +1,211 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::V2::Admin::Profiles do
include_context 'bearer authentication'
let!(:create_admin_permission) do
create :permission,
role: 'admin'
create :permission,
role: 'superadmin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:test_user) { create(:user, role: 'superadmin') }
let!(:profile1) { create(:profile) }
let!(:profile2) { create(:profile) }
let!(:profile3) { create(:profile) }
let!(:profile4) { create(:profile) }
let!(:profile5) { create(:profile) }
describe 'GET /api/v2/admin/profiles' do
context 'successful response' do
it 'returns all profiles' do
get '/api/v2/admin/profiles', headers: auth_header
result = JSON.parse(response.body)
expect(response).to be_successful
expect(result.count).to eq(Profile.count)
expect(result.first.keys).to match_array %w[first_name last_name dob address postcode city country state metadata created_at updated_at]
end
it 'returns paginated profiles' do
get '/api/v2/admin/profiles', params: { limit: 1, page: 1 }, headers: auth_header
result = JSON.parse(response.body)
expect(response).to be_successful
expect(response.headers.fetch('Total')).to eq '5'
expect(result.size).to eq 1
expect(result.first['first_name']).to eq profile1.first_name
expect(result.first['last_name']).to eq profile1.last_name
expect(result.first['dob']).to eq profile1.dob.to_s
get '/api/v2/admin/profiles', params: { limit: 1, page: 2 }, headers: auth_header
result = JSON.parse(response.body)
expect(response).to be_successful
expect(response.headers.fetch('Total')).to eq '5'
expect(result.size).to eq 1
expect(result.first['first_name']).to eq profile2.first_name
expect(result.first['last_name']).to eq profile2.last_name
expect(result.first['dob']).to eq profile2.dob.to_s
end
end
end
describe 'POST /api/v2/admin/profiles' do
let(:user) { create :user }
let!(:request_params) do
{
last_name: Faker::Name.last_name,
first_name: Faker::Name.first_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.street_address,
postcode: Faker::Address.zip_code
}
end
context 'successful response' do
it 'works correctly' do
expect { post '/api/v2/admin/profiles', params: request_params.merge(uid: user.uid), headers: auth_header }
.to change { Profile.count }.by(1)
expect(response.status).to eq(201)
expect(json_body.keys).to match_array %i[first_name last_name dob address postcode city country state metadata created_at updated_at]
expect(json_body[:first_name]).to eq Profile.last.first_name
expect(json_body[:last_name]).to eq Profile.last.last_name
expect(json_body[:dob]).to eq Profile.last.dob.to_s
expect(json_body[:state]).to eq('submitted')
expect(json_body[:metadata]).to be_blank
expect(Profile.last.author).to eq(test_user.uid)
end
end
end
describe 'PUT /api/v2/admin/profiles' do
context 'successful response' do
let!(:member_with_profile) do
@member = create :user, role: 'member'
create(:profile, user_id: @member.id, state: 'submitted')
end
it 'returns profile' do
put '/api/v2/admin/profiles', params: { uid: @member.uid, state: 'verified' }, headers: auth_header
expect(response.status).to eq(200)
profile = @member.profiles.last
expect(profile).to be
expect(json_body.keys).to match_array %i[first_name last_name dob address postcode city country state metadata created_at updated_at]
expect(json_body[:first_name]).to eq profile.first_name
expect(json_body[:last_name]).to eq profile.last_name
expect(json_body[:dob]).to eq profile.dob.to_s
expect(json_body[:state]).to eq('verified')
expect(profile.state).to eq('verified')
end
let!(:superadmin_with_profile) do
@user = create :user, role: 'superadmin'
create(:profile, user_id: @user.id, state: 'submitted')
end
it 'return profiles when superadmin updates superadmin' do
test_user.update!(role: 'superadmin')
put '/api/v2/admin/profiles', params: { uid: @user.uid, state: 'verified' }, headers: auth_header
expect(response.status).to eq(200)
profile = @user.profiles.last
expect(profile).to be
expect(json_body.keys).to match_array %i[first_name last_name dob address postcode city country state metadata created_at updated_at]
expect(json_body[:first_name]).to eq profile.first_name
expect(json_body[:last_name]).to eq profile.last_name
expect(json_body[:dob]).to eq profile.dob.to_s
expect(json_body[:state]).to eq('verified')
expect(profile.state).to eq('verified')
expect(profile.metadata).to be_blank
end
end
context 'unsuccessful response' do
let!(:admin_test) { test_user.update(role: 'admin') }
let!(:superadmin_with_profile) do
@user = create :user, role: 'superadmin'
create(:profile, user_id: @user.id, state: 'submitted', author: test_user.uid)
@user_admin = create :user, role: 'admin'
create(:profile, user_id: @user_admin.id, state: 'submitted', author: test_user.uid)
end
it 'return error when non-superadmin user updates superadmin' do
put '/api/v2/admin/profiles', params: { uid: @user.uid, state: 'rejected' }, headers: auth_header
result = JSON.parse(response.body)
expect(response.code).to eq '422'
expect(result['errors']).to eq(['admin.profiles.superadmin_change'])
end
it 'return error when author is trying to approve himself' do
allow(Barong::App.config).to receive_messages(profile_double_verification: 'true')
put '/api/v2/admin/profiles', params: { uid: @user_admin.uid, state: 'rejected' }, headers: auth_header
result = JSON.parse(response.body)
expect(response.code).to eq '422'
expect(result['errors']).to eq(['admin.profiles.second_admin_approval'])
end
it 'renders an error when profile doesnt exist' do
put '/api/v2/admin/profiles', params: { uid: '0', state: 'rejected' }, headers: auth_header
expect_status.to eq(404)
expect_body.to eq(errors: ['admin.profiles.doesnt_exist_or_not_editable'])
end
it 'renders an error when profile is not editable' do
put '/api/v2/admin/profiles', params: { uid: profile3.user.uid, state: 'rejected' }, headers: auth_header
expect_status.to eq(404)
expect_body.to eq(errors: ['admin.profiles.doesnt_exist_or_not_editable'])
end
end
context 'user with different amount of profile fields' do
let!(:profile) { create(:profile, user: test_user, last_name: nil, first_name: nil, state: 'submitted') }
it 'returns partial updated profile' do
put '/api/v2/admin/profiles', params: { uid: test_user.uid, state: 'rejected' }, headers: auth_header
expect(response.status).to eq(200)
profile = test_user.profiles.last
expect(profile).to be
expect(json_body.keys).to match_array %i[first_name last_name dob address postcode city country state metadata created_at updated_at]
expect(json_body[:first_name]).to eq profile.first_name
expect(json_body[:last_name]).to eq profile.last_name
expect(json_body[:dob]).to eq profile.dob.to_s
expect(json_body[:state]).to eq('rejected')
expect(profile.state).to eq('rejected')
end
it 'returns full updated profile' do
put '/api/v2/admin/profiles', params: { uid: test_user.uid, state: 'rejected' }, headers: auth_header
expect(response.status).to eq(200)
profile = test_user.profiles.last
expect(profile).to be
expect(json_body.keys).to match_array %i[first_name last_name dob address postcode city country state metadata created_at updated_at]
expect(json_body[:first_name]).to eq profile.first_name
expect(json_body[:last_name]).to eq profile.last_name
expect(json_body[:dob]).to eq profile.dob.to_s
expect(json_body[:state]).to eq('rejected')
expect(profile.state).to eq('rejected')
expect(profile.metadata).to be_blank
end
end
end
end

View File

@@ -0,0 +1,202 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::V2::Admin::Restrictions do
include_context 'bearer authentication'
before { create(:permission, role: 'superadmin') }
let!(:test_user) { create(:user, role: 'superadmin') }
describe 'GET /api/v2/admin/restrictions' do
before do
create(:restriction, scope: 'ip', value: '0.0.0.1', category: 'blacklist', updated_at: 1.days.ago)
create(:restriction, scope: 'ip', value: '0.0.0.0', category: 'blacklist', updated_at: 3.days.ago)
create(:restriction, scope: 'ip_subnet', value: '1.2.3.4/24', category: 'blacklist', updated_at: 3.days.ago)
end
context 'successful response' do
it 'returns all restrictions' do
get '/api/v2/admin/restrictions', headers: auth_header
expect(response).to be_successful
expect(json_body.count).to eq(Restriction.count)
end
it 'filters by scope' do
get '/api/v2/admin/restrictions', headers: auth_header, params: { scope: 'ip' }
expect(json_body.length).to eq Restriction.where(scope: 'ip').count
expect(json_body.map { |r| r[:scope] }).to all eq 'ip'
end
it 'filters by category' do
get '/api/v2/admin/restrictions', headers: auth_header, params: { category: 'blacklist' }
expect(json_body.length).to eq Restriction.where(category: 'blacklist').count
expect(json_body.map { |r| r[:category] }).to all eq 'blacklist'
end
it 'filters with date range' do
get '/api/v2/admin/restrictions', headers: auth_header, params: { from: 2.days.ago.to_i, range: 'updated' }
expected = Restriction.where("updated_at >= ?", 2.days.ago)
expect(json_body.map { |r| r[:id] }).to match_array expected.map(&:id)
end
it 'returns paginated restrictions' do
get '/api/v2/admin/restrictions', headers: auth_header, params: { limit: 1 }
result = json_body
expect(response).to be_successful
expect(response.headers.fetch('Total')).to eq '3'
expect(result.size).to eq 1
end
end
end
describe 'POST /api/v2/admin/whitelink' do
context 'with default expire time' do
it 'create a whitelink token' do
post '/api/v2/admin/restrictions/whitelink', headers: auth_header
expect(response).to be_successful
expect(json_body[:whitelink_token]).not_to be nil
end
end
context 'with custom expire time' do
it 'create a whitelink token' do
post '/api/v2/admin/restrictions/whitelink', headers: auth_header, params: { expire_time: 30 }
expect(response).to be_successful
expect(json_body[:whitelink_token]).not_to be nil
end
it 'returns error if expire time too big' do
post '/api/v2/admin/restrictions/whitelink', headers: auth_header, params: { expire_time: 31 }
expect(response.status).to eq 422
expect(json_body[:whitelink_token]).to be nil
expect(json_body[:errors]).to include "invalid_expire"
end
end
end
describe 'POST /api/v2/admin/restrictions' do
it 'creates new restriction' do
expect {
post '/api/v2/admin/restrictions', headers: auth_header, params: { scope: 'ip', value: '127.0.0.0', category: 'blacklist' }
}.to change { Restriction.count }.by(1)
expect(response).to be_successful
end
it 'creates new restriction with code' do
expect {
post '/api/v2/admin/restrictions', headers: auth_header, params: { scope: 'ip', value: '127.0.0.0', category: 'blacklist', code: 522 }
}.to change { Restriction.count }.by(1)
expect(response).to be_successful
end
it 'creates new whitelist restriction' do
expect {
post '/api/v2/admin/restrictions', headers: auth_header, params: { scope: 'ip', value: '127.0.0.0', category: 'whitelist' }
}.to change { Restriction.count }.by(1)
expect(response).to be_successful
end
it 'creates new maintenance restriction' do
expect {
post '/api/v2/admin/restrictions', headers: auth_header, params: { scope: 'ip', value: '127.0.0.0', category: 'maintenance' }
}.to change { Restriction.count }.by(1)
expect(response).to be_successful
end
context 'validation' do
it 'scope and state' do
post '/api/v2/admin/restrictions', headers: auth_header, params: { scope: 'office', value: '127.0.0.0', state: 'running' }
expect(json_body[:errors]).to include "admin.restriction.invalid_scope"
expect(json_body[:errors]).to include "admin.restriction.invalid_state"
end
it 'value' do
post '/api/v2/admin/restrictions', headers: auth_header, params: { scope: 'ip', value: '127.a.b.c', category: 'blacklist' }
expect(json_body[:errors]).to include "value.invalid"
end
it 'category' do
post '/api/v2/admin/restrictions', headers: auth_header, params: { scope: 'country', value: 'Ukraine', category: 'allow' }
expect(json_body[:errors]).to include "admin.restriction.invalid_category"
end
end
end
describe 'PUT /api/v2/admin/restrictions' do
let!(:restriction) { create(:restriction, scope: 'ip', value: '127.0.0.1', category: 'blacklist') }
it 'updates state' do
expect {
put '/api/v2/admin/restrictions', headers: auth_header, params: { id: restriction.id, state: 'disabled' }
}.to change { restriction.reload.state }.to('disabled')
end
it 'updates value' do
expect {
put '/api/v2/admin/restrictions', headers: auth_header, params: { id: restriction.id, value: '192.168.0.1' }
}.to change { restriction.reload.value }.to('192.168.0.1')
end
it 'does not change scope if value becomes invalid' do
expect {
put '/api/v2/admin/restrictions', headers: auth_header, params: { id: restriction.id, scope: 'ip_subnet' }
}.not_to change { restriction.reload.scope }
end
it 'validates value' do
put '/api/v2/admin/restrictions', headers: auth_header, params: { id: restriction.id, value: '192.168.0.abc' }
expect(response).not_to be_successful
end
it 'updates scope' do
expect {
put '/api/v2/admin/restrictions', headers: auth_header, params: { id: restriction.id, scope: 'ip_subnet', value: '192.168.0.1/24' }
}.to change { restriction.reload.scope }.to('ip_subnet').and change { restriction.reload.value }.to '192.168.0.1/24'
end
it 'updates category' do
expect {
put '/api/v2/admin/restrictions', headers: auth_header, params: { id: restriction.id, category: 'whitelist' }
}.to change { restriction.reload.category }.to('whitelist')
end
end
describe 'DELETE /api/v2/admin/restrictions' do
let!(:restriction) { create(:restriction, scope: 'ip', value: '127.0.0.1', category: 'blacklist') }
context 'successful response' do
let(:do_request) { delete '/api/v2/admin/restrictions', params: { id: restriction.id }, headers: auth_header }
it 'delete restriction' do
expect { do_request }.to change { Restriction.count }.by(-1)
expect(response).to be_successful
end
end
context 'unsuccessful response' do
it 'return error while restriction doesnt exist' do
delete '/api/v2/admin/restrictions', headers: auth_header, params: { id: 0 }
result = JSON.parse(response.body)
expect(response.code).to eq '404'
expect(result['errors']).to eq(['admin.restriction.doesnt_exist'])
end
end
end
end

File diff suppressed because it is too large Load Diff

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

View File

@@ -0,0 +1,141 @@
# encoding: UTF-8
# frozen_string_literal: true
require 'rack/cors'
require 'env-tweaks'
describe Rack::Cors, type: :request do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:member) { create(:member, :level_3) }
let(:frontend_url) { 'https://frontend.io' }
let(:local_url) { 'http://localhost:3000' }
let(:token) { jwt_for(member) }
let(:app) {
Rack::Builder.new do
use Rack::Cors do
allow do
origins Barong::CORS::Validations.validate_origins(ENV['API_CORS_ORIGINS'])
resource '/api/*',
methods: %i[get post delete put patch options head],
headers: :any,
credentials: ENV.true?('API_CORS_ALLOW_CREDENTIALS'),
max_age: Barong::CORS::Validations.validate_max_age(ENV['API_CORS_MAX_AGE'])
end
end
run Rails.application
end
}
def check_cors(response, origin, allow_crendentails, max_age = '3600')
expect(response.headers['Access-Control-Allow-Origin']).to eq(origin)
expect(response.headers['Access-Control-Allow-Methods']).to eq('GET, POST, DELETE, PUT, PATCH, OPTIONS, HEAD')
expect(response.headers['Access-Control-Allow-Credentials']).to eq(allow_crendentails)
expect(response.headers['Access-Control-Max-Age']).to eq(max_age)
end
def without_cors(response)
expect(response.headers['Access-Control-Allow-Origin']).to eq(nil)
expect(response.headers['Access-Control-Allow-Methods']).to eq(nil)
expect(response.headers['Access-Control-Allow-Credentials']).to eq(nil)
expect(response.headers['Access-Control-Max-Age']).to eq(nil)
end
context 'set API_CORS_ORIGINS as "*"' do
let(:origin) { '*' }
let(:allow_crendentails) { nil }
let(:max_age) { '3600' }
before do
ENV['API_CORS_ORIGINS'] = origin
ENV['API_CORS_ALLOW_CREDENTIALS'] = allow_crendentails
ENV['API_CORS_MAX_AGE'] = max_age
end
after do
ENV['API_CORS_ORIGINS'] = nil
ENV['API_CORS_ALLOW_CREDENTIALS'] = nil
ENV['API_CORS_MAX_AGE'] = nil
end
it 'sends CORS headers when requesting using GET from frontend url' do
get '/api/v2/identity/ping', headers: { 'origin' => frontend_url }
expect(response).to be_successful
check_cors(response, '*', allow_crendentails, max_age)
end
it 'sends CORS headers when requesting using GET from localhost' do
get '/api/v2/identity/ping', headers: { 'origin' => local_url }
expect(response).to be_successful
check_cors(response, '*', allow_crendentails, max_age)
end
end
context 'set multiple API_CORS_ORIGINS for frontend and localhost' do
let(:allow_crendentails) { 'true' }
let(:max_age) { '6200' }
before do
ENV['API_CORS_ORIGINS'] = "#{frontend_url},#{local_url}"
ENV['API_CORS_ALLOW_CREDENTIALS'] = allow_crendentails
ENV['API_CORS_MAX_AGE'] = max_age
end
after do
ENV['API_CORS_ORIGINS'] = nil
ENV['API_CORS_ALLOW_CREDENTIALS'] = nil
ENV['API_CORS_MAX_AGE'] = nil
end
it 'sends CORS headers when requesting using GET from frontend url' do
get '/api/v2/identity/ping', headers: { 'Origin' => frontend_url }
expect(response).to be_successful
check_cors(response, frontend_url, allow_crendentails, max_age)
end
it 'sends CORS headers when requesting using GET from localhost' do
get '/api/v2/identity/ping', headers: { 'Origin' => local_url }
expect(response).to be_successful
check_cors(response, local_url, allow_crendentails, max_age)
end
it 'doesn\'t sends CORS headers when requesting using GET from unkown domain' do
get '/api/v2/identity/ping', headers: { 'Origin' => 'http://domain.com' }
expect(response).to be_successful
without_cors(response)
end
end
context 'send invalid request' do
let(:allow_crendentails) { 'true' }
let(:max_age) { '3600' }
before do
ENV['API_CORS_ORIGINS'] = "#{frontend_url},#{local_url}"
ENV['API_CORS_ALLOW_CREDENTIALS'] = allow_crendentails
ENV['API_CORS_MAX_AGE'] = max_age
end
after do
ENV['API_CORS_ORIGINS'] = nil
ENV['API_CORS_ALLOW_CREDENTIALS'] = nil
ENV['API_CORS_MAX_AGE'] = nil
end
it 'sends CORS headers ever when user is not authenticated' do
get '/api/v2/resource/users/me', headers: { 'Origin' => local_url }
expect(response).to have_http_status 401
check_cors(response, local_url, allow_crendentails)
end
it 'sends CORS headers when invalid parameter supplied' do
get '/api/v2/resource/users/activity/topic', headers: auth_header.merge!('Origin' => local_url )
expect(response).to have_http_status 422
check_cors(response, local_url, allow_crendentails)
end
end
end

View File

@@ -0,0 +1,82 @@
# encoding: UTF-8
# frozen_string_literal: true
describe Barong::CORS::Validations do
describe 'validate origins' do
subject { Barong::CORS::Validations.validate_origins(ENV['API_CORS_ORIGINS']) }
context 'set API_CORS_ORIGINS as "*"' do
before do
ENV['API_CORS_ORIGINS'] = '*'
end
it do
is_expected.to eq('*')
end
end
context 'set mulitple API_CORS_ORIGINS with "*"' do
before do
ENV['API_CORS_ORIGINS'] = 'https://localhost,*,https://domain.com'
end
it do
is_expected.to eq('*')
end
end
context 'set multiple API_CORS_ORIGINS' do
before do
ENV['API_CORS_ORIGINS'] = 'https://localhost,https://domain.com'
end
it do
is_expected.to eq(['https://localhost','https://domain.com'])
end
end
context 'set invalid domain into API_CORS_ORIGINS' do
before do
ENV['API_CORS_ORIGINS'] = 'htt:://localhost'
end
it do
expect { subject }.to raise_error(Barong::CORS::Validations::Error)
end
end
end
describe 'validate max age' do
subject { Barong::CORS::Validations.validate_max_age(ENV['API_CORS_MAX_AGE']) }
context 'set API_CORS_MAX_AGE as "6200"' do
before do
ENV['API_CORS_MAX_AGE'] = '6200'
end
it do
is_expected.to eq('6200')
end
end
context 'set API_CORS_MAX_AGE as "6200.1"' do
before do
ENV['API_CORS_MAX_AGE'] = '6200.1'
end
it do
is_expected.to eq('3600')
end
end
context 'doesn\'t set API_CORS_MAX_AGE"' do
before do
ENV['API_CORS_MAX_AGE'] = nil
end
it do
is_expected.to eq('3600')
end
end
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
describe API::V2::Entities::Activity do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:record) { create(:activity, topic: 'otp', result: 'succeed', action: 'login') }
subject { OpenStruct.new API::V2::Entities::Activity.represent(record).serializable_hash }
it { expect(subject.topic).to eq record.topic }
it { expect(subject.result).to eq record.result }
it { expect(subject.user_ip).to eq record.user_ip }
it { expect(subject.action).to eq record.action }
it { expect(subject.data).to eq record.data }
it { expect(subject.user_agent).to eq record.user_agent }
it { expect(subject.created_at).to eq record.created_at.iso8601 }
end

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
describe API::V2::Entities::Label do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:record) { create(:label) }
subject { OpenStruct.new API::V2::Entities::Label.represent(record).serializable_hash }
it { expect(subject.key).to eq record.key }
it { expect(subject.value).to eq record.value }
it { expect(subject.scope).to eq record.scope }
it { expect(subject.created_at).to eq record.created_at.iso8601 }
it { expect(subject.updated_at).to eq record.updated_at&.iso8601 }
end

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
describe API::V2::Entities::UserWithKYC do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:record) { create(:user) }
subject { OpenStruct.new API::V2::Entities::UserWithKYC.represent(record).serializable_hash }
it { expect(subject.email).to eq record.email }
it { expect(subject.uid).to eq record.uid }
it { expect(subject.role).to eq record.role }
it { expect(subject.level).to eq record.level }
it { expect(subject.otp).to eq record.otp }
it { expect(subject.state).to eq record.state }
it { expect(subject.profiles).to eq record.profiles }
it { expect(subject.labels).to eq record.labels }
it { expect(subject.phones).to eq record.phones }
it { expect(subject.documents).to eq record.documents }
it { expect(subject.to_h.keys).not_to include(:activities) }
it { expect(subject.created_at).to eq record.created_at.iso8601 }
it { expect(subject.updated_at).to eq record.updated_at&.iso8601 }
end

View File

@@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::V2::Identity::General do
describe 'GET /api/v2/identity/time' do
it 'returns a server status' do
get '/api/v2/identity/ping'
expect_status_to_eq(200)
expect(json_body[:ping]).to eq('pong')
end
end
describe 'GET /api/v2/identity/time' do
it 'returns a current UNIX time' do
get '/api/v2/identity/time'
expect_status_to_eq(200)
expect(json_body[:time].to_i).to be <= Time.now.to_i
end
end
describe 'GET /api/v2/identity/configs' do
it 'returns some of the configurations' do
get '/api/v2/identity/configs'
expect_status_to_eq(200)
expect(json_body[:session_expire_time]).to eq(Barong::App.config.session_expire_time)
expect(json_body[:captcha_type]).to eq(Barong::App.config.captcha)
expect(json_body[:phone_verification_type]).to eq(Barong::App.config.phone_verification)
end
it 'returns all of the configurations with defaults' do
get '/api/v2/identity/configs'
expect_status_to_eq(200)
expect(json_body[:session_expire_time]).to eq(Barong::App.config.session_expire_time)
expect(json_body[:captcha_type]).to eq(Barong::App.config.captcha)
expect(json_body[:phone_verification_type]).to eq(Barong::App.config.phone_verification)
expect(json_body[:password_min_entropy]).to eq(Barong::App.config.password_min_entropy)
expect(json_body[:password_regexp]).to eq(Barong::App.config.password_regexp.to_s)
end
end
end

View File

@@ -0,0 +1,462 @@
# frozen_string_literal: true
describe API::V2::Identity::Sessions do
include_context 'geoip mock'
include ActiveSupport::Testing::TimeHelpers
let!(:create_member_permission) do
create :permission,
role: 'member',
verb: 'all'
end
before do
Rails.cache.delete('permissions')
allow(Barong::App.config).to receive_messages(captcha: 'recaptcha')
end
describe 'POST /api/v2/identity/sessions' do
before { allow(Barong::App.config).to receive_messages(captcha: 'none') }
let!(:email) { 'user@gmail.com' }
let!(:password) { 'testPassword111' }
let(:uri) { '/api/v2/identity/sessions' }
subject!(:user) do
create :user,
:with_profile,
email: email,
password: password,
password_confirmation: password
end
let(:otp_enabled) { false }
context 'With valid params' do
let(:do_request) { post uri, params: params }
let(:session_expire_time) do
Barong::App.config.session_expire_time
end
let(:check_session) do
get '/api/v2/auth/api/v2/tasty_endpoint'
end
let(:params) do
{
email: email,
password: password
}
end
context 'captcha behaviour when captcha policy is recaptcha' do
before { allow(Barong::App.config).to receive_messages(captcha: 'recaptcha') }
it 'doesnt require captcha if endpoint is not in the protection list' do
allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["user_create"]})
do_request
expect_status_to_eq 200
result = JSON.parse(response.body)
expect(result['profiles'][0]['last_name']).to eq user.profiles.first.sub_masked_last_name
expect(result['profiles'][0]['dob']).to eq user.profiles.first.sub_masked_dob
end
it 'require captcha if endpoint is in the protection list' do
allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["user_create", "session_create"]})
do_request
expect_status_to_eq 400
expect_body.to eq(errors: ["identity.captcha.required"])
end
end
it 'Check current credentials and returns session' do
do_request
expect(session.instance_variable_get(:@delegate)[:uid]).to eq(user.uid)
expect_status.to eq(200)
result = JSON.parse(response.body)
expect(result['profiles'][0]['last_name']).to eq user.profiles.first.sub_masked_last_name
expect(result['profiles'][0]['dob']).to eq user.profiles.first.sub_masked_dob
check_session
expect(response.status).to eq(200)
end
it 'Expires a session after configured time' do
do_request
travel session_expire_time + 30.minutes
check_session
expect(response.status).to eq(401)
end
let(:captcha_response) { nil }
let(:valid_response) { 'valid' }
let(:invalid_response) { 'invalid' }
before do
allow_any_instance_of(CaptchaService::RecaptchaVerifier).to receive(:verify_recaptcha)
.with(model: user,
skip_remote_ip: true,
response: valid_response) { true }
allow_any_instance_of(CaptchaService::RecaptchaVerifier).to receive(:verify_recaptcha)
.with(model: user,
skip_remote_ip: true,
response: invalid_response) { raise StandardError }
end
context 'when captcha response is blank' do
let(:params) do
{
email: email,
password: password,
captcha_response: captcha_response
}
end
end
context 'when captcha response is not valid' do
let(:params) do
{
email: email,
password: password,
captcha_response: invalid_response
}
end
before do
expect_any_instance_of(CaptchaService::RecaptchaVerifier).to receive(:verify_recaptcha) { false }
end
it 'renders an error' do
allow(Barong::App.config).to receive_messages(captcha: 'recaptcha')
do_request
expect(json_body[:errors]).to eq(["identity.captcha.verification_failed"])
expect_status_to_eq 422
end
end
context 'when captcha response is valid' do
let(:params) do
{
email: email,
password: password,
captcha_response: valid_response
}
end
before do
expect_any_instance_of(CaptchaService::RecaptchaVerifier).to receive(:verify_recaptcha) { true }
end
end
end
context 'With Invalid params' do
context 'Checks current credentials and returns error' do
it 'when email, password is missing' do
post uri
expect_body.to eq(errors: ["identity.session.missing_email", "identity.session.missing_password"])
expect(response.status).to eq(422)
end
it 'when password is missing' do
post uri, params: { email: email }
expect_body.to eq(errors: ["identity.session.missing_password"])
expect(response.status).to eq(422)
end
it 'when email is missing' do
post uri, params: { password: password }
expect_body.to eq(errors: ["identity.session.missing_email"])
expect(response.status).to eq(422)
end
it 'when email is blank' do
post uri, params: { email: '',password: password }
expect_body.to eq(errors: ["identity.session.invalid_params"])
expect(response.status).to eq(401)
end
it 'when password is blank' do
post uri, params: { email: email, password: '' }
expect_body.to eq(errors: ["identity.session.invalid_params"])
expect(response.status).to eq(401)
end
it 'when email is space' do
post uri, params: { email: ' ',password: password }
expect_body.to eq(errors: ["identity.session.invalid_params"])
expect(response.status).to eq(401)
end
it 'when password is space' do
post uri, params: { email: email, password: ' ' }
expect_body.to eq(errors: ["identity.session.invalid_params"])
expect(response.status).to eq(401)
end
context 'when Password is wrong' do
it 'returns errror' do
post uri, params: { email: email, password: 'password' }
expect_body.to eq(errors: ["identity.session.invalid_params"])
expect(response.status).to eq(401)
end
end
end
end
context 'User state related errors' do
context 'When user is banned' do
let!(:banned_email) { 'email@random.com' }
let!(:user_banned) do
create :user,
email: banned_email,
password: password,
password_confirmation: password,
state: 'banned'
end
it 'returns error on banned user' do
post uri, params: { email: banned_email, password: password }
expect_body.to eq(errors: ["identity.session.banned"])
expect(response.status).to eq(401)
end
end
let!(:pending_email) { 'pendingemail@random.com' }
let!(:user_pending) do
create :user,
email: pending_email,
password: password,
password_confirmation: password,
state: 'pending'
end
context 'Allow pending user to login by default' do
it 'returns error on non-active user' do
user_pending.update(state: 'not-active')
post uri, params: { email: pending_email, password: password }
expect_body.to eq(errors: ["identity.session.not_active"])
expect(response.status).to eq(401)
end
it 'sucessfull login for pending user' do
user_pending.update(state: 'pending')
expect(user_pending.state).to eq('pending')
post uri, params: { email: pending_email, password: password }
expect(response.status).to eq(200)
end
end
end
context 'event API behavior' do
before do
allow(EventAPI).to receive(:notify)
end
it 'receive system.session.create notify' do
allow_any_instance_of(API::V2::Utils).to receive(:remote_ip).and_return('192.168.0.1')
post uri, params: { email: email, password: password }, headers: { 'HTTP_USER_AGENT' => 'random-browser' }
expect(EventAPI).to have_received(:notify).with('system.session.create',
hash_including({ record: hash_including(user: anything, user_ip: '192.168.0.1', user_agent: 'random-browser') })
)
end
end
end
describe 'DELETE /api/v2/identity/sessions' do
before { allow(Barong::App.config).to receive_messages(captcha: 'none') }
let!(:email) { 'user@gmail.com' }
let!(:password) { 'testPassword111' }
let(:uri) { '/api/v2/identity/sessions' }
let(:params) do
{
email: email,
password: password
}
end
subject!(:user) do
create :user,
email: email,
password: password,
password_confirmation: password
end
context 'With invalid session' do
let(:do_create_session_request) { post uri, params: params }
let(:do_delete_session_request) { delete uri }
it 'receives 404 on delete session' do
do_delete_session_request
expect(response.status).to eq(404)
expect(response.body).to eq("{\"errors\":[\"identity.session.not_found\"]}")
end
end
context 'With valid session' do
let(:do_create_session_request) { post uri, params: params }
let(:do_delete_session_request) { delete uri }
it 'Deletes session' do
do_create_session_request
expect(session.instance_variable_get(:@delegate)[:uid]).to eq(user.uid)
do_delete_session_request
expect(session.instance_variable_get(:@delegate)[:uid]).to eq(nil)
end
it "return invalid set-cookie header on #logout" do
do_create_session_request
expect(session.instance_variable_get(:@delegate)[:uid]).to eq(user.uid)
do_delete_session_request
expect(response.status).to eq(200)
expect(response.headers['Set-Cookie']).not_to be_nil
expect(response.headers['Set-Cookie']).to include "barong_session"
end
end
end
# describe 'POST /api/v2/indentity/sessions/auth0' do
# let(:uri) { '/api/v2/identity/sessions/auth0' }
#
# context 'user doesnt exist' do
# context 'email verified' do
# let(:payload) do
# [
# {
# 'email': 'example@barong.io',
# 'email_verified': true,
# 'iss': 'https://domain.name/',
# 'sub': 'google-oauth2|100484476630231723',
# 'aud': 'test audience',
# 'iat': Time.now.to_i,
# 'exp': (Time.now + 5.minutes).to_i
# }.with_indifferent_access,
# {
# 'alg': 'RS256',
# 'typ': 'JWT',
# 'kid': 'ptd2123vE-G12GoDvJ8MQ'
# }
# ]
# end
#
# before do
# allow(Barong::Auth0::JWT).to receive(:verify).and_return(payload)
# end
#
# it 'create user and label' do
# expect(User.find_by(email: 'example@barong.io')).to eq nil
# post uri, params: { id_token: 'TestToken' }
#
# expect(response.status).to eq(201)
# result = JSON.parse(response.body)
# user = User.find_by(email: result['email'])
# expect(user).not_to be nil
# expect(user.level).to eq 1
# expect(user.state).to eq 'active'
# expect(user.labels.count).to eq 1
# expect(user.labels.find_by(key: 'email').value).to eq 'verified'
# end
# end
# end
#
# context 'user exists' do
# let(:payload) do
# [
# {
# 'email': 'example@barong.io',
# 'email_verified': true,
# 'iss': 'https://domain.name/',
# 'sub': 'google-oauth2|100484476630231723',
# 'aud': 'test audience',
# 'iat': Time.now.to_i,
# 'exp': (Time.now + 5.minutes).to_i
# }.with_indifferent_access,
# {
# 'alg': 'RS256',
# 'typ': 'JWT',
# 'kid': 'ptd2123vE-G12GoDvJ8MQ'
# }
# ]
# end
#
# before do
# allow(Barong::Auth0::JWT).to receive(:verify).and_return(payload)
# end
#
# let!(:user) { create(:user, email: 'example@barong.io')}
#
# it 'returns existing user with session' do
# expect(User.find_by(email: 'example@barong.io')).not_to eq nil
# post uri, params: { id_token: 'TestToken' }
#
# expect(response.status).to eq(201)
# result = JSON.parse(response.body)
# expect(result['email']).to eq user.email
# expect(result.keys).to match_array(['email','uid','role','level','otp','state','referral_uid','csrf_token','data','labels','phones','profiles','data_storages', 'created_at', 'updated_at'])
# end
# end
#
# context 'invalid params' do
# it 'without params' do
# post uri
# expect_body.to eq(errors: ['identity.session.missing_id_token', 'identity.session.empty_id_token'])
# expect(response.status).to eq(422)
# end
#
# it 'with empty param' do
# post uri, params: { id_token: '' }
# expect_body.to eq(errors: ['identity.session.empty_id_token'])
# expect(response.status).to eq(422)
# end
#
# context 'jwt expired' do
# before do
# allow(Barong::Auth0::JWT).to receive(:verify).and_raise(JWT::ExpiredSignature)
# end
#
# it 'raise an error' do
# post uri, params: { id_token: 'TestToken' }
# expect_body.to eq(errors: ['identity.session.auth0.invalid_params'])
# expect(response.status).to eq(422)
# end
# end
#
# context 'email is not verified' do
# let(:payload) do
# [
# {
# 'email': 'example@barong.io',
# 'email_verified': false,
# 'iss': 'https://domain.name/',
# 'sub': 'google-oauth2|100484476630231723',
# 'aud': 'test audience',
# 'iat': Time.now.to_i,
# 'exp': (Time.now + 5.minutes).to_i
# }.with_indifferent_access,
# {
# 'alg': 'RS256',
# 'typ': 'JWT',
# 'kid': 'ptd2123vE-G12GoDvJ8MQ'
# }
# ]
# end
#
# before do
# allow(Barong::Auth0::JWT).to receive(:verify).and_return(payload)
# end
#
# it 'doesnt create user and label' do
# expect(User.find_by(email: 'example@barong.io')).to eq nil
# post uri, params: { id_token: 'TestToken' }
#
# expect(User.find_by(email: 'example@barong.io')).to eq nil
# expect_body.to eq(errors: ['identity.session.auth0.invalid_params'])
# expect(response.status).to eq(401)
# end
# end
# end
# end
end

View File

@@ -0,0 +1,717 @@
# frozen_string_literal: true
require 'spec_helper'
include ActiveSupport::Testing::TimeHelpers
describe API::V2::Identity::Users do
include_context 'geoip mock'
before do
allow(Barong::App.config).to receive_messages(first_registration_superadmin: false)
end
let!(:create_member_permission) do
create :permission,
role: 'member',
verb: 'all'
create :permission,
role: 'superadmin',
verb: 'all'
create :permission,
role: 'member',
verb: 'all',
path: 'tasty_endpoint'
end
describe 'POST /api/v2/identity/users/access' do
context 'success' do
before do
allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return('192.168.0.2')
allow(Rails.cache).to receive(:read).and_return('active')
end
it 'creates a restriction in database with my ip' do
expect {
post '/api/v2/identity/users/access', params: { whitelink_token: 'testtoken' }
}.to change { Restriction.count }.by(1)
expect(response.status).to eq(201)
end
it 'works only first time' do
expect {
post '/api/v2/identity/users/access', params: { whitelink_token: 'testtoken' }
}.to change { Restriction.count }.by(1)
expect(response.status).to eq(201)
post '/api/v2/identity/users/access', params: { whitelink_token: 'testtoken' }
expect(response.status).to eq(422)
expect(json_body[:errors]).to include "value.taken"
end
end
context 'returns error' do
it 'if token is missing' do
post '/api/v2/identity/users/access'
expect(response.status).to eq(422)
expect(json_body[:errors]).to include "identity.user.missing_whitelink_token"
end
it 'if incorrect whitelink_token' do
post '/api/v2/identity/users/access', params: { whitelink_token: 'testtoken' }
expect(response.status).to eq(422)
expect(json_body[:errors]).to include "identity.user.access.invalid_token"
end
end
end
describe 'POST /api/v2/identity/users with default Barong::App.config.captcha' do
let(:do_request) { post '/api/v2/identity/users', params: params }
context 'when email is invalid' do
let(:params) { { email: 'bad_format', password: 'Password1' } }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["email.invalid", "password.weak"])
end
end
context 'when referral is unexist' do
let(:params) { { email: 'valid.email@gmail.com', password: 'Password1', refid: 'ID1231231231' } }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.referral_doesnt_exist"])
end
end
context 'when referral id is invalid' do
let(:params) { { email: 'valid.email@gmail.com', password: 'Password1', refid: 'UID123' } }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.invalid_referral_format"])
end
end
context 'when Password is invalid' do
let(:params) { { email: 'vadid.email@gmail.com', password: 'password' } }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["password.requirements"])
end
end
context 'when email and password are absent' do
let(:params) {}
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.missing_email", "identity.user.empty_email", "identity.user.missing_password", "identity.user.empty_password"])
end
end
context 'when email is blank' do
let(:params) { { email: '', password: 'zieV0Kai' } }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.empty_email"])
end
end
context 'when email is valid' do
let(:params) { { email: 'valid.email@gmail.com', password: 'eeC2BiCucxWEQ' } }
it 'creates an account' do
do_request
expect_status_to_eq 201
end
context 'first user registration' do
before do
allow(Barong::App.config).to receive_messages(first_registration_superadmin: true)
end
it 'creates superadmin user' do
post '/api/v2/identity/users', params: params
expect(response.status).to eq(201)
expect(json_body.keys).to match_array %i[email uid role level otp state referral_uid csrf_token data created_at updated_at labels phones profiles data_storages]
expect(json_body[:email]).to eq 'valid.email@gmail.com'
expect(json_body[:level]).to eq 1
expect(json_body[:role]).to eq 'superadmin'
expect(json_body[:state]).to eq 'active'
expect(json_body[:labels].count).to eq 1
expect(json_body[:labels][0][:key]).to eq 'email'
expect(json_body[:labels][0][:value]).to eq 'verified'
expect(json_body[:labels][0][:scope]).to eq 'private'
end
end
end
end
describe 'POST /api/v2/identity/users with reCAPTCHA Barong::App.config.captcha' do
before { allow(Barong::App.config).to receive_messages(captcha: 'recaptcha') }
let(:do_request_with_captcha) { post '/api/v2/identity/users', params: params_with_captcha }
let(:params_with_captcha) { { email: 'vadid.email@gmail.com', password: 'eeC2BiCucxWEQ', captcha_response: 'response' } }
let(:do_request) { post '/api/v2/identity/users', params: params }
let(:params) { { email: 'vadid.email@gmail.com', password: 'eeC2BiCucxWEQ' } }
context 'when reCAPTCHA is valid' do
it 'creates an account' do
allow_any_instance_of(CaptchaService::RecaptchaVerifier).to receive(:verify_recaptcha) { true }
do_request_with_captcha
expect_status_to_eq 201
end
it 'doesnt require captcha if endpoint is not in the protection list' do
allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["session_create"]})
do_request
expect_status_to_eq 201
end
it 'doesnt require captcha if protection list is empty' do
allow(BarongConfig).to receive(:list).and_return({})
do_request
expect_status_to_eq 201
end
it 'require captcha if endpoint is in the protection list' do
allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["user_create", "session_create"]})
do_request
expect_status_to_eq 400
expect_body.to eq(errors: ["identity.captcha.required"])
end
end
context 'when reCAPTCHA is invalid' do
before { allow_any_instance_of(CaptchaService::RecaptchaVerifier).to receive(:verify_recaptcha) { false } }
it 'renders an error' do
do_request_with_captcha
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.captcha.verification_failed"])
end
end
context 'when captcha_response is blank but Barong::App.config.captcha requires reCAPTCHA response' do
let(:params) { { email: 'vadid.email@gmail.com', password: 'eeC2BiCucxWEQ' } }
it 'renders an error' do
do_request
expect_status_to_eq 400
expect_body.to eq(errors: ["identity.captcha.required"])
end
end
end
describe 'POST /api/v2/identity/users with GeeTest Barong::App.config.captcha' do
before { allow(Barong::App.config).to receive_messages(captcha: 'geetest') }
let(:do_request) { post '/api/v2/identity/users', params: params }
let(:params) do
{ email: 'vadid.email@gmail.com', password: 'eeC2BiCucxWEQ',
captcha_response: { geetest_challenge: 'challenge',
geetest_validate: 'validate',
geetest_seccode: 'seccode' } }
end
context 'when GeeTest is valid' do
before { allow_any_instance_of(CaptchaService::GeetestVerifier).to receive(:validate) { true } }
it 'creates an account' do
do_request
expect_status_to_eq 201
end
end
context 'when GeeTest is invalid' do
before { allow_any_instance_of(CaptchaService::GeetestVerifier).to receive(:validate) { false } }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.captcha.verification_failed"])
end
end
context 'when captcha_response is blank but Barong::App.config.captcha requires Geetest response' do
let(:params) { { email: 'vadid.email@gmail.com', password: 'eeC2BiCucxWEQ' } }
it 'renders an error' do
do_request
expect_status_to_eq 400
expect_body.to eq(errors: ["identity.captcha.required"])
end
end
context 'when captcha_response has incorrect format' do
let(:params) do
{ email: 'vadid.email@gmail.com', password: 'eeC2BiCucxWEQ',
captcha_response: { empty: 'string' } }
end
it 'renders an error' do
do_request
expect_status_to_eq 400
expect_body.to eq(errors: ["identity.captcha.mandatory_fields"])
end
end
end
describe 'POST /api/v2/identity/users with data field' do
let(:do_request) { post '/api/v2/identity/users', params: params }
let(:params) do
{ email: 'vadid.email@gmail.com', password: 'eeC2BiCucxWEQ',
data: data }
end
context 'when data is not json compatible' do
let(:data) { 'phone_number: 380969999999' }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["data.invalid_format"])
end
end
context 'valid data' do
let(:data) { "{\"phone_number\":\"380969999999\"}" }
it 'creates user' do
do_request
expect_status_to_eq 201
end
end
end
describe 'session opening on /api/v2/identity/users' do
before do
Rails.cache.delete('permissions')
end
let(:email) { 'valid@email.com' }
let(:do_request) { post '/api/v2/identity/users', params: params }
let(:params) { { email: email, password: 'Tecohvi0' } }
let(:session_expire_time) do
Barong::App.config.session_expire_time
end
let(:check_session) do
get '/api/v2/auth/tasty_endpoint'
end
it 'Check current credentials and returns session' do
do_request
user = User.find_by(email: email)
expect(user).not_to be(nil)
expect(session.instance_variable_get(:@delegate)[:uid]).to eq(user.uid)
expect_status.to eq(201)
check_session
expect(response.status).to eq(200)
end
end
describe 'POST /api/v2/identity/users/email/generate_code' do
before { allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["user_create", "session_create"]}) }
let(:params) { { email: 'invalid@email.com' } }
let(:do_request) { post '/api/v2/identity/users/email/generate_code', params: params }
context 'when user is invalid' do
it 'doesnt render an error to prevent user enumeration' do
do_request
expect_status_to_eq 201
end
end
let(:params) { { email: 'valid-confirmed@email.com' } }
context 'when user is valid, email confirmed' do
it 'doesnt render an error to prevent user enumeration' do
create(:user, email: 'valid-confirmed@email.com', state: 'active')
do_request
expect_status_to_eq 201
end
end
context 'when user is valid' do
let(:user) { create(:user, state: 'pending') }
let(:params) { { email: user.email } }
it 'returns a success' do
do_request
expect_status_to_eq 201
end
end
context 'captcha behaviour when captcha policy is recaptcha' do
let(:user) { create(:user, state: 'pending') }
let(:params) { { email: user.email } }
before { allow(Barong::App.config).to receive_messages(captcha: 'recaptcha') }
it 'doesnt require captcha if endpoint is not in the protection list' do
allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["user_create", "session_create"]})
do_request
expect_status_to_eq 201
end
it 'require captcha if endpoint is in the protection list' do
allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["user_create", "session_create", "email_confirmation"]})
do_request
expect_status_to_eq 400
expect_body.to eq(errors: ["identity.captcha.required"])
end
end
end
describe 'session opening on /api/v2/identity/users/email/confirm_code' do
before do
Rails.cache.delete('permissions')
end
let(:user) { create(:user, state: 'pending', email: 'valid_email@email.com') }
let(:do_request) { post '/api/v2/identity/users/email/confirm_code', params: params }
let(:params) { { token: codec.encode(sub: 'confirmation', email: user.email, uid: user.uid) } }
let(:session_expire_time) do
Barong::App.config.session_expire_time
end
let(:check_session) do
get '/api/v2/auth/tasty_endpoint'
end
it 'Gives label email verified and opens a session' do
do_request
expect(user).not_to be(nil)
expect(session.instance_variable_get(:@delegate)[:uid]).to eq(user.uid)
expect_status.to eq(201)
check_session
expect(response.status).to eq(200)
end
end
describe 'POST /api/v2/identity/users/email/confirm_code' do
let(:do_request) { post '/api/v2/identity/users/email/confirm_code', params: params }
let(:params) { {} }
context 'when token is missing' do
it 'returns an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.missing_token", "identity.user.empty_token"])
end
end
context 'when token is invalid' do
let(:params) { { token: 'invalid token' } }
it 'returns an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["jwt.decode_and_verify.segments"])
end
end
context 'when token is valid' do
let(:user) { create(:user, :with_profile, state: 'pending', email: 'valid_email@email.com') }
let(:params) { { token: codec.encode(sub: 'confirmation', email: user.email, uid: user.uid) } }
it 'updates state to active' do
do_request
expect_status_to_eq 201
result = JSON.parse(response.body)
expect(result['profiles'][0]['last_name']).to eq user.profiles.first.sub_masked_last_name
expect(result['profiles'][0]['dob']).to eq user.profiles.first.sub_masked_dob
end
it 'returns utilized on the second attempt' do
token_params = params
post '/api/v2/identity/users/email/confirm_code', params: token_params
expect_status_to_eq 201
result = JSON.parse(response.body)
expect(result['profiles'][0]['last_name']).to eq user.profiles.first.sub_masked_last_name
expect(result['profiles'][0]['dob']).to eq user.profiles.first.sub_masked_dob
user.reload.update(state: 'pending')
post '/api/v2/identity/users/email/confirm_code', params: token_params
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.utilized_token"])
end
it 'returns expired error on second attempt after lifetime' do
token_params = params
post '/api/v2/identity/users/email/confirm_code', params: token_params
expect_status_to_eq 201
result = JSON.parse(response.body)
expect(result['profiles'][0]['last_name']).to eq user.profiles.first.sub_masked_last_name
expect(result['profiles'][0]['dob']).to eq user.profiles.first.sub_masked_dob
user.reload.update(state: 'pending')
travel Barong::App.config.jwt_expire_time + 10.seconds
post '/api/v2/identity/users/email/confirm_code', params: token_params
expect_status_to_eq 422
expect_body.to eq(errors: ["jwt.decode_and_verify.expired"])
end
end
end
describe 'POST /api/v2/identity/users/password/generate_code' do
let(:do_request) do
post '/api/v2/identity/users/password/generate_code', params: params
end
let(:params) { { email: email } }
context 'when email is unknown' do
let(:email) { 'unknown@gmail.com' }
it 'renders 201' do
do_request
expect_status_to_eq 201
expect_body.not_to eq(errors: ["identity.password.user_doesnt_exist"])
end
end
context 'when user is found by email' do
let!(:user) { create(:user, email: email) }
let(:email) { 'email@gmail.com' }
it 'sends reset password instructions' do
do_request
expect_status_to_eq 201
end
end
context 'multiple password reset requests' do
let!(:user) { create(:user, email: email) }
let(:email) { 'email@gmail.com' }
let(:password) { 'ZahSh8ei' }
let(:confirm_password) { 'ZahSh8ei' }
let(:log_in) { post '/api/v2/identity/sessions', params: { email: user.email, password: password } }
it 'works with last email' do
post '/api/v2/identity/users/password/generate_code', params: params
expect_status_to_eq 201
post '/api/v2/identity/users/password/generate_code', params: params
expect_status_to_eq 201
reset_token = Rails.cache.read("reset_password_#{user.email}")
reset_password_token = codec.encode(sub: 'reset', email: user.email, uid: user.uid, reset_token: reset_token)
post '/api/v2/identity/users/password/confirm_code', params: {
reset_password_token: reset_password_token,
password: password,
confirm_password: confirm_password
}
expect_status_to_eq 201
log_in
expect_status_to_eq 200
end
it 'returns error if prev link (non-utilized) is used' do
post '/api/v2/identity/users/password/generate_code', params: params
reset_token = Rails.cache.read("reset_password_#{user.email}")
expect_status_to_eq 201
post '/api/v2/identity/users/password/generate_code', params: params
expect_status_to_eq 201
reset_password_token = codec.encode(sub: 'reset', email: user.email, uid: user.uid, reset_token: reset_token)
post '/api/v2/identity/users/password/confirm_code', params: {
reset_password_token: reset_password_token,
password: password,
confirm_password: confirm_password
}
expect_status_to_eq 422
expect(response.body).to eq("{\"errors\":[\"identity.user.utilized_token\"]}")
end
it 'expires utilized token after lifetime but still returns error' do
post '/api/v2/identity/users/password/generate_code', params: params
reset_token = Rails.cache.read("reset_password_#{user.email}")
expect_status_to_eq 201
post '/api/v2/identity/users/password/generate_code', params: params
expect_status_to_eq 201
reset_password_token = codec.encode(sub: 'reset', email: user.email, uid: user.uid, reset_token: reset_token)
travel Barong::App.config.jwt_expire_time + 10.seconds
post '/api/v2/identity/users/password/confirm_code', params: {
reset_password_token: reset_password_token,
password: password,
confirm_password: confirm_password
}
expect_status_to_eq 422
expect(response.body).to eq("{\"errors\":[\"jwt.decode_and_verify.expired\"]}")
end
end
context 'captcha behaviour when captcha policy is recaptcha' do
let!(:user) { create(:user, email: email) }
let(:email) { 'email@gmail.com' }
before { allow(Barong::App.config).to receive_messages(captcha: 'recaptcha') }
it 'doesnt require captcha if endpoint is not in the protection list' do
allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["user_create", "session_create"]})
do_request
expect_status_to_eq 201
end
it 'require captcha if endpoint is in the protection list' do
allow(BarongConfig).to receive(:list).and_return({"captcha_protected_endpoints"=>["user_create", "session_create", "password_reset"]})
do_request
expect_status_to_eq 400
expect_body.to eq(errors: ["identity.captcha.required"])
end
end
end
describe 'PUT /api/v2/identity/users/password/confirm_code' do
let(:do_request) do
post '/api/v2/identity/users/password/confirm_code', params: params
end
let(:params) do
{
reset_password_token: reset_password_token,
password: password,
confirm_password: confirm_password
}
end
let(:reset_password_token) { '' }
let(:password) { '' }
let(:confirm_password) { '' }
context 'when params are blank' do
it 'renders 422 error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.empty_reset_password_token", "identity.user.empty_password", "identity.user.empty_confirm_password"])
end
end
context 'when Reset Password Token is invalid' do
let(:reset_password_token) { 'invalid' }
let(:password) { 'Gol4aid2' }
let(:confirm_password) { 'Gol4aid2' }
it 'renders 422 error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["jwt.decode_and_verify.segments"])
end
end
context 'when Reset Password Token and Password are valid ' do
let!(:user) { create(:user) }
let(:reset_password_token) { codec.encode(sub: 'reset', email: user.email, uid: user.uid) }
let(:password) { 'ZahSh8ei' }
let(:confirm_password) { 'ZahSh8ei' }
let(:log_in) { post '/api/v2/identity/sessions', params: { email: user.email, password: password } }
it 'resets a password' do
do_request
expect_status_to_eq 201
log_in
expect_status_to_eq 200
end
end
context 'When Reset Password Token is valid, passwords are weak' do
let!(:user) { create(:user) }
let(:reset_password_token) { codec.encode(sub:'reset', email: user.email, uid: user.uid) }
let(:password) { 'Simple' }
let(:confirm_password) { 'Simple' }
it 'returns weak password error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["password.requirements"])
end
end
context 'When Reset Password Token is valid, passwords don\'t match' do
let!(:user) { create(:user) }
let(:reset_password_token) { codec.encode(sub: 'reset', email: user.email, uid: user.uid) }
let(:password) { 'ZahSh8exwdi' }
let(:confirm_password) { 'ZahSh8ei' }
it 'returns 422 error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.passwords_doesnt_match"])
end
end
end
describe 'POST /api/v2/identity/users/email/confirm_email' do
let(:test_user) { create(:user, state: 'pending', email: 'valid.email@gmail.com') }
let(:do_request) { post '/api/v2/identity/users/email/confirm_email', params: params }
let(:params) { {} }
let(:valid_otp_code) { '1357' }
let(:invalid_otp_code) { '1234' }
before do
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, valid_otp_code) { true }
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, invalid_otp_code) { false }
end
context 'when token is missing' do
it 'returns an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.missing_code", "identity.user.empty_code",
"identity.user.missing_email", "identity.user.empty_email"])
end
end
context 'when code is invalid' do
let(:params) { { email: test_user.email, code: invalid_otp_code } }
it 'returns an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["identity.user.active_or_doesnt_exist"])
end
end
context 'when code is valid' do
let(:params) { { email: test_user.email, code: valid_otp_code } }
it 'returns an error' do
do_request
expect_status_to_eq 201
expect(json_body.keys).to match_array %i[email uid role level otp state referral_uid csrf_token data created_at updated_at labels phones profiles data_storages]
expect(json_body[:email]).to eq 'valid.email@gmail.com'
expect(json_body[:state]).to eq 'active'
end
end
end
end

View File

@@ -0,0 +1,374 @@
# frozen_string_literal: true
describe API::V2::Management::APIKeys, type: :request do
before do
defaults_for_management_api_v2_security_configuration!
management_api_v2_security_configuration.merge! \
scopes: {
write_apikeys: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
}
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:create_service_account_permission) do
create :permission,
role: 'service_account'
end
describe 'POST /api_keys' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_apikeys) }
let(:do_request) do
post_json '/api/v2/management/api_keys',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
let(:params) do
api_keys_params
end
context 'valid request' do
context 'service account' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_sa: true)
end
let!(:service_account) { create(:service_account) }
let(:api_keys_params) do
{
uid: service_account.uid,
algorithm: 'HS256',
scopes: 'trade'
}
end
it 'creates a api key' do
do_request
expect(response.status).to eq(201)
result = JSON.parse(response.body)
expect(result.keys).to match_array %w[kid algorithm scope state secret created_at updated_at]
expect(result['state']).to eq service_account.state
expect(result['scope']).to eq ['trade']
expect(result['algorithm']).to eq 'HS256'
end
end
context 'user' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_user: true)
end
let!(:user) { create(:user) }
let(:api_keys_params) do
{
uid: user.uid,
algorithm: 'HS256',
scopes: 'trade'
}
end
it 'creates a api key' do
do_request
expect(response.status).to eq(201)
result = JSON.parse(response.body)
expect(result.keys).to match_array %w[kid algorithm scope state secret created_at updated_at]
expect(result['state']).to eq user.state
expect(result['scope']).to eq ['trade']
expect(result['algorithm']).to eq 'HS256'
end
end
end
context 'invalid request' do
context 'vault inaccessible' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_sa: true)
end
let!(:service_account) { create(:service_account) }
let(:api_keys_params) do
{
uid: service_account.uid,
algorithm: 'HS256',
scopes: 'trade'
}
end
it 'should raise an error' do
allow(Vault::Rails).to receive(:encrypt).and_raise(Vault::VaultError)
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["api_key.could_not_save_secret"])
end
end
context 'service account doesnt exist' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_sa: true)
end
let(:api_keys_params) do
{
uid: ServiceAccount::UID_PREFIX + 'random',
algorithm: 'HS256',
scopes: 'trade'
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'service_account_doesnt_exist')
end
end
context 'user doesnt exists' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_user: true)
end
let(:api_keys_params) do
{
uid: UIDGenerator.generate(Barong::App.config.uid_prefix),
algorithm: 'HS256',
scopes: 'trade'
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'user_doesnt_exist')
end
end
context 'algorithm is invalid' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_sa: true)
end
let!(:service_account) { create(:service_account) }
let(:api_keys_params) do
{
uid: service_account.uid,
algorithm: 'random',
scopes: 'trade'
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ['kid.blank', 'algorithm.inclusion'])
end
end
context 'disabled endpoint' do
let(:api_keys_params) do
{
uid: UIDGenerator.generate(Barong::App.config.uid_prefix),
algorithm: 'HS256',
scopes: 'trade'
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'disabled_management_endpoint')
end
end
context 'unexisting uid' do
let(:api_keys_params) do
{
uid: 'random',
algorithm: 'HS256',
scopes: 'trade'
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'uid_prefix_doesnt_exist')
end
end
end
end
describe 'POST /api_keys/update' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_apikeys) }
let(:do_request) do
post_json '/api/v2/management/api_keys/update',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
let(:params) do
api_keys_params
end
let!(:api_key) { create(:api_key, :with_service_account) }
context 'valid request' do
context 'user' do
let!(:api_key) { create(:api_key, :with_user) }
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_user: true)
end
let(:params) do
{
uid: api_key.key_holder_account.uid,
kid: api_key.kid,
state: 'inactive',
scopes: 'sell'
}
end
it 'updates api key' do
do_request
expect(response.status).to eq(201)
result = JSON.parse(response.body)
expect(result.keys).to match_array %w[kid algorithm scope state created_at updated_at]
expect(result['state']).to eq 'inactive'
expect(result['scope']).to eq ['sell']
end
end
context 'service account' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_sa: true)
end
let(:params) do
{
uid: api_key.key_holder_account.uid,
kid: api_key.kid,
state: 'inactive',
scopes: 'sell'
}
end
it 'updates api key' do
do_request
expect(response.status).to eq(201)
result = JSON.parse(response.body)
expect(result.keys).to match_array %w[kid algorithm scope state created_at updated_at]
expect(result['state']).to eq 'inactive'
expect(result['scope']).to eq ['sell']
end
end
end
context 'invalid request' do
context 'api keys doesnt exist' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_sa: true)
end
let(:api_keys_params) do
{
uid: api_key.key_holder_account.uid,
kid: 'random kid'
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'api_key_doesnt_exist')
end
end
context 'service account doesnt exist' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_sa: true)
end
let(:api_keys_params) do
{
uid: ServiceAccount::UID_PREFIX + 'random',
kid: api_key.kid
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'service_account_doesnt_exist')
end
end
context 'user doesnt exist' do
before do
allow(Barong::App.config).to receive_messages(mgn_api_keys_user: true)
end
let(:api_keys_params) do
{
uid: UIDGenerator.generate(Barong::App.config.uid_prefix),
kid: api_key.kid
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'user_doesnt_exist')
end
end
context 'disabled endpoint' do
let(:api_keys_params) do
{
uid: UIDGenerator.generate(Barong::App.config.uid_prefix),
kid: api_key.kid
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'disabled_management_endpoint')
end
end
context 'unexisting uid' do
let(:api_keys_params) do
{
uid: 'random',
kid: api_key.kid
}
end
it 'should raise an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(error: 'uid_prefix_doesnt_exist')
end
end
end
end
end

View File

@@ -0,0 +1,101 @@
# frozen_string_literal: true
require 'rails_helper'
describe API::V2::Management::Documents, type: :request do
before do
defaults_for_management_api_v2_security_configuration!
management_api_v2_security_configuration.merge! \
scopes: {
write_documents: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
}
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user, :with_profile) }
describe 'Show user info' do
let(:data) do
{
scope: :write_documents
}
end
let!(:image) { fixture_file_upload('/files/documents_test.jpg', 'image/jpg') }
let(:signers) { %i[alex jeff] }
let(:params) do
{
doc_type: 'Passport',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
upload: [
image
]
}
end
let!(:optional_params) do
{
metadata: {
country: Faker::Address.country
}
}
end
let(:do_request) do
post_json '/api/v2/management/documents',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
let(:params) do {
uid: user.uid,
doc_type: 'Passport',
filename: 'documents_test',
file_ext: '.jpg',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
upload: Base64.strict_encode64(File.open('spec/fixtures/files/documents_test.jpg').read)
}
end
it 'push documents to user' do
post_json '/api/v2/management/documents', multisig_jwt_management_api_v2({ data: params }, *signers)
expect(response.status).to eq 201
expect(user.documents.length).to eq(1)
end
it 'push documents to user twice' do
post_json '/api/v2/management/documents', multisig_jwt_management_api_v2({ data: params }, *signers)
expect(response.status).to eq 201
expect(user.documents.length).to eq(1)
post_json '/api/v2/management/documents', multisig_jwt_management_api_v2({ data: params }, *signers)
expect(response.status).to eq 201
expect(user.documents.length).to eq(1)
end
context 'with empty doc_expire' do
it 'required doc expire' do
allow(Barong::App.config).to receive(:required_docs_expire).and_return(true)
expect {
post_json '/api/v2/management/documents', multisig_jwt_management_api_v2({ data: params.merge(doc_expire: nil) }, *signers)
}.not_to change { Document.count }
expect(response.status).to eq 422
end
it 'optional doc expire' do
allow(Barong::App.config).to receive(:required_docs_expire).and_return(false)
expect {
post_json '/api/v2/management/documents', multisig_jwt_management_api_v2({ data: params.merge(doc_expire: nil) }, *signers)
}.to change { Document.count }.by 1
expect(response.status).to eq 201
end
end
end
end

View File

@@ -0,0 +1,115 @@
# frozen_string_literal: true
require 'rails_helper'
describe API::V2::Management::JWTAuthenticationMiddleware, type: :request do
let(:member) { create(:member, :verified_identity) }
let(:config) { management_api_v2_security_configuration }
before do
defaults_for_management_api_v2_security_configuration!
config.merge! \
scopes: {
tools: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] }
}
end
it 'works in standard conditions' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({}, :alex)
expect(response).to be_successful
end
# FIXME fix swagger_doc
# it 'allows GET and doesn\'t require authentication for documentation' do
# get '/api/v2/management/swagger_doc'
# expect(response).to be_successful
# end
it 'doesn\'t allow query parameters' do
post '/api/v2/management/timestamp?foo=baz&baz=qux'
expect(response).to have_http_status(400)
expect(response.body).to match(/query parameters/i)
end
it 'requires JSON in the request body' do
post '/api/v2/management/timestamp', params: 'foo=baz&baz=qux'
expect(response).to have_http_status(400)
expect(response.body).to match(/only json/i)
end
it 'denies access when not enough signatures are supplied' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({})
expect(response).to have_http_status(401)
expect(response.body).to match(/not enough signatures/i)
end
it 'denies access when token is expired' do
config[:jwt][:verify_expiration] = true
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({ exp: 1.minute.ago.to_i }, :alex)
expect(response).to have_http_status(401)
expect(response.body).to match(/failed to verify jwt/i)
end
context 'valid issuer' do
before { config[:jwt][:verify_iss] = true }
before { config[:jwt].merge!(iss: 'qux') }
it 'validates issuer' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({ iss: 'qux' }, :alex)
expect(response).to be_successful
end
end
context 'invalid issuer' do
before { config[:jwt][:verify_iss] = true }
before { config[:jwt].merge!(iss: 'qux') }
it 'validates issuer' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({ iss: 'hacker' }, :alex)
expect(response).to have_http_status(401)
expect(response.body).to match(/failed to verify jwt/i)
end
end
context 'valid audience' do
before { config[:jwt][:verify_aud] = true }
before { config[:jwt].merge!(aud: 'qux') }
it 'validates audience' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({ aud: 'qux' }, :alex)
expect(response).to be_successful
end
end
context 'invalid audience' do
before { config[:jwt][:verify_aud] = true }
before { config[:jwt].merge!(aud: 'qux') }
it 'validates audience' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({ aud: 'hacker' }, :alex)
expect(response).to have_http_status(401)
expect(response.body).to match(/failed to verify jwt/i)
end
end
context 'missing JWT ID' do
before { config[:jwt][:verify_jti] = true }
it 'requires JTI' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({}, :alex)
expect(response).to have_http_status(401)
expect(response.body).to match(/failed to verify jwt/i)
end
end
context 'issued at in future' do
before { config[:jwt][:verify_iat] = true }
it 'denies access' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({ iat: 200.seconds.from_now.to_i }, :alex)
expect(response).to have_http_status(401)
expect(response.body).to match(/failed to verify jwt/i)
end
end
context 'issued at before future' do
before { config[:jwt][:verify_iat] = true }
it 'allows access' do
post_json '/api/v2/management/timestamp', multisig_jwt_management_api_v2({ iat: 3.seconds.ago.to_i }, :alex)
expect(response).to have_http_status(200)
end
end
end

View File

@@ -0,0 +1,304 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pry'
describe API::V2::Management::Labels, type: :request do
before do
defaults_for_management_api_v2_security_configuration!
management_api_v2_security_configuration.merge! \
scopes: {
write_labels: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
read_labels: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
read_users: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] }
}
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user) }
let!(:user_with_private_label) { create(:user) }
let!(:user_with_public_label) { create(:user) }
describe '#filter/users' do
let!(:private_label) do
create(:label, scope: 'private', key: 'profile', value: 'pending', user: user_with_private_label)
end
let!(:public_label) do
create(:label, scope: 'public', key: 'profile', value: 'pending', user: user_with_public_label)
end
let(:data) { { key: 'profile', value: 'pending', scope: 'private' } }
let(:signers) { %i[alex] }
let(:do_request) do
post_json '/api/v2/management/labels/filter/users',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
it 'filters users based on label attributes. doesnt mix PUBLIC and PRIVATE labels' do
# filtering params key: 'profile', value: 'pending', scope: 'private'
do_request
expect(json_body.count == 1)
expect(json_body.first[:email]).to eq(user_with_private_label.attributes.symbolize_keys[:email])
expect(json_body.first[:uid]).to eq(user_with_private_label.attributes.symbolize_keys[:uid])
end
it 'filters users based on label timestamp attributes' do
# filtering params key: 'profile', to: 1.day.ago
data.clear
data[:to] = 1.day.ago.to_i
data[:key] = 'profile'
public_label.update(created_at: 3.days.ago)
do_request
expect(json_body.count == 1)
expect(json_body.first[:email]).to eq(user_with_public_label.attributes.symbolize_keys[:email])
expect(json_body.first[:uid]).to eq(user_with_public_label.attributes.symbolize_keys[:uid])
end
it 'returns empty array if no mathes found' do
# filtering params key: 'documents', value: 'pending', scope: 'private'
data[:key] = 'documents'
do_request
expect(json_body.count == 0)
expect(json_body).to eq([])
end
it 'returns user with profile if extended is true' do
data[:extended] = true
do_request
expect(json_body.first).to include(:profiles)
end
end
describe '#list' do
let!(:private_label) do
create(:label, scope: 'private', user: user)
end
let!(:public_label) do
create(:label, scope: 'public', user: user)
end
let(:data) do
{
user_uid: user.uid
}
end
let(:signers) { %i[alex] }
let(:do_request) do
post_json '/api/v2/management/labels/list',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
it 'get list of user labels' do
do_request
expect(response.status).to eq 201
expect(json_body.size).to eq 2
end
end
describe 'create label' do
let(:data) do
{
user_uid: user.uid,
key: 'email',
value: 'verified',
scope: 'private'
}
end
let(:expected_attributes) do
{
'key' => 'email',
'value' => 'verified',
'user_id' => user.id,
'scope' => 'private'
}
end
let(:signers) { %i[alex jeff] }
let(:do_request) do
post_json '/api/v2/management/labels',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
it 'creates a label' do
expect { do_request }.to change { Label.count }.from(0).to(1)
expect(response.status).to eq 201
expect(Label.first.attributes).to include(expected_attributes)
end
it 'denies access unless enough signatures are supplied' do
signers.clear.concat %i[james jeff]
expect { do_request }.to_not change { Label.count }
expect(response.status).to eq 401
end
it 'denies when user is not found' do
data[:user_uid] = 'invalid'
expect { do_request }.to_not change { Label.count }
expect(response.status).to eq 404
end
context 'when data is blank' do
let(:data) { {} }
it 'renders errors' do
do_request
expect(response.status).to eq 422
expect_body.to eq(error: 'user_uid is missing, user_uid is empty, key is missing, key is empty, value is missing, value is empty')
end
end
end
describe 'update label' do
let!(:label) do
create(:label, key: 'email', value: 'verified', scope: 'private', description: 'initial label', user: user)
end
let(:data) do
{
user_uid: user.uid,
key: 'email',
value: 'rejected',
description: 'management api label update test'
}
end
let(:signers) { %i[alex jeff] }
let(:do_request) do
put_json '/api/v2/management/labels',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
it 'updates a label value' do
expect { do_request }.to change { label.reload.value }.from('verified').to('rejected')
expect(response.status).to eq 200
end
it 'updates a label value' do
expect { do_request }.to change { label.reload.description }.from('initial label').to('management api label update test')
expect(response.status).to eq 200
end
it 'denies access unless enough signatures are supplied' do
signers.clear.concat %i[james jeff]
expect { do_request }.to_not change { label.reload.value }
expect(response.status).to eq 401
end
it 'denies when user is not found' do
data[:user_uid] = 'invalid'
expect { do_request }.to_not change { label.reload.value }
expect(response.status).to eq 404
end
context 'when data is blank' do
let(:data) { {} }
it 'renders errors' do
do_request
expect(response.status).to eq 422
expect_body.to eq(error: 'user_uid is missing, user_uid is empty, key is missing, key is empty, value is missing, value is empty')
end
end
context 'when label doesnt exist yet with default replace policy' do
let(:data) do
{
user_uid: user.uid,
key: 'phone',
value: 'verified'
}
end
it 'creates a label' do
expect(user.labels.find_by(key: 'phone')).to eq(nil)
do_request
expect(response.status).to eq 200
expect(user.labels.find_by(key: 'phone')).not_to eq(nil)
end
context 'when data is incomplete' do
let(:data) do
{
user_uid: user.uid,
key: 'phone'
}
end
it 'receive an error' do
expect(user.labels.find_by(key: 'phone')).to eq(nil)
do_request
expect(response.status).to eq 422
expect(response.body).to eq "{\"error\":\"value is missing, value is empty\"}"
expect(user.labels.find_by(key: 'phone')).to eq(nil)
end
end
end
context 'when label doesnt exist yet with false replace policy' do
let(:data) do
{
user_uid: user.uid,
key: 'phone',
value: 'verified',
replace: false
}
end
it 'receives an error' do
expect(user.labels.find_by(key: 'phone')).to eq(nil)
do_request
expect(response.status).to eq 404
expect(user.labels.find_by(key: 'phone')).to eq(nil)
expect(response.body).to eq "{\"error\":\"label doesnt exist\"}"
end
end
end
describe 'delete label' do
let!(:label) do
create(:label, key: 'email', value: 'verified', scope: 'private', user: user)
end
let(:data) do
{
user_uid: user.uid,
key: 'email'
}
end
let(:signers) { %i[alex jeff] }
let(:do_request) do
post_json '/api/v2/management/labels/delete',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
it 'deletes a label' do
expect { do_request }.to change { Label.count }.from(1).to(0)
expect(response.status).to eq 204
end
it 'denies access unless enough signatures are supplied' do
signers.clear.concat %i[james jeff]
expect { do_request }.to_not change { label.reload.value }
expect(response.status).to eq 401
end
it 'denies when user is not found' do
data[:user_uid] = 'invalid'
expect { do_request }.to_not change { label.reload.value }
expect(response.status).to eq 404
end
end
end

View File

@@ -0,0 +1,96 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pry'
describe API::V2::Management::Otp, type: :request do
before do
defaults_for_management_api_v2_security_configuration!
management_api_v2_security_configuration.merge! \
scopes: {
otp_sign: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] }
}
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user, otp: otp_enabled) }
describe 'POST /otp/sign' do
let(:data) do
{
user_uid: user.uid,
otp_code: valid_otp_code,
jwt: jwt
}
end
let(:signers) { %i[alex jeff] }
let(:valid_otp_code) { '1111' }
let(:invalid_otp_code) { '1234' }
let(:jwt) { applogic_signed_jwt(amount: '1 BTC') }
let(:otp_enabled) { true }
let(:do_request) do
post_json '/api/v2/management/otp/sign',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
let(:valid_payload_keys) do
%i[payload signatures]
end
before do
allow(TOTPService).to receive(:validate?)
.with(user.uid, valid_otp_code) { true }
allow(TOTPService).to receive(:validate?)
.with(user.uid, invalid_otp_code) { false }
end
it 'signs a request' do
do_request
expect(json_body.keys).to eq(valid_payload_keys)
expect(response.status).to eq 201
end
it 'renders an error when jwt is not a Hash' do
data[:jwt] = 'invalid'
do_request
expect_body.to eq(error: 'jwt is invalid')
expect(response.status).to eq 422
end
it 'renders an error when jwt is not RFC' do
data[:jwt] = { data: 'invalid' }
do_request
expect(json_body[:error]).to include('JWT is invalid by the reason')
expect(response.status).to eq 422
end
it 'renders an error when otp code is invalid' do
data[:otp_code] = invalid_otp_code
do_request
expect_body.to eq(error: 'OTP code is invalid')
expect(response.status).to eq 422
end
it 'renders an error when account is not found' do
data[:user_uid] = 'invalid'
do_request
expect_body.to eq error: 'Record is not found'
expect(response.status).to eq 404
end
context 'when data is blank' do
let(:data) { {} }
it 'renders errors' do
do_request
expect_body.to eq(error: "user_uid is missing, user_uid is empty, otp_code is missing, otp_code is empty, jwt is missing, jwt is empty")
expect(response.status).to eq 422
end
end
end
end

View File

@@ -0,0 +1,319 @@
# frozen_string_literal: true
describe API::V2::Management::Phones, type: :request do
before do
defaults_for_management_api_v2_security_configuration!
management_api_v2_security_configuration.merge! \
scopes: {
write_phones: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
read_phones: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
}
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user) }
describe 'POST /phones/get' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :read_phones) }
let(:do_request) do
post_json '/api/v2/management/phones/get',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
let(:params) do
phone_params
end
context 'valid request' do
context 'user without phones' do
let(:phone_params) do
{
uid: user.uid
}
end
it 'get list of phones' do
do_request
expect_status_to_eq 201
expect_body.to eq []
end
end
context 'user with phones' do
let!(:phone) { create(:phone, user: user) }
let(:phone_params) do
{
uid: user.uid
}
end
it 'get list of phones' do
do_request
expect_status_to_eq 201
expect(json_body.count).to eq 1
expect(json_body.first[:number]).to eq phone.number
expect(json_body.first[:country]).to eq phone.country
end
end
end
context 'invalid request' do
let(:phone_params) do
{
uid: 'invalid_uid'
}
end
it 'renders an error' do
do_request
expect_body.to eq(error: 'user.doesnt_exist')
expect_status.to eq 422
end
end
end
describe 'POST /phones' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_phones) }
let(:do_request) do
post_json '/api/v2/management/phones',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
let(:params) do
phone_params
end
context 'valid request' do
let(:phone_params) do
{
uid: user.uid,
number: '12345678911'
}
end
it 'creates a phone' do
expect { do_request }.to change { Phone.count }.by(1)
expect_status_to_eq 201
result = JSON.parse(response.body)
expect(result['country']).to eq 'US'
expect(result['number']).to eq phone_params[:number]
end
end
context 'invalid request' do
context 'when phone is invalid' do
let(:phone_params) do
{
uid: user.uid,
number: '123'
}
end
it 'renders an error' do
do_request
expect_body.to eq(error: 'management.phone.invalid_num')
expect_status.to eq 400
end
end
context 'when phone is missing' do
let(:phone_params) do
{
uid: user.uid
}
end
it 'renders an error' do
do_request
expect_status.to eq 422
expect_body.to eq(error: 'number is missing, number is empty')
end
end
context 'when phone is already exists' do
let!(:phone) do
create(:phone, validated_at: validated_at)
end
let(:phone_params) do
{
uid: user.uid,
number: phone.number
}
end
context 'when phone verified' do
let(:validated_at) { 1.minutes.ago }
it 'renders an error' do
do_request
expect_body.to eq(error: 'management.phone.number_exist')
expect_status.to eq 400
end
end
context 'when phone verified but number is not sanitized' do
let(:validated_at) { 1.minutes.ago }
let(:phone_params) do
{
uid: user.uid,
number: '++#{phone.number}'
}
end
it 'renders an error' do
do_request
expect_body.to eq(error: 'management.phone.invalid_num')
expect_status.to eq 400
end
end
end
context 'when phone exists on international format' do
let(:phone_params) do
{
uid: user.uid,
number: '+44 07418084106'
}
end
let(:international_phone) { '447418084106' }
let!(:phone) do
create(:phone, validated_at: 1.minute.ago, number: international_phone)
end
it 'renders an error' do
do_request
expect_body.to eq(error: 'management.phone.number_exist')
expect_status.to eq 400
end
end
context 'when phone exists for current user' do
let!(:phone) { create(:phone, number: '447418084106', user_id: user.id)}
let(:phone_params) do
{
uid: user.uid,
number: '447418084106'
}
end
it 'renders an error' do
do_request
expect_body.to eq(error: 'management.phone.exists')
expect_status.to eq 400
end
end
end
end
describe 'POST /phone/delete' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_phones) }
let(:do_request) do
post_json '/api/v2/management/phones/delete',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
let(:params) do
phone_params
end
context 'valid request' do
context 'user with one phone number' do
let!(:phone) { create(:phone, number: '447418084106', user_id: user.id)}
let(:phone_params) do
{
uid: user.uid,
number: phone.number
}
end
it 'show deleted phone' do
expect { do_request }.to change { Phone.count }.by(-1)
expect_status.to eq 201
expect(json_body[:number]).to eq phone.number
expect(json_body[:country]).to eq phone.country
end
end
context 'user with several phone numbers' do
let!(:phone1) { create(:phone, number: '447418084106', user_id: user.id)}
let!(:phone2) { create(:phone, number: '447418084107', user_id: user.id)}
let(:phone_params) do
{
uid: user.uid,
number: phone2.number
}
end
it 'show deleted phone' do
expect { do_request }.to change { Phone.count }.by(-1)
expect_status.to eq 201
expect(json_body[:number]).to eq phone2.number
expect(json_body[:country]).to eq phone2.country
end
end
end
context 'invalid request' do
context 'invalid user' do
let(:phone_params) do
{
uid: 'invalid_uid',
number: 'number'
}
end
it 'renders an error' do
do_request
expect_body.to eq(error: 'user.doesnt_exist')
expect_status.to eq 422
end
end
context 'invalid phone number' do
let(:phone_params) do
{
uid: user.uid,
number: 'invalid_number'
}
end
it 'renders an error' do
do_request
expect_body.to eq(error: 'management.phone.doesnt_exists')
expect_status.to eq 422
end
end
context 'non existed phone number' do
let(:phone_params) do
{
uid: user.uid,
number: '1111111111'
}
end
it 'renders an error' do
do_request
expect_body.to eq(error: 'management.phone.doesnt_exists')
expect_status.to eq 422
end
end
end
end
end

View File

@@ -0,0 +1,134 @@
# frozen_string_literal: true
require 'rails_helper'
describe API::V2::Management::Profiles, type: :request do
before do
defaults_for_management_api_v2_security_configuration!
management_api_v2_security_configuration.merge! \
scopes: {
read_users: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
write_users: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] }
}
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_users) }
let(:do_request) do
post_json '/api/v2/management/profiles',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
context 'when profile params are provided' do
let(:params) do
profile_params
end
context 'when params are valid' do
let(:user) { create :user }
let(:profile_params) do
{
uid: user.uid,
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.state,
postcode: Faker::Address.zip_code
}
end
it 'creates a profile' do
expect { do_request }.to change { Profile.count }.by(1)
expect_status_to_eq 201
result = JSON.parse(response.body)
expect(result['profiles'][0]['state']).to eq 'drafted'
expect(result['profiles'][0]['last_name']).to eq profile_params[:last_name]
expect(result['profiles'][0]['dob']).to eq profile_params[:dob].to_s
end
end
context 'when postcode is not provided' do
let(:user) { create :user }
let(:profile_params) do
{
uid: user.uid,
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.state
}
end
it 'create a profile' do
expect { do_request }.to change { Profile.count }
expect_status_to_eq 201
result = JSON.parse(response.body)
expect(result['profiles'][0]['state']).to eq 'drafted'
expect(result['profiles'][0]['last_name']).to eq profile_params[:last_name]
expect(result['profiles'][0]['dob']).to eq profile_params[:dob].to_s
end
end
context 'when profile state is provided' do
let(:user) { create :user }
let(:profile_params) do
{
uid: user.uid,
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.state,
state: 'rejected'
}
end
it 'create a profile' do
expect { do_request }.to change { Profile.count }
expect_status_to_eq 201
result = JSON.parse(response.body)
expect(result['profiles'][0]['state']).to eq 'rejected'
expect(result['profiles'][0]['last_name']).to eq profile_params[:last_name]
expect(result['profiles'][0]['dob']).to eq profile_params[:dob].to_s
end
end
context 'when params are invalid' do
let(:user) { create :user }
let(:profile_params) do
{
uid: user.uid,
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
country: 'a',
city: Faker::Address.city,
address: Faker::Address.state,
postcode: Faker::Address.zip_code
}
end
it 'renders an error' do
expect { do_request }.to_not change { Profile.count }
expect_status_to_eq 422
expect(json_body[:error]).to eq ['Country must have alpha2 or alpha3 format']
end
end
end
end

View File

@@ -0,0 +1,373 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pry'
describe API::V2::Management::ServiceAccounts, type: :request do
before do
defaults_for_management_api_v2_security_configuration!
management_api_v2_security_configuration.merge! \
scopes: {
read_service_accounts: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
write_service_accounts: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] }
}
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:create_service_account_permission) do
create :permission,
role: 'service_account'
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:user) { create(:user, role: 'member') }
let!(:service_account) { create(:service_account, user: user) }
describe 'Show service_account info' do
let(:data) do
{
scope: :read_service_accounts
}
end
let(:expected_attributes) do
%i[email uid role level state user created_at updated_at]
end
let(:signers) { %i[alex jeff] }
let(:do_request) do
post_json '/api/v2/management/service_accounts/get',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
it 'reads service_account info by uid' do
data[:uid] = service_account.uid
do_request
expect(response.status).to eq 200
expect(json_body.keys).to eq expected_attributes
end
it 'reads service_account info by email' do
data[:email] = service_account.email
do_request
expect(response.status).to eq 200
expect(json_body.keys).to eq expected_attributes
end
it 'denies when uid is not found' do
data[:uid] = 'invalid'
do_request
expect(response.status).to eq 404
end
it 'denies when email is not found' do
data[:email] = 'invalid'
do_request
expect(response.status).to eq 404
end
end
describe 'Returns array of service accounts as collection' do
let(:data) do
{
scope: :read_service_accounts
}
end
let(:signers) { %i[alex jeff] }
let(:do_request) do
post_json '/api/v2/management/service_accounts/list',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
context 'service_accounts' do
let!(:test_service_account) { create(:service_account, email: 'testa@gmail.com', user: user) }
let!(:second_service_account) { create(:service_account, email: 'testb@gmail.com', user: user) }
let!(:third_service_account) { create(:service_account, email: 'testd@gmail.com', user: user) }
let!(:fourth_service_account) { create(:service_account, email: 'testc@gmail.com', user: user) }
include_context 'bearer authentication'
let(:do_service_accounts_request) do
post_json '/api/v2/management/service_accounts/get',
multisig_jwt_management_api_v2({ data: data }), headers: auth_header
end
it 'denies access for user JWT instead of management signature' do
do_service_accounts_request
expect(response.status).to eq 401
end
it 'denies access unless enough signatures are supplied' do
signers.clear.concat %i[james jeff]
do_request
expect(response.status).to eq 401
end
it 'returns list of service_accounts' do
do_request
service_accounts = JSON.parse(response.body)
expect(ServiceAccount.count).to eq service_accounts.count
end
context 'pagination test' do
let(:service_account_list_params) do
{
scope: :read_users,
limit: 2
}
end
it 'returns 1st page as default, limit 2 users per page' do
service_account_list_params[:page] = 1
post_json '/api/v2/management/service_accounts/list', multisig_jwt_management_api_v2({ data: service_account_list_params }, *signers), headers: auth_header
expect(response.headers.fetch('Total')).to eq ServiceAccount.all.count.to_s
expect(response.headers.fetch('Page')).to eq '1'
expect(response.headers.fetch('Per-Page')).to eq '2'
end
it 'returns 2nd page, limit 2 users per page' do
service_account_list_params[:page] = 2
post_json '/api/v2/management/service_accounts/list', multisig_jwt_management_api_v2({ data: service_account_list_params }, *signers), headers: auth_header
expect(response.headers.fetch('Total')).to eq ServiceAccount.all.count.to_s
expect(response.headers.fetch('Page')).to eq '2'
expect(response.headers.fetch('Per-Page')).to eq '2'
end
end
end
end
describe 'Create a service account' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_service_accounts) }
let(:do_request) do
post_json '/api/v2/management/service_accounts/create',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
context 'when uid is present' do
context 'when email is valid' do
let(:params) do
{
service_account_email: 'valid_email@example.com',
service_account_uid: 'Fai5aesoLEcx',
service_account_role: 'admin',
owner_uid: user.uid
}
end
it 'creates a service account' do
expect { do_request }.to change { ServiceAccount.count }.by(1)
expect_status_to_eq 201
end
end
context 'when params are blank' do
let(:params) { {} }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(error: 'service_account_role is missing, service_account_role is empty')
end
end
context 'when email is bad' do
let(:params) { { service_account_email: 'bad_email', service_account_uid: 'Fai5aesoLEcx', service_account_role: 'member', owner_uid: user.uid } }
it 'renders an error' do
expect { do_request }.to_not change { ServiceAccount.count }
expect_status_to_eq 422
expect_body.to eq(error: ['Email is invalid'])
end
end
context 'when role does not exist' do
let(:params) { { service_account_email: 'valid_email@example.com', service_account_uid: 'Fai5aesoLEcx', service_account_role: 'invalid', owner_uid: user.uid } }
it 'renders an error' do
expect { do_request }.to_not change { ServiceAccount.count }
expect_status_to_eq 422
expect_body.to eq(error: ['Role doesnt_exist'])
end
end
context 'when uid is not uniq, the same as user one' do
let(:params) { { service_account_email: 'valid_email@example.com', service_account_uid: user.uid, service_account_role: 'member', owner_uid: user.uid } }
it 'renders an error' do
expect { do_request }.to_not change { ServiceAccount.count }
expect_status_to_eq 422
expect(json_body[:error].first).to include 'Email or uid not_uniq'
end
end
end
end
describe 'Update a service account' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_service_accounts) }
let!(:service_account) { create(:service_account, :without_user) }
let(:do_request) do
post_json '/api/v2/management/service_accounts/update',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
context 'when params are blank' do
let(:params) { {} }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(error: 'uid is missing, uid is empty')
end
end
context 'when service account doesnt exists' do
let(:params) do
{
uid: 'example'
}
end
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(error: 'Service account doesnt exist')
end
end
context 'update service account' do
context do
let(:params) do
{
uid: service_account.uid
}
end
context 'service account without user' do
it 'shouldnt modify service account' do
do_request
expect_status_to_eq 201
res = JSON.parse(response.body)
expect(res['state']).to eq 'pending'
expect(res['level']).to eq 0
expect(res['user']).to eq nil
end
end
context 'service account with user' do
let!(:service_account) { create(:service_account) }
it 'shouldnt modify service account' do
owner = service_account.user
do_request
expect_status_to_eq 201
res = JSON.parse(response.body)
expect(res['state']).to eq owner.state
expect(res['level']).to eq owner.level
expect(res['user']['uid']).to eq owner.uid
end
end
end
context do
context 'service account without user' do
let!(:user_with_phone) { create(:user, :with_phone) }
let(:params) do
{
uid: service_account.uid,
owner_uid: user_with_phone.uid
}
end
it 'should modify service account' do
expect(service_account.level).to eq 0
expect(service_account.state).to eq 'pending'
expect(service_account.user).to eq nil
do_request
expect_status_to_eq 201
res = JSON.parse(response.body)
expect(res['user']['uid']).to eq user_with_phone.uid
expect(res['level']).to eq user_with_phone.level
expect(res['state']).to eq user_with_phone.state
end
end
context 'service account with user' do
let!(:user_with_phone) { create(:user, :with_phone) }
let!(:user_with_profile) { create(:user, :with_profile) }
let!(:service_account) { create(:service_account, user: user_with_phone)}
let(:params) do
{
uid: service_account.uid,
owner_uid: user_with_profile.uid
}
end
it 'should modify service account' do
expect(service_account.level).to eq user_with_phone.level
expect(service_account.state).to eq user_with_phone.state
expect(service_account.user.uid).to eq user_with_phone.uid
do_request
expect_status_to_eq 201
res = JSON.parse(response.body)
expect(res['user']['uid']).to eq user_with_profile.uid
expect(res['level']).to eq user_with_profile.level
expect(res['state']).to eq user_with_profile.state
end
end
end
end
end
describe 'Delete a service account' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_service_accounts) }
let(:do_request) do
post_json '/api/v2/management/service_accounts/delete',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
context do
let(:params) { { uid: service_account.uid } }
it do
do_request
expect_status_to_eq 200
service_account = JSON.parse(response.body)
expect(service_account['state']).to eq 'disabled'
end
end
context 'when params are blank' do
let(:params) { {} }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(error: 'uid is missing, uid is empty')
end
end
end
end

View File

@@ -0,0 +1,556 @@
# frozen_string_literal: true
require 'rails_helper'
require 'pry'
describe API::V2::Management::Users, type: :request do
before do
defaults_for_management_api_v2_security_configuration!
management_api_v2_security_configuration.merge! \
scopes: {
read_users: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
write_users: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] }
}
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user, :with_profile) }
describe 'Show user info' do
let(:data) do
{
scope: :read_users
}
end
let(:expected_attributes) do
%i[email uid role level otp state data profiles referral_uid created_at updated_at]
end
let(:extended_attributes) do
%i[email uid role level otp state data profiles labels phones documents data_storages comments referral_uid created_at updated_at]
end
let(:signers) { %i[alex jeff] }
let(:do_request) do
post_json '/api/v2/management/users/get',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
it 'reads extended user info by uid' do
data[:uid] = user.uid
do_request
expect(response.status).to eq 201
expect(json_body.keys).to match_array extended_attributes
expect(json_body).to include(:profiles, :comments, :phones, :documents)
expect(json_body[:profiles][0][:last_name]).to eq user.profiles[0].last_name
expect(json_body[:profiles][0][:dob]).to eq user.profiles[0].dob.to_s
end
it 'reads full user info by email' do
data[:email] = user.email
do_request
expect(response.status).to eq 201
expect(json_body.keys).to match_array extended_attributes
expect(json_body).to include(:profiles, :comments, :phones, :documents)
expect(json_body[:profiles][0][:last_name]).to eq user.profiles[0].last_name
expect(json_body[:profiles][0][:dob]).to eq user.profiles[0].dob.to_s
end
let!(:phone) do
create(:phone, validated_at: 1.minutes.ago, user_id: user.id)
end
it 'reads full user info by user phone' do
data[:phone_num] = phone.number
do_request
expect(response.status).to eq 201
expect(json_body.keys).to match_array extended_attributes
expect(json_body).to include(:profiles, :comments, :phones, :documents)
expect(json_body[:profiles][0][:last_name]).to eq user.profiles[0].last_name
expect(json_body[:profiles][0][:dob]).to eq user.profiles[0].dob.to_s
end
it 'allows only one of phone, uid, email' do
data[:phone_num] = phone.number
data[:email] = user.email
data[:uid] = user.uid
do_request
expect(response.status).to eq 422
expect_body.to eq(error: 'uid, email, phone_num are mutually exclusive')
end
it 'denies access unless enough signatures are supplied' do
signers.clear.concat %i[james jeff]
do_request
expect(response.status).to eq 401
end
it 'denies when uid is not found' do
data[:uid] = 'invalid'
do_request
expect(response.status).to eq 404
end
it 'denies when email is not found' do
data[:email] = 'invalid'
do_request
expect(response.status).to eq 404
end
it 'denies when email is not found' do
data[:phone_num] = 'invalid'
do_request
expect(response.status).to eq 404
end
context 'when data is blank' do
let(:data) { {} }
it 'renders errors' do
do_request
expect(response.status).to eq 422
expect_body.to eq(error: 'uid, email, phone_num are missing, exactly one parameter must be provided')
end
end
end
describe 'Returns array of users as collection' do
let(:data) do
{
scope: :read_users
}
end
let(:signers) { %i[alex jeff] }
let(:do_request) do
post_json '/api/v2/management/users/list',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
context 'users' do
let!(:test_user) { create(:user, email: 'testa@gmail.com', role: 'admin') }
let!(:second_user) { create(:user, email: 'testb@gmail.com') }
let!(:third_user) { create(:user, email: 'testd@gmail.com') }
let!(:fourth_user) { create(:user, email: 'testc@gmail.com') }
def validate_fields(user)
user.attributes.slice('email', 'role', 'level', 'otp', 'state', 'uid', 'data')
end
include_context 'bearer authentication'
let(:do_user_request) do
post_json '/api/v2/management/users/get',
multisig_jwt_management_api_v2({ data: data }), headers: auth_header
end
it 'denies access for user JWT instead of management signature' do
do_user_request
expect(response.status).to eq 401
end
it 'denies access unless enough signatures are supplied' do
signers.clear.concat %i[james jeff]
do_request
expect(response.status).to eq 401
end
it 'returns list of users' do
do_request
users = JSON.parse(response.body)
expect(User.count).to eq users.count
expect(validate_fields(User.first)).to eq users.first.except('referral_uid')
expect(validate_fields(User.second)).to eq users.second.except('referral_uid')
expect(validate_fields(User.third)).to eq users.third.except('referral_uid')
expect(validate_fields(User.last)).to eq users.last.except('referral_uid')
end
context 'pagination test' do
let(:users_list_params) do
{
scope: :read_users,
limit: 2
}
end
it 'returns 1st page as default, limit 2 users per page' do
users_list_params[:page] = 1
post_json '/api/v2/management/users/list', multisig_jwt_management_api_v2({ data: users_list_params }, *signers), headers: auth_header
expect(response.headers.fetch('Total')).to eq User.all.count.to_s
expect(response.headers.fetch('Page')).to eq '1'
expect(response.headers.fetch('Per-Page')).to eq '2'
end
it 'returns 2nd page, limit 2 users per page' do
users_list_params[:page] = 2
post_json '/api/v2/management/users/list', multisig_jwt_management_api_v2({ data: users_list_params }, *signers), headers: auth_header
expect(response.headers.fetch('Total')).to eq User.all.count.to_s
expect(response.headers.fetch('Page')).to eq '2'
expect(response.headers.fetch('Per-Page')).to eq '2'
end
end
context 'filtering test' do
let(:users_list_params) do
{
scope: :read_users
}
end
it 'returns filtered list of users when only one filter param given created_at and from' do
users_list_params[:range] = 'created'
User.first.update(created_at: 5.hours.ago)
users_list_params[:from] = 8.hours.ago.to_i
users_list_params[:to] = 2.hours.ago.to_i
post_json '/api/v2/management/users/list', multisig_jwt_management_api_v2({ data: users_list_params }, *signers), headers: auth_header
expect(response.status).to eq 200
expect(json_body.count).to eq (1)
end
it 'returns filtered list of users when only one filter param given updated_at' do
users_list_params[:range] = 'updated'
User.first.update(updated_at: 5.hours.ago)
users_list_params[:from] = 8.hours.ago.to_i
users_list_params[:to] = 2.hours.ago.to_i
post_json '/api/v2/management/users/list', multisig_jwt_management_api_v2({ data: users_list_params }, *signers), headers: auth_header
expect(response.status).to eq 200
expect(json_body.count).to eq (1)
expect(json_body[0]).not_to include(:profiles)
end
it 'returns filetered list of users with extended param' do
users_list_params[:extended] = true
post_json '/api/v2/management/users/list', multisig_jwt_management_api_v2({ data: users_list_params }, *signers), headers: auth_header
expect(response.status).to eq 200
expect(json_body[0]).to include(:profiles)
expect(json_body.first[:profiles][0][:last_name]).to eq user.profiles[0].last_name
expect(json_body.first[:profiles][0][:dob]).to eq user.profiles[0].dob.to_s
end
end
end
end
describe 'Update role and data of existing user' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_users) }
let(:do_request) do
post_json '/api/v2/management/users/update',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
context 'when data is present' do
let!(:user) { create :user, :with_profile }
let(:params) do
{
uid: user.uid,
data: { nationality: 'us' }.to_json
}
end
it 'updates data field' do
do_request
expect_status_to_eq 201
expect(json_body).to include(:profiles)
expect(json_body[:profiles][0][:last_name]).to eq user.profiles[0].last_name
expect(json_body[:profiles][0][:dob]).to eq user.profiles[0].dob.to_s
expect(user.reload.data).to eq({ nationality: 'us' }.to_json)
end
end
context 'when role is present' do
let(:user) { create :user }
let(:params) do
{
uid: user.uid,
role: 'admin'
}
end
it 'updates role field' do
do_request
expect_status_to_eq 201
expect(user.reload.role).to eq('admin')
end
end
context 'when data and role are present' do
let(:user) { create :user }
let(:params) do
{
uid: user.uid,
data: { nationality: 'us' }.to_json,
role: 'admin'
}
end
it 'updates data and role fields' do
do_request
expect_status_to_eq 201
expect(user.reload.data).to eq({ nationality: 'us' }.to_json)
expect(user.reload.role).to eq('admin')
end
end
context 'when none of parameters are present' do
let(:user) { create :user }
let(:params) do
{
uid: user.uid,
}
end
it 'renders error' do
do_request
expect_status_to_eq 422
expect_body.to eq(error: "role, data are missing, at least one parameter must be provided")
end
end
end
describe 'Create an user' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_users) }
let(:do_request) do
post_json '/api/v2/management/users',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
context 'when password is present' do
context 'when email and password are valid' do
let(:params) do
{
email: 'valid_email@example.com',
password: 'Fai5aesoLEcx'
}
end
it 'creates an user' do
expect { do_request }.to change { User.count }.by(1)
expect_status_to_eq 201
end
end
context 'when params are blank' do
let(:params) { {} }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(error: 'email is missing, email is empty, password is missing, password is empty')
end
end
context 'when email is bad' do
let(:params) { { email: 'bad_email', password: 'Password1' } }
it 'renders an error' do
expect { do_request }.to_not change { User.count }
expect_status_to_eq 422
expect_body.to eq(error: ['Email is invalid','Password weak'])
end
end
context 'creates an account with referral uid' do
let(:params) { { email: 'test@test.com', password: 'Fai5aesoLEcx', referral_uid: User.last.uid } }
it 'renders an error' do
expect { do_request }.to change { User.count }.by(1)
expect_status_to_eq 201
expect(json_body).to include(:email, :uid, :role, :level,
:state, :otp, :profiles)
end
end
context 'when password is bad' do
let(:params) { { email: 'valid_email@example.com', password: 'password' } }
it 'renders an error' do
expect { do_request }.to_not change { User.count }
expect_status_to_eq 422
expect(json_body[:error].first).to include 'Password requirements'
end
end
end
end
describe 'Imports an existing user' do
let(:signers) { %i[alex jeff] }
let(:data) { params.merge(scope: :write_users) }
let(:do_request) do
post_json '/api/v2/management/users/import',
multisig_jwt_management_api_v2({ data: data }, *signers)
end
let!(:email) { 'valid_email@example.com' }
let!(:password) { 'Fai5aesoLEcx' }
let!(:password_digest) do
User.new(password: password).send(:password_digest)
end
let(:params) do
{ email: email, password_digest: password_digest }
end
context 'when email and password_hash are valid' do
it 'creates an user and signs in with credentials' do
expect { do_request }.to change { User.count }.by(1)
expect_status_to_eq 201
expect(json_body).to include(:email, :uid, :role, :level,
:state, :otp, :profiles)
# TODO: Check if imported user is able to login
end
end
context 'when params are blank' do
let(:params) { {} }
it 'renders an error' do
do_request
expect_status_to_eq 422
expect_body.to eq(error: 'email is missing, email is empty, password_digest is missing, password_digest is empty')
end
end
context 'when phone is provided' do
let(:params) do
{
email: email,
password_digest: password_digest,
phone: phone
}
end
context 'when phone is valid' do
let(:phone) { build(:phone).number }
it 'creates a phone' do
expect { do_request }.to change { Phone.count }.by(1)
expect_status_to_eq 201
end
end
context 'when phone is invalid' do
let(:phone) { '12345' }
it 'renders an error' do
expect { do_request }.to_not change { Phone.count }
expect_status_to_eq 422
expect(json_body[:error]).to eq ['Number is invalid']
end
end
end
context 'when profile params are provided' do
let(:params) do
{
email: email,
password_digest: password_digest
}.merge(profile_params)
end
context 'when params are valid' do
let(:profile_params) do
{
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.state,
postcode: Faker::Address.zip_code
}
end
it 'creates a profile' do
expect { do_request }.to change { Profile.count }.by(1)
result = JSON.parse(response.body)
expect(result['profiles'][0]['last_name']).to eq profile_params[:last_name]
expect(result['profiles'][0]['dob']).to eq profile_params[:dob].to_s
expect(result['profiles'][0]['state']).to eq 'drafted'
expect_status_to_eq 201
end
end
context 'when postcode is not provided' do
let(:profile_params) do
{
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.state
}
end
it 'create a profile' do
expect { do_request }.to change { Profile.count }
result = JSON.parse(response.body)
expect(result['profiles'][0]['last_name']).to eq profile_params[:last_name]
expect(result['profiles'][0]['dob']).to eq profile_params[:dob].to_s
expect(result['profiles'][0]['state']).to eq 'drafted'
expect_status_to_eq 201
end
end
context 'when profile state is provided' do
let(:profile_params) do
{
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.state,
state: 'rejected'
}
end
it 'create a profile' do
expect { do_request }.to change { Profile.count }
result = JSON.parse(response.body)
expect(result['profiles'][0]['last_name']).to eq profile_params[:last_name]
expect(result['profiles'][0]['dob']).to eq profile_params[:dob].to_s
expect(result['profiles'][0]['state']).to eq 'rejected'
expect_status_to_eq 201
end
end
context 'when params are invalid' do
let(:profile_params) do
{
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
country: 'a',
city: Faker::Address.city,
address: Faker::Address.state,
postcode: Faker::Address.zip_code
}
end
it 'renders an error' do
expect { do_request }.to_not change { Profile.count }
expect_status_to_eq 422
expect(json_body[:error]).to eq ['Country must have alpha2 or alpha3 format']
end
end
end
end
end

View File

@@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::V2::Public::General do
describe 'GET /api/v2/public/time' do
it 'returns a server status' do
get '/api/v2/public/ping'
expect_status_to_eq(200)
expect(json_body[:ping]).to eq('pong')
end
end
describe 'GET /api/v2/public/time' do
it 'returns a current UNIX time' do
get '/api/v2/public/time'
expect_status_to_eq(200)
expect(json_body[:time].to_i).to be <= Time.now.to_i
end
end
describe 'GET /api/v2/public/configs' do
it 'returns some of the configurations' do
get '/api/v2/public/configs'
expect_status_to_eq(200)
expect(json_body[:session_expire_time]).to eq(Barong::App.config.session_expire_time)
expect(json_body[:captcha_type]).to eq(Barong::App.config.captcha)
expect(json_body[:phone_verification_type]).to eq(Barong::App.config.phone_verification)
end
it 'returns all of the configurations with defaults' do
get '/api/v2/public/configs'
expect_status_to_eq(200)
expect(json_body[:session_expire_time]).to eq(Barong::App.config.session_expire_time)
expect(json_body[:captcha_type]).to eq(Barong::App.config.captcha)
expect(json_body[:phone_verification_type]).to eq(Barong::App.config.phone_verification)
expect(json_body[:password_min_entropy]).to eq(Barong::App.config.password_min_entropy)
expect(json_body[:password_regexp]).to eq(Barong::App.config.password_regexp.to_s)
end
end
end

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
describe API::V2::Queries::ActivityFilter do
let(:initial_scope) { Activity.all }
let(:params) {{topic: 'session', action: 'login', uid: 'UI12345'}}
subject { described_class.new(initial_scope).call(params) }
it 'filters by uid and action' do
case ActiveRecord::Base.connection.adapter_name
when 'Mysql2'
expect(subject.to_sql).to include("`topic` = 'session'")
expect(subject.to_sql).to include("`action` = 'login'")
expect(subject.to_sql).to include("`uid` = 'UI12345'")
when 'PostgreSQL'
expect(subject.to_sql).to include("\"topic\" = 'session'")
expect(subject.to_sql).to include("\"action\" = 'login'")
expect(subject.to_sql).to include("\"uid\" = 'UI12345'")
else
raise "Unsupported adapter: #{ActiveRecord::Base.connection.adapter_name}"
end
end
end

View File

@@ -0,0 +1,135 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Addresses API test' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:image) { fixture_file_upload('/files/documents_test.jpg', 'image/jpg') }
before do
allow(Barong::App.config).to receive_messages(required_docs_expire: false)
end
describe 'POST /api/v2/resource/addresses/' do
let(:params) do
{
country: 'UA',
address: 'Yaroslaviv Val 15',
city: 'Kiev',
postcode: '101010',
upload: [
image
]
}
end
let(:last_document) { Document.last }
it 'saves 10 address documents successfully' do
10.times do
post '/api/v2/resource/addresses', headers: auth_header,
params: {
country: 'UA',
address: 'Yaroslaviv Val 15',
city: 'Kiev',
postcode: '101010',
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')]
}
end
expect(response.status).to eq(201)
end
it 'uploads 2 files at once' do
post '/api/v2/resource/addresses', headers: auth_header,
params: {
country: 'UA',
address: 'Yaroslaviv Val 15',
city: 'Kiev',
postcode: '101010',
upload: [
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
]
}
expect(response.status).to eq(201)
expect(test_user.documents.length).to eq(2)
end
it 'uploads 3 files at once' do
post '/api/v2/resource/addresses', headers: auth_header,
params: {
country: 'UA',
address: 'Yaroslaviv Val 15',
city: 'Kiev',
postcode: '101010',
upload: [
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
]
}
expect(response.status).to eq(201)
expect(test_user.documents.length).to eq(3)
end
it 'Checks if params are ok and returns success' do
post '/api/v2/resource/addresses', headers: auth_header, params: params
expect(response.status).to eq(201)
end
it 'Creates a pending address label' do
expect(test_user.labels.find_by(key: :address)).to eq(nil)
post '/api/v2/resource/addresses', headers: auth_header, params: params
expect(response.status).to eq(201)
expect(test_user.labels.find_by(key: :address)).not_to eq(nil)
expect(test_user.labels.find_by(key: :address).value).to eq('pending')
end
it 'Update rejected or verified label to pending documents label on new doc' do
expect(test_user.labels.find_by(key: :address)).to eq(nil)
test_user.labels.create(key: :address, value: 'verified')
test_user.reload
post '/api/v2/resource/addresses', headers: auth_header, params: params
expect(response.status).to eq(201)
expect(test_user.labels.find_by(key: :address)).not_to eq(nil)
expect(test_user.labels.find_by(key: :address).value).to eq('pending')
end
it 'triggers KYCService' do
expect(KycService).to receive(:address_step)
post '/api/v2/resource/addresses', headers: auth_header, params: params
expect(response.status).to eq(201)
end
end
context 'event API behavior' do
let!(:url) { '/api/v2/resource/addresses' }
let(:request_params) do
{
country: 'UA',
address: 'Yaroslaviv Val 15',
city: 'Kiev',
postcode: '101010',
upload: [
image
]
}
end
before do
allow(EventAPI).to receive(:notify)
end
it 'receive model.document.created notify' do
expect(EventAPI).to receive(:notify).ordered.with('model.user.created', hash_including(:record))
expect(EventAPI).to receive(:notify).ordered.with('model.document.created', hash_including(:record))
post url, headers: auth_header, params: request_params
end
end
end

View File

@@ -0,0 +1,263 @@
# frozen_string_literal: true
describe 'Api::V2::APIKeys' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:test_user) { create(:user, otp: otp_enabled) }
let(:otp_enabled) { true }
let!(:first_api_key) { create :api_key, key_holder_account: test_user }
let!(:second_api_key) { create :api_key, key_holder_account: test_user }
let(:valid_otp_code) { '1357' }
let(:invalid_otp_code) { '1234' }
let(:otp_code) { valid_otp_code }
before do
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, valid_otp_code) { true }
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, invalid_otp_code) { false }
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, nil) { false }
end
describe 'GET /api/v2/resource/api_keys/' do
let(:get_params) do
{
ordering: 'asc',
oreder_by: 'id'
}
end
let(:post_params) do
{
scope: 'trade',
algorithm: 'HS256'
}
end
let(:do_request) do
post '/api/v2/resource/api_keys',
params: post_params.merge(totp_code: otp_code),
headers: auth_header
end
let(:do_get_request) { get "/api/v2/resource/api_keys", params: get_params, headers: auth_header }
let(:expected_fields) do
{
kid: first_api_key.kid,
state: first_api_key.state,
scope: %w[trade]
}
end
let(:totp_code) { valid_otp_code }
it 'Return api key for current account in ASC order' do
do_get_request
expect(json_body.count).to eq 2
expect(response.status).to eq(200)
expect(json_body.first).to include(expected_fields)
end
it 'Return api key for current account in DESC order' do
get_params.merge!(ordering: 'desc')
do_get_request
expect(json_body.count).to eq 2
expect(response.status).to eq(200)
expect(json_body.second).to include(expected_fields)
end
context 'when invalid ordering' do
it 'renders an error' do
get_params.merge!(ordering: 'resc')
do_get_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.api_key.invalid_ordering"])
end
end
context 'when invalid order_by' do
it 'renders an error' do
get_params.merge!(order_by: 'invalid')
do_get_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.api_key.invalid_attribute"])
end
end
context 'when otp is not enabled' do
let(:otp_enabled) { false }
it 'renders an error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
end
end
context 'when code is invalid' do
let(:otp_code) { invalid_otp_code }
it 'renders an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
end
end
end
describe 'POST /api/v2/resource/api_keys' do
let(:do_request) do
post '/api/v2/resource/api_keys',
params: params.merge(totp_code: otp_code),
headers: auth_header
end
let(:otp_code) { valid_otp_code }
context 'when fields are valid' do
let(:params) do
{
scope: 'trade',
algorithm: 'HS256'
}
end
let(:expected_fields) do
{
state: 'active',
scope: params[:scope].split(','),
algorithm: 'HS256'
}
end
it 'Create an api key' do
expect { do_request }.to change { APIKey.count }.by(1)
expect(response.status).to eq(201)
expect_body.to include(expected_fields)
end
it 'does not create api key if vault is down' do
allow(Vault::Rails).to receive(:encrypt).and_raise(Vault::VaultError)
expect { do_request }.not_to change { APIKey.count }
expect(response.status).to eq(422)
expect_body.to eq(errors: ["api_key.could_not_save_secret"])
end
context 'when otp is not enabled' do
let(:otp_enabled) { false }
it 'renders an error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
end
end
context 'when code is invalid' do
let(:otp_code) { invalid_otp_code }
it 'renders an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
end
end
context 'when algorithm is invalid' do
it 'renders an error' do
params[:algorithm] = 'kek'
do_request
expect_body.to eq(errors: ["kid.blank", "algorithm.inclusion"])
end
end
end
end
describe 'PATCH /api/v2/resource/api_keys/:kid' do
let(:do_request) do
patch "/api/v2/resource/api_keys/#{first_api_key.kid}", params: params.merge(totp_code: otp_code),
headers: auth_header
end
let(:otp_code) { valid_otp_code }
context 'when valid fields' do
let(:params) do
{
state: 'inactive',
scope: 'sell',
algorithm: 'HS256'
}
end
it 'Updates a state' do
expect { do_request }.to change { first_api_key.reload.state }
.from('active').to('inactive')
expect(response.status).to eq(200)
end
it 'Updates a scope' do
expect { do_request }.to change { first_api_key.reload.scope }
.from(['trade']).to(['sell'])
expect(response.status).to eq(200)
end
context 'when otp is not enabled' do
let(:otp_enabled) { false }
it 'renders an error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
end
end
context 'when code is invalid' do
let(:otp_code) { invalid_otp_code }
it 'renders an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
end
end
end
end
describe 'DELETE /api/v2/resource/api_keys/:uid' do
let(:do_request) do
delete "/api/v2/resource/api_keys/#{first_api_key.kid}?totp_code=#{otp_code}",
headers: auth_header
end
let(:otp_code) { valid_otp_code }
it 'Removes an api key' do
do_request
expect(response.status).to eq(204)
end
context 'when otp is not enabled' do
let(:otp_enabled) { false }
it 'renders an error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
end
end
context 'when code is invalid' do
let(:otp_code) { invalid_otp_code }
it 'renders an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
end
end
end
end

View File

@@ -0,0 +1,58 @@
# frozen_string_literal: true
describe 'Api::V2::Resource::DataStorage' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
describe 'POST /api/v2/resource/data_storage' do
let(:url) { '/api/v2/resource/data_storage' }
context 'successful creation' do
let(:valid_params) do
{
title: 'personal',
data: { wife_last_name: Faker::Name.last_name, wife_first_name: Faker::Name.first_name }.to_json
}
end
it 'creates data_storage record' do
expect { post url, params: valid_params, headers: auth_header }.to change { DataStorage.count }.by(1)
expect(response.status).to eq(201)
end
it 'creates a label when recording data_storage' do
expect { post url, params: valid_params, headers: auth_header }.to change { Label.count }.by(1)
expect(response.status).to eq(201)
expect(test_user.labels.find_by(key: valid_params[:title])).not_to be_nil
end
end
context 'errors' do
let(:non_json_data_params) { { title: 'personal', data: 'My name is John Doe. Hello, World!' } }
let(:non_whitelisted_params) { { title: 'family_info', data: { wife_first_name: Faker::Name.first_name }.to_json } }
let(:blacklisted_params) { { title: 'document', data: { approved: true }.to_json } }
it 'doesnt accept non-json data' do
expect { post url, params: non_json_data_params, headers: auth_header }.not_to change { DataStorage.count }
expect(response.status).to eq(422)
expect(json_body).to eq({errors: ['data.invalid_format']})
end
it 'doesnt accept non-whitelisted title' do
expect { post url, params: non_whitelisted_params, headers: auth_header }.not_to change { DataStorage.count }
expect(response.status).to eq(422)
expect(json_body).to eq({errors: ['title.inclusion']})
end
it 'doesnt accept blacklisted title' do
expect { post url, params: blacklisted_params, headers: auth_header }.not_to change { DataStorage.count }
expect(response.status).to eq(422)
expect(json_body).to eq({errors: ['title.inclusion', 'title.exclusion']})
end
end
end
end

View File

@@ -0,0 +1,288 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Documents API test' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:image) { fixture_file_upload('/files/documents_test.jpg', 'image/jpg') }
describe 'POST /api/v2/resource/documents/' do
let(:params) do
{
doc_type: 'Passport',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
doc_category: 'front_side',
upload: [
image
]
}
end
let!(:optional_params) do
{
metadata: { country: Faker::Address.country }.to_json
}
end
let(:last_document) { Document.last }
it 'saves 10 documents successfully' do
10.times do
post '/api/v2/resource/documents', headers: auth_header,
params: {
doc_type: 'Passport',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')]
}
end
expect(response.status).to eq(201)
end
it 'renders an error when max documents already reached' do
11.times do
post '/api/v2/resource/documents', headers: auth_header,
params: {
doc_type: 'Passport',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')]
}
end
expect(response.status).to eq(400)
expect_body.to eq(errors: ['resource.documents.limit_will_be_reached'])
end
it 'uploads 2 files at once' do
post '/api/v2/resource/documents', headers: auth_header,
params: {
doc_type: 'Passport',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
upload: [
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
]
}
expect(response.status).to eq(201)
expect(test_user.documents.length).to eq(2)
end
it 'uploads 3 files at once' do
post '/api/v2/resource/documents', headers: auth_header,
params: {
doc_type: 'Passport',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
upload: [
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
]
}
expect(response.status).to eq(201)
expect(test_user.documents.length).to eq(3)
end
it 'doesn\'t upload more than 10 files at once' do
post '/api/v2/resource/documents', headers: auth_header,
params: {
doc_type: 'Passport',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
upload: [
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
]
}
expect(response.status).to eq(400)
end
it 'Checks if params are ok and returns success' do
post '/api/v2/resource/documents', headers: auth_header, params: params
expect(response.status).to eq(201)
end
it 'Creates a pending documents label' do
expect(test_user.labels.find_by(key: :document)).to eq(nil)
post '/api/v2/resource/documents', headers: auth_header, params: params
expect(response.status).to eq(201)
expect(test_user.labels.find_by(key: :document)).to eq(nil)
post '/api/v2/resource/documents', headers: auth_header,
params: params.merge({ doc_category: 'selfie',
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')],
identificator: test_user.documents.last.identificator })
expect(test_user.labels.find_by(key: :document).value).to eq('pending')
end
it 'Update rejected or verified label to pending documents label on new doc' do
expect(test_user.labels.find_by(key: :document)).to eq(nil)
test_user.labels.create(key: :document, value: 'verified')
test_user.reload
post '/api/v2/resource/documents', headers: auth_header, params: params
expect(response.status).to eq(201)
expect(test_user.labels.find_by(key: :document)).not_to eq(nil)
expect(test_user.labels.find_by(key: :document).value).to eq('verified')
post '/api/v2/resource/documents', headers: auth_header,
params: params.merge({ doc_category: 'selfie',
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')],
identificator: test_user.documents.last.identificator })
expect(test_user.labels.find_by(key: :document).value).to eq('pending')
end
it 'triggers KYCService' do
expect(KycService).to receive(:document_step)
post '/api/v2/resource/documents', headers: auth_header, params: params
expect(response.status).to eq(201)
end
it 'Creates document with optional params and returns success' do
post '/api/v2/resource/documents', headers: auth_header, params: params.merge(optional_params)
expect(response.status).to eq(201)
expect(last_document.metadata).to eq(optional_params[:metadata])
end
it 'renders an error if metadata is not json' do
post '/api/v2/resource/documents', headers: auth_header, params: params.merge({ metadata: '{ bar: baz }' })
expect_status_to_eq 422
expect_body.to eq(errors: ['metadata.invalid_format'])
end
it 'Checks provided params and returns error, cause some of them are not valid or absent' do
post '/api/v2/resource/documents', params: params.except(:doc_type), headers: auth_header
expect_body.to eq(errors: ['resource.document.missing_doc_type', 'resource.document.empty_doc_type'])
expect(response.status).to eq(422)
post '/api/v2/resource/documents', params: params.except(:doc_number), headers: auth_header
expect_body.to eq(errors: ['resource.document.missing_doc_number', 'resource.document.empty_doc_number'])
expect(response.status).to eq(422)
post '/api/v2/resource/documents', params: params.except(:upload), headers: auth_header
expect_body.to eq(errors: ['resource.document.missing_upload'])
expect(response.status).to eq(422)
post '/api/v2/resource/documents', params: params.except(:doc_expire).merge(doc_expire: 'blah'), headers: auth_header
expect_body.to eq(errors: ['resource.documents.expire_not_a_date'])
expect(response.status).to eq(422)
params0 = params
params0[:upload] = [Faker::Avatar.image]
post '/api/v2/resource/documents', params: params0, headers: auth_header
expect_body.to eq(errors: ['upload.blank'])
expect(response.status).to eq(422)
end
it 'Does not return error when docs expire is optional' do
allow(Barong::App.config).to receive(:required_docs_expire).and_return(false)
post '/api/v2/resource/documents', params: params.except(:doc_expire), headers: auth_header
expect(response.status).to eq(201)
end
it 'Returns error when docs expire is not optional' do
allow(Barong::App.config).to receive(:required_docs_expire).and_return(true)
post '/api/v2/resource/documents', params: params.except(:doc_expire), headers: auth_header
expect(response.status).to eq(422)
expect_body.to eq({ errors: ['resource.documents.invalid_format'] })
end
it 'Returns error when docs expire is not optional and date in past' do
allow(Barong::App.config).to receive(:required_docs_expire).and_return(true)
post '/api/v2/resource/documents', params: params.merge({ doc_expire: DateTime.now.to_date - 1 }), headers: auth_header
expect(response.status).to eq(422)
expect_body.to eq({ errors: ['resource.documents.already_expired'] })
end
it 'Doesnt return error when docs expire is optional and date in past' do
allow(Barong::App.config).to receive(:required_docs_expire).and_return(false)
post '/api/v2/resource/documents', params: params.merge({ doc_expire: DateTime.now.to_date - 1 }), headers: auth_header
expect(response.status).to eq(201)
end
it 'Returns user all his documents' do
post '/api/v2/resource/documents', params: params, headers: auth_header
expect(response.status).to eq(201)
get '/api/v2/resource/documents', headers: auth_header
response_arr = JSON.parse(response.body)
expect(response_arr.count).to eq(1)
expect(response_arr.last['upload']).to_not be_nil
expect(response_arr.last['doc_type']).to eq('Passport')
expect(response_arr.last['doc_expire']).to eq('3020-01-22')
expect(response_arr.last['doc_number']).to eq('AA****BB')
expect(response.status).to eq(200)
end
context 'without masking' do
before do
Barong::App.config.stub(:api_data_masking_enabled).and_return(false)
end
it 'Returns user all his documents' do
post '/api/v2/resource/documents', params: params, headers: auth_header
expect(response.status).to eq(201)
get '/api/v2/resource/documents', headers: auth_header
response_arr = JSON.parse(response.body)
expect(response_arr.count).to eq(1)
expect(response_arr.last['upload']).to_not be_nil
expect(response_arr.last['doc_type']).to eq('Passport')
expect(response_arr.last['doc_expire']).to eq('3020-01-22')
expect(response_arr.last['doc_number']).to eq('AA1234BB')
expect(response.status).to eq(200)
end
end
it 'Returns error without token' do
post '/api/v2/resource/documents', params: params
expect(response.status).to eq(401)
get '/api/v2/resource/documents', params: params
expect(response.status).to eq(401)
end
after(:all) { User.destroy_all }
end
context 'event API behavior' do
let!(:url) { '/api/v2/resource/documents' }
let(:request_params) do
{
doc_type: 'Passport',
doc_expire: '3020-01-22',
doc_number: 'AA1234BB',
upload: [
image
]
}
end
before do
allow(EventAPI).to receive(:notify)
end
it 'receive model.document.created notify' do
expect(EventAPI).to receive(:notify).ordered.with('model.user.created', hash_including(:record))
expect(EventAPI).to receive(:notify).ordered.with('model.document.created', hash_including(:record))
post url, headers: auth_header, params: request_params
end
end
end

View File

@@ -0,0 +1,88 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Labels API.' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let(:post_params) do
{
key: ::Faker::Internet.slug(glue: '-'),
value: ::Faker::Internet.slug(glue: '-')
}
end
context 'Happy paths for the current user.' do
let(:test_user) { create(:user, role: "admin") }
let!(:label) { create :label, user: test_user }
let!(:label2) { create :label, user: test_user }
let!(:label3) { create :label, user: test_user }
it 'Return lables for current user' do
get '/api/v2/resource/labels', headers: auth_header
expect(response.status).to eq(200)
expect(json_body.size).to eq(3)
expect(json_body.last[:key]).to eq(label3.key)
end
it 'Return a label by key' do
get "/api/v2/resource/labels/#{label.key}", headers: auth_header
expect(response.status).to eq(200)
expect(json_body[:key]).to eq(label.key)
end
it 'Create a label' do
post '/api/v2/resource/labels', params: post_params, headers: auth_header
expect(response.status).to eq(201)
persisted = Label.find_by(key: json_body[:key])
expect(persisted.key).to eq(post_params[:key])
expect(persisted.value).to eq(post_params[:value])
expect(persisted.scope).to eq('public')
expect(persisted.user_id).to eq(test_user.id)
end
it 'Update a label' do
patch "/api/v2/resource/labels/#{label.key}", params: post_params, headers: auth_header
expect(response.status).to eq(200)
persisted = Label.find_by(key: label.key)
expect(persisted.value).to eq(post_params[:value])
expect(persisted.scope).to eq('public')
end
it 'Delete a label' do
delete "/api/v2/resource/labels/#{label.key}", headers: auth_header
expect(response.status).to eq(204)
expect(Label.find_by(key: label.key)).to be_nil
end
end
context 'Errors.' do
let(:test_user) { create(:user, role: "admin") }
let!(:label) { create :label, user: test_user }
it 'Respond with errors on create if existing key is used for current user' do
post '/api/v2/resource/labels', params: post_params.merge(key: label.key), headers: auth_header
expect(response.status).to eq(422)
expect(response.body).to be_include('key.taken')
end
it 'Respond with error if attempted to update a private label' do
label.update(scope: 'private')
patch "/api/v2/resource/labels/#{label.key}", params: post_params, headers: auth_header
expect(response.status).to eq(400)
end
it 'Respond with error if key not found' do
get '/api/v2/resource/labels/blah', headers: auth_header
expect(response.status).to eq(404)
end
end
end

View File

@@ -0,0 +1,129 @@
# frozen_string_literal: true
describe 'Api::V2::Resource::Otp' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:do_request) { post '/api/v2/resource/otp/enable', headers: auth_header }
let(:otp_code) { '111111' }
context 'valid request' do
before do
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, otp_code) { true }
end
it 'user enables 2fa successfully' do
post '/api/v2/resource/otp/enable', headers: auth_header, params: {
code: otp_code
}
expect(response.status).to eq 201
expect(test_user.reload.otp).to eq true
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).not_to eq nil
end
it 'user disables 2fa successfully' do
test_user.update(otp: true)
test_user.labels.create(value: 'verified', key: :otp, scope: :private)
post '/api/v2/resource/otp/disable', headers: auth_header, params: {
code: otp_code
}
expect(response.status).to eq 200
expect(test_user.reload.otp).to eq false
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
end
it 'user verify otp Code' do
test_user.update(otp: true)
test_user.labels.create(value: 'verified', key: :otp, scope: :private)
post '/api/v2/resource/otp/verify', headers: auth_header, params: {
code: otp_code
}
expect(response.status).to eq 201
expect(test_user.reload.otp).to eq true
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).not_to eq nil
end
end
context 'incomplete request' do
it 'user receives error in enabling' do
post '/api/v2/resource/otp/enable', headers: auth_header, params: {
code: otp_code
}
expect(response.status).to eq 422
expect(test_user.reload.otp).to eq false
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
end
it 'user receives error in disabling' do
test_user.update(otp: true)
test_user.labels.create(key: :otp, scope: :private)
post '/api/v2/resource/otp/disable', headers: auth_header, params: {
code: otp_code
}
expect(response.status).to eq 422
expect(test_user.reload.otp).to eq true
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
end
end
context 'already done' do
before do
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, otp_code) { true }
end
it 'enabling otp' do
test_user.update(otp: true)
test_user.labels.create(value: 'verified', key: :otp, scope: :private)
post '/api/v2/resource/otp/enable', headers: auth_header, params: {
code: otp_code
}
expect(response.status).to eq 400
expect(test_user.reload.otp).to eq true
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).not_to eq nil
end
it 'disabling otp' do
post '/api/v2/resource/otp/disable', headers: auth_header, params: {
code: otp_code
}
expect(response.status).to eq 400
expect(test_user.reload.otp).to eq false
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
end
it 'otp disabled' do
post '/api/v2/resource/otp/verify', headers: auth_header, params: {
code: otp_code
}
expect(response.status).to eq 400
expect(test_user.reload.otp).to eq false
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
end
it 'otp enabled' do
test_user.update(otp: true)
test_user.labels.create(value: 'verified', key: :otp, scope: :private)
post '/api/v2/resource/otp/generate_qrcode', headers: auth_header
expect(response.status).to eq 400
expect(test_user.reload.otp).to eq true
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).not_to eq nil
end
end
end

View File

@@ -0,0 +1,469 @@
# frozen_string_literal: true
describe 'Api::V2::Resources::Phones' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
context 'Twilio Verify Service' do
before do
allow(Barong::App.config).to receive(:twilio_provider).and_return(TwilioVerifyService)
end
describe 'GET /api/v2/resource/phones' do
let!(:phone1) { create(:phone, user: test_user) }
let!(:phone2) { create(:phone, user: test_user, number: 12345677711 ) }
let(:do_request) do
get '/api/v2/resource/phones', headers: auth_header
end
it 'returns list of user\'s phones' do
do_request
expect(json_body.length).to eq 2
expect(json_body.first[:number]).to eq phone1.sub_masked_number
expect(json_body.first[:country]).to eq phone1.country
expect(json_body.first[:validated_at]).to eq phone1.validated_at
expect(json_body.second[:number]).to eq phone2.sub_masked_number
expect(json_body.second[:country]).to eq phone2.country
expect(json_body.second[:validated_at]).to eq phone2.validated_at
end
context 'list of users phone without masking' do
before do
Barong::App.config.stub(:api_data_masking_enabled).and_return(false)
end
it 'returns list of user\'s phones' do
do_request
expect(json_body.length).to eq 2
expect(json_body.first[:number]).to eq phone1.number
expect(json_body.first[:country]).to eq phone1.country
expect(json_body.first[:validated_at]).to eq phone1.validated_at
expect(json_body.second[:number]).to eq phone2.number
expect(json_body.second[:country]).to eq phone2.country
expect(json_body.second[:validated_at]).to eq phone2.validated_at
end
end
end
describe 'POST /api/v2/resource/phones' do
let(:do_request) do
post '/api/v2/resource/phones', params: params, headers: auth_header
end
let(:phone_number) { nil }
let(:params) { { phone_number: phone_number, channel: 'sms' } }
describe 'errors: ' do
context 'when phone is missing' do
let(:phone_number) { nil }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.empty_phone_number"])
expect_status.to eq 422
end
end
context 'when phone is invalid' do
let(:phone_number) { '123' }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.invalid_num"])
expect_status.to eq 400
end
end
context 'when phone is already exists' do
let!(:phone) do
create(:phone, validated_at: validated_at)
end
let(:phone_number) { phone.number }
context 'when phone verified' do
let(:validated_at) { 1.minutes.ago }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.number_exist"])
expect_status.to eq 400
end
end
context 'when phone is not verified' do
let(:validated_at) { 1.minutes.ago }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.number_exist"])
expect_status.to eq 400
end
end
context 'when phone verified but number is not sanitized' do
let(:validated_at) { 1.minutes.ago }
let(:phone_number) { "++#{phone.number}" }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.number_exist"])
expect_status.to eq 400
end
end
end
end
context 'valid story' do
before do
allow(Barong::App.config.twilio_provider).to receive(:send_code).and_return(false)
end
context 'when phone is not verified' do
let(:phone_number) { build(:phone).number }
it 'assigns a phone to account and send sms' do
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
end
end
context 'when phone is valid' do
let(:phone_number) { build(:phone).number }
it 'creates a phone and send code via sms channel' do
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
end
it 'creates a phone and send code via call channel' do
params['channel'] = 'call'
do_request
expect_body.to eq(message: 'Code was sent successfully via call')
expect_status.to eq 201
end
end
end
context 'when phone is on national format with zero' do
let(:phone_number) { '+44 07418084106' }
let(:international_phone) { '447418084106' }
it 'creates a phone and send sms' do
allow(Barong::App.config.twilio_provider).to receive(:send_code).and_return(false)
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
expect(Phone.last.number).to eq(international_phone)
end
end
context 'when phone exists on international format' do
let(:phone_number) { '+44 07418084106' }
let(:international_phone) { '447418084106' }
let!(:phone) do
create(:phone, validated_at: 1.minute.ago, number: international_phone)
end
it 'renders an error' do
do_request
expect_body.to eq errors: ["resource.phone.number_exist"]
expect_status.to eq 400
end
end
end
describe 'POST /api/v2/resource/phones/verify' do
let(:do_request) do
post '/api/v2/resource/phones/verify', params: params, headers: auth_header
end
let(:params) do
{
phone_number: phone_number,
verification_code: verification_code
}
end
let(:verification_code) { '12345' }
context 'when phone is missing and code is missing' do
let(:phone_number) { nil }
let(:verification_code) { '' }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.empty_phone_number", "resource.phone.empty_verification_code"])
expect_status.to eq 422
end
end
context 'when phone is invalid' do
let(:phone_number) { '123' }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.invalid_num"])
expect_status.to eq 400
end
end
context 'when phone is already exists and verified' do
let!(:phone) do
create(:phone, validated_at: 1.minutes.ago)
end
let(:phone_number) { phone.number }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.number_exist"])
expect_status.to eq 400
end
end
context 'when phone exists in international format' do
let!(:phone) do
create(:phone, validated_at: 1.minutes.ago, number: international_phone)
end
let(:phone_number) { '+44 07418084106' }
let(:international_phone) { '447418084106' }
it 'renders an error' do
do_request
expect_body.to eq(errors: ["resource.phone.number_exist"])
expect_status.to eq 400
end
end
context 'when phone is not found in test_user' do
let!(:phone) { create(:phone) }
let(:phone_number) { phone.number }
it 'rendens an error' do
allow(Barong::App.config.twilio_provider).to receive(:verify_code?).and_return(false)
do_request
expect_body.to eq(errors: ["resource.phone.doesnt_exist"])
expect_status.to eq 404
end
end
context 'when phone is found in test_user but code is invalid' do
let!(:phone) { create(:phone, user: test_user) }
let(:phone_number) { phone.number }
it 'rendens an error' do
allow(Barong::App.config.twilio_provider).to receive(:verify_code?).and_return(false)
do_request
expect_body.to eq(errors: ["resource.phone.verification_invalid"])
expect_status.to eq 404
end
end
context 'when phone and code is valid' do
let(:phone) { create(:phone, user: test_user) }
let(:phone_number) { phone.number }
it 'responses with success' do
allow(Barong::App.config.twilio_provider).to receive(:verify_code?).and_return(true)
set_level(test_user, 1)
do_request
expect_status.to eq 201
expect(phone.reload.validated_at).to be
test_user.update_level
expect(test_user.reload.level).to eq 2
end
end
end
end
context 'With Twilio SMS Sender service' do
let(:phone_number) { phone.number }
let(:do_request) do
post '/api/v2/resource/phones', params: params, headers: auth_header
end
let(:params) { { phone_number: phone_number, channel: 'sms' } }
let(:verification_code) { '12345' }
context 'valid story sms sender service' do
let(:mock_sms) { Barong::MockSMS }
before do
allow(Barong::App.config).to receive(:twilio_provider).and_return(MockPhoneVerifyService)
end
context 'when phone is not verified' do
let(:phone_number) { build(:phone).number }
it 'assigns a phone to account and send sms' do
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
expect(mock_sms.messages.last.to).to eq "+#{phone_number}"
end
it 'sends a default sms content' do
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
expect(mock_sms.messages.last.body).to start_with('Your verification code for Barong: ')
end
it 'sends a custom message with content before code' do
allow(Barong::App.config).to receive(:sms_content_template).and_return('Please confirm your phone with the following code: {{code}}')
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
expect(mock_sms.messages.last.body).to start_with('Please confirm your phone with the following code: ')
end
it 'sends a custom message with content after code' do
allow(Barong::App.config).to receive(:sms_content_template).and_return('{{code}} - this is your confirmation code')
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
expect(mock_sms.messages.last.body).to end_with(' - this is your confirmation code')
end
it 'sends a custom message with code in the midle of the content' do
allow(Barong::App.config).to receive(:sms_content_template).and_return('Following code: {{code}} should be used for phone confirmation')
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
expect(mock_sms.messages.last.body).to start_with('Following code: ')
expect(mock_sms.messages.last.body).to end_with(' should be used for phone confirmation')
end
end
context 'when phone is valid' do
let(:phone_number) { build(:phone).number }
it 'creates a phone and send sms' do
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
expect(mock_sms.messages.last.to).to eq "+#{phone_number}"
end
it 'doesnt change code in DB on phone initialize' do
do_request
expect_body.to eq(message: 'Code was sent successfully via sms')
expect_status.to eq 201
code_after_create = Phone.last.code
# Phone.last initilazes phone
code_after_initialize = Phone.last.code
expect(code_after_create).to eq code_after_initialize
end
end
end
context 'POST /api/v2/resource/phones/verify' do
let(:do_request) do
post '/api/v2/resource/phones/verify', params: params, headers: auth_header
end
let(:params) do
{
phone_number: phone_number,
verification_code: verification_code
}
end
let(:verification_code) { '12345' }
context 'when phone is missing and code is missing' do
let(:phone_number) { nil }
let(:verification_code) { '' }
it 'renders an error' do
do_request
expect_body.to eq(errors: ['resource.phone.empty_phone_number', 'resource.phone.empty_verification_code'])
expect_status.to eq 422
end
end
context 'when phone is invalid' do
let(:phone_number) { '123' }
it 'renders an error' do
do_request
expect_body.to eq(errors: ['resource.phone.invalid_num'])
expect_status.to eq 400
end
end
context 'when phone is already exists and verified' do
let!(:phone) do
create(:phone, validated_at: 1.minutes.ago)
end
let(:phone_number) { phone.number }
it 'renders an error' do
do_request
expect_body.to eq(errors: ['resource.phone.number_exist'])
expect_status.to eq 400
end
end
context 'when phone exists in international format' do
let!(:phone) do
create(:phone, validated_at: 1.minutes.ago, number: international_phone)
end
let(:phone_number) { '+44 07418084106' }
let(:international_phone) { '447418084106' }
it 'renders an error' do
do_request
expect_body.to eq(errors: ['resource.phone.number_exist'])
expect_status.to eq 400
end
end
context 'when phone is not found in test_user' do
let!(:phone) { create(:phone) }
let(:phone_number) { phone.number }
it 'rendens an error' do
do_request
expect_body.to eq(errors: ['resource.phone.doesnt_exist'])
expect_status.to eq 404
end
end
context 'when phone is found in test_user but code is invalid still approves because of mock provider' do
let!(:phone) { create(:phone, user: test_user) }
let(:phone_number) { phone.number }
it 'approves phone' do
do_request
expect_status.to eq 201
expect(phone.reload.validated_at).to be
end
end
context 'when phone and code is valid' do
let(:phone) { create(:phone, user: test_user) }
let(:phone_number) { phone.number }
let(:verification_code) { phone.code }
it 'responses with success' do
set_level(test_user, 1)
do_request
expect_status.to eq 201
expect(phone.reload.validated_at).to be
test_user.update_level
expect(test_user.reload.level).to eq 2
end
end
end
end
end

View File

@@ -0,0 +1,572 @@
# frozen_string_literal: true
describe 'API::V2::Resource::Profiles' do
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'local') }
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:optional_params) do
{
metadata: {
gender: Faker::Creature::Dog.gender,
place_of_birth: Faker::Address.city
}.to_json
}
end
describe 'POST /api/v2/resource/profiles' do
describe 'KYC verification' do
let!(:request_params) do
{
last_name: Faker::Name.last_name,
first_name: Faker::Name.first_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code,
city: Faker::Address.city,
address: Faker::Address.state,
postcode: Faker::Address.zip_code
}
end
let!(:url) { '/api/v2/resource/profiles' }
it 'triggers KycService' do
expect(KycService).to receive(:profile_step)
post url, params: request_params, headers: auth_header
expect(response.status).to eq(201)
end
context 'Local verification' do
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'local') }
it 'adds a label for first verification' do
expect(test_user.labels).to eq([])
post url, params: request_params, headers: auth_header
expect(test_user.reload.labels.count).to eq(1)
expect(test_user.reload.labels.first.key).to eq('profile')
expect(test_user.reload.labels.first.value).to eq('drafted')
end
it 'doesnt trigger KYCAID worker' do
expect(KycService).to receive(:profile_step)
expect(KYC::Kycaid::ApplicantWorker).not_to receive(:perform_async)
post url, params: request_params, headers: auth_header
expect(response.status).to eq(201)
end
it 'updates label on re-submit' do
expect(test_user.labels).to eq([])
post url, params: request_params, headers: auth_header
expect(test_user.reload.labels.count).to eq(1)
expect(test_user.reload.labels.first.key).to eq('profile')
expect(test_user.reload.labels.first.value).to eq('drafted')
put url, params: request_params.merge(confirm: true), headers: auth_header
expect(test_user.reload.labels.count).to eq(1)
expect(test_user.reload.labels.first.key).to eq('profile')
expect(test_user.reload.labels.first.value).to eq('submitted')
end
end
context 'KYCAID verification' do
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'kycaid') }
it 'adds a label for first verification' do
expect(test_user.labels).to eq([])
post url, params: request_params, headers: auth_header
expect(test_user.reload.labels.count).to eq(1)
expect(test_user.reload.labels.first.key).to eq('profile')
expect(test_user.reload.labels.first.value).to eq('drafted')
end
it 'updates label on re-submit' do
expect(test_user.labels).to eq([])
post url, params: request_params, headers: auth_header
expect(test_user.reload.labels.count).to eq(1)
expect(test_user.reload.labels.first.key).to eq('profile')
expect(test_user.reload.labels.first.value).to eq('drafted')
put url, params: request_params.merge(confirm: true), headers: auth_header
expect(test_user.reload.labels.count).to eq(1)
expect(test_user.reload.labels.first.key).to eq('profile')
expect(test_user.reload.labels.first.value).to eq('submitted')
end
it 'triggers KYCAID worker on submitted state' do
expect(KYC::Kycaid::ApplicantWorker).to receive(:perform_async)
post url, params: request_params.merge(confirm: true), headers: auth_header
expect(response.status).to eq(201)
end
it 'doesnt trigger KYCAID worker on drafted state' do
expect(KYC::Kycaid::ApplicantWorker).not_to receive(:perform_async)
post url, params: request_params, headers: auth_header
expect(response.status).to eq(201)
end
it 'doesnt trigger KYCAID worker on rejected state' do
post url, params: request_params.merge(confirm: true), headers: auth_header
expect(response.status).to eq(201)
expect(KYC::Kycaid::ApplicantWorker).not_to receive(:perform_async)
test_user.reload.profiles.last.update(state: 'rejected')
end
it 'doesnt trigger KYCAID worker on verified state' do
post url, params: request_params.merge(confirm: true), headers: auth_header
expect(response.status).to eq(201)
expect(KYC::Kycaid::ApplicantWorker).not_to receive(:perform_async)
test_user.reload.profiles.last.update(state: 'verified')
end
end
end
let!(:url) { '/api/v2/resource/profiles' }
let!(:user_info) { '/api/v2/resource/users/me' }
let!(:request_params) do
{
last_name: Faker::Name.last_name,
first_name: Faker::Name.first_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.state,
postcode: Faker::Address.zip_code
}
end
let!(:asian_params) do
{
last_name: "",
first_name: "채원",
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: "안산시",
address: "사직로3길 23",
postcode: Faker::Address.zip_code
}
end
it 'accept / , ; in address' do
request_params[:address] = '28/2 Kevin Brook; Miami, USA'
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:address]).to eq request_params[:address]
end
it 'accept . in address' do
request_params[:address] = 'Larkin Fork.South, New York/AP'
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:address]).to eq request_params[:address]
end
it 'accept # ~ \ : " & ( ) in address' do
request_params[:address] = '28~1/2 \"Kevin & Brook": (Miami, USA)'
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:address]).to eq request_params[:address]
end
it "accept ' in address" do
request_params[:address] = "'Larkin Fork' South New York"
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:address]).to eq request_params[:address]
end
it "accept in address" do
request_params[:address] = "'LarkinFork' South New York"
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:address]).to eq request_params[:address]
end
it "doesn't accept @ in address" do
request_params[:address] = "'LarkinFork' @South New York"
expect { post url, params: request_params, headers: auth_header }
.not_to change { Profile.count }
expect(response.status).to eq(422)
end
it 'accept . in city' do
request_params[:city] = 'St. Petersburg'
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:city]).to eq request_params[:city]
end
it 'accept dash in city' do
request_params[:city] = 'HubliDharwad'
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:city]).to eq request_params[:city]
end
it 'accept hyphen in city' do
request_params[:city] = 'Hubli-Dharwad'
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:city]).to eq request_params[:city]
end
it 'accept \' in city' do
request_params[:city] = 'Cava de\' Tirreni'
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:city]).to eq request_params[:city]
end
it "doesn't accept # in city" do
request_params[:city] = 'Cava de\' #Tirreni'
expect { post url, params: request_params, headers: auth_header }
.not_to change { Profile.count }
expect(response.status).to eq(422)
end
it "doesn't accept @ in city" do
request_params[:city] = 'Cava de\' @Tirreni'
expect { post url, params: request_params, headers: auth_header }
.not_to change { Profile.count }
expect(response.status).to eq(422)
end
it 'creates new profile with only required fields' do
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:last_name]).to eq request_params[:last_name].sub(/(?<=\A.{1})(.*)/) { |match| '*' * match.length }
expect(res[:first_name]).to eq request_params[:first_name]
expect(res[:dob]).to eq request_params[:dob].to_s.sub(/(?<=\A.{8})(.*)/) { |match| '*' * match.length }
expect(res[:country]).to eq request_params[:country]
expect(res[:city]).to eq request_params[:city]
expect(res[:address]).to eq request_params[:address]
expect(res[:postcode]).to eq request_params[:postcode]
expect(res[:metadata]).to be_blank
end
it 'creates new profile with only required fields without masking' do
Barong::App.config.stub(:api_data_masking_enabled).and_return(false)
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:last_name]).to eq request_params[:last_name]
expect(res[:first_name]).to eq request_params[:first_name]
expect(res[:dob]).to eq request_params[:dob].to_s
expect(res[:country]).to eq request_params[:country]
expect(res[:city]).to eq request_params[:city]
expect(res[:address]).to eq request_params[:address]
expect(res[:postcode]).to eq request_params[:postcode]
expect(res[:metadata]).to be_blank
end
it 'creates new profile with corean symbols fields' do
expect { post url, params: asian_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:last_name]).to eq asian_params[:last_name]
expect(res[:first_name]).to eq asian_params[:first_name]
expect(res[:dob]).to eq asian_params[:dob].to_s.sub(/(?<=\A.{8})(.*)/) { |match| '*' * match.length }
expect(res[:country]).to eq asian_params[:country]
expect(res[:city]).to eq asian_params[:city]
expect(res[:address]).to eq asian_params[:address]
expect(res[:postcode]).to eq asian_params[:postcode]
expect(res[:metadata]).to be_blank
end
it 'creates new profile with all metadata fields' do
expect { post url, params: request_params.merge(optional_params), headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
res = json_body
expect(res[:metadata]).to eq(optional_params[:metadata])
end
it 'renders an error if metadata is not json' do
expect { post url, params: request_params.merge({ metadata: '{ bar: baz }' }), headers: auth_header }
.not_to change { Profile.count }
expect_status_to_eq 422
expect_body.to eq(errors: ["metadata.invalid_format"])
end
context 'create another one profile with state drafted' do
before do
post url, params: request_params.merge(optional_params), headers: auth_header
end
it 'doesn\'t create new profile' do
post url, params: request_params.merge(optional_params), headers: auth_header
result = json_body
expect(*result[:errors]).to eq('state.exists')
end
end
context 'create another one profile with state submitted' do
before do
post url, params: request_params.merge(optional_params, confirm: true), headers: auth_header
end
it 'doesn\'t create new profile' do
post url, params: request_params.merge(optional_params, confirm: true), headers: auth_header
result = json_body
expect(*result[:errors]).to eq('state.exists')
end
end
context 'create another one profile with state submitted and drafted' do
before do
post url, params: request_params.merge(optional_params, confirm: true), headers: auth_header
end
it 'doesn\'t create new profile' do
post url, params: request_params.merge(optional_params), headers: auth_header
result = json_body
expect(*result[:errors]).to eq('state.exists')
end
end
context 'partial creating profile' do
context 'empty params' do
it 'returns profile' do
expect { post url, params: {}, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
expect(json_body[:state]).to eq('drafted')
expect(json_body[:metadata].blank?).to be_truthy
Profile::OPTIONAL_PARAMS.each { |p|
expect(json_body[p].blank?).to be_truthy
}
Profile::OPTIONAL_PARAMS.each { |p|
expect(json_body.with_indifferent_access[p].blank?).to be_truthy
}
expect(Profile.last.user.labels.find_by(key: 'profile').value).to eq('drafted')
end
end
context 'several params' do
let(:params) { { last_name: Faker::Name.last_name, first_name: Faker::Name.first_name } }
it 'returns profile' do
expect { post url, params: params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
expect(json_body[:first_name].nil?).to be_falsey
expect(json_body[:last_name].nil?).to be_falsey
expect(json_body[:state]).to eq('drafted')
expect(json_body[:metadata].blank?).to be_truthy
(Profile::OPTIONAL_PARAMS - params.stringify_keys.keys).each { |p|
expect(json_body.with_indifferent_access[p].blank?).to be_truthy
}
expect(Profile.last.user.labels.find_by(key: :profile).value).to eq('drafted')
end
end
context 'several params with profile confirmation' do
let(:params) { { last_name: Faker::Name.last_name, first_name: Faker::Name.first_name } }
it 'returns profile' do
expect { post url, params: params.merge(confirm: true), headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
expect(json_body[:first_name].nil?).to be_falsey
expect(json_body[:last_name].nil?).to be_falsey
expect(json_body[:state]).to eq('submitted')
expect(json_body[:metadata].blank?).to be_truthy
(Profile::OPTIONAL_PARAMS - params.stringify_keys.keys).each { |p|
expect(json_body.with_indifferent_access[p].blank?).to be_truthy
}
expect(Profile.last.user.labels.find_by(key: :profile).value).to eq('submitted')
end
end
context 'full profile params' do
it 'returns profile' do
expect { post url, params: request_params, headers: auth_header }
.to change { Profile.count }.by (1)
expect(response.status).to eq(201)
expect(json_body[:state]).to eq('drafted')
expect(json_body[:metadata].blank?).to be_truthy
request_params.keys.each { |p|
expect(json_body[p].present?).to be_truthy
}
expect(Profile.last.user.labels.find_by(key: 'profile').value).to eq('drafted')
end
end
end
end
describe 'GET /api/v2/resource/profiles/me' do
let!(:url) { '/api/v2/resource/profiles/me' }
let!(:request_params) do
{
first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name,
dob: Faker::Date.birthday,
address: Faker::Address.state,
postcode: Faker::Address.zip_code,
city: Faker::Address.city,
country: Faker::Address.country_code_long
}
end
it 'returns user profile data with metadata' do
post '/api/v2/resource/profiles', params: request_params.merge(optional_params),
headers: auth_header
expect(response.status).to eq(201)
get url, headers: auth_header
expect(response.status).to eq(200)
last_name = request_params[:last_name].sub(/(?<=\A.{1})(.*)/) { |match| '*' * match.length }
dob = request_params[:dob].to_s.sub(/(?<=\A.{8})(.*)/) { |match| '*' * match.length }
expected_json = request_params.merge(state: 'drafted', last_name: last_name,dob: dob).merge(optional_params).to_json
expect(JSON.parse(response.body)[0].except('created_at', 'updated_at')).to eq(JSON.parse(expected_json))
end
end
context 'event API behavior' do
let!(:url) { '/api/v2/resource/profiles' }
let!(:request_params) do
{
last_name: Faker::Name.last_name,
first_name: Faker::Name.first_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.state,
postcode: Faker::Address.zip_code
}
end
before do
allow(EventAPI).to receive(:notify)
end
it 'receive model.profile.created notify' do
expect(EventAPI).to receive(:notify).ordered.with('model.user.created', hash_including(:record))
expect(EventAPI).to receive(:notify).ordered.with('model.profile.created', hash_including(:record))
post url, params: request_params, headers: auth_header
end
it 'receive model.profile.created notify with user and profile params' do
expect(EventAPI).to receive(:notify).ordered.with('model.user.created', hash_including(:record))
expect(EventAPI).to receive(:notify).ordered.with(
'model.profile.created',
hash_including(
record: {
user: anything,
address: anything,
city: anything,
country: anything,
created_at: anything,
updated_at: anything,
dob: anything,
first_name: anything,
last_name: anything,
postcode: anything
}
)
)
post url, params: request_params, headers: auth_header
end
end
describe 'PUT /api/v2/resource/profiles' do
let!(:url) { '/api/v2/resource/profiles' }
let!(:request_params) do
{
last_name: Faker::Name.last_name,
first_name: Faker::Name.first_name,
dob: Faker::Date.birthday,
country: Faker::Address.country_code_long,
city: Faker::Address.city,
address: Faker::Address.street_address,
postcode: Faker::Address.zip_code
}
end
context 'user without profile' do
it 'renders an error when profile doesnt exist' do
put url, params: request_params, headers: auth_header
expect_status.to eq(404)
expect_body.to eq(errors: ['resource.profile.doesnt_exist_or_not_editable'])
end
end
context 'user with profile' do
let!(:profile) { create(:profile, user: test_user)}
it 'returns submitted profile' do
expect { put url, params: request_params.merge(confirm: true), headers: auth_header }
.not_to change { Profile.count }
expect(response.status).to eq(200)
expect(json_body[:state]).to eq('submitted')
expect(profile[:metadata]).to be_blank
end
end
context 'user with partial profile' do
let!(:profile) { create(:profile, user: test_user, last_name: nil, first_name: nil) }
it 'returns partial profile' do
expect { put url, params: request_params.except(:first_name), headers: auth_header }
.not_to change { Profile.count }
expect(response.status).to eq(200)
expect(json_body[:state]).to eq('drafted')
expect(json_body[:metadata]).to be_blank
end
it 'returns completed profile' do
expect { put url, params: request_params, headers: auth_header }
.not_to change { Profile.count }
expect(response.status).to eq(200)
expect(json_body[:state]).to eq('drafted')
expect(json_body[:metadata]).to be_blank
end
end
end
end

View File

@@ -0,0 +1,297 @@
# frozen_string_literal: true
describe 'Api::V2::Resource::ServiceAccounts' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:create_service_account_permission) do
create :permission,
role: 'service_account'
end
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:test_user) { create(:user, otp: otp_enabled, role: 'member') }
let!(:service_account) { create(:service_account, user: test_user) }
let(:otp_enabled) { true }
let!(:first_api_key) { create :api_key, key_holder_account: service_account }
let!(:second_api_key) { create :api_key, key_holder_account: service_account }
let(:valid_otp_code) { '1357' }
let(:invalid_otp_code) { '1234' }
let(:otp_code) { valid_otp_code }
before do
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, valid_otp_code) { true }
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, invalid_otp_code) { false }
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, nil) { false }
end
describe 'GET /api/v2/resource/service_accounts' do
let(:do_request) do
get '/api/v2/resource/service_accounts',
headers: auth_header
end
let(:expected_fields) do
{
uid: service_account.uid,
email: service_account.email,
owner_id: test_user.id,
role: service_account.role,
level: service_account.level,
state: service_account.state,
}
end
it 'Return service accounts for current user' do
do_request
expect(json_body.count).to eq 1
expect(response.status).to eq(200)
expect(json_body.first).to include(expected_fields)
end
end
describe 'GET /api/v2/resource/service_accounts/api_keys/' do
let(:get_params) do
{
service_account_uid: service_account.uid,
ordering: 'asc',
order_by: 'id'
}
end
let(:post_params) do
{
service_account_uid: service_account.uid,
scope: 'trade',
algorithm: 'HS256'
}
end
let(:do_request) do
post '/api/v2/resource/service_accounts/api_keys',
params: post_params.merge(totp_code: otp_code),
headers: auth_header
end
let(:do_get_request) { get "/api/v2/resource/service_accounts/api_keys", params: get_params, headers: auth_header }
let(:expected_fields) do
{
kid: first_api_key.kid,
state: first_api_key.state,
scope: %w[trade]
}
end
let(:totp_code) { valid_otp_code }
it 'Return api key for current account in ASC order' do
do_get_request
expect(json_body.count).to eq 2
expect(response.status).to eq(200)
expect(json_body.first).to include(expected_fields)
end
it 'Return api key for current account in DESC order' do
get_params.merge!(ordering: 'desc')
do_get_request
expect(json_body.count).to eq 2
expect(response.status).to eq(200)
expect(json_body.second).to include(expected_fields)
end
context 'when invalid ordering' do
it 'renders an error' do
get_params.merge!(ordering: 'resc')
do_get_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.service_accounts.invalid_ordering"])
end
end
context 'when invalid order_by' do
it 'renders an error' do
get_params.merge!(order_by: 'invalid')
do_get_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.service_accounts.invalid_attribute"])
end
end
context 'when otp is not enabled' do
let(:otp_enabled) { false }
it 'renders an error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.service_accounts.2fa_disabled"])
end
end
context 'when code is invalid' do
let(:otp_code) { invalid_otp_code }
it 'renders an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.service_accounts.invalid_totp"])
end
end
end
describe 'POST /api/v2/resource/api_keys' do
let(:do_request) do
post '/api/v2/resource/api_keys',
params: params.merge(totp_code: otp_code),
headers: auth_header
end
let(:otp_code) { valid_otp_code }
context 'when fields are valid' do
let(:params) do
{
scope: 'trade',
algorithm: 'HS256'
}
end
let(:expected_fields) do
{
state: 'active',
scope: params[:scope].split(','),
algorithm: 'HS256'
}
end
it 'Create an api key' do
expect { do_request }.to change { APIKey.count }.by(1)
expect(response.status).to eq(201)
expect_body.to include(expected_fields)
end
it 'does not create api key if vault is down' do
allow(Vault::Rails).to receive(:encrypt).and_raise(Vault::VaultError)
expect { do_request }.not_to change { APIKey.count }
expect(response.status).to eq(422)
expect_body.to eq(errors: ["api_key.could_not_save_secret"])
end
context 'when otp is not enabled' do
let(:otp_enabled) { false }
it 'renders an error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
end
end
context 'when code is invalid' do
let(:otp_code) { invalid_otp_code }
it 'renders an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
end
end
context 'when algorithm is invalid' do
it 'renders an error' do
params[:algorithm] = 'kek'
do_request
expect_body.to eq(errors: ["kid.blank", "algorithm.inclusion"])
end
end
end
end
describe 'PUT /api/v2/resource/service_accounts/api_keys/:kid' do
let(:do_request) do
put "/api/v2/resource/service_accounts/api_keys/#{first_api_key.kid}", params: params.merge(totp_code: otp_code),
headers: auth_header
end
let(:otp_code) { valid_otp_code }
context 'when valid fields' do
let(:params) do
{
service_account_uid: service_account.uid,
state: 'inactive',
scope: 'sell',
algorithm: 'HS256'
}
end
it 'Updates a state' do
expect { do_request }.to change { first_api_key.reload.state }
.from('active').to('inactive')
expect(response.status).to eq(200)
end
it 'Updates a scope' do
expect { do_request }.to change { first_api_key.reload.scope }
.from(['trade']).to(['sell'])
expect(response.status).to eq(200)
end
context 'when otp is not enabled' do
let(:otp_enabled) { false }
it 'renders an error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.service_accounts.2fa_disabled"])
end
end
context 'when code is invalid' do
let(:otp_code) { invalid_otp_code }
it 'renders an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.service_accounts.invalid_totp"])
end
end
end
end
describe 'DELETE /api/v2/resource/service_accounts/api_keys/:uid' do
let(:do_request) do
delete "/api/v2/resource/service_accounts/api_keys/#{first_api_key.kid}?totp_code=#{otp_code}&service_account_uid=#{service_account.uid}",
headers: auth_header
end
let(:otp_code) { valid_otp_code }
it 'Removes an api key' do
do_request
expect(response.status).to eq(204)
end
context 'when otp is not enabled' do
let(:otp_enabled) { false }
it 'renders an error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.service_accounts.2fa_disabled"])
end
end
context 'when code is invalid' do
let(:otp_code) { invalid_otp_code }
it 'renders an error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.service_accounts.invalid_totp"])
end
end
end
end

View File

@@ -0,0 +1,270 @@
# frozen_string_literal: true
describe 'Api::V1::Profiles' do
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
describe 'GET /api/v2/resource/users/me' do
it 'should reply permissions denied' do
get '/api/v2/resource/users/me'
expect_body.to eq(errors: ["jwt.decode_and_verify"])
expect(response.status).to eq(401)
end
it 'should allow traffic with Authorization' do
get '/api/v2/resource/users/me', headers: auth_header
expect(json_body[:email]).to eq(test_user.email)
expect(response.status).to eq(200)
end
end
let(:valid_otp_code) { '1357' }
let(:invalid_otp_code) { '1234' }
before do
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, valid_otp_code) { true }
allow(TOTPService).to receive(:validate?)
.with(test_user.uid, invalid_otp_code) { false }
end
describe 'PUT /api/v2/resource/users/me' do
let(:correct_data) { { bar: 'baz' }.to_json }
let(:incorrect_data) { 'random string' }
it 'changes user data field' do
put '/api/v2/resource/users/me', headers: auth_header, params: {
data: correct_data
}
expect(test_user.reload.data).to eq(correct_data)
expect(response.status).to eq 200
end
it 'receives invalid data error' do
put '/api/v2/resource/users/me', headers: auth_header, params: {
data: incorrect_data
}
expect(response.status).to eq 422
expect_body.to eq errors: ['data.invalid_format']
end
end
describe 'DELETE /api/v2/resource/users/me' do
it 'receives invalid password error' do
delete '/api/v2/resource/users/me', headers: auth_header, params: {
password: 'WrongPassword'
}
expect(response.status).to eq 422
expect(test_user.reload.state).to eq 'active'
expect_body.to eq errors: ['resource.user.invalid_password']
end
it 'receives otp missing error if 2fa is enabled and param is missing' do
test_user.update(otp: true)
delete '/api/v2/resource/users/me', headers: auth_header, params: {
password: 'Tecohvi0'
}
expect(response.status).to eq 422
expect(test_user.reload.state).to eq 'active'
expect_body.to eq errors: ['resource.user.missing_otp_code']
end
it 'receives otp empty error if 2fa is enabled and param is empty' do
test_user.update(otp: true)
delete '/api/v2/resource/users/me', headers: auth_header, params: {
password: 'Tecohvi0',
otp_code: ''
}
expect(response.status).to eq 422
expect(test_user.reload.state).to eq 'active'
expect_body.to eq errors: ['resource.user.empty_otp_code']
end
it 'receives invalid otp error with wrong otp code' do
test_user.update(otp: true)
delete '/api/v2/resource/users/me', headers: auth_header, params: {
password: 'Tecohvi0',
otp_code: invalid_otp_code
}
expect(response.status).to eq 422
expect(test_user.reload.state).to eq 'active'
expect_body.to eq errors: ['resource.user.invalid_otp']
end
it 'marks user as deleted with turned on 2fa and valid otp' do
test_user.update(otp: true)
delete '/api/v2/resource/users/me', headers: auth_header, params: {
password: 'Tecohvi0',
otp_code: valid_otp_code
}
expect(response.status).to eq 200
expect(test_user.reload.state).to eq 'deleted'
end
it 'marks user as deleted with turned off 2fa' do
delete '/api/v2/resource/users/me', headers: auth_header, params: {
password: 'Tecohvi0'
}
expect(response.status).to eq 200
expect(test_user.reload.state).to eq 'deleted'
end
end
describe 'POST /api/v2/resource/otp/disable' do
it 'with dasbled 2fa' do
post '/api/v2/resource/otp/disable', headers: auth_header, params: { code: valid_otp_code }
expect_body.to eq errors: ['resource.otp.not_enabled']
end
it 'with invalid code' do
test_user.update(otp: true)
post '/api/v2/resource/otp/disable', headers: auth_header, params: { code: invalid_otp_code }
expect_body.to eq errors: ['resource.otp.invalid']
end
it 'disables 2fa' do
test_user.update(otp: true)
expect {
post '/api/v2/resource/otp/disable', headers: auth_header, params: { code: valid_otp_code }
}.to change { test_user.reload.otp }.from(true).to(false)
expect(response.status).to eq 200
end
end
describe 'POST /api/v2/resource/users/activity' do
it 'allows only [password, otp, session, all] as a topic parameter' do
get '/api/v2/resource/users/activity/invalid', headers: auth_header
expect(response.status).to eq(422)
expect_body.to eq(errors: ['resource.user.wrong_topic'])
get '/api/v2/resource/users/activity/session', headers: auth_header
expect(response.status).to eq(422)
expect_body.to eq(errors: ['resource.user.no_activity'])
end
it 'sorts user activities' do
4.times { create(:activity, user: test_user) }
get '/api/v2/resource/users/activity/all', headers: auth_header
activities = JSON.parse(response.body)
expect(activities.first['id']).to be >= activities.last['id']
end
it 'sorts user activities by result' do
4.times { create(:activity, user: test_user, result: 'succeed') }
get '/api/v2/resource/users/activity/all', params: { result: 'succeed' }, headers: auth_header
activities = JSON.parse(response.body)
expect(activities.length).to eq(4)
end
it 'sorts user activities by time range' do
4.times { create(:activity, user: test_user, result: 'succeed') }
get '/api/v2/resource/users/activity/all', params: { time_from: 5.hours.ago.to_i, time_to: 1.hours.ago.to_i }, headers: auth_header
activities = JSON.parse(response.body)
expect(activities.length).to eq(1)
end
it 'sorts user activities by time range' do
4.times { create(:activity, user: test_user, result: 'succeed') }
4.times { create(:activity, user: test_user, result: 'succeed', created_at: 10.hours.ago) }
get '/api/v2/resource/users/activity/all', params: { time_from: 5.hours.ago.to_i}, headers: auth_header
activities = JSON.parse(response.body)
expect(activities.length).to eq(4)
end
end
describe 'POST /api/v2/resource/users/password' do
let(:do_request) do
put '/api/v2/resource/users/password', params: params, headers: auth_header
end
let(:do_confirm_request) do
post '/api/v2/resource/users/confirm_password', params: { code: otp_code }, headers: auth_header
end
let(:params) do
{
old_password: old_password,
new_password: new_password,
confirm_password: confirm_password
}
end
let(:old_password) { '' }
let(:new_password) { '' }
let(:confirm_password) { '' }
context 'when params are blank' do
it 'renders 400 error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.user.empty_old_password", "resource.user.empty_new_password", "resource.user.empty_confirm_password"])
end
end
context 'when old password is not right' do
let(:old_password) { 'invalid' }
let(:new_password) { 'Gol4aid2' }
let(:confirm_password) { 'Gol4aid2' }
it 'renders 400 error' do
do_request
expect(response.status).to eq(400)
expect_body.to eq(errors: ["resource.password.prev_pass_not_correct"])
end
end
context 'when new pass and confirmation are different' do
let(:old_password) { 'Tecohvi0' } # 'Tecohvi0' - test_user password
let(:new_password) { 'Gol4aid1' }
let(:confirm_password) { 'Gol4aid2' }
it 'renders 422 error' do
do_request
expect(response.status).to eq(422)
expect_body.to eq(errors: ["resource.password.doesnt_match"])
end
end
context 'when params are valid, passwords are weak' do
let(:old_password) { 'Tecohvi0' } # 'Tecohvi0' - test_user password
let(:new_password) { 'Simple' }
let(:confirm_password) { 'Simple' }
it 'returns weak password error' do
do_request
expect_status_to_eq 422
expect_body.to eq(errors: ["temp_password.requirements"])
end
end
context 'when params are valid, confirm code not valid' do
let(:old_password) { 'Tecohvi0' } # 'Tecohvi0' - test_user password
let(:new_password) { 'Gol4aid1' }
let(:confirm_password) { 'Gol4aid1' }
let(:otp_code) { invalid_otp_code }
let(:log_in) { post '/api/v2/identity/sessions', params: { email: test_user.email, password: new_password } }
it 'changes a password' do
do_request
do_confirm_request
expect_status_to_eq 422
expect_body.to eq(errors: ["resource.password.confirm_code_not_match"])
end
end
context 'when params are valid' do
let(:old_password) { 'Tecohvi0' } # 'Tecohvi0' - test_user password
let(:new_password) { 'Gol4aid1' }
let(:confirm_password) { 'Gol4aid1' }
let(:otp_code) { valid_otp_code }
let(:log_in) { post '/api/v2/identity/sessions', params: { email: test_user.email, password: new_password } }
it 'changes a password' do
do_request
expect(response.status).to eq(201)
do_confirm_request
expect(response.status).to eq(201)
log_in
expect(response.status).to eq(200)
end
end
end
end