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

11
spec/api/base_spec.rb Normal file
View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
require 'spec_helper'
describe API::Base do
describe 'API::Base' do
it 'should exist' do
expect(API::Base).to eq(API::Base)
end
end
end

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

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
FactoryBot.define do
factory :activity do
user { FactoryBot.create(:user) }
user_ip { Faker::Internet.ip_v4_address }
user_agent { Faker::Internet.user_agent }
category { %w[admin user].sample }
topic { %w[session password otp account].sample }
action { %w[otp::enable login logout signup].sample }
result { %w[succeed failed].sample }
data { {data: Faker::Lorem.sentence(word_count: 3, supplemental: true, random_words_to_add: 4)}.to_json }
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
FactoryBot.define do
factory :api_key, class: 'APIKey' do
kid { Faker::Crypto.sha256 }
secret { SecureRandom.hex(16) }
scope { %w[trade] }
algorithm { 'HS256' }
trait :with_service_account do
key_holder_account { create(:service_account) }
end
trait :with_user do
key_holder_account { create(:user) }
end
end
end

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
FactoryBot.define do
factory :document do
user
doc_type { 'Passport' }
doc_number { Faker::Code.asin }
doc_expire { Faker::Business.credit_card_expiry_date }
after(:build) do |doc|
doc.upload.download!(Faker::Company.logo)
end
end
end

10
spec/factories/label.rb Normal file
View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :label do
key { ::Faker::Internet.slug(glue: '-') }
value { ::Faker::Internet.slug(glue: '-') }
scope { 'public' }
user { create(:user) }
end
end

9
spec/factories/level.rb Normal file
View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :level do
key { Faker::Hacker.noun }
value { Faker::Hacker.adjective }
description { Faker::Hacker.say_something_smart }
end
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :permission do
role { %w[member trader broker admin accountant support technical compliance superadmin] }
path { 'api/v2' }
action { 'ACCEPT' }
verb { %w[get post put delete head patch all] }
end
end

8
spec/factories/phones.rb Normal file
View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :phone do
number { '12345678911' }
user
end
end

15
spec/factories/profile.rb Normal file
View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
FactoryBot.define do
factory :profile do
user { FactoryBot.create(:user) }
author { }
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
dob { Faker::Date.birthday }
address { Faker::Address.state }
city { Faker::Address.city }
country { Faker::Address.country_code_long }
postcode { Faker::Address.postcode }
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :restriction do
scope { 'ip_subnet' }
value { '0.0.0.0/0' }
end
end

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
FactoryBot.define do
factory :service_account do
user { FactoryBot.create(:user) }
email { Faker::Internet.email }
uid { UIDGenerator.generate("SI") }
trait :without_user do
user { nil }
end
end
end

36
spec/factories/user.rb Normal file
View File

@@ -0,0 +1,36 @@
# frozen_string_literal: true
FactoryBot.define do
factory :user do
email { Faker::Internet.email }
password { 'Tecohvi0' }
password_confirmation { 'Tecohvi0' }
state { 'active' }
trait :with_profile do
after(:create) do |user, _|
create(:profile, user: user)
end
end
trait :with_document do
after(:create) do |user, _|
create(:document, user: user)
end
end
trait :with_phone do
after(:create) do |user, _|
create(:phone, user: user)
end
end
trait :with_document_phone_profile do
after(:create) do |user, _|
create(:phone, user: user)
create(:profile, user: user)
create(:document, user: user)
end
end
end
end

BIN
spec/fixtures/files/documents_test.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Data Storage Titles configuraton class' do
context 'testing list function' do
let(:expected_values) { ['personal', 'company'] }
it 'reads configuration from the file' do
expect(UserStorageTitles.list).to eq(expected_values)
end
it 'cache preconfig and doesn\'t read from the file on every call' do
expect(UserStorageTitles.list).to eq(expected_values)
@template = {}
expect(UserStorageTitles.list).to eq(expected_values)
end
end
end

View File

@@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'Document Types configuraton class' do
context 'testing list function' do
let(:expected_values) { ['Passport', 'Identity card', 'Driver license', 'Utility Bill', 'Residental', 'Institutional'] }
before do
@template = { 'document_types' => ['Passport', 'Identity card', 'Driver license', 'Utility Bill', 'Residental', 'Institutional'] }
allow(YAML).to receive(:load_file).and_return(@template)
end
it 'reads configuration from the file' do
expect(DocumentTypes.list).to eq(expected_values)
end
it 'cache preconfig and doesn\'t read from the file on every call' do
expect(DocumentTypes.list).to eq(expected_values)
@template = {}
expect(DocumentTypes.list).to eq(expected_values)
end
end
end

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
require_dependency 'barong/jwt'
describe Barong::JWT do
let(:key) { OpenSSL::PKey::RSA.generate(2048) }
it 'should encode payload with claims' do
codec = Barong::JWT.new(key: key)
token = codec.encode({hello: 'world'})
decoded = ::JWT.decode(token, key.public_key,
true, { algorithm: 'RS256' })
expect(decoded.first.fetch('hello')).to eq('world')
end
end

View File

@@ -0,0 +1,404 @@
# frozen_string_literal: true
require_dependency 'barong/seed'
describe "Default seeds.yml template" do
let(:seeds) { Barong::Seed.new.seeds }
it "Generate seed using environement variables" do
expect(seeds["levels"].size).to eq(3)
# TODO fix 2 below expect
# expect(ENV).to receive(:fetch).with("BARONG_ADMIN_EMAIL", "admin@barong.io").and_call_original
# expect(ENV).to receive(:fetch).with("BARONG_ADMIN_PASSWORD", nil).and_return("123AZErty@")
end
end
describe Barong::Seed do
let(:create_permissions) do
create :permission,
role: 'member'
create :permission,
role: 'admin'
end
let(:levels) {
[
{
"key" => "email",
"value" => "verified",
"description" => "User clicked on the confirmation link"
},
{
"key" => "phone",
"value" => "verified",
"description" => "User entered a valid code from sms"
}
]
}
let(:permissions) {[]}
let(:restrictions) {[]}
let(:users) {[]}
let(:seeds) {
{
"users" => users,
"levels" => levels,
"permissions" => permissions,
"restrictions" => restrictions
}
}
let(:logger) { Logger.new('/dev/null') }
let(:seeder) { Barong::Seed.new }
before(:each) do
allow(seeder).to receive(:seeds).and_return(seeds)
allow(seeder).to receive(:logger).and_return(logger)
Level.delete_all
User.delete_all
Permission.delete_all
Restriction.delete_all
end
context "Seed simple and valid levels" do
it "seeds levels in database" do
seeder.seed_levels
expect(Level.count).to be 2
Level.all.each_with_index do |level, index|
expect(level.id).to eq (index+1)
expect(level.key).to eq levels[index]["key"]
expect(level.value).to eq levels[index]["value"]
expect(level.description).to eq levels[index]["description"]
end
end
it "skips existing levels" do
seeder.seed_levels
seeder.seed_levels
expect(Level.count).to be 2
end
end
context "Seed one admin user" do
let(:users) {
[
{
"email" => "admin@peatio.tech",
"password" => "123aZE@654",
"role" => "admin",
"state" => "active",
"level" => 2
}
]
}
it "seeds users in database" do
create_permissions
seeder.seed_levels
seeder.seed_users
expect(User.count).to eq 1
user = User.first
expect(user.email).to eq("admin@peatio.tech")
expect(user.role).to eq("admin")
expect(user.state).to eq("active")
expect(user.level).to eq(2)
end
end
context "User level doesn't match levels in databases" do
let(:users) {
[
{
"email" => "admin@peatio.tech",
"password" => "123aZE@654",
"role" => "admin",
"state" => "active",
"level" => 3
}
]
}
it "raises an explicit error" do
create_permissions
seeder.seed_levels
expect {
seeder.seed_users
}.to raise_error(Barong::Seed::ConfigError, "No enough levels found in database to grant the user to level 3")
end
end
context "User level is not set" do
let(:users) {
[
{
"email" => "admin@peatio.tech",
"password" => "123aZE@654",
"role" => "admin",
"state" => "active",
}
]
}
it "raises an explicit error" do
seeder.seed_levels
expect {
seeder.seed_users
}.to raise_error(Barong::Seed::ConfigError, "Level is missing for user admin@peatio.tech")
end
end
context "User email is not set" do
let(:users) {
[
{
"password" => "123aZE@654",
"role" => "admin",
"state" => "active",
"level" => 2
}
]
}
it "raises an explicit error" do
seeder.seed_levels
expect {
seeder.seed_users
}.to raise_error(Barong::Seed::ConfigError, "Email missing in users seed")
end
end
context "User state is not set" do
let(:users) {
[
{
"email" => "admin@peatio.tech",
"password" => "123aZE@654",
"role" => "admin",
"level" => 0
}
]
}
it "defaults state to pending" do
create_permissions
seeder.seed_levels
seeder.seed_users
expect(User.count).to eq 1
user = User.first
expect(user.email).to eq("admin@peatio.tech")
expect(user.role).to eq("admin")
expect(user.state).to eq("pending")
expect(user.level).to eq(0)
end
end
context "User role is not set" do
let(:users) {
[
{
"email" => "admin@peatio.tech",
"password" => "123aZE@654",
"state" => "active",
"level" => 2
}
]
}
it "defaults role to member" do
create_permissions
seeder.seed_levels
seeder.seed_users
expect(User.count).to eq 1
user = User.first
expect(user.email).to eq("admin@peatio.tech")
expect(user.role).to eq("member")
expect(user.state).to eq("active")
expect(user.level).to eq(2)
end
end
context "Seed one admin and a regular user" do
let(:users) {
[
{
"email" => "admin@peatio.tech",
"password" => "123aZE@654",
"role" => "admin",
"state" => "active",
"level" => 2
},
{
"email" => "user@example.com",
"password" => "123aZE@654",
"role" => "member",
"state" => "active",
"level" => 2
}
]
}
it "seeds users in database" do
create_permissions
seeder.seed_levels
seeder.seed_users
expect(User.count).to eq 2
admin = User.find_by(email: "admin@peatio.tech")
expect(admin.role).to eq("admin")
expect(admin.state).to eq("active")
expect(admin.level).to eq(2)
admin = User.find_by(email: "user@example.com")
expect(admin.role).to eq("member")
expect(admin.state).to eq("active")
expect(admin.level).to eq(2)
end
end
context "Seed simple GET /me member permissions" do
let(:permissions) {
[
{
"role" => "member",
"verb" => "GET",
"action" => "ACCEPT",
"path" => "api/v2/resource/users/me"
}
]
}
it "seeds permissions in database" do
seeder.seed_permissions
expect(Permission.count).to eq 1
permission = Permission.first
expect(permission.role).to eq("member")
expect(permission.verb).to eq("GET")
expect(permission.path).to eq("api/v2/resource/users/me")
end
end
context "Permission verb is not set" do
let(:permissions) {
[
{
"role" => "member",
"action" => "ACCEPT",
"path" => "api/v2/resource/users/me"
}
]
}
it "raises an explicit error" do
expect {
seeder.seed_permissions
}.to raise_error(Barong::Seed::ConfigError, "Can't create permission: Verb can't be blank")
end
end
context "Permission path is not set" do
let(:permissions) {
[
{
"role" => "member",
"action" => "ACCEPT",
"verb" => "GET"
}
]
}
it "raises an explicit error" do
expect {
seeder.seed_permissions
}.to raise_error(Barong::Seed::ConfigError, "Can't create permission: Path can't be blank")
end
end
context "Seed one admin GET and one accountant POST permissions" do
let(:permissions) {
[
{
"role" => "admin",
"verb" => "GET",
"action" => "ACCEPT",
"path" => "api/v2/admin/users/list"
},
{
"role" => "accountant",
"verb" => "POST",
"action" => "ACCEPT",
"path" => "api/v2/admin/users"
}
]
}
it "seeds permissions in database" do
seeder.seed_permissions
expect(Permission.count).to eq 2
admin_permission = Permission.find_by_role('admin')
expect(admin_permission.role).to eq("admin")
expect(admin_permission.verb).to eq("GET")
expect(admin_permission.path).to eq("api/v2/admin/users/list")
accountant_permission = Permission.find_by_role('accountant')
expect(accountant_permission.role).to eq("accountant")
expect(accountant_permission.verb).to eq("POST")
expect(accountant_permission.path).to eq("api/v2/admin/users")
end
end
context "Seed simple and valid restrictions" do
let(:restrictions) {
[
{
"category" => "maintenance",
"scope" => "all",
"value" => "all",
"state" => "enabled"
},
{
"category" => "blacklist",
"scope" => "all",
"value" => "all",
"state" => "enabled"
}
]
}
it "seeds restrictions in database" do
seeder.seed_restrictions
expect(Restriction.count).to be 2
Restriction.all.each_with_index do |restriction, index|
expect(restriction.category).to eq restrictions[index]["category"]
expect(restriction.scope).to eq restrictions[index]["value"]
expect(restriction.value).to eq restrictions[index]["value"]
expect(restriction.state).to eq restrictions[index]["state"]
end
end
end
context "Empty seed" do
it "returns without error and with kind log info" do
expect {
seeder.seed_restrictions
}.not_to raise_error
end
end
context "Missing params in restriction seed" do
let(:restrictions) {
[
{
"category" => "maintenance",
"scope" => "all",
"value" => "all",
}
]
}
it "raises error on invalid seed" do
expect {
seeder.seed_restrictions
}.to raise_error(Barong::Seed::ConfigError, "state is missing in restrictions seed")
end
end
end

