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

View File

@ -1,6 +1,7 @@
-module(capi_crypto).
-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_data() :: #{
@ -8,8 +9,7 @@
valid_until := deadline(),
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_token() :: dmsl_payment_tool_token_thrift:'PaymentToolToken'().
-type payment_tool_token_payload() :: dmsl_payment_tool_token_thrift:'PaymentToolTokenPayload'().
@ -22,11 +22,13 @@
-export([encode_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().
encode_token(TokenData) ->
PaymentToolToken = encode_payment_tool_token(TokenData),
ThriftType = {struct, struct, {dmsl_payment_tool_token_thrift, 'PaymentToolToken'}},
{ok, EncodedToken} = lechiffre:encode(ThriftType, PaymentToolToken),
{ok, EncodedToken} = lechiffre:encode(?THRIFT_TYPE, PaymentToolToken),
TokenVersion = token_version(),
<<TokenVersion/binary, ".", EncodedToken/binary>>.
@ -47,8 +49,7 @@ token_version() ->
<<"v2">>.
decrypt_token(EncryptedPaymentToolToken) ->
ThriftType = {struct, struct, {dmsl_payment_tool_token_thrift, 'PaymentToolToken'}},
case lechiffre:decode(ThriftType, EncryptedPaymentToolToken) of
case lechiffre:decode(?THRIFT_TYPE, EncryptedPaymentToolToken) of
{ok, PaymentToolToken} ->
Payload = PaymentToolToken#ptt_PaymentToolToken.payload,
ValidUntil = PaymentToolToken#ptt_PaymentToolToken.valid_until,
@ -73,11 +74,15 @@ encode_payment_tool_token(TokenData) ->
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) ->
undefined;
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.
encode_deadline(undefined) ->
@ -107,14 +112,16 @@ encode_payment_tool_token_payload({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) ->
undefined;
decode_bouncer_data(Content) ->
try
erlang:binary_to_term(base64:decode(Content))
catch
error:Error ->
Codec = thrift_strict_binary_codec:new(Content),
case thrift_strict_binary_codec:read(Codec, ?BOUNCER_THRIFT_TYPE) of
{ok, BouncerData, Codec1} ->
_ = thrift_strict_binary_codec:close(Codec1),
BouncerData;
Error ->
erlang:error({malformed_token, Error}, [Content])
end.

View File

@ -115,28 +115,33 @@ prepare('CreateCustomerAccessToken' = OperationID, Req, Context) ->
{ok, #{authorize => Authorize, process => Process}};
prepare('CreateBinding' = OperationID, Req, Context) ->
CustomerID = maps:get(customerID, Req),
CustomerBindingParams = maps:get('CustomerBindingParams', Req),
PaymentToken = decode_payment_token(CustomerBindingParams),
Authorize = fun() ->
Prototypes = [
{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)}
end,
Process = fun() ->
Result =
try
CustomerBindingParams = maps:get('CustomerBindingParams', Req),
#{payment_tool := PaymentTool} = PaymentToken,
{CustomerBindingID, RecPaymentToolID} = generate_binding_ids(
OperationID,
CustomerBindingParams,
PaymentTool,
Context
),
EncodedCustomerBindingParams = encode_customer_binding_params(
CustomerBindingID,
RecPaymentToolID,
CustomerBindingParams
CustomerBindingParams,
PaymentTool
),
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) ->
EventRange = #payproc_EventRange{},
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) ->
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),
UserID = capi_handler_utils:get_user_id(Context),
PaymentResource = maps:get(<<"paymentResource">>, CustomerBindingParams),
PaymentToolToken = maps:get(<<"paymentToolToken">>, PaymentResource),
PaymentTool = capi_handler_decoder_party:decode_payment_tool(encode_payment_tool_token(PaymentToolToken)),
CustomerBindingParamsEncrypted =
maps:put(
<<"paymentResource">>,
maps:put(
<<"paymentTool">>,
PaymentTool,
maps:remove(<<"paymentToolToken">>, PaymentResource)
),
CustomerBindingParams
),
PaymentResourceWoToken = maps:remove(<<"paymentToolToken">>, PaymentResource),
CustomerBindingParamsEncrypted = CustomerBindingParams#{
<<"paymentResource">> => PaymentResourceWoToken#{
<<"paymentTool">> => capi_handler_decoder_party:decode_payment_tool(PaymentTool)
}
},
Identity = capi_bender:make_identity(
{schema, capi_feature_schemas:customer_binding(), CustomerBindingParamsEncrypted}
@ -354,11 +362,9 @@ generate_binding_ids(OperationID, CustomerBindingParams, Context = #{woody_conte
encode_customer_binding_params(
CustomerBindingID,
RecPaymentToolID,
#{<<"paymentResource">> := PaymentResource}
#{<<"paymentResource">> := PaymentResource},
PaymentTool
) ->
PaymentToolToken = maps:get(<<"paymentToolToken">>, PaymentResource),
PaymentTool = encode_payment_tool_token(PaymentToolToken),
{ClientInfo, PaymentSession} =
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
{ok, TokenData} ->
#{payment_tool := PaymentTool, valid_until := ValidUntil} = TokenData,
% TODO #ED-162 Проверка времени жизни будет в bouncer, тут её следует убрать вместе с тестами
{ok, #{valid_until := ValidUntil} = TokenData} ->
case capi_utils:deadline_is_reached(ValidUntil) of
true ->
logger:warning("Payment tool token expired: ~p", [capi_utils:deadline_to_binary(ValidUntil)]),
capi_handler:respond(logic_error(invalidPaymentToolToken));
_ ->
PaymentTool
TokenData
end;
unrecognized ->
encode_legacy_payment_tool_token(Token);
% TODO-162: удалить устаревшие токены, заменить их на актуальные и поправить тесты
decode_legacy_payment_token(Token);
{error, {decryption_failed, Error}} ->
logger:warning("Payment tool token decryption failed: ~p", [Error]),
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
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
error:badarg ->
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(PaymentToken) ->
prepare_payment_tool_prototype(#{bouncer_data := BouncerData}) ->
#{
expiration => prepare_payment_tool_expire(PaymentToken),
scope => prepare_payment_tool_scope(PaymentToken)
payment_tool_context => BouncerData
}.
prepare_payment_tool_expire(#{valid_until := ValidUntil}) ->
capi_utils:deadline_to_binary(ValidUntil).
prepare_payment_tool_scope(#{bouncer_data := BouncerData}) ->
BouncerData.
%%
validate_allocation(Allocation) ->
case capi_allocation:validate(Allocation) of

View File

@ -112,7 +112,9 @@
]}
]},
{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]}
]}
]}
]}.