mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
feat(portal): Add REST API (#5579)
Why: * In order to manage a large number of Firezone Sites, Resources, Policies, etc... a REST API is needed as clicking through the UI is too time consuming, as well as prone to error. By providing a REST API Firezone customers will be able to manage things within their Firezone accounts with code.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
[
|
||||
import_deps: [:phoenix],
|
||||
import_deps: [:phoenix, :open_api_spex],
|
||||
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
|
||||
27
elixir/apps/api/lib/api/api_spec.ex
Normal file
27
elixir/apps/api/lib/api/api_spec.ex
Normal file
@@ -0,0 +1,27 @@
|
||||
defmodule API.ApiSpec do
|
||||
alias OpenApiSpex.{Components, Info, OpenApi, Paths, SecurityScheme, Server}
|
||||
alias API.{Endpoint, Router}
|
||||
@behaviour OpenApi
|
||||
|
||||
@impl OpenApi
|
||||
def spec do
|
||||
%OpenApi{
|
||||
servers: [
|
||||
# Populate the Server info from a phoenix endpoint
|
||||
Server.from_endpoint(Endpoint)
|
||||
],
|
||||
info: %Info{
|
||||
title: "Firezone API",
|
||||
version: "1.0"
|
||||
},
|
||||
# Populate the paths from a phoenix router
|
||||
paths: Paths.from_router(Router),
|
||||
components: %Components{
|
||||
securitySchemes: %{"authorization" => %SecurityScheme{type: "http", scheme: "bearer"}}
|
||||
},
|
||||
security: [%{"authorization" => []}]
|
||||
}
|
||||
# Discover request/response schemas from path specs
|
||||
|> OpenApiSpex.resolve_schema_modules()
|
||||
end
|
||||
end
|
||||
128
elixir/apps/api/lib/api/controllers/actor_controller.ex
Normal file
128
elixir/apps/api/lib/api/controllers/actor_controller.ex
Normal file
@@ -0,0 +1,128 @@
|
||||
defmodule API.ActorController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.Actors
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Actors"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Actors",
|
||||
parameters: [
|
||||
limit: [in: :query, description: "Limit Users returned", type: :integer, example: 10],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok: {"ActorsResponse", "application/json", API.Schemas.Actor.ListResponse}
|
||||
]
|
||||
|
||||
# List Actors
|
||||
def index(conn, params) do
|
||||
list_opts = Pagination.params_to_list_opts(params)
|
||||
|
||||
with {:ok, actors, metadata} <- Actors.list_actors(conn.assigns.subject, list_opts) do
|
||||
render(conn, :index, actors: actors, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :show,
|
||||
summary: "Show Actor",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Actor ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"ActorResponse", "application/json", API.Schemas.Actor.Response}
|
||||
]
|
||||
|
||||
# Show a specific Actor
|
||||
def show(conn, %{"id" => id}) do
|
||||
with {:ok, actor} <- Actors.fetch_actor_by_id(id, conn.assigns.subject) do
|
||||
render(conn, :show, actor: actor)
|
||||
end
|
||||
end
|
||||
|
||||
operation :create,
|
||||
summary: "Create an Actor",
|
||||
request_body:
|
||||
{"Actor attributes", "application/json", API.Schemas.Actor.Request, required: true},
|
||||
responses: [
|
||||
ok: {"ActorResponse", "application/json", API.Schemas.Actor.Response}
|
||||
]
|
||||
|
||||
# Create a new Actor
|
||||
def create(conn, %{"actor" => params}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, actor} <- Actors.create_actor(subject.account, params, subject) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", ~p"/actors/#{actor}")
|
||||
|> render(:show, actor: actor)
|
||||
end
|
||||
end
|
||||
|
||||
def create(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :update,
|
||||
summary: "Update an Actor",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Actor ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
request_body:
|
||||
{"Actor attributes", "application/json", API.Schemas.Actor.Request, required: true},
|
||||
responses: [
|
||||
ok: {"ActorResponse", "application/json", API.Schemas.Actor.Response}
|
||||
]
|
||||
|
||||
# Update an Actor
|
||||
def update(conn, %{"id" => id, "actor" => params}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, actor} <- Actors.fetch_actor_by_id(id, subject),
|
||||
{:ok, actor} <- Actors.update_actor(actor, params, subject) do
|
||||
render(conn, :show, actor: actor)
|
||||
end
|
||||
end
|
||||
|
||||
def update(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :delete,
|
||||
summary: "Delete an Actor",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Actor ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"ActorResponse", "application/json", API.Schemas.Actor.Response}
|
||||
]
|
||||
|
||||
# Delete an Actor
|
||||
def delete(conn, %{"id" => id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, actor} <- Actors.fetch_actor_by_id(id, subject),
|
||||
{:ok, actor} <- Actors.delete_actor(actor, subject) do
|
||||
render(conn, :show, actor: actor)
|
||||
end
|
||||
end
|
||||
end
|
||||
131
elixir/apps/api/lib/api/controllers/actor_group_controller.ex
Normal file
131
elixir/apps/api/lib/api/controllers/actor_group_controller.ex
Normal file
@@ -0,0 +1,131 @@
|
||||
defmodule API.ActorGroupController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.Actors
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Actor Groups"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Actor Groups",
|
||||
parameters: [
|
||||
limit: [in: :query, description: "Limit Actor Groups returned", type: :integer, example: 10],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Actor Group Response", "application/json", API.Schemas.ActorGroup.ListResponse}
|
||||
]
|
||||
|
||||
# List Actor Groups
|
||||
def index(conn, params) do
|
||||
list_opts = Pagination.params_to_list_opts(params)
|
||||
|
||||
with {:ok, actor_groups, metadata} <- Actors.list_groups(conn.assigns.subject, list_opts) do
|
||||
render(conn, :index, actor_groups: actor_groups, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :show,
|
||||
summary: "Show Actor Group",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Actor Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Actor Group Response", "application/json", API.Schemas.ActorGroup.Response}
|
||||
]
|
||||
|
||||
# Show a specific Actor Group
|
||||
def show(conn, %{"id" => id}) do
|
||||
with {:ok, actor_group} <- Actors.fetch_group_by_id(id, conn.assigns.subject) do
|
||||
render(conn, :show, actor_group: actor_group)
|
||||
end
|
||||
end
|
||||
|
||||
operation :create,
|
||||
summary: "Create Actor Group",
|
||||
parameters: [],
|
||||
request_body:
|
||||
{"Actor Group Attributes", "application/json", API.Schemas.ActorGroup.Request,
|
||||
required: true},
|
||||
responses: [
|
||||
ok: {"Actor Group Response", "application/json", API.Schemas.ActorGroup.Response}
|
||||
]
|
||||
|
||||
# Create a new Actor Group
|
||||
def create(conn, %{"actor_group" => params}) do
|
||||
params = Map.put(params, "type", "static")
|
||||
|
||||
with {:ok, actor_group} <- Actors.create_group(params, conn.assigns.subject) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", ~p"/actor_groups/#{actor_group}")
|
||||
|> render(:show, actor_group: actor_group)
|
||||
end
|
||||
end
|
||||
|
||||
def create(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :update,
|
||||
summary: "Update a Actor Group",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Actor Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
request_body:
|
||||
{"Actor Group Attributes", "application/json", API.Schemas.ActorGroup.Request,
|
||||
required: true},
|
||||
responses: [
|
||||
ok: {"Actor Group Response", "application/json", API.Schemas.ActorGroup.Response}
|
||||
]
|
||||
|
||||
# Update an Actor Group
|
||||
def update(conn, %{"id" => id, "actor_group" => params}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, actor_group} <- Actors.fetch_group_by_id(id, subject),
|
||||
{:ok, actor_group} <- Actors.update_group(actor_group, params, subject) do
|
||||
render(conn, :show, actor_group: actor_group)
|
||||
end
|
||||
end
|
||||
|
||||
def update(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :delete,
|
||||
summary: "Delete a Actor Group",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Actor Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Actor Group Response", "application/json", API.Schemas.ActorGroup.Response}
|
||||
]
|
||||
|
||||
# Delete an Actor Group
|
||||
def delete(conn, %{"id" => id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, actor_group} <- Actors.fetch_group_by_id(id, subject),
|
||||
{:ok, actor_group} <- Actors.delete_group(actor_group, subject) do
|
||||
render(conn, :show, actor_group: actor_group)
|
||||
end
|
||||
end
|
||||
end
|
||||
28
elixir/apps/api/lib/api/controllers/actor_group_json.ex
Normal file
28
elixir/apps/api/lib/api/controllers/actor_group_json.ex
Normal file
@@ -0,0 +1,28 @@
|
||||
defmodule API.ActorGroupJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Actors
|
||||
|
||||
@doc """
|
||||
Renders a list of Actor Groups.
|
||||
"""
|
||||
def index(%{actor_groups: actor_groups, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(actor_groups, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a single Actor Group
|
||||
"""
|
||||
def show(%{actor_group: actor_group}) do
|
||||
%{data: data(actor_group)}
|
||||
end
|
||||
|
||||
defp data(%Actors.Group{} = actor_group) do
|
||||
%{
|
||||
id: actor_group.id,
|
||||
name: actor_group.name
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,135 @@
|
||||
defmodule API.ActorGroupMembershipController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.Actors
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Actor Group Memberships"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Actor Group Memberships",
|
||||
parameters: [
|
||||
actor_group_id: [
|
||||
in: :path,
|
||||
description: "Actor Group ID",
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
],
|
||||
limit: [
|
||||
in: :query,
|
||||
description: "Limit Actor Group Memberships returned",
|
||||
type: :integer,
|
||||
example: 10
|
||||
],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok:
|
||||
{"Actor Group Membership Response", "application/json",
|
||||
API.Schemas.ActorGroupMembership.ListResponse}
|
||||
]
|
||||
|
||||
# List members for a given Actor Group
|
||||
def index(conn, %{"actor_group_id" => actor_group_id} = params) do
|
||||
list_opts =
|
||||
Pagination.params_to_list_opts(params)
|
||||
|> Keyword.put(:filter, group_id: actor_group_id)
|
||||
|
||||
with {:ok, actors, metadata} <- Actors.list_actors(conn.assigns.subject, list_opts) do
|
||||
render(conn, :index, actors: actors, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :update_put,
|
||||
summary: "Update Actor Group Memberships",
|
||||
parameters: [
|
||||
actor_group_id: [
|
||||
in: :path,
|
||||
description: "Actor Group ID",
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
request_body:
|
||||
{"Actor Group Membership Attributes", "application/json",
|
||||
API.Schemas.ActorGroupMembership.PutRequest, required: true},
|
||||
responses: [
|
||||
ok:
|
||||
{"Actor Group Membership Response", "application/json",
|
||||
API.Schemas.ActorGroupMembership.MembershipResponse}
|
||||
]
|
||||
|
||||
def update_put(
|
||||
conn,
|
||||
%{"actor_group_id" => actor_group_id, "memberships" => attrs}
|
||||
) do
|
||||
subject = conn.assigns.subject
|
||||
preload = [:memberships]
|
||||
filter = [deleted?: false, editable?: true]
|
||||
|
||||
with {:ok, group} <-
|
||||
Actors.fetch_group_by_id(actor_group_id, subject, preload: preload, filter: filter),
|
||||
{:ok, group} <- Actors.update_group(group, %{memberships: attrs}, subject) do
|
||||
render(conn, :memberships, memberships: group.memberships)
|
||||
end
|
||||
end
|
||||
|
||||
def update_put(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :update_patch,
|
||||
summary: "Update an Actor Group Membership",
|
||||
parameters: [
|
||||
actor_group_id: [
|
||||
in: :path,
|
||||
description: "Actor Group ID",
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
request_body:
|
||||
{"Actor Group Membership Attributes", "application/json",
|
||||
API.Schemas.ActorGroupMembership.PatchRequest, required: true},
|
||||
responses: [
|
||||
ok:
|
||||
{"Actor Group Membership Response", "application/json",
|
||||
API.Schemas.ActorGroupMembership.MembershipResponse}
|
||||
]
|
||||
|
||||
# Update Actor Group Memberships
|
||||
def update_patch(
|
||||
conn,
|
||||
%{"actor_group_id" => actor_group_id, "memberships" => params}
|
||||
) do
|
||||
add = Map.get(params, "add", [])
|
||||
remove = Map.get(params, "remove", [])
|
||||
subject = conn.assigns.subject
|
||||
preload = [:memberships]
|
||||
filter = [deleted?: false, editable?: true]
|
||||
|
||||
with {:ok, group} <-
|
||||
Actors.fetch_group_by_id(actor_group_id, subject, preload: preload, filter: filter),
|
||||
membership_attrs <- prepare_membership_attrs(group, add, remove),
|
||||
{:ok, group} <- Actors.update_group(group, %{memberships: membership_attrs}, subject) do
|
||||
render(conn, :memberships, memberships: group.memberships)
|
||||
end
|
||||
end
|
||||
|
||||
def update_patch(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
defp prepare_membership_attrs(group, add, remove) do
|
||||
to_add = MapSet.new(add) |> MapSet.reject(&(String.trim(&1) == ""))
|
||||
to_remove = MapSet.new(remove) |> MapSet.reject(&(String.trim(&1) == ""))
|
||||
member_ids = Enum.map(group.memberships, & &1.actor_id) |> MapSet.new()
|
||||
|
||||
membership_ids =
|
||||
MapSet.difference(member_ids, to_remove)
|
||||
|> MapSet.union(to_add)
|
||||
|
||||
if MapSet.size(membership_ids) == 0,
|
||||
do: [],
|
||||
else: Enum.map(membership_ids, &%{actor_id: &1})
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
defmodule API.ActorGroupMembershipJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Actors
|
||||
|
||||
@doc """
|
||||
Renders a list of Actors.
|
||||
"""
|
||||
def index(%{actors: actors, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(actors, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a list of Actor IDs for an Actor Group
|
||||
"""
|
||||
def memberships(%{memberships: memberships}) do
|
||||
actor_ids = for(membership <- memberships, do: membership.actor_id)
|
||||
%{data: %{actor_ids: actor_ids}}
|
||||
end
|
||||
|
||||
defp data(%Actors.Actor{} = actor) do
|
||||
%{
|
||||
id: actor.id,
|
||||
name: actor.name,
|
||||
type: actor.type
|
||||
}
|
||||
end
|
||||
end
|
||||
29
elixir/apps/api/lib/api/controllers/actor_json.ex
Normal file
29
elixir/apps/api/lib/api/controllers/actor_json.ex
Normal file
@@ -0,0 +1,29 @@
|
||||
defmodule API.ActorJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Actors
|
||||
|
||||
@doc """
|
||||
Renders a list of Actors.
|
||||
"""
|
||||
def index(%{actors: actors, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(actors, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a single Actor
|
||||
"""
|
||||
def show(%{actor: actor}) do
|
||||
%{data: data(actor)}
|
||||
end
|
||||
|
||||
defp data(%Actors.Actor{} = actor) do
|
||||
%{
|
||||
id: actor.id,
|
||||
name: actor.name,
|
||||
type: actor.type
|
||||
}
|
||||
end
|
||||
end
|
||||
16
elixir/apps/api/lib/api/controllers/changeset_json.ex
Normal file
16
elixir/apps/api/lib/api/controllers/changeset_json.ex
Normal file
@@ -0,0 +1,16 @@
|
||||
defmodule API.ChangesetJSON do
|
||||
def error(%{status: status, changeset: changeset}) do
|
||||
%{
|
||||
error: %{
|
||||
reason: Plug.Conn.Status.reason_phrase(status),
|
||||
validation_errors: Ecto.Changeset.traverse_errors(changeset, &translate_error/1)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp translate_error({msg, opts}) do
|
||||
Enum.reduce(opts, msg, fn {key, value}, acc ->
|
||||
String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
5
elixir/apps/api/lib/api/controllers/error_json.ex
Normal file
5
elixir/apps/api/lib/api/controllers/error_json.ex
Normal file
@@ -0,0 +1,5 @@
|
||||
defmodule API.ErrorJSON do
|
||||
def render(template, _assigns) do
|
||||
%{error: %{reason: Phoenix.Controller.status_message_from_template(template)}}
|
||||
end
|
||||
end
|
||||
@@ -1,9 +0,0 @@
|
||||
defmodule API.ExampleController do
|
||||
use API, :controller
|
||||
|
||||
def echo(conn, params) do
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(200, Jason.encode!(params))
|
||||
end
|
||||
end
|
||||
38
elixir/apps/api/lib/api/controllers/fallback_controller.ex
Normal file
38
elixir/apps/api/lib/api/controllers/fallback_controller.ex
Normal file
@@ -0,0 +1,38 @@
|
||||
defmodule API.FallbackController do
|
||||
use Phoenix.Controller
|
||||
|
||||
def call(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> put_view(json: API.ErrorJSON)
|
||||
|> render(:"404")
|
||||
end
|
||||
|
||||
def call(conn, {:error, :unauthorized}) do
|
||||
conn
|
||||
|> put_status(:unauthorized)
|
||||
|> put_view(json: API.ErrorJSON)
|
||||
|> render(:"401")
|
||||
end
|
||||
|
||||
def call(conn, {:error, :bad_request}) do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> put_view(json: API.ErrorJSON)
|
||||
|> render(:"400")
|
||||
end
|
||||
|
||||
def call(conn, {:error, :unprocessable_entity}) do
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> put_view(json: API.ErrorJSON)
|
||||
|> render(:"422")
|
||||
end
|
||||
|
||||
def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> put_view(json: API.ChangesetJSON)
|
||||
|> render(:error, status: 422, changeset: changeset)
|
||||
end
|
||||
end
|
||||
96
elixir/apps/api/lib/api/controllers/gateway_controller.ex
Normal file
96
elixir/apps/api/lib/api/controllers/gateway_controller.ex
Normal file
@@ -0,0 +1,96 @@
|
||||
defmodule API.GatewayController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.Gateways
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Gateways"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Gateways",
|
||||
parameters: [
|
||||
gateway_group_id: [
|
||||
in: :path,
|
||||
description: "Gateway Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
],
|
||||
limit: [in: :query, description: "Limit Gateways returned", type: :integer, example: 10],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Gateway Response", "application/json", API.Schemas.Gateway.ListResponse}
|
||||
]
|
||||
|
||||
# List Gateways
|
||||
def index(conn, params) do
|
||||
list_opts =
|
||||
params
|
||||
|> Pagination.params_to_list_opts()
|
||||
|> Keyword.put(:preload, :online?)
|
||||
|
||||
with {:ok, gateways, metadata} <- Gateways.list_gateways(conn.assigns.subject, list_opts) do
|
||||
render(conn, :index, gateways: gateways, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :show,
|
||||
summary: "Show Gateway",
|
||||
parameters: [
|
||||
gateway_group_id: [
|
||||
in: :path,
|
||||
description: "Gateway Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
],
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Gateway ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Gateway Response", "application/json", API.Schemas.Gateway.Response}
|
||||
]
|
||||
|
||||
# Show a specific Gateway
|
||||
def show(conn, %{"id" => id}) do
|
||||
with {:ok, gateway} <-
|
||||
Gateways.fetch_gateway_by_id(id, conn.assigns.subject, preload: :online?) do
|
||||
render(conn, :show, gateway: gateway)
|
||||
end
|
||||
end
|
||||
|
||||
operation :delete,
|
||||
summary: "Delete a Gateway",
|
||||
parameters: [
|
||||
gateway_group_id: [
|
||||
in: :path,
|
||||
description: "Gateway Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
],
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Gateway ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Gateway Response", "application/json", API.Schemas.Gateway.Response}
|
||||
]
|
||||
|
||||
# Delete a Gateway
|
||||
def delete(conn, %{"id" => id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, gateway} <- Gateways.fetch_gateway_by_id(id, subject),
|
||||
{:ok, gateway} <- Gateways.delete_gateway(gateway, subject) do
|
||||
render(conn, :show, gateway: gateway)
|
||||
end
|
||||
end
|
||||
end
|
||||
211
elixir/apps/api/lib/api/controllers/gateway_group_controller.ex
Normal file
211
elixir/apps/api/lib/api/controllers/gateway_group_controller.ex
Normal file
@@ -0,0 +1,211 @@
|
||||
defmodule API.GatewayGroupController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.{Gateways, Tokens}
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Gateway Groups"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Gateway Groups",
|
||||
parameters: [
|
||||
limit: [
|
||||
in: :query,
|
||||
description: "Limit Gateway Groups returned",
|
||||
type: :integer,
|
||||
example: 10
|
||||
],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Gateway Group Response", "application/json", API.Schemas.GatewayGroup.ListResponse}
|
||||
]
|
||||
|
||||
# List Gateway Groups / Sites
|
||||
def index(conn, params) do
|
||||
list_opts = Pagination.params_to_list_opts(params)
|
||||
|
||||
with {:ok, gateway_groups, metadata} <- Gateways.list_groups(conn.assigns.subject, list_opts) do
|
||||
render(conn, :index, gateway_groups: gateway_groups, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :show,
|
||||
summary: "Show Gateway Group",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Gateway Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Gateway Group Response", "application/json", API.Schemas.GatewayGroup.Response}
|
||||
]
|
||||
|
||||
# Show a specific Gateway Group / Site
|
||||
def show(conn, %{"id" => id}) do
|
||||
with {:ok, gateway_group} <- Gateways.fetch_group_by_id(id, conn.assigns.subject) do
|
||||
render(conn, :show, gateway_group: gateway_group)
|
||||
end
|
||||
end
|
||||
|
||||
operation :create,
|
||||
summary: "Create Gateway Group",
|
||||
parameters: [],
|
||||
request_body:
|
||||
{"Gateway Group Attributes", "application/json", API.Schemas.GatewayGroup.Request,
|
||||
required: true},
|
||||
responses: [
|
||||
ok: {"Gateway Group Response", "application/json", API.Schemas.GatewayGroup.Response}
|
||||
]
|
||||
|
||||
# Create a new Gateway Group / Site
|
||||
def create(conn, %{"gateway_group" => params}) do
|
||||
with {:ok, gateway_group} <- Gateways.create_group(params, conn.assigns.subject) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", ~p"/gateway_groups/#{gateway_group}")
|
||||
|> render(:show, gateway_group: gateway_group)
|
||||
end
|
||||
end
|
||||
|
||||
def create(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :update,
|
||||
summary: "Update a Gateway Group",
|
||||
request_body:
|
||||
{"Gateway Group Attributes", "application/json", API.Schemas.GatewayGroup.Request,
|
||||
required: true},
|
||||
responses: [
|
||||
ok: {"Gateway Group Response", "application/json", API.Schemas.GatewayGroup.Response}
|
||||
]
|
||||
|
||||
# Update a Gateway Group / Site
|
||||
def update(conn, %{"id" => id, "gateway_group" => params}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, gateway_group} <- Gateways.fetch_group_by_id(id, subject),
|
||||
{:ok, gateway_group} <- Gateways.update_group(gateway_group, params, subject) do
|
||||
render(conn, :show, gateway_group: gateway_group)
|
||||
end
|
||||
end
|
||||
|
||||
def update(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :delete,
|
||||
summary: "Delete a Gateway Group",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Gateway Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Gateway Group Response", "application/json", API.Schemas.GatewayGroup.Response}
|
||||
]
|
||||
|
||||
# Delete a Gateway Group / Site
|
||||
def delete(conn, %{"id" => id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, gateway_group} <- Gateways.fetch_group_by_id(id, subject),
|
||||
{:ok, gateway_group} <- Gateways.delete_group(gateway_group, subject) do
|
||||
render(conn, :show, gateway_group: gateway_group)
|
||||
end
|
||||
end
|
||||
|
||||
operation :create_token,
|
||||
summary: "Create a Gateway Token",
|
||||
parameters: [
|
||||
gateway_group_id: [
|
||||
in: :path,
|
||||
description: "Gateway Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok:
|
||||
{"New Gateway Token Response", "application/json", API.Schemas.GatewayGroupToken.NewToken}
|
||||
]
|
||||
|
||||
# Create a Gateway Group Token (used for deploying a gateway)
|
||||
def create_token(conn, %{"gateway_group_id" => gateway_group_id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, gateway_group} <- Gateways.fetch_group_by_id(gateway_group_id, subject),
|
||||
{:ok, gateway_token, encoded_token} <-
|
||||
Gateways.create_token(gateway_group, %{}, subject) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> render(:token, gateway_token: gateway_token, encoded_token: encoded_token)
|
||||
end
|
||||
end
|
||||
|
||||
operation :delete_token,
|
||||
summary: "Delete a Gateway Token",
|
||||
parameters: [
|
||||
gateway_group_id: [
|
||||
in: :path,
|
||||
description: "Gateway Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
],
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Gateway Token ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok:
|
||||
{"Deleted Gateway Token Response", "application/json",
|
||||
API.Schemas.GatewayGroupToken.DeletedToken}
|
||||
]
|
||||
|
||||
# Delete/Revoke a Gateway Group Token
|
||||
def delete_token(conn, %{"gateway_group_id" => _gateway_group_id, "id" => token_id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, token} <- Tokens.fetch_token_by_id(token_id, subject),
|
||||
{:ok, token} <- Tokens.delete_token(token, subject) do
|
||||
render(conn, :deleted_token, gateway_token: token)
|
||||
end
|
||||
end
|
||||
|
||||
operation :delete_all_tokens,
|
||||
summary: "Delete all Gateway Tokens for a given Gateway Group",
|
||||
parameters: [
|
||||
gateway_group_id: [
|
||||
in: :path,
|
||||
description: "Gateway Group ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok:
|
||||
{"Deleted Gateway Tokens Response", "application/json",
|
||||
API.Schemas.GatewayGroupToken.DeletedTokens}
|
||||
]
|
||||
|
||||
def delete_all_tokens(conn, %{"gateway_group_id" => gateway_group_id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, gateway_group} <- Gateways.fetch_group_by_id(gateway_group_id, subject),
|
||||
{:ok, deleted_tokens} <- Tokens.delete_tokens_for(gateway_group, subject) do
|
||||
render(conn, :deleted_tokens, %{tokens: deleted_tokens})
|
||||
end
|
||||
end
|
||||
end
|
||||
58
elixir/apps/api/lib/api/controllers/gateway_group_json.ex
Normal file
58
elixir/apps/api/lib/api/controllers/gateway_group_json.ex
Normal file
@@ -0,0 +1,58 @@
|
||||
defmodule API.GatewayGroupJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Gateways
|
||||
|
||||
@doc """
|
||||
Renders a list of Sites / Gateway Groups.
|
||||
"""
|
||||
def index(%{gateway_groups: gateway_groups, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(gateway_groups, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a single Site / Gateway Group
|
||||
"""
|
||||
def show(%{gateway_group: group}) do
|
||||
%{data: data(group)}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a Gateway Group Token
|
||||
"""
|
||||
def token(%{gateway_token: token, encoded_token: encoded_token}) do
|
||||
%{
|
||||
data: %{
|
||||
id: token.id,
|
||||
token: encoded_token
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a deleted Gateway Group Token
|
||||
"""
|
||||
def deleted_token(%{gateway_token: token}) do
|
||||
%{
|
||||
data: %{
|
||||
id: token.id
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render all deleted Gateway Group Tokens
|
||||
"""
|
||||
def deleted_tokens(%{tokens: tokens}) do
|
||||
%{data: for(token <- tokens, do: %{id: token.id})}
|
||||
end
|
||||
|
||||
defp data(%Gateways.Group{} = group) do
|
||||
%{
|
||||
id: group.id,
|
||||
name: group.name
|
||||
}
|
||||
end
|
||||
end
|
||||
31
elixir/apps/api/lib/api/controllers/gateway_json.ex
Normal file
31
elixir/apps/api/lib/api/controllers/gateway_json.ex
Normal file
@@ -0,0 +1,31 @@
|
||||
defmodule API.GatewayJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Gateways
|
||||
|
||||
@doc """
|
||||
Renders a list of Gateways.
|
||||
"""
|
||||
def index(%{gateways: gateways, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(gateways, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a single Gateway
|
||||
"""
|
||||
def show(%{gateway: gateway}) do
|
||||
%{data: data(gateway)}
|
||||
end
|
||||
|
||||
defp data(%Gateways.Gateway{} = gateway) do
|
||||
%{
|
||||
id: gateway.id,
|
||||
name: gateway.name,
|
||||
ipv4: gateway.ipv4,
|
||||
ipv6: gateway.ipv6,
|
||||
online: gateway.online?
|
||||
}
|
||||
end
|
||||
end
|
||||
136
elixir/apps/api/lib/api/controllers/identity_controller.ex
Normal file
136
elixir/apps/api/lib/api/controllers/identity_controller.ex
Normal file
@@ -0,0 +1,136 @@
|
||||
defmodule API.IdentityController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.Auth
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Identities"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Identities for an Actor",
|
||||
parameters: [
|
||||
actor_id: [in: :path, description: "Actor ID", type: :string],
|
||||
limit: [in: :query, description: "Limit Identities returned", type: :integer, example: 10],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Identity List Response", "application/json", API.Schemas.Identity.ListResponse}
|
||||
]
|
||||
|
||||
# List Identities
|
||||
def index(conn, %{"actor_id" => actor_id} = params) do
|
||||
subject = conn.assigns.subject
|
||||
list_opts = Pagination.params_to_list_opts(params)
|
||||
|
||||
with {:ok, actor} <- Domain.Actors.fetch_actor_by_id(actor_id, subject),
|
||||
{:ok, identities, metadata} <- Auth.list_identities_for(actor, subject, list_opts) do
|
||||
render(conn, :index, identities: identities, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :create,
|
||||
summary: "Create an Identity for an Actor",
|
||||
parameters: [
|
||||
actor_id: [in: :path, description: "Actor ID", type: :string]
|
||||
],
|
||||
request_body:
|
||||
{"Identity Attributes", "application/json", API.Schemas.Identity.Request, required: true},
|
||||
responses: [
|
||||
ok: {"Identity Response", "application/json", API.Schemas.Identity.Response}
|
||||
]
|
||||
|
||||
# Create an Identity
|
||||
def create(conn, %{
|
||||
"actor_id" => actor_id,
|
||||
"provider_id" => provider_id,
|
||||
"identity" => params
|
||||
}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
params =
|
||||
Map.put_new(
|
||||
params,
|
||||
"provider_identifier_confirmation",
|
||||
Map.get(params, "provider_identifier")
|
||||
)
|
||||
|
||||
with {:ok, actor} <- Domain.Actors.fetch_actor_by_id(actor_id, subject),
|
||||
{:ok, provider} <- Auth.fetch_provider_by_id(provider_id, subject),
|
||||
{:provider_check, true} <- valid_provider?(provider),
|
||||
{:ok, identity} <- Auth.create_identity(actor, provider, params) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", ~p"/actors/#{actor_id}/identities/#{identity.id}")
|
||||
|> render(:show, identity: identity)
|
||||
else
|
||||
{:provider_check, _false} -> {:error, :unprocessable_entity}
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
def create(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :show,
|
||||
summary: "Show Identity",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Identity ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Identity Response", "application/json", API.Schemas.Identity.Response}
|
||||
]
|
||||
|
||||
# Show a specific Identity
|
||||
def show(conn, %{"id" => id}) do
|
||||
with {:ok, identity} <- Auth.fetch_identity_by_id(id, conn.assigns.subject) do
|
||||
render(conn, :show, identity: identity)
|
||||
end
|
||||
end
|
||||
|
||||
operation :delete,
|
||||
summary: "Delete an Identity",
|
||||
parameters: [
|
||||
actor_id: [
|
||||
in: :path,
|
||||
description: "Actor ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
],
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Identity ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Identity Response", "application/json", API.Schemas.Identity.Response}
|
||||
]
|
||||
|
||||
# Delete an Identity
|
||||
def delete(conn, %{"id" => id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, identity} <- Auth.fetch_identity_by_id(id, subject),
|
||||
{:ok, identity} <- Auth.delete_identity(identity, subject) do
|
||||
render(conn, :show, identity: identity)
|
||||
end
|
||||
end
|
||||
|
||||
defp valid_provider?(provider) do
|
||||
valid? =
|
||||
Auth.fetch_provider_capabilities!(provider)
|
||||
|> Keyword.fetch!(:provisioners)
|
||||
|> Enum.member?(:manual)
|
||||
|
||||
{:provider_check, valid?}
|
||||
end
|
||||
end
|
||||
30
elixir/apps/api/lib/api/controllers/identity_json.ex
Normal file
30
elixir/apps/api/lib/api/controllers/identity_json.ex
Normal file
@@ -0,0 +1,30 @@
|
||||
defmodule API.IdentityJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Auth.Identity
|
||||
|
||||
@doc """
|
||||
Renders a list of Identities.
|
||||
"""
|
||||
def index(%{identities: identities, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(identities, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a single Identity
|
||||
"""
|
||||
def show(%{identity: identity}) do
|
||||
%{data: data(identity)}
|
||||
end
|
||||
|
||||
defp data(%Identity{} = identity) do
|
||||
%{
|
||||
id: identity.id,
|
||||
actor_id: identity.actor_id,
|
||||
provider_id: identity.provider_id,
|
||||
provider_identifier: identity.provider_identifier
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,81 @@
|
||||
defmodule API.IdentityProviderController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.Auth
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Identity Providers"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Identity Providers",
|
||||
parameters: [
|
||||
limit: [
|
||||
in: :query,
|
||||
description: "Limit Identity Providers returned",
|
||||
type: :integer,
|
||||
example: 10
|
||||
],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok:
|
||||
{"Identity Provider Response", "application/json",
|
||||
API.Schemas.IdentityProvider.ListResponse}
|
||||
]
|
||||
|
||||
def index(conn, params) do
|
||||
list_opts = Pagination.params_to_list_opts(params)
|
||||
|
||||
with {:ok, identity_providers, metadata} <-
|
||||
Auth.list_providers(conn.assigns.subject, list_opts) do
|
||||
render(conn, :index, identity_providers: identity_providers, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :show,
|
||||
summary: "Show Identity Provider",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Identity Provider ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok:
|
||||
{"Identity Provider Response", "application/json", API.Schemas.IdentityProvider.Response}
|
||||
]
|
||||
|
||||
def show(conn, %{"id" => id}) do
|
||||
with {:ok, identity_provider} <- Auth.fetch_provider_by_id(id, conn.assigns.subject) do
|
||||
render(conn, :show, identity_provider: identity_provider)
|
||||
end
|
||||
end
|
||||
|
||||
operation :delete,
|
||||
summary: "Delete a Identity Provider",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Identity Provider ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok:
|
||||
{"Identity Provider Response", "application/json", API.Schemas.IdentityProvider.Response}
|
||||
]
|
||||
|
||||
def delete(conn, %{"id" => id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, identity_provider} <- Auth.fetch_provider_by_id(id, subject),
|
||||
{:ok, identity_provider} <- Auth.delete_provider(identity_provider, subject) do
|
||||
render(conn, :show, identity_provider: identity_provider)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
defmodule API.IdentityProviderJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Auth.Provider
|
||||
|
||||
@doc """
|
||||
Renders a list of Identity Providers.
|
||||
"""
|
||||
def index(%{identity_providers: identity_providers, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(identity_providers, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a single Identity Provider.
|
||||
"""
|
||||
def show(%{identity_provider: identity_provider}) do
|
||||
%{data: data(identity_provider)}
|
||||
end
|
||||
|
||||
defp data(%Provider{} = provider) do
|
||||
%{
|
||||
id: provider.id,
|
||||
name: provider.name
|
||||
}
|
||||
end
|
||||
end
|
||||
35
elixir/apps/api/lib/api/controllers/pagination.ex
Normal file
35
elixir/apps/api/lib/api/controllers/pagination.ex
Normal file
@@ -0,0 +1,35 @@
|
||||
defmodule API.Pagination do
|
||||
alias LoggerJSON.Formatter.Metadata
|
||||
alias Domain.Repo.Paginator.Metadata
|
||||
|
||||
def params_to_list_opts(params) do
|
||||
[
|
||||
page: params_to_page(params)
|
||||
]
|
||||
end
|
||||
|
||||
def metadata(%Metadata{} = metadata) do
|
||||
%{
|
||||
count: metadata.count,
|
||||
limit: metadata.limit,
|
||||
next_page: metadata.next_page_cursor,
|
||||
prev_page: metadata.previous_page_cursor
|
||||
}
|
||||
end
|
||||
|
||||
defp params_to_page(%{"limit" => limit, "page_cursor" => cursor}) do
|
||||
[cursor: cursor, limit: String.to_integer(limit)]
|
||||
end
|
||||
|
||||
defp params_to_page(%{"limit" => limit}) do
|
||||
[limit: String.to_integer(limit)]
|
||||
end
|
||||
|
||||
defp params_to_page(%{"page_cursor" => cursor}) do
|
||||
[cursor: cursor]
|
||||
end
|
||||
|
||||
defp params_to_page(_params) do
|
||||
[]
|
||||
end
|
||||
end
|
||||
119
elixir/apps/api/lib/api/controllers/policy_controller.ex
Normal file
119
elixir/apps/api/lib/api/controllers/policy_controller.ex
Normal file
@@ -0,0 +1,119 @@
|
||||
defmodule API.PolicyController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.Policies
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Policies"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Policies",
|
||||
parameters: [
|
||||
limit: [in: :query, description: "Limit Policies returned", type: :integer, example: 10],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Policy Response", "application/json", API.Schemas.Policy.ListResponse}
|
||||
]
|
||||
|
||||
# List Policies
|
||||
def index(conn, params) do
|
||||
list_opts = Pagination.params_to_list_opts(params)
|
||||
|
||||
with {:ok, policies, metadata} <- Policies.list_policies(conn.assigns.subject, list_opts) do
|
||||
render(conn, :index, policies: policies, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :show,
|
||||
summary: "Show Policy",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Policy ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Policy Response", "application/json", API.Schemas.Policy.Response}
|
||||
]
|
||||
|
||||
# Show a specific Policy
|
||||
def show(conn, %{"id" => id}) do
|
||||
with {:ok, policy} <- Policies.fetch_policy_by_id(id, conn.assigns.subject) do
|
||||
render(conn, :show, policy: policy)
|
||||
end
|
||||
end
|
||||
|
||||
operation :create,
|
||||
summary: "Create Policy",
|
||||
parameters: [],
|
||||
request_body:
|
||||
{"Policy Attributes", "application/json", API.Schemas.Policy.Request, required: true},
|
||||
responses: [
|
||||
ok: {"Policy Response", "application/json", API.Schemas.Policy.Response}
|
||||
]
|
||||
|
||||
# Create a new Policy
|
||||
def create(conn, %{"policy" => params}) do
|
||||
with {:ok, policy} <- Policies.create_policy(params, conn.assigns.subject) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", ~p"/policies/#{policy}")
|
||||
|> render(:show, policy: policy)
|
||||
end
|
||||
end
|
||||
|
||||
def create(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :update,
|
||||
summary: "Update a Policy",
|
||||
request_body:
|
||||
{"Policy Attributes", "application/json", API.Schemas.Policy.Request, required: true},
|
||||
responses: [
|
||||
ok: {"Policy Response", "application/json", API.Schemas.Policy.Response}
|
||||
]
|
||||
|
||||
# Update a Policy
|
||||
def update(conn, %{"id" => id, "policy" => params}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, policy} <- Policies.fetch_policy_by_id(id, subject),
|
||||
{:ok, policy} <- Policies.update_policy(policy, params, subject) do
|
||||
render(conn, :show, policy: policy)
|
||||
end
|
||||
end
|
||||
|
||||
def update(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :delete,
|
||||
summary: "Delete a Policy",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Policy ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Policy Response", "application/json", API.Schemas.Policy.Response}
|
||||
]
|
||||
|
||||
# Delete a Policy
|
||||
def delete(conn, %{"id" => id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, policy} <- Policies.fetch_policy_by_id(id, subject),
|
||||
{:ok, policy} <- Policies.delete_policy(policy, subject) do
|
||||
render(conn, :show, policy: policy)
|
||||
end
|
||||
end
|
||||
end
|
||||
30
elixir/apps/api/lib/api/controllers/policy_json.ex
Normal file
30
elixir/apps/api/lib/api/controllers/policy_json.ex
Normal file
@@ -0,0 +1,30 @@
|
||||
defmodule API.PolicyJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Policies
|
||||
|
||||
@doc """
|
||||
Renders a list of Policies.
|
||||
"""
|
||||
def index(%{policies: policies, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(policies, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Render a single Policy
|
||||
"""
|
||||
def show(%{policy: policy}) do
|
||||
%{data: data(policy)}
|
||||
end
|
||||
|
||||
defp data(%Policies.Policy{} = policy) do
|
||||
%{
|
||||
id: policy.id,
|
||||
actor_group_id: policy.actor_group_id,
|
||||
resource_id: policy.resource_id,
|
||||
description: policy.description
|
||||
}
|
||||
end
|
||||
end
|
||||
122
elixir/apps/api/lib/api/controllers/resource_controller.ex
Normal file
122
elixir/apps/api/lib/api/controllers/resource_controller.ex
Normal file
@@ -0,0 +1,122 @@
|
||||
defmodule API.ResourceController do
|
||||
use API, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias API.Pagination
|
||||
alias Domain.Resources
|
||||
|
||||
action_fallback API.FallbackController
|
||||
|
||||
tags ["Resources"]
|
||||
|
||||
operation :index,
|
||||
summary: "List Resources",
|
||||
parameters: [
|
||||
limit: [in: :query, description: "Limit Resources returned", type: :integer, example: 10],
|
||||
page_cursor: [in: :query, description: "Next/Prev page cursor", type: :string]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Resource Response", "application/json", API.Schemas.Resource.ListResponse}
|
||||
]
|
||||
|
||||
def index(conn, params) do
|
||||
list_opts = Pagination.params_to_list_opts(params)
|
||||
|
||||
with {:ok, resources, metadata} <-
|
||||
Resources.list_resources(conn.assigns.subject, list_opts) do
|
||||
render(conn, :index, resources: resources, metadata: metadata)
|
||||
end
|
||||
end
|
||||
|
||||
operation :show,
|
||||
summary: "Show Resource",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Resource ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Resource Response", "application/json", API.Schemas.Resource.Response}
|
||||
]
|
||||
|
||||
def show(conn, %{"id" => id}) do
|
||||
with {:ok, resource} <- Resources.fetch_resource_by_id(id, conn.assigns.subject) do
|
||||
render(conn, :show, resource: resource)
|
||||
end
|
||||
end
|
||||
|
||||
operation :create,
|
||||
summary: "Create Resource",
|
||||
parameters: [],
|
||||
request_body:
|
||||
{"Resource Attributes", "application/json", API.Schemas.Resource.Request, required: true},
|
||||
responses: [
|
||||
ok: {"Resource Response", "application/json", API.Schemas.Resource.Response}
|
||||
]
|
||||
|
||||
def create(conn, %{"resource" => params}) do
|
||||
attrs = set_param_defaults(params)
|
||||
|
||||
with {:ok, resource} <- Resources.create_resource(attrs, conn.assigns.subject) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", ~p"/resources/#{resource}")
|
||||
|> render(:show, resource: resource)
|
||||
end
|
||||
end
|
||||
|
||||
def create(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :update,
|
||||
summary: "Update Resource",
|
||||
request_body:
|
||||
{"Resource Attributes", "application/json", API.Schemas.Resource.Request, required: true},
|
||||
responses: [
|
||||
ok: {"Resource Response", "application/json", API.Schemas.Resource.Response}
|
||||
]
|
||||
|
||||
def update(conn, %{"id" => id, "resource" => params}) do
|
||||
subject = conn.assigns.subject
|
||||
attrs = set_param_defaults(params)
|
||||
|
||||
with {:ok, resource} <- Resources.fetch_resource_by_id(id, subject),
|
||||
{:ok, resource} <- Resources.update_resource(resource, attrs, subject) do
|
||||
render(conn, :show, resource: resource)
|
||||
end
|
||||
end
|
||||
|
||||
def update(_conn, _params) do
|
||||
{:error, :bad_request}
|
||||
end
|
||||
|
||||
operation :delete,
|
||||
summary: "Delete Resource",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "Resource ID",
|
||||
type: :string,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Resource Response", "application/json", API.Schemas.Resource.Response}
|
||||
]
|
||||
|
||||
def delete(conn, %{"id" => id}) do
|
||||
subject = conn.assigns.subject
|
||||
|
||||
with {:ok, resource} <- Resources.fetch_resource_by_id(id, subject),
|
||||
{:ok, resource} <- Resources.delete_resource(resource, subject) do
|
||||
render(conn, :show, resource: resource)
|
||||
end
|
||||
end
|
||||
|
||||
defp set_param_defaults(params) do
|
||||
Map.put_new(params, "filters", %{})
|
||||
end
|
||||
end
|
||||
31
elixir/apps/api/lib/api/controllers/resource_json.ex
Normal file
31
elixir/apps/api/lib/api/controllers/resource_json.ex
Normal file
@@ -0,0 +1,31 @@
|
||||
defmodule API.ResourceJSON do
|
||||
alias API.Pagination
|
||||
alias Domain.Resources.Resource
|
||||
|
||||
@doc """
|
||||
Renders a list of resources.
|
||||
"""
|
||||
def index(%{resources: resources, metadata: metadata}) do
|
||||
%{
|
||||
data: Enum.map(resources, &data/1),
|
||||
metadata: Pagination.metadata(metadata)
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a single resource.
|
||||
"""
|
||||
def show(%{resource: resource}) do
|
||||
%{data: data(resource)}
|
||||
end
|
||||
|
||||
defp data(%Resource{} = resource) do
|
||||
%{
|
||||
id: resource.id,
|
||||
name: resource.name,
|
||||
address: resource.address,
|
||||
description: resource.address_description,
|
||||
type: resource.type
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -1,12 +1,12 @@
|
||||
defmodule API.ErrorView do
|
||||
def render("500.json", _assigns) do
|
||||
%{errors: %{detail: "internal_error"}}
|
||||
%{error: %{reason: "internal_error"}}
|
||||
end
|
||||
|
||||
# By default, Phoenix returns the status message from
|
||||
# the template name. For example, "404.json" becomes
|
||||
# "Not Found".
|
||||
def render(template, _assigns) do
|
||||
%{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
|
||||
%{error: %{reason: Phoenix.Controller.status_message_from_template(template)}}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,9 +11,16 @@ defmodule API.Plugs.Auth do
|
||||
assign(conn, :subject, subject)
|
||||
else
|
||||
_ ->
|
||||
# conn
|
||||
# |> put_resp_content_type("application/json")
|
||||
# |> send_resp(401, Jason.encode!(%{"error" => "invalid_access_token"}))
|
||||
# |> halt()
|
||||
|
||||
# TODO: BRIAN - Confirm that this change won't break anything with the clients or gateways
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(401, Jason.encode!(%{"error" => "invalid_access_token"}))
|
||||
|> put_status(401)
|
||||
|> Phoenix.Controller.put_view(json: API.ErrorJSON)
|
||||
|> Phoenix.Controller.render(:"401")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,16 +15,53 @@ defmodule API.Router do
|
||||
plug :accepts, ["html", "xml", "json"]
|
||||
end
|
||||
|
||||
pipeline :openapi do
|
||||
plug OpenApiSpex.Plug.PutApiSpec, module: API.ApiSpec
|
||||
end
|
||||
|
||||
scope "/openapi" do
|
||||
pipe_through :openapi
|
||||
|
||||
get "/", OpenApiSpex.Plug.RenderSpec, []
|
||||
end
|
||||
|
||||
scope "/swaggerui" do
|
||||
pipe_through :public
|
||||
|
||||
get "/", OpenApiSpex.Plug.SwaggerUI, path: "/openapi"
|
||||
end
|
||||
|
||||
scope "/", API do
|
||||
pipe_through :public
|
||||
|
||||
get "/healthz", HealthController, :healthz
|
||||
end
|
||||
|
||||
scope "/v1", API do
|
||||
scope "/", API do
|
||||
pipe_through :api
|
||||
|
||||
post "/echo", ExampleController, :echo
|
||||
resources "/resources", ResourceController, except: [:new, :edit]
|
||||
resources "/policies", PolicyController, except: [:new, :edit]
|
||||
|
||||
resources "/gateway_groups", GatewayGroupController, except: [:new, :edit] do
|
||||
post "/tokens", GatewayGroupController, :create_token
|
||||
delete "/tokens", GatewayGroupController, :delete_all_tokens
|
||||
delete "/tokens/:id", GatewayGroupController, :delete_token
|
||||
resources "/gateways", GatewayController, except: [:new, :edit, :create, :update]
|
||||
end
|
||||
|
||||
resources "/actors", ActorController, except: [:new, :edit] do
|
||||
resources "/identities", IdentityController, except: [:new, :edit, :update]
|
||||
post "/providers/:provider_id/identities/", IdentityController, :create
|
||||
end
|
||||
|
||||
resources "/actor_groups", ActorGroupController, except: [:new, :edit] do
|
||||
get "/memberships", ActorGroupMembershipController, :index
|
||||
put "/memberships", ActorGroupMembershipController, :update_put
|
||||
patch "/memberships", ActorGroupMembershipController, :update_patch
|
||||
end
|
||||
|
||||
resources "/identity_providers", IdentityProviderController, only: [:index, :show, :delete]
|
||||
end
|
||||
|
||||
scope "/integrations", API.Integrations do
|
||||
|
||||
170
elixir/apps/api/lib/api/schemas/actor_group_membership_schema.ex
Normal file
170
elixir/apps/api/lib/api/schemas/actor_group_membership_schema.ex
Normal file
@@ -0,0 +1,170 @@
|
||||
defmodule API.Schemas.ActorGroupMembership do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor Group Membership",
|
||||
description: "Actor Group Membership",
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Actor ID"},
|
||||
name: %Schema{type: :string, description: "Actor Name"},
|
||||
type: %Schema{type: :string, description: "Actor Type"}
|
||||
}
|
||||
},
|
||||
example: [
|
||||
%{
|
||||
"id" => "7cb89288-1fb3-433e-a522-2d087e45988d",
|
||||
"name" => "John Doe",
|
||||
"type" => "account_user"
|
||||
}
|
||||
]
|
||||
})
|
||||
end
|
||||
|
||||
defmodule PatchRequest do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.ActorGroupMembership
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor Group Membership Patch Request",
|
||||
description: "PATCH body for updating Actor Group Memberships",
|
||||
type: :object,
|
||||
properties: %{
|
||||
memberships: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
add: %Schema{
|
||||
type: :array,
|
||||
description: "Array of Actor IDs",
|
||||
items: %Schema{type: :string, description: "Actor ID"}
|
||||
},
|
||||
remove: %Schema{
|
||||
type: :array,
|
||||
description: "Array of Actor IDs",
|
||||
items: %Schema{type: :string, description: "Actor ID"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:memberships],
|
||||
example: %{
|
||||
"memberships" => %{
|
||||
"add" => ["1234-1234"],
|
||||
"remove" => ["2345-2345"]
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule PutRequest do
|
||||
require OpenApiSpex
|
||||
alias Ecto.Adapter.Schema
|
||||
alias Ecto.Adapter.Schema
|
||||
alias Ecto.Adapter.Schema
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.ActorGroupMembership
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor Group Membership Put Request",
|
||||
description: "PUT body for updating Actor Group Memberships",
|
||||
type: :object,
|
||||
properties: %{
|
||||
memberships: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
actor_id: %Schema{type: :string, description: "Actor ID"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:memberships],
|
||||
example: %{
|
||||
"memberships" => [
|
||||
%{"actor_id" => "1234-1234"},
|
||||
%{"actor_id" => "2345-2345"}
|
||||
]
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.ActorGroupMembership
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor Group Membership List Response",
|
||||
description: "Response schema for Actor Group Memberships",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{
|
||||
description: "Actor Group Membership details",
|
||||
type: :array,
|
||||
items: ActorGroupMembership.Schema
|
||||
},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "John Doe",
|
||||
"type" => "account_user"
|
||||
},
|
||||
%{
|
||||
"id" => "cc9f561a-444d-4083-ab38-0abc6cf2314c",
|
||||
"name" => "Jane Smith",
|
||||
"type" => "account_admin_user"
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule MembershipResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor Group Membership Response",
|
||||
description: "Response schema for Actor Group Membership Updates",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{
|
||||
type: :object,
|
||||
description: "Actor Group Memberships",
|
||||
properties: %{
|
||||
actor_ids: %Schema{
|
||||
description: "Actor IDs",
|
||||
type: :array,
|
||||
items: %Schema{type: :string, description: "Actor ID"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"actor_ids" => [
|
||||
"4ddfa557-7dfc-484f-894c-2024ec3fe9f7",
|
||||
"89d22f71-939d-442d-b148-897b730adfb4"
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
99
elixir/apps/api/lib/api/schemas/actor_group_schema.ex
Normal file
99
elixir/apps/api/lib/api/schemas/actor_group_schema.ex
Normal file
@@ -0,0 +1,99 @@
|
||||
defmodule API.Schemas.ActorGroup do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor Group",
|
||||
description: "Actor Group",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Actor Group ID"},
|
||||
name: %Schema{type: :string, description: "Actor Group Name"}
|
||||
},
|
||||
required: [:id, :name],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "Engineering"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Request do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.ActorGroup
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor Group Request",
|
||||
description: "POST body for creating an Actor Group",
|
||||
type: :object,
|
||||
properties: %{
|
||||
actor_group: %Schema{anyOf: [ActorGroup.Schema]}
|
||||
},
|
||||
required: [:actor_group],
|
||||
example: %{
|
||||
"actor_group" => %{
|
||||
"name" => "Engineering"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Response do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.ActorGroup
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor GroupResponse",
|
||||
description: "Response schema for single Actor Group",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: ActorGroup.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "Engineering"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.ActorGroup
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor Group List Response",
|
||||
description: "Response schema for multiple Actor Groups",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{description: "Actor Group details", type: :array, items: ActorGroup.Schema},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "Engineering"
|
||||
},
|
||||
%{
|
||||
"id" => "4ae929a7-1973-43f2-a1a8-9221b91a4c0e",
|
||||
"name" => "Finance"
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
109
elixir/apps/api/lib/api/schemas/actor_schema.ex
Normal file
109
elixir/apps/api/lib/api/schemas/actor_schema.ex
Normal file
@@ -0,0 +1,109 @@
|
||||
defmodule API.Schemas.Actor do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Actor",
|
||||
description: "Actor",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Actor ID"},
|
||||
name: %Schema{
|
||||
type: :string,
|
||||
description: "Actor Name",
|
||||
pattern: ~r/[a-zA-Z][a-zA-Z0-9_]+/
|
||||
},
|
||||
type: %Schema{type: :string, description: "Actor Type"}
|
||||
},
|
||||
required: [:name, :type],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "John Doe",
|
||||
"type" => "account_admin_user"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Request do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Actor
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ActorRequest",
|
||||
description: "POST body for creating an Actor",
|
||||
type: :object,
|
||||
properties: %{
|
||||
actor: %Schema{anyOf: [Actor.Schema]}
|
||||
},
|
||||
required: [:actor],
|
||||
example: %{
|
||||
"actor" => %{
|
||||
"name" => "Joe User",
|
||||
"type" => "account_admin_user"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Response do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Actor
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ActorResponse",
|
||||
description: "Response schema for single Actor",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: Actor.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "John Doe",
|
||||
"type" => "account_admin_user"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Actor
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ActorsResponse",
|
||||
description: "Response schema for multiple Actors",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{description: "Actors details", type: :array, items: Actor.Schema},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "John Doe",
|
||||
"type" => "account_admin_user"
|
||||
},
|
||||
%{
|
||||
"id" => "84e7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "Jane Smith",
|
||||
"type" => "account_user"
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
103
elixir/apps/api/lib/api/schemas/gateway_group_schema.ex
Normal file
103
elixir/apps/api/lib/api/schemas/gateway_group_schema.ex
Normal file
@@ -0,0 +1,103 @@
|
||||
defmodule API.Schemas.GatewayGroup do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Gateway Group",
|
||||
description: "Gateway Group",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Gateway Group ID"},
|
||||
name: %Schema{type: :string, description: "Gateway Group Name"}
|
||||
},
|
||||
required: [:id, :name],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "vpc-us-east"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Request do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.GatewayGroup
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Gateway GroupRequest",
|
||||
description: "POST body for creating a Gateway Group",
|
||||
type: :object,
|
||||
properties: %{
|
||||
gateway_group: %Schema{anyOf: [GatewayGroup.Schema]}
|
||||
},
|
||||
required: [:gateway_group],
|
||||
example: %{
|
||||
"gateway_group" => %{
|
||||
"name" => "vpc-us-east"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Response do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.GatewayGroup
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Gateway GroupResponse",
|
||||
description: "Response schema for single Gateway Group",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: GatewayGroup.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "vpc-us-east"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.GatewayGroup
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Gateway Group List Response",
|
||||
description: "Response schema for multiple Gateway Groups",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{
|
||||
description: "Gateway Group details",
|
||||
type: :array,
|
||||
items: GatewayGroup.Schema
|
||||
},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "vpc-us-east"
|
||||
},
|
||||
%{
|
||||
"id" => "6301d7d2-4938-4123-87de-282c01cca656",
|
||||
"name" => "vpc-us-west"
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,93 @@
|
||||
defmodule API.Schemas.GatewayGroupToken do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Gateway Group Token",
|
||||
description: "Gateway Group Token",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Gateway Group Token ID"},
|
||||
token: %Schema{type: :string, description: "Gateway Group Token"}
|
||||
},
|
||||
required: [:id, :token],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"token" => "secret-token-here"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule NewToken do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.GatewayGroupToken
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "New Gateway Group Token Response",
|
||||
description: "Response schema for a new Gateway Group Token",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: GatewayGroupToken.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"token" => "secret-token-here"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule DeletedToken do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.GatewayGroupToken
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Deleted Gateway Group Token Response",
|
||||
description: "Response schema for a new Gateway Group Token",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: GatewayGroupToken.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule DeletedTokens do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.GatewayGroupToken
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Deleted Gateway Group Token List Response",
|
||||
description: "Response schema for deleted Gateway Group Tokens",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{
|
||||
description: "Deleted Gateway Group Tokens",
|
||||
type: :array,
|
||||
items: GatewayGroupToken.Schema
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205"
|
||||
},
|
||||
%{
|
||||
"id" => "6301d7d2-4938-4123-87de-282c01cca656"
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
106
elixir/apps/api/lib/api/schemas/gateway_schema.ex
Normal file
106
elixir/apps/api/lib/api/schemas/gateway_schema.ex
Normal file
@@ -0,0 +1,106 @@
|
||||
defmodule API.Schemas.Gateway do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Gateway",
|
||||
description: "Gateway",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Gateway ID"},
|
||||
name: %Schema{
|
||||
type: :string,
|
||||
description: "Gateway Name",
|
||||
pattern: ~r/[a-zA-Z][a-zA-Z0-9_]+/
|
||||
},
|
||||
ipv4: %Schema{
|
||||
type: :string,
|
||||
description: "IPv4 Address of Gateway"
|
||||
},
|
||||
ipv6: %Schema{
|
||||
type: :string,
|
||||
description: "IPv6 Address of Gateway"
|
||||
},
|
||||
online: %Schema{
|
||||
type: :boolean,
|
||||
description: "Online status of Gateway"
|
||||
}
|
||||
},
|
||||
required: [:name, :type],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "vpc-us-east",
|
||||
"ipv4" => "1.2.3.4",
|
||||
"ipv6" => "",
|
||||
"online" => true
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Response do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Gateway
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Gateway Response",
|
||||
description: "Response schema for single Gateway",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: Gateway.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "vpc-us-east",
|
||||
"ipv4" => "1.2.3.4",
|
||||
"ipv6" => "",
|
||||
"online" => true
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Gateway
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "GatewaysResponse",
|
||||
description: "Response schema for multiple Gateways",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{description: "Gateways details", type: :array, items: Gateway.Schema},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "vpc-us-east",
|
||||
"ipv4" => "1.2.3.4",
|
||||
"ipv6" => "",
|
||||
"online" => true
|
||||
},
|
||||
%{
|
||||
"id" => "6ecc106b-75c1-48a5-846c-14782180c1ff",
|
||||
"name" => "vpc-us-west",
|
||||
"ipv4" => "5.6.7.8",
|
||||
"ipv6" => "",
|
||||
"online" => true
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
82
elixir/apps/api/lib/api/schemas/identity_provider_schema.ex
Normal file
82
elixir/apps/api/lib/api/schemas/identity_provider_schema.ex
Normal file
@@ -0,0 +1,82 @@
|
||||
defmodule API.Schemas.IdentityProvider do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Identity Provider",
|
||||
description: "Identity Provider",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Identity Provider ID"},
|
||||
name: %Schema{type: :string, description: "Identity Provider name"}
|
||||
},
|
||||
required: [:id, :name],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "OIDC Provider"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Response do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.IdentityProvider
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Identity Provider Response",
|
||||
description: "Response schema for single Identity Provider",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: IdentityProvider.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "OIDC Provider"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.IdentityProvider
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Identity Provider List Response",
|
||||
description: "Response schema for multiple Identity Providers",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{
|
||||
description: "Identity Provider details",
|
||||
type: :array,
|
||||
items: IdentityProvider.Schema
|
||||
},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "OIDC Provider"
|
||||
},
|
||||
%{
|
||||
"id" => "23ca9d03-c904-42c9-bd38-f89a6d57d3a8",
|
||||
"name" => "Okta"
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
111
elixir/apps/api/lib/api/schemas/identity_schema.ex
Normal file
111
elixir/apps/api/lib/api/schemas/identity_schema.ex
Normal file
@@ -0,0 +1,111 @@
|
||||
defmodule API.Schemas.Identity do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Identity",
|
||||
description: "Identity",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Identity ID"},
|
||||
actor_id: %Schema{type: :string, description: "Actor ID"},
|
||||
provider_id: %Schema{type: :string, description: "Identity Provider ID"},
|
||||
provider_identifier: %Schema{type: :string, description: "Identifier from Provider"}
|
||||
},
|
||||
required: [:id, :actor_id, :provider_id, :provider_identifier],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"actor_id" => "cdfa97e6-cca1-41db-8fc7-864daedb46df",
|
||||
"provider_id" => "989f9e96-e348-47ec-ba85-869fcd7adb19",
|
||||
"provider_identifier" => "foo@bar.com"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Request do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Identity
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "IdentityRequest",
|
||||
description: "POST body for creating a Identity",
|
||||
type: :object,
|
||||
properties: %{
|
||||
identity: %Schema{anyOf: [Identity.Schema]}
|
||||
},
|
||||
required: [:identity],
|
||||
example: %{
|
||||
"identity" => %{
|
||||
"actor_id" => "cdfa97e6-cca1-41db-8fc7-864daedb46df",
|
||||
"provider_id" => "989f9e96-e348-47ec-ba85-869fcd7adb19",
|
||||
"provider_identifier" => "foo@bar.com"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Response do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Identity
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "IdentityResponse",
|
||||
description: "Response schema for single Identity",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: Identity.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"actor_id" => "cdfa97e6-cca1-41db-8fc7-864daedb46df",
|
||||
"provider_id" => "989f9e96-e348-47ec-ba85-869fcd7adb19",
|
||||
"provider_identifier" => "foo@bar.com"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Identity
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Identity List Response",
|
||||
description: "Response schema for multiple Identities",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{description: "Identity details", type: :array, items: Identity.Schema},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"actor_id" => "8f44a02b-b8eb-406f-8202-4274bf60ebd0",
|
||||
"provider_id" => "6472d898-5b98-4c3b-b4b9-d3158c1891be",
|
||||
"provider_identifier" => "foo@bar.com"
|
||||
},
|
||||
%{
|
||||
"id" => "8a70eb96-e74b-4cdc-91b8-48c05ef74d4c",
|
||||
"actor_id" => "38c92cda-1ddb-45b3-9d1a-7efc375e00c1",
|
||||
"provider_id" => "04f13eed-4845-47c3-833e-fdd869fab96f",
|
||||
"provider_identifier" => "baz@bar.com"
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
111
elixir/apps/api/lib/api/schemas/policy_schema.ex
Normal file
111
elixir/apps/api/lib/api/schemas/policy_schema.ex
Normal file
@@ -0,0 +1,111 @@
|
||||
defmodule API.Schemas.Policy do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Policy",
|
||||
description: "Policy",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Policy ID"},
|
||||
actor_group_id: %Schema{type: :string, description: "Actor Group ID"},
|
||||
resource_id: %Schema{type: :string, description: "Resource ID"},
|
||||
description: %Schema{type: :string, description: "Policy Description"}
|
||||
},
|
||||
required: [:name, :type],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"actor_group_id" => "88eae9ce-9179-48c6-8430-770e38dd4775",
|
||||
"resource_id" => "a9f60587-793c-46ae-8525-597f43ab2fb1",
|
||||
"description" => "Policy to allow something"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Request do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Policy
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Policy Request",
|
||||
description: "POST body for creating a Policy",
|
||||
type: :object,
|
||||
properties: %{
|
||||
policy: %Schema{anyOf: [Policy.Schema]}
|
||||
},
|
||||
required: [:policy],
|
||||
example: %{
|
||||
"policy" => %{
|
||||
"resource_id" => "a9f60587-793c-46ae-8525-597f43ab2fb1",
|
||||
"actor_group_id" => "88eae9ce-9179-48c6-8430-770e38dd4775",
|
||||
"description" => "Policy to allow something"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Response do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Policy
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Policy Response",
|
||||
description: "Response schema for single Policy",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: Policy.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"resource_id" => "a9f60587-793c-46ae-8525-597f43ab2fb1",
|
||||
"actor_group_id" => "88eae9ce-9179-48c6-8430-770e38dd4775",
|
||||
"description" => "Policy to allow something"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Policy
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Policy List Response",
|
||||
description: "Response schema for multiple Policies",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{description: "Policy details", type: :array, items: Policy.Schema},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"resource_id" => "a9f60587-793c-46ae-8525-597f43ab2fb1",
|
||||
"actor_group_id" => "88eae9ce-9179-48c6-8430-770e38dd4775",
|
||||
"description" => "Policy to allow something"
|
||||
},
|
||||
%{
|
||||
"id" => "6301d7d2-4938-4123-87de-282c01cca656",
|
||||
"resource_id" => "9876bd25-0f6c-48fb-a9fd-196ba9be86e5",
|
||||
"actor_group_id" => "343385a2-5437-4c66-8744-1332421ff736",
|
||||
"description" => "Policy to allow something else"
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
118
elixir/apps/api/lib/api/schemas/resource_schema.ex
Normal file
118
elixir/apps/api/lib/api/schemas/resource_schema.ex
Normal file
@@ -0,0 +1,118 @@
|
||||
defmodule API.Schemas.Resource do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
defmodule Schema do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Resource",
|
||||
description: "Resource",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string, description: "Resource ID"},
|
||||
name: %Schema{type: :string, description: "Resource name"},
|
||||
address: %Schema{type: :string, description: "Resource address"},
|
||||
description: %Schema{type: :string, description: "Resource description"},
|
||||
type: %Schema{type: :string, description: "Resource type"}
|
||||
},
|
||||
required: [:name, :type],
|
||||
example: %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "Prod DB",
|
||||
"address" => "10.0.0.10",
|
||||
"description" => "Production Database",
|
||||
"type" => "ip"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Request do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Resource
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ResourceRequest",
|
||||
description: "POST body for creating a Resource",
|
||||
type: :object,
|
||||
properties: %{
|
||||
resource: %Schema{anyOf: [Resource.Schema]}
|
||||
},
|
||||
required: [:resource],
|
||||
example: %{
|
||||
"resource" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "Prod DB",
|
||||
"address" => "10.0.0.10",
|
||||
"description" => "Production Database",
|
||||
"type" => "ip"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule Response do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Resource
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ResourceResponse",
|
||||
description: "Response schema for single Resource",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: Resource.Schema
|
||||
},
|
||||
example: %{
|
||||
"data" => %{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "Prod DB",
|
||||
"address" => "10.0.0.10",
|
||||
"description" => "Production Database",
|
||||
"type" => "ip"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
defmodule ListResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
alias API.Schemas.Resource
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Gateway List Response",
|
||||
description: "Response schema for multiple Gateways",
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %Schema{description: "Resource details", type: :array, items: Resource.Schema},
|
||||
metadata: %Schema{description: "Pagination metadata", type: :object}
|
||||
},
|
||||
example: %{
|
||||
"data" => [
|
||||
%{
|
||||
"id" => "42a7f82f-831a-4a9d-8f17-c66c2bb6e205",
|
||||
"name" => "Prod DB",
|
||||
"address" => "10.0.0.10",
|
||||
"description" => "Production Database",
|
||||
"type" => "ip"
|
||||
},
|
||||
%{
|
||||
"id" => "3b9451c9-5616-48f8-827f-009ace22d015",
|
||||
"name" => "Admin Dashboard",
|
||||
"address" => "10.0.0.20",
|
||||
"description" => "Production Admin Dashboard",
|
||||
"type" => "ip"
|
||||
}
|
||||
],
|
||||
"metadata" => %{
|
||||
"limit" => 10,
|
||||
"total" => 100,
|
||||
"prev_page" => "123123425",
|
||||
"next_page" => "98776234123"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
@@ -58,6 +58,8 @@ defmodule API.MixProject do
|
||||
# Other deps
|
||||
{:jason, "~> 1.2"},
|
||||
{:remote_ip, "~> 1.1"},
|
||||
{:open_api_spex, "~> 3.18"},
|
||||
{:ymlr, "~> 2.0"},
|
||||
|
||||
# Test deps
|
||||
{:credo, "~> 1.5", only: [:dev, :test], runtime: false},
|
||||
|
||||
236
elixir/apps/api/test/api/controllers/actor_controller_test.exs
Normal file
236
elixir/apps/api/test/api/controllers/actor_controller_test.exs
Normal file
@@ -0,0 +1,236 @@
|
||||
defmodule API.ActorControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
alias Domain.Actors.Actor
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = get(conn, "/actors")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all actors", %{conn: conn, account: account, actor: actor} do
|
||||
actors = for _ <- 1..3, do: Fixtures.Actors.create_actor(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actors")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 4
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
actor_ids = Enum.map(actors, & &1.id) ++ [actor.id]
|
||||
|
||||
assert equal_ids?(data_ids, actor_ids)
|
||||
end
|
||||
|
||||
test "lists actors with limit", %{conn: conn, account: account, actor: actor} do
|
||||
actors = for _ <- 1..3, do: Fixtures.Actors.create_actor(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actors", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 4
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
actor_ids = (Enum.map(actors, & &1.id) ++ [actor.id]) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(data_ids, actor_ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe "show/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
actor = Fixtures.Actors.create_actor(%{account: account})
|
||||
conn = get(conn, "/actors/#{actor.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns a single actor", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor = Fixtures.Actors.create_actor(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actors/#{actor.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => actor.id,
|
||||
"name" => actor.name,
|
||||
"type" => Atom.to_string(actor.type)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "create/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = post(conn, "/actors", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, actor: api_actor} do
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actors")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "returns error on invalid attrs", %{conn: conn, actor: api_actor} do
|
||||
attrs = %{}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actors", actor: attrs)
|
||||
|
||||
assert resp = json_response(conn, 422)
|
||||
|
||||
assert resp ==
|
||||
%{
|
||||
"error" => %{
|
||||
"reason" => "Unprocessable Entity",
|
||||
"validation_errors" => %{
|
||||
"name" => ["can't be blank"],
|
||||
"type" => ["can't be blank"]
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
test "creates a actor with valid attrs", %{conn: conn, actor: api_actor} do
|
||||
# TODO: At the moment, API clients aren't allowed to create admin users
|
||||
attrs = %{
|
||||
"name" => "Test User",
|
||||
"type" => "account_user"
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actors", actor: attrs)
|
||||
|
||||
assert resp = json_response(conn, 201)
|
||||
|
||||
assert resp["data"]["name"] == attrs["name"]
|
||||
assert resp["data"]["type"] == attrs["type"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "update/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
actor = Fixtures.Actors.create_actor(%{account: account})
|
||||
conn = put(conn, "/actors/#{actor.id}", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor = Fixtures.Actors.create_actor(%{account: account, type: :account_admin_user})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/actors/#{actor.id}")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "updates an actor", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor = Fixtures.Actors.create_actor(%{account: account, type: :account_admin_user})
|
||||
_other_admin = Fixtures.Actors.create_actor(%{account: account, type: :account_admin_user})
|
||||
|
||||
attrs = %{"type" => "account_user"}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/actors/#{actor.id}", actor: attrs)
|
||||
|
||||
assert resp = json_response(conn, 200)
|
||||
|
||||
assert resp["data"]["id"] == actor.id
|
||||
assert resp["data"]["name"] == actor.name
|
||||
assert resp["data"]["type"] == attrs["type"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
actor = Fixtures.Actors.create_actor(%{account: account})
|
||||
conn = delete(conn, "/actors/#{actor.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes a resource", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor = Fixtures.Actors.create_actor(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/actors/#{actor.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => actor.id,
|
||||
"name" => actor.name,
|
||||
"type" => Atom.to_string(actor.type)
|
||||
}
|
||||
}
|
||||
|
||||
assert actor = Repo.get(Actor, actor.id)
|
||||
assert actor.deleted_at
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,226 @@
|
||||
defmodule API.ActorGroupControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
alias Domain.Actors.Group
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = get(conn, "/actor_groups")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all actor groups", %{conn: conn, account: account, actor: actor} do
|
||||
actor_groups = for _ <- 1..3, do: Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actor_groups")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 3
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
actor_group_ids = Enum.map(actor_groups, & &1.id)
|
||||
|
||||
assert equal_ids?(data_ids, actor_group_ids)
|
||||
end
|
||||
|
||||
test "lists actor groups with limit", %{conn: conn, account: account, actor: actor} do
|
||||
actor_groups = for _ <- 1..3, do: Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actor_groups", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 3
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
actor_group_ids = Enum.map(actor_groups, & &1.id) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(data_ids, actor_group_ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe "show/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
conn = get(conn, "/actor_groups/#{actor_group.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns a single actor group", %{conn: conn, account: account, actor: actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actor_groups/#{actor_group.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => actor_group.id,
|
||||
"name" => actor_group.name
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "create/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = post(conn, "/actor_groups", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, actor: actor} do
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actor_groups")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "returns error on invalid attrs", %{conn: conn, actor: actor} do
|
||||
attrs = %{}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actor_groups", actor_group: attrs)
|
||||
|
||||
assert resp = json_response(conn, 422)
|
||||
|
||||
assert resp ==
|
||||
%{
|
||||
"error" => %{
|
||||
"reason" => "Unprocessable Entity",
|
||||
"validation_errors" => %{"name" => ["can't be blank"]}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
test "creates an actor group with valid attrs", %{conn: conn, actor: actor} do
|
||||
attrs = %{
|
||||
"name" => "Test Actor Group"
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actor_groups", actor_group: attrs)
|
||||
|
||||
assert resp = json_response(conn, 201)
|
||||
|
||||
assert resp["data"]["name"] == attrs["name"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "update/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
conn = put(conn, "/actor_groups/#{actor_group.id}", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, account: account, actor: actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/actor_groups/#{actor_group.id}")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "updates an actor group", %{conn: conn, account: account, actor: actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
attrs = %{"name" => "Updated Actor Group"}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/actor_groups/#{actor_group.id}", actor_group: attrs)
|
||||
|
||||
assert resp = json_response(conn, 200)
|
||||
|
||||
assert resp["data"]["id"] == actor_group.id
|
||||
assert resp["data"]["name"] == attrs["name"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
conn = delete(conn, "/actor_groups/#{actor_group.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes an actor group", %{conn: conn, account: account, actor: actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/actor_groups/#{actor_group.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => actor_group.id,
|
||||
"name" => actor_group.name
|
||||
}
|
||||
}
|
||||
|
||||
assert actor_group = Repo.get(Group, actor_group.id)
|
||||
assert actor_group.deleted_at
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,253 @@
|
||||
defmodule API.ActorGroupMembershipControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
conn = get(conn, "/actor_groups/#{actor_group.id}/memberships")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all memberships", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
memberships =
|
||||
for _ <- 1..3,
|
||||
do: Fixtures.Actors.create_membership(%{account: account, group: actor_group})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actor_groups/#{actor_group.id}/memberships")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 3
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
membership_ids = Enum.map(memberships, & &1.actor_id)
|
||||
|
||||
assert equal_ids?(membership_ids, data_ids)
|
||||
end
|
||||
|
||||
test "lists identity providers with limit", %{conn: conn, account: account, actor: actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
memberships =
|
||||
for _ <- 1..3,
|
||||
do: Fixtures.Actors.create_membership(%{account: account, group: actor_group})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actor_groups/#{actor_group.id}/memberships", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 3
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
assert MapSet.size(data_ids) == 2
|
||||
|
||||
membership_ids =
|
||||
Enum.map(memberships, & &1.actor_id) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(data_ids, membership_ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe "update_patch/2" do
|
||||
test "adds actor to group", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
actor = Fixtures.Actors.create_actor(%{account: account})
|
||||
attrs = %{"add" => [actor.id]}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> patch("/actor_groups/#{actor_group.id}/memberships", memberships: attrs)
|
||||
|
||||
assert %{"data" => data} = json_response(conn, 200)
|
||||
assert data == %{"actor_ids" => [actor.id]}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> patch("/actor_groups/#{actor_group.id}/memberships")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "removes actor from group", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
actor1 = Fixtures.Actors.create_actor(%{account: account})
|
||||
actor2 = Fixtures.Actors.create_actor(%{account: account})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor1, group: actor_group})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor2, group: actor_group})
|
||||
|
||||
attrs = %{"remove" => [actor2.id]}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> patch("/actor_groups/#{actor_group.id}/memberships", memberships: attrs)
|
||||
|
||||
assert %{"data" => data} = json_response(conn, 200)
|
||||
assert data == %{"actor_ids" => [actor1.id]}
|
||||
end
|
||||
|
||||
test "adds and removes actors from group", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
actor1 = Fixtures.Actors.create_actor(%{account: account})
|
||||
actor2 = Fixtures.Actors.create_actor(%{account: account})
|
||||
actor3 = Fixtures.Actors.create_actor(%{account: account})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor1, group: actor_group})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor2, group: actor_group})
|
||||
|
||||
attrs = %{"add" => [actor3.id], "remove" => [actor2.id]}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> patch("/actor_groups/#{actor_group.id}/memberships", memberships: attrs)
|
||||
|
||||
assert %{"data" => data} = json_response(conn, 200)
|
||||
assert Enum.sort(data["actor_ids"]) == Enum.sort([actor1.id, actor3.id])
|
||||
end
|
||||
|
||||
test "group remains the same on empty params", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: api_actor
|
||||
} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
actor1 = Fixtures.Actors.create_actor(%{account: account})
|
||||
actor2 = Fixtures.Actors.create_actor(%{account: account})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor1, group: actor_group})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor2, group: actor_group})
|
||||
|
||||
attrs = %{}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> patch("/actor_groups/#{actor_group.id}/memberships", memberships: attrs)
|
||||
|
||||
assert %{"data" => data} = json_response(conn, 200)
|
||||
assert Enum.sort(data["actor_ids"]) == Enum.sort([actor1.id, actor2.id])
|
||||
end
|
||||
end
|
||||
|
||||
describe "update_put/2" do
|
||||
test "adds actor to group", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
actor = Fixtures.Actors.create_actor(%{account: account})
|
||||
attrs = [%{"actor_id" => actor.id}]
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/actor_groups/#{actor_group.id}/memberships", memberships: attrs)
|
||||
|
||||
assert %{"data" => data} = json_response(conn, 200)
|
||||
assert data == %{"actor_ids" => [actor.id]}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/actor_groups/#{actor_group.id}/memberships")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "removes actor from group", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
actor1 = Fixtures.Actors.create_actor(%{account: account})
|
||||
actor2 = Fixtures.Actors.create_actor(%{account: account})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor1, group: actor_group})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor2, group: actor_group})
|
||||
|
||||
attrs = [%{"actor_id" => actor1.id}]
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/actor_groups/#{actor_group.id}/memberships", memberships: attrs)
|
||||
|
||||
assert %{"data" => data} = json_response(conn, 200)
|
||||
assert data == %{"actor_ids" => [actor1.id]}
|
||||
end
|
||||
|
||||
test "adds and removes actors from group", %{conn: conn, account: account, actor: api_actor} do
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
actor1 = Fixtures.Actors.create_actor(%{account: account})
|
||||
actor2 = Fixtures.Actors.create_actor(%{account: account})
|
||||
actor3 = Fixtures.Actors.create_actor(%{account: account})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor1, group: actor_group})
|
||||
Fixtures.Actors.create_membership(%{account: account, actor: actor2, group: actor_group})
|
||||
|
||||
attrs = [%{"actor_id" => actor1.id}, %{"actor_id" => actor3.id}]
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/actor_groups/#{actor_group.id}/memberships", memberships: attrs)
|
||||
|
||||
assert %{"data" => data} = json_response(conn, 200)
|
||||
assert Enum.sort(data["actor_ids"]) == Enum.sort([actor1.id, actor3.id])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,22 +0,0 @@
|
||||
defmodule API.ExampleControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
|
||||
describe "echo/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = post(conn, "/v1/echo", %{"message" => "Hello, world!"})
|
||||
assert json_response(conn, 401) == %{"error" => "invalid_access_token"}
|
||||
end
|
||||
|
||||
test "returns 200 OK with the request body", %{conn: conn} do
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/v1/echo", Jason.encode!(%{"message" => "Hello, world!"}))
|
||||
|
||||
assert json_response(conn, 200) == %{"message" => "Hello, world!"}
|
||||
end
|
||||
end
|
||||
end
|
||||
174
elixir/apps/api/test/api/controllers/gateway_controller_test.exs
Normal file
174
elixir/apps/api/test/api/controllers/gateway_controller_test.exs
Normal file
@@ -0,0 +1,174 @@
|
||||
defmodule API.GatewayControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
alias Domain.Gateways.Gateway
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor,
|
||||
gateway_group: gateway_group
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn, gateway_group: gateway_group} do
|
||||
conn = get(conn, "/gateway_groups/#{gateway_group.id}/gateways")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all gateways for a gateway group", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: actor,
|
||||
gateway_group: gateway_group
|
||||
} do
|
||||
gateways =
|
||||
for _ <- 1..3,
|
||||
do: Fixtures.Gateways.create_gateway(%{account: account, group: gateway_group})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/gateway_groups/#{gateway_group.id}/gateways")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 3
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
gateway_ids = Enum.map(gateways, & &1.id)
|
||||
|
||||
assert equal_ids?(data_ids, gateway_ids)
|
||||
end
|
||||
|
||||
test "lists gateways with limit", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: actor,
|
||||
gateway_group: gateway_group
|
||||
} do
|
||||
gateways =
|
||||
for _ <- 1..3,
|
||||
do: Fixtures.Gateways.create_gateway(%{account: account, group: gateway_group})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/gateway_groups/#{gateway_group.id}/gateways", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 3
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
gateway_ids = Enum.map(gateways, & &1.id) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(data_ids, gateway_ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe "show/2" do
|
||||
test "returns error when not authorized", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
gateway_group: gateway_group
|
||||
} do
|
||||
gateway = Fixtures.Gateways.create_gateway(%{account: account, group: gateway_group})
|
||||
conn = get(conn, "/gateway_groups/#{gateway_group.id}/gateways/#{gateway.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns a single gateway", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: actor,
|
||||
gateway_group: gateway_group
|
||||
} do
|
||||
gateway = Fixtures.Gateways.create_gateway(%{account: account, group: gateway_group})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/gateway_groups/#{gateway_group.id}/gateways/#{gateway.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => gateway.id,
|
||||
"name" => gateway.name,
|
||||
"ipv4" => Domain.Types.IP.to_string(gateway.ipv4),
|
||||
"ipv6" => Domain.Types.IP.to_string(gateway.ipv6),
|
||||
"online" => false
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/2" do
|
||||
test "returns error when not authorized", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
gateway_group: gateway_group
|
||||
} do
|
||||
gateway = Fixtures.Gateways.create_gateway(%{account: account, group: gateway_group})
|
||||
conn = delete(conn, "/gateway_groups/#{gateway_group.id}/gateways/#{gateway.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes a gateway", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: actor,
|
||||
gateway_group: gateway_group
|
||||
} do
|
||||
gateway = Fixtures.Gateways.create_gateway(%{account: account, group: gateway_group})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/gateway_groups/#{gateway_group.id}/gateways/#{gateway.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => gateway.id,
|
||||
"name" => gateway.name,
|
||||
"ipv4" => Domain.Types.IP.to_string(gateway.ipv4),
|
||||
"ipv6" => Domain.Types.IP.to_string(gateway.ipv6),
|
||||
"online" => false
|
||||
}
|
||||
}
|
||||
|
||||
assert gateway = Repo.get(Gateway, gateway.id)
|
||||
assert gateway.deleted_at
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,308 @@
|
||||
defmodule API.GatewayGroupControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
alias Domain.Gateways.Group
|
||||
alias Domain.Tokens.Token
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = get(conn, "/gateway_groups")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all gateway groups", %{conn: conn, account: account, actor: actor} do
|
||||
gateway_groups = for _ <- 1..3, do: Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/gateway_groups")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 3
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
gateway_group_ids = Enum.map(gateway_groups, & &1.id)
|
||||
|
||||
assert equal_ids?(data_ids, gateway_group_ids)
|
||||
end
|
||||
|
||||
test "lists gateway groups with limit", %{conn: conn, account: account, actor: actor} do
|
||||
gateway_groups = for _ <- 1..3, do: Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/gateway_groups", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 3
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
gateway_group_ids = Enum.map(gateway_groups, & &1.id) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(data_ids, gateway_group_ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe "show/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
conn = get(conn, "/gateway_groups/#{gateway_group.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns a single gateway_group", %{conn: conn, account: account, actor: actor} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/gateway_groups/#{gateway_group.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => gateway_group.id,
|
||||
"name" => gateway_group.name
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "create/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = post(conn, "/gateway_groups", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, actor: actor} do
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/gateway_groups")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "returns error on invalid attrs", %{conn: conn, actor: actor} do
|
||||
attrs = %{"name" => String.duplicate("a", 65)}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/gateway_groups", gateway_group: attrs)
|
||||
|
||||
assert resp = json_response(conn, 422)
|
||||
|
||||
assert resp ==
|
||||
%{
|
||||
"error" => %{
|
||||
"reason" => "Unprocessable Entity",
|
||||
"validation_errors" => %{"name" => ["should be at most 64 character(s)"]}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
test "creates a gateway group with valid attrs", %{conn: conn, actor: actor} do
|
||||
attrs = %{
|
||||
"name" => "Example Site"
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/gateway_groups", gateway_group: attrs)
|
||||
|
||||
assert resp = json_response(conn, 201)
|
||||
|
||||
assert resp["data"]["name"] == attrs["name"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "update/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
conn = put(conn, "/gateway_groups/#{gateway_group.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, account: account, actor: actor} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/gateway_groups/#{gateway_group.id}")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "updates a gateway group", %{conn: conn, account: account, actor: actor} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
attrs = %{"name" => "Updated Site Name"}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/gateway_groups/#{gateway_group.id}", gateway_group: attrs)
|
||||
|
||||
assert resp = json_response(conn, 200)
|
||||
|
||||
assert resp["data"]["name"] == attrs["name"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
conn = delete(conn, "/gateway_groups/#{gateway_group.id}", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes a gateway group", %{conn: conn, account: account, actor: actor} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/gateway_groups/#{gateway_group.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => gateway_group.id,
|
||||
"name" => gateway_group.name
|
||||
}
|
||||
}
|
||||
|
||||
assert gateway_group = Repo.get(Group, gateway_group.id)
|
||||
assert gateway_group.deleted_at
|
||||
end
|
||||
end
|
||||
|
||||
describe "gateway group token create/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
conn = post(conn, "/gateway_groups/#{gateway_group.id}/tokens")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "creates a gateway token", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: actor
|
||||
} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/gateway_groups/#{gateway_group.id}/tokens")
|
||||
|
||||
assert %{"data" => %{"id" => _id, "token" => _token}} = json_response(conn, 201)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete single gateway token" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
token = Fixtures.Gateways.create_token(%{account: account, group: gateway_group})
|
||||
conn = delete(conn, "/gateway_groups/#{gateway_group.id}/tokens/#{token.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes gateway token", %{conn: conn, account: account, actor: actor} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
token = Fixtures.Gateways.create_token(%{account: account, group: gateway_group})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> delete("/gateway_groups/#{gateway_group.id}/tokens/#{token.id}")
|
||||
|
||||
assert %{"data" => %{"id" => _id}} = json_response(conn, 200)
|
||||
|
||||
assert token = Repo.get(Token, token.id)
|
||||
assert token.deleted_at
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete all gateway tokens" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
conn = delete(conn, "/gateway_groups/#{gateway_group.id}/tokens")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes all gateway tokens", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: actor
|
||||
} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
tokens =
|
||||
for _ <- 1..3,
|
||||
do: Fixtures.Gateways.create_token(%{account: account, group: gateway_group})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/gateway_groups/#{gateway_group.id}/tokens")
|
||||
|
||||
assert %{"data" => [%{"id" => _id1}, %{"id" => _id2}, %{"id" => _id3}]} =
|
||||
json_response(conn, 200)
|
||||
|
||||
Enum.map(tokens, fn token ->
|
||||
assert token = Repo.get(Token, token.id)
|
||||
assert token.deleted_at
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,246 @@
|
||||
defmodule API.IdentityControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
alias Domain.Auth.Identity
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn, actor: actor} do
|
||||
conn = get(conn, "/actors/#{actor.id}/identities")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all identities for actor", %{conn: conn, account: account, actor: actor} do
|
||||
identities =
|
||||
for _ <- 1..3, do: Fixtures.Auth.create_identity(%{account: account, actor: actor})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actors/#{actor.id}/identities")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 3
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
identity_ids = Enum.map(identities, & &1.id)
|
||||
|
||||
assert equal_ids?(data_ids, identity_ids)
|
||||
end
|
||||
|
||||
test "lists resources with limit", %{conn: conn, account: account, actor: actor} do
|
||||
identities =
|
||||
for _ <- 1..3, do: Fixtures.Auth.create_identity(%{account: account, actor: actor})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actors/#{actor.id}/identities", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 3
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
identity_ids = Enum.map(identities, & &1.id) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(data_ids, identity_ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe "show/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account, actor: actor} do
|
||||
identity = Fixtures.Auth.create_identity(%{account: account, actor: actor})
|
||||
conn = get(conn, "/actors/#{actor.id}/identities/#{identity.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns a single resource", %{conn: conn, account: account, actor: actor} do
|
||||
identity = Fixtures.Auth.create_identity(%{account: account, actor: actor})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/actors/#{actor.id}/identities/#{identity.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => identity.id,
|
||||
"actor_id" => actor.id,
|
||||
"provider_id" => identity.provider_id,
|
||||
"provider_identifier" => identity.provider_identifier
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "create/2" do
|
||||
test "returns error when not authorized", %{conn: conn, actor: actor} do
|
||||
conn = post(conn, "/actors/#{actor.id}/identities", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: api_actor
|
||||
} do
|
||||
actor = Fixtures.Actors.create_actor(account: account)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actors/#{actor.id}/identities")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "returns error on invalid identity provider", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: api_actor
|
||||
} do
|
||||
actor = Fixtures.Actors.create_actor(account: account)
|
||||
|
||||
attrs = %{}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actors/#{actor.id}/identities",
|
||||
provider_id: "1234",
|
||||
identity: attrs
|
||||
)
|
||||
|
||||
assert resp = json_response(conn, 404)
|
||||
assert resp == %{"error" => %{"reason" => "Not Found"}}
|
||||
end
|
||||
|
||||
test "returns error on invalid identity attrs", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: api_actor
|
||||
} do
|
||||
{oidc_provider, _bypass} =
|
||||
Fixtures.Auth.start_and_create_openid_connect_provider(account: account)
|
||||
|
||||
actor = Fixtures.Actors.create_actor(account: account)
|
||||
|
||||
attrs = %{}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actors/#{actor.id}/identities",
|
||||
provider_id: oidc_provider.id,
|
||||
identity: attrs
|
||||
)
|
||||
|
||||
assert resp = json_response(conn, 422)
|
||||
|
||||
assert resp ==
|
||||
%{
|
||||
"error" => %{
|
||||
"reason" => "Unprocessable Entity",
|
||||
"validation_errors" => %{"provider_identifier" => ["can't be blank"]}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
test "creates a resource with valid attrs", %{
|
||||
conn: conn,
|
||||
account: account,
|
||||
actor: api_actor
|
||||
} do
|
||||
{oidc_provider, _bypass} =
|
||||
Fixtures.Auth.start_and_create_openid_connect_provider(account: account)
|
||||
|
||||
actor = Fixtures.Actors.create_actor(account: account)
|
||||
|
||||
attrs = %{"provider_identifier" => "foo@local"}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(api_actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/actors/#{actor.id}/identities",
|
||||
provider_id: oidc_provider.id,
|
||||
identity: attrs
|
||||
)
|
||||
|
||||
assert resp = json_response(conn, 201)
|
||||
|
||||
assert resp["data"]["provider_identifier"] == attrs["provider_identifier"]
|
||||
assert resp["data"]["provider_id"] == oidc_provider.id
|
||||
assert resp["data"]["actor_id"] == actor.id
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account, actor: actor} do
|
||||
identity = Fixtures.Auth.create_identity(%{account: account, actor: actor})
|
||||
conn = delete(conn, "/actors/#{actor.id}/identities/#{identity.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes a resource", %{conn: conn, account: account, actor: actor} do
|
||||
identity = Fixtures.Auth.create_identity(%{account: account, actor: actor})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/actors/#{actor.id}/identities/#{identity.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => identity.id,
|
||||
"actor_id" => actor.id,
|
||||
"provider_id" => identity.provider_id,
|
||||
"provider_identifier" => identity.provider_identifier
|
||||
}
|
||||
}
|
||||
|
||||
assert identity = Repo.get(Identity, identity.id)
|
||||
assert identity.deleted_at
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,146 @@
|
||||
defmodule API.IdentityProviderControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
alias Domain.Auth.Provider
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = get(conn, "/identity_providers")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all identity_providers", %{conn: conn, account: account, actor: actor} do
|
||||
{oidc_provider, _bypass} =
|
||||
Fixtures.Auth.start_and_create_openid_connect_provider(account: account)
|
||||
|
||||
{google_provider, _bypass} =
|
||||
Fixtures.Auth.start_and_create_google_workspace_provider(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/identity_providers")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 3
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
|
||||
provider_ids =
|
||||
Enum.map([oidc_provider, google_provider], & &1.id) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(provider_ids, data_ids)
|
||||
end
|
||||
|
||||
test "lists identity providers with limit", %{conn: conn, account: account, actor: actor} do
|
||||
Fixtures.Auth.start_and_create_openid_connect_provider(%{account: account})
|
||||
Fixtures.Auth.start_and_create_google_workspace_provider(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/identity_providers", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 3
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
|
||||
assert length(data_ids) == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "show/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
{identity_provider, _bypass} =
|
||||
Fixtures.Auth.start_and_create_openid_connect_provider(%{account: account})
|
||||
|
||||
conn = get(conn, "/identity_providers/#{identity_provider.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns a single resource", %{conn: conn, account: account, actor: actor} do
|
||||
{identity_provider, _bypass} =
|
||||
Fixtures.Auth.start_and_create_openid_connect_provider(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/identity_providers/#{identity_provider.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => identity_provider.id,
|
||||
"name" => identity_provider.name
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
{identity_provider, _bypass} =
|
||||
Fixtures.Auth.start_and_create_openid_connect_provider(%{account: account})
|
||||
|
||||
conn = delete(conn, "/identity_providers/#{identity_provider.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes an identity provider", %{conn: conn, account: account, actor: actor} do
|
||||
{identity_provider, _bypass} =
|
||||
Fixtures.Auth.start_and_create_openid_connect_provider(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/identity_providers/#{identity_provider.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => identity_provider.id,
|
||||
"name" => identity_provider.name
|
||||
}
|
||||
}
|
||||
|
||||
assert identity_provider = Repo.get(Provider, identity_provider.id)
|
||||
assert identity_provider.deleted_at
|
||||
end
|
||||
end
|
||||
end
|
||||
238
elixir/apps/api/test/api/controllers/policy_controller_test.exs
Normal file
238
elixir/apps/api/test/api/controllers/policy_controller_test.exs
Normal file
@@ -0,0 +1,238 @@
|
||||
defmodule API.PolicyControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
alias Domain.Policies.Policy
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = get(conn, "/policies")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all policies", %{conn: conn, account: account, actor: actor} do
|
||||
policies = for _ <- 1..3, do: Fixtures.Policies.create_policy(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/policies", Jason.encode!(%{}))
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 3
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
policy_ids = Enum.map(policies, & &1.id)
|
||||
|
||||
assert equal_ids?(data_ids, policy_ids)
|
||||
end
|
||||
|
||||
test "lists policies with limit", %{conn: conn, account: account, actor: actor} do
|
||||
policies = for _ <- 1..3, do: Fixtures.Policies.create_policy(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/policies", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 3
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
policy_ids = Enum.map(policies, & &1.id) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(data_ids, policy_ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe "show/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
policy = Fixtures.Policies.create_policy(%{account: account})
|
||||
conn = get(conn, "/policies/#{policy.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns a single policy", %{conn: conn, account: account, actor: actor} do
|
||||
policy = Fixtures.Policies.create_policy(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/policies/#{policy.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => policy.id,
|
||||
"actor_group_id" => policy.actor_group_id,
|
||||
"resource_id" => policy.resource_id,
|
||||
"description" => policy.description
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "create/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = post(conn, "/policies", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, actor: actor} do
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/policies")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "returns error on invalid attrs", %{conn: conn, actor: actor} do
|
||||
attrs = %{}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/policies", policy: attrs)
|
||||
|
||||
assert resp = json_response(conn, 422)
|
||||
|
||||
assert resp ==
|
||||
%{
|
||||
"error" => %{
|
||||
"reason" => "Unprocessable Entity",
|
||||
"validation_errors" => %{
|
||||
"actor_group_id" => ["can't be blank"],
|
||||
"resource_id" => ["can't be blank"]
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
test "creates a policy with valid attrs", %{conn: conn, account: account, actor: actor} do
|
||||
resource = Fixtures.Resources.create_resource(%{account: account})
|
||||
actor_group = Fixtures.Actors.create_group(%{account: account})
|
||||
|
||||
attrs = %{
|
||||
"actor_group_id" => actor_group.id,
|
||||
"resource_id" => resource.id,
|
||||
"description" => "test policy"
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/policies", policy: attrs)
|
||||
|
||||
assert resp = json_response(conn, 201)
|
||||
|
||||
assert resp["data"]["actor_group_id"] == attrs["actor_group_id"]
|
||||
assert resp["data"]["resource_id"] == attrs["resource_id"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "update/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
policy = Fixtures.Policies.create_policy(%{account: account})
|
||||
conn = put(conn, "/policies/#{policy.id}", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, account: account, actor: actor} do
|
||||
policy = Fixtures.Policies.create_policy(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/policies/#{policy.id}")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "updates a policy", %{conn: conn, account: account, actor: actor} do
|
||||
policy = Fixtures.Policies.create_policy(%{account: account})
|
||||
|
||||
attrs = %{"description" => "updated policy description"}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/policies/#{policy.id}", policy: attrs)
|
||||
|
||||
assert resp = json_response(conn, 200)
|
||||
|
||||
assert resp["data"]["description"] == attrs["description"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
policy = Fixtures.Policies.create_policy(%{account: account})
|
||||
conn = delete(conn, "/policies/#{policy.id}", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes a policy", %{conn: conn, account: account, actor: actor} do
|
||||
policy = Fixtures.Policies.create_policy(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/policies/#{policy.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"id" => policy.id,
|
||||
"actor_group_id" => policy.actor_group_id,
|
||||
"resource_id" => policy.resource_id,
|
||||
"description" => policy.description
|
||||
}
|
||||
}
|
||||
|
||||
assert policy = Repo.get(Policy, policy.id)
|
||||
assert policy.deleted_at
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,248 @@
|
||||
defmodule API.ResourceControllerTest do
|
||||
use API.ConnCase, async: true
|
||||
alias Domain.Resources.Resource
|
||||
|
||||
setup do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
|
||||
|
||||
%{
|
||||
account: account,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
||||
describe "index/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = get(conn, "/resources")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "lists all resources", %{conn: conn, account: account, actor: actor} do
|
||||
resources = for _ <- 1..3, do: Fixtures.Resources.create_resource(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/resources")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert count == 3
|
||||
assert limit == 50
|
||||
assert is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"])
|
||||
resource_ids = Enum.map(resources, & &1.id)
|
||||
|
||||
assert equal_ids?(data_ids, resource_ids)
|
||||
end
|
||||
|
||||
test "lists resources with limit", %{conn: conn, account: account, actor: actor} do
|
||||
resources = for _ <- 1..3, do: Fixtures.Resources.create_resource(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/resources", limit: "2")
|
||||
|
||||
assert %{
|
||||
"data" => data,
|
||||
"metadata" => %{
|
||||
"count" => count,
|
||||
"limit" => limit,
|
||||
"next_page" => next_page,
|
||||
"prev_page" => prev_page
|
||||
}
|
||||
} = json_response(conn, 200)
|
||||
|
||||
assert limit == 2
|
||||
assert count == 3
|
||||
refute is_nil(next_page)
|
||||
assert is_nil(prev_page)
|
||||
|
||||
data_ids = Enum.map(data, & &1["id"]) |> MapSet.new()
|
||||
resource_ids = Enum.map(resources, & &1.id) |> MapSet.new()
|
||||
|
||||
assert MapSet.subset?(data_ids, resource_ids)
|
||||
end
|
||||
end
|
||||
|
||||
describe "show/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
resource = Fixtures.Resources.create_resource(%{account: account})
|
||||
conn = get(conn, "/resources/#{resource.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns a single resource", %{conn: conn, account: account, actor: actor} do
|
||||
resource = Fixtures.Resources.create_resource(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> get("/resources/#{resource.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"address" => resource.address,
|
||||
"description" => resource.address_description,
|
||||
"id" => resource.id,
|
||||
"name" => resource.name,
|
||||
"type" => Atom.to_string(resource.type)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "create/2" do
|
||||
test "returns error when not authorized", %{conn: conn} do
|
||||
conn = post(conn, "/resources", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, actor: actor} do
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/resources")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "returns error on invalid attrs", %{conn: conn, actor: actor} do
|
||||
attrs = %{}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/resources", resource: attrs)
|
||||
|
||||
assert resp = json_response(conn, 422)
|
||||
|
||||
assert resp ==
|
||||
%{
|
||||
"error" => %{
|
||||
"reason" => "Unprocessable Entity",
|
||||
"validation_errors" => %{
|
||||
"address" => ["can't be blank"],
|
||||
"connections" => ["can't be blank"],
|
||||
"name" => ["can't be blank"],
|
||||
"type" => ["can't be blank"]
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
test "creates a resource with valid attrs", %{conn: conn, account: account, actor: actor} do
|
||||
gateway_group = Fixtures.Gateways.create_group(%{account: account})
|
||||
|
||||
attrs = %{
|
||||
"address" => "google.com",
|
||||
"name" => "Google",
|
||||
"type" => "dns",
|
||||
"connections" => [
|
||||
%{"gateway_group_id" => gateway_group.id}
|
||||
]
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/resources", resource: attrs)
|
||||
|
||||
assert resp = json_response(conn, 201)
|
||||
|
||||
assert resp["data"]["address"] == attrs["address"]
|
||||
assert resp["data"]["description"] == nil
|
||||
assert resp["data"]["name"] == attrs["name"]
|
||||
assert resp["data"]["type"] == attrs["type"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "update/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
resource = Fixtures.Resources.create_resource(%{account: account})
|
||||
conn = put(conn, "/resources/#{resource.id}", %{})
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "returns error on empty params/body", %{conn: conn, account: account, actor: actor} do
|
||||
resource = Fixtures.Resources.create_resource(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/resources/#{resource.id}")
|
||||
|
||||
assert resp = json_response(conn, 400)
|
||||
assert resp == %{"error" => %{"reason" => "Bad Request"}}
|
||||
end
|
||||
|
||||
test "updates a resource", %{conn: conn, account: account, actor: actor} do
|
||||
resource = Fixtures.Resources.create_resource(%{account: account})
|
||||
|
||||
attrs = %{"name" => "Google"}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/resources/#{resource.id}", resource: attrs)
|
||||
|
||||
assert resp = json_response(conn, 200)
|
||||
|
||||
assert resp["data"]["address"] == resource.address
|
||||
assert resp["data"]["description"] == resource.address_description
|
||||
assert resp["data"]["name"] == attrs["name"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/2" do
|
||||
test "returns error when not authorized", %{conn: conn, account: account} do
|
||||
resource = Fixtures.Resources.create_resource(%{account: account})
|
||||
conn = delete(conn, "/resources/#{resource.id}")
|
||||
assert json_response(conn, 401) == %{"error" => %{"reason" => "Unauthorized"}}
|
||||
end
|
||||
|
||||
test "deletes a resource", %{conn: conn, account: account, actor: actor} do
|
||||
resource = Fixtures.Resources.create_resource(%{account: account})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> authorize_conn(actor)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/resources/#{resource.id}")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"data" => %{
|
||||
"address" => resource.address,
|
||||
"description" => resource.address_description,
|
||||
"id" => resource.id,
|
||||
"name" => resource.name,
|
||||
"type" => Atom.to_string(resource.type)
|
||||
}
|
||||
}
|
||||
|
||||
assert resource = Repo.get(Resource, resource.id)
|
||||
assert resource.deleted_at
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -56,4 +56,8 @@ defmodule API.ConnCase do
|
||||
|
||||
Plug.Conn.put_req_header(conn, "authorization", "Bearer " <> encoded_fragment)
|
||||
end
|
||||
|
||||
def equal_ids?(list1, list2) do
|
||||
MapSet.equal?(MapSet.new(list1), MapSet.new(list2))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,6 +11,12 @@ defmodule Domain.Accounts.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_own_account_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(_) do
|
||||
[]
|
||||
end
|
||||
|
||||
@@ -13,6 +13,13 @@ defmodule Domain.Actors.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_actors_permission(),
|
||||
edit_own_profile_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:account_user) do
|
||||
[
|
||||
edit_own_profile_permission()
|
||||
|
||||
@@ -20,8 +20,9 @@ defmodule Domain.Actors.Group do
|
||||
# ref https://github.com/firezone/firezone/issues/2162
|
||||
has_many :actors, through: [:memberships, :actor]
|
||||
|
||||
field :created_by, Ecto.Enum, values: ~w[system identity provider]a
|
||||
field :created_by, Ecto.Enum, values: ~w[actor identity provider system]a
|
||||
belongs_to :created_by_identity, Domain.Auth.Identity
|
||||
belongs_to :created_by_actor, Domain.Actors.Actor
|
||||
|
||||
belongs_to :account, Domain.Accounts.Account
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ defmodule Domain.Actors.Group.Changeset do
|
||||
|> changeset()
|
||||
|> put_change(:account_id, account.id)
|
||||
|> cast_membership_assocs(account.id)
|
||||
|> put_change(:created_by, :identity)
|
||||
|> put_change(:created_by_identity_id, subject.identity.id)
|
||||
|> put_created_by(subject)
|
||||
end
|
||||
|
||||
def create(%Accounts.Account{} = account, attrs) do
|
||||
|
||||
@@ -54,6 +54,15 @@ defmodule Domain.Auth.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_providers_permission(),
|
||||
manage_service_accounts_permission(),
|
||||
manage_own_identities_permission(),
|
||||
manage_identities_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(_role) do
|
||||
[]
|
||||
end
|
||||
|
||||
@@ -14,6 +14,13 @@ defmodule Domain.Clients.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_own_clients_permission(),
|
||||
manage_clients_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:account_user) do
|
||||
[
|
||||
manage_own_clients_permission()
|
||||
|
||||
@@ -13,6 +13,13 @@ defmodule Domain.Flows.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_flows_permission(),
|
||||
create_flows_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:account_user) do
|
||||
[
|
||||
create_flows_permission()
|
||||
|
||||
@@ -14,6 +14,13 @@ defmodule Domain.Gateways.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_gateways_permission(),
|
||||
connect_gateways_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(_) do
|
||||
[
|
||||
connect_gateways_permission()
|
||||
|
||||
@@ -13,8 +13,9 @@ defmodule Domain.Gateways.Group do
|
||||
|
||||
has_many :connections, Domain.Resources.Connection, foreign_key: :gateway_group_id
|
||||
|
||||
field :created_by, Ecto.Enum, values: ~w[identity]a
|
||||
field :created_by, Ecto.Enum, values: ~w[actor identity]a
|
||||
belongs_to :created_by_identity, Domain.Auth.Identity
|
||||
belongs_to :created_by_actor, Domain.Actors.Actor
|
||||
|
||||
field :deleted_at, :utc_datetime_usec
|
||||
timestamps()
|
||||
|
||||
@@ -9,8 +9,7 @@ defmodule Domain.Gateways.Group.Changeset do
|
||||
%Gateways.Group{account: account}
|
||||
|> changeset(attrs)
|
||||
|> put_change(:account_id, account.id)
|
||||
|> put_change(:created_by, :identity)
|
||||
|> put_change(:created_by_identity_id, subject.identity.id)
|
||||
|> put_created_by(subject)
|
||||
end
|
||||
|
||||
def update(%Gateways.Group{} = group, attrs, %Auth.Subject{}) do
|
||||
|
||||
@@ -13,6 +13,13 @@ defmodule Domain.Policies.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_policies_permission(),
|
||||
view_available_policies_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:account_user) do
|
||||
[
|
||||
# TODO: view_assigned_policies_permission()
|
||||
|
||||
@@ -10,8 +10,9 @@ defmodule Domain.Policies.Policy do
|
||||
belongs_to :resource, Domain.Resources.Resource
|
||||
belongs_to :account, Domain.Accounts.Account
|
||||
|
||||
field :created_by, Ecto.Enum, values: ~w[identity]a
|
||||
field :created_by, Ecto.Enum, values: ~w[actor identity]a
|
||||
belongs_to :created_by_identity, Domain.Auth.Identity
|
||||
belongs_to :created_by_actor, Domain.Actors.Actor
|
||||
|
||||
field :disabled_at, :utc_datetime_usec
|
||||
field :deleted_at, :utc_datetime_usec
|
||||
|
||||
@@ -14,8 +14,7 @@ defmodule Domain.Policies.Policy.Changeset do
|
||||
|> cast_embed(:conditions, with: &Domain.Policies.Condition.Changeset.changeset/3)
|
||||
|> changeset()
|
||||
|> put_change(:account_id, subject.account.id)
|
||||
|> put_change(:created_by, :identity)
|
||||
|> put_change(:created_by_identity_id, subject.identity.id)
|
||||
|> put_created_by(subject)
|
||||
end
|
||||
|
||||
def update(%Policy{} = policy, attrs) do
|
||||
|
||||
@@ -139,6 +139,19 @@ defmodule Domain.Repo.Changeset do
|
||||
end
|
||||
end
|
||||
|
||||
def put_created_by(changset, %Domain.Auth.Subject{identity: nil} = subject) do
|
||||
changset
|
||||
|> put_change(:created_by_actor_id, subject.actor.id)
|
||||
|> put_change(:created_by, :actor)
|
||||
end
|
||||
|
||||
def put_created_by(changeset, %Domain.Auth.Subject{} = subject) do
|
||||
changeset
|
||||
|> put_change(:created_by, :identity)
|
||||
|> put_change(:created_by_identity_id, subject.identity.id)
|
||||
|> put_change(:created_by_actor_id, subject.actor.id)
|
||||
end
|
||||
|
||||
# Validations
|
||||
|
||||
def validate_list(
|
||||
|
||||
@@ -19,6 +19,13 @@ defmodule Domain.Resources.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_resources_permission(),
|
||||
view_available_resources_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:service_account) do
|
||||
[
|
||||
view_available_resources_permission()
|
||||
|
||||
@@ -6,8 +6,9 @@ defmodule Domain.Resources.Connection do
|
||||
belongs_to :resource, Domain.Resources.Resource, primary_key: true
|
||||
belongs_to :gateway_group, Domain.Gateways.Group, primary_key: true
|
||||
|
||||
field :created_by, Ecto.Enum, values: ~w[identity]a
|
||||
field :created_by, Ecto.Enum, values: ~w[actor identity]a
|
||||
belongs_to :created_by_identity, Domain.Auth.Identity
|
||||
belongs_to :created_by_actor, Domain.Actors.Actor
|
||||
|
||||
belongs_to :account, Domain.Accounts.Account
|
||||
end
|
||||
|
||||
@@ -7,8 +7,7 @@ defmodule Domain.Resources.Connection.Changeset do
|
||||
|
||||
def changeset(account_id, connection, attrs, %Auth.Subject{} = subject) do
|
||||
changeset(account_id, connection, attrs)
|
||||
|> put_change(:created_by, :identity)
|
||||
|> put_change(:created_by_identity_id, subject.identity.id)
|
||||
|> put_created_by(subject)
|
||||
end
|
||||
|
||||
def changeset(account_id, connection, attrs) do
|
||||
|
||||
@@ -26,7 +26,8 @@ defmodule Domain.Resources.Resource do
|
||||
# because the actual preload query should also use joins and process policy conditions
|
||||
has_many :authorized_by_policies, Domain.Policies.Policy, where: [id: {:fragment, "FALSE"}]
|
||||
|
||||
field :created_by, Ecto.Enum, values: ~w[identity]a
|
||||
field :created_by, Ecto.Enum, values: ~w[identity actor]a
|
||||
belongs_to :created_by_actor, Domain.Actors.Actor
|
||||
belongs_to :created_by_identity, Domain.Auth.Identity
|
||||
|
||||
field :deleted_at, :utc_datetime_usec
|
||||
|
||||
@@ -18,8 +18,7 @@ defmodule Domain.Resources.Resource.Changeset do
|
||||
with: &Connection.Changeset.changeset(account.id, &1, &2, subject),
|
||||
required: true
|
||||
)
|
||||
|> put_change(:created_by, :identity)
|
||||
|> put_change(:created_by_identity_id, subject.identity.id)
|
||||
|> put_created_by(subject)
|
||||
end
|
||||
|
||||
def create(%Accounts.Account{} = account, attrs) do
|
||||
|
||||
@@ -13,6 +13,13 @@ defmodule Domain.Tokens.Authorizer do
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:api_client) do
|
||||
[
|
||||
manage_tokens_permission(),
|
||||
manage_own_tokens_permission()
|
||||
]
|
||||
end
|
||||
|
||||
def list_permissions_for_role(:account_user) do
|
||||
[
|
||||
manage_own_tokens_permission()
|
||||
|
||||
@@ -43,8 +43,9 @@ defmodule Domain.Tokens.Token do
|
||||
field :last_seen_at, :utc_datetime_usec
|
||||
|
||||
# Maybe this is not needed and they should be in the join tables (eg. relay_group_tokens)
|
||||
field :created_by, Ecto.Enum, values: ~w[system identity]a
|
||||
field :created_by, Ecto.Enum, values: ~w[actor identity system]a
|
||||
belongs_to :created_by_identity, Domain.Auth.Identity
|
||||
belongs_to :created_by_actor, Domain.Actors.Actor
|
||||
field :created_by_user_agent, :string
|
||||
field :created_by_remote_ip, Domain.Types.IP
|
||||
|
||||
|
||||
@@ -46,8 +46,7 @@ defmodule Domain.Tokens.Token.Changeset do
|
||||
:service_account_client
|
||||
])
|
||||
|> changeset()
|
||||
|> put_change(:created_by, :identity)
|
||||
|> put_change(:created_by_identity_id, subject.identity.id)
|
||||
|> put_created_by(subject)
|
||||
end
|
||||
|
||||
defp changeset(changeset) do
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
defmodule Domain.Repo.Migrations.AddCreatedByActor do
|
||||
use Ecto.Migration
|
||||
|
||||
@table_names ~w[
|
||||
actor_groups
|
||||
auth_identities
|
||||
auth_providers
|
||||
gateway_groups
|
||||
policies
|
||||
relay_groups
|
||||
resources
|
||||
resource_connections
|
||||
tokens
|
||||
]a
|
||||
|
||||
defp migrate_data(table_name) do
|
||||
"""
|
||||
UPDATE #{table_name} AS t
|
||||
SET created_by_actor_id = ai.actor_id
|
||||
FROM auth_identities AS ai
|
||||
WHERE t.created_by_identity_id = ai.id
|
||||
AND t.created_by = 'identity'
|
||||
AND t.created_by_identity_id is not null;
|
||||
"""
|
||||
|> execute("")
|
||||
end
|
||||
|
||||
def change do
|
||||
for table_name <- @table_names do
|
||||
alter table(table_name) do
|
||||
add(:created_by_actor_id, :uuid)
|
||||
end
|
||||
|
||||
migrate_data(table_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -95,6 +95,9 @@ config :api, API.Endpoint,
|
||||
# Do not include metadata nor timestamps in development logs
|
||||
config :logger, :default_formatter, format: "[$level] $message\n"
|
||||
|
||||
# Disable caching for OpenAPI spec to ensure it is refreshed
|
||||
config :open_api_spex, :cache_adapter, OpenApiSpex.Plug.NoneCache
|
||||
|
||||
# Set a higher stacktrace during development. Avoid configuring such
|
||||
# in production as building large stacktraces may be expensive.
|
||||
config :phoenix, :stacktrace_depth, 20
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||
"number": {:hex, :number, "1.0.5", "d92136f9b9382aeb50145782f116112078b3465b7be58df1f85952b8bb399b0f", [:mix], [{:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "c0733a0a90773a66582b9e92a3f01290987f395c972cb7d685f51dd927cd5169"},
|
||||
"observer_cli": {:hex, :observer_cli, "1.7.4", "3c1bfb6d91bf68f6a3d15f46ae20da0f7740d363ee5bc041191ce8722a6c4fae", [:mix, :rebar3], [{:recon, "~> 2.5.1", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "50de6d95d814f447458bd5d72666a74624eddb0ef98bdcee61a0153aae0865ff"},
|
||||
"open_api_spex": {:hex, :open_api_spex, "3.20.0", "d4fcf1ee297aa94a673cddb92734eb0bc7cac698be93949a223a50f724e3af89", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "2e9beea71142ff09f8f935579b39406e2c6b5a3978e7235978d7faf2f90cd081"},
|
||||
"openid_connect": {:git, "https://github.com/firezone/openid_connect.git", "e4d9dca8ae43c765c00a7d3dfa12d6f24f5b3418", [ref: "e4d9dca8ae43c765c00a7d3dfa12d6f24f5b3418"]},
|
||||
"opentelemetry": {:hex, :opentelemetry, "1.4.0", "f928923ed80adb5eb7894bac22e9a198478e6a8f04020ae1d6f289fdcad0b498", [:rebar3], [{:opentelemetry_api, "~> 1.3.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:opentelemetry_semantic_conventions, "~> 0.2", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: false]}], "hexpm", "50b32ce127413e5d87b092b4d210a3449ea80cd8224090fe68d73d576a3faa15"},
|
||||
"opentelemetry_api": {:hex, :opentelemetry_api, "1.3.0", "03e2177f28dd8d11aaa88e8522c81c2f6a788170fe52f7a65262340961e663f9", [:mix, :rebar3], [{:opentelemetry_semantic_conventions, "~> 0.2", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: false]}], "hexpm", "b9e5ff775fd064fa098dba3c398490b77649a352b40b0b730a6b7dc0bdd68858"},
|
||||
@@ -112,4 +113,5 @@
|
||||
"workos": {:git, "https://github.com/firezone/workos-elixir.git", "6d6995e2a765656a6834577837433393fac9b35b", [branch: "main"]},
|
||||
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
|
||||
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
|
||||
"ymlr": {:hex, :ymlr, "2.0.0", "7525b6da40250777c35456017ef44f7faec06da254eafcf9f9cfb0d65f4c8cb7", [:mix], [], "hexpm", "f9301ad7ea377213b506f6e58ddffd1a7743e24238bb70e572ee510bdc2d1d5a"},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user