View File

@@ -0,0 +1,40 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Postmaster, type: :mailer do
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
describe '#process_payload' do
let!(:user) { create(:user, email: 'test1@gmail.com') }
let(:record) { OpenStruct.new(domain: 'barong.com', token: 'blah-blah' ) }
let(:payload) do
{
user: user,
changes: nil,
record: record,
subject: 'Test Email',
template_name: 'email_confirmation.en.html.erb',
logo: 'https://storage.googleapis.com/public_peatio/logo.png'
}
end
let(:mail) { Postmaster.process_payload(payload) }
it 'renders the headers' do
expect(mail.subject).to eq('Test Email')
expect(mail.to).to eq(['test1@gmail.com'])
expect(mail.from).to eq(['noreply@barong.io'])
end
it 'renders the body' do
expect(mail.body.encoded).to match('Use this unique link to confirm your email test1@gmail.com')
end
end
end

View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true
# Preview all emails at http://localhost:3000/rails/mailers/mailer
class MailerPreview < ActionMailer::Preview
end

View File

@@ -0,0 +1,70 @@
# frozen_string_literal: true
describe Ability do
before do
allow(Ability).to receive(:admin_permissions).and_return({
"superadmin"=>{"manage"=>["User", "Activity", "Ability", "APIKey", "Profile", "Permission", "Label", "Restriction", "Level"]},
"admin"=>{"read"=>["Level", "APIKey", "Permission"], "manage"=>["User", "Activity", "Profile", "Label"]},
"compliance"=>{"read"=>["Level", "User", "Activity"], "manage"=>["Label"], "update"=>["Profile"]},
"support"=>{"read"=>["User", "Activity", "APIKey", "Profile", "Label", "Level"]}
})
end
let!(:create_permissions) do
create(:permission, role: 'superadmin', action: 'accept', verb: 'get')
create(:permission, role: 'admin', action: 'accept', verb: 'get')
create(:permission, role: 'compliance', action: 'accept', verb: 'get')
create(:permission, role: 'support', action: 'accept', verb: 'get')
end
context 'abilities for superadmin' do
let(:test_user) { create(:user, email: 'example@gmail.com', role: 'superadmin') }
subject(:ability) { AdminAbility.new(test_user) }
it { is_expected.to be_able_to(:manage, User.new) }
it { is_expected.to be_able_to(:manage, Activity.new) }
it { is_expected.to be_able_to(:manage, Profile.new) }
it { is_expected.to be_able_to(:manage, Permission.new) }
it { is_expected.to be_able_to(:manage, Label.new) }
it { is_expected.to be_able_to(:manage, Restriction.new) }
it { is_expected.to be_able_to(:manage, Level.new) }
end
context 'abilities for admin' do
let(:test_user) { create(:user, email: 'example@gmail.com', role: 'admin') }
subject(:ability) { AdminAbility.new(test_user) }
it { is_expected.to be_able_to(:read, Level.new) }
it { is_expected.to be_able_to(:read, APIKey.new) }
it { is_expected.to be_able_to(:read, Permission.new) }
it { is_expected.to be_able_to(:manage, User.new) }
it { is_expected.to be_able_to(:manage, Activity.new) }
it { is_expected.to be_able_to(:manage, Profile.new) }
it { is_expected.to be_able_to(:manage, Label.new) }
end
context 'abilities for compliance' do
let(:test_user) { create(:user, email: 'example@gmail.com', role: 'compliance') }
subject(:ability) { AdminAbility.new(test_user) }
it { is_expected.to be_able_to(:read, Level.new) }
it { is_expected.to be_able_to(:read, User.new) }
it { is_expected.to be_able_to(:read, Activity.new) }
it { is_expected.to be_able_to(:manage, Label.new) }
it { is_expected.to be_able_to(:update, Profile.new) }
end
context 'abilities for support' do
let(:test_user) { create(:user, email: 'example@gmail.com', role: 'support') }
subject(:ability) { AdminAbility.new(test_user) }
it { is_expected.to be_able_to(:read, User.new) }
it { is_expected.to be_able_to(:read, Activity.new) }
it { is_expected.to be_able_to(:read, APIKey.new) }
it { is_expected.to be_able_to(:read, Profile.new) }
it { is_expected.to be_able_to(:read, Label.new) }
it { is_expected.to be_able_to(:read, Level.new) }
end
end

View File

