ED-288: Relax internal wapi auth (#30)

This commit is contained in:
Alexey S 2021-11-08 11:10:58 +03:00 committed by GitHub
parent 4001a1c2e9
commit de8fca96d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 341 additions and 635 deletions

View File

@ -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}.

View File

@ -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).

View File

@ -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),

View File

@ -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.

View File

@ -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}} ->

View File

@ -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.

View File

@ -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.

View File

@ -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).

View File

@ -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(#{

View File

@ -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">> := #{

View File

@ -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)]).