Fix
This commit is contained in:
parent
748baff8f3
commit
1faafeee26
11 changed files with 126 additions and 48 deletions
|
@ -7,12 +7,11 @@ defmodule Nulla.HTTPSignature do
|
||||||
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)
|
||||||
|
|
||||||
signature_string = """
|
signature_string =
|
||||||
(request-target): post #{uri.path}
|
"(request-target): post #{uri.path}\n" <>
|
||||||
host: #{uri.host}
|
"host: #{uri.host}\n" <>
|
||||||
date: #{date}
|
"date: #{date}\n" <>
|
||||||
digest: #{digest}
|
"digest: #{digest}"
|
||||||
"""
|
|
||||||
|
|
||||||
user = User.get_user(id: actor.id)
|
user = User.get_user(id: actor.id)
|
||||||
|
|
||||||
|
@ -56,13 +55,16 @@ defmodule Nulla.HTTPSignature do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse_signature_header(header) do
|
defp parse_signature_header(header) do
|
||||||
|
header =
|
||||||
|
header
|
||||||
|
|> String.split(",")
|
||||||
|
|> Enum.map(fn pair ->
|
||||||
|
[k, v] = String.split(pair, "=", parts: 2)
|
||||||
|
{String.trim(k), String.trim(v, ~s("))}
|
||||||
|
end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
header
|
header
|
||||||
|> String.split(",")
|
|
||||||
|> Enum.map(fn pair ->
|
|
||||||
[k, v] = String.split(pair, "=", parts: 2)
|
|
||||||
{String.trim(k), String.trim(v, ~s("))}
|
|
||||||
end)
|
|
||||||
|> Enum.into(%{})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_signature_string(nil, _conn), do: {:error, :missing_headers}
|
defp build_signature_string(nil, _conn), do: {:error, :missing_headers}
|
||||||
|
@ -83,6 +85,9 @@ defmodule Nulla.HTTPSignature do
|
||||||
|
|
||||||
"(request-target): #{method} #{path}"
|
"(request-target): #{method} #{path}"
|
||||||
|
|
||||||
|
"host" ->
|
||||||
|
"host: #{conn.host}"
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
value = get_req_header(conn, header) |> List.first()
|
value = get_req_header(conn, header) |> List.first()
|
||||||
if value, do: "#{header}: #{value}", else: nil
|
if value, do: "#{header}: #{value}", else: nil
|
||||||
|
|
40
test/nulla/http_signature_test.exs
Normal file
40
test/nulla/http_signature_test.exs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
defmodule Nulla.HTTPSignatureTest do
|
||||||
|
use NullaWeb.ConnCase, async: false
|
||||||
|
import Plug.Conn
|
||||||
|
import Plug.Test
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
|
alias Nulla.HTTPSignature
|
||||||
|
alias Nulla.Snowflake
|
||||||
|
alias Nulla.Models.Actor
|
||||||
|
|
||||||
|
setup do
|
||||||
|
create_data()
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "make_headers/3 creates valid signature headers and verify/2 validates them" do
|
||||||
|
actor = Actor.get_actor(preferredUsername: "test")
|
||||||
|
target_actor = Actor.get_actor(preferredUsername: "test2")
|
||||||
|
|
||||||
|
follow_activity = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => Snowflake.next_id(),
|
||||||
|
"type" => "Follow",
|
||||||
|
"actor" => actor.ap_id,
|
||||||
|
"object" => target_actor.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
body = Jason.encode!(follow_activity)
|
||||||
|
headers = HTTPSignature.make_headers(body, target_actor.inbox, actor)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn(:post, "/users/test2/inbox", body)
|
||||||
|
|> put_req_header("content-type", Map.new(headers) |> Map.get("Content-Type"))
|
||||||
|
|> put_req_header("date", Map.new(headers) |> Map.get("Date"))
|
||||||
|
|> put_req_header("digest", Map.new(headers) |> Map.get("Digest"))
|
||||||
|
|> put_req_header("signature", Map.new(headers) |> Map.get("Signature"))
|
||||||
|
|> Map.put(:host, URI.parse(target_actor.inbox).host)
|
||||||
|
|
||||||
|
assert :ok = HTTPSignature.verify(conn, actor.publicKey["publicKeyPem"])
|
||||||
|
end
|
||||||
|
end
|
32
test/nulla/keys.exs
Normal file
32
test/nulla/keys.exs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
defmodule Nulla.KeysTest do
|
||||||
|
use NullaWeb.ConnCase, async: false
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
|
alias Nulla.Models.User
|
||||||
|
alias Nulla.Models.Actor
|
||||||
|
|
||||||
|
setup do
|
||||||
|
create()
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "verify user's keys" do
|
||||||
|
actor = Actor.get_actor(preferredUsername: "test")
|
||||||
|
user = User.get_user(id: actor.id)
|
||||||
|
|
||||||
|
message = "test message"
|
||||||
|
|
||||||
|
private_key =
|
||||||
|
:public_key.pem_decode(user.privateKeyPem)
|
||||||
|
|> hd()
|
||||||
|
|> :public_key.pem_entry_decode()
|
||||||
|
|
||||||
|
public_key =
|
||||||
|
:public_key.pem_decode(actor.publicKey["publicKeyPem"])
|
||||||
|
|> hd()
|
||||||
|
|> :public_key.pem_entry_decode()
|
||||||
|
|
||||||
|
signature = :public_key.sign(message, :sha256, private_key)
|
||||||
|
|
||||||
|
assert :public_key.verify(message, :sha256, signature, public_key)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,8 +1,9 @@
|
||||||
defmodule NullaWeb.ActorControllerTest do
|
defmodule NullaWeb.ActorControllerTest do
|
||||||
use NullaWeb.ConnCase
|
use NullaWeb.ConnCase
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Nulla.Fixtures.Data.create()
|
create_data()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
defmodule NullaWeb.FollowControllerTest do
|
defmodule NullaWeb.FollowControllerTest do
|
||||||
use NullaWeb.ConnCase
|
use NullaWeb.ConnCase
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Nulla.Fixtures.Data.create()
|
create_data()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
defmodule NullaWeb.InboxControllerTest do
|
defmodule NullaWeb.InboxControllerTest do
|
||||||
use NullaWeb.ConnCase
|
use NullaWeb.ConnCase
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
alias Nulla.Snowflake
|
alias Nulla.Snowflake
|
||||||
alias Nulla.Models.User
|
alias Nulla.Models.User
|
||||||
alias Nulla.Models.Actor
|
alias Nulla.Models.Actor
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Nulla.Fixtures.Data.create()
|
create_data()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
defmodule NullaWeb.NodeinfoControllerTest do
|
defmodule NullaWeb.NodeinfoControllerTest do
|
||||||
use NullaWeb.ConnCase
|
use NullaWeb.ConnCase
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Nulla.Fixtures.Data.create()
|
create_data()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
defmodule NullaWeb.NoteControllerTest do
|
defmodule NullaWeb.NoteControllerTest do
|
||||||
use NullaWeb.ConnCase
|
use NullaWeb.ConnCase
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
alias Nulla.Models.Actor
|
alias Nulla.Models.Actor
|
||||||
alias Nulla.Models.Note
|
alias Nulla.Models.Note
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Nulla.Fixtures.Data.create()
|
create_data()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
defmodule NullaWeb.OutboxControllerTest do
|
defmodule NullaWeb.OutboxControllerTest do
|
||||||
use NullaWeb.ConnCase
|
use NullaWeb.ConnCase
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Nulla.Fixtures.Data.create()
|
create_data()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
defmodule NullaWeb.WebfingerControllerTest do
|
defmodule NullaWeb.WebfingerControllerTest do
|
||||||
use NullaWeb.ConnCase
|
use NullaWeb.ConnCase
|
||||||
|
import Nulla.Fixtures.Data
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
Nulla.Fixtures.Data.create()
|
create_data()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,30 +4,24 @@ defmodule Nulla.Fixtures.Data do
|
||||||
alias Nulla.Models.Actor
|
alias Nulla.Models.Actor
|
||||||
alias Nulla.Models.Note
|
alias Nulla.Models.Note
|
||||||
|
|
||||||
def create do
|
def create_data do
|
||||||
endpoint_config = Application.fetch_env!(:nulla, NullaWeb.Endpoint)
|
|
||||||
ip = endpoint_config[:http][:ip]
|
|
||||||
host = :inet_parse.ntoa(ip) |> to_string()
|
|
||||||
port = endpoint_config[:http][:port]
|
|
||||||
base_url = "http://#{host}:#{port}"
|
|
||||||
|
|
||||||
{publicKeyPem, privateKeyPem} = KeyGen.gen()
|
{publicKeyPem, privateKeyPem} = KeyGen.gen()
|
||||||
|
|
||||||
{:ok, actor} =
|
{:ok, actor} =
|
||||||
Actor.create_actor(%{
|
Actor.create_actor(%{
|
||||||
domain: "localhost",
|
domain: "localhost",
|
||||||
ap_id: "#{base_url}/users/test",
|
ap_id: "http://localhost/users/test",
|
||||||
type: "Person",
|
type: "Person",
|
||||||
following: "#{base_url}/users/test/following",
|
following: "http://localhost/users/test/following",
|
||||||
followers: "#{base_url}/users/test/followers",
|
followers: "http://localhost/users/test/followers",
|
||||||
inbox: "#{base_url}/users/test/inbox",
|
inbox: "http://localhost/users/test/inbox",
|
||||||
outbox: "#{base_url}/users/test/outbox",
|
outbox: "http://localhost/users/test/outbox",
|
||||||
featured: "#{base_url}/users/test/collections/featured",
|
featured: "http://localhost/users/test/collections/featured",
|
||||||
featuredTags: "#{base_url}/users/test/collections/tags",
|
featuredTags: "http://localhost/users/test/collections/tags",
|
||||||
preferredUsername: "test",
|
preferredUsername: "test",
|
||||||
name: "Test",
|
name: "Test",
|
||||||
summary: "Test User",
|
summary: "Test User",
|
||||||
url: "#{base_url}/@test",
|
url: "http://localhost/@test",
|
||||||
manuallyApprovesFollowers: false,
|
manuallyApprovesFollowers: false,
|
||||||
discoverable: true,
|
discoverable: true,
|
||||||
indexable: true,
|
indexable: true,
|
||||||
|
@ -35,11 +29,11 @@ defmodule Nulla.Fixtures.Data do
|
||||||
memorial: false,
|
memorial: false,
|
||||||
publicKey:
|
publicKey:
|
||||||
Jason.OrderedObject.new(
|
Jason.OrderedObject.new(
|
||||||
id: "#{base_url}/users/test#main-key",
|
id: "http://localhost/users/test#main-key",
|
||||||
owner: "#{base_url}/users/test",
|
owner: "http://localhost/users/test",
|
||||||
publicKeyPem: publicKeyPem
|
publicKeyPem: publicKeyPem
|
||||||
),
|
),
|
||||||
endpoints: Jason.OrderedObject.new(sharedInbox: "#{base_url}/inbox")
|
endpoints: Jason.OrderedObject.new(sharedInbox: "http://localhost/inbox")
|
||||||
})
|
})
|
||||||
|
|
||||||
User.create_user(%{
|
User.create_user(%{
|
||||||
|
@ -62,18 +56,18 @@ defmodule Nulla.Fixtures.Data do
|
||||||
{:ok, actor} =
|
{:ok, actor} =
|
||||||
Actor.create_actor(%{
|
Actor.create_actor(%{
|
||||||
domain: "localhost",
|
domain: "localhost",
|
||||||
ap_id: "#{base_url}/users/test2",
|
ap_id: "http://localhost/users/test2",
|
||||||
type: "Person",
|
type: "Person",
|
||||||
following: "#{base_url}/users/test2/following",
|
following: "http://localhost/users/test2/following",
|
||||||
followers: "#{base_url}/users/test2/followers",
|
followers: "http://localhost/users/test2/followers",
|
||||||
inbox: "#{base_url}/users/test2/inbox",
|
inbox: "http://localhost/users/test2/inbox",
|
||||||
outbox: "#{base_url}/users/test2/outbox",
|
outbox: "http://localhost/users/test2/outbox",
|
||||||
featured: "#{base_url}/users/test2/collections/featured",
|
featured: "http://localhost/users/test2/collections/featured",
|
||||||
featuredTags: "#{base_url}/users/test2/collections/tags",
|
featuredTags: "http://localhost/users/test2/collections/tags",
|
||||||
preferredUsername: "test2",
|
preferredUsername: "test2",
|
||||||
name: "Test",
|
name: "Test",
|
||||||
summary: "Test User",
|
summary: "Test User",
|
||||||
url: "#{base_url}/@test2",
|
url: "http://localhost/@test2",
|
||||||
manuallyApprovesFollowers: false,
|
manuallyApprovesFollowers: false,
|
||||||
discoverable: true,
|
discoverable: true,
|
||||||
indexable: true,
|
indexable: true,
|
||||||
|
@ -81,11 +75,11 @@ defmodule Nulla.Fixtures.Data do
|
||||||
memorial: false,
|
memorial: false,
|
||||||
publicKey:
|
publicKey:
|
||||||
Jason.OrderedObject.new(
|
Jason.OrderedObject.new(
|
||||||
id: "#{base_url}/users/test2#main-key",
|
id: "http://localhost/users/test2#main-key",
|
||||||
owner: "#{base_url}/users/test2",
|
owner: "http://localhost/users/test2",
|
||||||
publicKeyPem: publicKeyPem
|
publicKeyPem: publicKeyPem
|
||||||
),
|
),
|
||||||
endpoints: Jason.OrderedObject.new(sharedInbox: "#{base_url}/inbox")
|
endpoints: Jason.OrderedObject.new(sharedInbox: "http://localhost/inbox")
|
||||||
})
|
})
|
||||||
|
|
||||||
User.create_user(%{
|
User.create_user(%{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue