<div align="center">
<img src="https://github.com/user-attachments/assets/f0352656-397d-4d90-999a-d3adbae1095f">
# Permit.Ecto
<p><strong>Ecto integration for Permit - Convert permissions to Ecto queries with automatic authorization scoping.
</strong></p>
[](https://curiosum.com/contact)
[](https://curiosum.com/services/elixir-software-development)
[]()
</div>
<br/>
## Purpose and usage
Permit.Ecto integrates [`Permit`](https://github.com/curiosum-dev/permit) with Ecto, providing means to convert permissions to Ecto queries and automatically constructing `Ecto.Query` scopes to preload records that meet authorization criteria.
Key features:
- **Automatic query generation** - Convert Permit permissions into Ecto queries
- **Authorization scoping** - Automatically scope database queries based on user permissions
- **Association support** - Handle has_one, has_many, and belongs_to relationships in permission rules
- **Seamless integration** - Works with Permit.Phoenix for automatic controller and LiveView authorization
[](https://hex.pm/packages/permit_ecto)
[](https://github.com/curiosum-dev/permit_ecto/actions)
[](https://codecov.io/gh/curiosum-dev/permit_ecto/branch/master)
[](https://github.com/curiosum-dev/permit_ecto/blob/master/LICENSE.md)
## Dependencies and related libraries
`Permit.Ecto` depends on `Permit`. It can be used to build custom integrations or in conjunction with `Permit.Phoenix`, which uses the generated `accessible_by/4` functions to automatically preload, authorize and inject records loaded via Ecto into controller assigns (see more in [`Permit.Phoenix documentation`](https://github.com/curiosum-dev/permit_phoenix)).
## Usage
```elixir
defmodule MyApp.Authorization do
use Permit.Ecto,
permissions_module: MyApp.Permissions,
repo: MyApp.Repo
end
defmodule MyApp.Permissions do
use Permit.Ecto.Permissions, actions_module: Permit.Actions.CrudActions
def can(%{role: :admin} = user) do
permit()
|> all(MyApp.Blog.Article)
end
def can(%{id: user_id} = user) do
# Checks can be performed directly for record columns as well as associated
# record values in has_one, has_many and belongs_to associations.
#
# For has_one and belongs_to relationships, given condition must be satisfied
# for the associated record. For has_many relationships, at least one associated
# record must satisfy the condition - as seen in example below.
permit()
|> all(MyApp.Blog.Article, author_id: user_id)
|> read(MyApp.Blog.Article, reviews: [approved: true])
end
def can(user), do: permit()
end
iex> MyApp.Repo.all(MyApp.Blog.Article)
[
%MyApp.Blog.Article{id: 1, author_id: 1},
%MyApp.Blog.Article{id: 2, author_id: 1},
%MyApp.Blog.Article{id: 3, author_id: 2}
]
# The `accessible_by!/3` function also has a `accessible_by/3` variant which returns `{:ok, ...}` tuples.
iex> MyApp.Permissions.accessible_by!(%MyApp.Users.User{id: 1}, :update, MyApp.Blog.Article) |> MyApp.Repo.all()
[%MyApp.Blog.Article{id: 1, ...}, %MyApp.Blog.Article{id: 2, ...}]
iex> MyApp.Permissions.accessible_by!(%MyApp.Users.User{id: 1}, :read, MyApp.Blog.Article) |> MyApp.Repo.all()
[%MyApp.Blog.Article{id: 1, ...}, %MyApp.Blog.Article{id: 2, ...}, %MyApp.Blog.Article{id: 3, ...}]
iex> MyApp.Permissions.accessible_by!(%MyApp.Users.User{id: 3, role: :admin}, :update, MyApp.Blog.Article) |> MyApp.Repo.all()
[%MyApp.Blog.Article{id: 1, ...}, %MyApp.Blog.Article{id: 2, ...}, %MyApp.Blog.Article{id: 3, ...}]
```
### Quick authorization checks
Generate Ecto queries based on permissions for direct database querying:
```elixir
# Generate query scoped to user permissions
query = MyApp.Authorization.accessible_by!(current_user, :read, Article)
articles = MyApp.Repo.all(query)
# Use with pagination and other Ecto features
query
|> where([a], a.published == true)
|> order_by([a], desc: a.inserted_at)
|> limit(10)
|> MyApp.Repo.all()
```
## Ecosystem
Permit.Ecto is part of the modular Permit ecosystem:
| Package | Version | Description |
|---------|---------|-------------|
| **[permit](https://hex.pm/packages/permit)** | [](https://hex.pm/packages/permit) | Core authorization library |
| **[permit_ecto](https://hex.pm/packages/permit_ecto)** | [](https://hex.pm/packages/permit_ecto) | Ecto integration for database queries |
| **[permit_phoenix](https://hex.pm/packages/permit_phoenix)** | [](https://hex.pm/packages/permit_phoenix) | Phoenix Controllers & LiveView integration |
| **[permit_absinthe](https://github.com/curiosum-dev/permit_absinthe)** | [](https://hex.pm/packages/permit_absinthe) | GraphQL API authorization via Absinthe |
## Installation
### Using Igniter (recommended)
If you use [Igniter](https://hex.pm/packages/igniter) for project setup, add `permit_ecto` and `igniter` to your deps and run:
```bash
mix permit_ecto.install
```
This creates your `Authorization` and `Authorization.Permissions` modules pre configured for Ecto.
**Options:**
| Option | Description | Default |
|---|---|---|
| `--authorization-module` | Authorization module name | `<MyApp>.Authorization` |
| `--permissions-module` | Permissions module name | `<MyApp>.Authorization.Permissions` |
| `--actions-module` | Actions module to use | `Permit.Actions.CrudActions` |
| `--repo` | Ecto repo module | auto-detected |
Example with options:
```bash
mix permit_ecto.install \
--authorization-module MyApp.Auth \
--permissions-module MyApp.Auth.Permissions \
--repo MyApp.Repo
```
### Manual installation
The package can be installed by adding `permit_ecto` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:permit, "~> 0.3.0"}, # Core authorization library
{:permit_ecto, "~> 0.2.4"} # Ecto integration
]
end
```
Then create your authorization module:
```elixir
defmodule MyApp.Authorization do
use Permit.Ecto,
permissions_module: MyApp.Authorization.Permissions,
repo: MyApp.Repo
end
defmodule MyApp.Authorization.Permissions do
use Permit.Ecto.Permissions, actions_module: Permit.Actions.CrudActions
def can(_user), do: permit()
end
```
For a complete setup with Phoenix or Absinthe integration, add `:permit_phoenix` or `:permit_absinthe`.
## Documentation
- **Permit.Ecto docs**: [hexdocs.pm/permit_ecto](https://hexdocs.pm/permit_ecto)
- **Core library**: [hexdocs.pm/permit](https://hexdocs.pm/permit)
## Contributing
We welcome contributions! Please see our [Contributing Guide](https://github.com/curiosum-dev/permit_ecto/blob/master/CONTRIBUTING.md) for details.
### Development setup
Just clone the repository, install dependencies normally, develop and run tests. When running Credo and Dialyzer, please use `MIX_ENV=test` to ensure tests and support files are validated, too.
### Community
- **Slack channel**: [Elixir Slack / #permit](https://elixir-lang.slack.com/archives/C091Q5S0GDU)
- **Issues**: [GitHub Issues](https://github.com/curiosum-dev/permit_ecto/issues)
- **Discussions**: [GitHub Discussions](https://github.com/curiosum-dev/permit/discussions)
- **Blog**: [Curiosum Blog](https://curiosum.com/blog?search=permit)
## Contact
* Library maintainer: [Michał Buszkiewicz](https://github.com/vincentvanbush)
* [**Curiosum**](https://curiosum.com) - Elixir development team behind Permit
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.