forked from AkkomaGang/akkoma
[#1427] Graceful clearance of OAuth admin scopes for non-admin users (no error raised).
PleromaFE and other clients may safely request admin scope(s): if user isn't an admin, request is successful but only non-admin scopes from request are granted.
This commit is contained in:
parent
79532a7f7c
commit
81b05340e9
2 changed files with 57 additions and 44 deletions
|
@ -79,7 +79,9 @@ defp authorize_admin_scopes(scopes, app_scopes, %User{} = user) do
|
||||||
if user.is_admin || !contains_admin_scopes?(scopes) || !contains_admin_scopes?(app_scopes) do
|
if user.is_admin || !contains_admin_scopes?(scopes) || !contains_admin_scopes?(app_scopes) do
|
||||||
{:ok, scopes}
|
{:ok, scopes}
|
||||||
else
|
else
|
||||||
{:error, :unsupported_scopes}
|
# Gracefully dropping admin scopes from requested scopes if user isn't an admin (not raising)
|
||||||
|
scopes = scopes -- OAuthScopesPlug.filter_descendants(scopes, ["admin"])
|
||||||
|
validate(scopes, app_scopes, user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -567,11 +567,18 @@ test "with existing authentication and OOB `redirect_uri`, redirects to app with
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /oauth/authorize" do
|
describe "POST /oauth/authorize" do
|
||||||
test "redirects with oauth authorization" do
|
test "redirects with oauth authorization, " <>
|
||||||
user = insert(:user)
|
"keeping only non-admin scopes for non-admin user" do
|
||||||
app = insert(:oauth_app, scopes: ["read", "write", "follow"])
|
app = insert(:oauth_app, scopes: ["read", "write", "admin"])
|
||||||
redirect_uri = OAuthController.default_redirect_uri(app)
|
redirect_uri = OAuthController.default_redirect_uri(app)
|
||||||
|
|
||||||
|
non_admin = insert(:user, is_admin: false)
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
|
||||||
|
for {user, expected_scopes} <- %{
|
||||||
|
non_admin => ["read:subscope", "write"],
|
||||||
|
admin => ["read:subscope", "write", "admin"]
|
||||||
|
} do
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> post("/oauth/authorize", %{
|
|> post("/oauth/authorize", %{
|
||||||
|
@ -580,7 +587,7 @@ test "redirects with oauth authorization" do
|
||||||
"password" => "test",
|
"password" => "test",
|
||||||
"client_id" => app.client_id,
|
"client_id" => app.client_id,
|
||||||
"redirect_uri" => redirect_uri,
|
"redirect_uri" => redirect_uri,
|
||||||
"scope" => "read:subscope write",
|
"scope" => "read:subscope write admin",
|
||||||
"state" => "statepassed"
|
"state" => "statepassed"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -593,7 +600,8 @@ test "redirects with oauth authorization" do
|
||||||
assert %{"state" => "statepassed", "code" => code} = query
|
assert %{"state" => "statepassed", "code" => code} = query
|
||||||
auth = Repo.get_by(Authorization, token: code)
|
auth = Repo.get_by(Authorization, token: code)
|
||||||
assert auth
|
assert auth
|
||||||
assert auth.scopes == ["read:subscope", "write"]
|
assert auth.scopes == expected_scopes
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns 401 for wrong credentials", %{conn: conn} do
|
test "returns 401 for wrong credentials", %{conn: conn} do
|
||||||
|
@ -623,13 +631,15 @@ test "returns 401 for wrong credentials", %{conn: conn} do
|
||||||
assert result =~ "Invalid Username/Password"
|
assert result =~ "Invalid Username/Password"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns 401 for missing scopes", %{conn: conn} do
|
test "returns 401 for missing scopes " <>
|
||||||
user = insert(:user)
|
"(including all admin-only scopes for non-admin user)" do
|
||||||
app = insert(:oauth_app)
|
user = insert(:user, is_admin: false)
|
||||||
|
app = insert(:oauth_app, scopes: ["read", "write", "admin"])
|
||||||
redirect_uri = OAuthController.default_redirect_uri(app)
|
redirect_uri = OAuthController.default_redirect_uri(app)
|
||||||
|
|
||||||
|
for scope_param <- ["", "admin:read admin:write"] do
|
||||||
result =
|
result =
|
||||||
conn
|
build_conn()
|
||||||
|> post("/oauth/authorize", %{
|
|> post("/oauth/authorize", %{
|
||||||
"authorization" => %{
|
"authorization" => %{
|
||||||
"name" => user.nickname,
|
"name" => user.nickname,
|
||||||
|
@ -637,7 +647,7 @@ test "returns 401 for missing scopes", %{conn: conn} do
|
||||||
"client_id" => app.client_id,
|
"client_id" => app.client_id,
|
||||||
"redirect_uri" => redirect_uri,
|
"redirect_uri" => redirect_uri,
|
||||||
"state" => "statepassed",
|
"state" => "statepassed",
|
||||||
"scope" => ""
|
"scope" => scope_param
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> html_response(:unauthorized)
|
|> html_response(:unauthorized)
|
||||||
|
@ -649,6 +659,7 @@ test "returns 401 for missing scopes", %{conn: conn} do
|
||||||
# Error message
|
# Error message
|
||||||
assert result =~ "This action is outside the authorized scopes"
|
assert result =~ "This action is outside the authorized scopes"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do
|
test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
Loading…
Reference in a new issue