TD-226: Remove UserInfo-based auth (#13)

This commit is contained in:
Alexey S 2022-04-05 14:51:12 +03:00 committed by GitHub
parent 0d6ab9407d
commit 76058e0aa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 216 additions and 326 deletions

View File

@ -2,10 +2,10 @@ ARG OTP_VERSION
# Build the release
FROM docker.io/library/erlang:${OTP_VERSION} AS builder
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Install thrift compiler
ARG THRIFT_VERSION
ARG TARGETARCH
RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${TARGETARCH}.tar.gz" \
| tar -xvz -C /usr/local/bin/
@ -16,8 +16,8 @@ COPY . /build/
# Build the release
WORKDIR /build
RUN rebar3 compile
RUN rebar3 as prod release
RUN rebar3 compile && \
rebar3 as prod release
# Make a runner image
FROM docker.io/library/erlang:${OTP_VERSION}-slim
@ -28,15 +28,15 @@ ARG SERVICE_NAME
ENV CHARSET=UTF-8
ENV LANG=C.UTF-8
# Expose SERVICE_NAME as env so CMD expands properly on start
ENV SERVICE_NAME=${SERVICE_NAME}
# Set runtime
WORKDIR /opt/${SERVICE_NAME}
COPY --from=builder /build/_build/prod/rel/${SERVICE_NAME} /opt/${SERVICE_NAME}
RUN echo "#!/bin/sh" >> /entrypoint.sh && \
echo "exec /opt/${SERVICE_NAME}/bin/${SERVICE_NAME} foreground" >> /entrypoint.sh && \
chmod +x /entrypoint.sh
ENTRYPOINT []
CMD /opt/${SERVICE_NAME}/bin/${SERVICE_NAME} foreground
CMD ["/entrypoint.sh"]
EXPOSE 8022

View File

@ -1,13 +1,17 @@
ARG OTP_VERSION
FROM docker.io/library/erlang:${OTP_VERSION}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Install thrift compiler
ARG THRIFT_VERSION
ARG BUILDARCH
RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${BUILDARCH}.tar.gz" \
ARG TARGETARCH
RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${TARGETARCH}.tar.gz" \
| tar -xvz -C /usr/local/bin/
# Set env
ENV CHARSET=UTF-8
ENV LANG=C.UTF-8
CMD /bin/bash
# Set runtime
CMD ["/bin/bash"]

View File

@ -16,7 +16,6 @@
scoper, % should be before any scoper event handler usage
gproc,
dmt_client,
woody_user_identity,
payproc_errors,
erl_health,
cache

View File

@ -1,16 +0,0 @@
-module(pm_access_control).
%%% HG access controll
-export([check_user/2]).
-spec check_user(woody_user_identity:user_identity(), dmsl_domain_thrift:'PartyID'()) -> ok | invalid_user.
check_user(#{id := PartyID, realm := <<"external">>}, PartyID) ->
ok;
check_user(#{id := _AnyID, realm := <<"internal">>}, _PartyID) ->
ok;
%% @TODO must be deleted when we get rid of #payproc_ServiceUser
check_user(#{id := _AnyID, realm := <<"service">>}, _PartyID) ->
ok;
check_user(_, _) ->
invalid_user.

View File

@ -7,15 +7,12 @@
-export([cleanup/0]).
-export([get_woody_context/1]).
-export([set_user_identity/2]).
-opaque context() :: #{
woody_context := woody_context(),
user_identity => user_identity()
woody_context := woody_context()
}.
-type options() :: #{
user_identity => user_identity(),
woody_context => woody_context()
}.
@ -24,7 +21,6 @@
%% Internal types
-type user_identity() :: woody_user_identity:user_identity().
-type woody_context() :: woody_context:ctx().
%% TODO change when moved to separate app
@ -61,14 +57,9 @@ cleanup() ->
ok.
-spec get_woody_context(context()) -> woody_context().
get_woody_context(Context) ->
#{woody_context := WoodyContext} = ensure_woody_user_info_set(Context),
get_woody_context(#{woody_context := WoodyContext}) ->
WoodyContext.
-spec set_user_identity(user_identity(), context()) -> context().
set_user_identity(Identity, Context) ->
Context#{user_identity => Identity}.
%% Internal functions
-spec ensure_woody_context_exists(options()) -> options().
@ -76,10 +67,3 @@ ensure_woody_context_exists(#{woody_context := _WoodyContext} = Options) ->
Options;
ensure_woody_context_exists(Options) ->
Options#{woody_context => woody_context:new()}.
-spec ensure_woody_user_info_set(context()) -> context().
ensure_woody_user_info_set(#{user_identity := Identity, woody_context := WoodyContext} = Context) ->
NewWoodyContext = woody_user_identity:put(Identity, WoodyContext),
Context#{woody_context := NewWoodyContext};
ensure_woody_user_info_set(Context) ->
Context.

View File

@ -14,25 +14,27 @@
handle_function(Func, Args, Opts) ->
scoper:scope(
partymgmt,
fun() -> handle_function_(Func, Args, Opts) end
fun() ->
handle_function_(Func, remove_user_info_arg(Func, Args), Opts)
end
).
-spec handle_function_(woody:func(), woody:args(), pm_woody_wrapper:handler_opts()) -> term() | no_return().
%% Party
handle_function_('Create', {UserInfo, PartyID, PartyParams}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('Create', {PartyID, PartyParams}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
pm_party_machine:start(PartyID, PartyParams);
handle_function_('Checkout', {UserInfo, PartyID, RevisionParam}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('Checkout', {PartyID, RevisionParam}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
checkout_party(PartyID, RevisionParam, #payproc_InvalidPartyRevision{});
handle_function_('Get', {UserInfo, PartyID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('Get', {PartyID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
pm_party_machine:get_party(PartyID);
handle_function_('GetRevision', {UserInfo, PartyID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetRevision', {PartyID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
pm_party_machine:get_last_revision(PartyID);
handle_function_('GetStatus', {UserInfo, PartyID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetStatus', {PartyID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
pm_party_machine:get_status(PartyID);
handle_function_(Fun, Args, _Opts) when
Fun =:= 'Block' orelse
@ -40,18 +42,17 @@ handle_function_(Fun, Args, _Opts) when
Fun =:= 'Suspend' orelse
Fun =:= 'Activate'
->
UserInfo = erlang:element(1, Args),
PartyID = erlang:element(2, Args),
ok = set_meta_and_check_access(UserInfo, PartyID),
PartyID = erlang:element(1, Args),
_ = set_party_mgmt_meta(PartyID),
call(PartyID, Fun, Args);
%% Contract
handle_function_('GetContract', {UserInfo, PartyID, ContractID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetContract', {PartyID, ContractID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
Party = pm_party_machine:get_party(PartyID),
ensure_contract(pm_party:get_contract(ContractID, Party));
handle_function_('ComputeContractTerms', Args, _Opts) ->
{UserInfo, PartyID, ContractID, Timestamp, PartyRevisionParams, DomainRevision, Varset} = Args,
ok = set_meta_and_check_access(UserInfo, PartyID),
{PartyID, ContractID, Timestamp, PartyRevisionParams, DomainRevision, Varset} = Args,
_ = set_party_mgmt_meta(PartyID),
Party = checkout_party(PartyID, PartyRevisionParams),
Contract = ensure_contract(pm_party:get_contract(ContractID, Party)),
VS =
@ -73,19 +74,19 @@ handle_function_('ComputeContractTerms', Args, _Opts) ->
Terms = pm_party:get_terms(Contract, Timestamp, DomainRevision),
pm_party:reduce_terms(Terms, DecodedVS, DomainRevision);
%% Shop
handle_function_('GetShop', {UserInfo, PartyID, ID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetShop', {PartyID, ID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
Party = pm_party_machine:get_party(PartyID),
ensure_shop(pm_party:get_shop(ID, Party));
handle_function_('GetShopContract', {UserInfo, PartyID, ID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetShopContract', {PartyID, ID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
Party = pm_party_machine:get_party(PartyID),
Shop = ensure_shop(pm_party:get_shop(ID, Party)),
Contract = pm_party:get_contract(Shop#domain_Shop.contract_id, Party),
Contractor = pm_party:get_contractor(Contract#domain_Contract.contractor_id, Party),
#payproc_ShopContract{shop = Shop, contract = Contract, contractor = Contractor};
handle_function_('ComputeShopTerms', {UserInfo, PartyID, ShopID, Timestamp, PartyRevision, Varset}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('ComputeShopTerms', {PartyID, ShopID, Timestamp, PartyRevision, Varset}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
Party = checkout_party(PartyID, PartyRevision),
Shop = ensure_shop(pm_party:get_shop(ShopID, Party)),
Contract = pm_party:get_contract(Shop#domain_Shop.contract_id, Party),
@ -105,16 +106,15 @@ handle_function_(Fun, Args, _Opts) when
Fun =:= 'SuspendShop' orelse
Fun =:= 'ActivateShop'
->
UserInfo = erlang:element(1, Args),
PartyID = erlang:element(2, Args),
ok = set_meta_and_check_access(UserInfo, PartyID),
PartyID = erlang:element(1, Args),
_ = set_party_mgmt_meta(PartyID),
call(PartyID, Fun, Args);
%% Claim
handle_function_('GetClaim', {UserInfo, PartyID, ID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetClaim', {PartyID, ID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
pm_party_machine:get_claim(ID, PartyID);
handle_function_('GetClaims', {UserInfo, PartyID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetClaims', {PartyID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
pm_party_machine:get_claims(PartyID);
handle_function_(Fun, Args, _Opts) when
Fun =:= 'CreateClaim' orelse
@ -123,36 +123,33 @@ handle_function_(Fun, Args, _Opts) when
Fun =:= 'DenyClaim' orelse
Fun =:= 'RevokeClaim'
->
UserInfo = erlang:element(1, Args),
PartyID = erlang:element(2, Args),
ok = set_meta_and_check_access(UserInfo, PartyID),
PartyID = erlang:element(1, Args),
_ = set_party_mgmt_meta(PartyID),
call(PartyID, Fun, Args);
%% Event
handle_function_('GetEvents', {UserInfo, PartyID, Range}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetEvents', {PartyID, Range}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
#payproc_EventRange{'after' = AfterID, limit = Limit} = Range,
pm_party_machine:get_public_history(PartyID, AfterID, Limit);
%% ShopAccount
handle_function_('GetAccountState', {UserInfo, PartyID, AccountID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetAccountState', {PartyID, AccountID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
Party = pm_party_machine:get_party(PartyID),
pm_party:get_account_state(AccountID, Party);
handle_function_('GetShopAccount', {UserInfo, PartyID, ShopID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetShopAccount', {PartyID, ShopID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
Party = pm_party_machine:get_party(PartyID),
pm_party:get_shop_account(ShopID, Party);
%% Providers
handle_function_('ComputeProvider', Args, _Opts) ->
{UserInfo, ProviderRef, DomainRevision, Varset} = Args,
ok = assume_user_identity(UserInfo),
{ProviderRef, DomainRevision, Varset} = Args,
Provider = get_provider(ProviderRef, DomainRevision),
VS = pm_varset:decode_varset(Varset),
ComputedProvider = pm_provider:reduce_provider(Provider, VS, DomainRevision),
_ = assert_provider_reduced(ComputedProvider),
ComputedProvider;
handle_function_('ComputeProviderTerminalTerms', Args, _Opts) ->
{UserInfo, ProviderRef, TerminalRef, DomainRevision, Varset} = Args,
ok = assume_user_identity(UserInfo),
{ProviderRef, TerminalRef, DomainRevision, Varset} = Args,
Provider = get_provider(ProviderRef, DomainRevision),
Terminal = get_terminal(TerminalRef, DomainRevision),
VS = pm_varset:decode_varset(Varset),
@ -185,49 +182,42 @@ handle_function_('ComputeProviderTerminal', {TerminalRef, DomainRevision, Varset
};
%% Globals
handle_function_('ComputeGlobals', Args, _Opts) ->
{UserInfo, DomainRevision, Varset} = Args,
ok = assume_user_identity(UserInfo),
{DomainRevision, Varset} = Args,
Globals = get_globals(DomainRevision),
VS = pm_varset:decode_varset(Varset),
pm_globals:reduce_globals(Globals, VS, DomainRevision);
%% RuleSets
%% Deprecated, will be replaced by 'ComputeRoutingRuleset'
handle_function_('ComputePaymentRoutingRuleset', Args, _Opts) ->
{UserInfo, RuleSetRef, DomainRevision, Varset} = Args,
ok = assume_user_identity(UserInfo),
RuleSet = get_payment_routing_ruleset(RuleSetRef, DomainRevision),
VS = pm_varset:decode_varset(Varset),
pm_ruleset:reduce_payment_routing_ruleset(RuleSet, VS, DomainRevision);
handle_function_('ComputeRoutingRuleset', Args, _Opts) ->
{UserInfo, RuleSetRef, DomainRevision, Varset} = Args,
ok = assume_user_identity(UserInfo),
handle_function_(ComputeRulesetFun, Args, _Opts) when
%% 'ComputePaymentRoutingRuleset' is deprecated, will be replaced by 'ComputeRoutingRuleset'
ComputeRulesetFun =:= 'ComputePaymentRoutingRuleset' orelse
ComputeRulesetFun =:= 'ComputeRoutingRuleset'
->
{RuleSetRef, DomainRevision, Varset} = Args,
RuleSet = get_payment_routing_ruleset(RuleSetRef, DomainRevision),
VS = pm_varset:decode_varset(Varset),
pm_ruleset:reduce_payment_routing_ruleset(RuleSet, VS, DomainRevision);
%% PartyMeta
handle_function_('GetMeta', {UserInfo, PartyID}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetMeta', {PartyID}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
pm_party_machine:get_meta(PartyID);
handle_function_('GetMetaData', {UserInfo, PartyID, NS}, _Opts) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
handle_function_('GetMetaData', {PartyID, NS}, _Opts) ->
_ = set_party_mgmt_meta(PartyID),
pm_party_machine:get_metadata(NS, PartyID);
handle_function_(Fun, Args, _Opts) when
Fun =:= 'SetMetaData' orelse
Fun =:= 'RemoveMetaData'
->
UserInfo = erlang:element(1, Args),
PartyID = erlang:element(2, Args),
ok = set_meta_and_check_access(UserInfo, PartyID),
PartyID = erlang:element(1, Args),
_ = set_party_mgmt_meta(PartyID),
call(PartyID, Fun, Args);
%% Payment Institutions
handle_function_(
'ComputePaymentInstitutionTerms',
{UserInfo, PaymentInstitutionRef, Varset},
{PaymentInstitutionRef, Varset},
_Opts
) ->
ok = assume_user_identity(UserInfo),
Revision = pm_domain:head(),
PaymentInstitution = get_payment_institution(PaymentInstitutionRef, Revision),
VS = pm_varset:decode_varset(Varset),
@ -235,8 +225,7 @@ handle_function_(
Terms = pm_party:get_terms(ContractTemplate, pm_datetime:format_now(), Revision),
pm_party:reduce_terms(Terms, VS, Revision);
handle_function_('ComputePaymentInstitution', Args, _Opts) ->
{UserInfo, PaymentInstitutionRef, DomainRevision, Varset} = Args,
ok = assume_user_identity(UserInfo),
{PaymentInstitutionRef, DomainRevision, Varset} = Args,
PaymentInstitution = get_payment_institution(PaymentInstitutionRef, DomainRevision),
VS = pm_varset:decode_varset(Varset),
pm_payment_institution:reduce_payment_institution(PaymentInstitution, VS, DomainRevision);
@ -244,10 +233,10 @@ handle_function_('ComputePaymentInstitution', Args, _Opts) ->
handle_function_(
'ComputePayoutCashFlow',
{UserInfo, PartyID, #payproc_PayoutParams{id = ShopID, amount = Amount, timestamp = Timestamp} = PayoutParams},
{PartyID, #payproc_PayoutParams{id = ShopID, amount = Amount, timestamp = Timestamp} = PayoutParams},
_Opts
) ->
ok = set_meta_and_check_access(UserInfo, PartyID),
_ = set_party_mgmt_meta(PartyID),
Party = checkout_party(PartyID, {timestamp, Timestamp}),
Shop = ensure_shop(pm_party:get_shop(ShopID, Party)),
Contract = pm_party:get_contract(Shop#domain_Shop.contract_id, Party),
@ -272,8 +261,27 @@ handle_function_(
%%
%% @TODO Delete after protocol migration
%% This is a migration measure to make sure we can accept both old and new (with no userinfo) protocol here
remove_user_info_arg('ComputeProviderTerminal', Args0) ->
Args0;
remove_user_info_arg(_Func, Args0) ->
erlang:delete_element(1, Args0).
add_user_info_arg('ComputeProviderTerminal', Args0) ->
Args0;
add_user_info_arg(_Func, Args0) ->
erlang:insert_element(1, Args0, undefined).
%%
call(PartyID, FunctionName, Args) ->
pm_party_machine:call(PartyID, party_management, {'PartyManagement', FunctionName}, Args).
pm_party_machine:call(
PartyID,
party_management,
{'PartyManagement', FunctionName},
add_user_info_arg(FunctionName, Args)
).
%%
@ -287,23 +295,6 @@ get_payout_tool(_Shop, Contract, #payproc_PayoutParams{payout_tool_id = ToolID})
get_payout_tool(Shop, Contract, _PayoutParams) ->
pm_contract:get_payout_tool(Shop#domain_Shop.payout_tool_id, Contract).
set_meta_and_check_access(UserInfo, PartyID) ->
ok = assume_user_identity(UserInfo),
_ = set_party_mgmt_meta(PartyID),
assert_party_accessible(PartyID).
-spec assert_party_accessible(
dmsl_domain_thrift:'PartyID'()
) -> ok | no_return().
assert_party_accessible(PartyID) ->
UserIdentity = pm_woody_handler_utils:get_user_identity(),
case pm_access_control:check_user(UserIdentity, PartyID) of
ok ->
ok;
invalid_user ->
throw(#payproc_InvalidUser{})
end.
assert_provider_reduced(#domain_Provider{terms = Terms}) ->
assert_provider_terms_reduced(Terms).
@ -315,9 +306,6 @@ assert_provider_terms_reduced(undefined) ->
set_party_mgmt_meta(PartyID) ->
scoper:add_meta(#{party_id => PartyID}).
assume_user_identity(UserInfo) ->
pm_woody_handler_utils:assume_user_identity(UserInfo).
checkout_party(PartyID, RevisionParam) ->
checkout_party(PartyID, RevisionParam, #payproc_PartyNotExistsYet{}).

View File

@ -113,7 +113,7 @@ process_signal(timeout, _Machine) ->
-spec process_call(call(), pm_machine:machine()) -> {pm_machine:response(), pm_machine:result()}.
process_call({{'PartyManagement', Fun}, Args}, Machine) ->
PartyID = erlang:element(2, Args),
process_call_(PartyID, Fun, Args, Machine);
process_call_(PartyID, Fun, remove_user_info_arg(Args), Machine);
process_call({{'ClaimCommitter', Fun}, Args}, Machine) ->
PartyID = erlang:element(1, Args),
process_call_(PartyID, Fun, Args, Machine).
@ -142,36 +142,39 @@ process_call_(PartyID, Fun, Args, Machine) ->
respond_w_exception(Exception)
end.
remove_user_info_arg(Args0) ->
erlang:delete_element(1, Args0).
%% Party
handle_call('Block', {_, _PartyID, Reason}, AuxSt, St) ->
handle_call('Block', {_PartyID, Reason}, AuxSt, St) ->
handle_block(party, Reason, AuxSt, St);
handle_call('Unblock', {_, _PartyID, Reason}, AuxSt, St) ->
handle_call('Unblock', {_PartyID, Reason}, AuxSt, St) ->
handle_unblock(party, Reason, AuxSt, St);
handle_call('Suspend', {_, _PartyID}, AuxSt, St) ->
handle_call('Suspend', {_PartyID}, AuxSt, St) ->
handle_suspend(party, AuxSt, St);
handle_call('Activate', {_, _PartyID}, AuxSt, St) ->
handle_call('Activate', {_PartyID}, AuxSt, St) ->
handle_activate(party, AuxSt, St);
%% Shop
handle_call('BlockShop', {_, _PartyID, ID, Reason}, AuxSt, St) ->
handle_call('BlockShop', {_PartyID, ID, Reason}, AuxSt, St) ->
handle_block({shop, ID}, Reason, AuxSt, St);
handle_call('UnblockShop', {_, _PartyID, ID, Reason}, AuxSt, St) ->
handle_call('UnblockShop', {_PartyID, ID, Reason}, AuxSt, St) ->
handle_unblock({shop, ID}, Reason, AuxSt, St);
handle_call('SuspendShop', {_, _PartyID, ID}, AuxSt, St) ->
handle_call('SuspendShop', {_PartyID, ID}, AuxSt, St) ->
handle_suspend({shop, ID}, AuxSt, St);
handle_call('ActivateShop', {_, _PartyID, ID}, AuxSt, St) ->
handle_call('ActivateShop', {_PartyID, ID}, AuxSt, St) ->
handle_activate({shop, ID}, AuxSt, St);
%% PartyMeta
handle_call('SetMetaData', {_, _PartyID, NS, Data}, AuxSt, St) ->
handle_call('SetMetaData', {_PartyID, NS, Data}, AuxSt, St) ->
respond(
ok,
[?party_meta_set(NS, Data)],
AuxSt,
St
);
handle_call('RemoveMetaData', {_, _PartyID, NS}, AuxSt, St) ->
handle_call('RemoveMetaData', {_PartyID, NS}, AuxSt, St) ->
_ = get_st_metadata(NS, St),
respond(
ok,
@ -181,7 +184,7 @@ handle_call('RemoveMetaData', {_, _PartyID, NS}, AuxSt, St) ->
);
%% Claim
handle_call('CreateClaim', {_, _PartyID, Changeset}, AuxSt, St) ->
handle_call('CreateClaim', {_PartyID, Changeset}, AuxSt, St) ->
ok = assert_party_operable(St),
{Claim, Changes} = create_claim(Changeset, St),
respond(
@ -190,7 +193,7 @@ handle_call('CreateClaim', {_, _PartyID, Changeset}, AuxSt, St) ->
AuxSt,
St
);
handle_call('UpdateClaim', {_, _PartyID, ID, ClaimRevision, Changeset}, AuxSt, St) ->
handle_call('UpdateClaim', {_PartyID, ID, ClaimRevision, Changeset}, AuxSt, St) ->
ok = assert_party_operable(St),
ok = assert_claim_modification_allowed(ID, ClaimRevision, St),
respond(
@ -199,7 +202,7 @@ handle_call('UpdateClaim', {_, _PartyID, ID, ClaimRevision, Changeset}, AuxSt, S
AuxSt,
St
);
handle_call('AcceptClaim', {_, _PartyID, ID, ClaimRevision}, AuxSt, St) ->
handle_call('AcceptClaim', {_PartyID, ID, ClaimRevision}, AuxSt, St) ->
ok = assert_claim_modification_allowed(ID, ClaimRevision, St),
Timestamp = pm_datetime:format_now(),
Revision = get_next_party_revision(St),
@ -215,7 +218,7 @@ handle_call('AcceptClaim', {_, _PartyID, ID, ClaimRevision}, AuxSt, St) ->
AuxSt,
St
);
handle_call('DenyClaim', {_, _PartyID, ID, ClaimRevision, Reason}, AuxSt, St) ->
handle_call('DenyClaim', {_PartyID, ID, ClaimRevision, Reason}, AuxSt, St) ->
ok = assert_claim_modification_allowed(ID, ClaimRevision, St),
Timestamp = pm_datetime:format_now(),
Claim = pm_claim:deny(Reason, Timestamp, get_st_claim(ID, St)),
@ -225,7 +228,7 @@ handle_call('DenyClaim', {_, _PartyID, ID, ClaimRevision, Reason}, AuxSt, St) ->
AuxSt,
St
);
handle_call('RevokeClaim', {_, _PartyID, ID, ClaimRevision, Reason}, AuxSt, St) ->
handle_call('RevokeClaim', {_PartyID, ID, ClaimRevision, Reason}, AuxSt, St) ->
ok = assert_party_operable(St),
ok = assert_claim_modification_allowed(ID, ClaimRevision, St),
Timestamp = pm_datetime:format_now(),

View File

@ -1,45 +0,0 @@
-module(pm_woody_handler_utils).
-include_lib("damsel/include/dmsl_payment_processing_thrift.hrl").
-type user_info() :: dmsl_payment_processing_thrift:'UserInfo'().
-type user_identity() :: woody_user_identity:user_identity().
-export([get_user_identity/0]).
-export([assume_user_identity/1]).
-spec get_user_identity() -> woody_user_identity:user_identity() | undefined.
get_user_identity() ->
try
Context = pm_context:load(),
woody_user_identity:get(pm_context:get_woody_context(Context))
catch
throw:{missing_required, _Key} ->
undefined
end.
-spec set_user_identity(user_identity()) -> ok.
set_user_identity(UserIdentity) ->
pm_context:save(pm_context:set_user_identity(UserIdentity, pm_context:load())).
-spec assume_user_identity(user_info()) -> ok.
assume_user_identity(UserInfo) ->
case get_user_identity() of
V when V /= undefined ->
ok;
undefined ->
set_user_identity(map_user_info(UserInfo))
end.
map_user_info(#payproc_UserInfo{id = PartyID, type = Type}) ->
#{
id => PartyID,
realm => map_user_type(Type)
}.
map_user_type({external_user, #payproc_ExternalUser{}}) ->
<<"external">>;
map_user_type({internal_user, #payproc_InternalUser{}}) ->
<<"internal">>;
map_user_type({service_user, #payproc_ServiceUser{}}) ->
<<"service">>.

View File

@ -11,8 +11,7 @@
-type handler_opts() :: #{
handler := module(),
default_handling_timeout => timeout(),
user_identity => undefined | woody_user_identity:user_identity()
default_handling_timeout => timeout()
}.
-type client_opts() :: #{

View File

@ -77,7 +77,7 @@ init_per_suite(C) ->
{Apps, _Ret} = pm_ct_helper:start_apps([woody, scoper, dmt_client, party_management]),
_ = pm_domain:insert(construct_domain_fixture()),
PartyID = erlang:list_to_binary([?MODULE_STRING, ".", erlang:integer_to_list(erlang:system_time())]),
ApiClient = pm_ct_helper:create_client(PartyID),
ApiClient = pm_ct_helper:create_client(),
[{apps, Apps}, {party_id, PartyID}, {api_client, ApiClient} | C].
-spec end_per_suite(config()) -> _.

View File

@ -6,8 +6,8 @@
-export([cfg/2]).
-export([create_client/0]).
-export([create_client/1]).
-export([create_client/2]).
-export([create_party_and_shop/5]).
-export([create_battle_ready_shop/5]).
@ -156,19 +156,16 @@ cfg(Key, Config) ->
%%
-spec create_client(woody_user_identity:id()) -> pm_client_api:t().
create_client(UserID) ->
create_client_w_context(UserID, woody_context:new()).
-spec create_client() -> pm_client_api:t().
create_client() ->
create_client_w_context(woody_context:new()).
-spec create_client(woody_user_identity:id(), woody:trace_id()) -> pm_client_api:t().
create_client(UserID, TraceID) ->
create_client_w_context(UserID, woody_context:new(TraceID)).
-spec create_client(woody:trace_id()) -> pm_client_api:t().
create_client(TraceID) ->
create_client_w_context(woody_context:new(TraceID)).
create_client_w_context(UserID, WoodyCtx) ->
pm_client_api:new(woody_user_identity:put(make_user_identity(UserID), WoodyCtx)).
make_user_identity(UserID) ->
#{id => genlib:to_binary(UserID), realm => <<"external">>}.
create_client_w_context(WoodyCtx) ->
pm_client_api:new(WoodyCtx).
%%

View File

@ -70,8 +70,6 @@
-export([shop_account_set_retrieval/1]).
-export([shop_account_retrieval/1]).
-export([party_access_control/1]).
-export([contract_not_found/1]).
-export([contract_creation/1]).
-export([contract_terms_retrieval/1]).
@ -88,7 +86,9 @@
-export([contract_w2w_terms/1]).
-export([compute_payment_institution_terms/1]).
-export([compute_payment_institution/1]).
-export([compute_payout_cash_flow/1]).
-export([compute_payout_cash_flow_payout_tool/1]).
-export([contractor_creation/1]).
-export([contractor_modification/1]).
@ -131,7 +131,6 @@ cfg(Key, C) ->
-spec all() -> [{group, group_name()}].
all() ->
[
{group, party_access_control},
{group, party_creation},
{group, party_revisioning},
{group, party_blocking_suspension},
@ -156,10 +155,6 @@ groups() ->
party_already_exists,
party_retrieval
]},
{party_access_control, [sequence], [
party_creation,
party_access_control
]},
{party_revisioning, [sequence], [
party_creation,
party_get_initial_revision,
@ -207,6 +202,7 @@ groups() ->
contract_payout_tool_creation,
contract_payout_tool_modification,
compute_payment_institution_terms,
compute_payment_institution,
contract_w2w_terms
]},
{shop_management, [sequence], [
@ -221,6 +217,7 @@ groups() ->
shop_already_exists,
shop_update,
compute_payout_cash_flow,
compute_payout_cash_flow_payout_tool,
{group, shop_blocking_suspension}
]},
{shop_blocking_suspension, [sequence], [
@ -305,7 +302,7 @@ init_per_group(shop_blocking_suspension, C) ->
C;
init_per_group(Group, C) ->
PartyID = list_to_binary(lists:concat([Group, ".", erlang:system_time()])),
ApiClient = pm_ct_helper:create_client(PartyID),
ApiClient = pm_ct_helper:create_client(),
Client = pm_client_party:start(PartyID, ApiClient),
[{party_id, PartyID}, {client, Client} | C].
@ -478,8 +475,6 @@ end_per_testcase(_Name, _C) ->
-spec shop_account_set_retrieval(config()) -> _ | no_return().
-spec shop_account_retrieval(config()) -> _ | no_return().
-spec party_access_control(config()) -> _ | no_return().
-spec contract_not_found(config()) -> _ | no_return().
-spec contract_creation(config()) -> _ | no_return().
-spec contract_terms_retrieval(config()) -> _ | no_return().
@ -494,7 +489,9 @@ end_per_testcase(_Name, _C) ->
-spec contract_adjustment_creation(config()) -> _ | no_return().
-spec contract_adjustment_expiration(config()) -> _ | no_return().
-spec compute_payment_institution_terms(config()) -> _ | no_return().
-spec compute_payment_institution(config()) -> _ | no_return().
-spec compute_payout_cash_flow(config()) -> _ | no_return().
-spec compute_payout_cash_flow_payout_tool(config()) -> _ | no_return().
-spec contract_w2w_terms(config()) -> _ | no_return().
-spec contractor_creation(config()) -> _ | no_return().
-spec contractor_modification(config()) -> _ | no_return().
@ -917,6 +914,22 @@ compute_payment_institution_terms(C) ->
?assert_different_term_sets(T2, T4),
?assert_different_term_sets(T3, T4).
compute_payment_institution(C) ->
Client = cfg(client, C),
DomainRevision = pm_domain:head(),
TermsFun = fun(PartyID) ->
#domain_PaymentInstitution{} =
pm_client_party:compute_payment_institution(
?pinst(4),
DomainRevision,
#payproc_Varset{party_id = PartyID},
Client
)
end,
T1 = TermsFun(<<"12345">>),
T2 = TermsFun(<<"67890">>),
?assert_different_term_sets(T1, T2).
-spec check_all_payment_methods(config()) -> _.
check_all_payment_methods(C) ->
Client = cfg(client, C),
@ -977,6 +990,30 @@ compute_payout_cash_flow(C) ->
}
] = pm_client_party:compute_payout_cash_flow(Params, Client).
compute_payout_cash_flow_payout_tool(C) ->
Client = cfg(client, C),
Params = #payproc_PayoutParams{
id = ?REAL_SHOP_ID,
amount = #domain_Cash{amount = 10000, currency = ?cur(<<"RUB">>)},
timestamp = pm_datetime:format_now(),
payout_tool_id = <<"1">>
},
[
#domain_FinalCashFlowPosting{
source = #domain_FinalCashFlowAccount{account_type = {merchant, settlement}},
destination = #domain_FinalCashFlowAccount{account_type = {merchant, payout}},
volume = #domain_Cash{amount = 7500, currency = ?cur(<<"RUB">>)}
},
#domain_FinalCashFlowPosting{
source = #domain_FinalCashFlowAccount{account_type = {merchant, settlement}},
destination = #domain_FinalCashFlowAccount{account_type = {system, settlement}},
volume = #domain_Cash{amount = 2500, currency = ?cur(<<"RUB">>)}
}
] = pm_client_party:compute_payout_cash_flow(Params, Client),
{exception, #payproc_PayoutToolNotFound{}} = pm_client_party:compute_payout_cash_flow(
Params#payproc_PayoutParams{payout_tool_id = <<"Nope">>}, Client
).
contract_w2w_terms(C) ->
Client = cfg(client, C),
ContractID = ?REAL_CONTRACT_ID,
@ -1571,56 +1608,6 @@ contract_w_contractor_creation(C) ->
ok = accept_claim(Claim, Client),
#domain_Contract{id = ContractID, contractor_id = ContractorID} = pm_client_party:get_contract(ContractID, Client).
%% Access control tests
party_access_control(C) ->
PartyID = cfg(party_id, C),
% External Success
GoodExternalClient = cfg(client, C),
#domain_Party{id = PartyID} = pm_client_party:get(GoodExternalClient),
% External Reject
BadExternalClient0 = pm_client_party:start(
#payproc_UserInfo{id = <<"FakE1D">>, type = {external_user, #payproc_ExternalUser{}}},
PartyID,
pm_client_api:new()
),
?invalid_user() = pm_client_party:get(BadExternalClient0),
pm_client_party:stop(BadExternalClient0),
% UserIdentity has priority
UserIdentity = #{
id => PartyID,
realm => <<"internal">>
},
Context = woody_user_identity:put(UserIdentity, woody_context:new()),
UserIdentityClient1 = pm_client_party:start(
#payproc_UserInfo{id = <<"FakE1D">>, type = {external_user, #payproc_ExternalUser{}}},
PartyID,
pm_client_api:new(Context)
),
#domain_Party{id = PartyID} = pm_client_party:get(UserIdentityClient1),
pm_client_party:stop(UserIdentityClient1),
% Internal Success
GoodInternalClient = pm_client_party:start(
#payproc_UserInfo{id = <<"F4KE1D">>, type = {internal_user, #payproc_InternalUser{}}},
PartyID,
pm_client_api:new()
),
#domain_Party{id = PartyID} = pm_client_party:get(GoodInternalClient),
pm_client_party:stop(GoodInternalClient),
% Service Success
GoodServiceClient = pm_client_party:start(
#payproc_UserInfo{id = <<"fAkE1D">>, type = {service_user, #payproc_ServiceUser{}}},
PartyID,
pm_client_api:new()
),
#domain_Party{id = PartyID} = pm_client_party:get(GoodServiceClient),
pm_client_party:stop(GoodServiceClient),
ok.
%% Compute providers
compute_provider_ok(C) ->
@ -2616,6 +2603,31 @@ construct_domain_fixture() ->
}
}},
{payment_institution, #domain_PaymentInstitutionObject{
ref = ?pinst(4),
data = #domain_PaymentInstitution{
name = <<"Chetky Payments Inc.">>,
system_account_set =
{decisions, [
#domain_SystemAccountSetDecision{
if_ = ?partycond(<<"12345">>, undefined),
then_ =
{value, ?sas(2)}
},
#domain_SystemAccountSetDecision{
if_ = ?partycond(<<"67890">>, undefined),
then_ =
{value, ?sas(1)}
}
]},
default_contract_template = {value, ?tmpl(2)},
providers = {value, ?ordset([])},
inspector = {value, ?insp(1)},
residences = [],
realm = live
}
}},
{globals, #domain_GlobalsObject{
ref = #domain_GlobalsRef{},
data = #domain_Globals{

View File

@ -1,6 +1,5 @@
-module(pm_client_api).
-export([new/0]).
-export([new/1]).
-export([call/4]).
@ -10,10 +9,6 @@
-type t() :: woody_context:ctx().
-spec new() -> t().
new() ->
woody_context:new().
-spec new(woody_context:ctx()) -> t().
new(Context) ->
Context.

View File

@ -3,8 +3,6 @@
-include_lib("damsel/include/dmsl_payment_processing_thrift.hrl").
-export([start/2]).
-export([start/3]).
-export([start_link/2]).
-export([stop/1]).
-export([create/2]).
@ -28,6 +26,7 @@
-export([get_shop_contract/2]).
-export([compute_shop_terms/5]).
-export([compute_payment_institution_terms/3]).
-export([compute_payment_institution/4]).
-export([compute_payout_cash_flow/2]).
-export([block_shop/3]).
@ -61,13 +60,9 @@
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).
%%
-type user_info() :: dmsl_payment_processing_thrift:'UserInfo'().
-type party_id() :: dmsl_domain_thrift:'PartyID'().
-type party_params() :: dmsl_payment_processing_thrift:'PartyParams'().
-type domain_revision() :: dmsl_domain_thrift:'DataRevision'().
@ -95,18 +90,7 @@
-spec start(party_id(), pm_client_api:t()) -> pid().
start(PartyID, ApiClient) ->
start(start, undefined, PartyID, ApiClient).
-spec start(user_info(), party_id(), pm_client_api:t()) -> pid().
start(UserInfo, PartyID, ApiClient) ->
start(start, UserInfo, PartyID, ApiClient).
-spec start_link(party_id(), pm_client_api:t()) -> pid().
start_link(PartyID, ApiClient) ->
start(start_link, undefined, PartyID, ApiClient).
start(Mode, UserInfo, PartyID, ApiClient) ->
{ok, Pid} = gen_server:Mode(?MODULE, {UserInfo, PartyID, ApiClient}, []),
{ok, Pid} = gen_server:start(?MODULE, {PartyID, ApiClient}, []),
Pid.
-spec stop(pid()) -> ok.
@ -190,6 +174,11 @@ compute_contract_terms(ID, Timestamp, PartyRevision, DomainRevision, Varset, Cli
compute_payment_institution_terms(Ref, Varset, Client) ->
call(Client, 'ComputePaymentInstitutionTerms', with_user_info([Ref, Varset])).
-spec compute_payment_institution(payment_intitution_ref(), domain_revision(), varset(), pid()) ->
dmsl_domain_thrift:'TermSet'() | woody_error:business_error().
compute_payment_institution(Ref, DomainRevision, Varset, Client) ->
call(Client, 'ComputePaymentInstitution', with_user_info([Ref, DomainRevision, Varset])).
-spec compute_payout_cash_flow(dmsl_payment_processing_thrift:'PayoutParams'(), pid()) ->
dmsl_domain_thrift:'FinalCashFlow'() | woody_error:business_error().
compute_payout_cash_flow(Params, Client) ->
@ -322,7 +311,6 @@ map_result_error({error, Error}) ->
-type event() :: dmsl_payment_processing_thrift:'Event'().
-record(state, {
user_info :: user_info(),
party_id :: party_id(),
poller :: pm_client_event_poller:st(event()),
client :: pm_client_api:t()
@ -331,14 +319,13 @@ map_result_error({error, Error}) ->
-type state() :: #state{}.
-type callref() :: {pid(), Tag :: reference()}.
-spec init({user_info(), party_id(), pm_client_api:t()}) -> {ok, state()}.
init({UserInfo, PartyID, ApiClient}) ->
-spec init({party_id(), pm_client_api:t()}) -> {ok, state()}.
init({PartyID, ApiClient}) ->
{ok, #state{
user_info = UserInfo,
party_id = PartyID,
client = ApiClient,
poller = pm_client_event_poller:new(
{party_management, 'GetEvents', [UserInfo, PartyID]},
{party_management, 'GetEvents', [undefined, PartyID]},
fun(Event) -> Event#payproc_Event.id end
)
}}.
@ -374,21 +361,8 @@ handle_cast(Cast, State) ->
_ = logger:warning("unexpected cast received: ~tp", [Cast]),
{noreply, State}.
-spec handle_info(_, state()) -> {noreply, state()}.
handle_info(Info, State) ->
_ = logger:warning("unexpected info received: ~tp", [Info]),
{noreply, State}.
-spec terminate(Reason, state()) -> ok when Reason :: normal | shutdown | {shutdown, term()} | term().
terminate(_Reason, _State) ->
ok.
-spec code_change(Vsn | {down, Vsn}, state(), term()) -> {error, noimpl} when Vsn :: term().
code_change(_OldVsn, _State, _Extra) ->
{error, noimpl}.
with_user_info(Args) ->
[fun(St) -> St#state.user_info end | Args].
[undefined | Args].
with_user_info_party_id(Args) ->
[fun(St) -> St#state.user_info end, fun(St) -> St#state.party_id end | Args].
[undefined, fun(St) -> St#state.party_id end | Args].

View File

@ -16,7 +16,7 @@ services:
machinegun:
condition: service_healthy
dominant:
condition: service_started
condition: service_healthy
shumway:
condition: service_healthy
ports:
@ -24,20 +24,20 @@ services:
command: /sbin/init
dominant:
image: ghcr.io/valitydev/dominant:sha-d3f2615
image: ghcr.io/valitydev/dominant:sha-eb1cccb
depends_on:
- machinegun
ports:
- "8022"
command: /opt/dominant/bin/dominant foreground
healthcheck:
test: curl http://localhost:8022/health
test: "/opt/dominant/bin/dominant ping"
interval: 5s
timeout: 1s
retries: 20
machinegun:
image: docker.io/rbkmoney/machinegun:c05a8c18cd4f7966d70b6ad84cac9429cdfe37ae
image: ghcr.io/valitydev/machinegun:sha-7f0a21a
ports:
- "8022"
command: /opt/machinegun/bin/machinegun foreground
@ -45,7 +45,7 @@ services:
- ./test/machinegun/config.yaml:/opt/machinegun/etc/config.yaml
- ./test/machinegun/cookie:/opt/machinegun/etc/cookie
healthcheck:
test: curl http://localhost:8022/health
test: "/opt/machinegun/bin/machinegun ping"
interval: 5s
timeout: 1s
retries: 20

View File

@ -30,7 +30,6 @@
{gproc, "0.9.0"},
{genlib, {git, "https://github.com/valitydev/genlib.git", {branch, "master"}}},
{woody, {git, "https://github.com/valitydev/woody_erlang.git", {branch, "master"}}},
{woody_user_identity, {git, "https://github.com/valitydev/woody_erlang_user_identity.git", {branch, "master"}}},
{damsel, {git, "https://github.com/valitydev/damsel.git", {branch, "master"}}},
{payproc_errors, {git, "https://github.com/valitydev/payproc-errors-erlang.git", {branch, "master"}}},
{mg_proto, {git, "https://github.com/valitydev/machinegun-proto.git", {branch, "master"}}},

View File

@ -64,11 +64,8 @@
{<<"woody">>,
{git,"https://github.com/valitydev/woody_erlang.git",
{ref,"0c2e16dfc8a51f6f63fcd74df982178a9aeab322"}},
0},
{<<"woody_user_identity">>,
{git,"https://github.com/valitydev/woody_erlang_user_identity.git",
{ref,"a480762fea8d7c08f105fb39ca809482b6cb042e"}},
0}]}.
0}
]}.
[
{pkg_hash,[
{<<"cache">>, <<"B23A5FE7095445A88412A6E614C933377E0137B44FFED77C9B3FEF1A731A20B2">>},