From e74f084f63f2a9b15d66d6c3fec29e850328c3a1 Mon Sep 17 00:00:00 2001 From: ttt161 Date: Mon, 21 Aug 2023 17:41:40 +0300 Subject: [PATCH] TD-642: fix with test-transaction (#13) * fix key metadata * fix bouncer context * cleanup * update swag and revert security to revoke * TD-635: update code for new spec * add vault client * add tests * fix style * TD-642: fix revoke * fix vault interaction --------- Co-authored-by: anatoliy.losev --- .env | 1 - apps/akm/priv/mails/request_revoke.dtl | 2 +- apps/akm/src/akm.app.src | 3 +- apps/akm/src/akm_apikeys_handler.erl | 20 ++--- apps/akm/src/akm_apikeys_processing.erl | 54 +++++++------ apps/akm/src/akm_auth.erl | 45 +++++++---- apps/akm/src/akm_bouncer_context.erl | 3 +- apps/akm/src/akm_handler.erl | 16 +--- apps/akm/src/akm_mailer.erl | 18 +++++ apps/akm/src/akm_sup.erl | 103 +++++++++++++++++++++++- apps/akm/test/akm_basic_test_SUITE.erl | 15 ++-- apps/akm/test/akm_client.erl | 6 +- apps/akm/test/akm_cth.erl | 49 +++++++++-- config/sys.config | 5 ++ rebar.config | 3 + rebar.lock | 11 ++- 16 files changed, 271 insertions(+), 83 deletions(-) diff --git a/.env b/.env index 58f32ea..7ee78ed 100644 --- a/.env +++ b/.env @@ -2,4 +2,3 @@ SERVICE_NAME=api-key-mgmt-v2 OTP_VERSION=25.3 REBAR_VERSION=3.18 THRIFT_VERSION=0.14.2.3 -DATABASE_URL=postgresql://postgres:postgres@db/apikeymgmtv2 \ No newline at end of file diff --git a/apps/akm/priv/mails/request_revoke.dtl b/apps/akm/priv/mails/request_revoke.dtl index 9e76e63..f8e840c 100644 --- a/apps/akm/priv/mails/request_revoke.dtl +++ b/apps/akm/priv/mails/request_revoke.dtl @@ -3,4 +3,4 @@ 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/revoke_party/revoke-api-key/{{ api_key_id }}?apiKeyRevokeToken={{ revoke_token }} diff --git a/apps/akm/src/akm.app.src b/apps/akm/src/akm.app.src index 6e7d067..c4be209 100644 --- a/apps/akm/src/akm.app.src +++ b/apps/akm/src/akm.app.src @@ -28,7 +28,8 @@ snowflake, woody_user_identity, erlydtl, - gen_smtp + gen_smtp, + canal ]}, {env, []} ]}. diff --git a/apps/akm/src/akm_apikeys_handler.erl b/apps/akm/src/akm_apikeys_handler.erl index b5ec21c..630410e 100644 --- a/apps/akm/src/akm_apikeys_handler.erl +++ b/apps/akm/src/akm_apikeys_handler.erl @@ -141,21 +141,21 @@ prepare(OperationID = 'RequestRevokeApiKey', Params, Context, _Opts) -> end, {ok, #{authorize => Authorize, process => Process}}; prepare( - _OperationID = 'RevokeApiKey', - #{'partyId' := _PartyID, 'apiKeyId' := ApiKeyId, 'apiKeyRevokeToken' := Token}, - _Context, + OperationID = 'RevokeApiKey', + #{'partyId' := PartyID, 'apiKeyId' := ApiKeyId, 'apiKeyRevokeToken' := Token}, + Context, _Opts ) -> - %% Result = akm_apikeys_processing:get_api_key(ApiKeyId), + Result = akm_apikeys_processing:get_api_key(ApiKeyId), Authorize = fun() -> - %% ApiKey = extract_api_key(Result), - %% Prototypes = [{operation, #{id => OperationID, party => PartyID, api_key => ApiKey}}], - %% Resolution = akm_auth:authorize_operation(Prototypes, Context), - %% {ok, Resolution} - {ok, allowed} + ApiKey = extract_api_key(Result), + Prototypes = [{operation, #{id => OperationID, party => PartyID, api_key => ApiKey}}], + Resolution = akm_auth:authorize_operation(Prototypes, Context), + {ok, Resolution} end, Process = fun() -> - case akm_apikeys_processing:revoke(ApiKeyId, Token) of + #{woody_context := WoodyContext} = Context, + case akm_apikeys_processing:revoke(ApiKeyId, Token, WoodyContext) of ok -> akm_handler_utils:reply_ok(204); {error, not_found} -> diff --git a/apps/akm/src/akm_apikeys_processing.erl b/apps/akm/src/akm_apikeys_processing.erl index 5f3f7a7..df9e5fd 100644 --- a/apps/akm/src/akm_apikeys_processing.erl +++ b/apps/akm/src/akm_apikeys_processing.erl @@ -8,29 +8,28 @@ -export([get_api_key/1]). -export([list_api_keys/4]). -export([request_revoke/4]). --export([revoke/2]). +-export([revoke/3]). -type list_keys_response() :: #{ results => [map()], continuationToken := binary() }. +-type woody_context() :: woody_context:ctx(). + -spec issue_api_key(_, _, _) -> _. issue_api_key(PartyID, #{<<"name">> := Name} = ApiKey0, WoodyContext) -> Metadata0 = maps:get(<<"metadata">>, ApiKey0, #{}), %% REWORK ненормальный ID, переработать ID = akm_id:generate_snowflake_id(), ContextV1Fragment = bouncer_context_helpers:make_auth_fragment(#{ - method => <<"IssueApiKey">>, + method => <<"ApiKeyToken">>, scope => [#{party => #{id => PartyID}}], token => #{id => ID} }), - %% TODO ??? maybe wrong, review it !!! - ContextFragment = #ctx_ContextFragment{type = 'v1_thrift_binary', content = term_to_binary(ContextV1Fragment)}, + {encoded_fragment, ContextFragment} = bouncer_client:bake_context_fragment(ContextV1Fragment), Status = "active", - Metadata = Metadata0#{ - <<"party.id">> => PartyID - }, + Metadata = akm_auth:put_party_to_metadata(PartyID, Metadata0), Client = token_keeper_client:offline_authority(get_authority_id(), WoodyContext), case token_keeper_authority_offline:create(ID, ContextFragment, Metadata, Client) of {ok, #{token := Token}} -> @@ -42,8 +41,8 @@ issue_api_key(PartyID, #{<<"name">> := Name} = ApiKey0, WoodyContext) -> ), [ApiKey | _] = to_marshalled_maps(Columns, Rows), Resp = #{ - <<"AccessToken">> => marshall_access_token(Token), - <<"ApiKey">> => ApiKey + <<"accessToken">> => Token, + <<"apiKey">> => ApiKey }, {ok, Resp}; {error, {auth_data, already_exists}} -> @@ -70,7 +69,7 @@ list_api_keys(PartyId, Status, Limit, Offset) -> {ok, Columns, Rows} = epgsql_pool:query( main_pool, "SELECT id, name, status, metadata, created_at FROM apikeys where party_id = $1 AND status = $2 " - "ORDER BY created_at LIMIT $3 OFFSET $4", + "ORDER BY created_at DESC LIMIT $3 OFFSET $4", [PartyId, Status, Limit, Offset] ), case erlang:length(Rows) < Limit of @@ -115,19 +114,33 @@ request_revoke(Email, PartyID, ApiKeyId, Status) -> end end. --spec revoke(binary(), binary()) -> ok | {error, not_found}. -revoke(ApiKeyId, RevokeToken) -> +-spec revoke(binary(), binary(), woody_context()) -> ok | {error, not_found}. +revoke(ApiKeyId, RevokeToken, WoodyContext) -> case get_full_api_key(ApiKeyId) of {ok, #{ <<"pending_status">> := PendingStatus, <<"revoke_token">> := RevokeToken }} -> - {ok, 1} = epgsql_pool:query( - main_pool, - "UPDATE apikeys SET status = $1, revoke_token = null WHERE id = $2", - [PendingStatus, ApiKeyId] - ), - ok; + Client = token_keeper_client:offline_authority(get_authority_id(), WoodyContext), + try + epgsql_pool:transaction( + main_pool, + fun(Worker) -> + {ok, _} = token_keeper_authority_offline:revoke(ApiKeyId, Client), + epgsql_pool:query( + Worker, + "UPDATE apikeys SET status = $1, revoke_token = null WHERE id = $2", + [PendingStatus, ApiKeyId] + ) + end + ) + of + {ok, 1} -> ok + catch + Ex:Er -> + logger:error("Can`t revoke ApiKey ~p with error: ~p:~p", [ApiKeyId, Ex, Er]), + {error, not_found} + end; _ -> {error, not_found} end. @@ -210,8 +223,3 @@ marshall_api_key(#{ <<"status">> => Status, <<"metadata">> => decode_json(Metadata) }. - -marshall_access_token(Token) -> - #{ - <<"accessToken">> => Token - }. diff --git a/apps/akm/src/akm_auth.erl b/apps/akm/src/akm_auth.erl index 9d1446f..2ebc4e2 100644 --- a/apps/akm/src/akm_auth.erl +++ b/apps/akm/src/akm_auth.erl @@ -14,7 +14,9 @@ -export([authorize_api_key/3]). -export([authorize_operation/2]). --export([make_auth_context/1]). +-export([put_party_to_metadata/1]). +-export([put_party_to_metadata/2]). +-export([get_party_from_metadata/1]). -export_type([resolution/0]). -export_type([preauth_context/0]). @@ -101,19 +103,6 @@ authorize_operation(Prototypes, Context) -> Fragments1 = akm_bouncer_context:build(Prototypes, Fragments), 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})) -> @@ -130,10 +119,24 @@ parse_api_key(_) -> {error, unsupported_auth_scheme}. %% +-spec put_party_to_metadata(binary()) -> map(). +put_party_to_metadata(PartyId) -> + put_party_to_metadata(PartyId, #{}). + +-spec put_party_to_metadata(binary(), map()) -> map(). +put_party_to_metadata(PartyId, MetaData) -> + put_metadata(get_metadata_mapped_key(party_id), PartyId, MetaData). + +-spec get_party_from_metadata(map()) -> binary() | undefined. +get_party_from_metadata(MetaData) -> + get_metadata(get_metadata_mapped_key(party_id), MetaData). get_metadata(Key, Metadata) -> maps:get(Key, Metadata, undefined). +put_metadata(Key, Value, Metadata) -> + maps:put(Key, Value, Metadata). + get_metadata_mapped_key(Key) -> maps:get(Key, get_meta_mappings()). @@ -217,4 +220,18 @@ determine_peer_test_() -> ) ]. +-spec metadata_test() -> _. +metadata_test() -> + application:set_env( + akm, + auth_config, + #{metadata_mappings => #{party_id => <<"dev.vality.party.id">>}} + ), + ?assertEqual(#{<<"dev.vality.party.id">> => <<"qqq">>}, put_party_to_metadata(<<"qqq">>)), + ?assertEqual( + #{<<"dev.vality.party.id">> => <<"qqq">>, 1 => 2}, + put_party_to_metadata(<<"qqq">>, #{1 => 2}) + ), + ?assertEqual(<<"qqq">>, get_party_from_metadata(#{<<"dev.vality.party.id">> => <<"qqq">>})). + -endif. diff --git a/apps/akm/src/akm_bouncer_context.erl b/apps/akm/src/akm_bouncer_context.erl index 4231335..3dbb63a 100644 --- a/apps/akm/src/akm_bouncer_context.erl +++ b/apps/akm/src/akm_bouncer_context.erl @@ -70,10 +70,11 @@ api_key_entity( #{ api_key := #{ <<"id">> := ApiKeyId, - <<"metadata">> := #{<<"party.id">> := PartyId} + <<"metadata">> := MetaData } } ) -> + PartyId = akm_auth:get_party_from_metadata(MetaData), #base_Entity{id = ApiKeyId, party = PartyId, type = <<"ApiKey">>}; api_key_entity(_) -> undefined. diff --git a/apps/akm/src/akm_handler.erl b/apps/akm/src/akm_handler.erl index e3e35cb..ddbd2d0 100644 --- a/apps/akm/src/akm_handler.erl +++ b/apps/akm/src/akm_handler.erl @@ -77,7 +77,7 @@ authorize_api_key(OperationID, ApiKey, _Context, _HandlerOpts) -> ) -> akm_apikeys_handler:request_result(). handle_request(OperationID, Req, SwagContext, Opts) -> - Header = maps:get('X-Request-Deadline', Req, undefined), + #{'X-Request-Deadline' := Header} = Req, case akm_utils:parse_deadline(Header) of {ok, Deadline} -> 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) -> _ = logger:info("Processing request ~p", [OperationID]), try - SwagContext = do_authorize_api_key(OperationID, SwagContext0, WoodyContext0), + SwagContext = do_authorize_api_key(SwagContext0, WoodyContext0), WoodyContext = put_user_identity(WoodyContext0, get_auth_context(SwagContext)), Context = create_handler_context(OperationID, SwagContext, WoodyContext), ok = set_context_meta(Context), @@ -117,9 +117,7 @@ process_request(OperationID, Req, SwagContext0, Opts, WoodyContext0) -> end. -spec create_woody_context(akm_apikeys_handler:request_data()) -> woody_context:ctx(). -create_woody_context(RequestData) -> - %% use dynamic request_id if not presented - RequestID = maps:get('X-Request-ID', RequestData, new_request_id()), +create_woody_context(#{'X-Request-ID' := RequestID}) -> RpcID = #{trace_id := TraceID} = woody_context:new_rpc_id(genlib:to_binary(RequestID)), ok = scoper:add_meta(#{request_id => RequestID, trace_id => TraceID}), woody_context:new(RpcID, undefined, akm_woody_client:get_service_deadline(akm)). @@ -153,10 +151,7 @@ attach_deadline(undefined, Context) -> attach_deadline(Deadline, Context) -> woody_context:set_deadline(Deadline, Context). -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) -> +do_authorize_api_key(SwagContext = #{auth_context := PreAuthContext}, WoodyContext) -> case akm_auth:authorize_api_key(PreAuthContext, make_token_context(SwagContext), WoodyContext) of {ok, AuthContext} -> SwagContext#{auth_context => AuthContext}; @@ -193,6 +188,3 @@ process_woody_error(_Source, resource_unavailable, _Details) -> akm_handler_utils:reply_error(504); process_woody_error(_Source, result_unknown, _Details) -> akm_handler_utils:reply_error(504). - -new_request_id() -> - base64:encode(crypto:strong_rand_bytes(24)). diff --git a/apps/akm/src/akm_mailer.erl b/apps/akm/src/akm_mailer.erl index 5829465..29a454c 100644 --- a/apps/akm/src/akm_mailer.erl +++ b/apps/akm/src/akm_mailer.erl @@ -93,3 +93,21 @@ wait_result() -> 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). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +-spec test() -> _. + +-spec to_int_test() -> _. +to_int_test() -> + ?assertEqual(123, to_int(123)), + ?assertEqual(123, to_int(<<"123">>)), + ?assertEqual(123, to_int("123")). + +-spec wait_test() -> _. +wait_test() -> + erlang:send_after(timeout() + 10, self(), timeout), + ?assertEqual({error, {failed_to_send, sending_email_timeout}}, wait_result()). + +-endif. diff --git a/apps/akm/src/akm_sup.erl b/apps/akm/src/akm_sup.erl index 892f31f..124334e 100644 --- a/apps/akm/src/akm_sup.erl +++ b/apps/akm/src/akm_sup.erl @@ -9,6 +9,9 @@ -define(TEMPLATE_FILE, "request_revoke.dtl"). -define(TEMPLATE_DIR, "/opt/api-key-mgmt-v2/templates"). +-define(VAULT_TOKEN_PATH, "/var/run/secrets/kubernetes.io/serviceaccount/token"). +-define(VAULT_ROLE, <<"api-key-mgmt-v2">>). +-define(VAULT_KEY_PG_CREDS, <<"api-key-mgmt-v2/pg_creds">>). %% API -export([start_link/0]). @@ -26,6 +29,7 @@ start_link() -> -spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. init([]) -> + ok = maybe_set_secrets(), ok = dbinit(), {ok, _} = compile_template(), {LogicHandlers, LogicHandlerSpecs} = get_logic_handler_info(), @@ -71,10 +75,10 @@ get_env_var(Name) -> dbinit() -> WorkDir = get_env_var("WORK_DIR"), - EnvPath = WorkDir ++ "/.env", + _ = set_database_url(), MigrationsPath = WorkDir ++ "/migrations", Cmd = "run", - case akm_db_migration:process(["-e", EnvPath, "-d", MigrationsPath, Cmd]) of + case akm_db_migration:process(["-d", MigrationsPath, Cmd]) of ok -> ok; {error, Reason} -> throw({migrations_error, Reason}) end. @@ -95,3 +99,98 @@ default_template_file() -> template_file() -> filename:join([?TEMPLATE_DIR, ?TEMPLATE_FILE]). + +set_database_url() -> + {ok, #{ + host := PgHost, + port := PgPort, + username := PgUser, + password := PgPassword, + database := DbName + }} = application:get_env(akm, epsql_connection), + %% DATABASE_URL=postgresql://postgres:postgres@db/apikeymgmtv2 + PgPortStr = erlang:integer_to_list(PgPort), + Value = + "postgresql://" ++ PgUser ++ ":" ++ PgPassword ++ "@" ++ PgHost ++ ":" ++ PgPortStr ++ "/" ++ DbName, + true = os:putenv("DATABASE_URL", Value). + +maybe_set_secrets() -> + TokenPath = application:get_env(akm, vault_token_path, ?VAULT_TOKEN_PATH), + case vault_client_auth(TokenPath) of + ok -> + Key = application:get_env(akm, vault_key_pg_creds, ?VAULT_KEY_PG_CREDS), + set_secrets(canal:read(Key)); + Error -> + logger:error("can`t auth vault client with error: ~p", [Error]), + skip + end, + ok. + +set_secrets( + { + ok, #{ + <<"pg_creds">> := #{ + <<"pg_user">> := PgUser, + <<"pg_password">> := PgPassword + } + } + } +) -> + logger:info("postgres credentials successfuly read from vault (as json)"), + {ok, ConnOpts} = application:get_env(akm, epsql_connection), + application:set_env( + akm, + epsql_connection, + ConnOpts#{ + username => unicode:characters_to_list(PgUser), + password => unicode:characters_to_list(PgPassword) + } + ), + ok; +set_secrets({ok, #{<<"pg_creds">> := PgCreds}}) -> + logger:info("postgres credentials successfuly read from vault (as string)"), + set_secrets({ok, #{<<"pg_creds">> => jsx:decode(PgCreds, [return_maps])}}); +set_secrets(Error) -> + logger:error("can`t read postgres credentials from vault with error: ~p", [Error]), + skip. + +vault_client_auth(TokenPath) -> + case read_maybe_linked_file(TokenPath) of + {ok, Token} -> + Role = application:get_env(akm, vault_role, ?VAULT_ROLE), + canal:auth({kubernetes, Role, Token}); + Error -> + Error + end. + +read_maybe_linked_file(MaybeLinkName) -> + case file:read_link(MaybeLinkName) of + {error, enoent} = Result -> + Result; + {error, einval} -> + file:read_file(MaybeLinkName); + {ok, Filename} -> + file:read_file(maybe_expand_relative(MaybeLinkName, Filename)) + end. + +maybe_expand_relative(BaseFilename, Filename) -> + filename:absname_join(filename:dirname(BaseFilename), Filename). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +-spec test() -> _. + +-spec set_secrets_error_test() -> _. +set_secrets_error_test() -> + ?assertEqual(skip, set_secrets(error)). + +-spec read_error_test() -> _. +read_error_test() -> + ?assertEqual({error, enoent}, read_maybe_linked_file("unknown_file")). + +-spec vault_auth_error_test() -> _. +vault_auth_error_test() -> + ?assertEqual({error, enoent}, vault_client_auth("unknown_file")). + +-endif. diff --git a/apps/akm/test/akm_basic_test_SUITE.erl b/apps/akm/test/akm_basic_test_SUITE.erl index 97f59b6..4878469 100644 --- a/apps/akm/test/akm_basic_test_SUITE.erl +++ b/apps/akm/test/akm_basic_test_SUITE.erl @@ -96,13 +96,13 @@ issue_get_key_success_test(Config) -> }, PartyId = <<"test_party">>, #{ - <<"AccessToken">> := #{<<"accessToken">> := ?ACCESS_TOKEN}, - <<"ApiKey">> := #{ + <<"accessToken">> := ?ACCESS_TOKEN, + <<"apiKey">> := #{ <<"createdAt">> := _DateTimeRfc3339, <<"id">> := ApiKeyId, <<"metadata">> := #{ <<"key">> := <<"value">>, - <<"party.id">> := <<"test_party">> + <<"dev.vality.party.id">> := <<"test_party">> }, <<"name">> := <<"live-site-integration">>, <<"status">> := <<"active">> @@ -128,9 +128,9 @@ list_keys_test(Config) -> %% check empty list #{<<"results">> := []} = akm_client:list_keys(Host, Port, PartyId), - ListKeys = lists:foldl( + ExpectedList = lists:foldl( fun(Num, Acc) -> - #{<<"ApiKey">> := ApiKey} = akm_client:issue_key( + #{<<"apiKey">> := ApiKey} = akm_client:issue_key( Host, Port, PartyId, @@ -141,7 +141,6 @@ list_keys_test(Config) -> [], lists:seq(1, 10) ), - ExpectedList = lists:reverse(ListKeys), %% check one batch #{ @@ -177,7 +176,7 @@ revoke_key_w_email_error_test(Config) -> PartyId = <<"revoke_party">>, #{ - <<"ApiKey">> := #{ + <<"apiKey">> := #{ <<"id">> := ApiKeyId } } = akm_client:issue_key(Host, Port, PartyId, #{name => <<"live-site-integration">>}), @@ -191,7 +190,7 @@ revoke_key_test(Config) -> PartyId = <<"revoke_party">>, #{ - <<"ApiKey">> := #{ + <<"apiKey">> := #{ <<"id">> := ApiKeyId } } = akm_client:issue_key(Host, Port, PartyId, #{name => <<"live-site-integration">>}), diff --git a/apps/akm/test/akm_client.erl b/apps/akm/test/akm_client.erl index 4a0cace..05175ec 100644 --- a/apps/akm/test/akm_client.erl +++ b/apps/akm/test/akm_client.erl @@ -71,7 +71,11 @@ request_revoke_key(Host, Port, PartyId, ApiKeyId) -> -spec revoke_key(inet:hostname() | inet:ip_address(), inet:port_number(), binary()) -> any(). revoke_key(Host, Port, PathWithQuery) -> - Headers = [], + Headers = [ + {<<"X-Request-ID">>, <<"request_revoke">>}, + {<<"content-type">>, <<"application/json; charset=utf-8">>}, + {<<"Authorization">>, <<"Bearer sffsdfsfsdfsdfs">>} + ], ConnPid = connect(Host, Port), Answer = get(ConnPid, PathWithQuery, Headers), disconnect(ConnPid), diff --git a/apps/akm/test/akm_cth.erl b/apps/akm/test/akm_cth.erl index 0033c9d..2d4f353 100644 --- a/apps/akm/test/akm_cth.erl +++ b/apps/akm/test/akm_cth.erl @@ -76,6 +76,7 @@ set_environment(State) -> prepare_config(State) -> AkmAddress = "::", + WorkDir = get_env_var("WORK_DIR"), AkmPort = get_free_port(), PgConfig = get_pg_config(), SysConfig = [ @@ -83,6 +84,8 @@ prepare_config(State) -> {ip, AkmAddress}, {port, AkmPort}, {transport, thrift}, + {bouncer_ruleset_id, <<"service/authz/api">>}, + {vault_token_path, WorkDir ++ "/rebar.config"}, {health_check, #{ disk => {erl_health, disk, ["/", 99]}, memory => {erl_health, cg_memory, [99]}, @@ -106,6 +109,11 @@ prepare_config(State) -> password => "password", username => "username" }} + ]}, + + {canal, [ + {url, "http://vault:8200"}, + {engine, kvv2} ]} ], @@ -120,9 +128,29 @@ prepare_config(State) -> mock_services(State) -> meck:expect( - akm_bouncer, + canal, + auth, + fun(_) -> ok end + ), + meck:expect( + canal, + read, + fun(_) -> + { + ok, + #{ + <<"pg_creds">> => jsx:encode(#{ + <<"pg_user">> => get_env_var("POSTGRES_USER", "postgres"), + <<"pg_password">> => get_env_var("POSTGRES_PASSWORD", "postgres") + }) + } + } + end + ), + meck:expect( + bouncer_client, judge, - fun(_, _) -> allowed end + fun(_, _, _) -> allowed end ), meck:expect( token_keeper_authority_offline, @@ -132,10 +160,17 @@ mock_services(State) -> end ), meck:expect( - akm_auth, - authorize_api_key, + token_keeper_authority_offline, + revoke, + fun(_ID, _Client) -> + {ok, ok} + end + ), + meck:expect( + token_keeper_authenticator, + authenticate, fun(_PreAuthContext, _TokenContext, _WoodyContext) -> - {ok, {authorized, ?AUTH_CTX}} + {ok, ?AUTH_CTX} end ), meck:expect( @@ -163,8 +198,8 @@ get_pg_config() -> #{ host => get_env_var("POSTGRES_HOST"), port => list_to_integer(get_env_var("POSTGRES_PORT", "5432")), - username => get_env_var("POSTGRES_USER", "postgres"), - password => get_env_var("POSTGRES_PASSWORD", "postgres"), + %% username => get_env_var("POSTGRES_USER", "postgres"), + %% password => get_env_var("POSTGRES_PASSWORD", "postgres"), database => get_env_var("POSTGRES_DB", "apikeymgmtv2") }. diff --git a/config/sys.config b/config/sys.config index 75bdc0f..e8c4d83 100644 --- a/config/sys.config +++ b/config/sys.config @@ -146,5 +146,10 @@ {prometheus, [ {collectors, [default]} + ]}, + + {canal, [ + {url, "http://vault:8200"}, + {engine, kvv2} ]} ]. diff --git a/rebar.config b/rebar.config index 8f62fa7..beedc05 100644 --- a/rebar.config +++ b/rebar.config @@ -47,6 +47,9 @@ {swag_client_apikeys, {git, "https://github.com/valitydev/swag-api-keys-v2.git", {branch, "release/erlang/client/master"}}}, + %% Vault client for getting secrets + {canal, {git, "https://github.com/valitydev/canal", {branch, 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"}}}, diff --git a/rebar.lock b/rebar.lock index fa1b2c9..3f1adfd 100644 --- a/rebar.lock +++ b/rebar.lock @@ -16,6 +16,10 @@ {ref,"b23c905db51915737fdab80c2a3af4c546b32799"}}, 0}, {<<"cache">>,{pkg,<<"cache">>,<<"2.3.3">>},1}, + {<<"canal">>, + {git,"https://github.com/valitydev/canal", + {ref,"621d3821cd0a6036fee75d8e3b2d17167f3268e4"}}, + 0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"2.6.1">>},2}, {<<"cg_mon">>, {git,"https://github.com/rbkmoney/cg_mon.git", @@ -104,6 +108,7 @@ {git,"https://github.com/potatosalad/erlang-jose.git", {ref,"991649695aaccd92c8effb1c1e88e6159fe8e9a6"}}, 0}, + {<<"jsone">>,{pkg,<<"jsone">>,<<"1.8.0">>},1}, {<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},1}, {<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},2}, {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},2}, @@ -133,11 +138,11 @@ {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.7">>},2}, {<<"swag_client_apikeys">>, {git,"https://github.com/valitydev/swag-api-keys-v2.git", - {ref,"dd3ccc414fc7b08a9b62acad7aefdbc600566062"}}, + {ref,"a56933dbc52bcf4ea68ae91bba1e8869730ae71d"}}, 0}, {<<"swag_server_apikeys">>, {git,"https://github.com/valitydev/swag-api-keys-v2.git", - {ref,"a0f3b2d46e9eba46c89b3bf81145629da8bf0a35"}}, + {ref,"66723162eeba4b6589df24b19066d626178067ab"}}, 0}, {<<"tds_proto">>, {git,"https://github.com/valitydev/tds-proto.git", @@ -183,6 +188,7 @@ {<<"gun">>, <<"160A9A5394800FCBA41BC7E6D421295CF9A7894C2252C0678244948E3336AD73">>}, {<<"hackney">>, <<"99DA4674592504D3FB0CFEF0DB84C3BA02B4508BAE2DFF8C0108BAA0D6E0977C">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, + {<<"jsone">>, <<"347FF1FA700E182E1F9C5012FA6D737B12C854313B9AE6954CA75D3987D6C06D">>}, {<<"jsx">>, <<"D12516BAA0BB23A59BB35DCCAF02A1BD08243FCBB9EFE24F2D9D056CCFF71268">>}, {<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>}, {<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>}, @@ -205,6 +211,7 @@ {<<"gun">>, <<"A10BC8D6096B9502205022334F719CC9A08D9ADCFBFC0DBEE9EF31B56274A20B">>}, {<<"hackney">>, <<"DE16FF4996556C8548D512F4DBE22DD58A587BF3332E7FD362430A7EF3986B16">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, + {<<"jsone">>, <<"08560B78624A12E0B5E7EC0271EC8CA38EF51F63D84D84843473E14D9B12618C">>}, {<<"jsx">>, <<"0C5CC8FDC11B53CC25CF65AC6705AD39E54ECC56D1C22E4ADB8F5A53FB9427F3">>}, {<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>}, {<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>},