diff --git a/docs/cloverage/codecov.json b/docs/cloverage/codecov.json index f90d8b1..241fe56 100644 --- a/docs/cloverage/codecov.json +++ b/docs/cloverage/codecov.json @@ -7,7 +7,7 @@ null, null, null, null, null, null, null, null, null, null, null, 40, null, 0, null, 1, null, null, null, null, null, true, 4, null, null, null, null, 1, null, null, null, null, 1, null, null, null, - null, null, true, null, null, 5, null, true, null, 1, null, null, + null, null, true, null, null, 5, null, null, null, 1, null, true, null, null, 1, null, null, null, null, true, null, null, 5, null, null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, true, 2, 2, 0, 0, 0, null, null, 1, null, @@ -51,15 +51,20 @@ "dog_and_duck/quack/picky.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, 1, null, null, null, null, - null, null, null, null, null, null, 1, null, 1, null, 1, 1, 1, 1, 1, - null, 1, null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, null, 1, null, - null, null, null, 1, null, null, null, null, 1, null, null, null, - null, null, null, true, 10, 9, true, 2, null, null, null, true, - null, null, 3, null, null, null, 1, null, null, null, null, null, - null, 0, 0, 0, 0, null, 0, null, 0, null, 0, 0, 0, null, 1, null, 0, - 0, 0, 0, 0, 0, null, null, null, null, null, 0, 0, null, null, null, - null, 0, 0, null, null, null, null, null, null, null], + null, null, null, null, null, null, null, null, null, null, null, 1, + null, null, null, null, null, null, null, null, null, null, 1, null, + 1, null, 1, 1, 1, 1, 1, null, 1, null, null, null, true, true, 7, 7, + 7, 7, 12, 7, null, 1, null, null, null, null, 1, null, null, null, + null, 1, null, null, null, null, null, null, true, 22, 18, true, 2, + null, null, null, 12, null, null, 3, null, 1, null, null, null, + null, null, null, null, null, 14, 14, 14, 14, null, 14, null, 14, + null, 14, 14, 14, null, 1, null, null, 12, 12, 12, 12, 1, null, + null, 12, 12, 3, null, null, 12, 3, null, null, 12, 4, null, null, + 12, null, 1, null, null, 5, 5, 5, 5, 5, 4, 3, 1, null, 1, null, 0, + 1, 5, null], + "dog_and_duck/quack/fault_messages.clj": + [null, 1, null, 1, null, 1, null, null, null, null, null, null, + null], "dog_and_duck/scratch/core.clj":[null, 1, null, 1, null, null, 0], "clj_activitypub/webfinger.clj": [null, 1, null, null, null, null, null, null, null, 1, null, 1, 1, diff --git a/docs/cloverage/coverage.xml b/docs/cloverage/coverage.xml index 2594ab7..53acaeb 100644 --- a/docs/cloverage/coverage.xml +++ b/docs/cloverage/coverage.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/cloverage/dog_and_duck/quack/fault_messages.clj.html b/docs/cloverage/dog_and_duck/quack/fault_messages.clj.html new file mode 100644 index 0000000..36cfe2a --- /dev/null +++ b/docs/cloverage/dog_and_duck/quack/fault_messages.clj.html @@ -0,0 +1,44 @@ + + + + dog_and_duck/quack/fault_messages.clj + + + + 001  (ns dog-and-duck.quack.fault-messages) +
+ + 002   +
+ + 003  (def messages +
+ + 004    "Actual fault messages to which fault codes resolve." +
+ + 005    {:id-not-https "Publicly facing content SHOULD use HTTPS URIs" +
+ + 006     :id-not-uri "identifiers must be publicly dereferencable URIs" +
+ + 007     :no-context "Section 3 of the ActivityPub specification states Implementers SHOULD include the ActivityPub context in their object definitions`." +
+ + 008     :no-id-persistent "Persistent objects MUST have unique global identifiers." +
+ + 009     :no-id-transient "The ActivityPub specification allows objects without `id` fields only if they are intentionally transient; even so it is preferred that the object should have an explicit null id." +
+ + 010     :null-id-persistent "Persistent objects MUST have non-null identifiers." +
+ + 011     :no-type "The ActivityPub specification states that the `type` field is optional, but it is hard to process objects with no known type." +
+ + 012     :not-an-object "ActivityStreams object must be JSON objects."}) +
+ + diff --git a/docs/cloverage/dog_and_duck/quack/picky.clj.html b/docs/cloverage/dog_and_duck/quack/picky.clj.html index 82aa1dd..ba4a0b1 100644 --- a/docs/cloverage/dog_and_duck/quack/picky.clj.html +++ b/docs/cloverage/dog_and_duck/quack/picky.clj.html @@ -14,424 +14,481 @@ 003                                Generally, each `-faults` function will return:
- 004                                1. `nil` if no faults were found; + 004                                
- 005                                2. a sequence of fault objects if faults were found. + 005                                1. `nil` if no faults were found;
- 006                                 + 006                                2. a sequence of fault objects if faults were found.
- 007                                Each fault object shall have the properties: + 007                                
- 008                                1. `:@context` whose value shall be the URL of a  + 008                                Each fault object shall have the properties:
- 009                                   document specifying this vocabulary; + 009                                
- 010                                2. `:type` whose value shall be `Fault`; + 010                                1. `:@context` whose value shall be the URL of a 
- 011                                3. `:severity` whose value shall be one of  + 011                                   document specifying this vocabulary;
- 012                                   `minor`, `should`, `must` or `critical`; + 012                                2. `:type` whose value shall be `Fault`;
- 013                                4. `:fault` whose value shall be a unique token + 013                                3. `:severity` whose value shall be one of 
- 014                                   representing the particular fault type; + 014                                   `minor`, `should`, `must` or `critical`;
- 015                                5. `:narrative` whose value shall be a natural + 015                                4. `:fault` whose value shall be a unique token
- 016                                   language description of the fault type. + 016                                   representing the particular fault type;
- 017                                 + 017                                5. `:narrative` whose value shall be a natural
- 018                                Note that the reason for the `:fault` property is + 018                                   language description of the fault type.
- 019                                to be able to have a well known place, linked to + 019                                
- 020                                from the @context URL, which allows narratives  + 020                                Note that the reason for the `:fault` property is
- 021                                for each fault type to be served in as many + 021                                to be able to have a well known place, linked to
- 022                                natural languages as possible. + 022                                from the @context URL, which allows narratives 
- 023                                 + 023                                for each fault type to be served in as many
- 024                                The idea further is that it should ultimately be + 024                                natural languages as possible.
- 025                                possible to serialise a fault report as a  + 025                                
- 026                                document which in its own right conforms to the + 026                                The idea further is that it should ultimately be
- 027                                ActivityStreams spec." + 027                                possible to serialise a fault report as a 
- 028   (:require [dog-and-duck.utils.process :refer [pid]])) + 028                                document which in its own right conforms to the +
+ + 029                                ActivityStreams spec." +
+ + 030      (:require [dog-and-duck.quack.fault-messages :refer [messages]] +
+ + 031                [dog-and-duck.utils.process :refer [pid]]) +
+ + 032      (:import [java.net URI URISyntaxException]))
- 029   + 033  
- 030  (def ^:const severity + 034  (def ^:const severity
- 031    "Severity of faults found, as follows: + 035    "Severity of faults found, as follows:
- 032      + 036     
- 033     1. `:minor` things which I consider to be faults, but which  + 037     1. `:minor` things which I consider to be faults, but which 
- 034        don't actually breach the spec; + 038        don't actually breach the spec;
- 035     2. `:should` instances where the spec says something SHOULD + 039     2. `:should` instances where the spec says something SHOULD
- 036        be done, which isn't; + 040        be done, which isn't;
- 037     3. `:must` instances where the spec says something MUST + 041     3. `:must` instances where the spec says something MUST
- 038        be done, which isn't; + 042        be done, which isn't;
- 039     4. `:critical` instances where I believe the fault means that + 043     4. `:critical` instances where I believe the fault means that
- 040        the object cannot be meaningfully processed." + 044        the object cannot be meaningfully processed."
- 041    #{:minor :should :must :critical}) + 045    #{:minor :should :must :critical})
- 042   + 046  
- 043  (def ^:const severity-filters + 047  (def ^:const severity-filters
- 044    "Hack for implementing a severity hierarchy" + 048    "Hack for implementing a severity hierarchy"
- 045    {:all #{} + 049    {:all #{}
- 046     :minor #{:minor} + 050     :minor #{:minor}
- 047     :should #{:minor :should} + 051     :should #{:minor :should}
- 048     :must #{:minor :should :must} + 052     :must #{:minor :should :must}
- 049     :critical severity}) + 053     :critical severity})
- 050   + 054  
- 051  (defn filter-severity + 055  (defn filter-severity
- 052    "Return a list of reports taken from these `reports` where the severity + 056    "Return a list of reports taken from these `reports` where the severity
- 053     of the report is greater than this `severity`." + 057     of the report is greater than this `severity`."
- 054    [reports severity] + 058    [reports severity]
- - 055    (assert  + + 059    (assert
- - 056     (and  + + 060     (and
- - 057      (coll? reports)  + + 061      (coll? reports)
- - 058      (every? map? reports)  -
- - 059      (every? :severity reports))) -
- - 060    (remove  -
- - 061     #((severity-filters severity) (:severity %)) -
- - 062     reports)) -
- - 063   + + 062      (every? map? reports)
- 064  (def ^:const activitystreams-context-uri + 063      (every? :severity reports)))
- - 065    "The URI of the context of an ActivityStreams object is expected to be this + + 064    (remove
- - 066     literal string." -
- - 067    "https://www.w3.org/ns/activitystreams") -
- - 068   -
- - 069  (def ^:const validation-fault-context-uri -
- - 070    "The URI of the context of a validation fault report object shall be this -
- - 071     literal string." -
- - 072    "https://simon-brooke.github.io/dog-and-duck/codox/Validation_Faults.html") -
- - 073   + + 065     #((severity-filters severity) (:severity %))
- 074  (defn context? + 066     reports)) +
+ + 067   +
+ + 068  (def ^:const activitystreams-context-uri
- 075    "Returns `true` iff `x` quacks like an ActivityStreams context, else false. + 069    "The URI of the context of an ActivityStreams object is expected to be this
- 076      + 070     literal string."
- 077     A context is either + 071    "https://www.w3.org/ns/activitystreams") +
+ + 072   +
+ + 073  (def ^:const validation-fault-context-uri
- 078     1. the URI (actually an IRI) `activitystreams-context-uri`, or + 074    "The URI of the context of a validation fault report object shall be this
- 079     2. a collection comprising that URI and a map." + 075     literal string."
- 080    [x] + 076    "https://simon-brooke.github.io/dog-and-duck/codox/Validation_Faults.html") +
+ + 077   +
+ + 078  (defn context? +
+ + 079    "Returns `true` iff `x` quacks like an ActivityStreams context, else false. +
+ + 080      +
+ + 081     A context is either +
+ + 082     1. the URI (actually an IRI) `activitystreams-context-uri`, or +
+ + 083     2. a collection comprising that URI and a map." +
+ + 084    [x]
- 081    (cond + 085    (cond
- 082      (nil? x) false + 086      (nil? x) false
- 083      (string? x) (and (= x activitystreams-context-uri) true) + 087      (string? x) (and (= x activitystreams-context-uri) true)
- 084      (coll? x) (and (context? (first (remove map? x))) + 088      (coll? x) (and (context? (first (remove map? x)))
- 085                     (= (count x) 2) + 089                     (= (count x) 2)
- 086                     true) + 090                     true)
- 087      :else false)) + 091      :else false))
- 088   + 092  
- - 089  (defmacro has-context? + + 093  (defmacro has-context?
- 090    "True if `x` is an ActivityStreams object with a valid context, else `false`." + 094    "True if `x` is an ActivityStreams object with a valid context, else `false`."
- 091    [x] + 095    [x]
- 092    `(context? ((keyword "@context") ~x))) + 096    `(context? ((keyword "@context") ~x)))
- 093   -
- - 094   -
- - 095   + 097  
- 096  (defn make-fault-object + 098  (defn make-fault-object
- 097    "Return a fault object with these `severity`, `fault` and `narrative` values. + 099    "Return a fault object with these `severity`, `fault` and `narrative` values.
- 098      + 100     
- 099     An ActivityPub object MUST have a globally unique ID. Whether this is  + 101     An ActivityPub object MUST have a globally unique ID. Whether this is 
- 100     meaningful depends on whether we persist fault report objects and serve + 102     meaningful depends on whether we persist fault report objects and serve
- 101     them, which at present I have no plans to do." + 103     them, which at present I have no plans to do."
- 102    [severity fault narrative] + 104    ;; TODO: should not pass in the narrative; instead should use the :fault value
- - 103    (assoc {} + + 105    ;; to look up the narrative in a resource file. +
+ + 106    [severity fault] +
+ + 107    (assoc {} +
+ + 108           (keyword "@context") validation-fault-context-uri +
+ + 109           :id (str "https://" +
+ + 110                    (.. java.net.InetAddress getLocalHost getHostName) +
+ + 111                    "/fault/" +
+ + 112                    pid +
+ + 113                    ":" +
+ + 114                    (inst-ms (java.util.Date.))) +
+ + 115           :type "Fault" +
+ + 116           :severity severity +
+ + 117           :fault fault +
+ + 118           :narrative (messages fault))) +
+ + 119   +
+ + 120  (defn object-faults +
+ + 121    "Return a list of faults found in object `x`, or `nil` if none are." +
+ + 122    [x] +
+ + 123    (let [faults (remove +
+ + 124                  empty? +
+ + 125                  (list +
+ + 126                   (when-not (map? x) +
+ + 127                     (make-fault-object +
+ + 128                      :critical +
+ + 129                      :not-an-object)) +
+ + 130                   (when-not +
+ + 131                    (has-context? x) +
+ + 132                     (make-fault-object +
+ + 133                      :should +
+ + 134                      :no-context)) +
+ + 135                   (when-not (:type x) +
+ + 136                     (make-fault-object +
+ + 137                      :minor +
+ + 138                      :no-type)) +
+ + 139                   (when-not (and (map? x) (contains? x :id)) +
+ + 140                     (make-fault-object +
+ + 141                      :minor +
+ + 142                      :no-id-transient))))] +
+ + 143      (if (empty? faults) nil faults))) +
+ + 144   +
+ + 145  (defn persistent-object-faults +
+ + 146    "Return a list of faults found in persistent object `x`, or `nil` if none are." +
+ + 147    [x] +
+ + 148    (let [faults (concat +
+ + 149                  (object-faults x) +
+ + 150                  (remove empty? +
+ + 151                          (list +
+ + 152                           (if (contains? x :id) +
+ + 153                             (try (let [id (URI. (:id x))] +
+ + 154                                    (when-not (= (.getScheme id) "https") +
+ + 155                                      (make-fault-object :should :id-not-https))) +
+ + 156                                  (catch URISyntaxException _ +
+ + 157                                    (make-fault-object :must :id-not-uri)) +
+ + 158                                  (catch NullPointerException _
- 104           (keyword "@context") validation-fault-context-uri + 159                                    (make-fault-object :must :null-id-persistent)))
- - 105           :id (str "https://" + + 160                             (make-fault-object :must :no-id-persistent)))))]
- - 106                    (.. java.net.InetAddress getLocalHost getHostName) -
- - 107                    "/fault/" -
- - 108                    pid -
- - 109                    ":" -
- - 110                    (inst-ms (java.util.Date.))) -
- - 111           :type "Fault" -
- - 112           :severity severity -
- - 113           :fault fault -
- - 114           :narrative narrative)) + + 161      (if (empty? faults) nil faults)))
- 115   -
- - 116  (defn object-faults -
- - 117    [x] -
- - 118    (remove  -
- - 119     empty? -
- - 120     (list -
- - 121      (when-not -
- - 122       (has-context? x) -
- - 123        (make-fault-object  -
- - 124         :should  -
- - 125         :no-context  -
- - 126         "Section 3 of the ActivityPub specification states  -
- - 127          `Implementers SHOULD include the ActivityPub context in  -
- - 128          their object definitions`.") -
- - 129      (when-not (:type x)  -
- - 130        (make-fault-object  -
- - 131         :minor  -
- - 132         :no-type -
- - 133         "The ActivityPub specification states that the `type` field is -
- - 134          optional, but it is hard to process objects with no known type.")) -
- - 135        (when-not (contains? x :id) -
- - 136          (make-fault-object -
- - 137           :minor -
- - 138           :no-id-transient -
- - 139           "The ActivityPub specification allows objects without `id` fields -
- - 140            only if they are intentionally transient; even so it is preferred -
- - 141            that the object should have an explicit null id." -
- - 142           )) -
- - 143      )))) + 162  
diff --git a/docs/cloverage/dog_and_duck/quack/quack.clj.html b/docs/cloverage/dog_and_duck/quack/quack.clj.html index e8bef6e..4318f94 100644 --- a/docs/cloverage/dog_and_duck/quack/quack.clj.html +++ b/docs/cloverage/dog_and_duck/quack/quack.clj.html @@ -268,7 +268,7 @@ 088  
- + 089  ;; (actor-type? "Group")
@@ -280,7 +280,7 @@ 092    "The set of types we will accept as verbs.
- + 093     
diff --git a/docs/cloverage/index.html b/docs/cloverage/index.html index 6d990c7..6f54e71 100644 --- a/docs/cloverage/index.html +++ b/docs/cloverage/index.html @@ -94,21 +94,32 @@ 35518 - dog-and-duck.quack.picky
113
114
-49.78 % + dog-and-duck.quack.fault-messages
19
+100.00 %
19
3
27
-44.90 % -1431149 + style="width:100.0%; + float:left;"> 3 +100.00 % +1213 + + + dog-and-duck.quack.picky
283
18
+94.02 % +
60
4
1
+98.46 % +1621165 dog-and-duck.quack.quack
Totals: -53.33 % +61.92 % -58.24 % +67.69 % diff --git a/docs/codox/Using_ActivityPub.html b/docs/codox/Using_ActivityPub.html index 36f193d..7c66e9d 100644 --- a/docs/codox/Using_ActivityPub.html +++ b/docs/codox/Using_ActivityPub.html @@ -1,5 +1,5 @@ -Using ActivityPub

Using ActivityPub

+Using ActivityPub

Using ActivityPub

Introduction

I do not know what I am doing; I am learning, and playing. Nothing in this document should be treated as good advice; it simply relates to the current state of my knowledge.

\ No newline at end of file diff --git a/docs/codox/Validation_Faults.html b/docs/codox/Validation_Faults.html index 09bf6f4..456b40f 100644 --- a/docs/codox/Validation_Faults.html +++ b/docs/codox/Validation_Faults.html @@ -1,6 +1,6 @@ -Validation Faults in ActivityPub documents

Validation Faults in ActivityPub documents

+Validation Faults in ActivityPub documents

Validation Faults in ActivityPub documents

Motivation

This document is intended to provide an extension vocabulary for ActivityStreams documents, which provides vocabulary for categorising and describing faults in ActivityPub documents.

The motivation is to be able to serialise a validation report on an ActivityPub document as an ActivityStreams document.

diff --git a/docs/codox/clj-activitypub.core.html b/docs/codox/clj-activitypub.core.html index 337fb9a..bfbb205 100644 --- a/docs/codox/clj-activitypub.core.html +++ b/docs/codox/clj-activitypub.core.html @@ -1,3 +1,3 @@ -clj-activitypub.core documentation

clj-activitypub.core

copied from Jahfer’s clj-activitypub library. 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.

activity

multimethod

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.

actor

(actor {:keys [user-id username public-key]})

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.

auth-headers

(auth-headers config {:keys [body headers]})

Given a config and request map of {:body … :headers …}, returns the original set of headers with Signature and Digest attributes appended.

config

(config {:keys [domain username username-route public-key private-key], :or {username-route "/users/", public-key nil, private-key nil}})

Creates hash of computed data relevant for most ActivityPub utilities.

fetch-user

(fetch-user user-id)

Fetches the customer account details located at user-id from a remote server. Will return cached results if they exist in memory.

gen-signature-header

(gen-signature-header config headers)

Generates a HTTP Signature string based on the provided map of headers.

obj

multimethod

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.

parse-account

(parse-account handle)

Given an ActivityPub handle (e.g. @jahfer@mastodon.social), produces a map containing {:domain … :username …}.

signature-headers

TODO: write docs

with-config

(with-config config)

Returns curried forms of the #activity and #obj multimethods in the form {:activity … :obj …}, with the initial parameter set to config.

\ No newline at end of file +clj-activitypub.core documentation

clj-activitypub.core

copied from Jahfer’s clj-activitypub library. 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.

activity

multimethod

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.

actor

(actor {:keys [user-id username public-key]})

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.

auth-headers

(auth-headers config {:keys [body headers]})

Given a config and request map of {:body … :headers …}, returns the original set of headers with Signature and Digest attributes appended.

config

(config {:keys [domain username username-route public-key private-key], :or {username-route "/users/", public-key nil, private-key nil}})

Creates hash of computed data relevant for most ActivityPub utilities.

fetch-user

(fetch-user user-id)

Fetches the customer account details located at user-id from a remote server. Will return cached results if they exist in memory.

gen-signature-header

(gen-signature-header config headers)

Generates a HTTP Signature string based on the provided map of headers.

obj

multimethod

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.

parse-account

(parse-account handle)

Given an ActivityPub handle (e.g. @jahfer@mastodon.social), produces a map containing {:domain … :username …}.

signature-headers

TODO: write docs

with-config

(with-config config)

Returns curried forms of the #activity and #obj multimethods in the form {:activity … :obj …}, with the initial parameter set to config.

\ No newline at end of file diff --git a/docs/codox/clj-activitypub.internal.crypto.html b/docs/codox/clj-activitypub.internal.crypto.html index f97368e..8213e22 100644 --- a/docs/codox/clj-activitypub.internal.crypto.html +++ b/docs/codox/clj-activitypub.internal.crypto.html @@ -1,3 +1,3 @@ -clj-activitypub.internal.crypto documentation

clj-activitypub.internal.crypto

copied from Jahfer’s clj-activitypub library. 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.

base64-encode

(base64-encode bytes)

TODO: write docs

private-key

(private-key private-pem-str)

TODO: write docs

sha256-base64

(sha256-base64 data)

TODO: write docs

sign

(sign data private-key)

TODO: write docs

\ No newline at end of file +clj-activitypub.internal.crypto documentation

clj-activitypub.internal.crypto

copied from Jahfer’s clj-activitypub library. 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.

base64-encode

(base64-encode bytes)

TODO: write docs

private-key

(private-key private-pem-str)

TODO: write docs

sha256-base64

(sha256-base64 data)

TODO: write docs

sign

(sign data private-key)

TODO: write docs

\ No newline at end of file diff --git a/docs/codox/clj-activitypub.internal.http-util.html b/docs/codox/clj-activitypub.internal.http-util.html index 754d7a6..aabc4a3 100644 --- a/docs/codox/clj-activitypub.internal.http-util.html +++ b/docs/codox/clj-activitypub.internal.http-util.html @@ -1,3 +1,3 @@ -clj-activitypub.internal.http-util documentation

clj-activitypub.internal.http-util

copied from Jahfer’s clj-activitypub library. 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.

date

(date)

TODO: write docs

digest

(digest body)

Accepts body from HTTP request and generates string for use in HTTP Digest request header.

encode-url-params

(encode-url-params params)

TODO: write docs

\ No newline at end of file +clj-activitypub.internal.http-util documentation

clj-activitypub.internal.http-util

copied from Jahfer’s clj-activitypub library. 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.

date

(date)

TODO: write docs

digest

(digest body)

Accepts body from HTTP request and generates string for use in HTTP Digest request header.

encode-url-params

(encode-url-params params)

TODO: write docs

\ No newline at end of file diff --git a/docs/codox/clj-activitypub.internal.thread-cache.html b/docs/codox/clj-activitypub.internal.thread-cache.html index 6ea40ed..77a71ca 100644 --- a/docs/codox/clj-activitypub.internal.thread-cache.html +++ b/docs/codox/clj-activitypub.internal.thread-cache.html @@ -1,3 +1,3 @@ -clj-activitypub.internal.thread-cache documentation

clj-activitypub.internal.thread-cache

copied from Jahfer’s clj-activitypub library. 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.

make

(make)(make cache-if-nil)

Creates a thread-local cache.

\ No newline at end of file +clj-activitypub.internal.thread-cache documentation

clj-activitypub.internal.thread-cache

copied from Jahfer’s clj-activitypub library. 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.

make

(make)(make cache-if-nil)

Creates a thread-local cache.

\ No newline at end of file diff --git a/docs/codox/clj-activitypub.webfinger.html b/docs/codox/clj-activitypub.webfinger.html index 3b44a47..2cd570e 100644 --- a/docs/codox/clj-activitypub.webfinger.html +++ b/docs/codox/clj-activitypub.webfinger.html @@ -1,3 +1,3 @@ -clj-activitypub.webfinger documentation

clj-activitypub.webfinger

copied from Jahfer’s clj-activitypub library. 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.

fetch-user-id

(fetch-user-id domain username)

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.

remote-uri-path

TODO: write docs

resource-url

(resource-url domain username & [params])

Builds a URL pointing to the user’s account on the remote server.

\ No newline at end of file +clj-activitypub.webfinger documentation

clj-activitypub.webfinger

copied from Jahfer’s clj-activitypub library. 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.

fetch-user-id

(fetch-user-id domain username)

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.

remote-uri-path

TODO: write docs

resource-url

(resource-url domain username & [params])

Builds a URL pointing to the user’s account on the remote server.

\ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.fault-messages.html b/docs/codox/dog-and-duck.quack.fault-messages.html new file mode 100644 index 0000000..fbb29ad --- /dev/null +++ b/docs/codox/dog-and-duck.quack.fault-messages.html @@ -0,0 +1,3 @@ + +dog-and-duck.quack.fault-messages documentation

dog-and-duck.quack.fault-messages

TODO: write docs

messages

Actual fault messages to which fault codes resolve.

\ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.picky.html b/docs/codox/dog-and-duck.quack.picky.html index 3de3700..067ac88 100644 --- a/docs/codox/dog-and-duck.quack.picky.html +++ b/docs/codox/dog-and-duck.quack.picky.html @@ -1,6 +1,6 @@ -dog-and-duck.quack.picky documentation

dog-and-duck.quack.picky

Fault-finder for ActivityPub documents.

+dog-and-duck.quack.picky documentation

dog-and-duck.quack.picky

Fault-finder for ActivityPub documents.

Generally, each -faults function will return:

  1. nil if no faults were found;
  2. @@ -15,12 +15,12 @@
  3. :narrative whose value shall be a natural language description of the fault type.

Note that the reason for the :fault property is to be able to have a well known place, linked to from the @context URL, which allows narratives for each fault type to be served in as many natural languages as possible.

-

The idea further is that it should ultimately be possible to serialise a fault report as a document which in its own right conforms to the ActivityStreams spec.

activitystreams-context-uri

The URI of the context of an ActivityStreams object is expected to be this literal string.

context?

(context? x)

Returns true iff x quacks like an ActivityStreams context, else false.

-

A context is either 1. the URI (actually an IRI) activitystreams-context-uri, or 2. a collection comprising that URI and a map.

filter-severity

(filter-severity reports severity)

Return a list of reports taken from these reports where the severity of the report is greater than this severity.

has-context?

macro

(has-context? x)

True if x is an ActivityStreams object with a valid context, else false.

make-fault-object

(make-fault-object severity fault narrative)

Return a fault object with these severity, fault and narrative values.

-

An ActivityPub object MUST have a globally unique ID. Whether this is meaningful depends on whether we persist fault report objects and serve them, which at present I have no plans to do.

object-faults

(object-faults x)

TODO: write docs

severity

Severity of faults found, as follows:

+

The idea further is that it should ultimately be possible to serialise a fault report as a document which in its own right conforms to the ActivityStreams spec.

activitystreams-context-uri

The URI of the context of an ActivityStreams object is expected to be this literal string.

context?

(context? x)

Returns true iff x quacks like an ActivityStreams context, else false.

+

A context is either 1. the URI (actually an IRI) activitystreams-context-uri, or 2. a collection comprising that URI and a map.

filter-severity

(filter-severity reports severity)

Return a list of reports taken from these reports where the severity of the report is greater than this severity.

has-context?

macro

(has-context? x)

True if x is an ActivityStreams object with a valid context, else false.

make-fault-object

(make-fault-object severity fault)

Return a fault object with these severity, fault and narrative values.

+

An ActivityPub object MUST have a globally unique ID. Whether this is meaningful depends on whether we persist fault report objects and serve them, which at present I have no plans to do.

object-faults

(object-faults x)

Return a list of faults found in object x, or nil if none are.

persistent-object-faults

(persistent-object-faults x)

Return a list of faults found in persistent object x, or nil if none are.

severity

Severity of faults found, as follows:

  1. :minor things which I consider to be faults, but which don’t actually breach the spec;
  2. :should instances where the spec says something SHOULD be done, which isn’t;
  3. :must instances where the spec says something MUST be done, which isn’t;
  4. :critical instances where I believe the fault means that the object cannot be meaningfully processed.
  5. -

severity-filters

Hack for implementing a severity hierarchy

validation-fault-context-uri

The URI of the context of a validation fault report object shall be this literal string.

\ No newline at end of file +

severity-filters

Hack for implementing a severity hierarchy

validation-fault-context-uri

The URI of the context of a validation fault report object shall be this literal string.

\ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.quack.html b/docs/codox/dog-and-duck.quack.quack.html index 4a768c3..f2f9782 100644 --- a/docs/codox/dog-and-duck.quack.quack.html +++ b/docs/codox/dog-and-duck.quack.quack.html @@ -1,6 +1,6 @@ -dog-and-duck.quack.quack documentation

dog-and-duck.quack.quack

Validator for ActivityPub objects: if it walks like a duck, and it quacks like a duck…

+dog-and-duck.quack.quack documentation

dog-and-duck.quack.quack

Validator for ActivityPub objects: if it walks like a duck, and it quacks like a duck…

**NOTE THAT the ActivityPub spec says

Servers SHOULD validate the content they receive to avoid content spoofing attacks

diff --git a/docs/codox/dog-and-duck.scratch.core.html b/docs/codox/dog-and-duck.scratch.core.html index 8adeb3a..552db14 100644 --- a/docs/codox/dog-and-duck.scratch.core.html +++ b/docs/codox/dog-and-duck.scratch.core.html @@ -1,3 +1,3 @@ -dog-and-duck.scratch.core documentation

dog-and-duck.scratch.core

TODO: write docs

foo

(foo x)

I don’t do a whole lot.

\ No newline at end of file +dog-and-duck.scratch.core documentation

dog-and-duck.scratch.core

TODO: write docs

foo

(foo x)

I don’t do a whole lot.

\ No newline at end of file diff --git a/docs/codox/dog-and-duck.scratch.parser.html b/docs/codox/dog-and-duck.scratch.parser.html index b14fd92..f5e260d 100644 --- a/docs/codox/dog-and-duck.scratch.parser.html +++ b/docs/codox/dog-and-duck.scratch.parser.html @@ -1,3 +1,3 @@ -dog-and-duck.scratch.parser documentation

dog-and-duck.scratch.parser

TODO: write docs

clean

(clean json)

Take this json input, and return a sequence of ActivityPub objects represented by it.

\ No newline at end of file +dog-and-duck.scratch.parser documentation

dog-and-duck.scratch.parser

TODO: write docs

clean

(clean json)

Take this json input, and return a sequence of ActivityPub objects represented by it.

\ No newline at end of file diff --git a/docs/codox/dog-and-duck.scratch.scratch.html b/docs/codox/dog-and-duck.scratch.scratch.html index 4f30252..586cc4d 100644 --- a/docs/codox/dog-and-duck.scratch.scratch.html +++ b/docs/codox/dog-and-duck.scratch.scratch.html @@ -1,3 +1,3 @@ -dog-and-duck.scratch.scratch documentation

dog-and-duck.scratch.scratch

Scratchpad where I try to understand how to do this stuff.

account

Fetch my account to mess with

account-handle

TODO: write docs

handle

TODO: write docs

kp

TODO: write docs

rsa

TODO: write docs

\ No newline at end of file +dog-and-duck.scratch.scratch documentation

dog-and-duck.scratch.scratch

Scratchpad where I try to understand how to do this stuff.

account

Fetch my account to mess with

account-handle

TODO: write docs

handle

TODO: write docs

kp

TODO: write docs

rsa

TODO: write docs

\ No newline at end of file diff --git a/docs/codox/dog-and-duck.utils.process.html b/docs/codox/dog-and-duck.utils.process.html index 62f2844..5d78380 100644 --- a/docs/codox/dog-and-duck.utils.process.html +++ b/docs/codox/dog-and-duck.utils.process.html @@ -1,3 +1,3 @@ -dog-and-duck.utils.process documentation

dog-and-duck.utils.process

TODO: write docs

pid

OK, this is hacky as fuck, but I hope it works. The problem is that the way to get the process id has changed several times during the history of Java development, and the code for one version of Java won’t even compile in a different version.

\ No newline at end of file +dog-and-duck.utils.process documentation

dog-and-duck.utils.process

TODO: write docs

pid

OK, this is hacky as fuck, but I hope it works. The problem is that the way to get the process id has changed several times during the history of Java development, and the code for one version of Java won’t even compile in a different version.

\ No newline at end of file diff --git a/docs/codox/index.html b/docs/codox/index.html index c24d74c..c51768e 100644 --- a/docs/codox/index.html +++ b/docs/codox/index.html @@ -1,3 +1,3 @@ -Dog-and-duck 0.1.0-SNAPSHOT

Dog-and-duck 0.1.0-SNAPSHOT

Released under the GPL-2.0-or-later

A playground for hacking ActivityPub stuff.

Installation

To install, add the following dependency to your project or build file:

[dog-and-duck "0.1.0-SNAPSHOT"]

Topics

Namespaces

clj-activitypub.core

copied from Jahfer’s clj-activitypub library. 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.

clj-activitypub.internal.crypto

copied from Jahfer’s clj-activitypub library. 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.

Public variables and functions:

clj-activitypub.internal.http-util

copied from Jahfer’s clj-activitypub library. 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.

Public variables and functions:

clj-activitypub.internal.thread-cache

copied from Jahfer’s clj-activitypub library. 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.

Public variables and functions:

clj-activitypub.webfinger

copied from Jahfer’s clj-activitypub library. 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.

Public variables and functions:

dog-and-duck.scratch.core

TODO: write docs

Public variables and functions:

dog-and-duck.scratch.parser

TODO: write docs

Public variables and functions:

dog-and-duck.scratch.scratch

Scratchpad where I try to understand how to do this stuff.

Public variables and functions:

dog-and-duck.utils.process

TODO: write docs

Public variables and functions:

\ No newline at end of file +Dog-and-duck 0.1.0-SNAPSHOT

Dog-and-duck 0.1.0-SNAPSHOT

Released under the GPL-2.0-or-later

A playground for hacking ActivityPub stuff.

Installation

To install, add the following dependency to your project or build file:

[dog-and-duck "0.1.0-SNAPSHOT"]

Topics

Namespaces

clj-activitypub.core

copied from Jahfer’s clj-activitypub library. 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.

clj-activitypub.internal.crypto

copied from Jahfer’s clj-activitypub library. 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.

Public variables and functions:

clj-activitypub.internal.http-util

copied from Jahfer’s clj-activitypub library. 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.

Public variables and functions:

clj-activitypub.internal.thread-cache

copied from Jahfer’s clj-activitypub library. 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.

Public variables and functions:

clj-activitypub.webfinger

copied from Jahfer’s clj-activitypub library. 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.

Public variables and functions:

dog-and-duck.quack.fault-messages

TODO: write docs

Public variables and functions:

dog-and-duck.scratch.core

TODO: write docs

Public variables and functions:

dog-and-duck.scratch.parser

TODO: write docs

Public variables and functions:

dog-and-duck.scratch.scratch

Scratchpad where I try to understand how to do this stuff.

Public variables and functions:

dog-and-duck.utils.process

TODO: write docs

Public variables and functions:

\ No newline at end of file diff --git a/docs/codox/intro.html b/docs/codox/intro.html index 66aa6ed..7abe3b9 100644 --- a/docs/codox/intro.html +++ b/docs/codox/intro.html @@ -1,6 +1,6 @@ -Introduction

Introduction

+Introduction

Introduction

The Old Dog and Duck

A Clojure library designed to implement the ActivityPub protocol, obviously.

The Dog and Duck, Derby

diff --git a/src/dog_and_duck/quack/fault_messages.clj b/src/dog_and_duck/quack/fault_messages.clj new file mode 100644 index 0000000..a3b6ef4 --- /dev/null +++ b/src/dog_and_duck/quack/fault_messages.clj @@ -0,0 +1,12 @@ +(ns dog-and-duck.quack.fault-messages) + +(def messages + "Actual fault messages to which fault codes resolve." + {:id-not-https "Publicly facing content SHOULD use HTTPS URIs" + :id-not-uri "identifiers must be publicly dereferencable URIs" + :no-context "Section 3 of the ActivityPub specification states Implementers SHOULD include the ActivityPub context in their object definitions`." + :no-id-persistent "Persistent objects MUST have unique global identifiers." + :no-id-transient "The ActivityPub specification allows objects without `id` fields only if they are intentionally transient; even so it is preferred that the object should have an explicit null id." + :null-id-persistent "Persistent objects MUST have non-null identifiers." + :no-type "The ActivityPub specification states that the `type` field is optional, but it is hard to process objects with no known type." + :not-an-object "ActivityStreams object must be JSON objects."}) \ No newline at end of file diff --git a/src/dog_and_duck/quack/picky.clj b/src/dog_and_duck/quack/picky.clj index 505f549..6d5ce6e 100644 --- a/src/dog_and_duck/quack/picky.clj +++ b/src/dog_and_duck/quack/picky.clj @@ -27,7 +27,9 @@ possible to serialise a fault report as a document which in its own right conforms to the ActivityStreams spec." - (:require [dog-and-duck.utils.process :refer [pid]])) + (:require [dog-and-duck.quack.fault-messages :refer [messages]] + [dog-and-duck.utils.process :refer [pid]]) + (:import [java.net URI URISyntaxException])) (def ^:const severity "Severity of faults found, as follows: @@ -54,12 +56,12 @@ "Return a list of reports taken from these `reports` where the severity of the report is greater than this `severity`." [reports severity] - (assert - (and - (coll? reports) - (every? map? reports) + (assert + (and + (coll? reports) + (every? map? reports) (every? :severity reports))) - (remove + (remove #((severity-filters severity) (:severity %)) reports)) @@ -93,15 +95,15 @@ [x] `(context? ((keyword "@context") ~x))) - - (defn make-fault-object "Return a fault object with these `severity`, `fault` and `narrative` values. An ActivityPub object MUST have a globally unique ID. Whether this is meaningful depends on whether we persist fault report objects and serve them, which at present I have no plans to do." - [severity fault narrative] + ;; TODO: should not pass in the narrative; instead should use the :fault value + ;; to look up the narrative in a resource file. + [severity fault] (assoc {} (keyword "@context") validation-fault-context-uri :id (str "https://" @@ -113,33 +115,48 @@ :type "Fault" :severity severity :fault fault - :narrative narrative)) + :narrative (messages fault))) (defn object-faults + "Return a list of faults found in object `x`, or `nil` if none are." [x] - (remove - empty? - (list - (when-not - (has-context? x) - (make-fault-object - :should - :no-context - "Section 3 of the ActivityPub specification states - `Implementers SHOULD include the ActivityPub context in - their object definitions`.") - (when-not (:type x) - (make-fault-object - :minor - :no-type - "The ActivityPub specification states that the `type` field is - optional, but it is hard to process objects with no known type.")) - (when-not (contains? x :id) - (make-fault-object - :minor - :no-id-transient - "The ActivityPub specification allows objects without `id` fields - only if they are intentionally transient; even so it is preferred - that the object should have an explicit null id." - )) - )))) + (let [faults (remove + empty? + (list + (when-not (map? x) + (make-fault-object + :critical + :not-an-object)) + (when-not + (has-context? x) + (make-fault-object + :should + :no-context)) + (when-not (:type x) + (make-fault-object + :minor + :no-type)) + (when-not (and (map? x) (contains? x :id)) + (make-fault-object + :minor + :no-id-transient))))] + (if (empty? faults) nil faults))) + +(defn persistent-object-faults + "Return a list of faults found in persistent object `x`, or `nil` if none are." + [x] + (let [faults (concat + (object-faults x) + (remove empty? + (list + (if (contains? x :id) + (try (let [id (URI. (:id x))] + (when-not (= (.getScheme id) "https") + (make-fault-object :should :id-not-https))) + (catch URISyntaxException _ + (make-fault-object :must :id-not-uri)) + (catch NullPointerException _ + (make-fault-object :must :null-id-persistent))) + (make-fault-object :must :no-id-persistent)))))] + (if (empty? faults) nil faults))) + diff --git a/test/dog_and_duck/quack/picky_test.clj b/test/dog_and_duck/quack/picky_test.clj new file mode 100644 index 0000000..6555c17 --- /dev/null +++ b/test/dog_and_duck/quack/picky_test.clj @@ -0,0 +1,91 @@ +(ns dog-and-duck.quack.picky-test + (:require [clojure.test :refer [deftest is testing]] + [dog-and-duck.quack.picky :refer [activitystreams-context-uri + filter-severity object-faults + persistent-object-faults]])) + +(deftest object-fault-tests + (let [perfect {(keyword "@context") activitystreams-context-uri + :id "https://somewhere.out.there/object/14323:1671654380083" + :type "Test"}] + (testing "no faults returned from fully specified objects" + (let [actual (object-faults perfect) + expected nil] + (is (= actual expected) "There should be no faults from a perfect object")) + (let [o (assoc perfect :age 10 :name "Sally") + actual (object-faults o) + expected nil] + (is (= actual expected) "Adding additional fields should not cause faults"))) + (testing "Faults returned from improperly specified objects" + (let [o "not an object" + r (object-faults o)] + (let [expected 4 + actual (count r)] + (is (= actual expected) "Expect to see four faults from a non-object")) + (let [expected 1 + actual (count (filter-severity r :must))] + (is (= actual expected) "Expect one :critical fault from a non-object")) + (let [expected :not-an-object + actual (:fault (first (filter-severity r :must)))] + (is (= actual expected) "Expect the one :critical fault to be :not-an-object"))) + (let [o {} + r (object-faults o)] + (let [expected 3 + actual (count r)] + (is (= actual expected) "Expect to see three faults from an empty object")) + (let [expected 0 + actual (count (filter-severity r :must))] + (is (= actual expected) "Expect no :critical faults from an empty object")) + (let [expected :no-context + actual (:fault (first (filter-severity r :minor)))] + (is (= actual expected) "Expect the one non-:minor fault to be :no-context"))) + (let [o (dissoc perfect (keyword "@context")) + r (object-faults o) + expected :no-context + actual (:fault (first (filter-severity r :all)))] + (is (= actual expected) + "Expect the one fault from an object with no context to be :no-context.")) + (let [o (dissoc perfect :id) + r (object-faults o) + expected :no-id-transient + actual (:fault (first (filter-severity r :all)))] + (is (= actual expected) + "Expect the one fault from an object with no id to be :no-id-transient.")) + (let [o (dissoc perfect :type) + r (object-faults o) + expected :no-type + actual (:fault (first (filter-severity r :all)))] + (is (= actual expected) + "Expect the one fault from an object with no type to be :no-type."))))) + +(deftest peristent-object-fault-tests + (let [perfect {(keyword "@context") activitystreams-context-uri + :id "https://somewhere.out.there/object/14323:1671654380083" + :type "Test"}] + (testing "no faults returned from fully specified objects" + (let [actual (persistent-object-faults perfect) + expected nil] + (is (= actual expected) "There should be no faults from a perfect object")) + (let [o (assoc perfect :age 10 :name "Sally") + actual (persistent-object-faults o) + expected nil] + (is (= actual expected) "Adding additional fields should not cause faults"))) + (testing "faults specific to persistent objects" + (let [o (dissoc perfect :id) + expected 1 + actual (count + (filter + #(= (:fault %) :no-id-persistent) + (persistent-object-faults o)))] + (is (= actual expected) + "The fault from a persistent object with no id should be :no-id-persistent")) + (let [o (assoc perfect :id "http://somewhere.out.there/object/14323:1671654380083") + expected :id-not-https + actual (-> o persistent-object-faults first :fault)] + (is (= actual expected) + "The fault from a persistent object with an HTTP id should be :id-not-https")) + (let [o (assoc perfect :id "not a valid URI") + expected :id-not-uri + actual (-> o persistent-object-faults first :fault)] + (is (= actual expected) + "The fault from a persistent object with an id which is not a valid URI should be :id-not-uri"))))) \ No newline at end of file