Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85827031
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
6 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/changelog.d/oauth-app.fix b/changelog.d/oauth-app.fix
new file mode 100644
index 000000000..eb917462f
--- /dev/null
+++ b/changelog.d/oauth-app.fix
@@ -0,0 +1 @@
+Prevent OAuth App flow from creating duplicate entries
diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
index 844673ae0..e5e8ea8f5 100644
--- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
@@ -1,57 +1,55 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AppController do
@moduledoc """
Controller for supporting app-related actions.
If authentication is an option, app tokens (user-unbound) must be supported.
"""
use Pleroma.Web, :controller
alias Pleroma.Maps
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Scopes
alias Pleroma.Web.OAuth.Token
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
plug(:skip_auth when action in [:create, :verify_credentials])
plug(Pleroma.Web.ApiSpec.CastAndValidate)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation
@doc "POST /api/v1/apps"
def create(%{body_params: params} = conn, _params) do
scopes = Scopes.fetch_scopes(params, ["read"])
user_id = get_user_id(conn)
app_attrs =
params
|> Map.take([:client_name, :redirect_uris, :website])
- |> Map.put(:scopes, scopes)
|> Maps.put_if_present(:user_id, user_id)
- with cs <- App.register_changeset(%App{}, app_attrs),
- {:ok, app} <- Repo.insert(cs) do
+ with {:ok, app} <- App.get_or_make(app_attrs, scopes) do
render(conn, "show.json", app: app)
end
end
defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id
defp get_user_id(_conn), do: nil
@doc """
GET /api/v1/apps/verify_credentials
Gets compact non-secret representation of the app. Supports app tokens and user tokens.
"""
def verify_credentials(%{assigns: %{token: %Token{} = token}} = conn, _) do
with %{app: %App{} = app} <- Repo.preload(token, :app) do
render(conn, "compact_non_secret.json", app: app)
end
end
end
diff --git a/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs
index bc9d4048c..1e2e68791 100644
--- a/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs
@@ -1,92 +1,139 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
use Pleroma.Web.ConnCase, async: true
alias Pleroma.Repo
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.Push
import Pleroma.Factory
test "apps/verify_credentials", %{conn: conn} do
user_bound_token = insert(:oauth_token)
app_bound_token = insert(:oauth_token, user: nil)
refute app_bound_token.user
for token <- [app_bound_token, user_bound_token] do
conn =
conn
|> put_req_header("authorization", "Bearer #{token.token}")
|> get("/api/v1/apps/verify_credentials")
app = Repo.preload(token, :app).app
expected = %{
"name" => app.client_name,
"website" => app.website,
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
}
assert expected == json_response_and_validate_schema(conn, 200)
end
end
test "creates an oauth app", %{conn: conn} do
app_attrs = build(:oauth_app)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/apps", %{
client_name: app_attrs.client_name,
redirect_uris: app_attrs.redirect_uris
})
[app] = Repo.all(App)
expected = %{
"name" => app.client_name,
"website" => app.website,
"client_id" => app.client_id,
"client_secret" => app.client_secret,
"id" => app.id |> to_string(),
"redirect_uri" => app.redirect_uris,
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
}
assert expected == json_response_and_validate_schema(conn, 200)
assert app.user_id == nil
end
test "creates an oauth app with a user", %{conn: conn} do
user = insert(:user)
app_attrs = build(:oauth_app)
conn =
conn
|> put_req_header("content-type", "application/json")
|> assign(:user, user)
|> post("/api/v1/apps", %{
client_name: app_attrs.client_name,
redirect_uris: app_attrs.redirect_uris
})
[app] = Repo.all(App)
expected = %{
"name" => app.client_name,
"website" => app.website,
"client_id" => app.client_id,
"client_secret" => app.client_secret,
"id" => app.id |> to_string(),
"redirect_uri" => app.redirect_uris,
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
}
assert expected == json_response_and_validate_schema(conn, 200)
assert app.user_id == user.id
end
+
+ test "creates an oauth app without a user", %{conn: conn} do
+ app_attrs = build(:oauth_app)
+
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/apps", %{
+ client_name: app_attrs.client_name,
+ redirect_uris: app_attrs.redirect_uris
+ })
+
+ [app] = Repo.all(App)
+
+ expected = %{
+ "name" => app.client_name,
+ "website" => app.website,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret,
+ "id" => app.id |> to_string(),
+ "redirect_uri" => app.redirect_uris,
+ "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
+ }
+
+ assert expected == json_response_and_validate_schema(conn, 200)
+ end
+
+ test "does not duplicate apps with the same client name", %{conn: conn} do
+ client_name = "BleromaSE"
+ redirect_uris = "https://bleroma.app/oauth-callback"
+
+ for _i <- 1..3 do
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/apps", %{
+ client_name: client_name,
+ redirect_uris: redirect_uris
+ })
+ |> json_response_and_validate_schema(200)
+ end
+
+ apps = Repo.all(App)
+
+ assert length(apps) == 1
+ assert List.first(apps).client_name == client_name
+ assert List.first(apps).redirect_uris == redirect_uris
+ end
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Oct 30, 1:34 PM (15 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1800169
Default Alt Text
(6 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline