+bouncer_data as BouncerContext

This commit is contained in:
dinama 2021-10-29 00:05:17 +03:00
parent c568a689d6
commit 82cd19cadf
No known key found for this signature in database
GPG Key ID: DC1F111BFEC80053
5 changed files with 70 additions and 64 deletions

View File

@ -1,7 +1,6 @@
-module(capi_bouncer_context). -module(capi_bouncer_context).
-include_lib("bouncer_proto/include/bouncer_context_v1_thrift.hrl"). -include_lib("bouncer_proto/include/bouncer_context_v1_thrift.hrl").
-include_lib("damsel/include/dmsl_payment_processing_thrift.hrl"). -include_lib("damsel/include/dmsl_payment_processing_thrift.hrl").
-include_lib("damsel/include/dmsl_webhooker_thrift.hrl"). -include_lib("damsel/include/dmsl_webhooker_thrift.hrl").
-include_lib("reporter_proto/include/reporter_reports_thrift.hrl"). -include_lib("reporter_proto/include/reporter_reports_thrift.hrl").
@ -9,11 +8,12 @@
-type fragment() :: bouncer_client:context_fragment(). -type fragment() :: bouncer_client:context_fragment().
-type acc() :: bouncer_context_helpers:context_fragment(). -type acc() :: bouncer_context_helpers:context_fragment().
-type payment_tool_context() :: bouncer_context_v1_thrift:'ContextPaymentTool'().
-type fragments() :: {acc(), _ExternalFragments :: #{_ID => fragment()}}. -type fragments() :: {acc(), _ExternalFragments :: #{_ID => fragment()}}.
-export_type([fragment/0]). -export_type([fragment/0]).
-export_type([acc/0]). -export_type([acc/0]).
-export_type([payment_tool_context/0]).
-export_type([fragments/0]). -export_type([fragments/0]).
-type prototypes() :: [ -type prototypes() :: [
@ -44,9 +44,7 @@
}. }.
-type prototype_payment_tool() :: #{ -type prototype_payment_tool() :: #{
invoice => entity_id(), payment_tool_context => payment_tool_context()
customer => entity_id(),
expiration => timestamp()
}. }.
-type prototype_payproc() :: #{ -type prototype_payproc() :: #{
@ -87,7 +85,6 @@
-type payout() :: payouts_payout_manager_thrift:'Payout'(). -type payout() :: payouts_payout_manager_thrift:'Payout'().
-type entity_id() :: binary(). -type entity_id() :: binary().
-type timestamp() :: binary().
-export_type([prototypes/0]). -export_type([prototypes/0]).
-export_type([prototype_operation/0]). -export_type([prototype_operation/0]).
@ -141,13 +138,7 @@ build(operation, Params = #{id := OperationID}, Acc, _WoodyCtx) ->
}; };
build(payment_tool, Params = #{}, Acc, _WoodyCtx) -> build(payment_tool, Params = #{}, Acc, _WoodyCtx) ->
Acc#bctx_v1_ContextFragment{ Acc#bctx_v1_ContextFragment{
payment_tool = #bctx_v1_ContextPaymentTool{ payment_tool = maps:get(payment_tool_context, Params, undefined)
scope = #bctx_v1_AuthScope{
invoice = maybe_entity(invoice, Params),
customer = maybe_entity(customer, Params)
},
expiration = maps:get(expiration, Params, undefined)
}
}; };
build(payproc, Params = #{}, Acc, WoodyCtx) -> build(payproc, Params = #{}, Acc, WoodyCtx) ->
Acc#bctx_v1_ContextFragment{ Acc#bctx_v1_ContextFragment{

View File

@ -1,6 +1,7 @@
-module(capi_crypto). -module(capi_crypto).
-include_lib("damsel/include/dmsl_payment_tool_token_thrift.hrl"). -include_lib("damsel/include/dmsl_payment_tool_token_thrift.hrl").
-include_lib("bouncer_proto/include/bouncer_context_v1_thrift.hrl").
-type token() :: binary(). -type token() :: binary().
-type token_data() :: #{ -type token_data() :: #{
@ -8,8 +9,7 @@
valid_until := deadline(), valid_until := deadline(),
bouncer_data => bouncer_data() bouncer_data => bouncer_data()
}. }.
-type bouncer_data() :: term(). -type bouncer_data() :: capi_bouncer_context:payment_tool_context().
-type payment_tool() :: dmsl_domain_thrift:'PaymentTool'(). -type payment_tool() :: dmsl_domain_thrift:'PaymentTool'().
-type payment_tool_token() :: dmsl_payment_tool_token_thrift:'PaymentToolToken'(). -type payment_tool_token() :: dmsl_payment_tool_token_thrift:'PaymentToolToken'().
-type payment_tool_token_payload() :: dmsl_payment_tool_token_thrift:'PaymentToolTokenPayload'(). -type payment_tool_token_payload() :: dmsl_payment_tool_token_thrift:'PaymentToolTokenPayload'().
@ -22,11 +22,13 @@
-export([encode_token/1]). -export([encode_token/1]).
-export([decode_token/1]). -export([decode_token/1]).
-define(THRIFT_TYPE, {struct, struct, {dmsl_payment_tool_token_thrift, 'PaymentToolToken'}}).
-define(BOUNCER_THRIFT_TYPE, {struct, struct, {bouncer_context_v1_thrift, 'ContextPaymentTool'}}).
-spec encode_token(token_data()) -> token(). -spec encode_token(token_data()) -> token().
encode_token(TokenData) -> encode_token(TokenData) ->
PaymentToolToken = encode_payment_tool_token(TokenData), PaymentToolToken = encode_payment_tool_token(TokenData),
ThriftType = {struct, struct, {dmsl_payment_tool_token_thrift, 'PaymentToolToken'}}, {ok, EncodedToken} = lechiffre:encode(?THRIFT_TYPE, PaymentToolToken),
{ok, EncodedToken} = lechiffre:encode(ThriftType, PaymentToolToken),
TokenVersion = token_version(), TokenVersion = token_version(),
<<TokenVersion/binary, ".", EncodedToken/binary>>. <<TokenVersion/binary, ".", EncodedToken/binary>>.
@ -47,8 +49,7 @@ token_version() ->
<<"v2">>. <<"v2">>.
decrypt_token(EncryptedPaymentToolToken) -> decrypt_token(EncryptedPaymentToolToken) ->
ThriftType = {struct, struct, {dmsl_payment_tool_token_thrift, 'PaymentToolToken'}}, case lechiffre:decode(?THRIFT_TYPE, EncryptedPaymentToolToken) of
case lechiffre:decode(ThriftType, EncryptedPaymentToolToken) of
{ok, PaymentToolToken} -> {ok, PaymentToolToken} ->
Payload = PaymentToolToken#ptt_PaymentToolToken.payload, Payload = PaymentToolToken#ptt_PaymentToolToken.payload,
ValidUntil = PaymentToolToken#ptt_PaymentToolToken.valid_until, ValidUntil = PaymentToolToken#ptt_PaymentToolToken.valid_until,
@ -73,11 +74,15 @@ encode_payment_tool_token(TokenData) ->
bouncer_data = encode_bouncer_data(BouncerContext) bouncer_data = encode_bouncer_data(BouncerContext)
}. }.
-spec encode_bouncer_data(bouncer_data()) -> binary() | undefined. -spec encode_bouncer_data(bouncer_data() | undefined) -> binary() | undefined.
encode_bouncer_data(undefined) -> encode_bouncer_data(undefined) ->
undefined; undefined;
encode_bouncer_data(BouncerData) -> encode_bouncer_data(BouncerData) ->
base64:encode(erlang:term_to_binary(BouncerData)). Codec = thrift_strict_binary_codec:new(),
case thrift_strict_binary_codec:write(Codec, ?BOUNCER_THRIFT_TYPE, BouncerData) of
{ok, Codec1} ->
thrift_strict_binary_codec:close(Codec1)
end.
-spec encode_deadline(deadline()) -> binary() | undefined. -spec encode_deadline(deadline()) -> binary() | undefined.
encode_deadline(undefined) -> encode_deadline(undefined) ->
@ -107,14 +112,16 @@ encode_payment_tool_token_payload({mobile_commerce, MobileCommerce}) ->
mobile_commerce = MobileCommerce mobile_commerce = MobileCommerce
}}. }}.
-spec decode_bouncer_data(binary()) -> bouncer_data() | undefined | no_return(). -spec decode_bouncer_data(binary() | undefined) -> bouncer_data() | undefined | no_return().
decode_bouncer_data(undefined) -> decode_bouncer_data(undefined) ->
undefined; undefined;
decode_bouncer_data(Content) -> decode_bouncer_data(Content) ->
try Codec = thrift_strict_binary_codec:new(Content),
erlang:binary_to_term(base64:decode(Content)) case thrift_strict_binary_codec:read(Codec, ?BOUNCER_THRIFT_TYPE) of
catch {ok, BouncerData, Codec1} ->
error:Error -> _ = thrift_strict_binary_codec:close(Codec1),
BouncerData;
Error ->
erlang:error({malformed_token, Error}, [Content]) erlang:error({malformed_token, Error}, [Content])
end. end.

