Skip to content

Commit

Permalink
adds set secret
Browse files Browse the repository at this point in the history
  • Loading branch information
zemuldo committed Oct 29, 2023
1 parent c16f23f commit 411f1f0
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 16 deletions.
22 changes: 18 additions & 4 deletions lib/ex_secrets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ defmodule ExSecrets do
iex> Application.delete_env(:ex_secrets, :providers)
:ok
"""
@spec get(String.t(), provider: atom(), default_value: any()) :: String.t() | nil
def get(key, opts \\ [])

def get(key, []) do
Expand Down Expand Up @@ -120,13 +121,26 @@ defmodule ExSecrets do
end
end

@doc """
Set secret value.
Supported for providers:
- :system_env
- :azure_key_vault
- :azure_managed_identity
- :google_secret_manager
Calling this function requires the provider to be configured with credentials that allow create secrets like Secret Admionistrator in Azure Key Vault.
"""

@spec set(Atom.t(), String.t(), String.t()) :: :ok | :error
def set(provider, key, value) do
with provider when is_atom(provider) <- Resolver.call(provider) do
Kernel.apply(provider, :set, [key, value])
with provider when is_atom(provider) <- Resolver.call(provider),
:ok <- Kernel.apply(provider, :set, [key, value]) do
Cache.save(key, value)
else
{:error, message} -> {:error, message}
_ -> {:error, :provider_not_found}
_ -> :error
end
end

Expand Down
10 changes: 5 additions & 5 deletions lib/providers/azure_key_vault.ex
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ defmodule ExSecrets.Providers.AzureKeyVault do
else
nil ->
case set_secret(name, value, %{}, nil) do
{:ok, value, _} ->
value
{:ok, _value, _} ->
:ok

_ ->
{:error, "Could not set secret, check credentials and permissions"}
:error
end
end
end
Expand All @@ -140,8 +140,8 @@ defmodule ExSecrets.Providers.AzureKeyVault do

def handle_call({:set, name, value}, _from, state) do
case set_secret(name, value, state, get_current_epoch()) do
{:ok, secret, state} -> {:reply, secret, state}
_ -> {:reply, nil, state}
{:ok, _secret, state} -> {:reply, :ok, state}
_ -> {:reply, :error, state}
end
end

Expand Down
10 changes: 5 additions & 5 deletions lib/providers/azure_managed_identity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ defmodule ExSecrets.Providers.AzureManagedIdentity do
else
nil ->
case set_secret(name, value, %{}, nil) do
{:ok, value, _} ->
value
{:ok, _value, _} ->
:ok

_ ->
{:error, "Could not set secret, check credentials and permissions"}
:error
end
end
end
Expand All @@ -77,8 +77,8 @@ defmodule ExSecrets.Providers.AzureManagedIdentity do

def handle_call({:set, name, value}, _from, state) do
case set_secret(name, value, state, get_current_epoch()) do
{:ok, secret, state} -> {:reply, secret, state}
_ -> {:reply, nil, state}
{:ok, _secret, state} -> {:reply, :ok, state}
_ -> {:reply, :error, state}
end
end

Expand Down
95 changes: 93 additions & 2 deletions lib/providers/google_secret_manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,22 @@ defmodule ExSecrets.Providers.GoogleSecretManager do
end
end

def set(_name, _value) do
{:error, "Not implemented"}
def set(name, value) do
name = name |> String.split("_") |> Enum.join("-")

with process when not is_nil(process) <-
GenServer.whereis(@process_name) do
GenServer.call(@process_name, {:set, name, value})
else
nil ->
case set_secret(name, value, %{}, nil) do
{:ok, _value, _} ->
:ok

_ ->
:error
end
end
end

def handle_call({:get, name}, _from, state) do
Expand All @@ -85,6 +99,13 @@ defmodule ExSecrets.Providers.GoogleSecretManager do
end
end

def handle_call({:set, name, value}, _from, state) do
case set_secret(name, value, state, get_current_epoch()) do
{:ok, _secret, state} -> {:reply, :ok, state}
_ -> {:reply, :error, state}
end
end

defp get_secret(
name,
%{"access_token" => access_token, "issued_at" => issued_at, "expires_in" => expires_in} =
Expand All @@ -110,6 +131,32 @@ defmodule ExSecrets.Providers.GoogleSecretManager do
end
end

defp set_secret(
name,
value,
%{"access_token" => access_token, "issued_at" => issued_at, "expires_in" => expires_in} =
state,
current_time
)
when issued_at + expires_in - current_time > 5 do
with {:ok, value} <- set_secret_call(name, value, access_token, state.cred),
true <- is_binary(value) do
{:ok, value, state}
else
_ -> {:error, "Failed to get secret"}
end
end

defp set_secret(name, value, state, _) do
with {:ok, cred} <- get_service_account_credentials(),
{:ok, %{"access_token" => access_token} = new_state} <- get_access_token(cred),
{:ok, value} <- set_secret_call(name, value, access_token, cred) do
{:ok, value, state |> Map.merge(new_state)}
else
_ -> {:error, "Failed to get secret"}
end
end

defp get_secret_call(name, access_token, cred) do
client = http_adpater()

Expand All @@ -127,6 +174,50 @@ defmodule ExSecrets.Providers.GoogleSecretManager do
end
end

defp set_secret_call(name, value, access_token, cred) do
client = http_adpater()

payload = %{
name: "projects/#{cred["project_id"]}/secrets/#{name}",
replication: %{
automatic: %{}
}
}

url =
"https://secretmanager.googleapis.com/v1/projects/#{cred["project_id"]}/secrets?secretId=#{name}"

with {:ok, %{status_code: status}} when status in [200, 409] <-
client.post(url, Poison.encode!(payload), %{
"Authorization" => "Bearer #{access_token}",
"content-type" => "application/json"
}),
{:ok, %{status_code: 200}} <- set_secret_version_call(name, value, access_token, cred) do
{:ok, value}
else
_ ->
{:error, "Failed to create secret"}
end
end

defp set_secret_version_call(name, value, access_token, cred) do
client = http_adpater()

payload = %{
payload: %{
data: Base.encode64(value)
}
}

url =
"https://secretmanager.googleapis.com/v1/projects/#{cred["project_id"]}/secrets/#{name}:addVersion"

client.post(url, Poison.encode!(payload), %{
"Authorization" => "Bearer #{access_token}",
"content-type" => "application/json"
})
end

defp get_access_token(cred) do
client = http_adpater()

Expand Down

0 comments on commit 411f1f0

Please sign in to comment.