FunWithFlags.UI

A Web dashboard for the FunWithFlags Elixir package.

This package is still a work in progress.

How to run

FunWithFlags.UI is just a plug and it can be run in a number of ways.

Standalone

wip

Mounted in Phoenix

The main plug can be mounted inside the Phoenix router with Phoenix.Router.forward/4.

defmodule MyPhoenixApp.Web.Router do
  use MyPhoenixApp.Web, :router

  pipeline :mounted_apps do
    plug :accepts, ["html"]
    plug :put_secure_browser_headers
  end

  scope path: "/feature-flags" do
    pipe_through :mounted_apps
    forward "/", FunWithFlags.UI.Router, namespace: "feature-flags"
  end
end

Mounted in another Plug application

Since it’s just a plug, it can also be mounted into any other Plug application using Plug.Router.forward/2.

defmodule Another.App do
  use Plug.Router
  forward "/feature-flags", to: FunWithFlags.UI.Router, init_opts: [namespace: "feature-flags"]
end

Security

For obvious reasons, you don’t want to make this web control panel publicly accessible.

The library itself doesn’t provide any auth functionality because, as a plug, it is easier to wrap it into the specific authentication of the host application.

The easiest thing to do is to protect it with HTTP Basic Auth, provided by the basic_auth plug.

For example, in Phoenix:

defmodule MyPhoenixApp.Web.Router do
  use MyPhoenixApp.Web, :router

  def my_basic_auth(conn, username, password) do
    if username == "foo" && password == "bar" do
      conn
    else
      Plug.Conn.halt(conn)
    end
  end

  pipeline :mounted_and_protected_apps do
    plug :accepts, ["html"]
    plug :put_secure_browser_headers
    plug BasicAuth, callback: &__MODULE__.my_basic_auth/3
  end

  scope path: "/feature-flags" do
    pipe_through :mounted_and_protected_apps
    forward "/", FunWithFlags.UI.Router, namespace: "feature-flags"
  end
end

Caveats

While the base fun_with_flags library is quite relaxed in terms of valid flag names, group names and actor identifers, this web dashboard extension applies some more restrictive rules. The reason is that all fun_with_flags cares about is that some flag and group names can be represented as an Elixir Atom, while actor IDs are just strings. Since you can use that API in your code, the library will only check that the parameters have the right type.

Things change on the web, however. Think about the binary "Ook? Ook!". In code, it can be accepted as a valid flag name:

{:ok, true} = FunWithFlags.enable(:"Ook? Ook!", for_group: :"weird, huh?")

On the web, however, the question mark makes working with URLs a bit tricky: in http://localhost:8080/flags/Ook?%20Ook!, the flag name will be Ook and the rest will be a query string.

For this reason this library enforces some stricter rules when creating flags and groups. Blank values are not allowed, ? neither, and flag names must match /^w+$/.

Installation

The package can be installed by adding fun_with_flags_ui to your list of dependencies in mix.exs:

def deps do
  [{:fun_with_flags_ui, "~> 0.0.2"}]
end