Add sender
This commit is contained in:
parent
20a3ed9e71
commit
fa350aa551
5 changed files with 85 additions and 25 deletions
|
@ -1,8 +1,7 @@
|
||||||
defmodule Nulla.HTTPSignature do
|
defmodule Nulla.HTTPSignature do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
alias Nulla.Models.User
|
|
||||||
|
|
||||||
def make_headers(body, inbox_url, actor) do
|
def make_headers(body, inbox_url, publicKeyId, privateKeyPem) do
|
||||||
digest = "SHA-256=" <> (:crypto.hash(:sha256, body) |> Base.encode64())
|
digest = "SHA-256=" <> (:crypto.hash(:sha256, body) |> Base.encode64())
|
||||||
date = DateTime.utc_now() |> Calendar.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
date = DateTime.utc_now() |> Calendar.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||||
uri = URI.parse(inbox_url)
|
uri = URI.parse(inbox_url)
|
||||||
|
@ -13,10 +12,8 @@ defmodule Nulla.HTTPSignature do
|
||||||
"date: #{date}\n" <>
|
"date: #{date}\n" <>
|
||||||
"digest: #{digest}"
|
"digest: #{digest}"
|
||||||
|
|
||||||
user = User.get_user(id: actor.id)
|
|
||||||
|
|
||||||
private_key =
|
private_key =
|
||||||
case :public_key.pem_decode(user.privateKeyPem) do
|
case :public_key.pem_decode(privateKeyPem) do
|
||||||
[entry] -> :public_key.pem_entry_decode(entry)
|
[entry] -> :public_key.pem_entry_decode(entry)
|
||||||
_ -> raise "Invalid PEM format"
|
_ -> raise "Invalid PEM format"
|
||||||
end
|
end
|
||||||
|
@ -27,7 +24,7 @@ defmodule Nulla.HTTPSignature do
|
||||||
|
|
||||||
signature_header =
|
signature_header =
|
||||||
"""
|
"""
|
||||||
keyId="#{actor.publicKey["id"]}",
|
keyId="#{publicKeyId}",
|
||||||
algorithm="rsa-sha256",
|
algorithm="rsa-sha256",
|
||||||
headers="(request-target) host date digest",
|
headers="(request-target) host date digest",
|
||||||
signature="#{signature}"
|
signature="#{signature}"
|
||||||
|
|
|
@ -3,6 +3,7 @@ defmodule Nulla.Models.Activity do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Nulla.Repo
|
alias Nulla.Repo
|
||||||
alias Nulla.Snowflake
|
alias Nulla.Snowflake
|
||||||
|
alias Nulla.Types.StringOrJson
|
||||||
|
|
||||||
@derive {Jason.Encoder, only: [:ap_id, :type, :actor, :object]}
|
@derive {Jason.Encoder, only: [:ap_id, :type, :actor, :object]}
|
||||||
@primary_key {:id, :integer, autogenerate: false}
|
@primary_key {:id, :integer, autogenerate: false}
|
||||||
|
@ -10,7 +11,7 @@ defmodule Nulla.Models.Activity do
|
||||||
field :ap_id, :string
|
field :ap_id, :string
|
||||||
field :type, :string
|
field :type, :string
|
||||||
field :actor, :string
|
field :actor, :string
|
||||||
field :object, :string
|
field :object, StringOrJson
|
||||||
field :to, {:array, :string}
|
field :to, {:array, :string}
|
||||||
field :cc, {:array, :string}
|
field :cc, {:array, :string}
|
||||||
|
|
||||||
|
|
24
lib/nulla/sender.ex
Normal file
24
lib/nulla/sender.ex
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule Nulla.Sender do
|
||||||
|
alias Nulla.ActivityPub
|
||||||
|
alias Nulla.HTTPSignature
|
||||||
|
|
||||||
|
def send_activity(method, inbox, activity, publicKeyId, privateKeyPem) do
|
||||||
|
body = Jason.encode!(ActivityPub.activity(activity))
|
||||||
|
headers = HTTPSignature.make_headers(body, inbox, publicKeyId, privateKeyPem)
|
||||||
|
request = Finch.build(method, inbox, headers, body)
|
||||||
|
|
||||||
|
case Finch.request(request, Nulla.Finch) do
|
||||||
|
{:ok, %Finch.Response{status: code}} when code in 200..299 ->
|
||||||
|
IO.puts("Activity #{activity.id} delivered successfully")
|
||||||
|
:ok
|
||||||
|
|
||||||
|
{:ok, %Finch.Response{status: code, body: resp}} ->
|
||||||
|
IO.inspect({:error, code, resp}, label: "Failed to deliver activity #{activity.id}")
|
||||||
|
{:error, {:http_error, code}}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
IO.inspect(reason, label: "Activity #{activity.id} delivery failed")
|
||||||
|
{:error, reason}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
46
lib/nulla/types/string_or_json.ex
Normal file
46
lib/nulla/types/string_or_json.ex
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
defmodule Nulla.Types.StringOrJson do
|
||||||
|
@behaviour Ecto.Type
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def type, do: :string
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def cast(value) when is_map(value) or is_list(value), do: {:ok, value}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def cast(value) when is_binary(value) do
|
||||||
|
case Jason.decode(value) do
|
||||||
|
{:ok, decoded} -> {:ok, decoded}
|
||||||
|
_ -> {:ok, value}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def cast(_), do: :error
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def dump(value) when is_map(value) or is_list(value), do: Jason.encode(value)
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def dump(value) when is_binary(value), do: {:ok, value}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def dump(_), do: :error
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def load(value) when is_binary(value) do
|
||||||
|
case Jason.decode(value) do
|
||||||
|
{:ok, decoded} -> {:ok, decoded}
|
||||||
|
_ -> {:ok, value}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def load(_), do: :error
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def embed_as(_format), do: :self
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def equal?(term1, term2), do: term1 == term2
|
||||||
|
end
|
|
@ -1,9 +1,10 @@
|
||||||
defmodule NullaWeb.InboxController do
|
defmodule NullaWeb.InboxController do
|
||||||
use NullaWeb, :controller
|
use NullaWeb, :controller
|
||||||
alias Nulla.ActivityPub
|
|
||||||
alias Nulla.Snowflake
|
alias Nulla.Snowflake
|
||||||
alias Nulla.HTTPSignature
|
alias Nulla.HTTPSignature
|
||||||
|
alias Nulla.Sender
|
||||||
alias Nulla.Utils
|
alias Nulla.Utils
|
||||||
|
alias Nulla.Models.User
|
||||||
alias Nulla.Models.Actor
|
alias Nulla.Models.Actor
|
||||||
alias Nulla.Models.Relation
|
alias Nulla.Models.Relation
|
||||||
alias Nulla.Models.Activity
|
alias Nulla.Models.Activity
|
||||||
|
@ -109,24 +110,15 @@ defmodule NullaWeb.InboxController do
|
||||||
}),
|
}),
|
||||||
{:ok, _relation} <-
|
{:ok, _relation} <-
|
||||||
Relation.get_or_create_relation(local_actor.id, remote_actor.id, followed_by: true) do
|
Relation.get_or_create_relation(local_actor.id, remote_actor.id, followed_by: true) do
|
||||||
activity = %Activity{accept_activity | object: Jason.decode!(accept_activity.object)}
|
user = User.get_user(id: local_actor.id)
|
||||||
body = Jason.encode!(ActivityPub.activity(activity))
|
|
||||||
headers = HTTPSignature.make_headers(body, remote_actor_json["inbox"], local_actor)
|
|
||||||
request = Finch.build(:post, remote_actor_json["inbox"], headers, body)
|
|
||||||
|
|
||||||
case Finch.request(request, Nulla.Finch) do
|
Sender.send_activity(
|
||||||
{:ok, %Finch.Response{status: code}} when code in 200..299 ->
|
:post,
|
||||||
IO.puts("Accept delivered successfully")
|
remote_actor.inbox,
|
||||||
:ok
|
accept_activity,
|
||||||
|
local_actor.publicKey["id"],
|
||||||
{:ok, %Finch.Response{status: code, body: resp}} ->
|
user.privateKeyPem
|
||||||
IO.inspect({:error, code, resp}, label: "Failed to deliver Accept")
|
)
|
||||||
{:error, {:http_error, code}}
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
IO.inspect(reason, label: "Finch delivery failed")
|
|
||||||
{:error, reason}
|
|
||||||
end
|
|
||||||
|
|
||||||
send_resp(conn, 200, "")
|
send_resp(conn, 200, "")
|
||||||
else
|
else
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue