Skip to content

Commit

Permalink
better documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
zemuldo committed Oct 24, 2023
1 parent ec70a82 commit 0b5993f
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 107 deletions.
101 changes: 98 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,112 @@ be found at <https://hexdocs.pm/ex_secrets>.

## Basic Usage

Secrets are first fetched using system environment. If found thats the value that is used. For this, no configuration is required.

```elixir
iex(1)> ExSecrets.get("FOO")
nil
iex(2)> System.put_env "FOO", "BAR"
:ok
iex(3)> ExSecrets.get("FOO")
"BAR"
iex(4)>
```

To overide secret fetch from system environment by default, Specify your own default provider.

```elixir
iex(1)> ExSecrets.get("FOO")
nil
iex(2)> Application.put_env(:ex_secrets, :default_provider, :dot_env)
:ok
iex(3)> ExSecrets.get("FOO")
nil
iex(4)> System.put_env "FOO", "BAR"
:ok
iex(5)> ExSecrets.get("FOO")
nil
iex(7)>
```

## Supported Providers

You can configure:

- Dot env file
- Azure Keyvault
- Azure Managed Identity
- Google Secret Manager

## Provider Config

Azure KeyVault configuration:

## HTTP Client Config
```
config :ex_secrets, :providers, %{
azure_key_vault: %{
tenant_id: "tenant-id",
client_id: "client-id",
client_secret: "client-secret",
key_vault_name: "key-vault-name"
}
}
```

Using certificate. You can use `client_certificate_path` or `client_certificate_string`. See Azure keyvault provider section for more details

```
config :ex_secrets, :providers, %{
azure_key_vault: %{
tenant_id: "tenant-id",
client_id: "client-id",
client_certificate_path: "/path-to/mycert.key",
client_certificate_string: "base 64 encoded string of the cert",
client_certificate_x5t: "x5t of the cert",
key_vault_name: "key-vault-name"
}
}
```

Azure Managed Identity Configuration:

## JSON Parser Config
```
config :ex_secrets, :providers, %{
azure_managed_identity: %{
key_vault_name: "key-vault-name"
}
}
```

Google Secret Manager

Using service account. You can use `service_account_credentials` or `service_account_credentials_path`. See Azure keyvault provider section for more details

```
config :ex_secrets, :providers, %{
google_secret_manager: %{
service_account_credentials: %{
"type" => "service_account",
"project_id" => "project-id",
"private_key_id" => "key-id",
"private_key" => "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----\n",
"client_email" => "secretaccess@project-id.iam.gserviceaccount.com",
"client_id" => "client-id",
"auth_uri" => "https://accounts.google.com/o/oauth2/auth",
"token_uri" => "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url" => "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url" => "https://www.googleapis.com/robot/v1/metadata/x509/secretaccess%40project-id.iam.gserviceaccount.com",
"universe_domain" => "googleapis.com"
},
service_account_credentials_path: "/path-to/cred.json"
}
}
```

## Configuration
Dotenv file:

```
config :ex_secrets, :providers, %{
dot_env: %{path: "/path/.env"}
}
```
12 changes: 3 additions & 9 deletions lib/cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,9 @@ defmodule ExSecrets.Cache do
end

def get(key) do
case System.get_env(key) do
value when is_binary(value) ->
value

nil ->
case GenServer.whereis(@store_name) do
nil -> System.get_env(key)
_ -> GenServer.call(@store_name, {:get, key})
end
case GenServer.whereis(@store_name) do
nil -> nil
_ -> GenServer.call(@store_name, {:get, key})
end
end