@@ -0,0 +1,65 @@
# frozen_string_literal: true
RSpec.describe Activity, type: :model do
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:activity) do
create :activity, topic: 'session',
action: 'login',
result: 'succeed',
user_agent: 'Magic Pony Browser'
end
context 'General' do
it { should belong_to(:user) }
it {
expect {
activity.update_attribute(:topic, 'otp')
}.to raise_error(ActiveRecord::ActiveRecordError, 'Activity is marked as readonly')
}
end
describe 'Validations' do
context 'Correct fields values' do
it { should allow_value('session').for(:topic) }
it { should allow_value('otp').for(:topic) }
it { should allow_value('password').for(:topic) }
it { should allow_value('succeed').for(:result) }
it { should allow_value('failed').for(:result) }
it { expect(activity.browser.ua).to eq 'Magic Pony Browser'}
end
context 'Banned actions and values' do
it { should_not allow_value('passed').for(:result) }
it { should_not allow_value('').for(:user_ip) }
it { expect(activity.browser.known?).to eq false }
it { expect(JSON.parse(activity.data)['note']).to eq 'Detected suspicious browser' }
end
end
describe 'Browser functionality' do
let(:valid_user_agent){'Android SDK 1.5r3: Mozilla/5.0 (Linux; U; Android 1.5; de-; sdk Build/CUPCAKE)
AppleWebkit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1'}
let!(:activity) do
create :activity, topic: 'session',
action: 'login',
result: 'succeed',
user_agent: valid_user_agent
end
context 'Detects browser information' do
it { expect(activity.browser.name).to eq 'Safari' }
it { expect(activity.browser.platform.android?).to eq true }
it { expect(activity.browser.webkit?).to eq true }
it { expect(activity.browser.full_version).to eq '3.1.2' }
it { expect(activity.browser.version).to eq '3' }
end
end
end

124
spec/models/api_key_spec.rb Normal file
View File

@@ -0,0 +1,124 @@
# frozen_string_literal: true
RSpec.describe APIKey, type: :model do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:create_service_account_permission) do
create :permission,
role: 'service_account'
end
describe 'Validations' do
subject(:api_key) { build(:api_key, :with_user) }
it { should validate_presence_of(:secret).with_message(/can't be blank/) }
it { should validate_uniqueness_of(:kid).with_message(/has already been taken/) }
context 'key holder state' do
context 'valid response' do
context 'user with active state' do
let!(:user) { create(:user, state: 'active') }
it 'creates api key' do
subject.key_holder_account = user
expect(subject.save).to eq true
expect(subject.errors.full_messages).to eq []
end
end
context 'service account with active state' do
let!(:user) { create(:user, state: 'active') }
let!(:service_account) { create(:service_account, owner_id: user.id) }
it 'creates api key' do
subject.key_holder_account = user
expect(subject.save).to eq true
expect(subject.errors.full_messages).to eq []
end
end
end
context 'invalid response' do
subject { build(:api_key) }
context 'user with non active state' do
let!(:user) { create(:user, state: 'pending') }
it 'render error message' do
subject.key_holder_account = user
expect(subject.save).to eq false
expect(subject.errors.full_messages).to include(/non active state for key holder account/)
end
end
context 'service account with non active state' do
let!(:user) { create(:user, state: 'pending') }
let!(:service_account) { create(:service_account, owner_id: user.id) }
it 'render error message' do
subject.key_holder_account = service_account
expect(subject.save).to eq false
expect(subject.errors.full_messages).to include(/non active state for key holder account/)
end
end
end
end
context 'api key state on update' do
context 'valid response' do
context 'user with active state' do
let!(:user) { create(:user, state: 'active') }
let!(:api_key) { create(:api_key, key_holder_account: user, state: 'disabled') }
it 'updates api key state' do
api_key.state = 'active'
expect(api_key.save).to eq true
expect(api_key.errors.full_messages).to eq []
end
end
context 'service account with non active state' do
let!(:user) { create(:user, state: 'active') }
let!(:service_account) { create(:service_account, owner_id: user.id) }
let!(:api_key) { create(:api_key, key_holder_account: service_account, state: 'disabled') }
it 'render error message' do
api_key.state = 'active'
expect(api_key.save).to eq true
expect(api_key.errors.full_messages).to eq []
end
end
end
context 'invalid response' do
context 'user with non active state' do
let!(:user) { create(:user, state: 'active') }
let!(:api_key) { create(:api_key, key_holder_account: user) }
it 'render error message' do
user.update(state: 'banned')
api_key.state = 'active'
expect(api_key.save).to eq false
expect(api_key.errors.full_messages).to include(/cant activate api key with disabled key holder account/)
end
end
context 'service account with non active state' do
let!(:user) { create(:user, state: 'active') }
let!(:service_account) { create(:service_account, owner_id: user.id) }
let!(:api_key) { create(:api_key, key_holder_account: service_account) }
it 'render error message' do
service_account.update(state: 'disabled')
api_key.state = 'active'
expect(api_key.save).to eq false
expect(api_key.errors.full_messages).to include(/cant activate api key with disabled key holder account/)
end
end
end
end
end
end

View File

@@ -0,0 +1,108 @@
# frozen_string_literal: true
RSpec.describe Document, type: :model do
## Test of relationships
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
it { should belong_to(:user) }
describe 'validation' do
let!(:document) { build :document, doc_expire: doc_expire }
subject do
document.valid?
document.errors.messages
end
let(:doc_expire) { Date.current.to_s }
it { is_expected.to be_blank }
context 'when doc_expire is expired' do
let(:doc_expire) { 1.day.ago.to_s }
it { is_expected.to eq(doc_expire: ['is invalid']) }
end
end
context 'Document creation' do
let!(:current_user) { create(:user) }
let(:create_document) { create :document, user: current_user, doc_type: 'Passport', doc_category: 'front_side' }
let(:create_document_second) { create :document, user: current_user, doc_type: 'Passport', doc_category: 'Selfie' }
let(:create_without_label) { create :document, user: current_user, update_labels: false }
let(:document_label) { current_user.labels.first }
context 'when it is first document' do
it 'adds new document label' do
create_document
expect { create_document_second }.to change { current_user.reload.labels.count }.from(0).to(1)
end
it 'new document label is document: pending' do
create_document
create_document_second
expect(document_label.key).to eq 'document'
expect(document_label.value).to eq 'pending'
end
end
context 'when user has label document: rejected' do
let!(:document_label) do
create :label,
scope: 'private',
key: 'document',
value: 'rejected',
user: current_user
end
it 'does not add new label' do
create_document
expect { create_document_second }.to_not change { Label.count }
end
it 'changes label value to pending' do
create_document
create_document_second
expect(current_user.labels.first.value).to eq 'pending'
end
end
context 'when user has label document: verified' do
let!(:document_label) do
create :label,
scope: 'private',
key: 'document',
value: 'verified',
user: current_user
end
it 'does not add new label' do
expect { create_document }.to_not change { Label.count }
end
it 'remains value verified' do
expect { create_document }.to_not change { document_label }
end
end
end
context 'submasked fields' do
let!(:current_user) { create(:user) }
let(:document) { create :document, user: current_user, doc_type: 'Passport', doc_number: 'M0993353' }
let(:document_without_doc_number) { create :document, user: current_user, doc_type: 'Passport', doc_number: nil }
context 'number' do
it { expect(document.sub_masked_doc_number).to eq 'M0****53' }
it { expect(document_without_doc_number.sub_masked_doc_number).to eq nil }
it 'should mask first 2 letters and last 2 digits' do
document.update(doc_type: 'Driver license', doc_number: 'BO231283013DSAS23')
expect(document.sub_masked_doc_number).to eq 'BO*************23'
end
end
end
end

252
spec/models/label_spec.rb Normal file
View File

@@ -0,0 +1,252 @@
# frozen_string_literal: true
RSpec.describe Label, type: :model do
let!(:create_admin_permission) do
create :permission,
role: 'admin'
end
let!(:create_member_permission) do
create :permission,
role: 'member'
end
it { should belong_to(:user) }
describe 'update user level if label defined as level', order: :defined do
let!(:email_verified_level) { Level.find(1) }
let!(:phone_verified_level) { Level.find(2) }
let!(:identity_verified_level) { Level.find(3) }
let!(:document_verified_level) { Level.find(4) }
context 'when user has no labels' do
let!(:user) { create(:user) }
it { expect(user.reload.level).to eq 0 }
it 'does not change level if valid label has public scope' do
expect do
create_label_with_level(user, email_verified_level, scope: 'public')
end.to_not change { user.reload.level }
end
it 'when checks labels-levels mappings' do
expect do
create_label_with_level(user, email_verified_level)
end.to change { user.reload.level }.from(0).to(1)
expect do
create_label_with_level(user, phone_verified_level)
end.to change { user.reload.level }.from(1).to(2)
expect do
create_label_with_level(user, identity_verified_level)
end.to change { user.reload.level }.from(2).to(3)
expect do
create_label_with_level(user, document_verified_level)
end.to change { user.reload.level }.from(3).to(4)
end
end
context 'when user has verified email and phone' do
let!(:user) { create(:user) }
before do
create_label_with_level(user, email_verified_level)
create_label_with_level(user, phone_verified_level)
end
it 'changes to level 2 when identity verified label applied' do
expect(user.reload.level).to eq(2)
end
it 'changes to level 3 when identity verified label applied' do
expect do
create_label_with_level(user, identity_verified_level)
end.to change { user.reload.level }.to(3)
end
it 'downgrades level to 1 when phone state changes to rejected' do
Label.find_by(user: user, key: phone_verified_level.key).update(value: 'rejected')
user.update_level
expect(user.reload.level).to eq 1
end
it 'does not change level if user has document verified label' do
expect do
create_label_with_level(user, document_verified_level)
end.to_not change { user.reload.level }
end
end
context 'when user has all label required for level 4' do
let!(:user) { create(:user) }
before do
create_label_with_level(user, email_verified_level)
create_label_with_level(user, phone_verified_level)
create_label_with_level(user, identity_verified_level)
create_label_with_level(user, document_verified_level)
end
it { expect(user.reload.level).to eq 4 }
it 'downgrades level to 0 when email verified label changes' do
Label.find_by(user: user, key: email_verified_level.key).update(value: 'rejected')
user.update_level
expect(user.reload.level).to eq 0
end
it 'downgrades level to 0 when email verified label changes' do
Label.find_by(user: user, key: email_verified_level.key).destroy
user.update_level
expect(user.reload.level).to eq 0
end
end
context 'document label changes' do
let!(:user) { create(:user) }
let(:last_mailer_delivery) { ActionMailer::Base.deliveries.last }
let!(:document_label) do
create(
:label,
user: user,
key: 'document',
value: 'pending',
scope: 'private'
)
end
end
context 'when label scope is changed from private to public and reverse' do
let!(:user) { create(:user) }
before do
create_label_with_level(user, email_verified_level)
create_label_with_level(user, phone_verified_level)
create_label_with_level(user, identity_verified_level)
create_label_with_level(user, document_verified_level)
end
it 'updates level' do
expect do
user.labels.last.update(scope: :public)
end.to change { user.reload.level }.from(4).to(3)
expect do
user.labels.last.update(scope: :private)
end.to change { user.reload.level }.from(3).to(4)
end
end
context '2 labels with same keys' do
let!(:user) { create(:user) }
let!(:label_public) do
create :label,
user: user,
key: email_verified_level.key,
value: email_verified_level.value,
scope: 'public'
end
let!(:label_private) do
create :label,
user: user,
key: email_verified_level.key,
value: email_verified_level.value,
scope: 'private'
end
context 'can be created with different scopes' do
it 'user has both labels' do
expect(user.labels).to include(label_private, label_public)
end
it 'user has level 1' do
expect(user.level).to eq 1
end
context 'when private label changes' do
it 'user level downgrades when value changed' do
expect { label_private.update(value: 'rejected') }.to change { user.reload.level }.to 0
end
it 'user level downgrades when key changed' do
expect { label_private.update(key: 'email0') }.to change { user.reload.level }.to 0
end
end
context 'when public label changes' do
it 'user level does not change' do
expect { label_public.update(value: 'rejected') }.to_not change { user.reload.level }
end
end
end
end
context 'when user has all required labels for level 4 in wrong scope' do
let!(:user) { create(:user) }
let!(:label_email_public) do
create :label,
user: user,
key: email_verified_level.key,
value: email_verified_level.value,
scope: 'public'
end
let!(:label_phone_public) do
create :label,
user: user,
key: phone_verified_level.key,
value: phone_verified_level.value,
scope: 'public'
end
let!(:label_identity_public) do
create :label,
user: user,
key: identity_verified_level.key,
value: identity_verified_level.value,
scope: 'public'
end
let!(:label_document_public) do
create :label,
user: user,
key: document_verified_level.key,
value: document_verified_level.value,
scope: 'public'
end
it { expect(user.reload.level).to eq 0 }
end
end
context 'downcase fields' do
let(:user) { create(:user) }
let(:label) { build(:label, user: user, key: 'PhoNe', value: 'VeRifiEd') }
it 'downcases key and value before save' do
label.save
expect(label.reload.key).to eq 'phone'
expect(label.reload.value).to eq 'verified'
end
end
context 'event api behaviour' do
let!(:user) { create(:user, state: 'pending') }
let(:label) { create(:label, user_id: user.id) }
before do
allow(EventAPI).to receive(:notify)
end
it 'receives event with label create' do
label
expect(EventAPI).to have_received(:notify).with('model.label.created',
hash_including(
record: hash_including(
id: label.id,
key: label.key,
value: label.value,
user: hash_including(uid: user.uid, level: 0, email: user.email)
)
))
end
end
end

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
RSpec.describe Level, type: :model do
it { should validate_presence_of(:key) }
it { should validate_presence_of(:value) }
it { should validate_presence_of(:description) }
end

41
spec/models/phone_spec.rb Normal file
View File

@@ -0,0 +1,41 @@
# frozen_string_literal: true
RSpec.describe Phone, type: :model do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
context 'submasked fields' do
let!(:phone) { create(:phone) }
context 'number' do
it 'should mask country code and last 4 digits' do
phone.update(number: '79225551234')
expect(phone.sub_masked_number).to eq '7******1234'
end
it 'should mask country code and last 4 digits' do
phone.update(number: '+201112341923')
expect(phone.sub_masked_number).to eq '20******1923'
end
it 'should mask country code and last 4 digits' do
phone.update(number: '+380971232322')
expect(phone.sub_masked_number).to eq '380*****2322'
end
it 'should return empty phone number' do
phone.update(number: '')
expect(phone.sub_masked_number).to eq ''
end
context 'default country code' do
it 'should mask country code' do
phone.update_attribute(:number, '1112222')
expect(phone.sub_masked_number).to eq '11*2222'
end
end
end
end
end

385
spec/models/profile_spec.rb Normal file
View File

@@ -0,0 +1,385 @@
# frozen_string_literal: true
RSpec.describe Profile, type: :model do
describe 'squish_spaces' do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:profile) do
create :profile, first_name: ' First Name ',
last_name: ' Last Name ',
city: ' New York ',
postcode: ' AB 135-144 '
end
it 'squishes spaces' do
profile.reload
expect(profile.first_name).to eq 'First Name'
expect(profile.last_name).to eq 'Last Name'
expect(profile.city).to eq 'New York'
expect(profile.postcode).to eq 'AB 135-144'
end
end
describe 'creating partial priofile' do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user) }
subject { Profile.create(params.merge(user: user)) }
context 'empty params' do
let!(:params) { {} }
it { expect(subject).to be_valid }
it { expect(subject.first_name.nil?).to be_truthy }
it { expect(subject.last_name.nil?).to be_truthy }
it { expect(subject.dob.nil?).to be_truthy }
it { expect(subject.address.nil?).to be_truthy }
it { expect(subject.postcode.nil?).to be_truthy }
it { expect(subject.city.nil?).to be_truthy }
it { expect(subject.country.nil?).to be_truthy }
it { expect(subject.metadata.nil?).to be_truthy }
it { expect(subject.state).to eq('drafted') }
it { expect(subject.user.labels.find_by(key: 'profile').value).to eq('drafted') }
context 'add empty params' do
let!(:params) {
{
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
}
}
before do
subject.update(params)
end
it { expect(subject).to be_valid }
it { expect(subject.first_name.present?).to be_truthy }
it { expect(subject.last_name.present?).to be_truthy }
it { expect(subject.dob.present?).to be_truthy }
it { expect(subject.address.present?).to be_truthy }
it { expect(subject.postcode.present?).to be_truthy }
it { expect(subject.city.present?).to be_truthy }
it { expect(subject.country.present?).to be_truthy }
it { expect(subject.metadata.nil?).to be_truthy }
it { expect(subject.state).to eq('drafted') }
it { expect(subject.user.labels.find_by(key: 'profile').value).to eq('drafted') }
end
end
context 'all profile params' do
let!(:params) {
{
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
}
}
it { expect(subject).to be_valid }
it { expect(subject.first_name.present?).to be_truthy }
it { expect(subject.last_name.present?).to be_truthy }
it { expect(subject.dob.present?).to be_truthy }
it { expect(subject.address.present?).to be_truthy }
it { expect(subject.postcode.present?).to be_truthy }
it { expect(subject.city.present?).to be_truthy }
it { expect(subject.country.present?).to be_truthy }
it { expect(subject.metadata.present?).to be_falsey }
it { expect(subject.state).to eq('drafted') }
it { expect(subject.user.labels.find_by(key: 'profile').value).to eq('drafted') }
end
end
context 'profile_state!' do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user) }
let!(:verified_profile) { create(:profile, user_id: user.id, state: 'verified') }
let!(:rejected_profile) { create(:profile, user_id: user.id, state: 'rejected') }
let!(:drafted_profile) { create(:profile, user_id: user.id, state: 'drafted') }
let!(:profile_params) {
{
user_id: user.id,
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
}
}
it 'profile with state drafted' do
profile = Profile.new(profile_params)
profile.save
profile.valid?
expect(profile.errors[:state]).to eq(['already exists'])
end
it 'profile with state submitted' do
profile = Profile.new(profile_params.merge(state: 'submitted'))
profile.save
profile.valid?
expect(profile.errors[:state]).to eq(['already exists'])
end
it 'profile with state verified' do
profile = Profile.new(profile_params.merge(state: 'verified'))
profile.save
expect(profile.valid?).to eq true
end
it 'profile with state rejected' do
profile = Profile.new(profile_params.merge(state: 'rejected'))
profile.save
expect(profile.valid?).to eq true
end
end
context 'create_or_update_profile_label' do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user) }
let!(:profile_params) {
{
user_id: user.id,
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
}
}
it 'creating profile label' do
expect(user.labels.count).to eq 0
Profile.create(profile_params)
expect(user.labels.count).to eq 1
expect(user.labels.first.key).to eq 'profile'
expect(user.labels.first.value).to eq 'drafted'
end
it 'updating profile label' do
profile = Profile.create(profile_params.merge(state: 'submitted'))
expect(user.labels.count).to eq 1
expect(user.labels.first.key).to eq 'profile'
expect(user.labels.first.value).to eq 'submitted'
expect(user.labels.count).to eq 1
profile.update(state: 'verified')
expect(user.labels.count).to eq 1
expect(user.labels.first.key).to eq 'profile'
expect(user.labels.first.value).to eq 'verified'
end
context 'dob param' do
let!(:profile) { create(:profile, user_id: user.id, state: 'verified') }
it 'should be valid dob' do
profile.update(dob: Time.now)
expect(profile.valid?).to eq true
end
it 'should be invalid dob format' do
profile.update(dob: '')
expect(profile.valid?).to eq false
expect(profile.errors[:dob]).to eq ["invalid date format"]
end
it 'should be invalid dob format' do
profile.update(dob: Time.now + 3.days)
expect(profile.valid?).to eq false
expect(profile.errors[:dob]).to eq ["cant be in future"]
end
end
end
context 'update_document_label' do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user) }
let!(:user_without_document) { create(:user) }
let!(:document_verified_level) { Level.find(4) }
let!(:document_verified_label) { create_label_with_level(user, document_verified_level) }
let!(:profile_params) {
{
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
}
}
it 'change document state when new profile created' do
expect(user.labels.find_by(key: :document).value).to eq 'verified'
Profile.create(profile_params.merge(user_id: user.id))
expect(user.labels.count).to eq 2
expect(user.labels.find_by(key: :document).value).to eq 'verified'
end
it 'do not change document state when document doesnt exist' do
expect(user_without_document.labels.find_by(key: :document)).to eq nil
Profile.create(profile_params.merge(user_id: user_without_document.id))
expect(user_without_document.labels.count).to eq 1
expect(user_without_document.labels.find_by(key: :document)).to eq nil
end
end
context 'event api behaviour' do
let!(:permission) { create(:permission, role: 'member') }
let!(:user) { create(:user, state: 'pending', role: 'member') }
let!(:profile) { create(:profile, user_id: user.id, first_name: old_name) }
let!(:old_name) { Faker::Name.first_name }
let!(:new_name) { Faker::Name.first_name }
let(:profile_update) { profile.update(first_name: new_name ) }
before do
allow(EventAPI).to receive(:notify)
end
it 'receives event with label create' do
profile_update
expect(EventAPI).to have_received(:notify).with('model.profile.updated',
hash_including(
changes: { first_name: old_name },
record: hash_including(first_name: new_name)
))
end
end
context 'validation' do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
context 'encrypted fields length' do
let!(:profile) { create(:profile) }
it 'encrypted first name should be valid with 255 characters' do
profile.update(first_name: Faker::Alphanumeric.alphanumeric(number: 255))
expect(profile.valid?).to eq true
end
it 'encrypted last name should be valid with 255 characters' do
profile.update(last_name: Faker::Alphanumeric.alphanumeric(number: 255))
expect(profile.valid?).to eq true
end
it 'encrypted address should be valid with 255 characters' do
profile.update(address: Faker::Alphanumeric.alphanumeric(number: 255))
expect(profile.valid?).to eq true
end
end
end
context 'submasked fields' do
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let!(:user) { create(:user) }
let!(:profile) do
create :profile, first_name: 'Oleksandr',
last_name: 'Berezniy',
city: 'New York',
postcode: 'AB 135-144',
dob: Date.new(2007, 5, 12)
end
let!(:profile_without_last_name) do
create :profile, first_name: 'Oleksandr',
last_name: nil,
city: 'New York',
postcode: 'AB 135-144'
end
let!(:profile_without_dob) do
create :profile, first_name: 'Oleksandr',
last_name: nil,
city: 'New York',
postcode: 'AB 135-144',
dob: nil
end
context 'last_name' do
it { expect(profile.sub_masked_last_name).to eq 'B*******' }
it 'should mask all letters except first one' do
profile.update(last_name: 'Teylor_1')
expect(profile.sub_masked_last_name).to eq 'T*******'
end
it 'should mask all letters except first one' do
profile.update(last_name: ' Last name ')
expect(profile.sub_masked_last_name).to eq 'L********'
end
it 'should mask all letters except first one' do
profile.update(last_name: '')
expect(profile.sub_masked_last_name).to eq ''
end
it 'should mask all letters except first one' do
expect(profile_without_last_name.sub_masked_last_name).to eq nil
end
end
context 'dob' do
it { expect(profile.sub_masked_dob).to eq '2007-05-**' }
it 'should mask only date numbers' do
profile.update(dob: Date.new(2020, 12, 12))
expect(profile.sub_masked_dob).to eq '2020-12-**'
end
it 'should mask only date numbers' do
expect(profile_without_dob.sub_masked_dob).to eq nil
end
end
end
end

