Removed Jahfer's clj-activitypub library.

Will, but have not yet, add a dependency on it.
This commit is contained in:
Simon Brooke 2022-12-29 23:16:46 +00:00
parent 09ebdafff5
commit c18a0a5b8e
7 changed files with 0 additions and 302 deletions

0
doc/Per-User Database Normal file
View file

View file

@ -1,3 +0,0 @@
# NOTE
Files in this directory are copied from [Jahfer's clj-activitypub library](https://github.com/jahfer/clj-activitypub). If and when Jahfer issues a release of that library, this directory will be deleted and a dependency on that library will be added to the project.

View file

@ -1,150 +0,0 @@
(ns clj-activitypub.core
"copied from [Jahfer's clj-activitypub library](https://github.com/jahfer/clj-activitypub).
If and when Jahfer issues a release of that library, this directory will be deleted and a
dependency on that library will be added to the project."
(:require [clj-activitypub.internal.crypto :as crypto]
[clj-activitypub.internal.thread-cache :as thread-cache]
[clj-activitypub.internal.http-util :as http]
[clj-http.client :as client]
[clojure.string :as str]))
(defn config
"Creates hash of computed data relevant for most ActivityPub utilities."
[{:keys [domain username username-route public-key private-key]
:or {username-route "/users/"
public-key nil
private-key nil}}]
(let [base-url (str "https://" domain)]
{:domain domain
:base-url base-url
:username username
:user-id (str base-url username-route username)
:public-key public-key
:private-key (when private-key
(crypto/private-key private-key))}))
(defn parse-account
"Given an ActivityPub handle (e.g. @jahfer@mastodon.social), produces
a map containing {:domain ... :username ...}."
[handle]
(let [[username domain] (filter #(not (str/blank? %))
(str/split handle #"@"))]
{:domain domain :username username}))
(def ^:private user-cache (thread-cache/make))
(defn fetch-user
"Fetches the customer account details located at user-id from a remote
server. Will return cached results if they exist in memory."
[user-id]
((:get-v user-cache)
user-id
#(:body
(client/get user-id {:as :json-string-keys
:throw-exceptions false
:ignore-unknown-host? true
:headers {"Accept" "application/activity+json"}}))))
(defn actor
"Accepts a config, and returns a map in the form expected by the ActivityPub
spec. See https://www.w3.org/TR/activitypub/#actor-objects for reference."
[{:keys [user-id username public-key]}]
{"@context" ["https://www.w3.org/ns/activitystreams"
"https://w3id.org/security/v1"]
:id user-id
:type "Person"
:preferredUsername username
:inbox (str user-id "/inbox")
:outbox (str user-id "/outbox")
:publicKey {:id (str user-id "#main-key")
:owner user-id
:publicKeyPem (or public-key "")}})
(def signature-headers ["(request-target)" "host" "date" "digest"])
(defn- str-for-signature [headers]
(let [headers-xf (reduce-kv
(fn [m k v]
(assoc m (str/lower-case k) v)) {} headers)]
(->> signature-headers
(select-keys headers-xf)
(reduce-kv (fn [coll k v] (conj coll (str k ": " v))) [])
(interpose "\n")
(apply str))))
(defn gen-signature-header
"Generates a HTTP Signature string based on the provided map of headers."
[config headers]
(let [{:keys [user-id private-key]} config
string-to-sign (str-for-signature headers)
signature (crypto/base64-encode (crypto/sign string-to-sign private-key))
sig-header-keys {"keyId" user-id
"headers" (str/join " " signature-headers)
"signature" signature}]
(->> sig-header-keys
(reduce-kv (fn [m k v]
(conj m (str k "=" "\"" v "\""))) [])
(interpose ",")
(apply str))))
(defn auth-headers
"Given a config and request map of {:body ... :headers ...}, returns the
original set of headers with Signature and Digest attributes appended."
[config {:keys [body headers]}]
(let [digest (http/digest body)
h (-> headers
(assoc "Digest" digest)
(assoc "(request-target)" "post /inbox"))]
(assoc headers
"Signature" (gen-signature-header config h)
"Digest" digest)))
(defmulti obj
"Produces a map representing an ActivityPub object which can be serialized
directly to JSON in the form expected by the ActivityStreams 2.0 spec.
See https://www.w3.org/TR/activitystreams-vocabulary/ for reference."
(fn [_config object-data] (:type object-data)))
(defmethod obj :note
[{:keys [user-id]}
{:keys [id published inReplyTo content to]
:or {published (http/date)
inReplyTo ""
to "https://www.w3.org/ns/activitystreams#Public"}}]
{"id" (str user-id "/notes/" id)
"type" "Note"
"published" published
"attributedTo" user-id
"inReplyTo" inReplyTo
"content" content
"to" to})
(defmulti activity
"Produces a map representing an ActivityPub activity which can be serialized
directly to JSON in the form expected by the ActivityStreams 2.0 spec.
See https://www.w3.org/TR/activitystreams-vocabulary/ for reference."
(fn [_config activity-type _data] activity-type))
(defmethod activity :create [{:keys [user-id]} _ data]
{"@context" ["https://www.w3.org/ns/activitystreams"
"https://w3id.org/security/v1"]
"type" "Create"
"actor" user-id
"object" data})
(defmethod activity :delete [{:keys [user-id]} _ data]
{"@context" ["https://www.w3.org/ns/activitystreams"
"https://w3id.org/security/v1"]
"type" "Delete"
"actor" user-id
"object" data})
(defn with-config
"Returns curried forms of the #activity and #obj multimethods in the form
{:activity ... :obj ...}, with the initial parameter set to config."
[config]
(let [f (juxt
#(partial activity %)
#(partial obj %))
[activity-fn obj-fn] (f config)]
{:activity activity-fn
:obj obj-fn}))

View file

@ -1,39 +0,0 @@
(ns clj-activitypub.internal.crypto
"copied from [Jahfer's clj-activitypub library](https://github.com/jahfer/clj-activitypub).
If and when Jahfer issues a release of that library, this directory will be deleted and a
dependency on that library will be added to the project."
(:require [clojure.java.io :as io])
(:import (java.util Base64)
(java.security MessageDigest SecureRandom Signature)))
(java.security.Security/addProvider
(org.bouncycastle.jce.provider.BouncyCastleProvider.))
(defn- keydata [reader]
(->> reader
(org.bouncycastle.openssl.PEMParser.)
(.readObject)))
(defn- pem-string->key-pair [string]
(let [kd (keydata (io/reader (.getBytes string)))]
(.getKeyPair (org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter.) kd)))
(defn private-key [private-pem-str]
(-> private-pem-str
(pem-string->key-pair)
(.getPrivate)))
(defn base64-encode [bytes]
(.encodeToString (Base64/getEncoder) bytes))
(defn sha256-base64 [data]
(let [digest (.digest (MessageDigest/getInstance "SHA-256") (.getBytes data))]
(base64-encode digest)))
(defn sign [data private-key]
(let [bytes (.getBytes data)
signer (doto (Signature/getInstance "SHA256withRSA")
(.initSign private-key (SecureRandom.))
(.update bytes))]
(.sign signer)))

View file

@ -1,28 +0,0 @@
(ns clj-activitypub.internal.http-util
"copied from [Jahfer's clj-activitypub library](https://github.com/jahfer/clj-activitypub).
If and when Jahfer issues a release of that library, this directory will be deleted and a
dependency on that library will be added to the project."
(:require [clj-activitypub.internal.crypto :as crypto])
(:import (java.net URLEncoder)
(java.time OffsetDateTime ZoneOffset)
(java.time.format DateTimeFormatter)))
(defn encode-url-params [params]
(->> params
(reduce-kv
(fn [coll k v]
(conj coll
(str (URLEncoder/encode (name k)) "=" (URLEncoder/encode (str v)))))
[])
(interpose "&")
(apply str)))
(defn date []
(-> (OffsetDateTime/now (ZoneOffset/UTC))
(.format DateTimeFormatter/RFC_1123_DATE_TIME)))
(defn digest
"Accepts body from HTTP request and generates string
for use in HTTP `Digest` request header."
[body]
(str "sha-256=" (crypto/sha256-base64 body)))

View file

@ -1,47 +0,0 @@
(ns clj-activitypub.internal.thread-cache
"copied from [Jahfer's clj-activitypub library](https://github.com/jahfer/clj-activitypub).
If and when Jahfer issues a release of that library, this directory will be deleted and a
dependency on that library will be added to the project.")
(defn- current-time
"Returns current time using UNIX epoch."
[]
(System/currentTimeMillis))
(defn- update-read-at [store k v]
(dosync
(commute store assoc k
(merge v {:read-at (current-time)}))))
(defn make
"Creates a thread-local cache."
([] (make false))
([cache-if-nil]
(let [store (ref {})]
(letfn [(cache-kv ([k v]
(dosync
(commute store assoc k
{:write-at (current-time)
:read-at (current-time)
:value v})
v)))
(get-v ([k]
(when-let [data (get @store k)]
(update-read-at store k data)
(:value data)))
([k compute-fn]
(let [storage @store]
(if (contains? storage k)
(get-v k)
(let [v (compute-fn)]
(when (or (not (nil? v)) cache-if-nil)
(cache-kv k v)
(get-v k)))))))
(lru ([]
(mapv
(fn [[k v]] [k (:value v)])
(sort-by #(-> % val :read-at) < @store))))]
{:cache-kv cache-kv
:get-v get-v
:cache-if-nil cache-if-nil
:lru lru}))))

View file

@ -1,35 +0,0 @@
(ns clj-activitypub.webfinger
"copied from [Jahfer's clj-activitypub library](https://github.com/jahfer/clj-activitypub).
If and when Jahfer issues a release of that library, this directory will be deleted and a
dependency on that library will be added to the project."
(:require [clj-http.client :as client]
[clj-activitypub.internal.http-util :as http]
[clj-activitypub.internal.thread-cache :as thread-cache]))
(def remote-uri-path "/.well-known/webfinger")
(defn- resource-str [domain username]
(str "acct:" username "@" domain))
(defn resource-url
"Builds a URL pointing to the user's account on the remote server."
[domain username & [params]]
(let [resource (resource-str domain username)
query-str (http/encode-url-params (merge params {:resource resource}))]
(str "https://" domain remote-uri-path "?" query-str)))
(def ^:private user-id-cache
(thread-cache/make))
(defn fetch-user-id
"Follows the webfinger request to a remote domain, retrieving the ID of the requested
account. Typically returns a string in the form of a URL."
[domain username]
((:get-v user-id-cache)
(str domain "@" username) ;; cache key
(fn []
(let [response (some-> (resource-url domain username {:rel "self"})
(client/get {:as :json :throw-exceptions false :ignore-unknown-host? true}))]
(some->> response :body :links
(some #(when (= (:type %) "application/activity+json") %))
:href)))))