### Adding Bodyguard Dependency Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Shows how to add Bodyguard as a dependency in the `mix.exs` file of an Elixir project. This is the first step in installing the library. ```Elixir # mix.exs def deps do [ {:bodyguard, "~> 2.4"} ] end ``` -------------------------------- ### Testing Bodyguard Policies Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Provides examples of testing Bodyguard policies using the top-level API functions `Bodyguard.permit/3` and `Bodyguard.permit?/3`. It shows how to assert successful and failing authorization checks and how to catch `Bodyguard.NotAuthorizedError`. ```Elixir assert :ok == Bodyguard.permit(MyApp.Blog, :successful_action, user) assert {:error, :unauthorized} == Bodyguard.permit(MyApp.Blog, :failing_action, user) assert Bodyguard.permit?(MyApp.Blog, :successful_action, user) refute Bodyguard.permit?(MyApp.Blog, :failing_action, user) error = assert_raise Bodyguard.NotAuthorizedError, fun -> Bodyguard.permit(MyApp.Blog, :failing_action, user) end assert %{status: 403, message: "not authorized"} = error ``` -------------------------------- ### Configuring Bodyguard.Plug.Authorize in Phoenix Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Demonstrates how to use the Bodyguard.Plug.Authorize plug in a Phoenix controller pipeline. It shows how to configure the policy, action, user, and parameters using getter functions and how to handle authorization failures with a fallback controller. Includes helper functions for fetching data and extracting parameters. ```Elixir # lib/my_app_web/controllers/post_controller.ex defmodule MyAppWeb.PostController do use MyAppWeb, :controller # Fetch the post and put into conn assigns plug :get_post when action in [:show] # Do the check plug Bodyguard.Plug.Authorize, policy: MyApp.Blog.Policy, action: {Phoenix.Controller, :action_name}, user: {MyApp.Authentication, :current_user}, params: {__MODULE__, :extract_post}, fallback: MyAppWeb.FallbackController def show(conn, _) do # Already assigned and authorized render(conn, "show.html") end defp get_post(conn, _) do assign(conn, :post, MyApp.Posts.get_post!(conn.params["id"])) end # Helper for the Authorize plug def extract_post(conn), do: conn.assigns.posts end ``` -------------------------------- ### Implementing Bodyguard Policy with Multiple Rules in Elixir Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Demonstrates a more comprehensive authorize/3 callback implementation within a context module (MyApp.Blog). It includes rules for admin users, creating posts, and modifying/deleting owned posts, using pattern matching and guards. ```Elixir # lib/my_app/blog/blog.ex defmodule MyApp.Blog do @behaviour Bodyguard.Policy alias __MODULE__ # Admin users can do anything def authorize(_, %Blog.User{role: :admin}, _), do: true # Regular users can create posts def authorize(:create_post, _, _), do: true # Regular users can modify their own posts def authorize(action, %Blog.User{id: user_id}, %Blog.Post{user_id: user_id}) when action in [:update_post, :delete_post], do: true # Catch-all: deny everything else def authorize(_, _, _), do: false end ``` -------------------------------- ### Defining and Using Bodyguard Policy in Elixir Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Shows how to implement the Bodyguard.Policy behaviour in a context module (MyApp.Blog) with an authorize/3 callback for the :update_post action. It also demonstrates how to check permissions using Bodyguard.permit/4 within a Phoenix controller action (MyAppWeb.PostController.update/2). ```Elixir # lib/my_app/blog/blog.ex defmodule MyApp.Blog do @behaviour Bodyguard.Policy # Admins can update anything def authorize(:update_post, %{role: :admin} = _user, _post), do: :ok # Users can update their owned posts def authorize(:update_post, %{id: user_id} = _user, %{user_id: user_id} = _post), do: :ok # Otherwise, denied def authorize(:update_post, _user, _post), do: :error end # lib/my_app_web/controllers/post_controller.ex defmodule MyAppWeb.PostController do use MyAppWeb, :controller def update(conn, %{"id" => id, "post" => post_params}) do user = conn.assigns.current_user post = MyApp.Blog.get_post!(id) with :ok <- Bodyguard.permit(MyApp.Blog, :update_post, user, post), {:ok, post} <- MyApp.Blog.update_post(post, post_params) do redirect(conn, to: post_path(conn, :show, post)) end end end ``` -------------------------------- ### Delegating Bodyguard Policy to a Separate Module in Elixir Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Illustrates how to keep authorization logic separate from the main context module by defining a dedicated policy module (MyApp.Blog.Policy) and using defdelegate in the context module (MyApp.Blog) to forward the authorize/3 calls. ```Elixir # lib/my_app/blog/blog.ex defmodule MyApp.Blog do defdelegate authorize(action, user, params), to: MyApp.Blog.Policy end # lib/my_app/blog/policy.ex defmodule MyApp.Blog.Policy do @behaviour Bodyguard.Policy def authorize(action, user, params), do: # ... end ``` -------------------------------- ### Implementing Bodyguard.Schema Behaviour Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Shows how to implement the Bodyguard.Schema behaviour on an Ecto schema module. The `scope/3` callback defines how to filter a query based on the user and action, typically restricting records to those owned by the user. ```Elixir # lib/my_app/blog/post.ex defmodule MyApp.Blog.Post do import Ecto.Query, only: [from: 2] @behaviour Bodyguard.Schema def scope(query, %MyApp.Blog.User{id: user_id}, _) do from ms in query, where: ms.user_id == ^user_id end end ``` -------------------------------- ### Using Bodyguard.scope/4 Helper Function Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Illustrates how to use the Bodyguard.scope/4 helper function in a context module. This function automatically calls the appropriate `scope/3` callback implemented on the schema module (e.g., MyApp.Blog.Post) to filter queries based on the provided user. ```Elixir # lib/my_app/blog/blog.ex defmodule MyApp.Blog do def list_user_posts(user) do MyApp.Blog.Post |> Bodyguard.scope(user) # <-- defers to MyApp.Blog.Post.scope/3 |> where(draft: false) |> Repo.all end end ``` -------------------------------- ### Handling Bodyguard Authorization Failures with action_fallback in Elixir Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Explains how to define a fallback controller (MyAppWeb.FallbackController) to handle {:error, :unauthorized} results returned by Bodyguard.permit. It shows how to configure a controller (MyAppWeb.PageController) to use this fallback controller via action_fallback. ```Elixir # lib/my_app_web/controllers/fallback_controller.ex defmodule MyAppWeb.FallbackController do use MyAppWeb, :controller def call(conn, {:error, :unauthorized}) do conn |> put_status(:forbidden) |> put_view(html: MyAppWeb.ErrorHTML) |> render(:"403") end end # lib/my_app_controllers/page_controller.ex defmodule MyAppWeb.PageController do use MyAppWeb, :controller # This can be defined here, or in the MyAppWeb.controller/0 macro action_fallback MyAppWeb.FallbackController # ...actions here... end ``` -------------------------------- ### Default Bodyguard Configuration Source: https://github.com/schrockwell/bodyguard/blob/main/README.md Displays the default configuration for the Bodyguard library. The `default_error` option specifies the reason returned in the `{:error, reason}` tuple when authorization fails. ```Elixir config :bodyguard, # The second element of the {:error, reason} tuple returned on auth failure default_error: :unauthorized ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.