towerops/lib/towerops/api_tokens/api_token.ex
Graham McIntire 56093bb493
refactor: use API token auth for profile imports instead of session cookies
Changes profile import endpoint to use standard API token authentication:

API Token Changes:
- Add user_id to api_tokens table (tracks who created the token)
- Update ApiTokens.verify_token/1 to return user along with org_id
- Update ApiAuth plug to assign current_user from token

Profile Import Changes:
- Move endpoint from /api/v1/admin/profiles/import to /api/v1/profiles/import
- Check user.is_superuser in controller instead of using RequireSuperuser plug
- Use api_v1 pipeline (Bearer token auth) instead of browser session
- Update documentation to show API token usage

Security:
- Only API tokens created by superusers can import profiles
- Returns 403 Forbidden if token user is not a superuser
- Logs import attempts with user email for audit trail

This provides a consistent API experience using Bearer tokens
instead of requiring browser session cookies.
2026-01-18 09:30:21 -06:00

40 lines
1.2 KiB
Elixir

defmodule Towerops.ApiTokens.ApiToken do
@moduledoc """
Schema for API tokens scoped to organizations.
API tokens allow programmatic access to the Towerops API for managing
sites, devices, and other resources within an organization.
The actual token value is only shown once when created and is then
hashed for secure storage.
"""
use Ecto.Schema
import Ecto.Changeset
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "api_tokens" do
field :name, :string
field :token_hash, :string
field :last_used_at, :utc_datetime
field :expires_at, :utc_datetime
belongs_to :organization, Towerops.Organizations.Organization
belongs_to :user, Towerops.Accounts.User
timestamps(type: :utc_datetime)
end
@doc false
def changeset(api_token, attrs) do
api_token
|> cast(attrs, [:organization_id, :user_id, :name, :token_hash, :expires_at, :last_used_at])
|> validate_required([:organization_id, :name, :token_hash])
|> validate_length(:name, min: 1, max: 255)
|> unique_constraint(:token_hash)
|> foreign_key_constraint(:organization_id)
|> foreign_key_constraint(:user_id)
end
end