View File

@@ -0,0 +1,86 @@
# frozen_string_literal: true
RSpec.describe Restriction, type: :model do
context 'create' do
it { should_not allow_value('planet').for(:scope) }
it 'valid address with ip scope' do
expect(Restriction.new(scope: 'ip', value: '127.0.0.1', category: 'blacklist').valid?).to be_truthy
end
it 'invalid address with ip scope' do
expect(Restriction.new(scope: 'ip', value: 'abc.0.0.1', category: 'blacklist').valid?).to be_falsey
end
it 'ip subnet with ip scope' do
expect(Restriction.new(scope: 'ip', value: '127.0.0.1/24', category: 'blacklist').valid?).to be_falsey
end
it 'valid subnet with ip_subnet scope' do
expect(Restriction.new(scope: 'ip_subnet', value: '127.0.0.1/24', category: 'blacklist').valid?).to be_truthy
end
it 'invalid subnet with ip_subnet scope' do
expect(Restriction.new(scope: 'ip_subnet', value: '127.0.0.1/one', category: 'blacklist').valid?).to be_falsey
end
it 'ip address with ip_subnet scope' do
expect(Restriction.new(scope: 'ip_subnet', value: '127.0.0.1', category: 'blacklist').valid?).to be_falsey
end
end
context 'session destroy' do
let!(:permission) { create :permission, role: 'member'}
let(:user) { create(:user) }
before do
allow(Rails.cache).to receive(:delete_matched).and_return(nil)
end
context 'blocklogin category' do
context 'after create' do
it do
expect(Rails.cache).to receive(:delete_matched)
Restriction.create!(scope: 'ip', value: '127.0.0.1', category: 'blocklogin', state: "enabled")
end
it do
expect(Rails.cache).to_not receive(:delete_matched)
Restriction.create!(scope: 'ip', value: '127.0.0.2', category: 'blocklogin', state: "disabled")
end
end
context 'after update' do
let!(:enabled_restriction) {create(:restriction, scope: 'ip', value: '127.0.0.3', category: 'blocklogin', state: "enabled")}
let!(:disabled_restriction) {create( :restriction, scope: 'ip', value: '127.0.0.4', category: 'blocklogin', state: "disabled")}
it do
expect(Rails.cache).to receive(:delete_matched)
disabled_restriction.update(state: "enabled")
end
it do
expect(Rails.cache).to_not receive(:delete_matched)
enabled_restriction.update(state: "enabled")
end
it do
expect(Rails.cache).to_not receive(:delete_matched)
disabled_restriction.update(state: "disabled")
end
it do
expect(Rails.cache).to_not receive(:delete_matched)
enabled_restriction.update(state: "disabled")
end
end
end
context 'another category' do
it 'it should not destroy sessions' do
expect(Rails.cache).to_not receive(:delete_matched)
Restriction.create!(scope: 'ip', value: '127.0.0.1', category: 'blacklist', state: "enabled")
end
end
end
end

409
spec/models/user_spec.rb Normal file
View File

