Initial commit
This commit is contained in:
39
spec/api/v2/admin/abilities_spec.rb
Normal file
39
spec/api/v2/admin/abilities_spec.rb
Normal 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
|
||||
145
spec/api/v2/admin/activities_spec.rb
Normal file
145
spec/api/v2/admin/activities_spec.rb
Normal 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
|
||||
105
spec/api/v2/admin/api_keys_spec.rb
Normal file
105
spec/api/v2/admin/api_keys_spec.rb
Normal 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
|
||||
39
spec/api/v2/admin/levels_spec.rb
Normal file
39
spec/api/v2/admin/levels_spec.rb
Normal 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
|
||||
49
spec/api/v2/admin/metrics_spec.rb
Normal file
49
spec/api/v2/admin/metrics_spec.rb
Normal 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
|
||||
144
spec/api/v2/admin/permissions_spec.rb
Normal file
144
spec/api/v2/admin/permissions_spec.rb
Normal 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
|
||||
211
spec/api/v2/admin/profiles_spec.rb
Normal file
211
spec/api/v2/admin/profiles_spec.rb
Normal 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
|
||||
202
spec/api/v2/admin/restrictions_spec.rb
Normal file
202
spec/api/v2/admin/restrictions_spec.rb
Normal 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
|
||||
1176
spec/api/v2/admin/users_spec.rb
Normal file
1176
spec/api/v2/admin/users_spec.rb
Normal file
File diff suppressed because it is too large
Load Diff
307
spec/api/v2/auth/audit_spec.rb
Normal file
307
spec/api/v2/auth/audit_spec.rb
Normal 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
|
||||
366
spec/api/v2/auth/auth_spec.rb
Normal file
366
spec/api/v2/auth/auth_spec.rb
Normal file
@@ -0,0 +1,366 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe '/api/v2/auth functionality test' do
|
||||
include_context 'geoip mock'
|
||||
|
||||
let(:uri) { '/api/v2/identity/sessions' }
|
||||
let!(:create_permissions) do
|
||||
create :permission, role: 'admin'
|
||||
create :permission, role: 'member', action: 'ACCEPT', verb: 'all', path: 'not_in_the_rules_path'
|
||||
create :permission, role: 'member', action: 'ACCEPT', verb: 'get', path: '/api/v2/resource/users/me'
|
||||
create :permission, role: 'accountant'
|
||||
end
|
||||
let!(:user) { create(:user) }
|
||||
let(:params) do
|
||||
{
|
||||
email: user.email,
|
||||
password: user.password
|
||||
}
|
||||
end
|
||||
|
||||
let(:do_create_session_request) { post uri, params: params }
|
||||
let(:auth_request) { '/api/v2/auth/not_in_the_rules_path' }
|
||||
let(:protected_request) { '/api/v2/resource/users/me' }
|
||||
|
||||
describe 'testing workability with session' do
|
||||
before do
|
||||
Rails.cache.delete('permissions')
|
||||
end
|
||||
|
||||
context 'with valid session' do
|
||||
before do
|
||||
do_create_session_request
|
||||
end
|
||||
|
||||
it 'returns bearer token on valid session' do
|
||||
get auth_request
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'allows any type of request' do
|
||||
available_types = %w[post get put head delete patch]
|
||||
available_types.each do |ping|
|
||||
method("#{ping}").call auth_request
|
||||
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
|
||||
get protected_request, headers: { 'Authorization' => response.headers['Authorization'] }
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ip related' do
|
||||
let(:create_session_uri) { uri }
|
||||
let(:auth_uri) { '/api/v2/auth/not_in_the_rules_path' }
|
||||
let(:ip_to_be_equal) { '168.238.57.64' }
|
||||
let(:ip_that_differs) { '168.27.3.225' }
|
||||
|
||||
context 'default ip behaviour from HTTP_X_FORWARDED_FOR via action_dispatch.remote_ip' do
|
||||
it 'works if ip from session matches request ip' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'denies access if ip from session doesnt match request ip' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_that_differs }
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'ip behaviour from TRUE_CLIENT_IP header' do
|
||||
before do
|
||||
allow(Barong::App.config).to receive_messages(gateway: 'akamai')
|
||||
end
|
||||
|
||||
it 'works if ip from session matches request ip' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_that_differs, 'HTTP_TRUE_CLIENT_IP' => ip_to_be_equal }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'uses HTTP_X_FORWARDED_FOR if TRUE_CLIENT_IP missing and works' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'uses HTTP_X_FORWARDED_FOR if TRUE_CLIENT_IP empty and works' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => '' }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => '' }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
end
|
||||
|
||||
it 'denies access if ip from session doesnt match request ip in HTTP_TRUE_CLIENT_IP' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => ip_to_be_equal }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_that_differs, 'HTTP_TRUE_CLIENT_IP' => ip_that_differs }
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'uses HTTP_X_FORWARDED_FOR if TRUE_CLIENT_IP empty or missing and denies access' do
|
||||
post create_session_uri, params: params, headers: { 'HTTP_X_FORWARDED_FOR' => ip_to_be_equal, 'HTTP_TRUE_CLIENT_IP' => '' }
|
||||
get auth_uri, headers: { 'HTTP_X_FORWARDED_FOR' => ip_that_differs }
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'testing session related errors' do
|
||||
it 'renders error if no session or api key headers provided' do
|
||||
get auth_request
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.invalid_session\"]}")
|
||||
end
|
||||
|
||||
it 'renders error if session belongs to non-active user' do
|
||||
do_create_session_request
|
||||
expect(response.status).to eq(200)
|
||||
user.update(state: 'banned')
|
||||
|
||||
get auth_request
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.user_not_active\"]}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'testing restrictions' do
|
||||
before do
|
||||
Rails.cache.delete('permissions')
|
||||
do_create_session_request
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
let(:do_restricted_request) { put '/api/v2/auth/api/v2/peatio/management/ping' }
|
||||
|
||||
it 'receives access error if path is blacklisted' do
|
||||
do_restricted_request
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.permission_denied\"]}")
|
||||
end
|
||||
|
||||
let(:do_whitelisted_request) { put '/api/v2/auth/api/v2/peatio/public/ping' }
|
||||
|
||||
it 'receives access error if path is blacklisted' do
|
||||
do_whitelisted_request
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to be_empty
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'testing workability with api keys' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:test_user) { create(:user, otp: otp_enabled) }
|
||||
let(:otp_enabled) { true }
|
||||
let!(:api_key) { create :api_key, key_holder_account: test_user }
|
||||
let(:otp_code) { '1357' }
|
||||
let(:nonce) { (Time.now.to_f * 1000).to_i }
|
||||
let(:kid) { api_key.kid }
|
||||
let(:secret) { api_key.secret }
|
||||
let(:data) { nonce.to_s + kid }
|
||||
let(:algorithm) { 'SHA' + api_key.algorithm[2..4]}
|
||||
let(:signature) { OpenSSL::HMAC.hexdigest(algorithm, secret, data) }
|
||||
|
||||
before do
|
||||
Rails.cache.delete('permissions')
|
||||
SecretStorage.store_secret(secret, api_key.kid)
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, otp_code) { true }
|
||||
end
|
||||
|
||||
context 'testing api key related errors' do
|
||||
it 'catches api key headers and renders error on missing header' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
}
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.invalid_api_key_headers\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error if nonce is string' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => 'nonce',
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.nonce_not_valid_timestamp\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error if nonce is zero' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => 0,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.nonce_not_valid_timestamp\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error if nonce is older than default 5 seconds in milliseconds' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => ((Time.now - 10.seconds).to_f * 1000).to_i,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.nonce_expired\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error if nonce is from future with milisecond' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => ((Time.now + 10.seconds).to_f * 1000).to_i,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.nonce_expired\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'catches api key headers and renders error on blank header' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => ' '
|
||||
}
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.invalid_api_key_headers\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
it 'renders error when signature is invalid' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => 'some-random-signature'
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.invalid_signature\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
let(:ban_api_key) { APIKey.last.update(state: 'banned') }
|
||||
|
||||
it 'renders error when api key state is not active' do
|
||||
ban_api_key
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.apikey_not_active\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
let(:ban_user) { test_user.update(state: 'banned') }
|
||||
|
||||
it 'renders error when api key state is not active' do
|
||||
ban_user
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.apikey_not_active\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
|
||||
let(:disable_user_2fa) { test_user.update(otp: false) }
|
||||
|
||||
it 'renders error when api key is valid but user have disabled 2fa' do
|
||||
disable_user_2fa
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.apikey_not_active\"]}")
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'testing api key with valid params' do
|
||||
it 'catches api key headers and works as expected with milisecond nonce' do
|
||||
get auth_request, headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to be_empty
|
||||
expect(response.headers['Authorization']).to include "Bearer"
|
||||
expect(response.headers['Authorization']).not_to be_nil
|
||||
|
||||
get protected_request, headers: { 'Authorization' => response.headers['Authorization'] }
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'testing restrictions' do
|
||||
let(:do_restricted_request) {
|
||||
put '/api/v2/auth/api/v2/peatio/management/ping', headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
}
|
||||
|
||||
it 'receives access error if path is blacklisted' do
|
||||
do_restricted_request
|
||||
expect(response.status).to eq(401)
|
||||
expect(response.body).to eq("{\"errors\":[\"authz.permission_denied\"]}")
|
||||
end
|
||||
|
||||
let(:do_whitelisted_request) {
|
||||
put '/api/v2/auth/api/v2/peatio/public/ping', headers: {
|
||||
'X-Auth-Apikey' => kid,
|
||||
'X-Auth-Nonce' => nonce,
|
||||
'X-Auth-Signature' => signature
|
||||
}
|
||||
}
|
||||
|
||||
it 'receives access error if path is whitelisted' do
|
||||
do_whitelisted_request
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to be_empty
|
||||
expect(response.headers['Authorization']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
138
spec/api/v2/auth/csrf_spec.rb
Normal file
138
spec/api/v2/auth/csrf_spec.rb
Normal 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
|
||||
144
spec/api/v2/auth/rbac_spec.rb
Normal file
144
spec/api/v2/auth/rbac_spec.rb
Normal 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
|
||||
196
spec/api/v2/auth/restriction_spec.rb
Normal file
196
spec/api/v2/auth/restriction_spec.rb
Normal 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
|
||||
158
spec/api/v2/auth/sessions_spec.rb
Normal file
158
spec/api/v2/auth/sessions_spec.rb
Normal 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
|
||||
141
spec/api/v2/cors/cors_spec.rb
Normal file
141
spec/api/v2/cors/cors_spec.rb
Normal 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
|
||||
82
spec/api/v2/cors/validations_spec.rb
Normal file
82
spec/api/v2/cors/validations_spec.rb
Normal 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
|
||||
20
spec/api/v2/entities/activities_spec.rb
Normal file
20
spec/api/v2/entities/activities_spec.rb
Normal 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
|
||||
17
spec/api/v2/entities/labels_spec.rb
Normal file
17
spec/api/v2/entities/labels_spec.rb
Normal 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
|
||||
27
spec/api/v2/entities/user_with_full_spec.rb
Normal file
27
spec/api/v2/entities/user_with_full_spec.rb
Normal 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
|
||||
|
||||
41
spec/api/v2/identity/general_spec.rb
Normal file
41
spec/api/v2/identity/general_spec.rb
Normal 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
|
||||
462
spec/api/v2/identity/sessions_spec.rb
Normal file
462
spec/api/v2/identity/sessions_spec.rb
Normal 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
|
||||
717
spec/api/v2/identity/users_spec.rb
Normal file
717
spec/api/v2/identity/users_spec.rb
Normal 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
|
||||
374
spec/api/v2/management/api_keys_spec.rb
Normal file
374
spec/api/v2/management/api_keys_spec.rb
Normal 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
|
||||
101
spec/api/v2/management/documents_spec.rb
Normal file
101
spec/api/v2/management/documents_spec.rb
Normal 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
|
||||
115
spec/api/v2/management/jwt_authentication_middleware_spec.rb
Normal file
115
spec/api/v2/management/jwt_authentication_middleware_spec.rb
Normal 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
|
||||
304
spec/api/v2/management/labels_spec.rb
Normal file
304
spec/api/v2/management/labels_spec.rb
Normal 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
|
||||
96
spec/api/v2/management/otp_spec.rb
Normal file
96
spec/api/v2/management/otp_spec.rb
Normal 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
|
||||
319
spec/api/v2/management/phones_spec.rb
Normal file
319
spec/api/v2/management/phones_spec.rb
Normal 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
|
||||
134
spec/api/v2/management/profiles_spec.rb
Normal file
134
spec/api/v2/management/profiles_spec.rb
Normal 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
|
||||
373
spec/api/v2/management/service_accounts_spec.rb
Normal file
373
spec/api/v2/management/service_accounts_spec.rb
Normal 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
|
||||
556
spec/api/v2/management/users_spec.rb
Normal file
556
spec/api/v2/management/users_spec.rb
Normal 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
|
||||
41
spec/api/v2/public/general_spec.rb
Normal file
41
spec/api/v2/public/general_spec.rb
Normal 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
|
||||
22
spec/api/v2/queries/activity_filter_spec.rb
Normal file
22
spec/api/v2/queries/activity_filter_spec.rb
Normal 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
|
||||
135
spec/api/v2/resource/addresses_spec.rb
Normal file
135
spec/api/v2/resource/addresses_spec.rb
Normal 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
|
||||
263
spec/api/v2/resource/api_keys_spec.rb
Normal file
263
spec/api/v2/resource/api_keys_spec.rb
Normal 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
|
||||
58
spec/api/v2/resource/data_storage_spec.rb
Normal file
58
spec/api/v2/resource/data_storage_spec.rb
Normal 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
|
||||
288
spec/api/v2/resource/documents_spec.rb
Normal file
288
spec/api/v2/resource/documents_spec.rb
Normal 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
|
||||
88
spec/api/v2/resource/labels_spec.rb
Normal file
88
spec/api/v2/resource/labels_spec.rb
Normal 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
|
||||
129
spec/api/v2/resource/otp_spec.rb
Normal file
129
spec/api/v2/resource/otp_spec.rb
Normal 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
|
||||
469
spec/api/v2/resource/phones_spec.rb
Normal file
469
spec/api/v2/resource/phones_spec.rb
Normal 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
|
||||
572
spec/api/v2/resource/profiles_spec.rb
Normal file
572
spec/api/v2/resource/profiles_spec.rb
Normal 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] = "'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 "doesn't accept @ in address" do
|
||||
request_params[:address] = "'Larkin–Fork' @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] = '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 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
|
||||
297
spec/api/v2/resource/service_accounts_spec.rb
Normal file
297
spec/api/v2/resource/service_accounts_spec.rb
Normal 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
|
||||
270
spec/api/v2/resource/users_spec.rb
Normal file
270
spec/api/v2/resource/users_spec.rb
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user