Current section

Files

Jump to
pike lib pike.ex
Raw

lib/pike.ex

defmodule Pike do
@moduledoc """
A lightweight, embeddable Elixir library for API key authentication and fine-grained authorization. Pike provides a plug-compatible middleware system and a resource-based DSL for defining authorization rules at the controller and action level.
## Authorization Flow
1. The `Pike.AuthorizationPlug` extracts the Bearer token from the request header
2. The configured store validates the key and checks permissions
3. Valid keys are assigned to `conn.assigns[:pike_api_key]` (or custom assign)
4. The controller's action permission requirements are checked via DSL
5. Unauthorized requests are handled by the configured responder
## Configuration
Pike can be customized at both the global and per-pipeline level:
* `store`: The storage backend (default: `Pike.Store.ETS`)
* `assign_to`: Where to assign the key (default: `:pike_api_key`)
* `on_auth_failure`: Failure handler (default: `Pike.Responder.Default`)
"""
@store Application.compile_env(:pike, :store, nil)
defp store(store) do
if store do
store
else
raise "Pike store not configured! Please set `:store` in your application config."
end
end
defp store_implements?(function, arity, store) do
function_exported?(store(store), function, arity)
end
@doc """
Fetches an API key struct from the configured store.
"""
def get_key(key, store \\ @store), do: store(store).get_key(key)
@doc """
Checks if the key is allowed to perform an action on a resource.
"""
def action?(key_struct, opts, store \\ @store) do
resource = Keyword.fetch!(opts, :resource)
action = Keyword.fetch!(opts, :action)
store(store).action?(key_struct, resource, action)
end
@doc """
Insert a key into the store (if supported).
Key struct example:
```
%{
key: "abc123",
enabled: true, # Optional, defaults to true
permissions: [
%{resource: "Products", scopes: [:read, :write]}
]
}
```
"""
def insert(key_struct, store \\ @store) do
if store_implements?(:insert, 1, store) do
store(store).insert(key_struct)
else
{:error, :insert_not_supported}
end
end
@doc """
Delete a key from the store (if supported).
"""
def delete_key(key, store \\ @store) do
if store_implements?(:delete_key, 1, store) do
store(store).delete_key(key)
else
{:error, :delete_not_supported}
end
end
@doc """
Update an existing key in the store (if supported).
"""
def update_key(key, updates, store \\ @store) do
if store_implements?(:update_key, 2, store) do
store(store).update_key(key, updates)
else
{:error, :update_not_supported}
end
end
@doc """
Enable a key that has been disabled.
"""
def enable_key(key, store \\ @store) do
update_key(key, %{enabled: true}, store)
end
@doc """
Disable a key temporarily without deleting it.
"""
def disable_key(key, store \\ @store) do
update_key(key, %{enabled: false}, store)
end
@doc """
Check if a key is enabled.
"""
def key_enabled?(key, store \\ @store) do
case get_key(key, store) do
{:ok, %{enabled: false}} -> false
{:ok, _} -> true
_ -> false
end
end
end