Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/capi/src/capi.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
woody,
capi_woody_client,
damsel,
fraudbusters_proto,
lechiffre,
bender_proto,
bender_client,
Expand Down
1 change: 1 addition & 0 deletions apps/capi/src/capi_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ map_error(validation_error, Error) ->

get_handlers() ->
[
capi_handler_antifraud,
capi_handler_categories,
capi_handler_countries,
capi_handler_invoice_templates,
Expand Down
66 changes: 66 additions & 0 deletions apps/capi/src/capi_handler_antifraud.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
-module(capi_handler_antifraud).

-include_lib("fraudbusters_proto/include/fb_proto_fraudbusters_thrift.hrl").
-include_lib("damsel/include/dmsl_base_thrift.hrl").

-behaviour(capi_handler).

-export([prepare/3]).

-import(capi_handler_utils, [logic_error/2]).

-spec prepare(
OperationID :: capi_handler:operation_id(),
Req :: capi_handler:request_data(),
Context :: capi_handler:processing_context()
) -> {ok, capi_handler:request_state()} | {error, noimpl}.
prepare('InspectUser' = OperationID, Req, Context) ->
Authorize = fun() ->
Prototypes = [{operation, #{id => OperationID}}],
{ok, capi_auth:authorize_operation(Prototypes, Context)}
end,
Process = fun() ->
Body = maps:get('UserInspectRequest', Req),
InspectUserContext = encode_inspect_user_context(Body),
Call = {inspector, 'InspectUserShops', {InspectUserContext}},
case capi_handler_utils:service_call(Call, Context) of
{ok, BlockedShops} ->
{ok, {200, #{}, decode_blocked_shops(BlockedShops)}};
{exception, #'base_InvalidRequest'{errors = Errors}} ->
FormattedErrors = capi_handler_utils:format_request_errors(Errors),
{ok, logic_error('invalidRequest', FormattedErrors)}
end
end,
{ok, #{authorize => Authorize, process => Process}};
prepare(_OperationID, _Req, _Context) ->
{error, noimpl}.

%%

encode_inspect_user_context(#{<<"customer">> := Customer, <<"shops">> := Shops}) ->
#'fraudbusters_InspectUserContext'{
user_info = encode_client_info(Customer),
shop_list = [encode_shop_context(S) || S <- Shops]
}.

encode_client_info(Customer) ->
Device = maps:get(<<"device">>, Customer, #{}),
Contact = maps:get(<<"contact">>, Customer, #{}),
#'fraudbusters_ClientInfo'{
ip = maps:get(<<"ip">>, Device, undefined),
fingerprint = maps:get(<<"fingerprint">>, Device, undefined),
email = maps:get(<<"email">>, Contact, undefined),
phone = maps:get(<<"phoneNumber">>, Contact, undefined)
}.

encode_shop_context(#{<<"partyID">> := PartyID, <<"shopID">> := ShopID}) ->
#'fraudbusters_ShopContext'{
party_id = PartyID,
shop_id = ShopID
}.

decode_blocked_shops(#'fraudbusters_BlockedShops'{shop_list = ShopList}) ->
#{<<"blockedShops">> => [decode_shop_context(S) || S <- ShopList]}.

decode_shop_context(#'fraudbusters_ShopContext'{party_id = PartyID, shop_id = ShopID}) ->
#{<<"partyID">> => PartyID, <<"shopID">> => ShopID}.
167 changes: 167 additions & 0 deletions apps/capi/test/capi_antifraud_tests_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
-module(capi_antifraud_tests_SUITE).

-include_lib("common_test/include/ct.hrl").
-include_lib("stdlib/include/assert.hrl").

-include_lib("fraudbusters_proto/include/fb_proto_fraudbusters_thrift.hrl").
-include_lib("capi_dummy_data.hrl").
-include_lib("capi_bouncer_data.hrl").

-export([all/0]).
-export([groups/0]).
-export([init_per_suite/1]).
-export([end_per_suite/1]).
-export([init_per_group/2]).
-export([end_per_group/2]).
-export([init_per_testcase/2]).
-export([end_per_testcase/2]).

-export([init/1]).

-export([
inspect_user_ok_test/1,
inspect_user_no_blocked_shops_test/1,
inspect_user_invalid_request_test/1,
inspect_user_forbidden_test/1
]).

-type test_case_name() :: atom().
-type config() :: [{atom(), any()}].
-type group_name() :: atom().

-behaviour(supervisor).

-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init([]) ->
{ok, {#{strategy => one_for_all, intensity => 1, period => 1}, []}}.

-spec all() -> [{group, test_case_name()}].
all() ->
[{group, operations_by_user_session_token}].

-spec groups() -> [{group_name(), list(), [test_case_name()]}].
groups() ->
[
{operations_by_user_session_token, [], [
inspect_user_ok_test,
inspect_user_no_blocked_shops_test,
inspect_user_invalid_request_test,
inspect_user_forbidden_test
]}
].

-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
capi_ct_helper:init_suite(?MODULE, Config).

-spec end_per_suite(config()) -> _.
end_per_suite(C) ->
_ = capi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
_ = [application:stop(App) || App <- proplists:get_value(apps, C)],
ok.

-spec init_per_group(group_name(), config()) -> config().
init_per_group(operations_by_user_session_token, Config) ->
SupPid = capi_ct_helper:start_mocked_service_sup(?MODULE),
Apps = capi_ct_helper_token_keeper:mock_user_session_token(SupPid),
[{context, capi_ct_helper:get_context(?API_TOKEN)}, {group_apps, Apps}, {group_test_sup, SupPid} | Config].

-spec end_per_group(group_name(), config()) -> _.
end_per_group(operations_by_user_session_token, C) ->
capi_utils:'maybe'(?config(group_test_sup, C), fun capi_ct_helper:stop_mocked_service_sup/1);
end_per_group(_Group, _C) ->
ok.

-spec init_per_testcase(test_case_name(), config()) -> config().
init_per_testcase(_Name, C) ->
[{test_sup, capi_ct_helper:start_mocked_service_sup(?MODULE)} | C].

-spec end_per_testcase(test_case_name(), config()) -> _.
end_per_testcase(_Name, C) ->
capi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
ok.

%%% Tests

-define(PARTY_ID, <<"party-1">>).
-define(SHOP_ID_1, <<"shop-1">>).
-define(SHOP_ID_2, <<"shop-2">>).

-define(INSPECT_USER_REQUEST, #{
<<"customer">> => #{
<<"device">> => #{
<<"fingerprint">> => <<"abc123">>,
<<"ip">> => <<"192.168.1.1">>
},
<<"contact">> => #{
<<"email">> => <<"test@example.com">>,
<<"phoneNumber">> => <<"+79001234567">>
}
},
<<"shops">> => [
#{<<"partyID">> => ?PARTY_ID, <<"shopID">> => ?SHOP_ID_1},
#{<<"partyID">> => ?PARTY_ID, <<"shopID">> => ?SHOP_ID_2}
]
}).

-spec inspect_user_ok_test(config()) -> _.
inspect_user_ok_test(Config) ->
_ = capi_ct_helper:mock_services(
[
{inspector, fun(
'InspectUserShops',
{#'fraudbusters_InspectUserContext'{
user_info = #'fraudbusters_ClientInfo'{
email = <<"test@example.com">>,
fingerprint = <<"abc123">>,
ip = <<"192.168.1.1">>,
phone = <<"+79001234567">>
}
}}
) ->
{ok, #'fraudbusters_BlockedShops'{
shop_list = [
#'fraudbusters_ShopContext'{party_id = ?PARTY_ID, shop_id = ?SHOP_ID_1}
]
}}
end}
],
Config
),
_ = capi_ct_helper_bouncer:mock_assert_op_ctx(<<"InspectUser">>, Config),
{ok, #{<<"blockedShops">> := [#{<<"partyID">> := ?PARTY_ID, <<"shopID">> := ?SHOP_ID_1}]}} =
capi_client_antifraud:inspect_user(?config(context, Config), ?INSPECT_USER_REQUEST).

-spec inspect_user_no_blocked_shops_test(config()) -> _.
inspect_user_no_blocked_shops_test(Config) ->
_ = capi_ct_helper:mock_services(
[
{inspector, fun('InspectUserShops', _) ->
{ok, #'fraudbusters_BlockedShops'{shop_list = []}}
end}
],
Config
),
_ = capi_ct_helper_bouncer:mock_assert_op_ctx(<<"InspectUser">>, Config),
{ok, #{<<"blockedShops">> := []}} =
capi_client_antifraud:inspect_user(?config(context, Config), ?INSPECT_USER_REQUEST).

-spec inspect_user_invalid_request_test(config()) -> _.
inspect_user_invalid_request_test(Config) ->
_ = capi_ct_helper:mock_services(
[
{inspector, fun('InspectUserShops', _) ->
{throwing, #'base_InvalidRequest'{errors = [<<"some error">>]}}
end}
],
Config
),
_ = capi_ct_helper_bouncer:mock_assert_op_ctx(<<"InspectUser">>, Config),
{error, {400, #{<<"code">> := <<"invalidRequest">>}}} =
capi_client_antifraud:inspect_user(?config(context, Config), ?INSPECT_USER_REQUEST).

-spec inspect_user_forbidden_test(config()) -> _.
inspect_user_forbidden_test(Config) ->
_ = capi_ct_helper_bouncer:mock_arbiter(capi_ct_helper_bouncer:judge_always_forbidden(), Config),
{error, {401, _}} =
capi_client_antifraud:inspect_user(?config(context, Config), ?INSPECT_USER_REQUEST).
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"use": "enc",
"kty": "oct",
"kid": "1111",
"alg": "dir",
"k": "d3JPWmpORzVqbGRrZ2s0aUdjQnJ6ZTh1OW1pdk1kR2Y"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"use": "enc",
"kty": "EC",
"kid": "kxdD0orVPGoAxWrqAMTeQ0U5MRoK47uZxWiSJdgo0t0",
"crv": "P-256",
"alg": "ECDH-ES",
"x": "nHi7TCgBwfrPuNTf49bGvJMczk6WZOI-mCKAghbrOlM",
"y": "_8kiXGOIWkfz57m8K5dmTfbYzCJVYHZZZisCfbYicr0",
"d": "i45qDiARZ5qbS_uzeT-CiKnPUe64qHitKaVdAvcN6TI"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"use": "enc",
"kty": "EC",
"kid": "kxdD0orVPGoAxWrqAMTeQ0U5MRoK47uZxWiSJdgo0t0",
"crv": "P-256",
"alg": "ECDH-ES",
"x": "nHi7TCgBwfrPuNTf49bGvJMczk6WZOI-mCKAghbrOlM",
"y": "_8kiXGOIWkfz57m8K5dmTfbYzCJVYHZZZisCfbYicr0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
-----END RSA PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg
7F/ZMtGbPFikJnnvRWvFB5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQ==
-----END PUBLIC KEY-----
14 changes: 14 additions & 0 deletions apps/capi_client/src/capi_client_antifraud.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-module(capi_client_antifraud).

-export([inspect_user/2]).

-type context() :: capi_client_lib:context().

-spec inspect_user(context(), map()) -> {ok, term()} | {error, term()}.
inspect_user(Context, Request) ->
Params = #{
body => Request
},
{Url, PreparedParams, Opts} = capi_client_lib:make_request(Context, Params),
Response = swag_client_inspector_api:inspect_user(Url, PreparedParams, Opts),
capi_client_lib:handle_response(Response).
2 changes: 2 additions & 0 deletions apps/capi_woody_client/src/capi_woody_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ apply_retry_step({wait, Timeout, Retry}, Deadline0, Error) ->
end.

-spec get_service_modname(service_name()) -> woody:service().
get_service_modname(inspector) ->
{fb_proto_fraudbusters_thrift, 'InspectorService'};
get_service_modname(invoicing) ->
{dmsl_payproc_thrift, 'Invoicing'};
get_service_modname(invoice_templating) ->
Expand Down
3 changes: 2 additions & 1 deletion config/sys.config
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@
party_management => <<"http://hellgate:8022/v1/processing/partymgmt">>,
invoicing => <<"http://hellgate:8022/v1/processing/invoicing">>,
invoice_templating => <<"http://hellgate:8022/v1/processing/invoice_templating">>,
webhook_manager => <<"http://hooker:8022/hook">>
webhook_manager => <<"http://hooker:8022/hook">>,
inspector => <<"http://fraudbusters:8022/v1/inspector">>
}},
{service_deadlines, #{
% milliseconds
Expand Down
1 change: 1 addition & 0 deletions rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
%% Libraries generated with swagger-codegen-erlang from valitydev/swag-payments
{swag_server, {git, "https://github.com/valitydev/swag-payments", {branch, "release/erlang/server/v3"}}},
{swag_client, {git, "https://github.com/valitydev/swag-payments", {branch, "release/erlang/client/v3"}}},
{fraudbusters_proto, {git, "https://github.com/valitydev/fraudbusters-proto.git", {branch, "master"}}},
%% OpenTelemetry deps
{opentelemetry_api, "1.4.0"},
{opentelemetry, "1.5.0"},
Expand Down
8 changes: 6 additions & 2 deletions rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
{git,"https://github.com/valitydev/feat.git",
{ref,"bf7dff68c822e58769da962e7f99c3e428a88551"}},
0},
{<<"fraudbusters_proto">>,
{git,"https://github.com/valitydev/fraudbusters-proto.git",
{ref,"6b26fc1463bdc9f2d0f6bebe482249c59d0b4259"}},
0},
{<<"genlib">>,
{git,"https://github.com/valitydev/genlib.git",
{ref,"d2324089afbbd9630e85fac554620f1de0b33dfe"}},
Expand Down Expand Up @@ -122,11 +126,11 @@
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.7">>},2},
{<<"swag_client">>,
{git,"https://github.com/valitydev/swag-payments",
{ref,"24d442d4e14eb8ccb7a960fcebdf9a388c2d4700"}},
{ref,"34f093017def0b15e5586197df76e6e66ae77c2a"}},
0},
{<<"swag_server">>,
{git,"https://github.com/valitydev/swag-payments",
{ref,"fddf13c86f824c75d9750cec90c9c742c7a377e7"}},
{ref,"fca7721f235be2f94ad3b6e9c675b284197231fc"}},
0},
{<<"thrift">>,
{git,"https://github.com/valitydev/thrift_erlang.git",
Expand Down
Loading