Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85630157
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/.woodpecker/lint.yaml b/.woodpecker/lint.yaml
index e1b5b85..f26a095 100644
--- a/.woodpecker/lint.yaml
+++ b/.woodpecker/lint.yaml
@@ -1,57 +1,57 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.exs", "mix.lock", ".woodpecker/**" ]
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.exs", "mix.lock", ".woodpecker/**" ]
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
elixir_image: &elixir_image
docker.io/elixir:1.15-alpine
steps:
mix-format:
image: *elixir_image
entrypoint: *script_file_entrypoint
failure: ignore
commands:
- |
if ! mix format --check-formatted; then
touch fail.stamp
exit 1
fi
credo:
- image: *elixir-image
+ image: *elixir_image
entrypoint: *script_file_entrypoint
failure: ignore
environment:
MIX_ENV: test
commands:
- adduser -D -h /home/testuser testuser
- mkdir -p /home/testuser/.mix /home/testuser/.hex
- chown -R testuser:testuser . /home/testuser
- su testuser -c "HOME=/home/testuser mix local.hex --force"
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
- su testuser -c "HOME=/home/testuser mix deps.get"
- |
- if ! su testuser -c "HOME=/home/testuser mix analyze"; then
+ if ! su testuser -c "HOME=/home/testuser mix credo"; then
touch fail.stamp
exit 1
fi
ensure-status:
- image: *elixir-image
+ image: *elixir_image
entrypoint: *script_file_entrypoint
commands: |
if test -f fail.stamp; then
echo "One or more previous steps failed. Failing workflow..."
exit 1
else
echo "All steps passed."
exit 0
fi
diff --git a/.woodpecker/unit-testing-elixir-1.19.yaml b/.woodpecker/unit-testing-elixir-1.19.yaml
index edda421..e7f928b 100644
--- a/.woodpecker/unit-testing-elixir-1.19.yaml
+++ b/.woodpecker/unit-testing-elixir-1.19.yaml
@@ -1,27 +1,27 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.exs", ".woodpecker/**" ]
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.exs", ".woodpecker/**" ]
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps:
- unit-testing-elixir-1.15:
+ unit-testing-elixir-1.19:
image: elixir:1.19-alpine
entrypoint: *script_file_entrypoint
environment:
MIX_ENV: test
commands:
- adduser -D -h /home/testuser testuser
- mkdir -p /home/testuser/.mix /home/testuser/.hex
- chown -R testuser:testuser . /home/testuser
- su testuser -c "HOME=/home/testuser mix local.hex --force"
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
- su testuser -c "HOME=/home/testuser mix deps.get"
- su testuser -c "HOME=/home/testuser mix test --preload-modules --warnings-as-errors"
diff --git a/lib/http_signatures/http_signatures.ex b/lib/http_signatures/http_signatures.ex
index 7b92776..2a1e332 100644
--- a/lib/http_signatures/http_signatures.ex
+++ b/lib/http_signatures/http_signatures.ex
@@ -1,153 +1,153 @@
# Pleroma: A lightweight social networking server
# SPDX-FileCopyrightText: 2017-2026 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: LGPL-3.0-only
# https://tools.ietf.org/html/draft-cavage-http-signatures-08
defmodule HTTPSignatures do
@moduledoc """
HTTP Signatures library.
"""
require Logger
@doc "Split raw signature string into a Map of individual components."
@spec split_signature(binary()) :: map()
def split_signature(sig) do
default = %{"headers" => "date"}
sig =
sig
|> String.trim()
|> String.split(",")
|> Enum.reduce(default, fn part, acc ->
[key | rest] = String.split(part, "=")
value = Enum.join(rest, "=")
Map.put(acc, key, String.trim(value, "\""))
end)
Map.put(sig, "headers", String.split(sig["headers"], ~r/\s/))
end
@spec validate(map(), map(), any()) :: boolean()
def validate(headers, signature, public_key) do
# Request-target isn't supposed to be in request headers coming from the outside,
# it is supposed to be added by the validate_conn/1 caller prior to calling us.
with :ok <- validate_required_headers(headers, signature["headers"]) do
sigstring = build_signing_string(headers, signature["headers"])
Logger.debug("Signature: #{signature["signature"]}")
Logger.debug("Sigstring: #{sigstring}")
{:ok, sig} = Base.decode64(signature["signature"])
:public_key.verify(sigstring, :sha256, sig, public_key)
else
{:error, :host_header} ->
Logger.error("#{__MODULE__}: required header Host not found in signature headers")
false
{:error, :request_target_header} ->
Logger.error(
"#{__MODULE__}: required header (request-target) or @request-target not found in signature headers"
)
false
end
end
@doc "Fetch public key and validate signature."
- @spec validate_conn(%Plug.Conn{}) :: boolean()
+ @spec validate_conn(Plug.Conn.t()) :: boolean()
def validate_conn(conn) do
adapter = Application.get_env(:http_signatures, :adapter)
with {:ok, public_key} <- adapter.fetch_public_key(conn) do
case validate_conn(conn, public_key) do
true ->
true
false ->
refetch_key_and_retry(conn, adapter)
end
else
e ->
Logger.debug("Could not validate against known public keys: #{inspect(e)}")
false
end
end
@doc "Validate signature using specified public key."
- @spec validate_conn(%Plug.Conn{}, any()) :: boolean()
+ @spec validate_conn(Plug.Conn.t(), any()) :: boolean()
def validate_conn(conn, public_key) do
headers = Enum.into(conn.req_headers, %{})
signature = split_signature(headers["signature"])
validate(headers, signature, public_key)
end
@doc "Get signature for conn in split form."
- @spec signature_for_conn(%Plug.Conn{}) :: map()
+ @spec signature_for_conn(Plug.Conn.t()) :: map()
def signature_for_conn(conn) do
with headers <- Enum.into(conn.req_headers, %{}),
signature when is_binary(signature) <- headers["signature"] do
split_signature(signature)
else
_ ->
%{}
end
end
@spec build_signing_string(map(), list()) :: String.t()
def build_signing_string(headers, used_headers) do
used_headers
|> Enum.map_join("\n", fn header -> "#{header}: #{headers[header]}" end)
end
@doc "Create rsa-sha256 signature."
@spec sign(any(), String.t(), map()) :: String.t()
def sign(private_key, key_id, headers) do
sigstring = build_signing_string(headers, Enum.sort(Map.keys(headers)))
signature =
:public_key.sign(sigstring, :sha256, private_key)
|> Base.encode64()
[
keyId: key_id,
algorithm: "rsa-sha256",
headers: Map.keys(headers) |> Enum.sort() |> Enum.join(" "),
signature: signature
]
|> Enum.map_join(",", fn {k, v} -> "#{k}=\"#{v}\"" end)
end
defp empty?(nil), do: true
defp empty?(""), do: true
defp empty?(_), do: false
defp has_header?(header_name, req_headers, sig_headers),
do: header_name in sig_headers && !empty?(req_headers[header_name])
defp has_request_target?(req_headers, sig_headers) do
has_header?("(request-target)", req_headers, sig_headers) ||
has_header?("@request-target", req_headers, sig_headers)
end
defp validate_required_headers(req_headers, sig_headers) do
cond do
not has_header?("host", req_headers, sig_headers) -> {:error, :host_header}
not has_request_target?(req_headers, sig_headers) -> {:error, :request_target_header}
true -> :ok
end
end
- @spec refetch_key_and_retry(%Plug.Conn{}, any()) :: boolean()
+ @spec refetch_key_and_retry(Plug.Conn.t(), any()) :: boolean()
defp refetch_key_and_retry(conn, adapter) do
Logger.debug("Could not validate, trying to refetch any relevant keys")
with {:ok, public_key} <- adapter.refetch_public_key(conn) do
validate_conn(conn, public_key)
else
_ ->
Logger.error("Could not validate after refetching relevant keys")
false
end
end
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Aug 9, 10:43 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1722838
Default Alt Text
(8 KB)
Attached To
Mode
R18 http_signatures
Attached
Detach File
Event Timeline
Log In to Comment