mirror of
https://github.com/valitydev/api-key-mgmt-v2.git
synced 2024-11-06 02:15:19 +00:00
Epic/td 651/deploy akm (#12)
* add logging * debug * debug * fix auth context * fix auth context * maybe fix release * fix release deps * fix authority config * fix auth context * fix api_key entity * fix mailer * fix test * debug revoke * fix api_key entity * debug auth context * add entities to context * debug revoke * debug revoke * debug revoke * fix auth context * debug email * fix revoke * fix auth context for revoke * fix style * delete headers from revoke method * cleanup --------- Co-authored-by: anatoliy.losev <losto@nix>
This commit is contained in:
parent
9c3e1f3968
commit
d9169e2bdd
@ -1 +1,6 @@
|
|||||||
|
Subject: Revoke key
|
||||||
|
From: no-reply@empayre.com
|
||||||
|
To: You
|
||||||
|
|
||||||
|
|
||||||
To revoke key, go to link: {{ url }}/apikeys/v2/orgs/{{ party_id }}/revoke-api-key/{{ api_key_id }}?apiKeyRevokeToken={{ revoke_token }}
|
To revoke key, go to link: {{ url }}/apikeys/v2/orgs/{{ party_id }}/revoke-api-key/{{ api_key_id }}?apiKeyRevokeToken={{ revoke_token }}
|
||||||
|
@ -78,13 +78,15 @@ prepare(OperationID = 'IssueApiKey', #{'partyId' := PartyID, 'ApiKeyIssue' := Ap
|
|||||||
end,
|
end,
|
||||||
{ok, #{authorize => Authorize, process => Process}};
|
{ok, #{authorize => Authorize, process => Process}};
|
||||||
prepare(OperationID = 'GetApiKey', #{'partyId' := PartyID, 'apiKeyId' := ApiKeyId}, Context, _Opts) ->
|
prepare(OperationID = 'GetApiKey', #{'partyId' := PartyID, 'apiKeyId' := ApiKeyId}, Context, _Opts) ->
|
||||||
|
Result = akm_apikeys_processing:get_api_key(ApiKeyId),
|
||||||
Authorize = fun() ->
|
Authorize = fun() ->
|
||||||
Prototypes = [{operation, #{id => OperationID, party => PartyID}}],
|
ApiKey = extract_api_key(Result),
|
||||||
|
Prototypes = [{operation, #{id => OperationID, party => PartyID, api_key => ApiKey}}],
|
||||||
Resolution = akm_auth:authorize_operation(Prototypes, Context),
|
Resolution = akm_auth:authorize_operation(Prototypes, Context),
|
||||||
{ok, Resolution}
|
{ok, Resolution}
|
||||||
end,
|
end,
|
||||||
Process = fun() ->
|
Process = fun() ->
|
||||||
case akm_apikeys_processing:get_api_key(ApiKeyId, PartyID) of
|
case Result of
|
||||||
{ok, ApiKey} ->
|
{ok, ApiKey} ->
|
||||||
akm_handler_utils:reply_ok(200, ApiKey);
|
akm_handler_utils:reply_ok(200, ApiKey);
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
@ -121,8 +123,10 @@ prepare(OperationID = 'RequestRevokeApiKey', Params, Context, _Opts) ->
|
|||||||
'apiKeyId' := ApiKeyId,
|
'apiKeyId' := ApiKeyId,
|
||||||
'RequestRevoke' := #{<<"status">> := Status}
|
'RequestRevoke' := #{<<"status">> := Status}
|
||||||
} = Params,
|
} = Params,
|
||||||
|
Result = akm_apikeys_processing:get_api_key(ApiKeyId),
|
||||||
Authorize = fun() ->
|
Authorize = fun() ->
|
||||||
Prototypes = [{operation, #{id => OperationID, party => PartyID}}],
|
ApiKey = extract_api_key(Result),
|
||||||
|
Prototypes = [{operation, #{id => OperationID, party => PartyID, api_key => ApiKey}}],
|
||||||
Resolution = akm_auth:authorize_operation(Prototypes, Context),
|
Resolution = akm_auth:authorize_operation(Prototypes, Context),
|
||||||
{ok, Resolution}
|
{ok, Resolution}
|
||||||
end,
|
end,
|
||||||
@ -137,18 +141,21 @@ prepare(OperationID = 'RequestRevokeApiKey', Params, Context, _Opts) ->
|
|||||||
end,
|
end,
|
||||||
{ok, #{authorize => Authorize, process => Process}};
|
{ok, #{authorize => Authorize, process => Process}};
|
||||||
prepare(
|
prepare(
|
||||||
OperationID = 'RevokeApiKey',
|
_OperationID = 'RevokeApiKey',
|
||||||
#{'partyId' := PartyID, 'apiKeyId' := ApiKeyId, 'apiKeyRevokeToken' := Token},
|
#{'partyId' := _PartyID, 'apiKeyId' := ApiKeyId, 'apiKeyRevokeToken' := Token},
|
||||||
Context,
|
_Context,
|
||||||
_Opts
|
_Opts
|
||||||
) ->
|
) ->
|
||||||
|
%% Result = akm_apikeys_processing:get_api_key(ApiKeyId),
|
||||||
Authorize = fun() ->
|
Authorize = fun() ->
|
||||||
Prototypes = [{operation, #{id => OperationID, party => PartyID}}],
|
%% ApiKey = extract_api_key(Result),
|
||||||
Resolution = akm_auth:authorize_operation(Prototypes, Context),
|
%% Prototypes = [{operation, #{id => OperationID, party => PartyID, api_key => ApiKey}}],
|
||||||
{ok, Resolution}
|
%% Resolution = akm_auth:authorize_operation(Prototypes, Context),
|
||||||
|
%% {ok, Resolution}
|
||||||
|
{ok, allowed}
|
||||||
end,
|
end,
|
||||||
Process = fun() ->
|
Process = fun() ->
|
||||||
case akm_apikeys_processing:revoke(PartyID, ApiKeyId, Token) of
|
case akm_apikeys_processing:revoke(ApiKeyId, Token) of
|
||||||
ok ->
|
ok ->
|
||||||
akm_handler_utils:reply_ok(204);
|
akm_handler_utils:reply_ok(204);
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
@ -156,3 +163,8 @@ prepare(
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
{ok, #{authorize => Authorize, process => Process}}.
|
{ok, #{authorize => Authorize, process => Process}}.
|
||||||
|
|
||||||
|
extract_api_key({ok, Apikey}) ->
|
||||||
|
Apikey;
|
||||||
|
extract_api_key(_) ->
|
||||||
|
undefined.
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
-include_lib("epgsql/include/epgsql.hrl").
|
-include_lib("epgsql/include/epgsql.hrl").
|
||||||
|
|
||||||
-export([issue_api_key/3]).
|
-export([issue_api_key/3]).
|
||||||
-export([get_api_key/2]).
|
-export([get_api_key/1]).
|
||||||
-export([list_api_keys/4]).
|
-export([list_api_keys/4]).
|
||||||
-export([request_revoke/4]).
|
-export([request_revoke/4]).
|
||||||
-export([revoke/3]).
|
-export([revoke/2]).
|
||||||
|
|
||||||
-type list_keys_response() :: #{
|
-type list_keys_response() :: #{
|
||||||
results => [map()],
|
results => [map()],
|
||||||
@ -50,12 +50,12 @@ issue_api_key(PartyID, #{<<"name">> := Name} = ApiKey0, WoodyContext) ->
|
|||||||
{error, already_exists}
|
{error, already_exists}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_api_key(binary(), binary()) -> {ok, map()} | {error, not_found}.
|
-spec get_api_key(binary()) -> {ok, map()} | {error, not_found}.
|
||||||
get_api_key(ApiKeyId, PartyId) ->
|
get_api_key(ApiKeyId) ->
|
||||||
Result = epgsql_pool:query(
|
Result = epgsql_pool:query(
|
||||||
main_pool,
|
main_pool,
|
||||||
"SELECT id, name, status, metadata, created_at FROM apikeys WHERE id = $1 AND party_id = $2",
|
"SELECT id, name, status, metadata, created_at FROM apikeys WHERE id = $1",
|
||||||
[ApiKeyId, PartyId]
|
[ApiKeyId]
|
||||||
),
|
),
|
||||||
case Result of
|
case Result of
|
||||||
{ok, _Columns, []} ->
|
{ok, _Columns, []} ->
|
||||||
@ -87,7 +87,7 @@ list_api_keys(PartyId, Status, Limit, Offset) ->
|
|||||||
-spec request_revoke(binary(), binary(), binary(), binary()) ->
|
-spec request_revoke(binary(), binary(), binary(), binary()) ->
|
||||||
{ok, revoke_email_sent} | {error, not_found}.
|
{ok, revoke_email_sent} | {error, not_found}.
|
||||||
request_revoke(Email, PartyID, ApiKeyId, Status) ->
|
request_revoke(Email, PartyID, ApiKeyId, Status) ->
|
||||||
case get_full_api_key(ApiKeyId, PartyID) of
|
case get_full_api_key(ApiKeyId) of
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
{error, not_found};
|
{error, not_found};
|
||||||
{ok, _ApiKey} ->
|
{ok, _ApiKey} ->
|
||||||
@ -100,8 +100,8 @@ request_revoke(Email, PartyID, ApiKeyId, Status) ->
|
|||||||
epgsql_pool:query(
|
epgsql_pool:query(
|
||||||
Worker,
|
Worker,
|
||||||
"UPDATE apikeys SET pending_status = $1, revoke_token = $2 "
|
"UPDATE apikeys SET pending_status = $1, revoke_token = $2 "
|
||||||
"WHERE id = $3 AND party_id = $4",
|
"WHERE id = $3",
|
||||||
[Status, Token, ApiKeyId, PartyID]
|
[Status, Token, ApiKeyId]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
@ -109,22 +109,23 @@ request_revoke(Email, PartyID, ApiKeyId, Status) ->
|
|||||||
{ok, 1} ->
|
{ok, 1} ->
|
||||||
{ok, revoke_email_sent}
|
{ok, revoke_email_sent}
|
||||||
catch
|
catch
|
||||||
_Ex:_Er ->
|
Ex:Er ->
|
||||||
|
logger:error("Failed to send email with ~p:~p", [Ex, Er]),
|
||||||
error(failed_to_send_email)
|
error(failed_to_send_email)
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec revoke(binary(), binary(), binary()) -> ok | {error, not_found}.
|
-spec revoke(binary(), binary()) -> ok | {error, not_found}.
|
||||||
revoke(PartyId, ApiKeyId, RevokeToken) ->
|
revoke(ApiKeyId, RevokeToken) ->
|
||||||
case get_full_api_key(ApiKeyId, PartyId) of
|
case get_full_api_key(ApiKeyId) of
|
||||||
{ok, #{
|
{ok, #{
|
||||||
<<"pending_status">> := PendingStatus,
|
<<"pending_status">> := PendingStatus,
|
||||||
<<"revoke_token">> := RevokeToken
|
<<"revoke_token">> := RevokeToken
|
||||||
}} ->
|
}} ->
|
||||||
{ok, 1} = epgsql_pool:query(
|
{ok, 1} = epgsql_pool:query(
|
||||||
main_pool,
|
main_pool,
|
||||||
"UPDATE apikeys SET status = $1, revoke_token = null WHERE id = $2 AND party_id = $3",
|
"UPDATE apikeys SET status = $1, revoke_token = null WHERE id = $2",
|
||||||
[PendingStatus, ApiKeyId, PartyId]
|
[PendingStatus, ApiKeyId]
|
||||||
),
|
),
|
||||||
ok;
|
ok;
|
||||||
_ ->
|
_ ->
|
||||||
@ -134,13 +135,13 @@ revoke(PartyId, ApiKeyId, RevokeToken) ->
|
|||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
|
||||||
get_authority_id() ->
|
get_authority_id() ->
|
||||||
application:get_env(akm, authority_id).
|
application:get_env(akm, authority_id, undefined).
|
||||||
|
|
||||||
get_full_api_key(ApiKeyId, PartyId) ->
|
get_full_api_key(ApiKeyId) ->
|
||||||
Result = epgsql_pool:query(
|
Result = epgsql_pool:query(
|
||||||
main_pool,
|
main_pool,
|
||||||
"SELECT * FROM apikeys WHERE id = $1 AND party_id = $2",
|
"SELECT * FROM apikeys WHERE id = $1",
|
||||||
[ApiKeyId, PartyId]
|
[ApiKeyId]
|
||||||
),
|
),
|
||||||
case Result of
|
case Result of
|
||||||
{ok, _Columns, []} ->
|
{ok, _Columns, []} ->
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
-define(APP, akm).
|
-define(APP, akm).
|
||||||
|
|
||||||
|
-include_lib("bouncer_proto/include/bouncer_ctx_thrift.hrl").
|
||||||
|
|
||||||
-export([get_subject_id/1]).
|
-export([get_subject_id/1]).
|
||||||
-export([get_party_id/1]).
|
-export([get_party_id/1]).
|
||||||
-export([get_user_id/1]).
|
-export([get_user_id/1]).
|
||||||
@ -12,6 +14,8 @@
|
|||||||
-export([authorize_api_key/3]).
|
-export([authorize_api_key/3]).
|
||||||
-export([authorize_operation/2]).
|
-export([authorize_operation/2]).
|
||||||
|
|
||||||
|
-export([make_auth_context/1]).
|
||||||
|
|
||||||
-export_type([resolution/0]).
|
-export_type([resolution/0]).
|
||||||
-export_type([preauth_context/0]).
|
-export_type([preauth_context/0]).
|
||||||
-export_type([auth_context/0]).
|
-export_type([auth_context/0]).
|
||||||
@ -97,6 +101,19 @@ authorize_operation(Prototypes, Context) ->
|
|||||||
Fragments1 = akm_bouncer_context:build(Prototypes, Fragments),
|
Fragments1 = akm_bouncer_context:build(Prototypes, Fragments),
|
||||||
akm_bouncer:judge(Fragments1, WoodyContext).
|
akm_bouncer:judge(Fragments1, WoodyContext).
|
||||||
|
|
||||||
|
-spec make_auth_context(binary()) -> auth_context().
|
||||||
|
make_auth_context(PartyId) ->
|
||||||
|
{
|
||||||
|
authorized,
|
||||||
|
#{
|
||||||
|
status => active,
|
||||||
|
context => #ctx_ContextFragment{type = 'v1_thrift_binary'},
|
||||||
|
metadata => #{
|
||||||
|
get_metadata_mapped_key(party_id) => PartyId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
get_token_keeper_fragment(?AUTHORIZED(#{context := Context})) ->
|
get_token_keeper_fragment(?AUTHORIZED(#{context := Context})) ->
|
||||||
|
@ -20,7 +20,7 @@ gather_context_fragments(TokenContextFragment, UserID, IPAddress, WoodyCtx) ->
|
|||||||
judge({Acc, External}, WoodyCtx) ->
|
judge({Acc, External}, WoodyCtx) ->
|
||||||
% TODO error out early?
|
% TODO error out early?
|
||||||
{ok, RulesetID} = application:get_env(akm, bouncer_ruleset_id),
|
{ok, RulesetID} = application:get_env(akm, bouncer_ruleset_id),
|
||||||
JudgeContext = #{fragments => External#{<<"akm">> => Acc}},
|
JudgeContext = #{fragments => External#{<<"apikeymgmt">> => Acc}},
|
||||||
bouncer_client:judge(RulesetID, JudgeContext, WoodyCtx).
|
bouncer_client:judge(RulesetID, JudgeContext, WoodyCtx).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
-type prototype_operation() :: #{
|
-type prototype_operation() :: #{
|
||||||
id => operation_id(),
|
id => operation_id(),
|
||||||
party => maybe_undefined(entity_id())
|
party => maybe_undefined(entity_id()),
|
||||||
|
api_key => maybe_undefined(entity_id())
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-type entity_id() :: binary().
|
-type entity_id() :: binary().
|
||||||
@ -49,28 +50,45 @@ build(Prototypes, {Acc0, External}) ->
|
|||||||
{Acc1, External}.
|
{Acc1, External}.
|
||||||
|
|
||||||
build(operation, Params = #{id := OperationID}, Acc) ->
|
build(operation, Params = #{id := OperationID}, Acc) ->
|
||||||
Acc#ctx_v1_ContextFragment{
|
PartyEntity = party_entity(Params),
|
||||||
|
ApiKeyEntity = api_key_entity(Params),
|
||||||
|
ListEntities = lists:filter(fun(E) -> E =/= undefined end, [PartyEntity, ApiKeyEntity]),
|
||||||
|
Ctx = Acc#ctx_v1_ContextFragment{
|
||||||
apikeymgmt = #ctx_v1_ContextApiKeyMgmt{
|
apikeymgmt = #ctx_v1_ContextApiKeyMgmt{
|
||||||
op = #ctx_v1_ApiKeyMgmtOperation{
|
op = #ctx_v1_ApiKeyMgmtOperation{
|
||||||
id = operation_id_to_binary(OperationID),
|
id = operation_id_to_binary(OperationID),
|
||||||
party = maybe_entity(party_id, Params),
|
party = PartyEntity,
|
||||||
api_key = maybe(api_key, Params)
|
api_key = ApiKeyEntity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.
|
},
|
||||||
|
maybe_add_entities(Ctx, ListEntities).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
maybe(Name, Params) ->
|
api_key_entity(
|
||||||
maps:get(Name, Params, undefined).
|
#{
|
||||||
|
api_key := #{
|
||||||
|
<<"id">> := ApiKeyId,
|
||||||
|
<<"metadata">> := #{<<"party.id">> := PartyId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
#base_Entity{id = ApiKeyId, party = PartyId, type = <<"ApiKey">>};
|
||||||
|
api_key_entity(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
maybe_entity(Name, Params) ->
|
party_entity(#{party := PartyId}) ->
|
||||||
case maps:get(Name, Params, undefined) of
|
#base_Entity{id = PartyId};
|
||||||
undefined ->
|
party_entity(_) ->
|
||||||
undefined;
|
undefined.
|
||||||
Value ->
|
|
||||||
#base_Entity{id = Value}
|
|
||||||
end.
|
|
||||||
|
|
||||||
operation_id_to_binary(V) ->
|
operation_id_to_binary(V) ->
|
||||||
erlang:atom_to_binary(V, utf8).
|
erlang:atom_to_binary(V, utf8).
|
||||||
|
|
||||||
|
maybe_add_entities(Ctx, []) ->
|
||||||
|
Ctx;
|
||||||
|
maybe_add_entities(Ctx, ListEntities) ->
|
||||||
|
Ctx#ctx_v1_ContextFragment{
|
||||||
|
entities = ordsets:from_list(ListEntities)
|
||||||
|
}.
|
||||||
|
@ -60,7 +60,7 @@ authorize_api_key(OperationID, ApiKey, _Context, _HandlerOpts) ->
|
|||||||
%% request validation checks before this stage.
|
%% request validation checks before this stage.
|
||||||
%% But since a decent chunk of authorization logic is already defined in the handler function
|
%% But since a decent chunk of authorization logic is already defined in the handler function
|
||||||
%% it is probably easier to move it there in its entirety.
|
%% it is probably easier to move it there in its entirety.
|
||||||
ok = scoper:add_scope('swag.server', #{api => wallet, operation_id => OperationID}),
|
ok = scoper:add_scope('swag.server', #{api => apikeymgmt, operation_id => OperationID}),
|
||||||
case akm_auth:preauthorize_api_key(ApiKey) of
|
case akm_auth:preauthorize_api_key(ApiKey) of
|
||||||
{ok, Context} ->
|
{ok, Context} ->
|
||||||
{true, Context};
|
{true, Context};
|
||||||
@ -77,7 +77,7 @@ authorize_api_key(OperationID, ApiKey, _Context, _HandlerOpts) ->
|
|||||||
) ->
|
) ->
|
||||||
akm_apikeys_handler:request_result().
|
akm_apikeys_handler:request_result().
|
||||||
handle_request(OperationID, Req, SwagContext, Opts) ->
|
handle_request(OperationID, Req, SwagContext, Opts) ->
|
||||||
#{'X-Request-Deadline' := Header} = Req,
|
Header = maps:get('X-Request-Deadline', Req, undefined),
|
||||||
case akm_utils:parse_deadline(Header) of
|
case akm_utils:parse_deadline(Header) of
|
||||||
{ok, Deadline} ->
|
{ok, Deadline} ->
|
||||||
WoodyContext = attach_deadline(Deadline, create_woody_context(Req)),
|
WoodyContext = attach_deadline(Deadline, create_woody_context(Req)),
|
||||||
@ -93,7 +93,7 @@ handle_request(OperationID, Req, SwagContext, Opts) ->
|
|||||||
process_request(OperationID, Req, SwagContext0, Opts, WoodyContext0) ->
|
process_request(OperationID, Req, SwagContext0, Opts, WoodyContext0) ->
|
||||||
_ = logger:info("Processing request ~p", [OperationID]),
|
_ = logger:info("Processing request ~p", [OperationID]),
|
||||||
try
|
try
|
||||||
SwagContext = do_authorize_api_key(SwagContext0, WoodyContext0),
|
SwagContext = do_authorize_api_key(OperationID, SwagContext0, WoodyContext0),
|
||||||
WoodyContext = put_user_identity(WoodyContext0, get_auth_context(SwagContext)),
|
WoodyContext = put_user_identity(WoodyContext0, get_auth_context(SwagContext)),
|
||||||
Context = create_handler_context(OperationID, SwagContext, WoodyContext),
|
Context = create_handler_context(OperationID, SwagContext, WoodyContext),
|
||||||
ok = set_context_meta(Context),
|
ok = set_context_meta(Context),
|
||||||
@ -117,7 +117,9 @@ process_request(OperationID, Req, SwagContext0, Opts, WoodyContext0) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
-spec create_woody_context(akm_apikeys_handler:request_data()) -> woody_context:ctx().
|
-spec create_woody_context(akm_apikeys_handler:request_data()) -> woody_context:ctx().
|
||||||
create_woody_context(#{'X-Request-ID' := RequestID}) ->
|
create_woody_context(RequestData) ->
|
||||||
|
%% use dynamic request_id if not presented
|
||||||
|
RequestID = maps:get('X-Request-ID', RequestData, new_request_id()),
|
||||||
RpcID = #{trace_id := TraceID} = woody_context:new_rpc_id(genlib:to_binary(RequestID)),
|
RpcID = #{trace_id := TraceID} = woody_context:new_rpc_id(genlib:to_binary(RequestID)),
|
||||||
ok = scoper:add_meta(#{request_id => RequestID, trace_id => TraceID}),
|
ok = scoper:add_meta(#{request_id => RequestID, trace_id => TraceID}),
|
||||||
woody_context:new(RpcID, undefined, akm_woody_client:get_service_deadline(akm)).
|
woody_context:new(RpcID, undefined, akm_woody_client:get_service_deadline(akm)).
|
||||||
@ -151,7 +153,10 @@ attach_deadline(undefined, Context) ->
|
|||||||
attach_deadline(Deadline, Context) ->
|
attach_deadline(Deadline, Context) ->
|
||||||
woody_context:set_deadline(Deadline, Context).
|
woody_context:set_deadline(Deadline, Context).
|
||||||
|
|
||||||
do_authorize_api_key(SwagContext = #{auth_context := PreAuthContext}, WoodyContext) ->
|
do_authorize_api_key('RevokeApiKey', #{cowboy_req := Req} = SwagContext, _WoodyContext) ->
|
||||||
|
PartyId = cowboy_req:binding(partyId, Req),
|
||||||
|
SwagContext#{auth_context => akm_auth:make_auth_context(PartyId)};
|
||||||
|
do_authorize_api_key(_OperationID, SwagContext = #{auth_context := PreAuthContext}, WoodyContext) ->
|
||||||
case akm_auth:authorize_api_key(PreAuthContext, make_token_context(SwagContext), WoodyContext) of
|
case akm_auth:authorize_api_key(PreAuthContext, make_token_context(SwagContext), WoodyContext) of
|
||||||
{ok, AuthContext} ->
|
{ok, AuthContext} ->
|
||||||
SwagContext#{auth_context => AuthContext};
|
SwagContext#{auth_context => AuthContext};
|
||||||
@ -188,3 +193,6 @@ process_woody_error(_Source, resource_unavailable, _Details) ->
|
|||||||
akm_handler_utils:reply_error(504);
|
akm_handler_utils:reply_error(504);
|
||||||
process_woody_error(_Source, result_unknown, _Details) ->
|
process_woody_error(_Source, result_unknown, _Details) ->
|
||||||
akm_handler_utils:reply_error(504).
|
akm_handler_utils:reply_error(504).
|
||||||
|
|
||||||
|
new_request_id() ->
|
||||||
|
base64:encode(crypto:strong_rand_bytes(24)).
|
||||||
|
@ -19,12 +19,19 @@ send_revoke_mail(Email, PartyID, ApiKeyID, Token) ->
|
|||||||
{api_key_id, ApiKeyID},
|
{api_key_id, ApiKeyID},
|
||||||
{revoke_token, Token}
|
{revoke_token, Token}
|
||||||
]),
|
]),
|
||||||
BinaryBody = erlang:iolist_to_binary(Body),
|
BinaryBody = unicode:characters_to_binary(Body),
|
||||||
|
logger:info("Try send email with body: ~p", [BinaryBody]),
|
||||||
Pid = self(),
|
Pid = self(),
|
||||||
case
|
case
|
||||||
gen_smtp_client:send(
|
gen_smtp_client:send(
|
||||||
{from_email(), [Email], BinaryBody},
|
{from_email(), [Email], BinaryBody},
|
||||||
[{relay, relay()}, {username, username()}, {password, password()}],
|
[
|
||||||
|
{ssl, true},
|
||||||
|
{relay, relay()},
|
||||||
|
{port, port()},
|
||||||
|
{username, username()},
|
||||||
|
{password, password()}
|
||||||
|
],
|
||||||
fun(Result) -> erlang:send(Pid, {sending_result, Result}) end
|
fun(Result) -> erlang:send(Pid, {sending_result, Result}) end
|
||||||
)
|
)
|
||||||
of
|
of
|
||||||
@ -54,9 +61,17 @@ password() ->
|
|||||||
#{password := Password} = get_env(),
|
#{password := Password} = get_env(),
|
||||||
Password.
|
Password.
|
||||||
|
|
||||||
|
timeout() ->
|
||||||
|
maps:get(timeout, get_env(), 3000).
|
||||||
|
|
||||||
|
port() ->
|
||||||
|
#{port := Port} = get_env(),
|
||||||
|
to_int(Port).
|
||||||
|
|
||||||
get_env() ->
|
get_env() ->
|
||||||
genlib_app:env(akm, mailer, #{
|
genlib_app:env(akm, mailer, #{
|
||||||
url => "vality.dev",
|
url => "https://vality.dev",
|
||||||
|
port => 465,
|
||||||
from_email => "example@example.com",
|
from_email => "example@example.com",
|
||||||
relay => "smtp.gmail.com",
|
relay => "smtp.gmail.com",
|
||||||
username => "username",
|
username => "username",
|
||||||
@ -65,11 +80,16 @@ get_env() ->
|
|||||||
}).
|
}).
|
||||||
|
|
||||||
wait_result() ->
|
wait_result() ->
|
||||||
|
Timeout = timeout(),
|
||||||
receive
|
receive
|
||||||
{sending_result, {ok, _Receipt}} ->
|
{sending_result, {ok, _Receipt}} ->
|
||||||
ok;
|
ok;
|
||||||
{sending_result, Error} ->
|
{sending_result, Error} ->
|
||||||
{error, Error}
|
{error, Error}
|
||||||
after 3000 ->
|
after Timeout ->
|
||||||
{error, {failed_to_send, sending_email_timeout}}
|
{error, {failed_to_send, sending_email_timeout}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
to_int(Value) when is_integer(Value) -> Value;
|
||||||
|
to_int(Value) when is_binary(Value) -> erlang:binary_to_integer(Value);
|
||||||
|
to_int(Value) when is_list(Value) -> erlang:list_to_integer(Value).
|
||||||
|
@ -71,11 +71,7 @@ request_revoke_key(Host, Port, PartyId, ApiKeyId) ->
|
|||||||
|
|
||||||
-spec revoke_key(inet:hostname() | inet:ip_address(), inet:port_number(), binary()) -> any().
|
-spec revoke_key(inet:hostname() | inet:ip_address(), inet:port_number(), binary()) -> any().
|
||||||
revoke_key(Host, Port, PathWithQuery) ->
|
revoke_key(Host, Port, PathWithQuery) ->
|
||||||
Headers = [
|
Headers = [],
|
||||||
{<<"X-Request-ID">>, <<"revoke_key">>},
|
|
||||||
{<<"content-type">>, <<"application/json; charset=utf-8">>},
|
|
||||||
{<<"Authorization">>, <<"Bearer sffsdfsfsdfsdfs">>}
|
|
||||||
],
|
|
||||||
ConnPid = connect(Host, Port),
|
ConnPid = connect(Host, Port),
|
||||||
Answer = get(ConnPid, PathWithQuery, Headers),
|
Answer = get(ConnPid, PathWithQuery, Headers),
|
||||||
disconnect(ConnPid),
|
disconnect(ConnPid),
|
||||||
|
@ -100,6 +100,7 @@ prepare_config(State) ->
|
|||||||
|
|
||||||
{mailer, #{
|
{mailer, #{
|
||||||
url => "http://vality.dev",
|
url => "http://vality.dev",
|
||||||
|
port => 465,
|
||||||
from_email => "example@example.com",
|
from_email => "example@example.com",
|
||||||
relay => "smtp4dev",
|
relay => "smtp4dev",
|
||||||
password => "password",
|
password => "password",
|
||||||
@ -119,8 +120,8 @@ prepare_config(State) ->
|
|||||||
|
|
||||||
mock_services(State) ->
|
mock_services(State) ->
|
||||||
meck:expect(
|
meck:expect(
|
||||||
akm_auth,
|
akm_bouncer,
|
||||||
authorize_operation,
|
judge,
|
||||||
fun(_, _) -> allowed end
|
fun(_, _) -> allowed end
|
||||||
),
|
),
|
||||||
meck:expect(
|
meck:expect(
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
{elvis_style, atom_naming_convention, #{
|
{elvis_style, atom_naming_convention, #{
|
||||||
ignore => [
|
ignore => [
|
||||||
akm_apikeys_handler,
|
akm_apikeys_handler,
|
||||||
akm_apikeys_processing
|
akm_apikeys_processing,
|
||||||
|
akm_handler
|
||||||
]
|
]
|
||||||
}},
|
}},
|
||||||
{elvis_style, invalid_dynamic_call, #{
|
{elvis_style, invalid_dynamic_call, #{
|
||||||
|
@ -39,8 +39,6 @@
|
|||||||
{woody_user_identity, {git, "https://github.com/valitydev/woody_erlang_user_identity.git", {branch, "master"}}},
|
{woody_user_identity, {git, "https://github.com/valitydev/woody_erlang_user_identity.git", {branch, "master"}}},
|
||||||
{bouncer_proto, {git, "https://github.com/valitydev/bouncer-proto.git", {branch, "master"}}},
|
{bouncer_proto, {git, "https://github.com/valitydev/bouncer-proto.git", {branch, "master"}}},
|
||||||
{bouncer_client, {git, "https://github.com/valitydev/bouncer-client-erlang", {branch, "master"}}},
|
{bouncer_client, {git, "https://github.com/valitydev/bouncer-client-erlang", {branch, "master"}}},
|
||||||
{epgsql, {git, "https://github.com/epgsql/epgsql.git", {tag, "4.7.1"}}},
|
|
||||||
{epgsql_pool, {git, "https://github.com/wgnet/epgsql_pool", {branch, "master"}}},
|
|
||||||
{token_keeper_client, {git, "https://github.com/valitydev/token-keeper-client", {branch, "master"}}},
|
{token_keeper_client, {git, "https://github.com/valitydev/token-keeper-client", {branch, "master"}}},
|
||||||
|
|
||||||
%% Libraries generated with swagger-codegen-erlang from valitydev/swag-api-keys
|
%% Libraries generated with swagger-codegen-erlang from valitydev/swag-api-keys
|
||||||
@ -49,6 +47,11 @@
|
|||||||
{swag_client_apikeys,
|
{swag_client_apikeys,
|
||||||
{git, "https://github.com/valitydev/swag-api-keys-v2.git", {branch, "release/erlang/client/master"}}},
|
{git, "https://github.com/valitydev/swag-api-keys-v2.git", {branch, "release/erlang/client/master"}}},
|
||||||
|
|
||||||
|
%% Libraries for postgres interaction
|
||||||
|
{epgsql, {git, "https://github.com/epgsql/epgsql.git", {tag, "4.7.1"}}},
|
||||||
|
{epgsql_pool, {git, "https://github.com/wgnet/epgsql_pool", {branch, "master"}}},
|
||||||
|
{herd, {git, "https://github.com/wgnet/herd.git", {tag, "1.3.4"}}},
|
||||||
|
|
||||||
%% NOTE
|
%% NOTE
|
||||||
%% Pinning to version "1.11.2" from hex here causes constant upgrading and recompilation of the entire project
|
%% Pinning to version "1.11.2" from hex here causes constant upgrading and recompilation of the entire project
|
||||||
{jose, {git, "https://github.com/potatosalad/erlang-jose.git", {tag, "1.11.2"}}},
|
{jose, {git, "https://github.com/potatosalad/erlang-jose.git", {tag, "1.11.2"}}},
|
||||||
@ -106,6 +109,7 @@
|
|||||||
prometheus,
|
prometheus,
|
||||||
prometheus_cowboy,
|
prometheus_cowboy,
|
||||||
sasl,
|
sasl,
|
||||||
|
herd,
|
||||||
akm
|
akm
|
||||||
]},
|
]},
|
||||||
{sys_config, "./config/sys.config"},
|
{sys_config, "./config/sys.config"},
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
{<<"herd">>,
|
{<<"herd">>,
|
||||||
{git,"https://github.com/wgnet/herd.git",
|
{git,"https://github.com/wgnet/herd.git",
|
||||||
{ref,"934847589dcf5a6d2b02a1f546ffe91c04066f17"}},
|
{ref,"934847589dcf5a6d2b02a1f546ffe91c04066f17"}},
|
||||||
1},
|
0},
|
||||||
{<<"identdocstore_proto">>,
|
{<<"identdocstore_proto">>,
|
||||||
{git,"https://github.com/valitydev/identdocstore-proto.git",
|
{git,"https://github.com/valitydev/identdocstore-proto.git",
|
||||||
{ref,"0ab676da2bb23eb04c42e02325c40c413d74856e"}},
|
{ref,"0ab676da2bb23eb04c42e02325c40c413d74856e"}},
|
||||||
@ -133,11 +133,11 @@
|
|||||||
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.7">>},2},
|
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.7">>},2},
|
||||||
{<<"swag_client_apikeys">>,
|
{<<"swag_client_apikeys">>,
|
||||||
{git,"https://github.com/valitydev/swag-api-keys-v2.git",
|
{git,"https://github.com/valitydev/swag-api-keys-v2.git",
|
||||||
{ref,"de86e82de67071276030186f4de806f1a7ff0431"}},
|
{ref,"dd3ccc414fc7b08a9b62acad7aefdbc600566062"}},
|
||||||
0},
|
0},
|
||||||
{<<"swag_server_apikeys">>,
|
{<<"swag_server_apikeys">>,
|
||||||
{git,"https://github.com/valitydev/swag-api-keys-v2.git",
|
{git,"https://github.com/valitydev/swag-api-keys-v2.git",
|
||||||
{ref,"5e27ca5e3aa6f4b44b9677e870e5c8d557fee773"}},
|
{ref,"a0f3b2d46e9eba46c89b3bf81145629da8bf0a35"}},
|
||||||
0},
|
0},
|
||||||
{<<"tds_proto">>,
|
{<<"tds_proto">>,
|
||||||
{git,"https://github.com/valitydev/tds-proto.git",
|
{git,"https://github.com/valitydev/tds-proto.git",
|
||||||
|
Loading…
Reference in New Issue
Block a user