[#923] Merge remote-tracking branch 'remotes/upstream/develop' into twitter_oauth
# Conflicts: # config/config.exs # lib/pleroma/web/auth/pleroma_authenticator.ex
This commit is contained in:
commit
2a96283efb
|
@ -34,7 +34,7 @@
|
||||||
# Upload configuration
|
# Upload configuration
|
||||||
config :pleroma, Pleroma.Upload,
|
config :pleroma, Pleroma.Upload,
|
||||||
uploader: Pleroma.Uploaders.Local,
|
uploader: Pleroma.Uploaders.Local,
|
||||||
filters: [],
|
filters: [Pleroma.Upload.Filter.Dedupe],
|
||||||
link_name: true,
|
link_name: true,
|
||||||
proxy_remote: false,
|
proxy_remote: false,
|
||||||
proxy_opts: [
|
proxy_opts: [
|
||||||
|
@ -370,6 +370,17 @@
|
||||||
rel: false
|
rel: false
|
||||||
]
|
]
|
||||||
|
|
||||||
|
config :pleroma, :ldap,
|
||||||
|
enabled: System.get_env("LDAP_ENABLED") == "true",
|
||||||
|
host: System.get_env("LDAP_HOST") || "localhost",
|
||||||
|
port: String.to_integer(System.get_env("LDAP_PORT") || "389"),
|
||||||
|
ssl: System.get_env("LDAP_SSL") == "true",
|
||||||
|
sslopts: [],
|
||||||
|
tls: System.get_env("LDAP_TLS") == "true",
|
||||||
|
tlsopts: [],
|
||||||
|
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
|
||||||
|
uid: System.get_env("LDAP_UID") || "cn"
|
||||||
|
|
||||||
config :pleroma, :auth, oauth_consumer_enabled: false
|
config :pleroma, :auth, oauth_consumer_enabled: false
|
||||||
|
|
||||||
config :ueberauth,
|
config :ueberauth,
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# Print only warnings and errors during test
|
# Print only warnings and errors during test
|
||||||
config :logger, level: :warn
|
config :logger, level: :warn
|
||||||
|
|
||||||
config :pleroma, Pleroma.Upload, link_name: false
|
config :pleroma, Pleroma.Upload, filters: [], link_name: false
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# Custom emoji
|
||||||
|
|
||||||
To add custom emoji:
|
To add custom emoji:
|
||||||
* Add the image file(s) to `priv/static/emoji/custom`
|
* Add the image file(s) to `priv/static/emoji/custom`
|
||||||
* In case of conflicts: add the desired shortcode with the path to `config/custom_emoji.txt`, comma-separated and one per line
|
* In case of conflicts: add the desired shortcode with the path to `config/custom_emoji.txt`, comma-separated and one per line
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# Message Rewrite Facility configuration
|
||||||
The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
|
The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
|
||||||
|
|
||||||
Possible uses include:
|
Possible uses include:
|
||||||
|
|
|
@ -108,3 +108,11 @@ See [Admin-API](Admin-API.md)
|
||||||
* Response: JSON string. Returns the user flavour or the default one.
|
* Response: JSON string. Returns the user flavour or the default one.
|
||||||
* Example response: "glitch"
|
* Example response: "glitch"
|
||||||
* Note: This is intended to be used only by mastofe
|
* Note: This is intended to be used only by mastofe
|
||||||
|
|
||||||
|
## `/api/pleroma/notifications/read`
|
||||||
|
### Mark a single notification as read
|
||||||
|
* Method `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `id`: notifications's id
|
||||||
|
* Response: JSON. Returns `{"status": "success"}` if the reading was successful, otherwise returns `{"error": "error_msg"}`
|
||||||
|
|
|
@ -331,3 +331,26 @@ config :auto_linker,
|
||||||
rel: false
|
rel: false
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## :ldap
|
||||||
|
|
||||||
|
Use LDAP for user authentication. When a user logs in to the Pleroma
|
||||||
|
instance, the name and password will be verified by trying to authenticate
|
||||||
|
(bind) to an LDAP server. If a user exists in the LDAP directory but there
|
||||||
|
is no account with the same name yet on the Pleroma instance then a new
|
||||||
|
Pleroma account will be created with the same name as the LDAP user name.
|
||||||
|
|
||||||
|
* `enabled`: enables LDAP authentication
|
||||||
|
* `host`: LDAP server hostname
|
||||||
|
* `port`: LDAP port, e.g. 389 or 636
|
||||||
|
* `ssl`: true to use SSL, usually implies the port 636
|
||||||
|
* `sslopts`: additional SSL options
|
||||||
|
* `tls`: true to start TLS, usually implies the port 389
|
||||||
|
* `tlsopts`: additional TLS options
|
||||||
|
* `base`: LDAP base, e.g. "dc=example,dc=com"
|
||||||
|
* `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base"
|
||||||
|
|
||||||
|
## Pleroma.Web.Auth.Authenticator
|
||||||
|
|
||||||
|
* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator
|
||||||
|
* `Pleroma.Web.Auth.LDAPAuthenticator`: LDAP authentication
|
||||||
|
|
|
@ -95,6 +95,13 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||||
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||||
Meta.allow_tag_with_these_attributes("a", ["name", "title", "class"])
|
Meta.allow_tag_with_these_attributes("a", ["name", "title", "class"])
|
||||||
|
|
||||||
|
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
||||||
|
"tag",
|
||||||
|
"nofollow",
|
||||||
|
"noopener",
|
||||||
|
"noreferrer"
|
||||||
|
])
|
||||||
|
|
||||||
# paragraphs and linebreaks
|
# paragraphs and linebreaks
|
||||||
Meta.allow_tag_with_these_attributes("br", [])
|
Meta.allow_tag_with_these_attributes("br", [])
|
||||||
Meta.allow_tag_with_these_attributes("p", [])
|
Meta.allow_tag_with_these_attributes("p", [])
|
||||||
|
@ -137,6 +144,13 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||||
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||||
Meta.allow_tag_with_these_attributes("a", ["name", "title", "class"])
|
Meta.allow_tag_with_these_attributes("a", ["name", "title", "class"])
|
||||||
|
|
||||||
|
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
||||||
|
"tag",
|
||||||
|
"nofollow",
|
||||||
|
"noopener",
|
||||||
|
"noreferrer"
|
||||||
|
])
|
||||||
|
|
||||||
Meta.allow_tag_with_these_attributes("abbr", ["title"])
|
Meta.allow_tag_with_these_attributes("abbr", ["title"])
|
||||||
|
|
||||||
Meta.allow_tag_with_these_attributes("b", [])
|
Meta.allow_tag_with_these_attributes("b", [])
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Notification do
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
schema "notifications" do
|
schema "notifications" do
|
||||||
field(:seen, :boolean, default: false)
|
field(:seen, :boolean, default: false)
|
||||||
|
@ -22,6 +23,11 @@ defmodule Pleroma.Notification do
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def changeset(%Notification{} = notification, attrs) do
|
||||||
|
notification
|
||||||
|
|> cast(attrs, [:seen])
|
||||||
|
end
|
||||||
|
|
||||||
# TODO: Make generic and unify (see activity_pub.ex)
|
# TODO: Make generic and unify (see activity_pub.ex)
|
||||||
defp restrict_max(query, %{"max_id" => max_id}) do
|
defp restrict_max(query, %{"max_id" => max_id}) do
|
||||||
from(activity in query, where: activity.id < ^max_id)
|
from(activity in query, where: activity.id < ^max_id)
|
||||||
|
@ -68,6 +74,14 @@ def set_read_up_to(%{id: user_id} = _user, id) do
|
||||||
Repo.update_all(query, [])
|
Repo.update_all(query, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def read_one(%User{} = user, notification_id) do
|
||||||
|
with {:ok, %Notification{} = notification} <- get(user, notification_id) do
|
||||||
|
notification
|
||||||
|
|> changeset(%{seen: true})
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def get(%{id: user_id} = _user, id) do
|
def get(%{id: user_id} = _user, id) do
|
||||||
query =
|
query =
|
||||||
from(
|
from(
|
||||||
|
|
|
@ -311,7 +311,25 @@ defp build_resp_content_disposition_header(headers, opts) do
|
||||||
end
|
end
|
||||||
|
|
||||||
if attachment? do
|
if attachment? do
|
||||||
disposition = "attachment; filename=" <> Keyword.get(opts, :attachment_name, "attachment")
|
name =
|
||||||
|
try do
|
||||||
|
{{"content-disposition", content_disposition_string}, _} =
|
||||||
|
List.keytake(headers, "content-disposition", 0)
|
||||||
|
|
||||||
|
[name | _] =
|
||||||
|
Regex.run(
|
||||||
|
~r/filename="((?:[^"\\]|\\.)*)"/u,
|
||||||
|
content_disposition_string || "",
|
||||||
|
capture: :all_but_first
|
||||||
|
)
|
||||||
|
|
||||||
|
name
|
||||||
|
rescue
|
||||||
|
MatchError -> Keyword.get(opts, :attachment_name, "attachment")
|
||||||
|
end
|
||||||
|
|
||||||
|
disposition = "attachment; filename=\"#{name}\""
|
||||||
|
|
||||||
List.keystore(headers, "content-disposition", 0, {"content-disposition", disposition})
|
List.keystore(headers, "content-disposition", 0, {"content-disposition", disposition})
|
||||||
else
|
else
|
||||||
headers
|
headers
|
||||||
|
|
|
@ -1423,4 +1423,8 @@ defp paginate(query, page, page_size) do
|
||||||
offset: ^((page - 1) * page_size)
|
offset: ^((page - 1) * page_size)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def showing_reblogs?(%User{} = user, %User{} = target) do
|
||||||
|
target.ap_id not in user.info.muted_reblogs
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,6 +21,7 @@ defmodule Pleroma.User.Info do
|
||||||
field(:blocks, {:array, :string}, default: [])
|
field(:blocks, {:array, :string}, default: [])
|
||||||
field(:domain_blocks, {:array, :string}, default: [])
|
field(:domain_blocks, {:array, :string}, default: [])
|
||||||
field(:mutes, {:array, :string}, default: [])
|
field(:mutes, {:array, :string}, default: [])
|
||||||
|
field(:muted_reblogs, {:array, :string}, default: [])
|
||||||
field(:deactivated, :boolean, default: false)
|
field(:deactivated, :boolean, default: false)
|
||||||
field(:no_rich_text, :boolean, default: false)
|
field(:no_rich_text, :boolean, default: false)
|
||||||
field(:ap_enabled, :boolean, default: false)
|
field(:ap_enabled, :boolean, default: false)
|
||||||
|
@ -259,4 +260,16 @@ def roles(%Info{is_moderator: is_moderator, is_admin: is_admin}) do
|
||||||
moderator: is_moderator
|
moderator: is_moderator
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_reblog_mute(info, ap_id) do
|
||||||
|
params = %{muted_reblogs: info.muted_reblogs ++ [ap_id]}
|
||||||
|
|
||||||
|
cast(info, params, [:muted_reblogs])
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_reblog_mute(info, ap_id) do
|
||||||
|
params = %{muted_reblogs: List.delete(info.muted_reblogs, ap_id)}
|
||||||
|
|
||||||
|
cast(info, params, [:muted_reblogs])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -370,20 +370,38 @@ def flag(
|
||||||
content: content
|
content: content
|
||||||
} = params
|
} = params
|
||||||
) do
|
) do
|
||||||
additional = params[:additional] || %{}
|
|
||||||
|
|
||||||
# only accept false as false value
|
# only accept false as false value
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
|
forward = !(params[:forward] == false)
|
||||||
|
|
||||||
%{
|
additional = params[:additional] || %{}
|
||||||
|
|
||||||
|
params = %{
|
||||||
actor: actor,
|
actor: actor,
|
||||||
context: context,
|
context: context,
|
||||||
account: account,
|
account: account,
|
||||||
statuses: statuses,
|
statuses: statuses,
|
||||||
content: content
|
content: content
|
||||||
}
|
}
|
||||||
|> make_flag_data(additional)
|
|
||||||
|> insert(local)
|
additional =
|
||||||
|
if forward do
|
||||||
|
Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
|
||||||
|
else
|
||||||
|
Map.merge(additional, %{"to" => [], "cc" => []})
|
||||||
|
end
|
||||||
|
|
||||||
|
with flag_data <- make_flag_data(params, additional),
|
||||||
|
{:ok, activity} <- insert(flag_data, local),
|
||||||
|
:ok <- maybe_federate(activity) do
|
||||||
|
Enum.each(User.all_superusers(), fn superuser ->
|
||||||
|
superuser
|
||||||
|
|> Pleroma.AdminEmail.report(actor, account, statuses, content)
|
||||||
|
|> Pleroma.Mailer.deliver_async()
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, activity}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities_for_context(context, opts \\ %{}) do
|
def fetch_activities_for_context(context, opts \\ %{}) do
|
||||||
|
@ -679,6 +697,18 @@ defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids})
|
||||||
|
|
||||||
defp restrict_pinned(query, _), do: query
|
defp restrict_pinned(query, _), do: query
|
||||||
|
|
||||||
|
defp restrict_muted_reblogs(query, %{"muting_user" => %User{info: info}}) do
|
||||||
|
muted_reblogs = info.muted_reblogs || []
|
||||||
|
|
||||||
|
from(
|
||||||
|
activity in query,
|
||||||
|
where: fragment("not ?->>'type' = 'Announce'", activity.data),
|
||||||
|
where: fragment("not ? = ANY(?)", activity.actor, ^muted_reblogs)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_muted_reblogs(query, _), do: query
|
||||||
|
|
||||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
base_query =
|
base_query =
|
||||||
from(
|
from(
|
||||||
|
@ -706,6 +736,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_replies(opts)
|
|> restrict_replies(opts)
|
||||||
|> restrict_reblogs(opts)
|
|> restrict_reblogs(opts)
|
||||||
|> restrict_pinned(opts)
|
|> restrict_pinned(opts)
|
||||||
|
|> restrict_muted_reblogs(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities(recipients, opts \\ %{}) do
|
def fetch_activities(recipients, opts \\ %{}) do
|
||||||
|
|
|
@ -355,6 +355,40 @@ defp get_follow_activity(follow_object, followed) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Flag objects are placed ahead of the ID check because Mastodon 2.8 and earlier send them
|
||||||
|
# with nil ID.
|
||||||
|
def handle_incoming(%{"type" => "Flag", "object" => objects, "actor" => actor} = data) do
|
||||||
|
with context <- data["context"] || Utils.generate_context_id(),
|
||||||
|
content <- data["content"] || "",
|
||||||
|
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||||
|
|
||||||
|
# Reduce the object list to find the reported user.
|
||||||
|
%User{} = account <-
|
||||||
|
Enum.reduce_while(objects, nil, fn ap_id, _ ->
|
||||||
|
with %User{} = user <- User.get_cached_by_ap_id(ap_id) do
|
||||||
|
{:halt, user}
|
||||||
|
else
|
||||||
|
_ -> {:cont, nil}
|
||||||
|
end
|
||||||
|
end),
|
||||||
|
|
||||||
|
# Remove the reported user from the object list.
|
||||||
|
statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do
|
||||||
|
params = %{
|
||||||
|
actor: actor,
|
||||||
|
context: context,
|
||||||
|
account: account,
|
||||||
|
statuses: statuses,
|
||||||
|
content: content,
|
||||||
|
additional: %{
|
||||||
|
"cc" => [account.ap_id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivityPub.flag(params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# disallow objects with bogus IDs
|
# disallow objects with bogus IDs
|
||||||
def handle_incoming(%{"id" => nil}), do: :error
|
def handle_incoming(%{"id" => nil}), do: :error
|
||||||
def handle_incoming(%{"id" => ""}), do: :error
|
def handle_incoming(%{"id" => ""}), do: :error
|
||||||
|
|
|
@ -621,7 +621,13 @@ def make_create_data(params, additional) do
|
||||||
#### Flag-related helpers
|
#### Flag-related helpers
|
||||||
|
|
||||||
def make_flag_data(params, additional) do
|
def make_flag_data(params, additional) do
|
||||||
status_ap_ids = Enum.map(params.statuses || [], & &1.data["id"])
|
status_ap_ids =
|
||||||
|
Enum.map(params.statuses || [], fn
|
||||||
|
%Activity{} = act -> act.data["id"]
|
||||||
|
act when is_map(act) -> act["id"]
|
||||||
|
act when is_binary(act) -> act
|
||||||
|
end)
|
||||||
|
|
||||||
object = [params.account.ap_id] ++ status_ap_ids
|
object = [params.account.ap_id] ++ status_ap_ids
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
|
145
lib/pleroma/web/auth/ldap_authenticator.ex
Normal file
145
lib/pleroma/web/auth/ldap_authenticator.ex
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@behaviour Pleroma.Web.Auth.Authenticator
|
||||||
|
|
||||||
|
@connection_timeout 10_000
|
||||||
|
@search_timeout 10_000
|
||||||
|
|
||||||
|
def get_user(%Plug.Conn{} = conn, params) do
|
||||||
|
if Pleroma.Config.get([:ldap, :enabled]) do
|
||||||
|
{name, password} =
|
||||||
|
case params do
|
||||||
|
%{"authorization" => %{"name" => name, "password" => password}} ->
|
||||||
|
{name, password}
|
||||||
|
|
||||||
|
%{"grant_type" => "password", "username" => name, "password" => password} ->
|
||||||
|
{name, password}
|
||||||
|
end
|
||||||
|
|
||||||
|
case ldap_user(name, password) do
|
||||||
|
%User{} = user ->
|
||||||
|
{:ok, user}
|
||||||
|
|
||||||
|
{:error, {:ldap_connection_error, _}} ->
|
||||||
|
# When LDAP is unavailable, try default authenticator
|
||||||
|
Pleroma.Web.Auth.PleromaAuthenticator.get_user(conn, params)
|
||||||
|
|
||||||
|
error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Fall back to default authenticator
|
||||||
|
Pleroma.Web.Auth.PleromaAuthenticator.get_user(conn, params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_or_create_user_by_oauth(conn, params), do: get_user(conn, params)
|
||||||
|
|
||||||
|
def handle_error(%Plug.Conn{} = _conn, error) do
|
||||||
|
error
|
||||||
|
end
|
||||||
|
|
||||||
|
def auth_template, do: nil
|
||||||
|
|
||||||
|
defp ldap_user(name, password) do
|
||||||
|
ldap = Pleroma.Config.get(:ldap, [])
|
||||||
|
host = Keyword.get(ldap, :host, "localhost")
|
||||||
|
port = Keyword.get(ldap, :port, 389)
|
||||||
|
ssl = Keyword.get(ldap, :ssl, false)
|
||||||
|
sslopts = Keyword.get(ldap, :sslopts, [])
|
||||||
|
|
||||||
|
options =
|
||||||
|
[{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] ++
|
||||||
|
if sslopts != [], do: [{:sslopts, sslopts}], else: []
|
||||||
|
|
||||||
|
case :eldap.open([to_charlist(host)], options) do
|
||||||
|
{:ok, connection} ->
|
||||||
|
try do
|
||||||
|
if Keyword.get(ldap, :tls, false) do
|
||||||
|
:application.ensure_all_started(:ssl)
|
||||||
|
|
||||||
|
case :eldap.start_tls(
|
||||||
|
connection,
|
||||||
|
Keyword.get(ldap, :tlsopts, []),
|
||||||
|
@connection_timeout
|
||||||
|
) do
|
||||||
|
:ok ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
error ->
|
||||||
|
Logger.error("Could not start TLS: #{inspect(error)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
bind_user(connection, ldap, name, password)
|
||||||
|
after
|
||||||
|
:eldap.close(connection)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
Logger.error("Could not open LDAP connection: #{inspect(error)}")
|
||||||
|
{:error, {:ldap_connection_error, error}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp bind_user(connection, ldap, name, password) do
|
||||||
|
uid = Keyword.get(ldap, :uid, "cn")
|
||||||
|
base = Keyword.get(ldap, :base)
|
||||||
|
|
||||||
|
case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do
|
||||||
|
:ok ->
|
||||||
|
case User.get_by_nickname_or_email(name) do
|
||||||
|
%User{} = user ->
|
||||||
|
user
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
register_user(connection, base, uid, name, password)
|
||||||
|
end
|
||||||
|
|
||||||
|
error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp register_user(connection, base, uid, name, password) do
|
||||||
|
case :eldap.search(connection, [
|
||||||
|
{:base, to_charlist(base)},
|
||||||
|
{:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))},
|
||||||
|
{:scope, :eldap.wholeSubtree()},
|
||||||
|
{:attributes, ['mail', 'email']},
|
||||||
|
{:timeout, @search_timeout}
|
||||||
|
]) do
|
||||||
|
{:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _}} ->
|
||||||
|
with {_, [mail]} <- List.keyfind(attributes, 'mail', 0) do
|
||||||
|
params = %{
|
||||||
|
email: :erlang.list_to_binary(mail),
|
||||||
|
name: name,
|
||||||
|
nickname: name,
|
||||||
|
password: password,
|
||||||
|
password_confirmation: password
|
||||||
|
}
|
||||||
|
|
||||||
|
changeset = User.register_changeset(%User{}, params)
|
||||||
|
|
||||||
|
case User.register(changeset) do
|
||||||
|
{:ok, user} -> user
|
||||||
|
error -> error
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Logger.error("Could not find LDAP attribute mail: #{inspect(attributes)}")
|
||||||
|
{:error, :ldap_registration_missing_attributes}
|
||||||
|
end
|
||||||
|
|
||||||
|
error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,9 +8,16 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
||||||
|
|
||||||
@behaviour Pleroma.Web.Auth.Authenticator
|
@behaviour Pleroma.Web.Auth.Authenticator
|
||||||
|
|
||||||
def get_user(%Plug.Conn{} = _conn, %{
|
def get_user(%Plug.Conn{} = _conn, params) do
|
||||||
"authorization" => %{"name" => name, "password" => password}
|
{name, password} =
|
||||||
}) do
|
case params do
|
||||||
|
%{"authorization" => %{"name" => name, "password" => password}} ->
|
||||||
|
{name, password}
|
||||||
|
|
||||||
|
%{"grant_type" => "password", "username" => name, "password" => password} ->
|
||||||
|
{name, password}
|
||||||
|
end
|
||||||
|
|
||||||
with {_, %User{} = user} <- {:user, User.get_by_nickname_or_email(name)},
|
with {_, %User{} = user} <- {:user, User.get_by_nickname_or_email(name)},
|
||||||
{_, true} <- {:checkpw, Pbkdf2.checkpw(password, user.password_hash)} do
|
{_, true} <- {:checkpw, Pbkdf2.checkpw(password, user.password_hash)} do
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
|
@ -20,8 +27,6 @@ def get_user(%Plug.Conn{} = _conn, %{
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_user(%Plug.Conn{} = _conn, _params), do: {:error, :missing_credentials}
|
|
||||||
|
|
||||||
def get_or_create_user_by_oauth(
|
def get_or_create_user_by_oauth(
|
||||||
%Plug.Conn{assigns: %{ueberauth_auth: %{provider: provider, uid: uid} = auth}},
|
%Plug.Conn{assigns: %{ueberauth_auth: %{provider: provider, uid: uid} = auth}},
|
||||||
_params
|
_params
|
||||||
|
|
|
@ -284,14 +284,9 @@ def report(user, data) do
|
||||||
actor: user,
|
actor: user,
|
||||||
account: account,
|
account: account,
|
||||||
statuses: statuses,
|
statuses: statuses,
|
||||||
content: content_html
|
content: content_html,
|
||||||
|
forward: data["forward"] || false
|
||||||
}) do
|
}) do
|
||||||
Enum.each(User.all_superusers(), fn superuser ->
|
|
||||||
superuser
|
|
||||||
|> Pleroma.AdminEmail.report(user, account, statuses, content_html)
|
|
||||||
|> Pleroma.Mailer.deliver_async()
|
|
||||||
end)
|
|
||||||
|
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
{:error, err} -> {:error, err}
|
{:error, err} -> {:error, err}
|
||||||
|
@ -299,4 +294,24 @@ def report(user, data) do
|
||||||
{:account, nil} -> {:error, "Account not found"}
|
{:account, nil} -> {:error, "Account not found"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def hide_reblogs(user, muted) do
|
||||||
|
ap_id = muted.ap_id
|
||||||
|
|
||||||
|
if ap_id not in user.info.muted_reblogs do
|
||||||
|
info_changeset = User.Info.add_reblog_mute(user.info, ap_id)
|
||||||
|
changeset = Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset)
|
||||||
|
User.update_and_set_cache(changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_reblogs(user, muted) do
|
||||||
|
ap_id = muted.ap_id
|
||||||
|
|
||||||
|
if ap_id in user.info.muted_reblogs do
|
||||||
|
info_changeset = User.Info.remove_reblog_mute(user.info, ap_id)
|
||||||
|
changeset = Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset)
|
||||||
|
User.update_and_set_cache(changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -723,11 +723,25 @@ def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) d
|
||||||
|
|
||||||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
with %User{} = followed <- Repo.get(User, id),
|
with %User{} = followed <- Repo.get(User, id),
|
||||||
|
false <- User.following?(follower, followed),
|
||||||
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("relationship.json", %{user: follower, target: followed})
|
|> render("relationship.json", %{user: follower, target: followed})
|
||||||
else
|
else
|
||||||
|
true ->
|
||||||
|
followed = User.get_cached_by_id(id)
|
||||||
|
|
||||||
|
{:ok, follower} =
|
||||||
|
case conn.params["reblogs"] do
|
||||||
|
true -> CommonAPI.show_reblogs(follower, followed)
|
||||||
|
false -> CommonAPI.hide_reblogs(follower, followed)
|
||||||
|
end
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("relationship.json", %{user: follower, target: followed})
|
||||||
|
|
||||||
{:error, message} ->
|
{:error, message} ->
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/json")
|
|> put_resp_content_type("application/json")
|
||||||
|
|
|
@ -55,7 +55,7 @@ def render("relationship.json", %{user: %User{} = user, target: %User{} = target
|
||||||
muting_notifications: false,
|
muting_notifications: false,
|
||||||
requested: requested,
|
requested: requested,
|
||||||
domain_blocking: false,
|
domain_blocking: false,
|
||||||
showing_reblogs: false,
|
showing_reblogs: User.showing_reblogs?(user, target),
|
||||||
endorsed: false
|
endorsed: false
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
defmodule Pleroma.Web.OAuth.OAuthController do
|
defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Comeonin.Pbkdf2
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.Auth.Authenticator
|
alias Pleroma.Web.Auth.Authenticator
|
||||||
|
@ -154,6 +153,7 @@ def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do
|
||||||
fixed_token = fix_padding(params["code"]),
|
fixed_token = fix_padding(params["code"]),
|
||||||
%Authorization{} = auth <-
|
%Authorization{} = auth <-
|
||||||
Repo.get_by(Authorization, token: fixed_token, app_id: app.id),
|
Repo.get_by(Authorization, token: fixed_token, app_id: app.id),
|
||||||
|
%User{} = user <- Repo.get(User, auth.user_id),
|
||||||
{:ok, token} <- Token.exchange_token(app, auth),
|
{:ok, token} <- Token.exchange_token(app, auth),
|
||||||
{:ok, inserted_at} <- DateTime.from_naive(token.inserted_at, "Etc/UTC") do
|
{:ok, inserted_at} <- DateTime.from_naive(token.inserted_at, "Etc/UTC") do
|
||||||
response = %{
|
response = %{
|
||||||
|
@ -162,7 +162,8 @@ def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do
|
||||||
refresh_token: token.refresh_token,
|
refresh_token: token.refresh_token,
|
||||||
created_at: DateTime.to_unix(inserted_at),
|
created_at: DateTime.to_unix(inserted_at),
|
||||||
expires_in: 60 * 10,
|
expires_in: 60 * 10,
|
||||||
scope: Enum.join(token.scopes, " ")
|
scope: Enum.join(token.scopes, " "),
|
||||||
|
me: user.ap_id
|
||||||
}
|
}
|
||||||
|
|
||||||
json(conn, response)
|
json(conn, response)
|
||||||
|
@ -175,11 +176,10 @@ def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do
|
||||||
|
|
||||||
def token_exchange(
|
def token_exchange(
|
||||||
conn,
|
conn,
|
||||||
%{"grant_type" => "password", "username" => name, "password" => password} = params
|
%{"grant_type" => "password"} = params
|
||||||
) do
|
) do
|
||||||
with %App{} = app <- get_app_from_request(conn, params),
|
with {_, {:ok, %User{} = user}} <- {:get_user, Authenticator.get_user(conn, params)},
|
||||||
%User{} = user <- User.get_by_nickname_or_email(name),
|
%App{} = app <- get_app_from_request(conn, params),
|
||||||
true <- Pbkdf2.checkpw(password, user.password_hash),
|
|
||||||
{:auth_active, true} <- {:auth_active, User.auth_active?(user)},
|
{:auth_active, true} <- {:auth_active, User.auth_active?(user)},
|
||||||
scopes <- oauth_scopes(params, app.scopes),
|
scopes <- oauth_scopes(params, app.scopes),
|
||||||
[] <- scopes -- app.scopes,
|
[] <- scopes -- app.scopes,
|
||||||
|
@ -191,7 +191,8 @@ def token_exchange(
|
||||||
access_token: token.token,
|
access_token: token.token,
|
||||||
refresh_token: token.refresh_token,
|
refresh_token: token.refresh_token,
|
||||||
expires_in: 60 * 10,
|
expires_in: 60 * 10,
|
||||||
scope: Enum.join(token.scopes, " ")
|
scope: Enum.join(token.scopes, " "),
|
||||||
|
me: user.ap_id
|
||||||
}
|
}
|
||||||
|
|
||||||
json(conn, response)
|
json(conn, response)
|
||||||
|
|
|
@ -195,6 +195,12 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/blocks_import", UtilController, :blocks_import)
|
post("/blocks_import", UtilController, :blocks_import)
|
||||||
post("/follow_import", UtilController, :follow_import)
|
post("/follow_import", UtilController, :follow_import)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope [] do
|
||||||
|
pipe_through(:oauth_read)
|
||||||
|
|
||||||
|
post("/notifications/read", UtilController, :notifications_read)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/oauth", Pleroma.Web.OAuth do
|
scope "/oauth", Pleroma.Web.OAuth do
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.Streamer do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.MastodonAPI.NotificationView
|
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||||
|
|
||||||
|
@ -199,10 +200,12 @@ def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = ite
|
||||||
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
|
||||||
blocks = user.info.blocks || []
|
blocks = user.info.blocks || []
|
||||||
mutes = user.info.mutes || []
|
mutes = user.info.mutes || []
|
||||||
|
reblog_mutes = user.info.muted_reblogs || []
|
||||||
|
|
||||||
parent = Object.normalize(item.data["object"])
|
parent = Object.normalize(item.data["object"])
|
||||||
|
|
||||||
unless is_nil(parent) or item.actor in blocks or item.actor in mutes or
|
unless is_nil(parent) or item.actor in blocks or item.actor in mutes or
|
||||||
|
item.actor in reblog_mutes or not ActivityPub.contain_activity(item, user) or
|
||||||
parent.data["actor"] in blocks or parent.data["actor"] in mutes do
|
parent.data["actor"] in blocks or parent.data["actor"] in mutes do
|
||||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||||
end
|
end
|
||||||
|
@ -233,7 +236,8 @@ def push_to_socket(topics, topic, item) do
|
||||||
blocks = user.info.blocks || []
|
blocks = user.info.blocks || []
|
||||||
mutes = user.info.mutes || []
|
mutes = user.info.mutes || []
|
||||||
|
|
||||||
unless item.actor in blocks or item.actor in mutes do
|
unless item.actor in blocks or item.actor in mutes or
|
||||||
|
not ActivityPub.contain_activity(item, user) do
|
||||||
send(socket.transport_pid, {:text, represent_update(item, user)})
|
send(socket.transport_pid, {:text, represent_update(item, user)})
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
|
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.PasswordResetToken
|
alias Pleroma.PasswordResetToken
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -142,6 +143,17 @@ def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
|
||||||
|
with {:ok, _} <- Notification.read_one(user, notification_id) do
|
||||||
|
json(conn, %{status: "success"})
|
||||||
|
else
|
||||||
|
{:error, message} ->
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/json")
|
||||||
|
|> send_resp(403, Jason.encode!(%{"error" => message}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def config(conn, _params) do
|
def config(conn, _params) do
|
||||||
instance = Pleroma.Config.get(:instance)
|
instance = Pleroma.Config.get(:instance)
|
||||||
instance_fe = Pleroma.Config.get(:fe)
|
instance_fe = Pleroma.Config.get(:fe)
|
||||||
|
|
9
mix.exs
9
mix.exs
|
@ -23,11 +23,14 @@ def project do
|
||||||
logo: "priv/static/static/logo.png",
|
logo: "priv/static/static/logo.png",
|
||||||
extras: [
|
extras: [
|
||||||
"README.md",
|
"README.md",
|
||||||
"docs/config.md",
|
|
||||||
"docs/Pleroma-API.md",
|
|
||||||
"docs/Admin-API.md",
|
"docs/Admin-API.md",
|
||||||
"docs/Clients.md",
|
"docs/Clients.md",
|
||||||
"docs/Differences-in-MastodonAPI-Responses.md"
|
"docs/config.md",
|
||||||
|
"docs/Custom-Emoji.md",
|
||||||
|
"docs/Differences-in-MastodonAPI-Responses.md",
|
||||||
|
"docs/Message-Rewrite-Facility-configuration.md",
|
||||||
|
"docs/Pleroma-API.md",
|
||||||
|
"docs/static_dir.md"
|
||||||
],
|
],
|
||||||
main: "readme",
|
main: "readme",
|
||||||
output: "priv/static/doc"
|
output: "priv/static/doc"
|
||||||
|
|
|
@ -10,6 +10,8 @@ defmodule Pleroma.HTMLTest do
|
||||||
<b>this is in bold</b>
|
<b>this is in bold</b>
|
||||||
<p>this is a paragraph</p>
|
<p>this is a paragraph</p>
|
||||||
this is a linebreak<br />
|
this is a linebreak<br />
|
||||||
|
this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
|
||||||
|
this is a link with not allowed "rel" attribute: <a href="http://example.com/" rel="tag noallowed">example.com</a>
|
||||||
this is an image: <img src="http://example.com/image.jpg"><br />
|
this is an image: <img src="http://example.com/image.jpg"><br />
|
||||||
<script>alert('hacked')</script>
|
<script>alert('hacked')</script>
|
||||||
"""
|
"""
|
||||||
|
@ -24,6 +26,8 @@ test "works as expected" do
|
||||||
this is in bold
|
this is in bold
|
||||||
this is a paragraph
|
this is a paragraph
|
||||||
this is a linebreak
|
this is a linebreak
|
||||||
|
this is a link with allowed "rel" attribute: example.com
|
||||||
|
this is a link with not allowed "rel" attribute: example.com
|
||||||
this is an image:
|
this is an image:
|
||||||
alert('hacked')
|
alert('hacked')
|
||||||
"""
|
"""
|
||||||
|
@ -44,6 +48,8 @@ test "normalizes HTML as expected" do
|
||||||
this is in bold
|
this is in bold
|
||||||
<p>this is a paragraph</p>
|
<p>this is a paragraph</p>
|
||||||
this is a linebreak<br />
|
this is a linebreak<br />
|
||||||
|
this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
|
||||||
|
this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
|
||||||
this is an image: <img src="http://example.com/image.jpg" /><br />
|
this is an image: <img src="http://example.com/image.jpg" /><br />
|
||||||
alert('hacked')
|
alert('hacked')
|
||||||
"""
|
"""
|
||||||
|
@ -66,6 +72,8 @@ test "normalizes HTML as expected" do
|
||||||
<b>this is in bold</b>
|
<b>this is in bold</b>
|
||||||
<p>this is a paragraph</p>
|
<p>this is a paragraph</p>
|
||||||
this is a linebreak<br />
|
this is a linebreak<br />
|
||||||
|
this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
|
||||||
|
this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
|
||||||
this is an image: <img src="http://example.com/image.jpg" /><br />
|
this is an image: <img src="http://example.com/image.jpg" /><br />
|
||||||
alert('hacked')
|
alert('hacked')
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -424,6 +424,19 @@ test "retrieves ids up to max_id" do
|
||||||
assert length(activities) == 20
|
assert length(activities) == 20
|
||||||
assert last == last_expected
|
assert last == last_expected
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "doesn't return reblogs for users for whom reblogs have been muted" do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
user = insert(:user)
|
||||||
|
booster = insert(:user)
|
||||||
|
{:ok, user} = CommonAPI.hide_reblogs(user, booster)
|
||||||
|
|
||||||
|
{:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
|
||||||
|
|
||||||
|
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
||||||
|
|
||||||
|
refute Enum.member?(activities, activity)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "like an object" do
|
describe "like an object" do
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -764,6 +765,30 @@ test "it remaps video URLs as attachments if necessary" do
|
||||||
|
|
||||||
assert object.data["attachment"] == [attachment]
|
assert object.data["attachment"] == [attachment]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it accepts Flag activities" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
|
||||||
|
object = Object.normalize(activity.data["object"])
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"cc" => [user.ap_id],
|
||||||
|
"object" => [user.ap_id, object.data["id"]],
|
||||||
|
"type" => "Flag",
|
||||||
|
"content" => "blocked AND reported!!!",
|
||||||
|
"actor" => other_user.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
||||||
|
|
||||||
|
assert activity.data["object"] == [user.ap_id, object.data["id"]]
|
||||||
|
assert activity.data["content"] == "blocked AND reported!!!"
|
||||||
|
assert activity.data["actor"] == other_user.ap_id
|
||||||
|
assert activity.data["cc"] == [user.ap_id]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "prepare outgoing" do
|
describe "prepare outgoing" do
|
||||||
|
|
|
@ -221,4 +221,27 @@ test "creates a report" do
|
||||||
} = flag_activity
|
} = flag_activity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "reblog muting" do
|
||||||
|
setup do
|
||||||
|
muter = insert(:user)
|
||||||
|
|
||||||
|
muted = insert(:user)
|
||||||
|
|
||||||
|
[muter: muter, muted: muted]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "add a reblog mute", %{muter: muter, muted: muted} do
|
||||||
|
{:ok, muter} = CommonAPI.hide_reblogs(muter, muted)
|
||||||
|
|
||||||
|
assert Pleroma.User.showing_reblogs?(muter, muted) == false
|
||||||
|
end
|
||||||
|
|
||||||
|
test "remove a reblog mute", %{muter: muter, muted: muted} do
|
||||||
|
{:ok, muter} = CommonAPI.hide_reblogs(muter, muted)
|
||||||
|
{:ok, muter} = CommonAPI.show_reblogs(muter, muted)
|
||||||
|
|
||||||
|
assert Pleroma.User.showing_reblogs?(muter, muted) == true
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -144,7 +144,7 @@ test "represent a relationship" do
|
||||||
muting_notifications: false,
|
muting_notifications: false,
|
||||||
requested: false,
|
requested: false,
|
||||||
domain_blocking: false,
|
domain_blocking: false,
|
||||||
showing_reblogs: false,
|
showing_reblogs: true,
|
||||||
endorsed: false
|
endorsed: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ test "represent an embedded relationship" do
|
||||||
muting_notifications: false,
|
muting_notifications: false,
|
||||||
requested: false,
|
requested: false,
|
||||||
domain_blocking: false,
|
domain_blocking: false,
|
||||||
showing_reblogs: false,
|
showing_reblogs: true,
|
||||||
endorsed: false
|
endorsed: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1632,7 +1632,7 @@ test "updates the user's bio", %{conn: conn} do
|
||||||
assert user = json_response(conn, 200)
|
assert user = json_response(conn, 200)
|
||||||
|
|
||||||
assert user["note"] ==
|
assert user["note"] ==
|
||||||
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a data-user=") <>
|
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe" rel="tag">#cofe</a> with <span class="h-card"><a data-user=") <>
|
||||||
user2.id <>
|
user2.id <>
|
||||||
~s(" class="u-url mention" href=") <>
|
~s(" class="u-url mention" href=") <>
|
||||||
user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
|
user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
|
||||||
|
|
189
test/web/oauth/ldap_authorization_test.exs
Normal file
189
test/web/oauth/ldap_authorization_test.exs
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
import Pleroma.Factory
|
||||||
|
import ExUnit.CaptureLog
|
||||||
|
import Mock
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
ldap_authenticator =
|
||||||
|
Pleroma.Config.get(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator)
|
||||||
|
|
||||||
|
ldap_enabled = Pleroma.Config.get([:ldap, :enabled])
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Pleroma.Config.put(Pleroma.Web.Auth.Authenticator, ldap_authenticator)
|
||||||
|
Pleroma.Config.put([:ldap, :enabled], ldap_enabled)
|
||||||
|
end)
|
||||||
|
|
||||||
|
Pleroma.Config.put(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.LDAPAuthenticator)
|
||||||
|
Pleroma.Config.put([:ldap, :enabled], true)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "authorizes the existing user using LDAP credentials" do
|
||||||
|
password = "testpassword"
|
||||||
|
user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
|
||||||
|
app = insert(:oauth_app, scopes: ["read", "write"])
|
||||||
|
|
||||||
|
host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
|
||||||
|
port = Pleroma.Config.get([:ldap, :port])
|
||||||
|
|
||||||
|
with_mocks [
|
||||||
|
{:eldap, [],
|
||||||
|
[
|
||||||
|
open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
|
||||||
|
simple_bind: fn _connection, _dn, ^password -> :ok end,
|
||||||
|
close: fn _connection ->
|
||||||
|
send(self(), :close_connection)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
]}
|
||||||
|
] do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> post("/oauth/token", %{
|
||||||
|
"grant_type" => "password",
|
||||||
|
"username" => user.nickname,
|
||||||
|
"password" => password,
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"client_secret" => app.client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"access_token" => token} = json_response(conn, 200)
|
||||||
|
|
||||||
|
token = Repo.get_by(Token, token: token)
|
||||||
|
|
||||||
|
assert token.user_id == user.id
|
||||||
|
assert_received :close_connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates a new user after successful LDAP authorization" do
|
||||||
|
password = "testpassword"
|
||||||
|
user = build(:user)
|
||||||
|
app = insert(:oauth_app, scopes: ["read", "write"])
|
||||||
|
|
||||||
|
host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
|
||||||
|
port = Pleroma.Config.get([:ldap, :port])
|
||||||
|
|
||||||
|
with_mocks [
|
||||||
|
{:eldap, [],
|
||||||
|
[
|
||||||
|
open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
|
||||||
|
simple_bind: fn _connection, _dn, ^password -> :ok end,
|
||||||
|
equalityMatch: fn _type, _value -> :ok end,
|
||||||
|
wholeSubtree: fn -> :ok end,
|
||||||
|
search: fn _connection, _options ->
|
||||||
|
{:ok,
|
||||||
|
{:eldap_search_result, [{:eldap_entry, '', [{'mail', [to_charlist(user.email)]}]}],
|
||||||
|
[]}}
|
||||||
|
end,
|
||||||
|
close: fn _connection ->
|
||||||
|
send(self(), :close_connection)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
]}
|
||||||
|
] do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> post("/oauth/token", %{
|
||||||
|
"grant_type" => "password",
|
||||||
|
"username" => user.nickname,
|
||||||
|
"password" => password,
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"client_secret" => app.client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"access_token" => token} = json_response(conn, 200)
|
||||||
|
|
||||||
|
token = Repo.get_by(Token, token: token) |> Repo.preload(:user)
|
||||||
|
|
||||||
|
assert token.user.nickname == user.nickname
|
||||||
|
assert_received :close_connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "falls back to the default authorization when LDAP is unavailable" do
|
||||||
|
password = "testpassword"
|
||||||
|
user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
|
||||||
|
app = insert(:oauth_app, scopes: ["read", "write"])
|
||||||
|
|
||||||
|
host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
|
||||||
|
port = Pleroma.Config.get([:ldap, :port])
|
||||||
|
|
||||||
|
with_mocks [
|
||||||
|
{:eldap, [],
|
||||||
|
[
|
||||||
|
open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:error, 'connect failed'} end,
|
||||||
|
simple_bind: fn _connection, _dn, ^password -> :ok end,
|
||||||
|
close: fn _connection ->
|
||||||
|
send(self(), :close_connection)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
]}
|
||||||
|
] do
|
||||||
|
log =
|
||||||
|
capture_log(fn ->
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> post("/oauth/token", %{
|
||||||
|
"grant_type" => "password",
|
||||||
|
"username" => user.nickname,
|
||||||
|
"password" => password,
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"client_secret" => app.client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"access_token" => token} = json_response(conn, 200)
|
||||||
|
|
||||||
|
token = Repo.get_by(Token, token: token)
|
||||||
|
|
||||||
|
assert token.user_id == user.id
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert log =~ "Could not open LDAP connection: 'connect failed'"
|
||||||
|
refute_received :close_connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "disallow authorization for wrong LDAP credentials" do
|
||||||
|
password = "testpassword"
|
||||||
|
user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
|
||||||
|
app = insert(:oauth_app, scopes: ["read", "write"])
|
||||||
|
|
||||||
|
host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
|
||||||
|
port = Pleroma.Config.get([:ldap, :port])
|
||||||
|
|
||||||
|
with_mocks [
|
||||||
|
{:eldap, [],
|
||||||
|
[
|
||||||
|
open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
|
||||||
|
simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end,
|
||||||
|
close: fn _connection ->
|
||||||
|
send(self(), :close_connection)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
]}
|
||||||
|
] do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> post("/oauth/token", %{
|
||||||
|
"grant_type" => "password",
|
||||||
|
"username" => user.nickname,
|
||||||
|
"password" => password,
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"client_secret" => app.client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"error" => "Invalid credentials"} = json_response(conn, 400)
|
||||||
|
assert_received :close_connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -132,11 +132,12 @@ test "issues a token for an all-body request" do
|
||||||
"client_secret" => app.client_secret
|
"client_secret" => app.client_secret
|
||||||
})
|
})
|
||||||
|
|
||||||
assert %{"access_token" => token} = json_response(conn, 200)
|
assert %{"access_token" => token, "me" => ap_id} = json_response(conn, 200)
|
||||||
|
|
||||||
token = Repo.get_by(Token, token: token)
|
token = Repo.get_by(Token, token: token)
|
||||||
assert token
|
assert token
|
||||||
assert token.scopes == auth.scopes
|
assert token.scopes == auth.scopes
|
||||||
|
assert user.ap_id == ap_id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
|
test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
|
||||||
|
|
|
@ -202,4 +202,34 @@ test "it send wanted private posts to list" do
|
||||||
|
|
||||||
Task.await(task)
|
Task.await(task)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it doesn't send muted reblogs" do
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
user3 = insert(:user)
|
||||||
|
CommonAPI.hide_reblogs(user1, user2)
|
||||||
|
|
||||||
|
task =
|
||||||
|
Task.async(fn ->
|
||||||
|
refute_receive {:text, _}, 1_000
|
||||||
|
end)
|
||||||
|
|
||||||
|
fake_socket = %{
|
||||||
|
transport_pid: task.pid,
|
||||||
|
assigns: %{
|
||||||
|
user: user1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
|
||||||
|
{:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
|
||||||
|
|
||||||
|
topics = %{
|
||||||
|
"public" => [fake_socket]
|
||||||
|
}
|
||||||
|
|
||||||
|
Streamer.push_to_socket(topics, "public", announce_activity)
|
||||||
|
|
||||||
|
Task.await(task)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
describe "POST /api/pleroma/follow_import" do
|
describe "POST /api/pleroma/follow_import" do
|
||||||
|
@ -52,6 +55,25 @@ test "it returns HTTP 200", %{conn: conn} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/notifications/read" do
|
||||||
|
test "it marks a single notification as read", %{conn: conn} do
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
{:ok, activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
|
||||||
|
{:ok, activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"})
|
||||||
|
{:ok, [notification1]} = Notification.create_notifications(activity1)
|
||||||
|
{:ok, [notification2]} = Notification.create_notifications(activity2)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> assign(:user, user1)
|
||||||
|
|> post("/api/pleroma/notifications/read", %{"id" => "#{notification1.id}"})
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
assert Repo.get(Notification, notification1.id).seen
|
||||||
|
refute Repo.get(Notification, notification2.id).seen
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "GET /api/statusnet/config.json" do
|
describe "GET /api/statusnet/config.json" do
|
||||||
test "it returns the managed config", %{conn: conn} do
|
test "it returns the managed config", %{conn: conn} do
|
||||||
Pleroma.Config.put([:instance, :managed_config], false)
|
Pleroma.Config.put([:instance, :managed_config], false)
|
||||||
|
|
|
@ -82,7 +82,7 @@ test "a create activity with a html status" do
|
||||||
result = ActivityView.render("activity.json", activity: activity)
|
result = ActivityView.render("activity.json", activity: activity)
|
||||||
|
|
||||||
assert result["statusnet_html"] ==
|
assert result["statusnet_html"] ==
|
||||||
"<a class=\"hashtag\" data-tag=\"bike\" href=\"http://localhost:4001/tag/bike\">#Bike</a> log - Commute Tuesday<br /><a href=\"https://pla.bike/posts/20181211/\">https://pla.bike/posts/20181211/</a><br /><a class=\"hashtag\" data-tag=\"cycling\" href=\"http://localhost:4001/tag/cycling\">#cycling</a> <a class=\"hashtag\" data-tag=\"chscycling\" href=\"http://localhost:4001/tag/chscycling\">#CHScycling</a> <a class=\"hashtag\" data-tag=\"commute\" href=\"http://localhost:4001/tag/commute\">#commute</a><br />MVIMG_20181211_054020.jpg"
|
"<a class=\"hashtag\" data-tag=\"bike\" href=\"http://localhost:4001/tag/bike\" rel=\"tag\">#Bike</a> log - Commute Tuesday<br /><a href=\"https://pla.bike/posts/20181211/\">https://pla.bike/posts/20181211/</a><br /><a class=\"hashtag\" data-tag=\"cycling\" href=\"http://localhost:4001/tag/cycling\" rel=\"tag\">#cycling</a> <a class=\"hashtag\" data-tag=\"chscycling\" href=\"http://localhost:4001/tag/chscycling\" rel=\"tag\">#CHScycling</a> <a class=\"hashtag\" data-tag=\"commute\" href=\"http://localhost:4001/tag/commute\" rel=\"tag\">#commute</a><br />MVIMG_20181211_054020.jpg"
|
||||||
|
|
||||||
assert result["text"] ==
|
assert result["text"] ==
|
||||||
"#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg"
|
"#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg"
|
||||||
|
|
Loading…
Reference in a new issue