View File

@ -115,28 +115,33 @@ prepare('CreateCustomerAccessToken' = OperationID, Req, Context) ->
{ok, #{authorize => Authorize, process => Process}}; {ok, #{authorize => Authorize, process => Process}};
prepare('CreateBinding' = OperationID, Req, Context) -> prepare('CreateBinding' = OperationID, Req, Context) ->
CustomerID = maps:get(customerID, Req), CustomerID = maps:get(customerID, Req),
CustomerBindingParams = maps:get('CustomerBindingParams', Req),
PaymentToken = decode_payment_token(CustomerBindingParams),
Authorize = fun() -> Authorize = fun() ->
Prototypes = [ Prototypes = [
{operation, #{id => OperationID, customer => CustomerID}}, {operation, #{id => OperationID, customer => CustomerID}},
{payproc, #{customer => CustomerID}} {payproc, #{customer => CustomerID}},
{payment_tool, prepare_payment_tool_prototype(PaymentToken)}
], ],
{ok, capi_auth:authorize_operation(Prototypes, Context)} {ok, capi_auth:authorize_operation(Prototypes, Context)}
end, end,
Process = fun() -> Process = fun() ->
Result = Result =
try try
CustomerBindingParams = maps:get('CustomerBindingParams', Req), #{payment_tool := PaymentTool} = PaymentToken,
{CustomerBindingID, RecPaymentToolID} = generate_binding_ids( {CustomerBindingID, RecPaymentToolID} = generate_binding_ids(
OperationID, OperationID,
CustomerBindingParams, CustomerBindingParams,
PaymentTool,
Context Context
), ),
EncodedCustomerBindingParams = encode_customer_binding_params( EncodedCustomerBindingParams = encode_customer_binding_params(
CustomerBindingID, CustomerBindingID,
RecPaymentToolID, RecPaymentToolID,
CustomerBindingParams CustomerBindingParams,
PaymentTool
), ),
Call = {customer_management, 'StartBinding', {CustomerID, EncodedCustomerBindingParams}}, Call = {customer_management, 'StartBinding', {CustomerID, EncodedCustomerBindingParams}},
@ -285,6 +290,15 @@ prepare(_OperationID, _Req, _Context) ->
%% %%
prepare_payment_tool_prototype(#{bouncer_data := BouncerData}) ->
#{
payment_tool_context => BouncerData
};
prepare_payment_tool_prototype(_Other) ->
#{}.
%%
get_customer_by_id(CustomerID, Context) -> get_customer_by_id(CustomerID, Context) ->
EventRange = #payproc_EventRange{}, EventRange = #payproc_EventRange{},
capi_handler_utils:service_call({customer_management, 'Get', {CustomerID, EventRange}}, Context). capi_handler_utils:service_call({customer_management, 'Get', {CustomerID, EventRange}}, Context).
@ -316,23 +330,17 @@ encode_customer_params(CustomerID, PartyID, Params) ->
encode_customer_metadata(Meta) -> encode_customer_metadata(Meta) ->
capi_json_marshalling:marshal(Meta). capi_json_marshalling:marshal(Meta).
generate_binding_ids(OperationID, CustomerBindingParams, Context = #{woody_context := WoodyContext}) -> generate_binding_ids(OperationID, CustomerBindingParams, PaymentTool, Context = #{woody_context := WoodyContext}) ->
ExternalID = maps:get(<<"externalID">>, CustomerBindingParams, undefined), ExternalID = maps:get(<<"externalID">>, CustomerBindingParams, undefined),
UserID = capi_handler_utils:get_user_id(Context), UserID = capi_handler_utils:get_user_id(Context),
PaymentResource = maps:get(<<"paymentResource">>, CustomerBindingParams), PaymentResource = maps:get(<<"paymentResource">>, CustomerBindingParams),
PaymentToolToken = maps:get(<<"paymentToolToken">>, PaymentResource), PaymentResourceWoToken = maps:remove(<<"paymentToolToken">>, PaymentResource),
PaymentTool = capi_handler_decoder_party:decode_payment_tool(encode_payment_tool_token(PaymentToolToken)), CustomerBindingParamsEncrypted = CustomerBindingParams#{
CustomerBindingParamsEncrypted = <<"paymentResource">> => PaymentResourceWoToken#{
maps:put( <<"paymentTool">> => capi_handler_decoder_party:decode_payment_tool(PaymentTool)
<<"paymentResource">>, }
maps:put( },
<<"paymentTool">>,
PaymentTool,
maps:remove(<<"paymentToolToken">>, PaymentResource)
),
CustomerBindingParams
),
Identity = capi_bender:make_identity( Identity = capi_bender:make_identity(
{schema, capi_feature_schemas:customer_binding(), CustomerBindingParamsEncrypted} {schema, capi_feature_schemas:customer_binding(), CustomerBindingParamsEncrypted}
@ -354,11 +362,9 @@ generate_binding_ids(OperationID, CustomerBindingParams, Context = #{woody_conte
encode_customer_binding_params( encode_customer_binding_params(
CustomerBindingID, CustomerBindingID,
RecPaymentToolID, RecPaymentToolID,
#{<<"paymentResource">> := PaymentResource} #{<<"paymentResource">> := PaymentResource},
PaymentTool
) -> ) ->
PaymentToolToken = maps:get(<<"paymentToolToken">>, PaymentResource),
PaymentTool = encode_payment_tool_token(PaymentToolToken),
{ClientInfo, PaymentSession} = {ClientInfo, PaymentSession} =
capi_handler_utils:unwrap_payment_session(maps:get(<<"paymentSession">>, PaymentResource)), capi_handler_utils:unwrap_payment_session(maps:get(<<"paymentSession">>, PaymentResource)),
@ -372,27 +378,34 @@ encode_customer_binding_params(
} }
}. }.
encode_payment_tool_token(Token) -> decode_payment_token(#{<<"paymentResource">> := PaymentResource}) ->
decode_payment_token(PaymentResource);
decode_payment_token(#{<<"paymentToolToken">> := Token}) ->
case capi_crypto:decode_token(Token) of case capi_crypto:decode_token(Token) of
{ok, TokenData} -> % TODO #ED-162 Проверка времени жизни будет в bouncer, тут её следует убрать вместе с тестами
#{payment_tool := PaymentTool, valid_until := ValidUntil} = TokenData, {ok, #{valid_until := ValidUntil} = TokenData} ->
case capi_utils:deadline_is_reached(ValidUntil) of case capi_utils:deadline_is_reached(ValidUntil) of
true -> true ->
logger:warning("Payment tool token expired: ~p", [capi_utils:deadline_to_binary(ValidUntil)]), logger:warning("Payment tool token expired: ~p", [capi_utils:deadline_to_binary(ValidUntil)]),
capi_handler:respond(logic_error(invalidPaymentToolToken)); capi_handler:respond(logic_error(invalidPaymentToolToken));
_ -> _ ->
PaymentTool TokenData
end; end;
unrecognized -> unrecognized ->
encode_legacy_payment_tool_token(Token); % TODO-162: удалить устаревшие токены, заменить их на актуальные и поправить тесты
decode_legacy_payment_token(Token);
{error, {decryption_failed, Error}} -> {error, {decryption_failed, Error}} ->
logger:warning("Payment tool token decryption failed: ~p", [Error]), logger:warning("Payment tool token decryption failed: ~p", [Error]),
capi_handler:respond(logic_error(invalidPaymentToolToken)) capi_handler:respond(logic_error(invalidPaymentToolToken))
end. end;
decode_payment_token(_Other) ->
undefined.
encode_legacy_payment_tool_token(Token) -> decode_legacy_payment_token(Token) ->
try try
capi_handler_encoder:encode_payment_tool(capi_utils:base64url_to_map(Token)) #{
payment_tool => capi_handler_encoder:encode_payment_tool(capi_utils:base64url_to_map(Token))
}
catch catch
error:badarg -> error:badarg ->
capi_handler:respond(logic_error(invalidPaymentToolToken)) capi_handler:respond(logic_error(invalidPaymentToolToken))

View File

@ -513,18 +513,11 @@ prepare(_OperationID, _Req, _Context) ->
prepare_payment_tool_prototype(undefined) -> prepare_payment_tool_prototype(undefined) ->
#{}; #{};
prepare_payment_tool_prototype(PaymentToken) -> prepare_payment_tool_prototype(#{bouncer_data := BouncerData}) ->
#{ #{
expiration => prepare_payment_tool_expire(PaymentToken), payment_tool_context => BouncerData
scope => prepare_payment_tool_scope(PaymentToken)
}. }.
prepare_payment_tool_expire(#{valid_until := ValidUntil}) ->
capi_utils:deadline_to_binary(ValidUntil).
prepare_payment_tool_scope(#{bouncer_data := BouncerData}) ->
BouncerData.
%% %%
validate_allocation(Allocation) -> validate_allocation(Allocation) ->
case capi_allocation:validate(Allocation) of case capi_allocation:validate(Allocation) of

View File

@ -112,7 +112,9 @@
]} ]}
]}, ]},
{test, [ {test, [
{dialyzer, [{plt_extra_apps, [eunit, common_test, runtime_tools, bender_proto, payout_manager_proto]}]} {dialyzer, [
{plt_extra_apps, [eunit, common_test, runtime_tools, bender_proto, payout_manager_proto]}
]}
]} ]}
]}. ]}.