forked from AkkomaGang/akkoma
Merge branch 'feature/1584-client-captcha-options' into 'develop'
Creating trusted app from adminFE & mix task Closes #1584 See merge request pleroma/pleroma!2252
This commit is contained in:
commit
a17bfb5fab
15 changed files with 678 additions and 3 deletions
|
@ -155,6 +155,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Add an option `authorized_fetch_mode` to require HTTP signatures for AP fetches.
|
- Add an option `authorized_fetch_mode` to require HTTP signatures for AP fetches.
|
||||||
- ActivityPub: support for `replies` collection (output for outgoing federation & fetching on incoming federation).
|
- ActivityPub: support for `replies` collection (output for outgoing federation & fetching on incoming federation).
|
||||||
- Mix task to refresh counter cache (`mix pleroma.refresh_counter_cache`)
|
- Mix task to refresh counter cache (`mix pleroma.refresh_counter_cache`)
|
||||||
|
- Mix task to create trusted OAuth App.
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
|
||||||
|
@ -201,6 +202,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- ActivityPub: `[:activitypub, :note_replies_output_limit]` setting sets the number of note self-replies to output on outgoing federation.
|
- ActivityPub: `[:activitypub, :note_replies_output_limit]` setting sets the number of note self-replies to output on outgoing federation.
|
||||||
- Admin API: `GET /api/pleroma/admin/stats` to get status count by visibility scope
|
- Admin API: `GET /api/pleroma/admin/stats` to get status count by visibility scope
|
||||||
- Admin API: `GET /api/pleroma/admin/statuses` - list all statuses (accepts `godmode` and `local_only`)
|
- Admin API: `GET /api/pleroma/admin/statuses` - list all statuses (accepts `godmode` and `local_only`)
|
||||||
|
- Admin API: endpoints for create/update/delete OAuth Apps.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -1088,3 +1088,104 @@ Loads json generated from `config/descriptions.exs`.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/oauth_app`
|
||||||
|
|
||||||
|
### List OAuth app
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- *optional* `name`
|
||||||
|
- *optional* `client_id`
|
||||||
|
- *optional* `page`
|
||||||
|
- *optional* `page_size`
|
||||||
|
- *optional* `trusted`
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "App name",
|
||||||
|
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
|
||||||
|
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
|
||||||
|
"redirect_uri": "https://example.com/oauth-callback",
|
||||||
|
"website": "https://example.com",
|
||||||
|
"trusted": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"count": 17,
|
||||||
|
"page_size": 50
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `POST /api/pleroma/admin/oauth_app`
|
||||||
|
|
||||||
|
### Create OAuth App
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `name`
|
||||||
|
- `redirect_uris`
|
||||||
|
- `scopes`
|
||||||
|
- *optional* `website`
|
||||||
|
- *optional* `trusted`
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "App name",
|
||||||
|
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
|
||||||
|
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
|
||||||
|
"redirect_uri": "https://example.com/oauth-callback",
|
||||||
|
"website": "https://example.com",
|
||||||
|
"trusted": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- On failure:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"redirect_uris": "can't be blank",
|
||||||
|
"name": "can't be blank"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `PATCH /api/pleroma/admin/oauth_app/:id`
|
||||||
|
|
||||||
|
### Update OAuth App
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- *optional* `name`
|
||||||
|
- *optional* `redirect_uris`
|
||||||
|
- *optional* `scopes`
|
||||||
|
- *optional* `website`
|
||||||
|
- *optional* `trusted`
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "App name",
|
||||||
|
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
|
||||||
|
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
|
||||||
|
"redirect_uri": "https://example.com/oauth-callback",
|
||||||
|
"website": "https://example.com",
|
||||||
|
"trusted": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `DELETE /api/pleroma/admin/oauth_app/:id`
|
||||||
|
|
||||||
|
### Delete OAuth App
|
||||||
|
|
||||||
|
- Params: None
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
- On success: `204`, empty response
|
||||||
|
- On failure:
|
||||||
|
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
16
docs/administration/CLI_tasks/oauth_app.md
Normal file
16
docs/administration/CLI_tasks/oauth_app.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Creating trusted OAuth App
|
||||||
|
|
||||||
|
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||||
|
|
||||||
|
## Create trusted OAuth App.
|
||||||
|
|
||||||
|
Optional params:
|
||||||
|
* `-s SCOPES` - scopes for app, e.g. `read,write,follow,push`.
|
||||||
|
|
||||||
|
```sh tab="OTP"
|
||||||
|
./bin/pleroma_ctl app create -n APP_NAME -r REDIRECT_URI
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh tab="From Source"
|
||||||
|
mix pleroma.app create -n APP_NAME -r REDIRECT_URI
|
||||||
|
```
|
49
lib/mix/tasks/pleroma/app.ex
Normal file
49
lib/mix/tasks/pleroma/app.ex
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.App do
|
||||||
|
@moduledoc File.read!("docs/administration/CLI_tasks/oauth_app.md")
|
||||||
|
use Mix.Task
|
||||||
|
|
||||||
|
import Mix.Pleroma
|
||||||
|
|
||||||
|
@shortdoc "Creates trusted OAuth App"
|
||||||
|
|
||||||
|
def run(["create" | options]) do
|
||||||
|
start_pleroma()
|
||||||
|
|
||||||
|
{opts, _} =
|
||||||
|
OptionParser.parse!(options,
|
||||||
|
strict: [name: :string, redirect_uri: :string, scopes: :string],
|
||||||
|
aliases: [n: :name, r: :redirect_uri, s: :scopes]
|
||||||
|
)
|
||||||
|
|
||||||
|
scopes =
|
||||||
|
if opts[:scopes] do
|
||||||
|
String.split(opts[:scopes], ",")
|
||||||
|
else
|
||||||
|
["read", "write", "follow", "push"]
|
||||||
|
end
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
client_name: opts[:name],
|
||||||
|
redirect_uris: opts[:redirect_uri],
|
||||||
|
trusted: true,
|
||||||
|
scopes: scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
with {:ok, app} <- Pleroma.Web.OAuth.App.create(params) do
|
||||||
|
shell_info("#{app.client_name} successfully created:")
|
||||||
|
shell_info("App client_id: " <> app.client_id)
|
||||||
|
shell_info("App client_secret: " <> app.client_secret)
|
||||||
|
else
|
||||||
|
{:error, changeset} ->
|
||||||
|
shell_error("Creating failed:")
|
||||||
|
|
||||||
|
Enum.each(Pleroma.Web.OAuth.App.errors(changeset), fn {key, error} ->
|
||||||
|
shell_error("#{key}: #{error}")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -27,7 +27,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.MastodonAPI.AppView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -1017,6 +1019,83 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
|
||||||
conn |> json("")
|
conn |> json("")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def oauth_app_create(conn, params) do
|
||||||
|
params =
|
||||||
|
if params["name"] do
|
||||||
|
Map.put(params, "client_name", params["name"])
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
result =
|
||||||
|
case App.create(params) do
|
||||||
|
{:ok, app} ->
|
||||||
|
AppView.render("show.json", %{app: app, admin: true})
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
App.errors(changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
json(conn, result)
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_app_update(conn, params) do
|
||||||
|
params =
|
||||||
|
if params["name"] do
|
||||||
|
Map.put(params, "client_name", params["name"])
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
with {:ok, app} <- App.update(params) do
|
||||||
|
json(conn, AppView.render("show.json", %{app: app, admin: true}))
|
||||||
|
else
|
||||||
|
{:error, changeset} ->
|
||||||
|
json(conn, App.errors(changeset))
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_app_list(conn, params) do
|
||||||
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
|
search_params = %{
|
||||||
|
client_name: params["name"],
|
||||||
|
client_id: params["client_id"],
|
||||||
|
page: page,
|
||||||
|
page_size: page_size
|
||||||
|
}
|
||||||
|
|
||||||
|
search_params =
|
||||||
|
if Map.has_key?(params, "trusted") do
|
||||||
|
Map.put(search_params, :trusted, params["trusted"])
|
||||||
|
else
|
||||||
|
search_params
|
||||||
|
end
|
||||||
|
|
||||||
|
with {:ok, apps, count} <- App.search(search_params) do
|
||||||
|
json(
|
||||||
|
conn,
|
||||||
|
AppView.render("index.json",
|
||||||
|
apps: apps,
|
||||||
|
count: count,
|
||||||
|
page_size: page_size,
|
||||||
|
admin: true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def oauth_app_delete(conn, params) do
|
||||||
|
with {:ok, _app} <- App.destroy(params["id"]) do
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
_ -> json_response(conn, :bad_request, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def stats(conn, _) do
|
def stats(conn, _) do
|
||||||
count = Stats.get_status_visibility_count()
|
count = Stats.get_status_visibility_count()
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,7 @@ def create(
|
||||||
|> Map.put("fullname", params["fullname"] || nickname)
|
|> Map.put("fullname", params["fullname"] || nickname)
|
||||||
|> Map.put("bio", params["bio"] || "")
|
|> Map.put("bio", params["bio"] || "")
|
||||||
|> Map.put("confirm", params["password"])
|
|> Map.put("confirm", params["password"])
|
||||||
|
|> Map.put("trusted_app", app.trusted)
|
||||||
|
|
||||||
with :ok <- validate_email_param(params),
|
with :ok <- validate_email_param(params),
|
||||||
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
||||||
|
|
|
@ -7,6 +7,21 @@ defmodule Pleroma.Web.MastodonAPI.AppView do
|
||||||
|
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
|
|
||||||
|
def render("index.json", %{apps: apps, count: count, page_size: page_size, admin: true}) do
|
||||||
|
%{
|
||||||
|
apps: render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json", %{admin: true}),
|
||||||
|
count: count,
|
||||||
|
page_size: page_size
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{admin: true, app: %App{} = app} = assigns) do
|
||||||
|
"show.json"
|
||||||
|
|> render(Map.delete(assigns, :admin))
|
||||||
|
|> Map.put(:trusted, app.trusted)
|
||||||
|
|> Map.put(:id, app.id)
|
||||||
|
end
|
||||||
|
|
||||||
def render("show.json", %{app: %App{} = app}) do
|
def render("show.json", %{app: %App{} = app}) do
|
||||||
%{
|
%{
|
||||||
id: app.id |> to_string,
|
id: app.id |> to_string,
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.OAuth.App do
|
defmodule Pleroma.Web.OAuth.App do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
@ -16,14 +17,24 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
field(:website, :string)
|
field(:website, :string)
|
||||||
field(:client_id, :string)
|
field(:client_id, :string)
|
||||||
field(:client_secret, :string)
|
field(:client_secret, :string)
|
||||||
|
field(:trusted, :boolean, default: false)
|
||||||
|
|
||||||
|
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
|
||||||
|
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec changeset(App.t(), map()) :: Ecto.Changeset.t()
|
||||||
|
def changeset(struct, params) do
|
||||||
|
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec register_changeset(App.t(), map()) :: Ecto.Changeset.t()
|
||||||
def register_changeset(struct, params \\ %{}) do
|
def register_changeset(struct, params \\ %{}) do
|
||||||
changeset =
|
changeset =
|
||||||
struct
|
struct
|
||||||
|> cast(params, [:client_name, :redirect_uris, :scopes, :website])
|
|> changeset(params)
|
||||||
|> validate_required([:client_name, :redirect_uris, :scopes])
|
|> validate_required([:client_name, :redirect_uris, :scopes])
|
||||||
|
|
||||||
if changeset.valid? do
|
if changeset.valid? do
|
||||||
|
@ -41,6 +52,21 @@ def register_changeset(struct, params \\ %{}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec create(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def create(params) do
|
||||||
|
with changeset <- __MODULE__.register_changeset(%__MODULE__{}, params) do
|
||||||
|
Repo.insert(changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def update(params) do
|
||||||
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, params["id"]),
|
||||||
|
changeset <- changeset(app, params) do
|
||||||
|
Repo.update(changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets app by attrs or create new with attrs.
|
Gets app by attrs or create new with attrs.
|
||||||
And updates the scopes if need.
|
And updates the scopes if need.
|
||||||
|
@ -65,4 +91,58 @@ defp update_scopes(%__MODULE__{} = app, scopes) do
|
||||||
|> change(%{scopes: scopes})
|
|> change(%{scopes: scopes})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec search(map()) :: {:ok, [App.t()], non_neg_integer()}
|
||||||
|
def search(params) do
|
||||||
|
query = from(a in __MODULE__)
|
||||||
|
|
||||||
|
query =
|
||||||
|
if params[:client_name] do
|
||||||
|
from(a in query, where: a.client_name == ^params[:client_name])
|
||||||
|
else
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
query =
|
||||||
|
if params[:client_id] do
|
||||||
|
from(a in query, where: a.client_id == ^params[:client_id])
|
||||||
|
else
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
query =
|
||||||
|
if Map.has_key?(params, :trusted) do
|
||||||
|
from(a in query, where: a.trusted == ^params[:trusted])
|
||||||
|
else
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
query =
|
||||||
|
from(u in query,
|
||||||
|
limit: ^params[:page_size],
|
||||||
|
offset: ^((params[:page] - 1) * params[:page_size])
|
||||||
|
)
|
||||||
|
|
||||||
|
count = Repo.aggregate(__MODULE__, :count, :id)
|
||||||
|
|
||||||
|
{:ok, Repo.all(query), count}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec destroy(pos_integer()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||||
|
def destroy(id) do
|
||||||
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||||
|
Repo.delete(app)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec errors(Ecto.Changeset.t()) :: map()
|
||||||
|
def errors(changeset) do
|
||||||
|
Enum.reduce(changeset.errors, %{}, fn
|
||||||
|
{:client_name, {error, _}}, acc ->
|
||||||
|
Map.put(acc, :name, error)
|
||||||
|
|
||||||
|
{key, {error, _}}, acc ->
|
||||||
|
Map.put(acc, key, error)
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -211,6 +211,11 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||||
get("/stats", AdminAPIController, :stats)
|
get("/stats", AdminAPIController, :stats)
|
||||||
|
|
||||||
|
get("/oauth_app", AdminAPIController, :oauth_app_list)
|
||||||
|
post("/oauth_app", AdminAPIController, :oauth_app_create)
|
||||||
|
patch("/oauth_app/:id", AdminAPIController, :oauth_app_update)
|
||||||
|
delete("/oauth_app/:id", AdminAPIController, :oauth_app_delete)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||||
|
|
||||||
def register_user(params, opts \\ []) do
|
def register_user(params, opts \\ []) do
|
||||||
token = params["token"]
|
token = params["token"]
|
||||||
|
trusted_app? = params["trusted_app"]
|
||||||
|
|
||||||
params = %{
|
params = %{
|
||||||
nickname: params["nickname"],
|
nickname: params["nickname"],
|
||||||
|
@ -29,7 +30,7 @@ def register_user(params, opts \\ []) do
|
||||||
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
|
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
|
||||||
# true if captcha is disabled or enabled and valid, false otherwise
|
# true if captcha is disabled or enabled and valid, false otherwise
|
||||||
captcha_ok =
|
captcha_ok =
|
||||||
if not captcha_enabled do
|
if trusted_app? || not captcha_enabled do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
Pleroma.Captcha.validate(
|
Pleroma.Captcha.validate(
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddTrustedToApps do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:apps) do
|
||||||
|
add(:trusted, :boolean, default: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -294,7 +294,7 @@ def follow_activity_factory do
|
||||||
|
|
||||||
def oauth_app_factory do
|
def oauth_app_factory do
|
||||||
%Pleroma.Web.OAuth.App{
|
%Pleroma.Web.OAuth.App{
|
||||||
client_name: "Some client",
|
client_name: sequence(:client_name, &"Some client #{&1}"),
|
||||||
redirect_uris: "https://example.com/callback",
|
redirect_uris: "https://example.com/callback",
|
||||||
scopes: ["read", "write", "follow", "push", "admin"],
|
scopes: ["read", "write", "follow", "push", "admin"],
|
||||||
website: "https://example.com",
|
website: "https://example.com",
|
||||||
|
|
65
test/tasks/app_test.exs
Normal file
65
test/tasks/app_test.exs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.AppTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Mix.shell(Mix.Shell.Process)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Mix.shell(Mix.Shell.IO)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "creates new app" do
|
||||||
|
test "with default scopes" do
|
||||||
|
name = "Some name"
|
||||||
|
redirect = "https://example.com"
|
||||||
|
Mix.Tasks.Pleroma.App.run(["create", "-n", name, "-r", redirect])
|
||||||
|
|
||||||
|
assert_app(name, redirect, ["read", "write", "follow", "push"])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with custom scopes" do
|
||||||
|
name = "Another name"
|
||||||
|
redirect = "https://example.com"
|
||||||
|
|
||||||
|
Mix.Tasks.Pleroma.App.run([
|
||||||
|
"create",
|
||||||
|
"-n",
|
||||||
|
name,
|
||||||
|
"-r",
|
||||||
|
redirect,
|
||||||
|
"-s",
|
||||||
|
"read,write,follow,push,admin"
|
||||||
|
])
|
||||||
|
|
||||||
|
assert_app(name, redirect, ["read", "write", "follow", "push", "admin"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with errors" do
|
||||||
|
Mix.Tasks.Pleroma.App.run(["create"])
|
||||||
|
{:mix_shell, :error, ["Creating failed:"]}
|
||||||
|
{:mix_shell, :error, ["name: can't be blank"]}
|
||||||
|
{:mix_shell, :error, ["redirect_uris: can't be blank"]}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assert_app(name, redirect, scopes) do
|
||||||
|
app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name)
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :info, [message]}
|
||||||
|
assert message == "#{name} successfully created:"
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :info, [message]}
|
||||||
|
assert message == "App client_id: #{app.client_id}"
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :info, [message]}
|
||||||
|
assert message == "App client_secret: #{app.client_secret}"
|
||||||
|
|
||||||
|
assert app.scopes == scopes
|
||||||
|
assert app.redirect_uris == redirect
|
||||||
|
end
|
||||||
|
end
|
|
@ -3517,6 +3517,191 @@ test "status visibility count", %{conn: conn} do
|
||||||
response["status_visibility"]
|
response["status_visibility"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/oauth_app" do
|
||||||
|
test "errors", %{conn: conn} do
|
||||||
|
response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
|
||||||
|
|
||||||
|
assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "success", %{conn: conn} do
|
||||||
|
base_url = Pleroma.Web.base_url()
|
||||||
|
app_name = "Trusted app"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> post("/api/pleroma/admin/oauth_app", %{
|
||||||
|
name: app_name,
|
||||||
|
redirect_uris: base_url
|
||||||
|
})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"name" => ^app_name,
|
||||||
|
"redirect_uri" => ^base_url,
|
||||||
|
"trusted" => false
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with trusted", %{conn: conn} do
|
||||||
|
base_url = Pleroma.Web.base_url()
|
||||||
|
app_name = "Trusted app"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> post("/api/pleroma/admin/oauth_app", %{
|
||||||
|
name: app_name,
|
||||||
|
redirect_uris: base_url,
|
||||||
|
trusted: true
|
||||||
|
})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"name" => ^app_name,
|
||||||
|
"redirect_uri" => ^base_url,
|
||||||
|
"trusted" => true
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/oauth_app" do
|
||||||
|
setup do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
{:ok, app: app}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "list", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{"apps" => apps, "count" => count, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert length(apps) == count
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with page size", %{conn: conn} do
|
||||||
|
insert(:oauth_app)
|
||||||
|
page_size = 1
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
|
||||||
|
|
||||||
|
assert length(apps) == page_size
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by client name", %{conn: conn, app: app} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by client id", %{conn: conn, app: app} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only trusted", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app, trusted: true)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/oauth_app", %{trusted: true})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||||
|
|
||||||
|
assert returned["client_id"] == app.client_id
|
||||||
|
assert returned["name"] == app.client_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /api/pleroma/admin/oauth_app/:id" do
|
||||||
|
test "with id", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
|
||||||
|
|> json_response(:no_content)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with non existance id", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> delete("/api/pleroma/admin/oauth_app/0")
|
||||||
|
|> json_response(:bad_request)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PATCH /api/pleroma/admin/oauth_app/:id" do
|
||||||
|
test "with id", %{conn: conn} do
|
||||||
|
app = insert(:oauth_app)
|
||||||
|
|
||||||
|
name = "another name"
|
||||||
|
url = "https://example.com"
|
||||||
|
scopes = ["admin"]
|
||||||
|
id = app.id
|
||||||
|
website = "http://website.com"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
|
||||||
|
name: name,
|
||||||
|
trusted: true,
|
||||||
|
redirect_uris: url,
|
||||||
|
scopes: scopes,
|
||||||
|
website: website
|
||||||
|
})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => _,
|
||||||
|
"client_secret" => _,
|
||||||
|
"id" => ^id,
|
||||||
|
"name" => ^name,
|
||||||
|
"redirect_uri" => ^url,
|
||||||
|
"trusted" => true,
|
||||||
|
"website" => ^website
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without id", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> patch("/api/pleroma/admin/oauth_app/0")
|
||||||
|
|> json_response(:bad_request)
|
||||||
|
|
||||||
|
assert response == ""
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Needed for testing
|
# Needed for testing
|
||||||
|
|
|
@ -944,6 +944,73 @@ test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_
|
||||||
res = post(conn, "/api/v1/accounts", valid_params)
|
res = post(conn, "/api/v1/accounts", valid_params)
|
||||||
assert json_response(res, 403) == %{"error" => "Invalid credentials"}
|
assert json_response(res, 403) == %{"error" => "Invalid credentials"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "registration from trusted app" do
|
||||||
|
clear_config([Pleroma.Captcha, :enabled], true)
|
||||||
|
app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> post("/oauth/token", %{
|
||||||
|
"grant_type" => "client_credentials",
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"client_secret" => app.client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
|
||||||
|
|
||||||
|
response =
|
||||||
|
build_conn()
|
||||||
|
|> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
|
||||||
|
|> post("/api/v1/accounts", %{
|
||||||
|
nickname: "nickanme",
|
||||||
|
agreement: true,
|
||||||
|
email: "email@example.com",
|
||||||
|
fullname: "Lain",
|
||||||
|
username: "Lain",
|
||||||
|
password: "some_password",
|
||||||
|
confirm: "some_password"
|
||||||
|
})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"access_token" => access_token,
|
||||||
|
"created_at" => _,
|
||||||
|
"scope" => ["read", "write", "follow", "push"],
|
||||||
|
"token_type" => "Bearer"
|
||||||
|
} = response
|
||||||
|
|
||||||
|
response =
|
||||||
|
build_conn()
|
||||||
|
|> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
|
||||||
|
|> get("/api/v1/accounts/verify_credentials")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"acct" => "Lain",
|
||||||
|
"bot" => false,
|
||||||
|
"display_name" => "Lain",
|
||||||
|
"follow_requests_count" => 0,
|
||||||
|
"followers_count" => 0,
|
||||||
|
"following_count" => 0,
|
||||||
|
"locked" => false,
|
||||||
|
"note" => "",
|
||||||
|
"source" => %{
|
||||||
|
"fields" => [],
|
||||||
|
"note" => "",
|
||||||
|
"pleroma" => %{
|
||||||
|
"actor_type" => "Person",
|
||||||
|
"discoverable" => false,
|
||||||
|
"no_rich_text" => false,
|
||||||
|
"show_role" => true
|
||||||
|
},
|
||||||
|
"privacy" => "public",
|
||||||
|
"sensitive" => false
|
||||||
|
},
|
||||||
|
"statuses_count" => 0,
|
||||||
|
"username" => "Lain"
|
||||||
|
} = response
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "create account by app / rate limit" do
|
describe "create account by app / rate limit" do
|
||||||
|
|
Loading…
Reference in a new issue