@@ -0,0 +1,409 @@
# frozen_string_literal: true
RSpec.describe User, type: :model do
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'local') }
let!(:create_member_permission) do
create :permission,
role: 'member'
end
context 'User model basic syntax' do
## Test of data length
it { should validate_length_of(:data) }
# 1100 char string
let(:big_str) { "string bar"*11 }
it { should_not allow_value(big_str).for(:data)}
## Test of validations
it { should validate_presence_of(:email) }
it { should have_many(:documents).dependent(:destroy) }
## Test of relationships
it { should have_many(:profiles).dependent(:destroy) }
## Test of UID creation
it 'creates default uid with prefix ID' do
default_user = create(:user)
expect(default_user.uid).to start_with(Barong::App.config.uid_prefix)
end
it 'uid prefix can be changed by ENV' do
allow(Barong::App.config).to receive(:uid_prefix).and_return('GG')
default_user = create(:user)
expect(default_user.uid).to start_with('GG')
end
it 'uid_prefix doesnt case sensitive and always converts to big letters' do
allow(Barong::App.config).to receive(:uid_prefix).and_return('aa')
default_user = create(:user)
expect(default_user.uid).to start_with('AA')
end
it do
usr = create(:user)
payload = usr.as_payload
expect(payload['email']).to eq(usr.email)
end
describe '#referral' do
let!(:user1) { create(:user) }
let!(:user2) { create(:user, referral_id: user1.id) }
it 'return error when referral doesnt exist' do
record = User.new(uid: 'ID122312323', email: 'test@barong.io', password: 'Oo213Wqw')
record.referral_id = 0
record.valid?
expect(record.errors[:referral_id]).to eq(['doesnt_exist'])
end
it 'return refferal uid' do
expect(user2.referral_uid).to eq user1.uid
end
end
end
describe '#submitted_profile' do
let!(:user_with_drafted_profile) { create(:user) }
let!(:user_with_submitted_profiles) { create(:user) }
let!(:user_without_profile) { create(:user) }
let!(:drafted_profile) { create(:profile, user: user_with_drafted_profile) }
let!(:submitted_profile) { create(:profile, user: user_with_submitted_profiles, state: 'submitted') }
it { expect(user_with_drafted_profile.submitted_profile).to eq nil }
it { expect(user_without_profile.submitted_profile).to eq nil }
it { expect(user_with_submitted_profiles.submitted_profile).to eq submitted_profile }
end
describe '#drafted_profile' do
let!(:user_with_drafted_profile) { create(:user) }
let!(:user_without_profile) { create(:user) }
let!(:drafted_profile) { create(:profile, user: user_with_drafted_profile) }
it { expect(user_with_drafted_profile.drafted_profile).to eq drafted_profile }
it { expect(user_without_profile.drafted_profile).to eq nil }
end
describe '#password' do
it { should_not allow_value('Password1').for(:password)}
it { should_not allow_value('Password1123').for(:password)}
it { should_not allow_value('password').for(:password)}
it { should_not allow_value('password1').for(:password)}
it { should_not allow_value('Qq123123').for(:password)}
it { should_not allow_value('QqQq123123').for (:password)}
it { should_not allow_value('X2qL32').for(:password)}
it { should_not allow_value('eoV0qu').for(:password)}
it { should allow_value('Iequ4geiEWQw').for(:password)}
it { should allow_value('Xwqe213PZCXwe').for(:password)}
it { should allow_value('Kal31ewwqXrew').for(:password)}
end
let(:uploaded_file) { fixture_file_upload('/files/documents_test.jpg', 'image/jpg') }
context 'User with 2 or more documents' do
it do
user = User.create!(email: 'test@gmail.com', password: 'KeeKi7zoWExzc')
expect(User.count).to eq 1
document1 = user.documents.create!(upload: uploaded_file,
doc_type: 'Passport',
doc_number: 'MyString',
doc_expire: '01-01-3020')
document2 = user.documents.create!(upload: uploaded_file,
doc_type: 'Passport',
doc_number: 'MyString',
doc_expire: '01-02-3020')
expect(user.reload.documents).to eq([document1, document2])
end
after(:all) { User.destroy_all }
end
describe 'Iso8601TimeFormat' do
let!(:user) { create(:user) }
around(:each) { |example| Time.use_zone('Pacific/Midway') { example.run } }
it 'parses time in utc and iso8601' do
expect(user.format_iso8601_time(user.created_at)).to \
eq user.created_at.utc.iso8601
end
it 'skips nil' do
expect(user.format_iso8601_time(nil)).to eq nil
end
it 'parses date to iso8601' do
expect(user.format_iso8601_time(user.created_at.to_date)).to \
eq user.created_at.to_date.iso8601
end
end
describe 'States and labels dependency' do
let(:reqs_list) { {} }
let!(:create_permissions) do
create :permission, role: 'member'
end
before do
allow(BarongConfig).to receive(:list) { reqs_list }
end
context 'function testing' do
let!(:user) { create(:user) }
let!(:user_with_no_labels) { create(:user) }
let!(:user_with_labels) do
create(:label, user_id: user.id, key: 'email', value: 'verified', scope: 'private')
create(:label, user_id: user.id, key: 'phone', value: 'verified', scope: 'private')
end
let(:reqs_list) {
{
"activation_requirements" => {
"phone" => "verified",
"documents" => "verified"
},
"state_triggers" => {
"active_one_of_1_label" => ['email'],
"active_one_of_3_labels" => ['first', 'second', 'third']
}
}
}
context 'labels_include?' do
it { expect(user.labels_include?({ 'email' => 'verified' })).to be_truthy }
it { expect(user.labels_include?({ 'email' => 'pending' })).to be_falsey }
it { expect(user.labels_include?({ 'email' => 'verified', 'phone' => 'verified' })).to be_truthy }
it { expect(user.labels_include?({ 'email' => 'verified', 'phone' => 'pending' })).to be_falsey }
end
context 'private_labels_to_hash' do
it { expect(user.private_labels_to_hash).to eq({ 'email' => 'verified', 'phone' => 'verified' }) }
it { expect(user_with_no_labels.private_labels_to_hash).to eq({}) }
end
end
describe 'testing workability with ALL mapping type' do
let!(:user) { create(:user, state: 'pending') }
let(:reqs_list) {
{
"activation_requirements" => {
"phone" => "verified",
"documents" => "verified"
},
"state_triggers" => {
"active_one_of_1_label" => ['email'],
"active_one_of_3_labels" => ['first', 'second', 'third']
}
}
}
context 'changing state on adding label' do
context 'sucessfully' do
it 'changes state when only one label required' do
# email required
expect(user.state).to eq('pending')
user.labels.create(key: 'email', value: 'verified', scope: 'private')
expect(user.state).to eq('active_one_of_1_label')
end
it 'rollback from active to pending only in case of deleted one of activation reqs' do
# [phone documents] required
expect(user.state).to eq('pending')
user.labels.create(key: 'phone', value: 'verified', scope: 'private')
expect(user.state).to eq('pending')
user.labels.create(key: 'documents', value: 'verified', scope: 'private')
expect(user.state).to eq('active')
user.labels.create(key: 'random', value: 'verified', scope: 'private')
expect(user.state).to eq('active')
user.labels.last.destroy
expect(user.state).to eq('active')
user.labels.find_by_key('phone').destroy
expect(user.state).to eq('pending')
end
it 'changes state when 2 label required' do
# [phone documents] required
expect(user.state).to eq('pending')
user.labels.create(key: 'phone', value: 'verified', scope: 'private')
expect(user.state).to eq('pending')
user.labels.create(key: 'documents', value: 'verified', scope: 'private')
expect(user.state).to eq('active')
end
it 'changes state when one out of 3 label required' do
# first or second third required
expect(user.state).to eq('pending')
user.labels.create(key: 'first', value: 'verified', scope: 'private')
expect(user.state).to eq('active_one_of_3_labels')
user.labels.find_by_key('first').destroy
expect(user.state).to eq('pending')
user.labels.create(key: 'third', value: 'verified', scope: 'private')
expect(user.state).to eq('active_one_of_3_labels')
user.labels.find_by_key('third').destroy
expect(user.state).to eq('pending')
user.labels.create(key: 'second', value: 'verified', scope: 'private')
expect(user.state).to eq('active_one_of_3_labels')
end
end
context 'not enough labels' do
it 'doesnt change state if provided 1 out of 2 labels only' do
# [phone documents] required
expect(user.state).to eq('pending')
user.labels.create(key: 'documents', value: 'verified', scope: 'private')
expect(user.state).to eq('pending')
end
end
end
context 'changing state on deleting' do
context 'ALL policy' do
let!(:give_user_active_state) do
user.labels.create(key: 'phone', value: 'verified', scope: 'private')
user.labels.create(key: 'documents', value: 'verified', scope: 'private')
end
it 'recalculates from active to when remove all active labels' do
expect(user.state).to eq('active')
Label.find_by(key: 'documents', user: user).destroy
Label.find_by(key: 'phone', user: user).destroy
user.reload
expect(user.state).to eq('pending')
end
it 'recalculates from active_2_labels to active when pending one of active labels on ALL policy' do
expect(user.state).to eq('active')
Label.find_by(key: 'documents', user: user).destroy
user.reload
expect(user.state).to eq('pending')
end
end
end
end
describe 'testing workability if config is missing' do
let!(:user) { create(:user, state: 'pending') }
let(:reqs_list) {
{
"activation_requirements" => {
"email" => "verified"
},
}
}
context 'not changing state on adding label' do
it 'doesnt changes state if state_triggers config is missing' do
expect(user.state).to eq('pending')
user.labels.create(key: 'trade', value: 'suspicious', scope: 'private')
expect(user.state).to eq('pending')
end
end
end
describe 'testing workability with ANY mapping type' do
let!(:user) { create(:user, state: 'pending') }
let(:reqs_list) {
{
"activation_requirements" => {
"email" => "verified"
},
"state_triggers" => {
"locked" => ['trade', 'withdraw']
}
}
}
context 'changing state on adding label' do
context 'sucessfully' do
it 'changes state when only when 1 label out of 2 matches' do
expect(user.state).to eq('pending')
user.labels.create(key: 'trade', value: 'suspicious', scope: 'private')
expect(user.state).to eq('locked')
end
end
context 'not enough labels' do
it 'doesnt change state when no label matches' do
expect(user.state).to eq('pending')
user.labels.create(key: 'random', value: 'suspicious', scope: 'private')
expect(user.state).to eq('pending')
end
end
end
context 'changing state on deleting' do
context 'ANY policy' do
let!(:give_user_locked_state) do
user.labels.create(key: 'trade', value: 'suspicious', scope: 'private')
user.labels.create(key: 'withdraw', value: 'suspicious', scope: 'private')
end
it 'doesnt recalculate state if remove one of ANY and another one of ANY still here with ANY policy' do
expect(user.state).to eq('locked')
user.labels.find_by(key: 'trade').destroy
user.reload
expect(user.state).to eq('locked')
end
it 'recalculates if removed all of ANY and there is no more match' do
expect(user.state).to eq('locked')
user.labels.find_by(key: 'trade').destroy
user.labels.find_by(key: 'withdraw').destroy
user.reload
expect(user.state).to eq('pending')
end
end
end
end
end
describe 'event api behaviour' do
let!(:user) { create(:user) }
context 'user updated' do
before do
allow(EventAPI).to receive(:notify)
end
it 'receives event with change info' do
user.update(level: 2)
expect(EventAPI).to have_received(:notify).with('model.user.updated',
hash_including(
changes: { level: 0 },
record: hash_including(uid: user.uid, level: 2, email: user.email)
))
end
end
end
end

74
spec/rails_helper.rb Normal file
View File

@@ -0,0 +1,74 @@
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'cancan/matchers'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end

View File

@@ -0,0 +1,154 @@
# frozen_string_literal: true
describe EncryptionService do
context 'current_salt' do
before(:each) do
EncryptionService.instance_variable_set(:@cache, {})
end
it { expect(EncryptionService.current_salt).to eq Time.now.strftime('%Y%W') }
it do
allow(Time).to receive(:now).and_return(1.week.ago)
expect(EncryptionService.current_salt).to eq Time.now.strftime('%Y%W')
end
end
context 'pack' do
before(:each) do
EncryptionService.instance_variable_set(:@cache, {})
end
it { expect(EncryptionService.pack('salt', 'value')).to eq 'salt.value' }
it { expect(EncryptionService.pack('salt', nil)).to eq 'salt.' }
it { expect(EncryptionService.pack('salt', '')).to eq 'salt.' }
end
context 'unpack' do
before(:each) do
EncryptionService.instance_variable_set(:@cache, {})
end
it { expect(EncryptionService.unpack('salt.value')).to eq ['salt', 'value'] }
it { expect(EncryptionService.unpack('salt.value.value')).to eq ['salt', 'value.value'] }
it { expect(EncryptionService.unpack(' salt.value')).to eq ['salt', 'value'] }
it { expect(EncryptionService.unpack(' salt_underscore.value')).to eq ['salt_underscore', 'value'] }
it { expect(EncryptionService.unpack('salt.')).to eq ['salt', ''] }
it { expect { EncryptionService.unpack('salt') }.to raise_error('Invalid encrypted value: salt') }
end
context 'encrypt' do
before(:each) do
EncryptionService.instance_variable_set(:@cache, {})
end
let(:time) { Time.now.strftime('%Y%W') }
it { expect(EncryptionService.encrypt('value')).to match(/#{time}/) }
end
context 'decrypt' do
before(:each) do
EncryptionService.instance_variable_set(:@cache, {})
end
let(:encrypted_key) { EncryptionService.encrypt('value') }
it { expect(EncryptionService.decrypt(encrypted_key)).to eq 'value' }
end
context 'private methods' do
context 'delete_expired_keys' do
context 'ivalid cases' do
context 'cache as number' do
it 'shouldnt delete expired hash when hash is invalid' do
EncryptionService.instance_variable_set(:@cache, 5)
EncryptionService.send(:delete_expired_keys)
expect(EncryptionService.instance_variable_get(:@cache)).to eq 5
end
end
context 'empty hash' do
it 'shouldnt delete expired hash when hash is invalid' do
EncryptionService.instance_variable_set(:@cache, {})
EncryptionService.send(:delete_expired_keys)
expect(EncryptionService.instance_variable_get(:@cache)).to eq ({})
end
end
context 'invalid hash' do
it 'shouldnt delete expired hash when hash is invalid' do
EncryptionService.instance_variable_set(:@cache, {'test': 'value'})
EncryptionService.send(:delete_expired_keys)
expect(EncryptionService.instance_variable_get(:@cache)).to eq ({'test': 'value'})
end
end
context 'hash without expire date' do
it 'shouldnt delete expired hash when hash is invalid' do
EncryptionService.instance_variable_set(:@cache, {'test': {'key': 'generated_key'}})
EncryptionService.send(:delete_expired_keys)
expect(EncryptionService.instance_variable_get(:@cache)).to eq ({'test': {'key': 'generated_key'}})
end
end
end
context 'valid cases' do
context 'should not delete keys' do
it 'shouldnt delete hash keys' do
EncryptionService.instance_variable_set(:@cache,
{
'test1': {key: 'generated_key', expire_date: Time.now + 3.hours },
'test2': {key: 'generated_key', expire_date: Time.now + 2.hours },
'test3': {key: 'generated_key', expire_date: Time.now + 1.hours },
}.with_indifferent_access
)
EncryptionService.send(:delete_expired_keys)
expect(EncryptionService.instance_variable_get(:@cache).keys).to match_array %w[test1 test2 test3]
end
end
context 'should delete keys' do
it 'should delete hash keys' do
EncryptionService.instance_variable_set(:@cache,
{
'test1': {key: 'generated_key', expire_date: Time.now - 3.hours },
'test2': {key: 'generated_key', expire_date: Time.now - 2.hours },
'test3': {key: 'generated_key', expire_date: Time.now - 1.hours },
}.with_indifferent_access
)
EncryptionService.send(:delete_expired_keys)
expect(EncryptionService.instance_variable_get(:@cache)).to eq({})
end
end
end
end
context 'get_master_key' do
context 'empty cache' do
it 'should generate key by salt' do
EncryptionService.instance_variable_set(:@cache, {})
key = EncryptionService.send(:get_master_key, 'salt')
expect(EncryptionService.instance_variable_get(:@cache)['salt']['key']).to eq key
expect(EncryptionService.instance_variable_get(:@cache)['salt']['expire_date'].to_date).to eq 1.week.from_now.to_date
end
end
context 'non empty hash' do
let(:values) {
{
"salt":{
"key": "\xD5\xD8e\\N{1}\xEB\x9DP\x033)\x10{X\x91\xA1V\xB7\xCC\xE1L2\xDF\xC5,\f\x1Fh\xC6",
"expire_date": 1.week.from_now
}
}
}
it 'should get key from hash' do
EncryptionService.instance_variable_set(:@cache, values)
allow(Time).to receive(:now).and_return(1.week.ago)
key = EncryptionService.send(:get_master_key, 'salt')
expect(key).to eq values['salt']['key']
end
end
end
end
end

View File

@@ -0,0 +1,97 @@
# frozen_string_literal: true
describe EventMailer do
let(:event_mailer) { EventMailer.new('', '', '')}
let(:event) {
{:record=>
{:user=>
{:uid=>"ID8434CD6E8E",
:email=>"admin@barong.io",
:role=>"admin",
:level=>1,
:otp=>false,
:state=>"active",
:referral_uid=>nil,
:created_at=>"2020-05-26T07:01:04Z",
:updated_at=>"2020-05-26T08:30:54Z"},
:user_ip=>"::1",
:user_agent=>"PostmanRuntime/7.25.0"},
:name=>"system.session.create",
:state=>"sdasd"
}
}
describe "#nested_hash_value" do
it 'return event value' do
expect(event_mailer.send(:safe_dig, event, %i[name])).to eq event[:name]
expect(event_mailer.send(:safe_dig, event, %i[record user state])).to eq event[:record][:user][:state]
expect(event_mailer.send(:safe_dig, event, %i[record user uid])).to eq event[:record][:user][:uid]
expect(event_mailer.send(:safe_dig, event, %i[record user_agent])).to eq event[:record][:user_agent]
expect(event_mailer.send(:safe_dig, event, %i[record user_ip])).to eq event[:record][:user_ip]
expect(event_mailer.send(:safe_dig, event, %i[record name])).to eq nil
end
end
describe "#skip_event" do
context 'AND expression' do
let(:expression) { {
:and=>
{:"record.user_ip"=>"::1", :"record.user.role"=>"member"}
}
}
it 'should skip event' do
expect(event_mailer.send(:skip_event, event, expression)).to eq true
end
it 'shouldnt skip event' do
expression[:and][:"record.user.role"] = 'admin'
expect(event_mailer.send(:skip_event, event, expression)).to eq false
end
end
context 'OR expression' do
let(:expression) { {
:or=>
{:"record.user_ip"=>"::1", :"record.user.role"=>"member"}
}
}
it 'shouldnt skip event' do
expect(event_mailer.send(:skip_event, event, expression)).to eq false
end
it 'should skip event' do
expression[:or][:"record.user_ip"] = 'test'
expect(event_mailer.send(:skip_event, event, expression)).to eq true
end
end
context 'NOT expression' do
let(:expression) { {
:not=>
{:"record.user_ip"=>"::1"}
}
}
it 'shoul skip event' do
expect(event_mailer.send(:skip_event, event, expression)).to eq true
end
it 'shouldnt skip event' do
expression[:not][:"record.user_ip"] = 'test'
expect(event_mailer.send(:skip_event, event, expression)).to eq false
end
it 'should skip event' do
expression[:not][:'record.user.email'] = 'admin@barong.io'
expect(event_mailer.send(:skip_event, event, expression)).to eq true
end
it 'shouldnt skip event' do
expression[:not][:'record.user.email'] = 'admin1@barong.io'
expect(event_mailer.send(:skip_event, event, expression)).to eq false
end
end
end
end

View File

@@ -0,0 +1,148 @@
# frozen_string_literal: true
describe TOTPService do
let(:uid) { 'uid' }
let(:email) { 'email' }
let(:period) { '60' }
describe '.with_human_error' do
it 'renders human error when vault is down' do
expect do
described_class.with_human_error do
raise Vault::VaultError, 'Message connection refused message'
end
end.to raise_error(described_class::Error, '2FA server is under maintenance')
end
it 'renders human error when code was used twice' do
expect do
described_class.with_human_error do
raise Vault::VaultError, 'Message code already used message'
end
end.to raise_error(described_class::Error,
'This code was already used. Wait until the next time period')
end
it 'renders error when called without block' do
expect do
described_class.with_human_error
end.to raise_error(ArgumentError, 'Block is required')
end
end
describe '.server_available?' do
subject { described_class.server_available? }
context 'when server is available' do
before { expect(described_class).to receive(:read_data) { ['data'] } }
it { is_expected.to eq true }
end
context 'when server is not available' do
before { expect(described_class).to receive(:read_data) { [] } }
it { is_expected.to eq false }
end
context 'when exception raised' do
before do
expect(described_class).to receive(:read_data).and_raise(StandardError, 'vault error')
end
it { is_expected.to eq false }
end
end
describe '.otp_secret' do
let(:otp_url) { 'otpauth://totp/Alvand:admin@barong.io?secret=code' }
let(:otp) { double(data: { url: otp_url }) }
it { expect(described_class.otp_secret(otp)).to eq 'code' }
end
describe '.safe_create' do
it 'does not create secret when it exists' do
expect(described_class).to receive(:exist?).with(uid) { true }
expect(described_class).to_not receive(:create)
described_class.safe_create(uid, email, period)
end
it 'creates secret when it does not exist' do
expect(described_class).to receive(:exist?).with(uid) { false }
expect(described_class).to receive(:create).with(uid, email, period)
described_class.safe_create(uid, email, period)
end
end
describe '.create' do
let(:create_params) do
{
generate: true,
issuer: 'Alvand',
account_name: 'email',
period: period,
qr_size: 300
}
end
it 'creates secret' do
expect(described_class).to receive(:write_data)
.with('totp/keys/alvand_uid', create_params)
described_class.create(uid, email, period)
end
end
describe '.exist?' do
it 'creates secret' do
expect(described_class).to receive(:read_data)
.with('totp/keys/alvand_uid') { ['data'] }
described_class.exist?(uid)
end
end
describe '.validate?' do
before do
allow(described_class).to receive(:write_data) { double(data: data) }
allow(described_class).to receive(:read_data) { double(data: data) }
end
let(:data) { { valid: true } }
subject { described_class.validate?(uid, 'code') }
context 'when not exist' do
before { expect(described_class).to receive(:exist?) { false } }
it { is_expected.to eq false }
end
context 'when valid' do
before { expect(described_class).to receive(:exist?) { true } }
it { is_expected.to eq true }
end
context 'when invalid' do
before { expect(described_class).to receive(:exist?) { true } }
let(:data) { { valid: false } }
it { is_expected.to eq false }
end
end
describe '.delete' do
before { expect(described_class).to receive(:delete_data) }
it { expect(described_class.delete(uid)) }
end
describe 'private methods' do
let(:fake_vault) { double(read: 'read', write: 'writed', delete: 'deleted') }
before { stub_const('Vault', double(logical: fake_vault)) }
it 'read_data reads from vault storage' do
expect(described_class.send(:read_data, 'key')).to eq 'read'
end
it 'write_data writes to vault storage' do
expect(described_class.send(:write_data, 'key', {})).to eq 'writed'
end
it 'delete_data deletes from vault storage' do
expect(described_class.send(:delete_data, 'key')).to eq 'deleted'
end
end
end

161
spec/spec_helper.rb Normal file
View File

@@ -0,0 +1,161 @@
# frozen_string_literal: true
require 'rails_helper'
require_dependency 'barong/jwt'
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause
# this file to always be loaded, without a need to explicitly require it in any
# files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, consider making
# a separate helper file that requires the additional dependencies and performs
# the additional setup, and require it from the spec files that actually need
# it.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f }
RSpec.configure do |config|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
# See https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example
config.before(:suite) do
FileUtils.rm_rf(File.join(__dir__, 'tmp', 'cache'))
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, clean_database_with_truncation: true) do
FileUtils.rm_rf(File.join(__dir__, 'tmp', 'cache'))
DatabaseCleaner.clean_with :truncation
end
config.append_after(:each) do
DatabaseCleaner.clean
end
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.before(:each) do
DatabaseCleaner.start
allow(Ability).to receive(:abilities).and_return(
'roles' => %w[admin manager accountant superadmin technical compliance support],
'admin_permissions' => {
'superadmin' => { 'manage' => %w[User Activity Ability APIKey Profile Permission Label Restriction Level] },
'admin' => { 'read' => %w[Level APIKey Permission], 'manage' => %w[User Activity Profile Label] },
'compliance' => { 'read' => %w[User Activity Level], 'manage' => %w[Label], 'update' => %w[Profile] },
'support' => { 'read' => %w[User Activity APIKey Profile Label Level] }
}
)
allow_any_instance_of(Barong::Authorize).to receive(:validate_csrf!).and_return(true)
allow(UserStorageTitles).to receive(:list).and_return(%w[personal company])
%w[email phone identity document].each_with_index do |key, index|
FactoryBot.create(:level, id: index + 1, key: key, value: 'verified')
end
end
config.after(:each) do
DatabaseCleaner.clean
end
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
# have no way to turn it off -- the option exists only for backwards
# compatibility in RSpec 3). It causes shared context metadata to be
# inherited by the metadata hash of host groups and examples, rather than
# triggering implicit auto-inclusion in groups with matching metadata.
config.shared_context_metadata_behavior = :apply_to_host_groups
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
# # This allows you to limit a spec run to individual examples or groups
# # you care about by tagging them with `:focus` metadata. When nothing
# # is tagged with `:focus`, all examples get run. RSpec also provides
# # aliases for `it`, `describe`, and `context` that include `:focus`
# # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
# config.filter_run_when_matching :focus
#
# # Allows RSpec to persist some state between runs in order to support
# # the `--only-failures` and `--next-failure` CLI options. We recommend
# # you configure your source control system to ignore this file.
# config.example_status_persistence_file_path = "spec/examples.txt"
#
# # Limits the available syntax to the non-monkey patched syntax that is
# # recommended. For more details, see:
# # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
# # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
# # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
# config.disable_monkey_patching!
#
# # Many RSpec users commonly either run the entire suite or an individual
# # file, and it's useful to allow more verbose output when running an
# # individual spec file.
# if config.files_to_run.one?
# # Use the documentation formatter for detailed output,
# # unless a formatter has already been configured
# # (e.g. via a command-line flag).
# config.default_formatter = "doc"
# end
#
# # Print the 10 slowest examples and example groups at the
# # end of the spec run, to help surface which specs are running
# # particularly slow.
# config.profile_examples = 10
#
# # Seed global randomization in this process using the `--seed` CLI option.
# # Setting this allows you to use `--seed` to deterministically reproduce
# # test failures related to randomization by passing the same `--seed` value
# # as the one that triggered the failure.
# Kernel.srand config.seed
end

105
spec/support/api_helpers.rb Normal file
View File

@@ -0,0 +1,105 @@
# frozen_string_literal: true
module APIHelpers
extend Memoist
def json_body
JSON.parse(response.body, symbolize_names: true)
end
def expect_status_to_eq(status)
expect_status.to eq status
end
def expect_status
expect(response.status)
end
def expect_body
expect(json_body)
end
def post_json(destination, body, headers = {})
post destination,
params: build_body(body),
headers: headers.reverse_merge('Content-Type' => 'application/json')
end
def delete_json(destination, body, headers = {})
delete destination,
params: build_body(body),
headers: headers.reverse_merge('Content-Type' => 'application/json')
end
def put_json(destination, body, headers = {})
put destination,
params: build_body(body),
headers: headers.reverse_merge('Content-Type' => 'application/json')
end
def build_body(body)
body.is_a?(String) ? body : body.to_json
end
def create_label_with_level(user, level, scope: 'private')
create(:label, user: user,
key: level.key,
value: level.value,
scope: scope)
end
def set_level(user, level)
raise "level doesn't exist" if Level.last.id < level
levels = Level.where(id: 1..level)
levels.each do |lvl|
Label.find_or_create_by(user: user, key: lvl.key, value: lvl.value, scope: 'private')
end
end
def applogic_signed_jwt(payload)
multisig_jwt(payload, management_api_v2_keychain, :james, management_api_v2_algorithms)
end
def multisig_jwt(payload, keychain, signers, algorithms)
JWT::Multisig.generate_jwt(payload, keychain.slice(*signers), algorithms)
end
def multisig_jwt_management_api_v2(payload, *signers)
multisig_jwt(payload, management_api_v2_keychain, signers, management_api_v2_algorithms)
end
def management_api_v2_keychain
require 'openssl'
{ james: OpenSSL::PKey::RSA.generate(2048),
john: OpenSSL::PKey::RSA.generate(2048),
david: OpenSSL::PKey::RSA.generate(2048),
robert: OpenSSL::PKey::RSA.generate(2048),
alex: OpenSSL::PKey::RSA.generate(2048),
jeff: OpenSSL::PKey::RSA.generate(2048) }
end
memoize :management_api_v2_keychain
def management_api_v2_algorithms
management_api_v2_keychain.each_with_object({}) { |(k, _v), memo| memo[k] = 'RS256' }
end
memoize :management_api_v2_algorithms
def management_api_v2_security_configuration
API::V2::Management::JWTAuthenticationMiddleware.security_configuration
end
def defaults_for_management_api_v2_security_configuration!
config = { jwt: {} }
config[:keychain] = management_api_v2_keychain.each_with_object({}) do |(signer, key), memo|
memo[signer] = { algorithm: management_api_v2_algorithms.fetch(signer), value: key.public_key }
end
API::V2::Management::JWTAuthenticationMiddleware.security_configuration = config
end
def codec
@_codec ||= Barong::JWT.new(key: Barong::App.config.keystore.private_key)
end
end
RSpec.configure { |config| config.include APIHelpers }

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
shared_context 'bearer authentication' do
let(:test_user) { create(:user) }
let(:jwt_token) do
pkey = Rails.application.config.x.keystore.private_key
codec = Barong::JWT.new(key: pkey)
codec.encode(test_user.as_payload)
end
let(:auth_header) { { 'Authorization' => "Bearer #{jwt_token}" } }
end

View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end

View File

@@ -0,0 +1,31 @@
# frozen_string_literal: true
shared_context 'geoip mock' do
let(:london_ip) { '196.245.163.202' }
let(:tokyo_ip) { '140.227.60.114' }
before do
class DummyReader
def get(ip)
case ip
when '196.245.163.202'
{
'country' => { 'names' => { 'en' => 'United Kingdom' } },
'continent' => { 'names' => { 'en' => 'Europe' } }
}
when '140.227.60.114'
{
'country' => { 'names' => { 'en' => 'Japan' } },
'continent' => { 'names' => { 'en' => 'Asia' } }
}
else
{}
end
end
end
reader = DummyReader.new
allow(Barong::GeoIP).to receive(:reader).and_return(reader)
end
end

10
spec/support/helper.rb Normal file
View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
module Helper
def to_readable(field)
field.to_s.humanize.downcase
end
end
RSpec.configure { |config| config.include Helper }

View File

@@ -0,0 +1,93 @@
# frozen_string_literal: true
describe 'KYC::Kycaid::AddressWorker' do
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'kycaid') }
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:user) { create(:user) }
let!(:profile) { create(:profile, country: 'UA', state: 'submitted', applicant_id: '84e51f8a0677a646fd185fc741717ad9a8b3', user_id: user.id) }
let!(:document) { create(:document, identificator: '84e51f8a06', user_id: user.id, doc_type: 'Address') }
let!(:label) { create(:label, key: 'address', user_id: user.id, value: 'pending', scope: 'private') }
let(:address_params) { { "identificator": '84e51f8a06', 'user_id': document.user.id, "country": 'UA', "city": 'Kiev', "postcode": '101010' } }
describe 'successful verification' do
let(:successful_docs_response) { OpenStruct.new(address_id: '84e51f8a0677a646fd185fc741717ad9a8b3') }
let(:successful_verification_response) { OpenStruct.new(verification_id: '84e51f8a0677a646fd185fc741717ad9a8b3') }
before { allow(KYCAID::Address).to receive(:create).and_return(successful_docs_response) }
before { allow(KYCAID::Verification).to receive(:create).and_return(successful_verification_response) }
before { allow_any_instance_of(KYC::Kycaid::AddressWorker).to receive(:address_params).and_return({}) }
context 'perform' do
before { allow(KYCAID::Address).to receive(:create).and_return(successful_docs_response) }
before { allow(KYCAID::Verification).to receive(:create).and_return(successful_verification_response) }
it 'creates a document record' do
expect(document.metadata).to eq(nil)
expect(KYCAID::Address).to receive(:create)
KYC::Kycaid::AddressWorker.new.perform(address_params)
expect(document.reload.metadata).not_to eq(nil)
end
it 'creates a verification request' do
expect(document.metadata).to eq(nil)
expect(KYCAID::Address).to receive(:create)
expect(KYCAID::Verification).to receive(:create)
KYC::Kycaid::AddressWorker.new.perform(address_params)
expect(document.reload.metadata).not_to eq(nil)
end
end
end
describe 'failed verification' do
before { allow_any_instance_of(KYC::Kycaid::AddressWorker).to receive(:address_params).and_return({}) }
let(:unauthorized_response) { OpenStruct.new(error: { "type": 'unauthorized' }) }
let(:unsuccessful_response) do
OpenStruct.new(type: 'validation', errors: [{ "parameter": 'front_file', "message": 'Image is blured' }])
end
context 'unathorized' do
before { allow(KYCAID::Address).to receive(:create).and_return(unauthorized_response) }
it 'does not create a address because of error' do
expect(document.metadata).to eq(nil)
expect(KYCAID::Address).to receive(:create)
expect(KYCAID::Verification).not_to receive(:create)
KYC::Kycaid::AddressWorker.new.perform(address_params)
expect(document.reload.metadata).to eq(nil)
end
end
context 'validation failed' do
before { allow(KYCAID::Address).to receive(:create).and_return(unsuccessful_response) }
it 'does not create a address' do
expect(document.metadata).to eq(nil)
expect(KYCAID::Address).to receive(:create)
KYC::Kycaid::AddressWorker.new.perform(address_params)
expect(document.metadata).to eq(nil)
end
it 'gets a rejected label' do
expect(document.metadata).to eq(nil)
expect(document.user.labels.count).to eq(2)
expect(document.user.labels.find_by(key: :address).value).to eq('pending')
expect(KYCAID::Address).to receive(:create)
KYC::Kycaid::AddressWorker.new.perform(address_params)
expect(document.metadata).to eq(nil)
expect(document.user.labels.count).to eq(2)
expect(document.user.labels.find_by(key: :address).value).to eq('rejected')
end
end
end
end