Expand Down
80 changes: 49 additions & 31 deletions lib/ex_secrets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,51 @@ defmodule ExSecrets do
Configuration is available for all secret providers:
Provider specific configurations.
Azure KeyVault configuration:
config :ex_secrets, :providers, %{
azure_key_vault: %{
tenant_id: "tenant-id",
client_id: "client-id",
client_secret: "client-secret",
key_vault_name: "key-vault-name"
}
Azure Managed Identity Configuration:
config :ex_secrets, :providers, %{
azure_managed_identity: %{
key_vault_name: "KKEYvault-name"
}
Dotenv file:
config :ex_secrets, :providers, %{
dot_env: %{path: "/path/.env"}
})
"""

alias ExSecrets.Cache
alias ExSecrets.Providers.SystemEnv
alias ExSecrets.Utils.Resolver
alias ExSecrets.Utils.SecretFetchLimiter

@doc """
Get secret value
## Examples
iex(1)> ExSecrets.get("FOO")
nil
iex(2)> Application.put_env(:ex_secrets, :default_provider, :dot_env)
:ok
iex(3)> ExSecrets.get("FOO")
nil
iex(4)> System.put_env "FOO", "BAR"
:ok
iex(5)> ExSecrets.get("FOO")
"BAR"
iex(6)> System.delete_env "FOO"
:ok
iex(7)> Application.delete_env(:ex_secrets, :default_provider)
:ok
"""
def get(key) do
case get_default_prider() do
case get_any_provider() do
provider when is_atom(provider) -> get(key, provider)
_ -> get_default(key)
end
end

@doc """
Get secret value with provider name
Get secret value with provider name.
## Examples
iex(1)> Application.put_env(:ex_secrets, :providers, %{dot_env: %{path: "test/support/fixtures/dot_env_test.env"}})
:ok
iex(2)> ExSecrets.get("JAVA", :dot_env)
"SCRIPT"
iex(3)> ExSecrets.get("JAVA")
"SCRIPT"
iex(4)> Application.delete_env(:ex_secrets, :providers)
:ok
"""
def get(key, provider) do
with value when not is_nil(value) <- Cache.get(key) do
Expand All @@ -60,6 +65,16 @@ defmodule ExSecrets do

@doc """
Get secret value with provider name and default value
## Examples
iex(1)> Application.put_env(:ex_secrets, :providers, %{dot_env: %{path: "test/support/fixtures/dot_env_test.env"}})
:ok
iex(2)> ExSecrets.get("ERL", :dot_env)
nil
iex(3)> ExSecrets.get("ERL", :dot_env, "ANG")
"ANG"
iex(4)> Application.delete_env(:ex_secrets, :providers)
:ok
"""
def get(key, provider, default) do
with value when not is_nil(value) <- Cache.get(key) do
Expand All @@ -81,21 +96,24 @@ defmodule ExSecrets do
value <- Kernel.apply(provider, :get, [key]) do
value
else
_ -> nil
_ -> get_default(key)
end
end

defp get_default(key) do
with value when not is_nil(value) <- Cache.get(key) do
value
with value when is_nil(value) <- Cache.get(key),
provider <- get_default_prider() do
Cache.pass_by(
key,
SecretFetchLimiter.allow(key, ExSecrets, :get_using_provider, [key, provider])
)
else
nil ->
Cache.pass_by(key, SystemEnv.get(key))
value -> value
end
end

defp get_default_prider() do
Application.get_env(:ex_secrets, :default_provider, get_any_provider())
Application.get_env(:ex_secrets, :default_provider, :system_env)
end

defp get_any_provider() do
Expand All @@ -104,7 +122,7 @@ defmodule ExSecrets do
provider <- Map.keys(providers) |> Kernel.++([:system_env]) |> Enum.at(0) do
provider
else
_ -> raise(ExSecrets.Exceptions.InvalidConfiguration)
_ -> nil
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/providers/azure_key_vault.ex
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ defmodule ExSecrets.Providers.AzureKeyVault do
defp build_claims_body(%{"cert" => cert}) when is_binary(cert) do
client_id = Config.provider_config_value(:azure_key_vault, :client_id)

case get_cert() |> IO.inspect() do
case get_cert() do
{:ok, cert} ->
URI.encode_query(%{
"client_id" => client_id,
Expand Down
10 changes: 7 additions & 3 deletions lib/providers/dot_env.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ defmodule ExSecrets.Providers.DotEnv do
defp read_env() do
path = Config.provider_config_value(:dot_env, :path)

with {:ok, s} <- File.read(path),
with true <- is_binary(path),
true <- File.exists?(path),
{:ok, s} <- File.read(path),
[_ | _] = envs <- String.split(s, ~r{(\r\n|\r|\n|\\n)}, trim: true) do
Enum.each(envs, &put_env/1)
else
Expand All @@ -40,11 +42,13 @@ defmodule ExSecrets.Providers.DotEnv do
defp get_scripted(key) do
path = Config.provider_config_value(:dot_env, :path)

with {:ok, s} <- File.read(path),
with true <- is_binary(path),
true <- File.exists?(path),
{:ok, s} <- File.read(path),
[_ | _] = envs <- String.split(s, ~r{(\r\n|\r|\n|\\n)}, trim: true) do
Enum.find(envs, &(get_k_v(&1) |> is_value(key))) |> get_v()
else
_ -> raise(raise(ExSecrets.Exceptions.InvalidConfiguration, ".env is not found"))
_ -> nil
end
end

Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ExSecrets.MixProject do
def project do
[
app: :ex_secrets,
version: "0.1.6",
version: "0.2.0",
elixir: "~> 1.13",
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
Expand Down
1 change: 1 addition & 0 deletions test/application_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ defmodule ExSecrets.ApplicationTestsUserManyProviders do

setup do
Application.put_env(:ex_secrets, :providers, %{dot_env: %{}})
on_exit(fn -> Application.delete_env(:ex_secrets, :providers) end)
end

test "test" do
Expand Down
Loading

0 comments on commit 0b5993f

Please sign in to comment.