Packages
erlcloud
3.8.1
3.8.3
3.8.2
3.8.1
3.7.6
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.8
3.6.7
3.6.5
3.6.4
3.6.3
3.6.2
3.6.1
3.6.0
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.5
3.4.3
3.4.1
3.4.0
3.3.9
3.3.8
3.3.7
3.3.6
3.3.5
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.18
3.2.17
3.2.16
3.2.15
3.2.14
3.2.13
3.2.12
3.2.11
3.2.10
3.2.7
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.17
3.1.16
3.1.14
3.1.13
3.1.12
3.1.11
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.2
2.2.1
2.2.0
2.1.0
2.0.5
2.0.4
2.0.3
2.0.0
0.13.10
0.13.9
0.13.8
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.0
0.12.0
0.11.0
0.9.2
0.9.2-rc.1
0.9.1
0.9.0
AWS APIs library for Erlang
Current section
Files
Jump to
Current section
Files
src/erlcloud_sm.erl
-module(erlcloud_sm).
-author("joshua@halloapp.com").
-include("erlcloud.hrl").
-include("erlcloud_aws.hrl").
%%% Library initialization.
-export([new/2, new/3, new/4]).
%%% API
-export([
create_secret_binary/3, create_secret_binary/4, create_secret_binary/5,
create_secret_string/3, create_secret_string/4, create_secret_string/5,
delete_resource_policy/1, delete_resource_policy/2,
delete_secret/1, delete_secret/2, delete_secret/3,
describe_secret/1, describe_secret/2,
get_resource_policy/1, get_resource_policy/2,
get_secret_value/2, get_secret_value/3,
put_resource_policy/2, put_resource_policy/3, put_resource_policy/4,
put_secret_binary/3, put_secret_binary/4, put_secret_binary/5,
put_secret_string/3, put_secret_string/4, put_secret_string/5
]).
%%%------------------------------------------------------------------------------
%%% Shared types
%%%------------------------------------------------------------------------------
-type sm_response() :: {ok, proplists:proplist()} | {error, term()}.
-type get_secret_value_option() :: {version_id | version_stage, binary()}.
-type get_secret_value_options() :: [get_secret_value_option()].
%% replica region is expected to be a proplist of the following tuples:
%% [{<<"KmsKeyId">>, binary()}, {<<"Region">>, binary()}]
-type replica_region() :: [proplist()].
-type replica_regions() :: [replica_region()].
-type create_secret_option() :: {add_replica_regions, replica_regions()}
| {client_request_token, binary()}
| {description, binary()}
| {force_overwrite_replica_secret, boolean()}
| {kms_key_id, binary()}
| {secret_binary, binary()} %% Note AWS accepts either SecretBinary or SecretString,
| {secret_string, binary()} %% not both at the same time
| {tags, proplist()}.
-type create_secret_options() :: [create_secret_option()].
-type delete_secret_option() :: {force_delete_without_recovery, boolean()} %% Note you can't use both this parameter and RecoveryWindowInDays.
| {recovery_window_in_days, pos_integer()}. %% If none of these two options are specified then SM defaults to 30 day recovery window
-type delete_secret_options() :: [delete_secret_option()].
-type put_resource_policy_option() :: {block_public_policy, boolean()}.
-type put_resource_policy_options() :: [put_resource_policy_option()].
-type put_secret_value_option() :: {client_request_token, binary()}
| {secret_binary, binary()}
| {secret_string, binary()}
| {version_stages, [binary()]}.
-type put_secret_value_options() :: [put_secret_value_option()].
%%%------------------------------------------------------------------------------
%%% Library initialization.
%%%------------------------------------------------------------------------------
-spec new(string(), string()) -> aws_config().
new(AccessKeyID, SecretAccessKey) ->
#aws_config{
access_key_id = AccessKeyID,
secret_access_key = SecretAccessKey
}.
-spec new(string(), string(), string()) -> aws_config().
new(AccessKeyID, SecretAccessKey, Host) ->
#aws_config{
access_key_id = AccessKeyID,
secret_access_key = SecretAccessKey,
sm_host = Host
}.
-spec new(string(), string(), string(), non_neg_integer()) -> aws_config().
new(AccessKeyID, SecretAccessKey, Host, Port) ->
#aws_config{
access_key_id = AccessKeyID,
secret_access_key = SecretAccessKey,
sm_host = Host,
sm_port = Port
}.
%%------------------------------------------------------------------------------
%% CreateSecret - SecretBinary
%%------------------------------------------------------------------------------
%% @doc
%% Creates a new secret binary. The function internally base64-encodes the binary
%% as it is expected by the AWS SecretManager API, so raw blob is expected
%% to be passed as an attribute.
%%
%% ClientRequestToken is used by AWS for secret versioning purposes.
%% It is recommended to be a UUID type value, and is requred to be between
%% 32 and 64 characters.
%%
%% To store a text secret use CreateSecret - SecretString version of the function
%% instead.
%%
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_CreateSecret.html]
%%
%% Example:
%% Name = <<"my-secret-binary">>,
%% ClientRequestToken = <<"7537a353-0de0-4b98-bf55-f8365821ed36">>,
%% %% some binary to store (say, an RSA private key's exponent)
%% {[_E, _Pub], [_E, _N, Priv, _P1, _P2, _E1, _E2, _C]} = crypto:generate_key(rsa, {2048,65537}),
%% erlcloud_sm:create_secret_binary(Name, ClientRequestToken, Priv).
%% @end
%%------------------------------------------------------------------------------
-spec create_secret_binary(Name :: binary(), ClientRequestToken :: binary(),
SecretBinary :: binary()) -> sm_response().
create_secret_binary(Name, ClientRequestToken, SecretBinary) ->
create_secret_binary(Name, ClientRequestToken, SecretBinary, []).
-spec create_secret_binary(Name :: binary(), ClientRequestToken :: binary(),
SecretBinary :: binary(),
Opts :: create_secret_options()) -> sm_response().
create_secret_binary(Name, ClientRequestToken, SecretBinary, Opts) ->
create_secret_binary(Name, ClientRequestToken, SecretBinary, Opts, erlcloud_aws:default_config()).
-spec create_secret_binary(Name :: binary(), ClientRequestToken :: binary(),
SecretBinary :: binary(), Opts :: create_secret_options(),
Config :: aws_config()) -> sm_response().
create_secret_binary(Name, ClientRequestToken, SecretBinary, Opts, Config) ->
Secret = {secret_binary, base64:encode(SecretBinary)},
create_secret(Name, ClientRequestToken, [Secret | Opts], Config).
%%------------------------------------------------------------------------------
%% CreateSecret - SecretString
%%------------------------------------------------------------------------------
%% @doc
%% Creates a new secret string. The API expects SecretString is a text data to
%% encrypt and store in the SecretManager. It is recommended a JSON structure
%% of key/value pairs is used for the secret value.
%%
%% ClientRequestToken is used by AWS for secret versioning purposes.
%% It is recommended to be a UUID type value, and is requred to be between
%% 32 and 64 characters.
%%
%% To store a binary (which will be base64 encoded by the library, as it is
%% expected by AWS SecretManager API), use CreateSecret - SecretBinary version
%% of the function.
%%
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_CreateSecret.html]
%%
%% Example:
%% Name = <<"my-secret-string">>,
%% ClientRequestToken = <<"7537a353-0de0-4b98-bf55-f8365821ed37">>,
%% %% some user/password json to store
%% Secret = jsx:encode(#{<<"user">> => <<"my-user">>, <<"password">> => <<"superSecretPassword">>}),
%% erlcloud_sm:create_secret_string(Name, ClientRequestToken, Secret).
%%
%% @end
%%------------------------------------------------------------------------------
-spec create_secret_string(Name :: binary(), ClientRequestToken :: binary(),
SecretString :: binary()) -> sm_response().
create_secret_string(Name, ClientRequestToken, SecretString) ->
create_secret_string(Name, ClientRequestToken, SecretString, []).
-spec create_secret_string(Name :: binary(), ClientRequestToken :: binary(),
SecretString :: binary(), Opts :: create_secret_options()) -> sm_response().
create_secret_string(Name, ClientRequestToken, SecretString, Opts) ->
create_secret_string(Name, ClientRequestToken, SecretString, Opts, erlcloud_aws:default_config()).
-spec create_secret_string(Name :: binary(), ClientRequestToken :: binary(),
SecretString :: binary(), Opts :: create_secret_options(),
Config :: aws_config()) -> sm_response().
create_secret_string(Name, ClientRequestToken, SecretString, Opts, Config) ->
Secret = {secret_string, SecretString},
create_secret(Name, ClientRequestToken, [Secret | Opts], Config).
%%------------------------------------------------------------------------------
%% DeleteResourcePolicy
%%------------------------------------------------------------------------------
%% @doc
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteResourcePolicy.html]
%% @end
%%------------------------------------------------------------------------------
-spec delete_resource_policy(SecretId :: binary()) -> sm_response().
delete_resource_policy(SecretId) ->
delete_resource_policy(SecretId, erlcloud_aws:default_config()).
-spec delete_resource_policy(SecretId :: binary(), Config :: aws_config()) -> sm_response().
delete_resource_policy(SecretId, Config) ->
Json = [{<<"SecretId">>, SecretId}],
sm_request(Config, "secretsmanager.DeleteResourcePolicy", Json).
%%------------------------------------------------------------------------------
%% DeleteSecret
%%------------------------------------------------------------------------------
%% @doc
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DeleteSecret.html]
%% @end
%%------------------------------------------------------------------------------
-spec delete_secret(SecretId :: binary()) -> sm_response().
delete_secret(SecretId) ->
delete_secret(SecretId, []).
-spec delete_secret(SecretId :: binary(), Opts :: delete_secret_options()) -> sm_response().
delete_secret(SecretId, Opts) ->
delete_secret(SecretId, Opts, erlcloud_aws:default_config()).
-spec delete_secret(SecretId :: binary(), Opts :: delete_secret_options(),
Config :: aws_config()) -> sm_response().
delete_secret(SecretId, Opts, Config) ->
Json = lists:map(
fun
({force_delete_without_recovery, Val}) -> {<<"ForceDeleteWithoutRecovery">>, Val};
({recovery_window_in_days, Val}) -> {<<"RecoveryWindowInDays">>, Val};
(Other) -> Other
end,
[{<<"SecretId">>, SecretId} | Opts]),
sm_request(Config, "secretsmanager.DeleteSecret", Json).
%%------------------------------------------------------------------------------
%% DescribeSecret
%%------------------------------------------------------------------------------
%% @doc
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_DescibeSecret.html]
%% @end
%%------------------------------------------------------------------------------
-spec describe_secret(SecretId :: binary()) -> sm_response().
describe_secret(SecretId) ->
describe_secret(SecretId, erlcloud_aws:default_config()).
-spec describe_secret(SecretId :: binary(), Config :: aws_config()) -> sm_response().
describe_secret(SecretId, Config) ->
Json = [{<<"SecretId">>, SecretId}],
sm_request(Config, "secretsmanager.DescribeSecret", Json).
%%------------------------------------------------------------------------------
%% GetResourcePolicy
%%------------------------------------------------------------------------------
%% @doc
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetResourcePolicy.html]
%% @end
%%------------------------------------------------------------------------------
-spec get_resource_policy(SecretId :: binary()) -> sm_response().
get_resource_policy(SecretId) ->
get_resource_policy(SecretId, erlcloud_aws:default_config()).
-spec get_resource_policy(SecretId :: binary(), Config :: aws_config()) -> sm_response().
get_resource_policy(SecretId, Config) ->
Json = [{<<"SecretId">>, SecretId}],
sm_request(Config, "secretsmanager.GetResourcePolicy", Json).
%%------------------------------------------------------------------------------
%% GetSecretValue
%%------------------------------------------------------------------------------
%% @doc
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html]
%% @end
%%------------------------------------------------------------------------------
-spec get_secret_value(SecretId :: binary(), Opts :: get_secret_value_options()) -> sm_response().
get_secret_value(SecretId, Opts) ->
get_secret_value(SecretId, Opts, erlcloud_aws:default_config()).
-spec get_secret_value(SecretId :: binary(), Opts :: get_secret_value_options(),
Config :: aws_config()) -> sm_response().
get_secret_value(SecretId, Opts, Config) ->
Json = lists:map(
fun
({version_id, Val}) -> {<<"VersionId">>, Val};
({version_stage, Val}) -> {<<"VersionStage">>, Val};
(Other) -> Other
end,
[{<<"SecretId">>, SecretId} | Opts]),
sm_request(Config, "secretsmanager.GetSecretValue", Json).
%%------------------------------------------------------------------------------
%% PutResourcePolicy
%%------------------------------------------------------------------------------
%% @doc
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_PutResourcePolicy.html]
%% @end
%%------------------------------------------------------------------------------
-spec put_resource_policy(SecretId :: binary(), ResourcePolicy :: binary()) -> sm_response().
put_resource_policy(SecretId, ResourcePolicy) ->
put_resource_policy(SecretId, ResourcePolicy, []).
-spec put_resource_policy(SecretId :: binary(), ResourcePolicy :: binary(),
Opts :: put_resource_policy_options()) -> sm_response().
put_resource_policy(SecretId, ResourcePolicy, Opts) ->
put_resource_policy(SecretId, ResourcePolicy, Opts, erlcloud_aws:default_config()).
-spec put_resource_policy(SecretId :: binary(), ResourcePolicy :: binary(),
Opts :: put_resource_policy_options(),
Config :: aws_config()) -> sm_response().
put_resource_policy(SecretId, ResourcePolicy, Opts, Config) ->
Json = lists:map(
fun
({block_public_policy, Val}) -> {<<"BlockPublicPolicy">>, Val};
(Other) -> Other
end,
[{<<"SecretId">>, SecretId}, {<<"ResourcePolicy">>, ResourcePolicy} | Opts]),
sm_request(Config, "secretsmanager.PutResourcePolicy", Json).
%%------------------------------------------------------------------------------
%% PutSecretValue
%%------------------------------------------------------------------------------
%% @doc
%% SM API:
%% [https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_PutSecretValue.html]
%% @end
%%------------------------------------------------------------------------------
-spec put_secret_string(SecretId :: binary(), ClientRequestToken :: binary(),
SecretString :: binary()) -> sm_response().
put_secret_string(SecretId, ClientRequestToken, SecretString) ->
put_secret_string(SecretId, ClientRequestToken, SecretString, []).
-spec put_secret_string(SecretId :: binary(), ClientRequestToken :: binary(),
SecretString :: binary(), Opts :: put_secret_value_options()) -> sm_response().
put_secret_string(SecretId, ClientRequestToken, SecretString, Opts) ->
put_secret_string(SecretId, ClientRequestToken, SecretString, Opts, erlcloud_aws:default_config()).
-spec put_secret_string(SecretId :: binary(), ClientRequestToken :: binary(),
SecretString :: binary(), Opts :: put_secret_value_options(),
Config :: aws_config()) -> sm_response().
put_secret_string(SecretId, ClientRequestToken, SecretString, Opts, Config) ->
Secret = {secret_string, SecretString},
put_secret(SecretId, ClientRequestToken, [Secret | Opts], Config).
-spec put_secret_binary(SecretId :: binary(), ClientRequestToken :: binary(),
SecretBinary :: binary()) -> sm_response().
put_secret_binary(SecretId, ClientRequestToken, SecretBinary) ->
put_secret_binary(SecretId, ClientRequestToken, SecretBinary, []).
-spec put_secret_binary(SecretId :: binary(), ClientRequestToken :: binary(),
SecretBinary :: binary(), Opts :: put_secret_value_options()) -> sm_response().
put_secret_binary(SecretId, ClientRequestToken, SecretBinary, Opts) ->
put_secret_binary(SecretId, ClientRequestToken, SecretBinary, Opts, erlcloud_aws:default_config()).
-spec put_secret_binary(SecretId :: binary(), ClientRequestToken :: binary(),
SecretBinary :: binary(), Opts :: put_secret_value_options(),
Config :: aws_config()) -> sm_response().
put_secret_binary(SecretId, ClientRequestToken, SecretBinary, Opts, Config) ->
Secret = {secret_binary, base64:encode(SecretBinary)},
put_secret(SecretId, ClientRequestToken, [Secret | Opts], Config).
%%%------------------------------------------------------------------------------
%%% Internal Functions
%%%------------------------------------------------------------------------------
create_secret(SecretName, ClientRequestToken, Opts, Config) ->
Opts1 = [{client_request_token, ClientRequestToken} | Opts],
Json = create_secret_payload(SecretName, Opts1),
sm_request(Config, "secretsmanager.CreateSecret", Json).
put_secret(SecretId, ClientRequestToken, Opts, Config) ->
Json = lists:map(
fun
({secret_binary, Val}) -> {<<"SecretBinary">>, Val};
({secret_string, Val}) -> {<<"SecretString">>, Val};
({version_stages, Val}) -> {<<"VersionStages">>, Val};
(Other) -> Other
end,
[{<<"SecretId">>, SecretId}, {<<"ClientRequestToken">>, ClientRequestToken} | Opts]),
sm_request(Config, "secretsmanager.PutSecretValue", Json).
sm_request(Config, Operation, Body) ->
case erlcloud_aws:update_config(Config) of
{ok, Config1} ->
sm_request_no_update(Config1, Operation, Body);
{error, Reason} ->
{error, Reason}
end.
sm_request_no_update(Config, Operation, Body) ->
Payload = jsx:encode(Body),
Headers = headers(Config, Operation, Payload),
Request = #aws_request{service = sm,
uri = uri(Config),
method = post,
request_headers = Headers,
request_body = Payload},
case erlcloud_aws:request_to_return(erlcloud_retry:request(Config, Request, fun sm_result_fun/1)) of
{ok, {_RespHeaders, <<>>}} -> {ok, []};
{ok, {_RespHeaders, RespBody}} -> {ok, jsx:decode(RespBody, [{return_maps, false}])};
{error, _} = Error -> Error
end.
headers(Config, Operation, Body) ->
Headers = [{"host", Config#aws_config.sm_host},
{"x-amz-target", Operation},
{"content-type", "application/x-amz-json-1.1"}],
Region = erlcloud_aws:aws_region_from_host(Config#aws_config.sm_host),
erlcloud_aws:sign_v4_headers(Config, Headers, Body, Region, "secretsmanager").
uri(#aws_config{sm_scheme = Scheme, sm_host = Host} = Config) ->
lists:flatten([Scheme, Host, port_spec(Config)]).
port_spec(#aws_config{sm_port = 443}) ->
"";
port_spec(#aws_config{sm_port = Port}) ->
[":", erlang:integer_to_list(Port)].
-spec sm_result_fun(Request :: aws_request()) -> aws_request().
sm_result_fun(#aws_request{response_type = ok} = Request) ->
Request;
sm_result_fun(#aws_request{response_type = error,
error_type = aws, response_status = Status} = Request) when Status >= 500 ->
Request#aws_request{should_retry = true};
sm_result_fun(#aws_request{response_type = error, error_type = aws} = Request) ->
Request#aws_request{should_retry = false}.
create_secret_payload(SecretName, Opts) ->
Json = lists:map(
fun
({add_replica_regions, Val}) -> {<<"AddReplicaRegions">>, Val};
({client_request_token, Val}) -> {<<"ClientRequestToken">>, Val};
({description, Val}) -> {<<"Description">>, Val};
({force_overwrite_replica_secret, Val}) -> {<<"ForceOverwriteReplicaSecret">>, Val};
({kms_key_id, Val}) -> {<<"KmsKeyId">>, Val};
({secret_binary, Val}) -> {<<"SecretBinary">>, Val};
({secret_string, Val}) -> {<<"SecretString">>, Val};
({tags, Val}) -> {<<"Tags">>, Val};
(Other) -> Other
end,
[{<<"Name">>, SecretName} | Opts]),
Json.