View File

@@ -0,0 +1,90 @@
# frozen_string_literal: true
describe 'KYC::ApplicantWorker' do
include_context 'bearer authentication'
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'kycaid') }
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:profile) { create(:profile, country: "UA", state: 'submitted') }
describe 'successful verification' do
let(:successful_response) { OpenStruct.new(applicant_id: "84e51f8a0677a646fd185fc741717ad9a8b3") }
let(:params) {{ type: 'PERSON', first_name: profile.first_name, last_name: profile.last_name, dob: profile.dob,
residence_country: profile.country, email: profile.user.email, phone: profile.user.phones&.last&.number }}
before { allow(KYCAID::Applicant).to receive(:create).with(params).and_return(successful_response)}
context 'perform' do
it 'creates an applicant' do
expect(profile.applicant_id).to eq(nil)
expect(KYCAID::Applicant).to receive(:create)
KYC::Kycaid::ApplicantWorker.new.perform(profile.id)
expect(profile.reload.applicant_id).not_to eq(nil)
end
it 'changes a label to verified' do
expect(profile.applicant_id).to eq(nil)
expect(profile.user.labels.count).to eq(1)
expect(profile.user.labels.first.key).to eq('profile')
expect(profile.user.labels.first.value).to eq('submitted')
expect(KYCAID::Applicant).to receive(:create)
KYC::Kycaid::ApplicantWorker.new.perform(profile.id)
expect(profile.reload.applicant_id).not_to eq(nil)
expect(profile.user.labels.count).to eq(1)
expect(profile.user.labels.first.key).to eq('profile')
expect(profile.user.labels.first.value).to eq('verified')
end
end
end
describe 'failed verification' do
let(:unauthorized_response) { OpenStruct.new(error: { "type": "unauthorized"}) }
let(:unsuccessful_response) {
OpenStruct.new(type: "validation", errors: [{"parameter": "residence_country", "message": "Country of residence is not valid"}])
}
let(:params) {{ type: 'PERSON', first_name: profile.first_name, last_name: profile.last_name, dob: profile.dob,
residence_country: profile.country, email: profile.user.email, phone: profile.user.phones&.last&.number }}
context 'unathorized' do
before { allow(KYCAID::Applicant).to receive(:create).with(params).and_return(unauthorized_response)}
it 'does not create an applicant' do
expect(profile.applicant_id).to eq(nil)
expect(KYCAID::Applicant).to receive(:create)
KYC::Kycaid::ApplicantWorker.new.perform(profile.id)
expect(profile.reload.applicant_id).to eq(nil)
end
end
context 'validation failed' do
before { allow(KYCAID::Applicant).to receive(:create).with(params).and_return(unsuccessful_response)}
it 'does not create an applicant' do
expect(profile.applicant_id).to eq(nil)
expect(KYCAID::Applicant).to receive(:create)
KYC::Kycaid::ApplicantWorker.new.perform(profile.id)
expect(profile.reload.applicant_id).to eq(nil)
end
it 'gets a rejected label' do
expect(profile.applicant_id).to eq(nil)
expect(KYCAID::Applicant).to receive(:create)
KYC::Kycaid::ApplicantWorker.new.perform(profile.id)
expect(profile.reload.applicant_id).to eq(nil)
expect(profile.user.labels.count).to eq(1)
expect(profile.user.labels.first.key).to eq('profile')
expect(profile.user.labels.first.value).to eq('rejected')
end
end
end
end

