Store Last.fm API key with encrypted secret

This commit is contained in:
Claudio Ortolina
2025-05-06 15:18:14 +01:00
parent d5b29b20bb
commit 95f71519d3
12 changed files with 87 additions and 9 deletions
+15
View File
@@ -49,6 +49,21 @@ config :music_library, MusicLibrary.Repo,
MusicLibrary.Repo.extension_path("unicode") MusicLibrary.Repo.extension_path("unicode")
] ]
cloak_encryption_key =
System.get_env("CLOAK_ENCRYPTION_KEY") ||
raise """
environment variable CLOAK_ENCRYPTION_KEY is missing.
Generate one with: iex> 32 |> :crypto.strong_rand_bytes() |> Base.encode64()
"""
config :music_library, MusicLibrary.Vault,
json_library: JSON,
ciphers: [
default:
{Cloak.Ciphers.AES.GCM,
tag: "AES.GCM.V1", key: Base.decode64!(cloak_encryption_key), iv_length: 12}
]
if config_env() == :prod do if config_env() == :prod do
database_path = database_path =
System.get_env("DATABASE_PATH") || System.get_env("DATABASE_PATH") ||
+1
View File
@@ -9,6 +9,7 @@ defmodule MusicLibrary.Application do
def start(_type, _args) do def start(_type, _args) do
children = [ children = [
MusicLibraryWeb.Telemetry, MusicLibraryWeb.Telemetry,
MusicLibrary.Vault,
MusicLibrary.Repo, MusicLibrary.Repo,
MusicLibrary.ErrorRepo, MusicLibrary.ErrorRepo,
MusicLibrary.BackgroundRepo, MusicLibrary.BackgroundRepo,
+3
View File
@@ -0,0 +1,3 @@
defmodule MusicLibrary.Encrypted.Binary do
use Cloak.Ecto.Binary, vault: MusicLibrary.Vault
end
+14
View File
@@ -0,0 +1,14 @@
defmodule MusicLibrary.Secrets do
alias MusicLibrary.Repo
alias MusicLibrary.Secrets.Secret
def store(name, value) do
%Secret{}
|> Secret.changeset(%{name: name, value: value})
|> Repo.insert(on_conflict: :replace_all)
end
def get!(name) do
Repo.get!(Secret, name)
end
end
+17
View File
@@ -0,0 +1,17 @@
defmodule MusicLibrary.Secrets.Secret do
use Ecto.Schema
import Ecto.Changeset
@primary_key {:name, :string, autogenerate: false}
schema "secrets" do
field :value, MusicLibrary.Encrypted.Binary
timestamps(type: :utc_datetime)
end
def changeset(secret, attrs) do
secret
|> cast(attrs, [:name, :value])
|> validate_required([:name, :value])
end
end
+3
View File
@@ -0,0 +1,3 @@
defmodule MusicLibrary.Vault do
use Cloak.Vault, otp_app: :music_library
end
@@ -1,18 +1,18 @@
defmodule MusicLibraryWeb.LastFmController do defmodule MusicLibraryWeb.LastFmController do
use MusicLibraryWeb, :controller use MusicLibraryWeb, :controller
def callback(conn, %{"token" => token}) do alias MusicLibrary.Secrets
case LastFm.get_session(token) do
{:ok, session} ->
conn
|> put_session(:last_fm_user, session.name)
|> put_session(:last_fm_key, session.key)
|> put_flash(:info, "Successfully authenticated with Last.fm.")
|> redirect(to: ~p"/")
def callback(conn, %{"token" => token}) do
with {:ok, session} <- LastFm.get_session(token),
{:ok, _secret} <- Secrets.store("last_fm_key", session.key) do
conn
|> put_flash(:info, gettext("Successfully connected your Last.fm account"))
|> redirect(to: ~p"/")
else
{:error, reason} -> {:error, reason} ->
conn conn
|> put_flash(:error, "Failed to authenticate with Last.fm: #{reason}") |> put_flash(:error, "Failed to connect your Last.fm account: #{reason}")
|> redirect(to: ~p"/") |> redirect(to: ~p"/")
end end
end end
+1
View File
@@ -50,6 +50,7 @@ defmodule MusicLibrary.MixProject do
{:ecto_sql, "~> 3.10"}, {:ecto_sql, "~> 3.10"},
{:ecto_sqlite3, ">= 0.0.0"}, {:ecto_sqlite3, ">= 0.0.0"},
{:ecto_sqlite3_extras, "~> 1.2.2"}, {:ecto_sqlite3_extras, "~> 1.2.2"},
{:cloak_ecto, "~> 1.3"},
# UI # UI
{:phoenix_html, "~> 4.2"}, {:phoenix_html, "~> 4.2"},
+2
View File
@@ -4,6 +4,8 @@
"castore": {:hex, :castore, "1.0.12", "053f0e32700cbec356280c0e835df425a3be4bc1e0627b714330ad9d0f05497f", [:mix], [], "hexpm", "3dca286b2186055ba0c9449b4e95b97bf1b57b47c1f2644555879e659960c224"}, "castore": {:hex, :castore, "1.0.12", "053f0e32700cbec356280c0e835df425a3be4bc1e0627b714330ad9d0f05497f", [:mix], [], "hexpm", "3dca286b2186055ba0c9449b4e95b97bf1b57b47c1f2644555879e659960c224"},
"cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"},
"circular_buffer": {:hex, :circular_buffer, "0.4.1", "477f370fd8cfe1787b0a1bade6208bbd274b34f1610e41f1180ba756a7679839", [:mix], [], "hexpm", "633ef2e059dde0d7b89bbab13b1da9d04c6685e80e68fbdf41282d4fae746b72"}, "circular_buffer": {:hex, :circular_buffer, "0.4.1", "477f370fd8cfe1787b0a1bade6208bbd274b34f1610e41f1180ba756a7679839", [:mix], [], "hexpm", "633ef2e059dde0d7b89bbab13b1da9d04c6685e80e68fbdf41282d4fae746b72"},
"cloak": {:hex, :cloak, "1.1.4", "aba387b22ea4d80d92d38ab1890cc528b06e0e7ef2a4581d71c3fdad59e997e7", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "92b20527b9aba3d939fab0dd32ce592ff86361547cfdc87d74edce6f980eb3d7"},
"cloak_ecto": {:hex, :cloak_ecto, "1.3.0", "0de127c857d7452ba3c3367f53fb814b0410ff9c680a8d20fbe8b9a3c57a1118", [:mix], [{:cloak, "~> 1.1.1", [hex: :cloak, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "314beb0c123b8a800418ca1d51065b27ba3b15f085977e65c0f7b2adab2de1cc"},
"credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
+5
View File
@@ -779,3 +779,8 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Disc %{no}" msgid "Disc %{no}"
msgstr "" msgstr ""
#: lib/music_library_web/controllers/last_fm_controller.ex
#, elixir-autogen, elixir-format
msgid "Successfully connected your Last.fm account"
msgstr ""
+5
View File
@@ -779,3 +779,8 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Disc %{no}" msgid "Disc %{no}"
msgstr "" msgstr ""
#: lib/music_library_web/controllers/last_fm_controller.ex
#, elixir-autogen, elixir-format
msgid "Successfully connected your Last.fm account"
msgstr ""
@@ -0,0 +1,12 @@
defmodule MusicLibrary.Repo.Migrations.CreateSecrets do
use Ecto.Migration
def change do
create table(:secrets, primary_key: false) do
add :name, :string, primary_key: true
add :value, :binary, null: false
timestamps()
end
end
end