require Logger
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :index)
plug(
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.InviteTokenOperation
+
@doc "Get list of created invites"
def index(conn, _params) do
invites = UserInviteToken.list_invites()
end
@doc "Create an account registration invite token"
- def create(conn, params) do
- opts = %{}
-
- opts =
- if params["max_use"],
- do: Map.put(opts, :max_use, params["max_use"]),
- else: opts
-
- opts =
- if params["expires_at"],
- do: Map.put(opts, :expires_at, params["expires_at"]),
- else: opts
-
- {:ok, invite} = UserInviteToken.create_invite(opts)
+ def create(%{body_params: params} = conn, _) do
+ {:ok, invite} = UserInviteToken.create_invite(params)
json(conn, AccountView.render("invite.json", %{invite: invite}))
end
@doc "Revokes invite by token"
- def revoke(conn, %{"token" => token}) do
+ def revoke(%{body_params: %{token: token}} = conn, _) do
with {:ok, invite} <- UserInviteToken.find_by_token(token),
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
conn
end
@doc "Sends registration invite via email"
- def email(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
+ def email(%{assigns: %{user: user}, body_params: %{email: email} = params} = conn, _) do
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
{:ok, invite_token} <- UserInviteToken.create_invite(),
user,
invite_token,
email,
- params["name"]
+ params[:name]
),
{:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
json_response(conn, :no_content, "")
defmodule Pleroma.Web.ApiSpec.Admin.InviteTokenOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
- alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.Schemas.ApiError
- alias Pleroma.Web.ApiSpec.Schemas.FlakeID
- alias Pleroma.Web.ApiSpec.Schemas.Status
- alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
import Pleroma.Web.ApiSpec.Helpers
- import Pleroma.Web.ApiSpec.StatusOperation, only: [id_param: 0]
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
def index_operation do
%Operation{
- tags: ["Admin", "Statuses"],
- operationId: "AdminAPI.StatusController.index",
- security: [%{"oAuth" => ["read:statuses"]}],
- parameters: [
- Operation.parameter(
- :godmode,
- :query,
- %Schema{type: :boolean, default: false},
- "Allows to see private statuses"
- ),
- Operation.parameter(
- :local_only,
- :query,
- %Schema{type: :boolean, default: false},
- "Excludes remote statuses"
- ),
- Operation.parameter(
- :with_reblogs,
- :query,
- %Schema{type: :boolean, default: false},
- "Allows to see reblogs"
- ),
- Operation.parameter(
- :page,
- :query,
- %Schema{type: :integer, default: 1},
- "Page"
- ),
- Operation.parameter(
- :page_size,
- :query,
- %Schema{type: :integer, default: 50},
- "Number of statuses to return"
- )
- ],
+ tags: ["Admin", "Invites"],
+ summary: "Get a list of generated invites",
+ operationId: "AdminAPI.InviteTokenController.index",
+ security: [%{"oAuth" => ["read:invites"]}],
responses: %{
200 =>
- Operation.response("Array of statuses", "application/json", %Schema{
- type: :array,
- items: status()
+ Operation.response("Intites", "application/json", %Schema{
+ type: :object,
+ properties: %{
+ invites: %Schema{type: :array, items: invite()}
+ },
+ example: %{
+ "invites" => [
+ %{
+ "id" => 123,
+ "token" => "kSQtDj_GNy2NZsL9AQDFIsHN5qdbguB6qRg3WHw6K1U=",
+ "used" => true,
+ "expires_at" => nil,
+ "uses" => 0,
+ "max_use" => nil,
+ "invite_type" => "one_time"
+ }
+ ]
+ }
})
}
}
end
- def show_operation do
+ def create_operation do
%Operation{
- tags: ["Admin", "Statuses"],
- summary: "Show Status",
- operationId: "AdminAPI.StatusController.show",
- parameters: [id_param()],
- security: [%{"oAuth" => ["read:statuses"]}],
+ tags: ["Admin", "Invites"],
+ summary: "Create an account registration invite token",
+ operationId: "AdminAPI.InviteTokenController.create",
+ security: [%{"oAuth" => ["write:invites"]}],
+ requestBody:
+ request_body("Parameters", %Schema{
+ type: :object,
+ properties: %{
+ max_use: %Schema{type: :integer},
+ expires_at: %Schema{type: :string, format: :date, example: "2020-04-20"}
+ }
+ }),
responses: %{
- 200 => Operation.response("Status", "application/json", Status),
- 404 => Operation.response("Not Found", "application/json", ApiError)
+ 200 => Operation.response("Invite", "application/json", invite())
}
}
end
- def update_operation do
+ def revoke_operation do
%Operation{
- tags: ["Admin", "Statuses"],
- summary: "Change the scope of an individual reported status",
- operationId: "AdminAPI.StatusController.update",
- parameters: [id_param()],
- security: [%{"oAuth" => ["write:statuses"]}],
- requestBody: request_body("Parameters", update_request(), required: true),
+ tags: ["Admin", "Invites"],
+ summary: "Revoke invite by token",
+ operationId: "AdminAPI.InviteTokenController.revoke",
+ security: [%{"oAuth" => ["write:invites"]}],
+ requestBody:
+ request_body(
+ "Parameters",
+ %Schema{
+ type: :object,
+ required: [:token],
+ properties: %{
+ token: %Schema{type: :string}
+ }
+ },
+ required: true
+ ),
responses: %{
- 200 => Operation.response("Status", "application/json", Status),
- 400 => Operation.response("Error", "application/json", ApiError)
+ 200 => Operation.response("Invite", "application/json", invite()),
+ 400 => Operation.response("Bad Request", "application/json", ApiError),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
- def delete_operation do
+ def email_operation do
%Operation{
- tags: ["Admin", "Statuses"],
- summary: "Delete an individual reported status",
- operationId: "AdminAPI.StatusController.delete",
- parameters: [id_param()],
- security: [%{"oAuth" => ["write:statuses"]}],
+ tags: ["Admin", "Invites"],
+ summary: "Sends registration invite via email",
+ operationId: "AdminAPI.InviteTokenController.email",
+ security: [%{"oAuth" => ["write:invites"]}],
+ requestBody:
+ request_body(
+ "Parameters",
+ %Schema{
+ type: :object,
+ required: [:email],
+ properties: %{
+ email: %Schema{type: :string, format: :email},
+ name: %Schema{type: :string}
+ }
+ },
+ required: true
+ ),
responses: %{
- 200 => empty_object_response(),
- 404 => Operation.response("Not Found", "application/json", ApiError)
+ 204 => no_content_response(),
+ 400 => Operation.response("Bad Request", "application/json", ApiError),
+ 403 => Operation.response("Forbidden", "application/json", ApiError)
}
}
end
- defp status do
- %Schema{
- anyOf: [
- Status,
- %Schema{
- type: :object,
- properties: %{
- account: %Schema{allOf: [Account, admin_account()]}
- }
- }
- ]
- }
- end
-
- defp admin_account do
+ defp invite do
%Schema{
+ title: "Invite",
type: :object,
properties: %{
- id: FlakeID,
- avatar: %Schema{type: :string},
- nickname: %Schema{type: :string},
- display_name: %Schema{type: :string},
- deactivated: %Schema{type: :boolean},
- local: %Schema{type: :boolean},
- roles: %Schema{
- type: :object,
- properties: %{
- admin: %Schema{type: :boolean},
- moderator: %Schema{type: :boolean}
- }
- },
- tags: %Schema{type: :string},
- confirmation_pending: %Schema{type: :string}
- }
- }
- end
-
- defp update_request do
- %Schema{
- type: :object,
- properties: %{
- sensitive: %Schema{
- type: :boolean,
- description: "Mark status and attached media as sensitive?"
- },
- visibility: VisibilityScope
+ id: %Schema{type: :integer},
+ token: %Schema{type: :string},
+ used: %Schema{type: :boolean},
+ expires_at: %Schema{type: :string, format: :date, nullable: true},
+ uses: %Schema{type: :integer},
+ max_use: %Schema{type: :integer, nullable: true},
+ invite_type: %Schema{
+ type: :string,
+ enum: ["one_time", "reusable", "date_limited", "reusable_date_limited"]
+ }
},
example: %{
- "visibility" => "private",
- "sensitive" => "false"
+ "id" => 123,
+ "token" => "kSQtDj_GNy2NZsL9AQDFIsHN5qdbguB6qRg3WHw6K1U=",
+ "used" => true,
+ "expires_at" => nil,
+ "uses" => 0,
+ "max_use" => nil,
+ "invite_type" => "one_time"
}
}
end
recipient_name = "J. D."
conn =
- post(
- conn,
- "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
- )
+ conn
+ |> put_req_header("content-type", "application/json;charset=utf-8")
+ |> post("/api/pleroma/admin/users/email_invite", %{
+ email: recipient_email,
+ name: recipient_name
+ })
- assert json_response(conn, :no_content)
+ assert json_response_and_validate_schema(conn, :no_content)
token_record = List.last(Repo.all(Pleroma.UserInviteToken))
assert token_record
build_conn()
|> assign(:user, non_admin_user)
|> assign(:token, token)
- |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
+ |> put_req_header("content-type", "application/json;charset=utf-8")
+ |> post("/api/pleroma/admin/users/email_invite", %{
+ email: "foo@bar.com",
+ name: "JD"
+ })
assert json_response(conn, :forbidden)
end
conn
|> put_req_header("content-type", "application/json;charset=utf-8")
|> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
- |> json_response(:no_content)
+ |> json_response_and_validate_schema(:no_content)
token_record =
Pleroma.UserInviteToken
Config.put([:instance, :registrations_open], false)
Config.put([:instance, :invites_enabled], false)
- conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/users/email_invite", %{
+ email: "foo@bar.com",
+ name: "JD"
+ })
- assert json_response(conn, :bad_request) ==
+ assert json_response_and_validate_schema(conn, :bad_request) ==
%{
"error" =>
"To send invites you need to set the `invites_enabled` option to true."
Config.put([:instance, :registrations_open], true)
Config.put([:instance, :invites_enabled], true)
- conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/users/email_invite", %{
+ email: "foo@bar.com",
+ name: "JD"
+ })
- assert json_response(conn, :bad_request) ==
+ assert json_response_and_validate_schema(conn, :bad_request) ==
%{
"error" =>
"To send invites you need to set the `registrations_open` option to false."
describe "POST /api/pleroma/admin/users/invite_token" do
test "without options", %{conn: conn} do
- conn = post(conn, "/api/pleroma/admin/users/invite_token")
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/users/invite_token")
- invite_json = json_response(conn, 200)
+ invite_json = json_response_and_validate_schema(conn, 200)
invite = UserInviteToken.find_by_token!(invite_json["token"])
refute invite.used
refute invite.expires_at
test "with expires_at", %{conn: conn} do
conn =
- post(conn, "/api/pleroma/admin/users/invite_token", %{
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/users/invite_token", %{
"expires_at" => Date.to_string(Date.utc_today())
})
- invite_json = json_response(conn, 200)
+ invite_json = json_response_and_validate_schema(conn, 200)
invite = UserInviteToken.find_by_token!(invite_json["token"])
refute invite.used
end
test "with max_use", %{conn: conn} do
- conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
- invite_json = json_response(conn, 200)
+ invite_json = json_response_and_validate_schema(conn, 200)
invite = UserInviteToken.find_by_token!(invite_json["token"])
refute invite.used
refute invite.expires_at
test "with max use and expires_at", %{conn: conn} do
conn =
- post(conn, "/api/pleroma/admin/users/invite_token", %{
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/users/invite_token", %{
"max_use" => 150,
"expires_at" => Date.to_string(Date.utc_today())
})
- invite_json = json_response(conn, 200)
+ invite_json = json_response_and_validate_schema(conn, 200)
invite = UserInviteToken.find_by_token!(invite_json["token"])
refute invite.used
assert invite.expires_at == Date.utc_today()
test "no invites", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/users/invites")
- assert json_response(conn, 200) == %{"invites" => []}
+ assert json_response_and_validate_schema(conn, 200) == %{"invites" => []}
end
test "with invite", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/users/invites")
- assert json_response(conn, 200) == %{
+ assert json_response_and_validate_schema(conn, 200) == %{
"invites" => [
%{
"expires_at" => nil,
test "with token", %{conn: conn} do
{:ok, invite} = UserInviteToken.create_invite()
- conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
- assert json_response(conn, 200) == %{
+ assert json_response_and_validate_schema(conn, 200) == %{
"expires_at" => nil,
"id" => invite.id,
"invite_type" => "one_time",
end
test "with invalid token", %{conn: conn} do
- conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
- assert json_response(conn, :not_found) == %{"error" => "Not found"}
+ assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
end
end
end