View File

@@ -0,0 +1,93 @@
# frozen_string_literal: true
describe 'KYC::Kycaid::DocumentWorker' do
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'kycaid') }
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:user) { create(:user) }
let!(:profile) { create(:profile, country: 'UA', state: 'submitted', applicant_id: '84e51f8a0677a646fd185fc741717ad9a8b3', user_id: user.id) }
let!(:document) { create(:document, identificator: '84e51f8a06', doc_type: 'Passport', doc_category: 'front_side', user_id: user.id) }
let!(:document_second) { create(:document, identificator: '84e51f8a06', doc_type: 'Passport', doc_category: 'selfie', user_id: user.id) }
describe 'successful verification' do
let(:successful_docs_response) { OpenStruct.new(document_id: '84e51f8a0677a646fd185fc741717ad9a8b3') }
let(:successful_verification_response) { OpenStruct.new(verification_id: '84e51f8a0677a646fd185fc741717ad9a8b3') }
before { allow(KYCAID::Document).to receive(:create).and_return(successful_docs_response) }
before { allow(KYCAID::Verification).to receive(:create).and_return(successful_verification_response) }
before { allow_any_instance_of(KYC::Kycaid::DocumentWorker).to receive(:document_params).and_return({}) }
before { allow_any_instance_of(KYC::Kycaid::DocumentWorker).to receive(:selfie_image_params).and_return({}) }
context 'perform' do
it 'creates a document record' do
expect(document.metadata).to eq(nil)
expect(KYCAID::Document).to receive(:create)
KYC::Kycaid::DocumentWorker.new.perform(document.user.id, '84e51f8a06')
expect(document.reload.metadata).not_to eq(nil)
end
it 'creates a verification request' do
expect(document.metadata).to eq(nil)
expect(KYCAID::Document).to receive(:create)
expect(KYCAID::Verification).to receive(:create)
KYC::Kycaid::DocumentWorker.new.perform(document.user.id, '84e51f8a06')
expect(document.reload.metadata).not_to eq(nil)
end
end
end
describe 'failed verification' do
before { allow_any_instance_of(KYC::Kycaid::DocumentWorker).to receive(:document_params).and_return({}) }
before { allow_any_instance_of(KYC::Kycaid::DocumentWorker).to receive(:selfie_image_params).and_return({}) }
let(:unauthorized_response) { OpenStruct.new(error: { "type": 'unauthorized' }) }
let(:unsuccessful_response) do
OpenStruct.new(type: 'validation', errors: [{ "parameter": 'front_file', "message": 'Image is blured' }])
end
context 'unathorized' do
before { allow(KYCAID::Document).to receive(:create).and_return(unauthorized_response) }
it 'does not create a document because of error' do
expect(document.metadata).to eq(nil)
expect(KYCAID::Document).to receive(:create)
expect(KYCAID::Verification).not_to receive(:create)
KYC::Kycaid::DocumentWorker.new.perform(document.user.id, '84e51f8a06')
expect(document.reload.metadata).to eq(nil)
end
end
context 'validation failed' do
before { allow(KYCAID::Document).to receive(:create).and_return(unsuccessful_response) }
it 'does not create a document' do
expect(document.metadata).to eq(nil)
expect(KYCAID::Document).to receive(:create)
KYC::Kycaid::DocumentWorker.new.perform(document.user.id, '84e51f8a06')
expect(document.metadata).to eq(nil)
end
it 'gets a rejected label' do
expect(document.metadata).to eq(nil)
expect(document.user.labels.count).to eq(2)
expect(document.user.labels.find_by(key: :document).value).to eq('pending')
expect(KYCAID::Document).to receive(:create)
KYC::Kycaid::DocumentWorker.new.perform(document.user.id, '84e51f8a06')
expect(document.metadata).to eq(nil)
expect(document.user.labels.count).to eq(2)
expect(document.user.labels.find_by(key: :document).value).to eq('rejected')
end
end
end
end

View File

@@ -0,0 +1,87 @@
# frozen_string_literal: true
describe 'KYC::Kycaid::VerificationWorker' do
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'kycaid') }
include_context 'bearer authentication'
let!(:create_member_permission) do
create :permission,
role: 'member'
end
let(:user) { create(:user) }
let!(:profile) { create(:profile, country: 'UA', state: 'submitted', applicant_id: '84e51f8a0677a646fd185fc741717ad9a8b3', user_id: user.id) }
let!(:document) { create(:document, identificator: '84e51f8a06', doc_type: 'Passport', doc_category: 'front_side', user_id: user.id) }
let!(:document_second) { create(:document, identificator: '84e51f8a06', doc_type: 'Passport', doc_category: 'selfie', user_id: user.id) }
describe 'successful verifications' do
let(:successful_verification_response) { OpenStruct.new(status: 'completed', verifications: { document: { verified: true } }) }
before { allow(KYCAID::Verification).to receive(:fetch).and_return(successful_verification_response) }
context 'perform' do
it 'fetches verification data' do
expect(user.labels.count).to eq(2)
expect(user.labels.find_by(key: :document, scope: :private).value).to eq('pending')
expect(KYCAID::Verification).to receive(:fetch)
KYC::Kycaid::VerificationsWorker.new.perform({ applicant_id: profile.applicant_id, verification_id: '84e51f8a' })
end
it 'changes label accordingly to verification' do
expect(user.labels.count).to eq(2)
expect(user.labels.find_by(key: :document, scope: :private).value).to eq('pending')
expect(KYCAID::Verification).to receive(:fetch)
KYC::Kycaid::VerificationsWorker.new.perform({ applicant_id: profile.applicant_id, verification_id: '84e51f8a' })
expect(user.labels.count).to eq(2)
expect(user.labels.reload.find_by(key: :document, scope: :private).value).to eq('verified')
end
end
end
describe 'failed verification' do
let(:unsuccessful_response) { OpenStruct.new(status: 'completed', verifications: { document: { verified: false } }) }
before { allow(KYCAID::Verification).to receive(:fetch).and_return(unsuccessful_response) }
let(:unauthorized_response) { OpenStruct.new(error: { "type": 'unauthorized' }) }
context 'unathorized' do
before { allow(KYCAID::Verification).to receive(:fetch).and_return(unauthorized_response) }
it 'does not create a document because of error' do
expect(user.labels.count).to eq(2)
expect(user.labels.find_by(key: :document, scope: :private).value).to eq('pending')
expect(KYCAID::Verification).to receive(:fetch)
KYC::Kycaid::VerificationsWorker.new.perform({ applicant_id: profile.applicant_id, verification_id: '84e51f8a' })
expect(user.labels.reload.find_by(key: :document, scope: :private).value).to eq('pending')
end
end
context 'verification declined' do
it 'fetches verification data' do
expect(user.labels.count).to eq(2)
expect(user.labels.find_by(key: :document, scope: :private).value).to eq('pending')
expect(KYCAID::Verification).to receive(:fetch)
KYC::Kycaid::VerificationsWorker.new.perform({ applicant_id: profile.applicant_id, verification_id: '84e51f8a' })
end
it 'changes label accordingly to verification' do
expect(user.labels.count).to eq(2)
expect(user.labels.find_by(key: :document, scope: :private).value).to eq('pending')
expect(KYCAID::Verification).to receive(:fetch)
KYC::Kycaid::VerificationsWorker.new.perform({ applicant_id: profile.applicant_id, verification_id: '84e51f8a' })
expect(user.labels.count).to eq(2)
expect(user.labels.reload.find_by(key: :document, scope: :private).value).to eq('rejected')
end
end
end
end