Update
This commit is contained in:
parent
3f329cf59e
commit
2b5b658809
2 changed files with 64 additions and 53 deletions
|
@ -1,11 +1,10 @@
|
||||||
defmodule Nulla.HTTPSignature do
|
defmodule Nulla.HTTPSignature do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
def verify(conn, actor_json) do
|
def verify(conn, public_key_pem) do
|
||||||
with [sig_header] <- get_req_header(conn, "signature"),
|
with [sig_header] <- get_req_header(conn, "signature"),
|
||||||
signature_map <- parse_signature_header(sig_header),
|
signature_map <- parse_signature_header(sig_header),
|
||||||
{:ok, signed_string} <- build_signature_string(signature_map["headers"], conn),
|
{:ok, signed_string} <- build_signature_string(signature_map["headers"], conn),
|
||||||
{:ok, public_key_pem} <- extract_public_key(actor_json),
|
|
||||||
true <- verify_signature(public_key_pem, signed_string, signature_map["signature"]) do
|
true <- verify_signature(public_key_pem, signed_string, signature_map["signature"]) do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
|
@ -54,9 +53,6 @@ defmodule Nulla.HTTPSignature do
|
||||||
{:ok, result}
|
{:ok, result}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp extract_public_key(%{"publicKey" => %{"publicKeyPem" => pem}}), do: {:ok, pem}
|
|
||||||
defp extract_public_key(_), do: {:error, :no_public_key}
|
|
||||||
|
|
||||||
defp verify_signature(public_key_pem, signed_string, signature_base64) do
|
defp verify_signature(public_key_pem, signed_string, signature_base64) do
|
||||||
public_key =
|
public_key =
|
||||||
:public_key.pem_decode(public_key_pem)
|
:public_key.pem_decode(public_key_pem)
|
||||||
|
|
|
@ -19,14 +19,25 @@ defmodule NullaWeb.InboxController do
|
||||||
|
|
||||||
with local_actor <- Actor.get_actor(ap_id: target_uri),
|
with local_actor <- Actor.get_actor(ap_id: target_uri),
|
||||||
{:ok, remote_actor_json} <- Utils.fetch_remote_actor(actor_uri),
|
{:ok, remote_actor_json} <- Utils.fetch_remote_actor(actor_uri),
|
||||||
:ok <- HTTPSignature.verify(conn, remote_actor_json),
|
:ok <- HTTPSignature.verify(conn, remote_actor_json["publicKey"]["publicKeyPem"]),
|
||||||
{:ok, remote_actor} <- get_or_create_actor(remote_actor_json),
|
{:ok, remote_actor} <- get_or_create_actor(remote_actor_json),
|
||||||
{:ok, follow_activity} <-
|
{:ok, follow_activity} <-
|
||||||
create_follow_activity(follow_id, remote_actor.ap_id, target_uri),
|
Activity.create_activity(%{
|
||||||
|
ap_id: follow_id,
|
||||||
|
type: "Follow",
|
||||||
|
actor: remote_actor.ap_id,
|
||||||
|
object: target_uri
|
||||||
|
}),
|
||||||
{:ok, accept_activity} <-
|
{:ok, accept_activity} <-
|
||||||
create_accept_activity(accept_id, local_actor, follow_activity),
|
Activity.create_activity(%{
|
||||||
|
id: accept_id,
|
||||||
|
ap_id: "https://#{local_actor.domain}/activities/accept/#{accept_id}",
|
||||||
|
type: "Accept",
|
||||||
|
actor: local_actor.ap_id,
|
||||||
|
object: Jason.encode!(follow_activity)
|
||||||
|
}),
|
||||||
{:ok, _relation} <- get_or_create_relation(local_actor.id, remote_actor.id),
|
{:ok, _relation} <- get_or_create_relation(local_actor.id, remote_actor.id),
|
||||||
:ok <- deliver_accept(accept_activity, remote_actor_json, local_actor) do
|
:ok <- deliver_accept(accept_activity, remote_actor_json["inbox"], local_actor) do
|
||||||
send_resp(conn, 200, "")
|
send_resp(conn, 200, "")
|
||||||
else
|
else
|
||||||
error ->
|
error ->
|
||||||
|
@ -56,25 +67,6 @@ defmodule NullaWeb.InboxController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_follow_activity(follow_id, actor_id, target_uri) do
|
|
||||||
Activity.create_activity(%{
|
|
||||||
ap_id: follow_id,
|
|
||||||
type: "Follow",
|
|
||||||
actor: actor_id,
|
|
||||||
object: target_uri
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp create_accept_activity(accept_id, local_actor, follow_activity) do
|
|
||||||
Activity.create_activity(%{
|
|
||||||
id: accept_id,
|
|
||||||
ap_id: "https://#{local_actor.domain}/activities/accept/#{accept_id}",
|
|
||||||
type: "Accept",
|
|
||||||
actor: local_actor.ap_id,
|
|
||||||
object: Jason.encode!(follow_activity)
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_or_create_relation(local_actor_id, remote_actor_id) do
|
defp get_or_create_relation(local_actor_id, remote_actor_id) do
|
||||||
case Relation.get_relation(local_actor_id: local_actor_id, remote_actor_id: remote_actor_id) do
|
case Relation.get_relation(local_actor_id: local_actor_id, remote_actor_id: remote_actor_id) do
|
||||||
nil ->
|
nil ->
|
||||||
|
@ -89,25 +81,15 @@ defmodule NullaWeb.InboxController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp deliver_accept(accept_activity, remote_actor_json, local_actor) do
|
defp deliver_accept(accept_activity, inbox_url, local_actor) do
|
||||||
inbox_url = remote_actor_json["inbox"]
|
|
||||||
accept_activity = %Activity{accept_activity | object: Jason.decode!(accept_activity.object)}
|
accept_activity = %Activity{accept_activity | object: Jason.decode!(accept_activity.object)}
|
||||||
body = Jason.encode!(ActivityPub.activity(accept_activity))
|
body = Jason.encode!(ActivityPub.activity(accept_activity))
|
||||||
|
digest = "SHA-256=" <> (:crypto.hash(:sha256, body) |> Base.encode64())
|
||||||
digest =
|
date = DateTime.utc_now() |> Calendar.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||||
:crypto.hash(:sha256, body)
|
|
||||||
|> Base.encode64()
|
|
||||||
|> then(&("SHA-256=" <> &1))
|
|
||||||
|
|
||||||
date =
|
|
||||||
DateTime.utc_now()
|
|
||||||
|> Calendar.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
|
||||||
|
|
||||||
uri = URI.parse(inbox_url)
|
uri = URI.parse(inbox_url)
|
||||||
request_target = "post #{uri.path}"
|
|
||||||
|
|
||||||
signature_string = """
|
signature_string = """
|
||||||
(request-target): #{request_target}
|
(request-target): post #{uri.path}
|
||||||
host: #{uri.host}
|
host: #{uri.host}
|
||||||
date: #{date}
|
date: #{date}
|
||||||
digest: #{digest}
|
digest: #{digest}
|
||||||
|
@ -117,11 +99,8 @@ defmodule NullaWeb.InboxController do
|
||||||
|
|
||||||
private_key =
|
private_key =
|
||||||
case :public_key.pem_decode(user.privateKeyPem) do
|
case :public_key.pem_decode(user.privateKeyPem) do
|
||||||
[{_type, der, _rest}] ->
|
[entry] -> :public_key.pem_entry_decode(entry)
|
||||||
:public_key.der_decode(:RSAPrivateKey, der)
|
_ -> raise "Invalid PEM format"
|
||||||
|
|
||||||
_ ->
|
|
||||||
raise "Invalid PEM format"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
signature =
|
signature =
|
||||||
|
@ -132,18 +111,17 @@ defmodule NullaWeb.InboxController do
|
||||||
"""
|
"""
|
||||||
keyId="#{local_actor.publicKey["id"]}",
|
keyId="#{local_actor.publicKey["id"]}",
|
||||||
algorithm="rsa-sha256",
|
algorithm="rsa-sha256",
|
||||||
headers="(request-target) host date digest content-type",
|
headers="(request-target) host date digest",
|
||||||
signature="#{signature}"
|
signature="#{signature}"
|
||||||
"""
|
"""
|
||||||
|> String.replace("\n", "")
|
|> String.replace("\n", "")
|
||||||
|> String.trim()
|
|> String.trim()
|
||||||
|
|
||||||
headers = [
|
headers = [
|
||||||
{"host", uri.host},
|
{"Content-Type", "application/activity+json"},
|
||||||
{"date", date},
|
{"Date", date},
|
||||||
{"digest", digest},
|
{"Digest", digest},
|
||||||
{"signature", signature_header},
|
{"Signature", signature_header}
|
||||||
{"content-type", "application/activity+json"}
|
|
||||||
]
|
]
|
||||||
|
|
||||||
request = Finch.build(:post, inbox_url, headers, body)
|
request = Finch.build(:post, inbox_url, headers, body)
|
||||||
|
@ -162,4 +140,41 @@ defmodule NullaWeb.InboxController do
|
||||||
{:error, reason}
|
{:error, reason}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inbox(conn, %{
|
||||||
|
"id" => accept_id,
|
||||||
|
"type" => "Accept",
|
||||||
|
"actor" => actor_uri,
|
||||||
|
"object" => object
|
||||||
|
}) do
|
||||||
|
with {:ok, remote_actor_json} <- Utils.fetch_remote_actor(actor_uri),
|
||||||
|
:ok <- HTTPSignature.verify(conn, remote_actor_json),
|
||||||
|
{:ok, remote_actor} <- get_or_create_actor(remote_actor_json),
|
||||||
|
{:ok, follow_activity} <- decode_follow_activity(object),
|
||||||
|
{:ok, local_actor} <- Actor.get_actor(ap_id: follow_activity["object"]),
|
||||||
|
{:ok, _relation} <- Relation.mark_follow_accepted(local_actor.id, remote_actor.id) do
|
||||||
|
send_resp(conn, 200, "")
|
||||||
|
else
|
||||||
|
error ->
|
||||||
|
IO.inspect(error, label: "Accept error")
|
||||||
|
json(conn, %{"error" => "Failed to process Accept"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decode_follow_activity(object) when is_map(object), do: {:ok, object}
|
||||||
|
defp decode_follow_activity(object) when is_binary(object), do: Jason.decode(object)
|
||||||
|
|
||||||
|
def inbox(conn, %{
|
||||||
|
"id" => accept_id,
|
||||||
|
"type" => "Undo",
|
||||||
|
"actor" => actor_uri,
|
||||||
|
"object" => object
|
||||||
|
}) do
|
||||||
|
send_resp(conn, 200, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
def inbox(conn, params) do
|
||||||
|
IO.inspect(params)
|
||||||
|
send_resp(conn, 400, "")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue