# EctoTenancyEnforcer

EctoTenancyEnforcer provides a way to ensure that all queries made from your Elixir application, using Ecto, have tenancy set. If a query is made that
does not have tenancy set, you can handle that as an error or have it throw an exception.

This library does not try to set tenancy on your queries. There are a lot more edge cases in that, and the stakes are a lot higher. That makes
this library opinionated that it is a good idea for you to always set tenancy yourself.

Both `where` and `joins` are checked for tenancy, but more may be added over time if a valid use case arises.

## Configuration

The `EctoTenancyEnforcer.enforce/2` (and !) functions are to be used in the `Ecto.Repo.prepare_query/3` callback. This callback
provides the entire `Ecto.Query` struct that is then introspected.

If you want to handle the tenancy error yourself (or log it), then use the non-bang version. If you want it to error out and
not execute the query, then use the bang version.

The `enforced_schemas` key is where you configure your schemas that are tenant'd. The default column is assumed to be `tenant_id`,
but you can customize it. To customize it, use the format `{MySchema, :my_tenant_column}` like in the following example.

defmodule MyApp.Repo do
  use Ecto.Repo,
    otp_app: :my_app,
    adapter: Ecto.Adapters.Postgres

  @enforced_schemas [Company, {Person, :tenant_id}, {Alternate, :team_id}]

  def init(_type, config) do
    config = Keyword.merge(config, Application.get_env(:my_app, MyApp.Repo))
    {:ok, config}

  def prepare_query(_operation, query, opts) do
    unless Keyword.get(opts, :tenancy_unchecked) do
      EctoTenancyEnforcer.enforce!(query, enforced_schemas: @enforced_schemas)

    {query, opts}

## Query Examples

The following queries are all allowed (assuming `Person` and `Company` are enforced schemas):

The following queries would not be allowed:

## Preloading

Unfortunately, I can not currently find a way to do preloading in a functional manner. This is one situation where I think that the tenancy should
come from the source objects. Due to this, you must always use an `Ecto.Query` for preloading.

The following examples demonstrate allowed and not-allowed preloading:


## Contributing