From de8fca96d2a4f5b54fcb7b89a6b8c8e3ef31eef3 Mon Sep 17 00:00:00 2001 From: Alexey S Date: Mon, 8 Nov 2021 11:10:58 +0300 Subject: [PATCH] ED-288: Relax internal wapi auth (#30) --- apps/wapi/src/wapi_access_backend.erl | 113 --------- apps/wapi/src/wapi_backend_utils.erl | 37 +++ apps/wapi/src/wapi_destination_backend.erl | 19 +- apps/wapi/src/wapi_identity_backend.erl | 151 +++++------- apps/wapi/src/wapi_report_backend.erl | 13 +- apps/wapi/src/wapi_w2w_backend.erl | 33 +-- apps/wapi/src/wapi_wallet_backend.erl | 57 ++--- apps/wapi/src/wapi_wallet_handler.erl | 265 +++++++++++++-------- apps/wapi/src/wapi_webhook_backend.erl | 105 +++----- apps/wapi/src/wapi_withdrawal_backend.erl | 157 +----------- apps/wapi/test/wapi_ct_helper.erl | 26 +- 11 files changed, 341 insertions(+), 635 deletions(-) delete mode 100644 apps/wapi/src/wapi_access_backend.erl diff --git a/apps/wapi/src/wapi_access_backend.erl b/apps/wapi/src/wapi_access_backend.erl deleted file mode 100644 index 03a52ea..0000000 --- a/apps/wapi/src/wapi_access_backend.erl +++ /dev/null @@ -1,113 +0,0 @@ --module(wapi_access_backend). - --include_lib("fistful_proto/include/ff_proto_identity_thrift.hrl"). --include_lib("fistful_proto/include/ff_proto_wallet_thrift.hrl"). --include_lib("fistful_proto/include/ff_proto_destination_thrift.hrl"). --include_lib("fistful_proto/include/ff_proto_w2w_transfer_thrift.hrl"). --include_lib("fistful_proto/include/ff_proto_withdrawal_thrift.hrl"). - --export([get_resource_owner/2]). --export([check_resource/3]). --export([check_resource_by_id/3]). - --type id() :: binary(). --type resource_type() :: - identity - | wallet - | destination - | withdrawal - | w2w_transfer. - --type handler_context() :: wapi_handler:context(). --type data() :: - ff_proto_identity_thrift:'IdentityState'() - | ff_proto_wallet_thrift:'WalletState'(). - --define(CTX_NS, <<"com.rbkmoney.wapi">>). - -%% Pipeline - --spec get_resource_owner(resource_type(), data()) -> {ok, id()}. -get_resource_owner(Resource, Data) -> - {ok, get_owner(get_context_from_state(Resource, Data))}. - --spec check_resource(resource_type(), data(), handler_context()) -> ok | {error, unauthorized}. -check_resource(Resource, Data, HandlerContext) -> - Owner = get_owner(get_context_from_state(Resource, Data)), - check_resource_access(is_resource_owner(Owner, HandlerContext)). - --spec check_resource_by_id(resource_type(), id(), handler_context()) -> ok | {error, notfound | unauthorized}. -check_resource_by_id(Resource, ID, HandlerContext) -> - case get_context_by_id(Resource, ID, HandlerContext) of - {error, notfound} = Error -> - Error; - Context -> - Owner = get_owner(Context), - check_resource_access(is_resource_owner(Owner, HandlerContext)) - end. - -%% -%% Internal -%% - -get_context_by_id(identity, IdentityID, WoodyCtx) -> - Request = {fistful_identity, 'GetContext', {IdentityID}}, - case wapi_handler_utils:service_call(Request, WoodyCtx) of - {ok, Context} -> - Context; - {exception, #fistful_IdentityNotFound{}} -> - {error, notfound} - end; -get_context_by_id(wallet, WalletID, WoodyCtx) -> - Request = {fistful_wallet, 'GetContext', {WalletID}}, - case wapi_handler_utils:service_call(Request, WoodyCtx) of - {ok, Context} -> - Context; - {exception, #fistful_WalletNotFound{}} -> - {error, notfound} - end; -get_context_by_id(destination, DestinationID, WoodyCtx) -> - Request = {fistful_destination, 'GetContext', {DestinationID}}, - case wapi_handler_utils:service_call(Request, WoodyCtx) of - {ok, Context} -> - Context; - {exception, #fistful_DestinationNotFound{}} -> - {error, notfound} - end; -get_context_by_id(w2w_transfer, W2WTransferID, WoodyCtx) -> - Request = {fistful_w2w_transfer, 'GetContext', {W2WTransferID}}, - case wapi_handler_utils:service_call(Request, WoodyCtx) of - {ok, Context} -> - Context; - {exception, #fistful_W2WNotFound{}} -> - {error, notfound} - end; -get_context_by_id(withdrawal, WithdrawalID, WoodyCtx) -> - Request = {fistful_withdrawal, 'GetContext', {WithdrawalID}}, - case wapi_handler_utils:service_call(Request, WoodyCtx) of - {ok, Context} -> - Context; - {exception, #fistful_WithdrawalNotFound{}} -> - {error, notfound} - end. - -get_context_from_state(identity, #idnt_IdentityState{context = Context}) -> - Context; -get_context_from_state(wallet, #wlt_WalletState{context = Context}) -> - Context; -get_context_from_state(destination, #dst_DestinationState{context = Context}) -> - Context; -get_context_from_state(w2w_transfer, #w2w_transfer_W2WTransferState{context = Context}) -> - Context; -get_context_from_state(withdrawal, #wthd_WithdrawalState{context = Context}) -> - Context. - -get_owner(ContextThrift) -> - Context = wapi_codec:unmarshal(context, ContextThrift), - wapi_backend_utils:get_from_ctx(<<"owner">>, Context). - -is_resource_owner(Owner, HandlerCtx) -> - Owner =:= wapi_handler_utils:get_owner(HandlerCtx). - -check_resource_access(true) -> ok; -check_resource_access(false) -> {error, unauthorized}. diff --git a/apps/wapi/src/wapi_backend_utils.erl b/apps/wapi/src/wapi_backend_utils.erl index 43929a7..882d87d 100644 --- a/apps/wapi/src/wapi_backend_utils.erl +++ b/apps/wapi/src/wapi_backend_utils.erl @@ -1,6 +1,11 @@ -module(wapi_backend_utils). -include_lib("fistful_proto/include/ff_proto_base_thrift.hrl"). +-include_lib("fistful_proto/include/ff_proto_identity_thrift.hrl"). +-include_lib("fistful_proto/include/ff_proto_wallet_thrift.hrl"). +-include_lib("fistful_proto/include/ff_proto_destination_thrift.hrl"). +-include_lib("fistful_proto/include/ff_proto_w2w_transfer_thrift.hrl"). +-include_lib("fistful_proto/include/ff_proto_withdrawal_thrift.hrl"). -define(EXTERNAL_ID, <<"externalID">>). -define(CTX_NS, <<"com.rbkmoney.wapi">>). @@ -33,6 +38,18 @@ | destination | withdrawal | w2w_transfer. +-type entity_type() :: + identity + | wallet + | destination + | withdrawal + | w2w_transfer. +-type entity_state() :: + ff_proto_identity_thrift:'IdentityState'() + | ff_proto_wallet_thrift:'WalletState'() + | ff_proto_destination_thrift:'DestinationState'() + | ff_proto_w2w_transfer_thrift:'W2WTransferState'() + | ff_proto_withdrawal_thrift:'WithdrawalState'(). -export([gen_id/3]). -export([gen_id/4]). @@ -45,6 +62,7 @@ -export([create_params_hash/1]). -export([decode_resource/1]). -export([tokenize_resource/1]). +-export([get_entity_owner/2]). %% Pipeline @@ -172,3 +190,22 @@ tokenize_resource({bank_card, BankCard}) -> create_params_hash(Map); tokenize_resource(Value) -> create_params_hash(Value). + +-spec get_entity_owner(entity_type(), entity_state()) -> {ok, id()}. +get_entity_owner(Type, State) -> + {ok, get_context_owner(get_context_from_state(Type, State))}. + +get_context_from_state(identity, #idnt_IdentityState{context = Context}) -> + Context; +get_context_from_state(wallet, #wlt_WalletState{context = Context}) -> + Context; +get_context_from_state(destination, #dst_DestinationState{context = Context}) -> + Context; +get_context_from_state(w2w_transfer, #w2w_transfer_W2WTransferState{context = Context}) -> + Context; +get_context_from_state(withdrawal, #wthd_WithdrawalState{context = Context}) -> + Context. + +get_context_owner(ContextThrift) -> + Context = wapi_codec:unmarshal(context, ContextThrift), + wapi_backend_utils:get_from_ctx(<<"owner">>, Context). diff --git a/apps/wapi/src/wapi_destination_backend.erl b/apps/wapi/src/wapi_destination_backend.erl index 5bae375..a95baa8 100644 --- a/apps/wapi/src/wapi_destination_backend.erl +++ b/apps/wapi/src/wapi_destination_backend.erl @@ -14,19 +14,17 @@ %% Pipeline --import(wapi_pipeline, [do/1, unwrap/1, unwrap/2]). +-import(wapi_pipeline, [do/1, unwrap/1]). -spec create(req_data(), handler_context()) -> {ok, response_data()} | {error, DestinationError} when DestinationError :: {invalid_resource_token, binary()} - | {identity, unauthorized} | {identity, notfound} | {currency, notfound} | inaccessible | {external_id_conflict, {id(), external_id()}}. -create(Params = #{<<"identity">> := IdentityID}, HandlerContext) -> +create(Params, HandlerContext) -> do(fun() -> - unwrap(identity, wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext)), ResourceThrift = unwrap(construct_resource(maps:get(<<"resource">>, Params))), ID = unwrap(generate_id(Params, ResourceThrift, HandlerContext)), unwrap(create_request(ID, Params, ResourceThrift, HandlerContext)) @@ -83,19 +81,13 @@ create_request(ID, Params, ResourceThrift, HandlerContext) -> -spec get(id(), handler_context()) -> {ok, response_data(), id()} - | {error, {destination, notfound}} - | {error, {destination, unauthorized}}. + | {error, {destination, notfound}}. get(DestinationID, HandlerContext) -> Request = {fistful_destination, 'Get', {DestinationID, #'EventRange'{}}}, case service_call(Request, HandlerContext) of {ok, DestinationThrift} -> - case wapi_access_backend:check_resource(destination, DestinationThrift, HandlerContext) of - ok -> - {ok, Owner} = wapi_access_backend:get_resource_owner(destination, DestinationThrift), - {ok, unmarshal(destination, DestinationThrift), Owner}; - {error, unauthorized} -> - {error, {destination, unauthorized}} - end; + {ok, Owner} = wapi_backend_utils:get_entity_owner(destination, DestinationThrift), + {ok, unmarshal(destination, DestinationThrift), Owner}; {exception, #fistful_DestinationNotFound{}} -> {error, {destination, notfound}} end. @@ -103,7 +95,6 @@ get(DestinationID, HandlerContext) -> -spec get_by_external_id(external_id(), handler_context()) -> {ok, response_data(), id()} | {error, {destination, notfound}} - | {error, {destination, unauthorized}} | {error, {external_id, {unknown_external_id, external_id()}}}. get_by_external_id(ExternalID, HandlerContext = #{woody_context := WoodyContext}) -> PartyID = wapi_handler_utils:get_owner(HandlerContext), diff --git a/apps/wapi/src/wapi_identity_backend.erl b/apps/wapi/src/wapi_identity_backend.erl index 2882d13..8ab0421 100644 --- a/apps/wapi/src/wapi_identity_backend.erl +++ b/apps/wapi/src/wapi_identity_backend.erl @@ -28,12 +28,11 @@ -spec get_identity(id(), handler_context()) -> {ok, response_data(), id()} - | {error, {identity, notfound}} - | {error, {identity, unauthorized}}. + | {error, {identity, notfound}}. get_identity(IdentityID, HandlerContext) -> case get_thrift_identity(IdentityID, HandlerContext) of {ok, IdentityThrift} -> - {ok, Owner} = wapi_access_backend:get_resource_owner(identity, IdentityThrift), + {ok, Owner} = wapi_backend_utils:get_entity_owner(identity, IdentityThrift), {ok, unmarshal(identity, IdentityThrift), Owner}; {error, _} = Error -> Error @@ -86,7 +85,6 @@ get_identities(_Params, _Context) -> result( map(), {identity, notfound} - | {identity, unauthorized} | {challenge, pending} | {challenge, {class, notfound}} | {challenge, {proof, notfound}} @@ -104,86 +102,68 @@ create_identity_challenge(IdentityID, Params, HandlerContext) -> end. create_identity_challenge(ChallengeID, IdentityID, Params, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - ChallengeParams = marshal(challenge_params, {ChallengeID, Params}), - Request = {fistful_identity, 'StartChallenge', {IdentityID, ChallengeParams}}, - case service_call(Request, HandlerContext) of - {ok, Challenge} -> - {ok, unmarshal(challenge, {Challenge, HandlerContext})}; - {exception, #fistful_IdentityNotFound{}} -> - {error, {identity, notfound}}; - {exception, #fistful_ChallengePending{}} -> - {error, {challenge, pending}}; - {exception, #fistful_ChallengeClassNotFound{}} -> - {error, {challenge, {class, notfound}}}; - {exception, #fistful_ProofNotFound{}} -> - {error, {challenge, {proof, notfound}}}; - {exception, #fistful_ProofInsufficient{}} -> - {error, {challenge, {proof, insufficient}}}; - {exception, #fistful_ChallengeLevelIncorrect{}} -> - {error, {challenge, level}}; - {exception, #fistful_ChallengeConflict{}} -> - {error, {challenge, conflict}}; - {exception, Details} -> - {error, Details} - end; - {error, unauthorized} -> - {error, {identity, unauthorized}} + ChallengeParams = marshal(challenge_params, {ChallengeID, Params}), + Request = {fistful_identity, 'StartChallenge', {IdentityID, ChallengeParams}}, + case service_call(Request, HandlerContext) of + {ok, Challenge} -> + {ok, unmarshal(challenge, {Challenge, HandlerContext})}; + {exception, #fistful_IdentityNotFound{}} -> + {error, {identity, notfound}}; + {exception, #fistful_ChallengePending{}} -> + {error, {challenge, pending}}; + {exception, #fistful_ChallengeClassNotFound{}} -> + {error, {challenge, {class, notfound}}}; + {exception, #fistful_ProofNotFound{}} -> + {error, {challenge, {proof, notfound}}}; + {exception, #fistful_ProofInsufficient{}} -> + {error, {challenge, {proof, insufficient}}}; + {exception, #fistful_ChallengeLevelIncorrect{}} -> + {error, {challenge, level}}; + {exception, #fistful_ChallengeConflict{}} -> + {error, {challenge, conflict}}; + {exception, Details} -> + {error, Details} end. -spec get_identity_challenge(id(), id(), handler_context()) -> result( map(), {identity, notfound} - | {identity, unauthorized} | {challenge, notfound} ). get_identity_challenge(IdentityID, ChallengeID, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - Request = {fistful_identity, 'GetChallenges', {IdentityID}}, - case service_call(Request, HandlerContext) of - {ok, Challenges} -> - get_challenge_by_id(ChallengeID, Challenges, HandlerContext); - {exception, #fistful_IdentityNotFound{}} -> - {error, {identity, notfound}}; - {exception, Details} -> - {error, Details} - end; - {error, unauthorized} -> - {error, {identity, unauthorized}} + Request = {fistful_identity, 'GetChallenges', {IdentityID}}, + case service_call(Request, HandlerContext) of + {ok, Challenges} -> + get_challenge_by_id(ChallengeID, Challenges, HandlerContext); + {exception, #fistful_IdentityNotFound{}} -> + {error, {identity, notfound}}; + {exception, Details} -> + {error, Details} end. -spec get_identity_challenges(id(), status(), handler_context()) -> result( map(), {identity, notfound} - | {identity, unauthorized} | {challenge, notfound} ). get_identity_challenges(IdentityID, Status, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - Request = {fistful_identity, 'GetChallenges', {IdentityID}}, - case service_call(Request, HandlerContext) of - {ok, Challenges} -> - Filtered = filter_challenges_by_status(Status, Challenges, HandlerContext, []), - {ok, unmarshal({list, challenge}, Filtered)}; - {exception, #fistful_IdentityNotFound{}} -> - {error, {identity, notfound}}; - {exception, Details} -> - {error, Details} - end; - {error, unauthorized} -> - {error, {identity, unauthorized}} + Request = {fistful_identity, 'GetChallenges', {IdentityID}}, + case service_call(Request, HandlerContext) of + {ok, Challenges} -> + Filtered = filter_challenges_by_status(Status, Challenges, HandlerContext, []), + {ok, unmarshal({list, challenge}, Filtered)}; + {exception, #fistful_IdentityNotFound{}} -> + {error, {identity, notfound}}; + {exception, Details} -> + {error, Details} end. -spec get_identity_challenge_events(params(), handler_context()) -> result( map(), {identity, notfound} - | {identity, unauthorized} ). get_identity_challenge_events( Params = #{ @@ -193,45 +173,26 @@ get_identity_challenge_events( }, HandlerContext ) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - Cursor = maps:get('eventCursor', Params, undefined), - EventRange = marshal(event_range, {Cursor, Limit}), - Request = {fistful_identity, 'GetEvents', {IdentityID, EventRange}}, - case service_call(Request, HandlerContext) of - {ok, Events} -> - Filtered = filter_events_by_challenge_id(ChallengeID, Events, []), - {ok, unmarshal({list, identity_challenge_event}, Filtered)}; - {exception, #fistful_IdentityNotFound{}} -> - {error, {identity, notfound}}; - {exception, Details} -> - {error, Details} - end; - {error, unauthorized} -> - {error, {identity, unauthorized}} + Cursor = maps:get('eventCursor', Params, undefined), + EventRange = marshal(event_range, {Cursor, Limit}), + Request = {fistful_identity, 'GetEvents', {IdentityID, EventRange}}, + case service_call(Request, HandlerContext) of + {ok, Events} -> + Filtered = filter_events_by_challenge_id(ChallengeID, Events, []), + {ok, unmarshal({list, identity_challenge_event}, Filtered)}; + {exception, #fistful_IdentityNotFound{}} -> + {error, {identity, notfound}}; + {exception, Details} -> + {error, Details} end. -spec get_identity_challenge_event(params(), handler_context()) -> result( map(), {identity, notfound} - | {identity, unauthorized} | {event, notfound} ). get_identity_challenge_event( - Params = #{ - 'identityID' := IdentityID - }, - HandlerContext -) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - get_identity_challenge_event_(Params, HandlerContext); - {error, unauthorized} -> - {error, {identity, unauthorized}} - end. - -get_identity_challenge_event_( #{ 'identityID' := IdentityID, 'challengeID' := ChallengeID, @@ -259,18 +220,12 @@ get_identity_challenge_event_( -spec get_thrift_identity(id(), handler_context()) -> {ok, identity_state()} - | {error, {identity, notfound}} - | {error, {identity, unauthorized}}. + | {error, {identity, notfound}}. get_thrift_identity(IdentityID, HandlerContext) -> Request = {fistful_identity, 'Get', {IdentityID, #'EventRange'{}}}, case service_call(Request, HandlerContext) of {ok, IdentityThrift} -> - case wapi_access_backend:check_resource(identity, IdentityThrift, HandlerContext) of - ok -> - {ok, IdentityThrift}; - {error, unauthorized} -> - {error, {identity, unauthorized}} - end; + {ok, IdentityThrift}; {exception, #fistful_IdentityNotFound{}} -> {error, {identity, notfound}} end. diff --git a/apps/wapi/src/wapi_report_backend.erl b/apps/wapi/src/wapi_report_backend.erl index 1a9417a..8c9a9f2 100644 --- a/apps/wapi/src/wapi_report_backend.erl +++ b/apps/wapi/src/wapi_report_backend.erl @@ -17,8 +17,7 @@ -spec create_report(req_data(), handler_context()) -> {ok, response_data()} | {error, Error} when Error :: - {identity, unauthorized} - | {identity, notfound} + {identity, notfound} | invalid_request | invalid_contract. create_report( @@ -51,8 +50,7 @@ create_report( -spec get_report(integer(), binary(), handler_context()) -> {ok, response_data()} | {error, Error} when Error :: - {identity, unauthorized} - | {identity, notfound} + {identity, notfound} | notfound. get_report(ReportID, IdentityID, HandlerContext) -> get_report(identityID, ReportID, IdentityID, HandlerContext). @@ -76,8 +74,7 @@ get_report(contractID, ReportID, ContractID, HandlerContext) -> -spec get_reports(req_data(), handler_context()) -> {ok, response_data()} | {error, Error} when Error :: - {identity, unauthorized} - | {identity, notfound} + {identity, notfound} | invalid_request | {dataset_too_big, integer()}. get_reports(#{identityID := IdentityID} = Params, HandlerContext) -> @@ -118,9 +115,7 @@ download_file(FileID, ExpiresAt, HandlerContext) -> %% Internal -spec get_contract_id_from_identity(id(), handler_context()) -> {ok, id()} | {error, Error} when - Error :: - {identity, unauthorized} - | {identity, notfound}. + Error :: {identity, notfound}. get_contract_id_from_identity(IdentityID, HandlerContext) -> case wapi_identity_backend:get_thrift_identity(IdentityID, HandlerContext) of {ok, #idnt_IdentityState{contract_id = ContractID}} -> diff --git a/apps/wapi/src/wapi_w2w_backend.erl b/apps/wapi/src/wapi_w2w_backend.erl index c022472..f8a32f8 100644 --- a/apps/wapi/src/wapi_w2w_backend.erl +++ b/apps/wapi/src/wapi_w2w_backend.erl @@ -15,23 +15,17 @@ -spec create_transfer(req_data(), handler_context()) -> {ok, response_data()} | {error, CreateError} when CreateError :: {external_id_conflict, external_id()} - | {wallet_from, unauthorized} | {wallet_from | wallet_to, notfound | inaccessible} | bad_w2w_transfer_amount | not_allowed_currency | inconsistent_currency. -create_transfer(Params = #{<<"sender">> := SenderID}, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(wallet, SenderID, HandlerContext) of - ok -> - case wapi_backend_utils:gen_id(w2w_transfer, Params, HandlerContext) of - {ok, ID} -> - Context = wapi_backend_utils:make_ctx(Params, HandlerContext), - create_transfer(ID, Params, Context, HandlerContext); - {error, {external_id_conflict, _}} = Error -> - Error - end; - {error, unauthorized} -> - {error, {wallet_from, unauthorized}} +create_transfer(Params, HandlerContext) -> + case wapi_backend_utils:gen_id(w2w_transfer, Params, HandlerContext) of + {ok, ID} -> + Context = wapi_backend_utils:make_ctx(Params, HandlerContext), + create_transfer(ID, Params, Context, HandlerContext); + {error, {external_id_conflict, _}} = Error -> + Error end. create_transfer(ID, Params, Context, HandlerContext) -> @@ -53,21 +47,14 @@ create_transfer(ID, Params, Context, HandlerContext) -> end. -spec get_transfer(id(), handler_context()) -> {ok, response_data(), id()} | {error, GetError} when - GetError :: - {w2w_transfer, unauthorized} - | {w2w_transfer, {unknown_w2w_transfer, id()}}. + GetError :: {w2w_transfer, {unknown_w2w_transfer, id()}}. get_transfer(ID, HandlerContext) -> EventRange = #'EventRange'{}, Request = {fistful_w2w_transfer, 'Get', {ID, EventRange}}, case service_call(Request, HandlerContext) of {ok, TransferThrift} -> - case wapi_access_backend:check_resource(w2w_transfer, TransferThrift, HandlerContext) of - ok -> - {ok, Owner} = wapi_access_backend:get_resource_owner(w2w_transfer, TransferThrift), - {ok, unmarshal(transfer, TransferThrift), Owner}; - {error, unauthorized} -> - {error, {w2w_transfer, unauthorized}} - end; + {ok, Owner} = wapi_backend_utils:get_entity_owner(w2w_transfer, TransferThrift), + {ok, unmarshal(transfer, TransferThrift), Owner}; {exception, #fistful_W2WNotFound{}} -> {error, {w2w_transfer, {unknown_w2w_transfer, ID}}} end. diff --git a/apps/wapi/src/wapi_wallet_backend.erl b/apps/wapi/src/wapi_wallet_backend.erl index 8c67d0e..b2245ea 100644 --- a/apps/wapi/src/wapi_wallet_backend.erl +++ b/apps/wapi/src/wapi_wallet_backend.erl @@ -17,25 +17,17 @@ -spec create(req_data(), handler_context()) -> {ok, response_data()} | {error, WalletError} when WalletError :: - {identity, unauthorized} - | {identity, notfound} + {identity, notfound} | {currency, notfound} | inaccessible | {external_id_conflict, id()}. -create(Params = #{<<"identity">> := IdentityID}, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - case wapi_backend_utils:gen_id(wallet, Params, HandlerContext) of - {ok, ID} -> - Context = wapi_backend_utils:make_ctx(Params, HandlerContext), - create(ID, Params, Context, HandlerContext); - {error, {external_id_conflict, _}} = Error -> - Error - end; - {error, unauthorized} -> - {error, {identity, unauthorized}}; - {error, notfound} -> - {error, {identity, notfound}} +create(Params, HandlerContext) -> + case wapi_backend_utils:gen_id(wallet, Params, HandlerContext) of + {ok, ID} -> + Context = wapi_backend_utils:make_ctx(Params, HandlerContext), + create(ID, Params, Context, HandlerContext); + {error, {external_id_conflict, _}} = Error -> + Error end. create(WalletID, Params, Context, HandlerContext) -> @@ -57,7 +49,6 @@ create(WalletID, Params, Context, HandlerContext) -> -spec get_by_external_id(external_id(), handler_context()) -> {ok, response_data(), id()} | {error, {wallet, notfound}} - | {error, {wallet, unauthorized}} | {error, {external_id, {unknown_external_id, external_id()}}}. get_by_external_id(ExternalID, #{woody_context := WoodyContext} = HandlerContext) -> PartyID = wapi_handler_utils:get_owner(HandlerContext), @@ -71,40 +62,26 @@ get_by_external_id(ExternalID, #{woody_context := WoodyContext} = HandlerContext -spec get(id(), handler_context()) -> {ok, response_data(), id()} - | {error, {wallet, notfound}} - | {error, {wallet, unauthorized}}. + | {error, {wallet, notfound}}. get(WalletID, HandlerContext) -> Request = {fistful_wallet, 'Get', {WalletID, #'EventRange'{}}}, case service_call(Request, HandlerContext) of {ok, WalletThrift} -> - case wapi_access_backend:check_resource(wallet, WalletThrift, HandlerContext) of - ok -> - {ok, Owner} = wapi_access_backend:get_resource_owner(wallet, WalletThrift), - {ok, unmarshal(wallet, WalletThrift), Owner}; - {error, unauthorized} -> - {error, {wallet, unauthorized}} - end; + {ok, Owner} = wapi_backend_utils:get_entity_owner(wallet, WalletThrift), + {ok, unmarshal(wallet, WalletThrift), Owner}; {exception, #fistful_WalletNotFound{}} -> {error, {wallet, notfound}} end. -spec get_account(id(), handler_context()) -> {ok, response_data()} - | {error, {wallet, notfound}} - | {error, {wallet, unauthorized}}. + | {error, {wallet, notfound}}. get_account(WalletID, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(wallet, WalletID, HandlerContext) of - ok -> - Request = {fistful_wallet, 'GetAccountBalance', {WalletID}}, - case service_call(Request, HandlerContext) of - {ok, AccountBalanceThrift} -> - {ok, unmarshal(wallet_account_balance, AccountBalanceThrift)}; - {exception, #fistful_WalletNotFound{}} -> - {error, {wallet, notfound}} - end; - {error, unauthorized} -> - {error, {wallet, unauthorized}}; - {error, notfound} -> + Request = {fistful_wallet, 'GetAccountBalance', {WalletID}}, + case service_call(Request, HandlerContext) of + {ok, AccountBalanceThrift} -> + {ok, unmarshal(wallet_account_balance, AccountBalanceThrift)}; + {exception, #fistful_WalletNotFound{}} -> {error, {wallet, notfound}} end. diff --git a/apps/wapi/src/wapi_wallet_handler.erl b/apps/wapi/src/wapi_wallet_handler.erl index 2b380cb..7ba2913 100644 --- a/apps/wapi/src/wapi_wallet_handler.erl +++ b/apps/wapi/src/wapi_wallet_handler.erl @@ -196,8 +196,7 @@ prepare(OperationID = 'GetIdentity', Req = #{'identityID' := IdentityId}, Contex {ResultIdentity, ResultOwner} = case wapi_identity_backend:get_identity(IdentityId, Context) of {ok, Identity, Owner} -> {Identity, Owner}; - {error, {identity, notfound}} -> {undefined, undefined}; - {error, {identity, unauthorized}} -> {undefined, undefined} + {error, {identity, notfound}} -> {undefined, undefined} end, Authorize = fun() -> Prototypes = [ @@ -209,6 +208,7 @@ prepare(OperationID = 'GetIdentity', Req = #{'identityID' := IdentityId}, Contex end, Process = fun() -> wapi_handler:respond_if_undefined(ResultIdentity, wapi_handler_utils:reply_ok(404)), + respond_if_not_owned(OperationID, ResultOwner, Context, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultIdentity) end, {ok, #{authorize => Authorize, process => Process}}; @@ -266,10 +266,10 @@ prepare( end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_identity_backend:get_identity_challenges(IdentityId, Status, Context) of {ok, Challenges} -> wapi_handler_utils:reply_ok(200, Challenges); - {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404); - {error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404) + {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404) end end, {ok, #{authorize => Authorize, process => Process}}; @@ -293,13 +293,12 @@ prepare( end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_identity_backend:create_identity_challenge(IdentityId, Params, Context) of {ok, Challenge = #{<<"id">> := ChallengeId}} -> wapi_handler_utils:reply_ok(202, Challenge, get_location('GetIdentityChallenge', [ChallengeId], Opts)); {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(404); {error, {challenge, conflict}} -> wapi_handler_utils:reply_ok(409); {error, {external_id_conflict, ID}} -> @@ -341,10 +340,10 @@ prepare( end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_identity_backend:get_identity_challenge(IdentityId, ChallengeId, Context) of {ok, Challenge} -> wapi_handler_utils:reply_ok(200, Challenge); {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404); - {error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404); {error, {challenge, notfound}} -> wapi_handler_utils:reply_ok(404) end end, @@ -361,10 +360,10 @@ prepare(OperationID = 'PollIdentityChallengeEvents', Req = #{'identityID' := Ide end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_identity_backend:get_identity_challenge_events(Req, Context) of {ok, Events} -> wapi_handler_utils:reply_ok(200, Events); - {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404); - {error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404) + {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404) end end, {ok, #{authorize => Authorize, process => Process}}; @@ -380,10 +379,10 @@ prepare(OperationID = 'GetIdentityChallengeEvent', Req = #{'identityID' := Ident end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_identity_backend:get_identity_challenge_event(Req, Context) of {ok, Event} -> wapi_handler_utils:reply_ok(200, Event); {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(404); - {error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(404); {error, {event, notfound}} -> wapi_handler_utils:reply_ok(404) end end, @@ -405,6 +404,7 @@ prepare(OperationID = 'ListWallets', Req, Context, _Opts) -> end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_stat_backend:list_wallets(Req, Context) of {ok, List} -> wapi_handler_utils:reply_ok(200, List); @@ -425,8 +425,7 @@ prepare(OperationID = 'GetWallet', Req = #{'walletID' := WalletId}, Context, _Op {ResultWallet, ResultWalletOwner} = case wapi_wallet_backend:get(WalletId, Context) of {ok, Wallet, Owner} -> {Wallet, Owner}; - {error, {wallet, notfound}} -> {undefined, undefined}; - {error, {wallet, unauthorized}} -> {undefined, undefined} + {error, {wallet, notfound}} -> {undefined, undefined} end, Authorize = fun() -> Prototypes = [ @@ -438,6 +437,7 @@ prepare(OperationID = 'GetWallet', Req = #{'walletID' := WalletId}, Context, _Op end, Process = fun() -> wapi_handler:respond_if_undefined(ResultWallet, wapi_handler_utils:reply_ok(404)), + respond_if_not_owned(OperationID, ResultWalletOwner, Context, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultWallet) end, {ok, #{authorize => Authorize, process => Process}}; @@ -446,7 +446,6 @@ prepare(OperationID = 'GetWalletByExternalID', Req = #{externalID := ExternalID} case wapi_wallet_backend:get_by_external_id(ExternalID, Context) of {ok, Wallet = #{<<"id">> := ID}, Owner} -> {Wallet, Owner, ID}; {error, {wallet, notfound}} -> {undefined, undefined, undefined}; - {error, {wallet, unauthorized}} -> {undefined, undefined, undefined}; {error, {external_id, {unknown_external_id, ExternalID}}} -> {undefined, undefined, undefined} end, Authorize = fun() -> @@ -462,6 +461,7 @@ prepare(OperationID = 'GetWalletByExternalID', Req = #{externalID := ExternalID} end, Process = fun() -> wapi_handler:respond_if_undefined(ResultWallet, wapi_handler_utils:reply_ok(404)), + respond_if_not_owned(OperationID, ResultWalletOwner, Context, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultWallet) end, {ok, #{authorize => Authorize, process => Process}}; @@ -477,11 +477,18 @@ prepare(OperationID = 'CreateWallet', Req = #{'Wallet' := Params = #{<<"identity end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok( + 422, + wapi_handler_utils:get_error_msg(<<"No such identity">>) + ) + ), case wapi_wallet_backend:create(Params, Context) of {ok, Wallet = #{<<"id">> := WalletId}} -> wapi_handler_utils:reply_ok(201, Wallet, get_location('GetWallet', [WalletId], Opts)); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); {error, {currency, notfound}} -> @@ -505,10 +512,10 @@ prepare(OperationID = 'GetWalletAccount', Req = #{'walletID' := WalletId}, Conte end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_wallet_backend:get_account(WalletId, Context) of {ok, WalletAccount} -> wapi_handler_utils:reply_ok(200, WalletAccount); - {error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404); - {error, {wallet, unauthorized}} -> wapi_handler_utils:reply_ok(404) + {error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(404) end end, {ok, #{authorize => Authorize, process => Process}}; @@ -532,6 +539,7 @@ prepare( end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_backend_utils:issue_grant_token({wallets, WalletId, Asset}, Expiration, Context) of {ok, Token} -> wapi_handler_utils:reply_ok(201, #{ @@ -564,6 +572,7 @@ prepare(OperationID = 'ListDestinations', Req, Context, _Opts) -> end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_stat_backend:list_destinations(Req, Context) of {ok, StatResult} -> wapi_handler_utils:reply_ok(200, StatResult); @@ -584,8 +593,7 @@ prepare(OperationID = 'GetDestination', Req = #{'destinationID' := DestinationId {ResultDestination, ResultDestinationOwner} = case wapi_destination_backend:get(DestinationId, Context) of {ok, Destination, Owner} -> {Destination, Owner}; - {error, {destination, notfound}} -> {undefined, undefined}; - {error, {destination, unauthorized}} -> {undefined, undefined} + {error, {destination, notfound}} -> {undefined, undefined} end, Authorize = fun() -> Prototypes = [ @@ -606,6 +614,7 @@ prepare(OperationID = 'GetDestination', Req = #{'destinationID' := DestinationId end, Process = fun() -> wapi_handler:respond_if_undefined(ResultDestination, wapi_handler_utils:reply_ok(404)), + respond_if_not_owned(OperationID, ResultDestinationOwner, Context, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultDestination) end, {ok, #{authorize => Authorize, process => Process}}; @@ -614,7 +623,6 @@ prepare(OperationID = 'GetDestinationByExternalID', Req = #{'externalID' := Exte case wapi_destination_backend:get_by_external_id(ExternalID, Context) of {ok, Wallet = #{<<"id">> := ID}, Owner} -> {Wallet, Owner, ID}; {error, {destination, notfound}} -> {undefined, undefined, undefined}; - {error, {destination, unauthorized}} -> {undefined, undefined, undefined}; {error, {external_id, {unknown_external_id, ExternalID}}} -> {undefined, undefined, undefined} end, Authorize = fun() -> @@ -636,6 +644,7 @@ prepare(OperationID = 'GetDestinationByExternalID', Req = #{'externalID' := Exte end, Process = fun() -> wapi_handler:respond_if_undefined(ResultDestination, wapi_handler_utils:reply_ok(404)), + respond_if_not_owned(OperationID, ResultDestinationOwner, Context, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultDestination) end, {ok, #{authorize => Authorize, process => Process}}; @@ -656,11 +665,18 @@ prepare( end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok( + 422, + wapi_handler_utils:get_error_msg(<<"No such identity">>) + ) + ), case wapi_destination_backend:create(Params, Context) of {ok, Destination = #{<<"id">> := DestinationId}} -> wapi_handler_utils:reply_ok(201, Destination, get_location('GetDestination', [DestinationId], Opts)); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); {error, {identity, notfound}} -> wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); {error, {currency, notfound}} -> @@ -698,6 +714,7 @@ prepare( end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case issue_grant_token({destinations, DestinationId}, Expiration, Context) of {ok, Token} -> wapi_handler_utils:reply_ok(201, #{ @@ -734,6 +751,8 @@ prepare(OperationID = 'CreateQuote', Req = #{'WithdrawalQuoteParams' := Params}, end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + %% NOTE: This means grants are non-functional, fix ASAP + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(422)), case wapi_withdrawal_backend:create_quote(Req, Context) of {ok, Quote} -> wapi_handler_utils:reply_ok(202, Quote); @@ -743,8 +762,6 @@ prepare(OperationID = 'CreateQuote', Req = #{'WithdrawalQuoteParams' := Params}, wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Destination unauthorized">>)); {error, {wallet, notfound}} -> wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>)); - {error, {wallet, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Wallet unauthorized">>)); {error, {forbidden_currency, _}} -> wapi_handler_utils:reply_ok( 422, @@ -799,6 +816,8 @@ prepare(OperationID = 'CreateWithdrawal', Req = #{'WithdrawalParameters' := Para end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + %% NOTE: This means grants are non-functional, fix ASAP + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(422)), case wapi_withdrawal_backend:create(Params, Context) of {ok, Withdrawal = #{<<"id">> := WithdrawalId}} -> wapi_handler_utils:reply_ok(202, Withdrawal, get_location('GetWithdrawal', [WithdrawalId], Opts)); @@ -813,8 +832,6 @@ prepare(OperationID = 'CreateWithdrawal', Req = #{'WithdrawalParameters' := Para wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>)); {error, {wallet, {inaccessible, _}}} -> wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Wallet inaccessible">>)); - {error, {wallet, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"Wallet unauthorized">>)); {error, {quote_invalid_party, _}} -> wapi_handler_utils:reply_ok( 422, @@ -874,8 +891,7 @@ prepare(OperationID = 'GetWithdrawal', Req = #{'withdrawalID' := WithdrawalId}, {ResultWithdrawal, ResultWithdrawalOwner} = case wapi_withdrawal_backend:get(WithdrawalId, Context) of {ok, Withdrawal, Owner} -> {Withdrawal, Owner}; - {error, {withdrawal, notfound}} -> {undefined, undefined}; - {error, {withdrawal, unauthorized}} -> {undefined, undefined} + {error, {withdrawal, notfound}} -> {undefined, undefined} end, Authorize = fun() -> Prototypes = [ @@ -892,6 +908,7 @@ prepare(OperationID = 'GetWithdrawal', Req = #{'withdrawalID' := WithdrawalId}, end, Process = fun() -> wapi_handler:respond_if_undefined(ResultWithdrawal, wapi_handler_utils:reply_ok(404)), + respond_if_not_owned(OperationID, ResultWithdrawalOwner, Context, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultWithdrawal) end, {ok, #{authorize => Authorize, process => Process}}; @@ -900,7 +917,6 @@ prepare(OperationID = 'GetWithdrawalByExternalID', Req = #{'externalID' := Exter case wapi_withdrawal_backend:get_by_external_id(ExternalID, Context) of {ok, Wallet = #{<<"id">> := ID}, Owner} -> {Wallet, Owner, ID}; {error, {withdrawal, notfound}} -> {undefined, undefined, undefined}; - {error, {withdrawal, unauthorized}} -> {undefined, undefined, undefined}; {error, {external_id, {unknown_external_id, ExternalID}}} -> {undefined, undefined, undefined} end, Authorize = fun() -> @@ -918,6 +934,7 @@ prepare(OperationID = 'GetWithdrawalByExternalID', Req = #{'externalID' := Exter end, Process = fun() -> wapi_handler:respond_if_undefined(ResultWithdrawal, wapi_handler_utils:reply_ok(404)), + respond_if_not_owned(OperationID, ResultWithdrawalOwner, Context, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultWithdrawal) end, {ok, #{authorize => Authorize, process => Process}}; @@ -942,6 +959,7 @@ prepare(OperationID = 'ListWithdrawals', Req, Context, _Opts) -> end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_stat_backend:list_withdrawals(Req, Context) of {ok, List} -> wapi_handler_utils:reply_ok(200, List); @@ -970,12 +988,11 @@ prepare(OperationID = 'PollWithdrawalEvents', Req = #{'withdrawalID' := Withdraw end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_withdrawal_backend:get_events(Req, Context) of {ok, Events} -> wapi_handler_utils:reply_ok(200, Events); {error, {withdrawal, notfound}} -> - wapi_handler_utils:reply_ok(404); - {error, {withdrawal, unauthorized}} -> wapi_handler_utils:reply_ok(404) end end, @@ -1000,13 +1017,12 @@ prepare( end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_withdrawal_backend:get_event(WithdrawalId, EventId, Context) of {ok, Event} -> wapi_handler_utils:reply_ok(200, Event); {error, {withdrawal, notfound}} -> wapi_handler_utils:reply_ok(404); - {error, {withdrawal, unauthorized}} -> - wapi_handler_utils:reply_ok(404); {error, {event, notfound}} -> wapi_handler_utils:reply_ok(404) end @@ -1032,6 +1048,7 @@ prepare(OperationID = 'ListDeposits', Req, Context, _Opts) -> end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_stat_backend:list_deposits(Req, Context) of {ok, List} -> wapi_handler_utils:reply_ok(200, List); @@ -1067,6 +1084,7 @@ prepare(OperationID = 'ListDepositReverts', Req, Context, _Opts) -> end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_stat_backend:list_deposit_reverts(Req, Context) of {ok, List} -> wapi_handler_utils:reply_ok(200, List); @@ -1102,6 +1120,7 @@ prepare(OperationID = 'ListDepositAdjustments', Req, Context, _Opts) -> end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(404)), case wapi_stat_backend:list_deposit_adjustments(Req, Context) of {ok, List} -> wapi_handler_utils:reply_ok(200, List); @@ -1142,6 +1161,15 @@ prepare( wapi_handler_utils:get_error_msg(<<"No such wallet sender">>) ) ), + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok( + 422, + wapi_handler_utils:get_error_msg(<<"No such wallet sender">>) + ) + ), case wapi_w2w_backend:create_transfer(Params, Context) of {ok, W2WTransfer} -> wapi_handler_utils:reply_ok(202, W2WTransfer); @@ -1150,11 +1178,6 @@ prepare( 422, wapi_handler_utils:get_error_msg(<<"No such wallet sender">>) ); - {error, {wallet_from, unauthorized}} -> - wapi_handler_utils:reply_ok( - 422, - wapi_handler_utils:get_error_msg(<<"No such wallet sender">>) - ); {error, {wallet_from, inaccessible}} -> wapi_handler_utils:reply_ok( 422, @@ -1192,8 +1215,7 @@ prepare(OperationID = 'GetW2WTransfer', Req = #{w2wTransferID := W2WTransferId}, {ResultW2WTransfer, ResultW2WTransferOwner} = case wapi_w2w_backend:get_transfer(W2WTransferId, Context) of {ok, W2WTransfer, Owner} -> {W2WTransfer, Owner}; - {error, {w2w_transfer, {unknown_w2w_transfer, _ID}}} -> {undefined, undefined}; - {error, {w2w_transfer, unauthorized}} -> {undefined, undefined} + {error, {w2w_transfer, {unknown_w2w_transfer, _ID}}} -> {undefined, undefined} end, Authorize = fun() -> Prototypes = [ @@ -1214,6 +1236,7 @@ prepare(OperationID = 'GetW2WTransfer', Req = #{w2wTransferID := W2WTransferId}, end, Process = fun() -> wapi_handler:respond_if_undefined(ResultW2WTransfer, wapi_handler_utils:reply_ok(404)), + respond_if_not_owned(OperationID, ResultW2WTransferOwner, Context, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultW2WTransfer) end, {ok, #{authorize => Authorize, process => Process}}; @@ -1242,17 +1265,10 @@ prepare( end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context(OperationID, AuthContext, Context, wapi_handler_utils:reply_ok(422)), case wapi_webhook_backend:create_webhook(Req, Context) of {ok, Webhook} -> - wapi_handler_utils:reply_ok(201, Webhook); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); - {error, {identity, notfound}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); - {error, {wallet, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>)); - {error, {wallet, notfound}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such wallet">>)) + wapi_handler_utils:reply_ok(201, Webhook) end end, {ok, #{authorize => Authorize, process => Process}}; @@ -1268,13 +1284,18 @@ prepare(OperationID = 'GetWebhooks', Req = #{identityID := IdentityID}, Context, end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok( + 422, + wapi_handler_utils:get_error_msg(<<"No such identity">>) + ) + ), case wapi_webhook_backend:get_webhooks(IdentityID, Context) of {ok, Webhooks} -> - wapi_handler_utils:reply_ok(200, Webhooks); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); - {error, {identity, notfound}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)) + wapi_handler_utils:reply_ok(200, Webhooks) end end, {ok, #{authorize => Authorize, process => Process}}; @@ -1282,7 +1303,7 @@ prepare(OperationID = 'GetWebhookByID', Req = #{identityID := IdentityID, webhoo AuthContext = build_auth_context( [ {identity, IdentityID}, - {webhook, WebhookID, IdentityID} + {webhook, WebhookID} ], [], Context @@ -1297,15 +1318,20 @@ prepare(OperationID = 'GetWebhookByID', Req = #{identityID := IdentityID, webhoo end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), - case wapi_webhook_backend:get_webhook(WebhookID, IdentityID, Context) of + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok( + 422, + wapi_handler_utils:get_error_msg(<<"No such identity">>) + ) + ), + case wapi_webhook_backend:get_webhook(WebhookID, Context) of {ok, Webhook} -> wapi_handler_utils:reply_ok(200, Webhook); {error, notfound} -> - wapi_handler_utils:reply_ok(404); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); - {error, {identity, notfound}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)) + wapi_handler_utils:reply_ok(404) end end, {ok, #{authorize => Authorize, process => Process}}; @@ -1313,7 +1339,7 @@ prepare(OperationID = 'DeleteWebhookByID', Req = #{identityID := IdentityID, web AuthContext = build_auth_context( [ {identity, IdentityID}, - {webhook, WebhookID, IdentityID} + {webhook, WebhookID} ], [], Context @@ -1328,15 +1354,20 @@ prepare(OperationID = 'DeleteWebhookByID', Req = #{identityID := IdentityID, web end, Process = fun() -> respond_if_any_undefined_in_auth_context(AuthContext, wapi_handler_utils:reply_ok(404)), - case wapi_webhook_backend:delete_webhook(WebhookID, IdentityID, Context) of + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok( + 422, + wapi_handler_utils:get_error_msg(<<"No such identity">>) + ) + ), + case wapi_webhook_backend:delete_webhook(WebhookID, Context) of ok -> wapi_handler_utils:reply_ok(204); {error, notfound} -> - wapi_handler_utils:reply_ok(404); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)); - {error, {identity, notfound}} -> - wapi_handler_utils:reply_ok(422, wapi_handler_utils:get_error_msg(<<"No such identity">>)) + wapi_handler_utils:reply_ok(404) end end, {ok, #{authorize => Authorize, process => Process}}; @@ -1360,6 +1391,16 @@ prepare(OperationID = 'CreateReport', Req = #{identityID := IdentityID}, Context <<"description">> => <<"identity not found">> }) ), + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok(400, #{ + <<"errorType">> => <<"NotFound">>, + <<"name">> => <<"identity">>, + <<"description">> => <<"identity not found">> + }) + ), case wapi_report_backend:create_report(Req, Context) of {ok, Report} -> wapi_handler_utils:reply_ok(201, Report); @@ -1369,12 +1410,6 @@ prepare(OperationID = 'CreateReport', Req = #{identityID := IdentityID}, Context <<"name">> => <<"identity">>, <<"description">> => <<"identity not found">> }); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(400, #{ - <<"errorType">> => <<"NotFound">>, - <<"name">> => <<"identity">>, - <<"description">> => <<"identity not found">> - }); {error, invalid_request} -> wapi_handler_utils:reply_ok(400, #{ <<"errorType">> => <<"NoMatch">>, @@ -1406,8 +1441,6 @@ prepare( Report; {error, {identity, notfound}} -> undefined; - {error, {identity, unauthorized}} -> - undefined; {error, notfound} -> undefined end, @@ -1433,6 +1466,16 @@ prepare( <<"description">> => <<"identity not found">> }) ), + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok(400, #{ + <<"errorType">> => <<"NotFound">>, + <<"name">> => <<"identity">>, + <<"description">> => <<"identity not found">> + }) + ), wapi_handler:respond_if_undefined(ResultReport, wapi_handler_utils:reply_ok(404)), wapi_handler_utils:reply_ok(200, ResultReport) end, @@ -1456,6 +1499,16 @@ prepare(OperationID = 'GetReports', Req = #{identityID := IdentityID}, Context, <<"description">> => <<"identity not found">> }) ), + respond_if_any_misowned_in_auth_context( + OperationID, + AuthContext, + Context, + wapi_handler_utils:reply_ok(400, #{ + <<"errorType">> => <<"NotFound">>, + <<"name">> => <<"identity">>, + <<"description">> => <<"identity not found">> + }) + ), case wapi_report_backend:get_reports(Req, Context) of {ok, ReportList} -> wapi_handler_utils:reply_ok(200, ReportList); @@ -1465,12 +1518,6 @@ prepare(OperationID = 'GetReports', Req = #{identityID := IdentityID}, Context, <<"name">> => <<"identity">>, <<"description">> => <<"identity not found">> }); - {error, {identity, unauthorized}} -> - wapi_handler_utils:reply_ok(400, #{ - <<"errorType">> => <<"NotFound">>, - <<"name">> => <<"identity">>, - <<"description">> => <<"identity not found">> - }); {error, invalid_request} -> wapi_handler_utils:reply_ok(400, #{ <<"errorType">> => <<"NoMatch">>, @@ -1566,42 +1613,35 @@ build_auth_context({identity, IdentityID}, Context) -> {ResultIdentity, ResultIdentityOwner} = case wapi_identity_backend:get_identity(IdentityID, Context) of {ok, Identity, Owner} -> {Identity, Owner}; - {error, {identity, notfound}} -> {undefined, undefined}; - {error, {identity, unauthorized}} -> {undefined, undefined} + {error, {identity, notfound}} -> {undefined, undefined} end, {identity, {IdentityID, ResultIdentity, ResultIdentityOwner}}; build_auth_context({wallet, WalletID}, Context) -> {ResultWallet, ResultWalletOwner} = case wapi_wallet_backend:get(WalletID, Context) of {ok, Wallet, Owner} -> {Wallet, Owner}; - {error, {wallet, notfound}} -> {undefined, undefined}; - {error, {wallet, unauthorized}} -> {undefined, undefined} + {error, {wallet, notfound}} -> {undefined, undefined} end, {wallet, {WalletID, ResultWallet, ResultWalletOwner}}; build_auth_context({destination, DestinationID}, Context) -> {ResultDestination, ResultDestinationOwner} = case wapi_destination_backend:get(DestinationID, Context) of {ok, Destination, Owner} -> {Destination, Owner}; - {error, {destination, notfound}} -> {undefined, undefined}; - {error, {destination, unauthorized}} -> {undefined, undefined} + {error, {destination, notfound}} -> {undefined, undefined} end, {destination, {DestinationID, ResultDestination, ResultDestinationOwner}}; build_auth_context({withdrawal, WithdrawalId}, Context) -> {ResultWithdrawal, ResultWithdrawalOwner} = case wapi_withdrawal_backend:get(WithdrawalId, Context) of {ok, Withdrawal, Owner} -> {Withdrawal, Owner}; - {error, {withdrawal, notfound}} -> {undefined, undefined}; - {error, {withdrawal, unauthorized}} -> {undefined, undefined} + {error, {withdrawal, notfound}} -> {undefined, undefined} end, {withdrawal, {WithdrawalId, ResultWithdrawal, ResultWithdrawalOwner}}; -build_auth_context({webhook, WebhookId, IdentityID}, Context) -> +build_auth_context({webhook, WebhookId}, Context) -> ResultWebhook = - case wapi_webhook_backend:get_webhook(WebhookId, IdentityID, Context) of + case wapi_webhook_backend:get_webhook(WebhookId, Context) of {ok, Webhook} -> Webhook; - {error, notfound} -> {undefined, undefined}; - {error, {webhook, notfound}} -> {undefined, undefined}; - {error, {identity, notfound}} -> {undefined, undefined}; - {error, {identity, unauthorized}} -> {undefined, undefined} + {error, notfound} -> undefined end, {webhook, {WebhookId, ResultWebhook}}. @@ -1657,6 +1697,39 @@ respond_if_any_undefined_in_auth_context(AuthContext, Respond) -> ok end. +%% @TODO: following 2 functions are to be removed when legacy auth is removed +respond_if_any_misowned_in_auth_context(OperationID, AuthContext, HandlerContext, Respond) -> + Owner = wapi_handler_utils:get_owner(HandlerContext), + case + lists:all( + fun + ({_Type, {_, _, EntityOwner}}) -> EntityOwner =:= Owner; + ({_Type, {_, _}}) -> true; + (_) -> false + end, + AuthContext + ) + of + true -> + ok; + false -> + _ = logger:warning("Bouncer allowed operation ~p, but some entities are not owned by acting party", [ + OperationID + ]), + wapi_handler:respond_if_undefined(undefined, Respond) + end. + +respond_if_not_owned(OperationID, Owner, HandlerContext, Respond) -> + case Owner =:= wapi_handler_utils:get_owner(HandlerContext) of + true -> + ok; + false -> + _ = logger:warning("Bouncer allowed operation ~p, but some entities are not owned by acting party", [ + OperationID + ]), + wapi_handler:respond_if_undefined(undefined, Respond) + end. + % seconds -define(DEFAULT_URL_LIFETIME, 60). diff --git a/apps/wapi/src/wapi_webhook_backend.erl b/apps/wapi/src/wapi_webhook_backend.erl index fc6ad39..7df1021 100644 --- a/apps/wapi/src/wapi_webhook_backend.erl +++ b/apps/wapi/src/wapi_webhook_backend.erl @@ -2,8 +2,8 @@ -export([create_webhook/2]). -export([get_webhooks/2]). --export([get_webhook/3]). --export([delete_webhook/3]). +-export([get_webhook/2]). +-export([delete_webhook/2]). -type id() :: binary() | undefined. -type ctx() :: wapi_handler:context(). @@ -13,83 +13,43 @@ -include_lib("fistful_proto/include/ff_proto_webhooker_thrift.hrl"). --spec create_webhook(req_data(), handler_context()) -> {ok, response_data()} | {error, CreateError} when - CreateError :: - {identity, notfound} - | {identity, unauthorized} - | {wallet, notfound} - | {wallet, unauthorized}. +-spec create_webhook(req_data(), handler_context()) -> {ok, response_data()}. create_webhook(#{'Webhook' := Params}, HandlerContext) -> WebhookParams = marshal_webhook_params(Params), - IdentityID = WebhookParams#webhooker_WebhookParams.identity_id, - WalletID = WebhookParams#webhooker_WebhookParams.wallet_id, - case check_wallet(WalletID, HandlerContext) of - ok -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - Call = {webhook_manager, 'Create', {WebhookParams}}, - Result = wapi_handler_utils:service_call(Call, HandlerContext), - process_create_webhook_result(Result); - {error, Error} -> - {error, {identity, Error}} - end; - {error, Error} -> - {error, {wallet, Error}} - end. + Call = {webhook_manager, 'Create', {WebhookParams}}, + Result = wapi_handler_utils:service_call(Call, HandlerContext), + process_create_webhook_result(Result). --spec get_webhooks(id(), ctx()) -> {ok, response_data()} | {error, GetError} when - GetError :: - {identity, notfound} - | {identity, unauthorized}. +-spec get_webhooks(id(), ctx()) -> {ok, response_data()}. get_webhooks(IdentityID, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - Call = {webhook_manager, 'GetList', {IdentityID}}, - Result = wapi_handler_utils:service_call(Call, HandlerContext), - process_get_webhooks_result(Result); - {error, Error} -> - {error, {identity, Error}} - end. + Call = {webhook_manager, 'GetList', {IdentityID}}, + Result = wapi_handler_utils:service_call(Call, HandlerContext), + process_get_webhooks_result(Result). --spec get_webhook(id(), id(), ctx()) -> {ok, response_data()} | {error, GetError} when +-spec get_webhook(id(), ctx()) -> {ok, response_data()} | {error, GetError} when GetError :: - notfound - | {webhook, notfound} - | {identity, notfound} - | {identity, unauthorized}. -get_webhook(WebhookID, IdentityID, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - case encode_webhook_id(WebhookID) of - {error, notfound} -> - {error, {webhook, notfound}}; - EncodedID -> - Call = {webhook_manager, 'Get', {EncodedID}}, - Result = wapi_handler_utils:service_call(Call, HandlerContext), - process_get_webhook_result(Result) - end; - {error, Error} -> - {error, {identity, Error}} + notfound. +get_webhook(WebhookID, HandlerContext) -> + case encode_webhook_id(WebhookID) of + {error, notfound} -> + {error, {webhook, notfound}}; + EncodedID -> + Call = {webhook_manager, 'Get', {EncodedID}}, + Result = wapi_handler_utils:service_call(Call, HandlerContext), + process_get_webhook_result(Result) end. --spec delete_webhook(id(), id(), ctx()) -> ok | {error, DeleteError} when +-spec delete_webhook(id(), ctx()) -> ok | {error, DeleteError} when DeleteError :: - notfound - | {identity, notfound} - | {identity, unauthorized}. -delete_webhook(WebhookID, IdentityID, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(identity, IdentityID, HandlerContext) of - ok -> - case encode_webhook_id(WebhookID) of - {error, notfound} -> - {error, {webhook, notfound}}; - EncodedID -> - Call = {webhook_manager, 'Delete', {EncodedID}}, - Result = wapi_handler_utils:service_call(Call, HandlerContext), - process_delete_webhook_result(Result) - end; - {error, Error} -> - {error, {identity, Error}} + notfound. +delete_webhook(WebhookID, HandlerContext) -> + case encode_webhook_id(WebhookID) of + {error, notfound} -> + {error, {webhook, notfound}}; + EncodedID -> + Call = {webhook_manager, 'Delete', {EncodedID}}, + Result = wapi_handler_utils:service_call(Call, HandlerContext), + process_delete_webhook_result(Result) end. process_create_webhook_result({ok, #webhooker_Webhook{} = Webhook}) -> @@ -116,11 +76,6 @@ encode_webhook_id(WebhookID) -> {error, notfound} end. -check_wallet(undefined, _) -> - ok; -check_wallet(WalletID, HandlerContext) -> - wapi_access_backend:check_resource_by_id(wallet, WalletID, HandlerContext). - %% marshaling marshal_webhook_params(#{ diff --git a/apps/wapi/src/wapi_withdrawal_backend.erl b/apps/wapi/src/wapi_withdrawal_backend.erl index 30b07bd..86bb156 100644 --- a/apps/wapi/src/wapi_withdrawal_backend.erl +++ b/apps/wapi/src/wapi_withdrawal_backend.erl @@ -17,7 +17,7 @@ -type create_error() :: {destination, notfound | unauthorized} - | {wallet, notfound | unauthorized} + | {wallet, notfound} | {external_id_conflict, id()} | {quote_invalid_party, _} | {quote_invalid_wallet, _} @@ -33,7 +33,7 @@ -type create_quote_error() :: {destination, notfound | unauthorized} - | {wallet, notfound | unauthorized} + | {wallet, notfound} | {forbidden_currency, _} | {forbidden_amount, _} | {invalid_amount, _} @@ -107,19 +107,13 @@ create(Params, Context, HandlerContext) -> -spec get(id(), handler_context()) -> {ok, response_data(), id()} - | {error, {withdrawal, notfound}} - | {error, {withdrawal, unauthorized}}. + | {error, {withdrawal, notfound}}. get(WithdrawalID, HandlerContext) -> Request = {fistful_withdrawal, 'Get', {WithdrawalID, #'EventRange'{}}}, case service_call(Request, HandlerContext) of {ok, WithdrawalThrift} -> - case wapi_access_backend:check_resource(withdrawal, WithdrawalThrift, HandlerContext) of - ok -> - {ok, Owner} = wapi_access_backend:get_resource_owner(withdrawal, WithdrawalThrift), - {ok, unmarshal(withdrawal, WithdrawalThrift), Owner}; - {error, unauthorized} -> - {error, {withdrawal, unauthorized}} - end; + {ok, Owner} = wapi_backend_utils:get_entity_owner(withdrawal, WithdrawalThrift), + {ok, unmarshal(withdrawal, WithdrawalThrift), Owner}; {exception, #fistful_WithdrawalNotFound{}} -> {error, {withdrawal, notfound}} end. @@ -127,7 +121,6 @@ get(WithdrawalID, HandlerContext) -> -spec get_by_external_id(external_id(), handler_context()) -> {ok, response_data(), id()} | {error, {withdrawal, notfound}} - | {error, {withdrawal, unauthorized}} | {error, {external_id, {unknown_external_id, external_id()}}}. get_by_external_id(ExternalID, HandlerContext = #{woody_context := WoodyContext}) -> PartyID = wapi_handler_utils:get_owner(HandlerContext), @@ -141,14 +134,6 @@ get_by_external_id(ExternalID, HandlerContext = #{woody_context := WoodyContext} -spec create_quote(req_data(), handler_context()) -> {ok, response_data()} | {error, create_quote_error()}. create_quote(#{'WithdrawalQuoteParams' := Params}, HandlerContext) -> - case authorize_quote(Params, HandlerContext) of - ok -> - create_quote_(Params, HandlerContext); - {error, _} = Error -> - Error - end. - -create_quote_(Params, HandlerContext) -> CreateQuoteParams = marshal(create_quote_params, Params), Request = {fistful_withdrawal, 'GetQuote', {CreateQuoteParams}}, case service_call(Request, HandlerContext) of @@ -194,18 +179,12 @@ create_quote_(Params, HandlerContext) -> end. -spec get_events(req_data(), handler_context()) -> - {ok, response_data()} - | {error, {withdrawal, notfound}} - | {error, {withdrawal, unauthorized}}. + {ok, response_data()} | {error, {withdrawal, notfound}}. get_events(Params = #{'withdrawalID' := WithdrawalId, 'limit' := Limit}, HandlerContext) -> Cursor = maps:get('eventCursor', Params, undefined), case get_events(WithdrawalId, {Cursor, Limit}, HandlerContext) of {ok, Events} -> {ok, Events}; - {error, {withdrawal, unauthorized}} = Error -> - Error; - {error, {withdrawal, notfound}} = Error -> - Error; {exception, #fistful_WithdrawalNotFound{}} -> {error, {withdrawal, notfound}} end. @@ -213,7 +192,6 @@ get_events(Params = #{'withdrawalID' := WithdrawalId, 'limit' := Limit}, Handler -spec get_event(id(), integer(), handler_context()) -> {ok, response_data()} | {error, {withdrawal, notfound}} - | {error, {withdrawal, unauthorized}} | {error, {event, notfound}}. get_event(WithdrawalId, EventId, HandlerContext) -> case get_events(WithdrawalId, {EventId - 1, 1}, HandlerContext) of @@ -221,10 +199,6 @@ get_event(WithdrawalId, EventId, HandlerContext) -> {ok, Event}; {ok, []} -> {error, {event, notfound}}; - {error, {withdrawal, unauthorized}} = Error -> - Error; - {error, {withdrawal, notfound}} = Error -> - Error; {exception, #fistful_WithdrawalNotFound{}} -> {error, {withdrawal, notfound}} end. @@ -249,19 +223,12 @@ get_events(WithdrawalId, EventRange, HandlerContext) -> {ok, Events0} -> Events1 = lists:filter(fun event_filter/1, Events0), {ok, unmarshal({list, event}, Events1)}; - {error, _} = Error -> - Error; {exception, _} = Exception -> Exception end. get_events_(WithdrawalId, EventRange, HandlerContext) -> - case authorize_resource_by_bearer(withdrawal, WithdrawalId, HandlerContext) of - ok -> - collect_events(WithdrawalId, EventRange, HandlerContext, []); - {error, _} = Error -> - Error - end. + collect_events(WithdrawalId, EventRange, HandlerContext, []). collect_events(WithdrawalId, {Cursor, Limit}, HandlerContext, AccEvents) -> Request = {fistful_withdrawal, 'GetEvents', {WithdrawalId, marshal_event_range(Cursor, Limit)}}, @@ -281,29 +248,9 @@ event_filter(_) -> false. %% Validators - -authorize_quote(Params = #{<<"walletID">> := WalletID}, HandlerContext) -> - do(fun() -> - unwrap(wallet, wapi_access_backend:check_resource_by_id(wallet, WalletID, HandlerContext)), - case maps:get(<<"destinationID">>, Params, undefined) of - undefined -> - ok; - DestinationID -> - unwrap( - destination, - wapi_access_backend:check_resource_by_id( - destination, - DestinationID, - HandlerContext - ) - ) - end - end). - check_withdrawal_params(Params0, HandlerContext) -> do(fun() -> Params1 = unwrap(try_decode_quote_token(Params0)), - unwrap(authorize_withdrawal(Params1, HandlerContext)), Params2 = unwrap(maybe_check_quote_token(Params1, HandlerContext)), ID = unwrap(wapi_backend_utils:gen_id(withdrawal, Params2, HandlerContext)), Params2#{<<"id">> => ID} @@ -325,96 +272,6 @@ try_decode_quote_token(Params = #{<<"quoteToken">> := QuoteToken}) -> try_decode_quote_token(Params) -> {ok, Params}. -authorize_withdrawal(Params, HandlerContext) -> - case authorize_resource(wallet, Params, HandlerContext) of - ok -> - case authorize_resource(destination, Params, HandlerContext) of - ok -> - ok; - {error, _} = Error -> - Error - end; - {error, _} = Error -> - Error - end. - -authorize_resource(Resource, Params, HandlerContext) -> - case authorize_resource_by_grant(Resource, Params) of - ok -> - ok; - {error, missing} -> - authorize_resource_by_bearer(Resource, maps:get(genlib:to_binary(Resource), Params), HandlerContext) - end. - -authorize_resource_by_bearer(Resource, ResourceID, HandlerContext) -> - case wapi_access_backend:check_resource_by_id(Resource, ResourceID, HandlerContext) of - ok -> - ok; - {error, unauthorized} -> - {error, {Resource, unauthorized}}; - {error, notfound} -> - {error, {Resource, notfound}} - end. - -authorize_resource_by_grant(R = destination, #{ - <<"destination">> := ID, - <<"destinationGrant">> := Grant -}) -> - authorize_resource_by_grant(R, Grant, get_resource_accesses(R, ID, write), undefined); -authorize_resource_by_grant(R = wallet, #{ - <<"wallet">> := ID, - <<"walletGrant">> := Grant, - <<"body">> := WithdrawalBody -}) -> - authorize_resource_by_grant(R, Grant, get_resource_accesses(R, ID, write), WithdrawalBody); -authorize_resource_by_grant(_, _) -> - {error, missing}. - -authorize_resource_by_grant(Resource, Grant, Access, Params) -> - do(fun() -> - {_, _, Claims} = unwrap(Resource, uac_authorizer_jwt:verify(Grant, #{})), - unwrap(Resource, verify_access(Access, Claims)), - unwrap(Resource, verify_claims(Resource, Claims, Params)) - end). - -get_resource_accesses(Resource, ID, Permission) -> - [{get_resource_accesses(Resource, ID), Permission}]. - -get_resource_accesses(destination, ID) -> - [party, {destinations, ID}]; -get_resource_accesses(wallet, ID) -> - [party, {wallets, ID}]. - -verify_access(Access, #{<<"resource_access">> := #{?DOMAIN := ACL}}) -> - do_verify_access(Access, ACL); -% Legacy grants support -verify_access(Access, #{<<"resource_access">> := #{<<"common-api">> := ACL}}) -> - do_verify_access(Access, ACL); -verify_access(_, _) -> - {error, {unauthorized, {grant, insufficient_access}}}. - -do_verify_access(Access, ACL) -> - case - lists:all( - fun({Scope, Permission}) -> lists:member(Permission, uac_acl:match(Scope, ACL)) end, - Access - ) - of - true -> ok; - false -> {error, {unauthorized, {grant, insufficient_access}}} - end. - -verify_claims(destination, _Claims, _) -> - ok; -verify_claims( - wallet, - #{<<"amount">> := GrantAmount, <<"currency">> := Currency}, - #{<<"amount">> := ReqAmount, <<"currency">> := Currency} -) when GrantAmount >= ReqAmount -> - ok; -verify_claims(_, _, _) -> - {error, {unauthorized, {grant, insufficient_claims}}}. - maybe_check_quote_token( Params = #{ <<"quoteToken">> := #{ diff --git a/apps/wapi/test/wapi_ct_helper.erl b/apps/wapi/test/wapi_ct_helper.erl index 2c92dde..ea8a194 100644 --- a/apps/wapi/test/wapi_ct_helper.erl +++ b/apps/wapi/test/wapi_ct_helper.erl @@ -237,21 +237,17 @@ mock_services_(Services, Config) when is_list(Config) -> mock_services_(Services, ?config(test_sup, Config)); mock_services_(Services, SupPid) when is_pid(SupPid) -> Name = lists:map(fun get_service_name/1, Services), - - Port = get_random_port(), {ok, IP} = inet:parse_address(?WAPI_IP), - ChildSpec = woody_server:child_spec( - {dummy, Name}, - #{ - ip => IP, - port => Port, - event_handler => scoper_woody_event_handler, - handlers => lists:map(fun mock_service_handler/1, Services) - } - ), - + ServerID = {dummy, Name}, + WoodyOpts = #{ + ip => IP, + port => 0, + event_handler => scoper_woody_event_handler, + handlers => lists:map(fun mock_service_handler/1, Services) + }, + ChildSpec = woody_server:child_spec(ServerID, WoodyOpts), {ok, _} = supervisor:start_child(SupPid, ChildSpec), - + {_IP, Port} = woody_server:get_addr(ServerID, WoodyOpts), lists:foldl( fun(Service, Acc) -> ServiceName = get_service_name(Service), @@ -294,10 +290,6 @@ mock_service_handler({ServiceName, WoodyService, Fun}) -> mock_service_handler(ServiceName, WoodyService, Fun) -> {make_path(ServiceName), {WoodyService, {wapi_dummy_service, #{function => Fun}}}}. -% TODO not so failproof, ideally we need to bind socket first and then give to a ranch listener -get_random_port() -> - rand:uniform(32768) + 32767. - make_url(ServiceName, Port) -> iolist_to_binary(["http://", ?WAPI_HOST_NAME, ":", integer_to_list(Port), make_path(ServiceName)]).