Purge SSH frontend (#23)
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/23
This commit is contained in:
parent
0543954d14
commit
e6cd61619f
|
@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- SSH frontend, to be potentially re-enabled via a bridge rather than wired into the main system
|
||||||
|
|
||||||
## 2.5.2
|
## 2.5.2
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -607,9 +607,6 @@
|
||||||
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
|
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
|
||||||
uid: System.get_env("LDAP_UID") || "cn"
|
uid: System.get_env("LDAP_UID") || "cn"
|
||||||
|
|
||||||
config :esshd,
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
oauth_consumer_strategies =
|
oauth_consumer_strategies =
|
||||||
System.get_env("OAUTH_CONSUMER_STRATEGIES")
|
System.get_env("OAUTH_CONSUMER_STRATEGIES")
|
||||||
|> to_string()
|
|> to_string()
|
||||||
|
|
|
@ -835,24 +835,6 @@ This will probably take a long time.
|
||||||
|
|
||||||
## Alternative client protocols
|
## Alternative client protocols
|
||||||
|
|
||||||
### BBS / SSH access
|
|
||||||
|
|
||||||
To enable simple command line interface accessible over ssh, add a setting like this to your configuration file:
|
|
||||||
|
|
||||||
```exs
|
|
||||||
app_dir = File.cwd!
|
|
||||||
priv_dir = Path.join([app_dir, "priv/ssh_keys"])
|
|
||||||
|
|
||||||
config :esshd,
|
|
||||||
enabled: true,
|
|
||||||
priv_dir: priv_dir,
|
|
||||||
handler: "Pleroma.BBS.Handler",
|
|
||||||
port: 10_022,
|
|
||||||
password_authenticator: "Pleroma.BBS.Authenticator"
|
|
||||||
```
|
|
||||||
|
|
||||||
Feel free to adjust the priv_dir and port number. Then you will have to create the key for the keys (in the example `priv/ssh_keys`) and create the host keys with `ssh-keygen -m PEM -N "" -b 2048 -t rsa -f ssh_host_rsa_key`. After restarting, you should be able to connect to your Pleroma instance with `ssh username@server -p $PORT`
|
|
||||||
|
|
||||||
### :gopher
|
### :gopher
|
||||||
* `enabled`: Enables the gopher interface
|
* `enabled`: Enables the gopher interface
|
||||||
* `ip`: IP address to bind to
|
* `ip`: IP address to bind to
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.BBS.Authenticator do
|
|
||||||
use Sshd.PasswordAuthenticator
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.Plugs.AuthenticationPlug
|
|
||||||
|
|
||||||
def authenticate(username, password) do
|
|
||||||
username = to_string(username)
|
|
||||||
password = to_string(password)
|
|
||||||
|
|
||||||
with %User{} = user <- User.get_by_nickname(username) do
|
|
||||||
AuthenticationPlug.checkpw(password, user.password_hash)
|
|
||||||
else
|
|
||||||
_e -> false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,149 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.BBS.Handler do
|
|
||||||
use Sshd.ShellHandler
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.HTML
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
|
|
||||||
def on_shell(username, _pubkey, _ip, _port) do
|
|
||||||
:ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!")
|
|
||||||
user = Pleroma.User.get_cached_by_nickname(to_string(username))
|
|
||||||
Logger.debug("#{inspect(user)}")
|
|
||||||
loop(run_state(user: user))
|
|
||||||
end
|
|
||||||
|
|
||||||
def on_connect(username, ip, port, method) do
|
|
||||||
Logger.debug(fn ->
|
|
||||||
"""
|
|
||||||
Incoming SSH shell #{inspect(self())} requested for #{username} from #{inspect(ip)}:#{inspect(port)} using #{inspect(method)}
|
|
||||||
"""
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def on_disconnect(username, ip, port) do
|
|
||||||
Logger.debug(fn ->
|
|
||||||
"Disconnecting SSH shell for #{username} from #{inspect(ip)}:#{inspect(port)}"
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp loop(state) do
|
|
||||||
self_pid = self()
|
|
||||||
counter = state.counter
|
|
||||||
prefix = state.prefix
|
|
||||||
user = state.user
|
|
||||||
|
|
||||||
input = spawn(fn -> io_get(self_pid, prefix, counter, user.nickname) end)
|
|
||||||
wait_input(state, input)
|
|
||||||
end
|
|
||||||
|
|
||||||
def puts_activity(activity) do
|
|
||||||
status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity})
|
|
||||||
IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})")
|
|
||||||
IO.puts(HTML.strip_tags(status.content))
|
|
||||||
IO.puts("")
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_command(state, "help") do
|
|
||||||
IO.puts("Available commands:")
|
|
||||||
IO.puts("help - This help")
|
|
||||||
IO.puts("home - Show the home timeline")
|
|
||||||
IO.puts("p <text> - Post the given text")
|
|
||||||
IO.puts("r <id> <text> - Reply to the post with the given id")
|
|
||||||
IO.puts("quit - Quit")
|
|
||||||
|
|
||||||
state
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_command(%{user: user} = state, "r " <> text) do
|
|
||||||
text = String.trim(text)
|
|
||||||
[activity_id, rest] = String.split(text, " ", parts: 2)
|
|
||||||
|
|
||||||
with %Activity{} <- Activity.get_by_id(activity_id),
|
|
||||||
{:ok, _activity} <-
|
|
||||||
CommonAPI.post(user, %{status: rest, in_reply_to_status_id: activity_id}) do
|
|
||||||
IO.puts("Replied!")
|
|
||||||
else
|
|
||||||
_e -> IO.puts("Could not reply...")
|
|
||||||
end
|
|
||||||
|
|
||||||
state
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_command(%{user: user} = state, "p " <> text) do
|
|
||||||
text = String.trim(text)
|
|
||||||
|
|
||||||
with {:ok, _activity} <- CommonAPI.post(user, %{status: text}) do
|
|
||||||
IO.puts("Posted!")
|
|
||||||
else
|
|
||||||
_e -> IO.puts("Could not post...")
|
|
||||||
end
|
|
||||||
|
|
||||||
state
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_command(state, "home") do
|
|
||||||
user = state.user
|
|
||||||
|
|
||||||
params =
|
|
||||||
%{}
|
|
||||||
|> Map.put(:type, ["Create"])
|
|
||||||
|> Map.put(:blocking_user, user)
|
|
||||||
|> Map.put(:muting_user, user)
|
|
||||||
|> Map.put(:user, user)
|
|
||||||
|
|
||||||
activities =
|
|
||||||
[user.ap_id | Pleroma.User.following(user)]
|
|
||||||
|> ActivityPub.fetch_activities(params)
|
|
||||||
|
|
||||||
Enum.each(activities, fn activity ->
|
|
||||||
puts_activity(activity)
|
|
||||||
end)
|
|
||||||
|
|
||||||
state
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_command(state, command) do
|
|
||||||
IO.puts("Unknown command '#{command}'")
|
|
||||||
state
|
|
||||||
end
|
|
||||||
|
|
||||||
defp wait_input(state, input) do
|
|
||||||
receive do
|
|
||||||
{:input, ^input, "quit\n"} ->
|
|
||||||
IO.puts("Exiting...")
|
|
||||||
|
|
||||||
{:input, ^input, code} when is_binary(code) ->
|
|
||||||
code = String.trim(code)
|
|
||||||
|
|
||||||
state = handle_command(state, code)
|
|
||||||
|
|
||||||
loop(%{state | counter: state.counter + 1})
|
|
||||||
|
|
||||||
{:error, :interrupted} ->
|
|
||||||
IO.puts("Caught Ctrl+C...")
|
|
||||||
loop(%{state | counter: state.counter + 1})
|
|
||||||
|
|
||||||
{:input, ^input, msg} ->
|
|
||||||
:ok = Logger.warn("received unknown message: #{inspect(msg)}")
|
|
||||||
loop(%{state | counter: state.counter + 1})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_state(opts) do
|
|
||||||
%{prefix: "pleroma", counter: 1, user: opts[:user]}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp io_get(pid, prefix, counter, username) do
|
|
||||||
prompt = prompt(prefix, counter, username)
|
|
||||||
send(pid, {:input, self(), IO.gets(:stdio, prompt)})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp prompt(prefix, counter, username) do
|
|
||||||
prompt = "#{username}@#{prefix}:#{counter}>"
|
|
||||||
prompt <> " "
|
|
||||||
end
|
|
||||||
end
|
|
1
mix.exs
1
mix.exs
|
@ -179,7 +179,6 @@ defp deps do
|
||||||
{:joken, "~> 2.0"},
|
{:joken, "~> 2.0"},
|
||||||
{:benchee, "~> 1.0"},
|
{:benchee, "~> 1.0"},
|
||||||
{:pot, "~> 1.0"},
|
{:pot, "~> 1.0"},
|
||||||
{:esshd, "~> 0.1.0", runtime: Application.get_env(:esshd, :enabled, false)},
|
|
||||||
{:ex_const, "~> 0.2"},
|
{:ex_const, "~> 0.2"},
|
||||||
{:plug_static_index_html, "~> 1.0.0"},
|
{:plug_static_index_html, "~> 1.0.0"},
|
||||||
{:flake_id, "~> 0.1.0"},
|
{:flake_id, "~> 0.1.0"},
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.BBS.HandlerTest do
|
|
||||||
use Pleroma.DataCase, async: true
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.BBS.Handler
|
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
|
|
||||||
import ExUnit.CaptureIO
|
|
||||||
import Pleroma.Factory
|
|
||||||
import Ecto.Query
|
|
||||||
|
|
||||||
test "getting the home timeline" do
|
|
||||||
user = insert(:user)
|
|
||||||
followed = insert(:user)
|
|
||||||
|
|
||||||
{:ok, user, followed} = User.follow(user, followed)
|
|
||||||
|
|
||||||
{:ok, _first} = CommonAPI.post(user, %{status: "hey"})
|
|
||||||
{:ok, _second} = CommonAPI.post(followed, %{status: "hello"})
|
|
||||||
|
|
||||||
output =
|
|
||||||
capture_io(fn ->
|
|
||||||
Handler.handle_command(%{user: user}, "home")
|
|
||||||
end)
|
|
||||||
|
|
||||||
assert output =~ user.nickname
|
|
||||||
assert output =~ followed.nickname
|
|
||||||
|
|
||||||
assert output =~ "hey"
|
|
||||||
assert output =~ "hello"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "posting" do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
output =
|
|
||||||
capture_io(fn ->
|
|
||||||
Handler.handle_command(%{user: user}, "p this is a test post")
|
|
||||||
end)
|
|
||||||
|
|
||||||
assert output =~ "Posted"
|
|
||||||
|
|
||||||
activity =
|
|
||||||
Repo.one(
|
|
||||||
from(a in Activity,
|
|
||||||
where: fragment("?->>'type' = ?", a.data, "Create")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert activity.actor == user.ap_id
|
|
||||||
object = Object.normalize(activity, fetch: false)
|
|
||||||
assert object.data["content"] == "this is a test post"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "replying" do
|
|
||||||
user = insert(:user)
|
|
||||||
another_user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(another_user, %{status: "this is a test post"})
|
|
||||||
activity_object = Object.normalize(activity, fetch: false)
|
|
||||||
|
|
||||||
output =
|
|
||||||
capture_io(fn ->
|
|
||||||
Handler.handle_command(%{user: user}, "r #{activity.id} this is a reply")
|
|
||||||
end)
|
|
||||||
|
|
||||||
assert output =~ "Replied"
|
|
||||||
|
|
||||||
reply =
|
|
||||||
Repo.one(
|
|
||||||
from(a in Activity,
|
|
||||||
where: fragment("?->>'type' = ?", a.data, "Create"),
|
|
||||||
where: a.actor == ^user.ap_id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert reply.actor == user.ap_id
|
|
||||||
|
|
||||||
reply_object_data = Object.normalize(reply, fetch: false).data
|
|
||||||
assert reply_object_data["content"] == "this is a reply"
|
|
||||||
assert reply_object_data["inReplyTo"] == activity_object.data["id"]
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue