forked from AkkomaGang/akkoma
Merge branch 'feature/admin-api' into 'develop'
Add a admin API See merge request pleroma/pleroma!366
This commit is contained in:
commit
675653ceb7
9 changed files with 412 additions and 14 deletions
|
@ -14,9 +14,11 @@ defmodule Mix.Tasks.RelayFollow do
|
||||||
def run([target]) do
|
def run([target]) do
|
||||||
Mix.Task.run("app.start")
|
Mix.Task.run("app.start")
|
||||||
|
|
||||||
:ok = Relay.follow(target)
|
with {:ok, activity} <- Relay.follow(target) do
|
||||||
|
|
||||||
# put this task to sleep to allow the genserver to push out the messages
|
# put this task to sleep to allow the genserver to push out the messages
|
||||||
:timer.sleep(500)
|
:timer.sleep(500)
|
||||||
|
else
|
||||||
|
{:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,9 +13,11 @@ defmodule Mix.Tasks.RelayUnfollow do
|
||||||
def run([target]) do
|
def run([target]) do
|
||||||
Mix.Task.run("app.start")
|
Mix.Task.run("app.start")
|
||||||
|
|
||||||
:ok = Relay.unfollow(target)
|
with {:ok, activity} <- Relay.follow(target) do
|
||||||
|
|
||||||
# put this task to sleep to allow the genserver to push out the messages
|
# put this task to sleep to allow the genserver to push out the messages
|
||||||
:timer.sleep(500)
|
:timer.sleep(500)
|
||||||
|
else
|
||||||
|
{:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
32
lib/mix/tasks/set_admin.ex
Normal file
32
lib/mix/tasks/set_admin.ex
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
defmodule Mix.Tasks.SetAdmin do
|
||||||
|
use Mix.Task
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Sets admin status
|
||||||
|
Usage: set_admin nickname [true|false]
|
||||||
|
"""
|
||||||
|
def run([nickname | rest]) do
|
||||||
|
Application.ensure_all_started(:pleroma)
|
||||||
|
|
||||||
|
status =
|
||||||
|
case rest do
|
||||||
|
[status] -> status == "true"
|
||||||
|
_ -> true
|
||||||
|
end
|
||||||
|
|
||||||
|
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
||||||
|
info =
|
||||||
|
user.info
|
||||||
|
|> Map.put("is_admin", !!status)
|
||||||
|
|
||||||
|
cng = User.info_changeset(user, %{info: info})
|
||||||
|
{:ok, user} = User.update_and_set_cache(cng)
|
||||||
|
|
||||||
|
IO.puts("Admin status of #{nickname}: #{user.info["is_admin"]}")
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
IO.puts("No local user #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
lib/pleroma/plugs/user_is_admin_plug.ex
Normal file
19
lib/pleroma/plugs/user_is_admin_plug.ex
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
defmodule Pleroma.Plugs.UserIsAdminPlug do
|
||||||
|
import Plug.Conn
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
def init(options) do
|
||||||
|
options
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(%{assigns: %{user: %User{info: %{"is_admin" => true}}}} = conn, _) do
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, _) do
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/json")
|
||||||
|
|> send_resp(403, Jason.encode!(%{error: "User is not admin."}))
|
||||||
|
|> halt
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,11 +12,12 @@ def follow(target_instance) do
|
||||||
%User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance),
|
%User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance),
|
||||||
{:ok, activity} <- ActivityPub.follow(local_user, target_user) do
|
{:ok, activity} <- ActivityPub.follow(local_user, target_user) do
|
||||||
Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}")
|
Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}")
|
||||||
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
e -> Logger.error("error: #{inspect(e)}")
|
e ->
|
||||||
|
Logger.error("error: #{inspect(e)}")
|
||||||
|
{:error, e}
|
||||||
end
|
end
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def unfollow(target_instance) do
|
def unfollow(target_instance) do
|
||||||
|
@ -24,11 +25,12 @@ def unfollow(target_instance) do
|
||||||
%User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance),
|
%User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance),
|
||||||
{:ok, activity} <- ActivityPub.unfollow(local_user, target_user) do
|
{:ok, activity} <- ActivityPub.unfollow(local_user, target_user) do
|
||||||
Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}")
|
Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}")
|
||||||
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
e -> Logger.error("error: #{inspect(e)}")
|
e ->
|
||||||
|
Logger.error("error: #{inspect(e)}")
|
||||||
|
{:error, e}
|
||||||
end
|
end
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||||
|
|
158
lib/pleroma/web/admin_api/admin_api_controller.ex
Normal file
158
lib/pleroma/web/admin_api/admin_api_controller.ex
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
alias Pleroma.{User, Repo}
|
||||||
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
action_fallback(:errors)
|
||||||
|
|
||||||
|
def user_delete(conn, %{"nickname" => nickname}) do
|
||||||
|
user = User.get_by_nickname(nickname)
|
||||||
|
|
||||||
|
if user.local == true do
|
||||||
|
User.delete(user)
|
||||||
|
else
|
||||||
|
User.delete(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(nickname)
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_create(
|
||||||
|
conn,
|
||||||
|
%{"nickname" => nickname, "email" => email, "password" => password}
|
||||||
|
) do
|
||||||
|
new_user = %{
|
||||||
|
nickname: nickname,
|
||||||
|
name: nickname,
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
password_confirmation: password,
|
||||||
|
bio: "."
|
||||||
|
}
|
||||||
|
|
||||||
|
User.register_changeset(%User{}, new_user)
|
||||||
|
|> Repo.insert!()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(new_user.nickname)
|
||||||
|
end
|
||||||
|
|
||||||
|
def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
|
||||||
|
when permission_group in ["moderator", "admin"] do
|
||||||
|
user = User.get_by_nickname(nickname)
|
||||||
|
|
||||||
|
info =
|
||||||
|
user.info
|
||||||
|
|> Map.put("is_" <> permission_group, true)
|
||||||
|
|
||||||
|
cng = User.info_changeset(user, %{info: info})
|
||||||
|
{:ok, user} = User.update_and_set_cache(cng)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(user.info)
|
||||||
|
end
|
||||||
|
|
||||||
|
def right_get(conn, %{"nickname" => nickname}) do
|
||||||
|
user = User.get_by_nickname(nickname)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(user.info)
|
||||||
|
end
|
||||||
|
|
||||||
|
def right_add(conn, _) do
|
||||||
|
conn
|
||||||
|
|> put_status(404)
|
||||||
|
|> json(%{error: "No such permission_group"})
|
||||||
|
end
|
||||||
|
|
||||||
|
def right_delete(
|
||||||
|
%{assigns: %{user: %User{:nickname => admin_nickname}}} = conn,
|
||||||
|
%{
|
||||||
|
"permission_group" => permission_group,
|
||||||
|
"nickname" => nickname
|
||||||
|
}
|
||||||
|
)
|
||||||
|
when permission_group in ["moderator", "admin"] do
|
||||||
|
if admin_nickname == nickname do
|
||||||
|
conn
|
||||||
|
|> put_status(403)
|
||||||
|
|> json(%{error: "You can't revoke your own admin status."})
|
||||||
|
else
|
||||||
|
user = User.get_by_nickname(nickname)
|
||||||
|
|
||||||
|
info =
|
||||||
|
user.info
|
||||||
|
|> Map.put("is_" <> permission_group, false)
|
||||||
|
|
||||||
|
cng = User.info_changeset(user, %{info: info})
|
||||||
|
{:ok, user} = User.update_and_set_cache(cng)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(user.info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def right_delete(conn, _) do
|
||||||
|
conn
|
||||||
|
|> put_status(404)
|
||||||
|
|> json(%{error: "No such permission_group"})
|
||||||
|
end
|
||||||
|
|
||||||
|
def relay_follow(conn, %{"relay_url" => target}) do
|
||||||
|
{status, message} = Relay.follow(target)
|
||||||
|
|
||||||
|
if status == :ok do
|
||||||
|
conn
|
||||||
|
|> json(target)
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_status(500)
|
||||||
|
|> json(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def relay_unfollow(conn, %{"relay_url" => target}) do
|
||||||
|
{status, message} = Relay.unfollow(target)
|
||||||
|
|
||||||
|
if status == :ok do
|
||||||
|
conn
|
||||||
|
|> json(target)
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
|> put_status(500)
|
||||||
|
|> json(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@shortdoc "Get a account registeration invite token (base64 string)"
|
||||||
|
def get_invite_token(conn, _params) do
|
||||||
|
{:ok, token} = Pleroma.UserInviteToken.create_token()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(token.token)
|
||||||
|
end
|
||||||
|
|
||||||
|
@shortdoc "Get a password reset token (base64 string) for given nickname"
|
||||||
|
def get_password_reset(conn, %{"nickname" => nickname}) do
|
||||||
|
(%User{local: true} = user) = User.get_by_nickname(nickname)
|
||||||
|
{:ok, token} = Pleroma.PasswordResetToken.create_token(user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(token.token)
|
||||||
|
end
|
||||||
|
|
||||||
|
def errors(conn, {:param_cast, _}) do
|
||||||
|
conn
|
||||||
|
|> put_status(400)
|
||||||
|
|> json("Invalid parameters")
|
||||||
|
end
|
||||||
|
|
||||||
|
def errors(conn, _) do
|
||||||
|
conn
|
||||||
|
|> put_status(500)
|
||||||
|
|> json("Something went wrong")
|
||||||
|
end
|
||||||
|
end
|
|
@ -31,6 +31,21 @@ defmodule Pleroma.Web.Router do
|
||||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :admin_api do
|
||||||
|
plug(:accepts, ["json"])
|
||||||
|
plug(:fetch_session)
|
||||||
|
plug(Pleroma.Plugs.OAuthPlug)
|
||||||
|
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||||
|
plug(Pleroma.Plugs.UserFetcherPlug)
|
||||||
|
plug(Pleroma.Plugs.SessionAuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.LegacyAuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.AuthenticationPlug)
|
||||||
|
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||||
|
plug(Pleroma.Plugs.SetUserSessionIdPlug)
|
||||||
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
||||||
|
plug(Pleroma.Plugs.UserIsAdminPlug)
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :mastodon_html do
|
pipeline :mastodon_html do
|
||||||
plug(:accepts, ["html"])
|
plug(:accepts, ["html"])
|
||||||
plug(:fetch_session)
|
plug(:fetch_session)
|
||||||
|
@ -79,6 +94,23 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/emoji", UtilController, :emoji)
|
get("/emoji", UtilController, :emoji)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
|
pipe_through(:admin_api)
|
||||||
|
delete("/user", AdminAPIController, :user_delete)
|
||||||
|
post("/user", AdminAPIController, :user_create)
|
||||||
|
|
||||||
|
get("/permission_group/:nickname", AdminAPIController, :right_get)
|
||||||
|
get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get)
|
||||||
|
post("/permission_group/:nickname/:permission_group", AdminAPIController, :right_add)
|
||||||
|
delete("/permission_group/:nickname/:permission_group", AdminAPIController, :right_delete)
|
||||||
|
|
||||||
|
post("/relay", AdminAPIController, :relay_follow)
|
||||||
|
delete("/relay", AdminAPIController, :relay_unfollow)
|
||||||
|
|
||||||
|
get("/invite_token", AdminAPIController, :get_invite_token)
|
||||||
|
get("/password_reset", AdminAPIController, :get_password_reset)
|
||||||
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.TwitterAPI do
|
scope "/", Pleroma.Web.TwitterAPI do
|
||||||
pipe_through(:pleroma_html)
|
pipe_through(:pleroma_html)
|
||||||
get("/ostatus_subscribe", UtilController, :remote_follow)
|
get("/ostatus_subscribe", UtilController, :remote_follow)
|
||||||
|
|
39
test/plugs/user_is_admin_plug_test.exs
Normal file
39
test/plugs/user_is_admin_plug_test.exs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
defmodule Pleroma.Plugs.UserIsAdminPlugTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.UserIsAdminPlug
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "accepts a user that is admin", %{conn: conn} do
|
||||||
|
user = insert(:user, info: %{"is_admin" => true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|
||||||
|
ret_conn =
|
||||||
|
conn
|
||||||
|
|> UserIsAdminPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn == ret_conn
|
||||||
|
end
|
||||||
|
|
||||||
|
test "denies a user that isn't admin", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> UserIsAdminPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
|
||||||
|
test "denies when a user isn't set", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> UserIsAdminPlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.status == 403
|
||||||
|
end
|
||||||
|
end
|
112
test/web/admin_api/admin_api_controller_test.exs
Normal file
112
test/web/admin_api/admin_api_controller_test.exs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.{Repo, User}
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
import ExUnit.CaptureLog
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/user" do
|
||||||
|
test "Delete" do
|
||||||
|
admin = insert(:user, info: %{"is_admin" => true})
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> delete("/api/pleroma/admin/user?nickname=#{user.nickname}")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == user.nickname
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Create" do
|
||||||
|
admin = insert(:user, info: %{"is_admin" => true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/user", %{
|
||||||
|
"nickname" => "lain",
|
||||||
|
"email" => "lain@example.org",
|
||||||
|
"password" => "test"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == "lain"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/permission_group" do
|
||||||
|
test "GET is giving user_info" do
|
||||||
|
admin = insert(:user, info: %{"is_admin" => true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> get("/api/pleroma/admin/permission_group/#{admin.nickname}")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == admin.info
|
||||||
|
end
|
||||||
|
|
||||||
|
test "/:right POST, can add to a permission group" do
|
||||||
|
admin = insert(:user, info: %{"is_admin" => true})
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
user_info =
|
||||||
|
user.info
|
||||||
|
|> Map.put("is_admin", true)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/permission_group/#{user.nickname}/admin")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == user_info
|
||||||
|
end
|
||||||
|
|
||||||
|
test "/:right DELETE, can remove from a permission group" do
|
||||||
|
admin = insert(:user, info: %{"is_admin" => true})
|
||||||
|
user = insert(:user, info: %{"is_admin" => true})
|
||||||
|
|
||||||
|
user_info =
|
||||||
|
user.info
|
||||||
|
|> Map.put("is_admin", false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> delete("/api/pleroma/admin/permission_group/#{user.nickname}/admin")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == user_info
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "/api/pleroma/admin/invite_token" do
|
||||||
|
admin = insert(:user, info: %{"is_admin" => true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> get("/api/pleroma/admin/invite_token")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
end
|
||||||
|
|
||||||
|
test "/api/pleroma/admin/password_reset" do
|
||||||
|
admin = insert(:user, info: %{"is_admin" => true})
|
||||||
|
user = insert(:user, info: %{"is_admin" => true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> get("/api/pleroma/admin/password_reset?nickname=#{user.nickname}")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue