I am fairly new to Elixir/Phoenix and I would like someone to help sense check/review a high level sketch for this scenario:
Suppose that in my application, a Document belongs to a Folder (both of them are Ecto schemas). In addition, we require that during the creation of a Document, the chosen Folder should meet some business requirements (e.g., the Folder must be owned by the current user, or the Folder is not older than X, etc). To create a Document, let’s define this function:
def create_document(user, attrs) do
%Document{}
|> Document.changeset(user, attrs)
|> Repo.insert()
end
In this case, attrs
is provided by the user (e.g., coming from a controller). Thus, a malicious user can specify an invalid folder_id
in it and we should guard against this in Document.changeset/3
. Here is my implementation
defmodule MyApp.Document do
import Ecto.Changeset
def changeset(document, user, attrs) do
document
|> cast(attrs, [:attr1, :attr2]) # Omit folder_id from the list
|> put_validated_folder(user, attrs["folder_id"])
|> validate_required([:attr1, :attr2, :folder_id])
end
defp put_validated_folder(changeset, user, folder_id) do
# Let's pretend we have get_folder which returns
# - {:ok, folder} if the specified folder_id is "valid" based on some business logic
# - {:error, reason} otherwise
case get_folder(user, folder_id) do
{:ok, folder} ->
put_assoc(changeset, :folder, folder)
_ ->
changeset
end
end
end
Is this a good approach to this problem? Are there other better/more idiomatic ways?