ExEssentials.Web.Plugs.DisableServices behaviour (ExEssentials v0.4.2)

View Source

A Plug that conditionally disables specific controller actions based on a feature flag, with optional support for group-based evaluation using FunWithFlags.

Overview

This plug allows developers to temporarily disable specific Phoenix actions (e.g., :create, :delete) by checking if a feature flag is enabled for the current context (typically, the current user or realm).

It works by intercepting the connection and returning a 503 Service Unavailable response if the feature flag is enabled and the requested action is among the disabled ones.

Group-based targeting is supported through FunWithFlags, using a struct that includes the current user as current_user.

Configuration

You must provide the following options when using the plug:

  • :disabled_actions – a list of controller actions (atoms) that should be disabled if the flag is active.
  • :flag_name – the name of the feature flag to check (defaults to :disable_services_enabled).

This plug uses FunWithFlags.enabled?/2 with a custom context struct that supports group checks via FunWithFlags.Group.

Dependencies

To use this plug, your application must be configured with:

{:fun_with_flags, "~> 1.13"},
{:ecto_sql, "~> 3.4"},
{:postgrex, ">= 0.0.0"},

And you must configure the FunWithFlags adapter to use Ecto with a repo.

Usage

Simple usage (default behavior)

By default, the plug uses conn.assigns.user_name or conn.params["user_name"] to resolve the group.

plug ExEssentials.Web.Plugs.DisableServices,
  disabled_actions: [:create, :delete],
  flag_name: :services_disabled

Customized usage with use and @impl

To change how the group name is extracted from the connection (e.g., use realm instead of user_name), you can define your own module and override the get_current_user/1 callback:

defmodule MyApp.Web.Plugs.DisableServices do
  use ExEssentials.Web.Plugs.DisableServices

  @impl ExEssentials.Web.Plugs.DisableServices
  def get_current_user(%Plug.Conn{assigns: %{realm: realm}}), do: realm
  def get_current_user(%Plug.Conn{params: params}), do: Map.get(params, "realm")
  def get_current_user(_), do: nil
end

And in your router or endpoint:

plug MyApp.Web.Plugs.DisableServices,
  disabled_actions: [:create, :delete],
  flag_name: :services_disabled

Example with FunWithFlags

To enable or disable the flag at runtime:

FunWithFlags.enable(:services_disabled)
FunWithFlags.disable(:services_disabled, for_group: "admin")

This will enable the flag globally, but disable it for users in the "admin" group.

Notes

Summary

Functions

Callback implementation for Plug.call/2.

Extracts the current user identifier from the connection for use in group-based flag evaluation.

Callback implementation for Plug.init/1.

Callbacks

get_current_user(t)

@callback get_current_user(Plug.Conn.t()) :: String.t() | nil

Functions

call(conn, opts)

Callback implementation for Plug.call/2.

get_current_user(arg1)

Extracts the current user identifier from the connection for use in group-based flag evaluation.

This function looks first in conn.assigns[:user_name], and if not found, in conn.params["user_name"].

Examples

iex> conn = %Plug.Conn{assigns: %{user_name: "admin"}}
iex> ExEssentials.Web.Plugs.DisableServices.get_current_user(conn)
"admin"

iex> conn = %Plug.Conn{params: %{"user_name" => "admin"}}
iex> ExEssentials.Web.Plugs.DisableServices.get_current_user(conn)
"admin"

iex> ExEssentials.Web.Plugs.DisableServices.get_current_user(%Plug.Conn{})
nil

init(opts)

Callback implementation for Plug.init/1.