I didn’t know how to store a list (in CI variable maybe) that contains a regex that I will need for the production env (maybe store it as string and then covert it to regex in config file). So to conclude I need this :
For development => origin: [“*”]
For production => origin: [~r/^https?://(?:[a-z0-9_-]+[.])*test.com$/, “https://otherorigin.com”]
Then the function could be different in different environments:
defmodule MyCorsHelper do
if Mix.env() == :prod do
@origin_regex ~r/^https?://(?:[a-z0-9_-]+[.])*test.com$/
def origin(conn) do
# get config from conn or environment etc
[@origin_regex, "https://otherorigin.com"]
end
else
def origin(conn) do
# use conn if needed
["*"]
end
end
end
Some notes:
moving the Mix.env check inside of the origin function may give you a nasty surprise in production; depending on how you’re deploying the whole Mix namespace may not be available at runtime
calling System.get_env in a plug’s config is tricky, since the init function that uses that data runs at compile time in production builds
You can use that dodgy method of setting config, :myapp, :env, :prod/:dev in each config file and using Application.compile_env(:myapp, :env) to mitigate concerns of the Mix.env method.
If this is part of your CI you can write file as part of the pipeline with correct value based on Mix.env when doing build. you can create a cors.<env>.exs as sample files and then copy them to your project to cors.exs and have cors.exs imported in your main config. This should be doable in your pipeline keeping configs clean without logic complications
defmodule CorsHelper do
if Application.compile_env!(:appname, :environment) == "production" do
@origin_regex ~r/^https?://(?:[a-z0-9_-]+[.])*example.com$/
def origin(conn) do
[@origin_regex, "https://otherorigin.com"]
end
else
def origin(conn) do
["*"]
end
end
end
The compiler will find files most anywhere in the project, but config is a somewhat odd place to put this. Maybe in the yourappname_web directory in lib, next to router.ex etc?
There’s already MIX_ENV which is used by Phoenix; why add another one? As a general practice, prefer specific environment variables / configs over a broad “env” concept.
Also note that putting that in runtime.exs will not let you configure the value at runtime, it will only offer the choice of:
set ENV to the same value at runtime as it was for compilation and work
set ENV to something different at runtime and crash
def cors_origins() do
if System.get_env("ENV") == "production" do
[@origin_regex, "https://other.com"]
else
["*"]
end
end
def cors_headers() do
["test_wrong_header"]
end
The origins are good, but I have an impression that all headers are allowed, If I deploy with that list [“test_wrong_header”], the request is accepted.