mirror of
https://github.com/valitydev/fistful-server.git
synced 2024-11-06 02:35:18 +00:00
FF-172: Screen for pans in destination using swagger_validator callback (#206)
This commit is contained in:
parent
e8a3bad7ed
commit
0d7150a39a
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -41,7 +41,7 @@ build('fistful-server', 'docker-host', finalHook) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runStage('dialyze') {
|
runStage('dialyze') {
|
||||||
withWsCache("_build/default/rebar3_21.3.8.7_plt") {
|
withWsCache("_build/default/rebar3_22.3.1_plt") {
|
||||||
sh 'make wc_dialyze'
|
sh 'make wc_dialyze'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
Makefile
2
Makefile
@ -20,7 +20,7 @@ BASE_IMAGE_NAME := service-erlang
|
|||||||
BASE_IMAGE_TAG := da0ab769f01b650b389d18fc85e7418e727cbe96
|
BASE_IMAGE_TAG := da0ab769f01b650b389d18fc85e7418e727cbe96
|
||||||
|
|
||||||
# Build image tag to be used
|
# Build image tag to be used
|
||||||
BUILD_IMAGE_TAG := 4536c31941b9c27c134e8daf0fd18848809219c9
|
BUILD_IMAGE_TAG := 442c2c274c1d8e484e5213089906a4271641d95e
|
||||||
|
|
||||||
REGISTRY := dr2.rbkmoney.com
|
REGISTRY := dr2.rbkmoney.com
|
||||||
|
|
||||||
|
@ -112,6 +112,11 @@ start_app(wapi = AppName) ->
|
|||||||
decryption_key_paths => [
|
decryption_key_paths => [
|
||||||
"/opt/wapi/config/jwk.json"
|
"/opt/wapi/config/jwk.json"
|
||||||
]
|
]
|
||||||
|
}},
|
||||||
|
{swagger_handler_opts, #{
|
||||||
|
validation_opts => #{
|
||||||
|
custom_validator => wapi_swagger_validator
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
]), #{}};
|
]), #{}};
|
||||||
|
|
||||||
|
@ -28,7 +28,8 @@ init([]) ->
|
|||||||
{LogicHandlers, LogicHandlerSpecs} = get_logic_handler_info(),
|
{LogicHandlers, LogicHandlerSpecs} = get_logic_handler_info(),
|
||||||
HealthCheck = enable_health_logging(genlib_app:env(wapi, health_check, #{})),
|
HealthCheck = enable_health_logging(genlib_app:env(wapi, health_check, #{})),
|
||||||
HealthRoutes = [{'_', [erl_health_handle:get_route(HealthCheck)]}],
|
HealthRoutes = [{'_', [erl_health_handle:get_route(HealthCheck)]}],
|
||||||
SwaggerSpec = wapi_swagger_server:child_spec(HealthRoutes, LogicHandlers),
|
SwaggerHandlerOpts = genlib_app:env(wapi, swagger_handler_opts, #{}),
|
||||||
|
SwaggerSpec = wapi_swagger_server:child_spec(HealthRoutes, LogicHandlers, SwaggerHandlerOpts),
|
||||||
{ok, {
|
{ok, {
|
||||||
{one_for_all, 0, 1},
|
{one_for_all, 0, 1},
|
||||||
[LechiffreSpec] ++
|
[LechiffreSpec] ++
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
-module(wapi_swagger_server).
|
-module(wapi_swagger_server).
|
||||||
|
|
||||||
-export([child_spec/2]).
|
-export([child_spec/3]).
|
||||||
|
|
||||||
-export_type([logic_handler/0]).
|
-export_type([logic_handler/0]).
|
||||||
-export_type([logic_handlers/0]).
|
-export_type([logic_handlers/0]).
|
||||||
@ -8,16 +8,18 @@
|
|||||||
-type logic_handler() :: swag_server_wallet:logic_handler(_).
|
-type logic_handler() :: swag_server_wallet:logic_handler(_).
|
||||||
-type logic_handlers() :: #{atom() => logic_handler()}.
|
-type logic_handlers() :: #{atom() => logic_handler()}.
|
||||||
|
|
||||||
|
-type swagger_handler_opts() :: swag_server_wallet_router:swagger_handler_opts().
|
||||||
|
|
||||||
-define(APP, wapi).
|
-define(APP, wapi).
|
||||||
-define(DEFAULT_ACCEPTORS_POOLSIZE, 100).
|
-define(DEFAULT_ACCEPTORS_POOLSIZE, 100).
|
||||||
-define(DEFAULT_IP_ADDR, "::").
|
-define(DEFAULT_IP_ADDR, "::").
|
||||||
-define(DEFAULT_PORT, 8080).
|
-define(DEFAULT_PORT, 8080).
|
||||||
|
|
||||||
-spec child_spec(cowboy_router:routes(), logic_handlers()) ->
|
-spec child_spec(cowboy_router:routes(), logic_handlers(), swagger_handler_opts()) ->
|
||||||
supervisor:child_spec().
|
supervisor:child_spec().
|
||||||
child_spec(HealthRoutes, LogicHandlers) ->
|
child_spec(HealthRoutes, LogicHandlers, SwaggerHandlerOpts) ->
|
||||||
{Transport, TransportOpts} = get_socket_transport(),
|
{Transport, TransportOpts} = get_socket_transport(),
|
||||||
CowboyOpts = get_cowboy_config(HealthRoutes, LogicHandlers),
|
CowboyOpts = get_cowboy_config(HealthRoutes, LogicHandlers, SwaggerHandlerOpts),
|
||||||
ranch:child_spec(?MODULE, Transport, TransportOpts, cowboy_clear, CowboyOpts).
|
ranch:child_spec(?MODULE, Transport, TransportOpts, cowboy_clear, CowboyOpts).
|
||||||
|
|
||||||
get_socket_transport() ->
|
get_socket_transport() ->
|
||||||
@ -26,11 +28,14 @@ get_socket_transport() ->
|
|||||||
AcceptorsPool = genlib_app:env(?APP, acceptors_poolsize, ?DEFAULT_ACCEPTORS_POOLSIZE),
|
AcceptorsPool = genlib_app:env(?APP, acceptors_poolsize, ?DEFAULT_ACCEPTORS_POOLSIZE),
|
||||||
{ranch_tcp, #{socket_opts => [{ip, IP}, {port, Port}], num_acceptors => AcceptorsPool}}.
|
{ranch_tcp, #{socket_opts => [{ip, IP}, {port, Port}], num_acceptors => AcceptorsPool}}.
|
||||||
|
|
||||||
get_cowboy_config(HealthRoutes, LogicHandlers) ->
|
get_cowboy_config(HealthRoutes, LogicHandlers, SwaggerHandlerOpts) ->
|
||||||
Dispatch =
|
Dispatch =
|
||||||
cowboy_router:compile(squash_routes(
|
cowboy_router:compile(squash_routes(
|
||||||
HealthRoutes ++
|
HealthRoutes ++
|
||||||
swag_server_wallet_router:get_paths(maps:get(wallet, LogicHandlers))
|
swag_server_wallet_router:get_paths(
|
||||||
|
maps:get(wallet, LogicHandlers),
|
||||||
|
SwaggerHandlerOpts
|
||||||
|
)
|
||||||
)),
|
)),
|
||||||
CowboyOpts = #{
|
CowboyOpts = #{
|
||||||
env => #{
|
env => #{
|
||||||
|
80
apps/wapi/src/wapi_swagger_validator.erl
Normal file
80
apps/wapi/src/wapi_swagger_validator.erl
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
-module(wapi_swagger_validator).
|
||||||
|
|
||||||
|
-type param_rule() :: swag_server_wallet_param_validator:param_rule().
|
||||||
|
-type schema_rule() :: swag_server_wallet_schema_validator:schema_rule().
|
||||||
|
-type value() :: swag_server_wallet:value().
|
||||||
|
-type param_context() :: swag_server_wallet_param_validator:context().
|
||||||
|
-type schema_context() :: swag_server_wallet_schema_validator:context().
|
||||||
|
|
||||||
|
-type validate_param_result() ::
|
||||||
|
ok | {ok, term()} | pass | error | {error, Error :: term()}.
|
||||||
|
|
||||||
|
-type validate_schema_result() ::
|
||||||
|
jesse_state:state() | pass | no_return().
|
||||||
|
|
||||||
|
-behaviour(swag_server_wallet_custom_validator).
|
||||||
|
|
||||||
|
-export([validate_param/3]).
|
||||||
|
-export([validate_schema/4]).
|
||||||
|
|
||||||
|
-spec validate_param(param_rule(), value(), param_context()) ->
|
||||||
|
validate_param_result().
|
||||||
|
validate_param(_Rule, _Value, _Meta) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
-spec validate_schema(schema_rule(), value(), schema_context(), jesse_state:state()) ->
|
||||||
|
validate_schema_result().
|
||||||
|
|
||||||
|
validate_schema(
|
||||||
|
{<<"type">>, <<"string">>},
|
||||||
|
Value,
|
||||||
|
#{
|
||||||
|
operation_id := 'CreateDestination',
|
||||||
|
definition_name := 'Destination',
|
||||||
|
% current_path := [<<"name">>], % check all fields
|
||||||
|
msg_type := request
|
||||||
|
},
|
||||||
|
JesseState
|
||||||
|
) when is_binary(Value) ->
|
||||||
|
case check_destination_name(Value) of
|
||||||
|
ok ->
|
||||||
|
pass; % pass back to the built-in validator
|
||||||
|
error ->
|
||||||
|
jesse_error:handle_data_invalid(wrong_format, Value, JesseState)
|
||||||
|
end;
|
||||||
|
validate_schema(_Rule, _Value, _Meta, _JesseState) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
check_destination_name(Name) ->
|
||||||
|
case re:run(Name, <<"\\d{12,19}">>, [{capture, all, binary}, global]) of
|
||||||
|
nomatch -> ok;
|
||||||
|
{match, Captured} -> check_luhn(Captured)
|
||||||
|
end.
|
||||||
|
|
||||||
|
check_luhn([]) ->
|
||||||
|
ok;
|
||||||
|
check_luhn([Captured | Rest]) ->
|
||||||
|
case lists:any(fun do_check_luhn/1, Captured) of
|
||||||
|
true -> error;
|
||||||
|
false -> check_luhn(Rest)
|
||||||
|
end.
|
||||||
|
|
||||||
|
do_check_luhn(String) ->
|
||||||
|
do_check_luhn(String, 0).
|
||||||
|
|
||||||
|
do_check_luhn(<<CheckSum>>, Sum) ->
|
||||||
|
case Sum * 9 rem 10 of
|
||||||
|
M when M =:= CheckSum - $0 ->
|
||||||
|
true;
|
||||||
|
_M ->
|
||||||
|
false
|
||||||
|
end;
|
||||||
|
do_check_luhn(<<N, Rest/binary>>, Sum) when byte_size(Rest) rem 2 =:= 1 ->
|
||||||
|
case (N - $0) * 2 of
|
||||||
|
M when M >= 10 ->
|
||||||
|
do_check_luhn(Rest, Sum + M div 10 + M rem 10);
|
||||||
|
M ->
|
||||||
|
do_check_luhn(Rest, Sum + M)
|
||||||
|
end;
|
||||||
|
do_check_luhn(<<N, Rest/binary>>, Sum) ->
|
||||||
|
do_check_luhn(Rest, Sum + N - $0).
|
@ -357,7 +357,6 @@ create_destination(Params = #{<<"identity">> := IdenityId}, Context) ->
|
|||||||
CreateFun = fun(ID, EntityCtx) ->
|
CreateFun = fun(ID, EntityCtx) ->
|
||||||
_ = check_resource(identity, IdenityId, Context),
|
_ = check_resource(identity, IdenityId, Context),
|
||||||
DestinationParams = from_swag(destination_params, Params),
|
DestinationParams = from_swag(destination_params, Params),
|
||||||
_ = check_destination_params(DestinationParams),
|
|
||||||
Resource = unwrap(construct_resource(maps:get(resource, DestinationParams))),
|
Resource = unwrap(construct_resource(maps:get(resource, DestinationParams))),
|
||||||
ff_destination:create(
|
ff_destination:create(
|
||||||
DestinationParams#{id => ID, resource => Resource},
|
DestinationParams#{id => ID, resource => Resource},
|
||||||
@ -841,13 +840,6 @@ when Type =:= <<"CryptoWalletDestinationResource">> ->
|
|||||||
currency => from_swag(crypto_wallet_currency, Resource)
|
currency => from_swag(crypto_wallet_currency, Resource)
|
||||||
})}}}.
|
})}}}.
|
||||||
|
|
||||||
%%@TODO delete as soon as a more permanent solution is in place
|
|
||||||
check_destination_params(#{name := Name}) ->
|
|
||||||
case re:run(Name, <<"\\d{12,19}">>, [{capture, none}]) of
|
|
||||||
nomatch -> ok;
|
|
||||||
match -> throw({illegal_pattern, name})
|
|
||||||
end.
|
|
||||||
|
|
||||||
encode_resource_bank_card(BankCard, AuthData) ->
|
encode_resource_bank_card(BankCard, AuthData) ->
|
||||||
EncodedBankCard = encode_bank_card(BankCard),
|
EncodedBankCard = encode_bank_card(BankCard),
|
||||||
{bank_card, EncodedBankCard#{auth_data => {session, #{session_id => AuthData}}}}.
|
{bank_card, EncodedBankCard#{auth_data => {session, #{session_id => AuthData}}}}.
|
||||||
|
@ -272,12 +272,6 @@ process_request('CreateDestination', #{'Destination' := Params}, Context, Opts)
|
|||||||
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
wapi_handler_utils:logic_error(external_id_conflict, {ID, ExternalID});
|
||||||
{error, invalid} ->
|
{error, invalid} ->
|
||||||
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Invalid currency">>));
|
wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Invalid currency">>));
|
||||||
{error, {illegal_pattern, Name}} ->
|
|
||||||
wapi_handler_utils:reply_error(400, #{
|
|
||||||
<<"errorType">> => <<"SchemaViolated">>,
|
|
||||||
<<"name">> => Name,
|
|
||||||
<<"description">> => <<"Illegal pattern found in parameter">>
|
|
||||||
});
|
|
||||||
{error, {invalid_resource_token, Type}} ->
|
{error, {invalid_resource_token, Type}} ->
|
||||||
wapi_handler_utils:reply_error(400, #{
|
wapi_handler_utils:reply_error(400, #{
|
||||||
<<"errorType">> => <<"InvalidResourceToken">>,
|
<<"errorType">> => <<"InvalidResourceToken">>,
|
||||||
|
@ -173,11 +173,25 @@ create_destination_failed_test(C) ->
|
|||||||
{error, {400, #{<<"errorType">> := <<"InvalidResourceToken">>}}}
|
{error, {400, #{<<"errorType">> := <<"InvalidResourceToken">>}}}
|
||||||
= create_destination(IdentityID, Resource0, C),
|
= create_destination(IdentityID, Resource0, C),
|
||||||
%%
|
%%
|
||||||
DestinationName = <<"4242424242424242">>,
|
DestinationName0 = <<"abc4242424242424242">>,
|
||||||
CardToken = store_bank_card(C),
|
CardToken = store_bank_card(C),
|
||||||
Resource1 = make_bank_card_resource(CardToken),
|
Resource1 = make_bank_card_resource(CardToken),
|
||||||
{error, {400, #{<<"errorType">> := <<"SchemaViolated">>}}}
|
{error, {response_validation_failed, _,
|
||||||
= create_destination(DestinationName, IdentityID, Resource1, C).
|
#{
|
||||||
|
<<"errorType">> := <<"schema_violated">>,
|
||||||
|
<<"name">> := <<"Destination">>
|
||||||
|
}
|
||||||
|
}} = create_destination(DestinationName0, IdentityID, Resource1, C),
|
||||||
|
DestinationName1 = <<"abc1231241241241244">>,
|
||||||
|
IdentityID1 = <<"4242424242424242">>,
|
||||||
|
{error, {response_validation_failed, _,
|
||||||
|
#{
|
||||||
|
<<"errorType">> := <<"schema_violated">>,
|
||||||
|
<<"name">> := <<"Destination">>
|
||||||
|
}
|
||||||
|
}} = create_destination(DestinationName1, IdentityID1, Resource1, C),
|
||||||
|
DestinationName2 = <<"1231241241241244">>,
|
||||||
|
{ok, _} = create_destination(DestinationName2, IdentityID, Resource1, C).
|
||||||
|
|
||||||
-spec withdrawal_to_bank_card_test(config()) -> test_return().
|
-spec withdrawal_to_bank_card_test(config()) -> test_return().
|
||||||
|
|
||||||
|
@ -45,6 +45,10 @@
|
|||||||
{git,"git@github.com:rbkmoney/dmt_core.git",
|
{git,"git@github.com:rbkmoney/dmt_core.git",
|
||||||
{ref,"8ac78cb1c94abdcdda6675dd7519893626567573"}},
|
{ref,"8ac78cb1c94abdcdda6675dd7519893626567573"}},
|
||||||
1},
|
1},
|
||||||
|
{<<"email_validator">>,
|
||||||
|
{git,"https://github.com/rbkmoney/email_validator.git",
|
||||||
|
{ref,"be90c6ebd34d29fa9390136469b99d8a68ad4996"}},
|
||||||
|
0},
|
||||||
{<<"erl_health">>,
|
{<<"erl_health">>,
|
||||||
{git,"https://github.com/rbkmoney/erlang-health.git",
|
{git,"https://github.com/rbkmoney/erlang-health.git",
|
||||||
{ref,"c190cb8de0359b933a27cd20ddc74180c0e5f5c4"}},
|
{ref,"c190cb8de0359b933a27cd20ddc74180c0e5f5c4"}},
|
||||||
|
Loading…
Reference in New Issue
Block a user