diff options
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | docs/API/admin_api.md | 101 | ||||
| -rw-r--r-- | docs/administration/CLI_tasks/oauth_app.md | 16 | ||||
| -rw-r--r-- | lib/mix/tasks/pleroma/app.ex | 49 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/admin_api_controller.ex | 79 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 1 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/views/app_view.ex | 15 | ||||
| -rw-r--r-- | lib/pleroma/web/oauth/app.ex | 82 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 5 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api.ex | 3 | ||||
| -rw-r--r-- | priv/repo/migrations/20200227122417_add_trusted_to_apps.exs | 9 | ||||
| -rw-r--r-- | test/support/factory.ex | 2 | ||||
| -rw-r--r-- | test/tasks/app_test.exs | 65 | ||||
| -rw-r--r-- | test/web/admin_api/admin_api_controller_test.exs | 185 | ||||
| -rw-r--r-- | test/web/mastodon_api/controllers/account_controller_test.exs | 67 | 
15 files changed, 678 insertions, 3 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md index 804d3aa91..ce6737408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.  - 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 create trusted OAuth App.  <details>    <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.  - 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: endpoints for create/update/delete OAuth Apps.  </details>  ### Fixed diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md index 0ba88470a..6202c5a1a 100644 --- a/docs/API/admin_api.md +++ b/docs/API/admin_api.md @@ -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
\ No newline at end of file diff --git a/docs/administration/CLI_tasks/oauth_app.md b/docs/administration/CLI_tasks/oauth_app.md new file mode 100644 index 000000000..4d6bfc25a --- /dev/null +++ b/docs/administration/CLI_tasks/oauth_app.md @@ -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 +```
\ No newline at end of file diff --git a/lib/mix/tasks/pleroma/app.ex b/lib/mix/tasks/pleroma/app.ex new file mode 100644 index 000000000..463e2449f --- /dev/null +++ b/lib/mix/tasks/pleroma/app.ex @@ -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 diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 8de7d70a3..9c79310c0 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -27,7 +27,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do    alias Pleroma.Web.AdminAPI.Search    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.Endpoint +  alias Pleroma.Web.MastodonAPI.AppView    alias Pleroma.Web.MastodonAPI.StatusView +  alias Pleroma.Web.OAuth.App    alias Pleroma.Web.Router    require Logger @@ -1017,6 +1019,83 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      conn |> json("")    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      count = Stats.get_status_visibility_count() diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 28e80789d..e8e59ac66 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -104,6 +104,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do        |> Map.put("fullname", params["fullname"] || nickname)        |> Map.put("bio", params["bio"] || "")        |> Map.put("confirm", params["password"]) +      |> Map.put("trusted_app", app.trusted)      with :ok <- validate_email_param(params),           {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index d934e2107..36071cd25 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -7,6 +7,21 @@ defmodule Pleroma.Web.MastodonAPI.AppView do    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      %{        id: app.id |> to_string, diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex index 01ed326f4..6a6d5f2e2 100644 --- a/lib/pleroma/web/oauth/app.ex +++ b/lib/pleroma/web/oauth/app.ex @@ -5,6 +5,7 @@  defmodule Pleroma.Web.OAuth.App do    use Ecto.Schema    import Ecto.Changeset +  import Ecto.Query    alias Pleroma.Repo    @type t :: %__MODULE__{} @@ -16,14 +17,24 @@ defmodule Pleroma.Web.OAuth.App do      field(:website, :string)      field(:client_id, :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()    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      changeset =        struct -      |> cast(params, [:client_name, :redirect_uris, :scopes, :website]) +      |> changeset(params)        |> validate_required([:client_name, :redirect_uris, :scopes])      if changeset.valid? do @@ -41,6 +52,21 @@ defmodule Pleroma.Web.OAuth.App do      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 """    Gets app by attrs or create new  with attrs.    And updates the scopes if need. @@ -65,4 +91,58 @@ defmodule Pleroma.Web.OAuth.App do      |> change(%{scopes: scopes})      |> Repo.update()    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 diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index e9739983d..7e5960949 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -211,6 +211,11 @@ defmodule Pleroma.Web.Router do      post("/reload_emoji", AdminAPIController, :reload_emoji)      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    scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index f9c0994da..7a1ba6936 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do    def register_user(params, opts \\ []) do      token = params["token"] +    trusted_app? = params["trusted_app"]      params = %{        nickname: params["nickname"], @@ -29,7 +30,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do      captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])      # true if captcha is disabled or enabled and valid, false otherwise      captcha_ok = -      if not captcha_enabled do +      if trusted_app? || not captcha_enabled do          :ok        else          Pleroma.Captcha.validate( diff --git a/priv/repo/migrations/20200227122417_add_trusted_to_apps.exs b/priv/repo/migrations/20200227122417_add_trusted_to_apps.exs new file mode 100644 index 000000000..4e2a62af0 --- /dev/null +++ b/priv/repo/migrations/20200227122417_add_trusted_to_apps.exs @@ -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 diff --git a/test/support/factory.ex b/test/support/factory.ex index af639b6cd..f0b797fd4 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -294,7 +294,7 @@ defmodule Pleroma.Factory do    def oauth_app_factory do      %Pleroma.Web.OAuth.App{ -      client_name: "Some client", +      client_name: sequence(:client_name, &"Some client #{&1}"),        redirect_uris: "https://example.com/callback",        scopes: ["read", "write", "follow", "push", "admin"],        website: "https://example.com", diff --git a/test/tasks/app_test.exs b/test/tasks/app_test.exs new file mode 100644 index 000000000..b8f03566d --- /dev/null +++ b/test/tasks/app_test.exs @@ -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 diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 158966365..f80dbf8dd 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -3517,6 +3517,191 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                 response["status_visibility"]      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  # Needed for testing diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index a450a732c..61c2697b2 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -944,6 +944,73 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do        res = post(conn, "/api/v1/accounts", valid_params)        assert json_response(res, 403) == %{"error" => "Invalid credentials"}      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    describe "create account by app / rate limit" do | 
