summaryrefslogtreecommitdiffstats
path: root/core/ld.py
blob: 2211ba9db4bbae50a6f5b0c2a881f271f62f4614 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
import urllib.parse as urllib_parse

from pyld import jsonld
from pyld.jsonld import JsonLdError

schemas = {
    "www.w3.org/ns/activitystreams": {
        "contentType": "application/ld+json",
        "documentUrl": "http://www.w3.org/ns/activitystreams",
        "contextUrl": None,
        "document": {
            "@context": {
                "@vocab": "_:",
                "xsd": "http://www.w3.org/2001/XMLSchema#",
                "as": "https://www.w3.org/ns/activitystreams#",
                "ldp": "http://www.w3.org/ns/ldp#",
                "vcard": "http://www.w3.org/2006/vcard/ns#",
                "id": "@id",
                "type": "@type",
                "Accept": "as:Accept",
                "Activity": "as:Activity",
                "IntransitiveActivity": "as:IntransitiveActivity",
                "Add": "as:Add",
                "Announce": "as:Announce",
                "Application": "as:Application",
                "Arrive": "as:Arrive",
                "Article": "as:Article",
                "Audio": "as:Audio",
                "Block": "as:Block",
                "Collection": "as:Collection",
                "CollectionPage": "as:CollectionPage",
                "Relationship": "as:Relationship",
                "Create": "as:Create",
                "Delete": "as:Delete",
                "Dislike": "as:Dislike",
                "Document": "as:Document",
                "Event": "as:Event",
                "Follow": "as:Follow",
                "Flag": "as:Flag",
                "Group": "as:Group",
                "Ignore": "as:Ignore",
                "Image": "as:Image",
                "Invite": "as:Invite",
                "Join": "as:Join",
                "Leave": "as:Leave",
                "Like": "as:Like",
                "Link": "as:Link",
                "Mention": "as:Mention",
                "Note": "as:Note",
                "Object": "as:Object",
                "Offer": "as:Offer",
                "OrderedCollection": "as:OrderedCollection",
                "OrderedCollectionPage": "as:OrderedCollectionPage",
                "Organization": "as:Organization",
                "Page": "as:Page",
                "Person": "as:Person",
                "Place": "as:Place",
                "Profile": "as:Profile",
                "Question": "as:Question",
                "Reject": "as:Reject",
                "Remove": "as:Remove",
                "Service": "as:Service",
                "TentativeAccept": "as:TentativeAccept",
                "TentativeReject": "as:TentativeReject",
                "Tombstone": "as:Tombstone",
                "Undo": "as:Undo",
                "Update": "as:Update",
                "Video": "as:Video",
                "View": "as:View",
                "Listen": "as:Listen",
                "Read": "as:Read",
                "Move": "as:Move",
                "Travel": "as:Travel",
                "IsFollowing": "as:IsFollowing",
                "IsFollowedBy": "as:IsFollowedBy",
                "IsContact": "as:IsContact",
                "IsMember": "as:IsMember",
                "subject": {"@id": "as:subject", "@type": "@id"},
                "relationship": {"@id": "as:relationship", "@type": "@id"},
                "actor": {"@id": "as:actor", "@type": "@id"},
                "attributedTo": {"@id": "as:attributedTo", "@type": "@id"},
                "attachment": {"@id": "as:attachment", "@type": "@id"},
                "bcc": {"@id": "as:bcc", "@type": "@id"},
                "bto": {"@id": "as:bto", "@type": "@id"},
                "cc": {"@id": "as:cc", "@type": "@id"},
                "context": {"@id": "as:context", "@type": "@id"},
                "current": {"@id": "as:current", "@type": "@id"},
                "first": {"@id": "as:first", "@type": "@id"},
                "generator": {"@id": "as:generator", "@type": "@id"},
                "icon": {"@id": "as:icon", "@type": "@id"},
                "image": {"@id": "as:image", "@type": "@id"},
                "inReplyTo": {"@id": "as:inReplyTo", "@type": "@id"},
                "items": {"@id": "as:items", "@type": "@id"},
                "instrument": {"@id": "as:instrument", "@type": "@id"},
                "orderedItems": {
                    "@id": "as:items",
                    "@type": "@id",
                    "@container": "@list",
                },
                "last": {"@id": "as:last", "@type": "@id"},
                "location": {"@id": "as:location", "@type": "@id"},
                "next": {"@id": "as:next", "@type": "@id"},
                "object": {"@id": "as:object", "@type": "@id"},
                "oneOf": {"@id": "as:oneOf", "@type": "@id"},
                "anyOf": {"@id": "as:anyOf", "@type": "@id"},
                "closed": {"@id": "as:closed", "@type": "xsd:dateTime"},
                "origin": {"@id": "as:origin", "@type": "@id"},
                "accuracy": {"@id": "as:accuracy", "@type": "xsd:float"},
                "prev": {"@id": "as:prev", "@type": "@id"},
                "preview": {"@id": "as:preview", "@type": "@id"},
                "replies": {"@id": "as:replies", "@type": "@id"},
                "result": {"@id": "as:result", "@type": "@id"},
                "audience": {"@id": "as:audience", "@type": "@id"},
                "partOf": {"@id": "as:partOf", "@type": "@id"},
                "tag": {"@id": "as:tag", "@type": "@id"},
                "target": {"@id": "as:target", "@type": "@id"},
                "to": {"@id": "as:to", "@type": "@id"},
                "url": {"@id": "as:url", "@type": "@id"},
                "altitude": {"@id": "as:altitude", "@type": "xsd:float"},
                "content": "as:content",
                "contentMap": {"@id": "as:content", "@container": "@language"},
                "name": "as:name",
                "nameMap": {"@id": "as:name", "@container": "@language"},
                "duration": {"@id": "as:duration", "@type": "xsd:duration"},
                "endTime": {"@id": "as:endTime", "@type": "xsd:dateTime"},
                "height": {"@id": "as:height", "@type": "xsd:nonNegativeInteger"},
                "href": {"@id": "as:href", "@type": "@id"},
                "hreflang": "as:hreflang",
                "latitude": {"@id": "as:latitude", "@type": "xsd:float"},
                "longitude": {"@id": "as:longitude", "@type": "xsd:float"},
                "mediaType": "as:mediaType",
                "published": {"@id": "as:published", "@type": "xsd:dateTime"},
                "radius": {"@id": "as:radius", "@type": "xsd:float"},
                "rel": "as:rel",
                "startIndex": {
                    "@id": "as:startIndex",
                    "@type": "xsd:nonNegativeInteger",
                },
                "startTime": {"@id": "as:startTime", "@type": "xsd:dateTime"},
                "summary": "as:summary",
                "summaryMap": {"@id": "as:summary", "@container": "@language"},
                "totalItems": {
                    "@id": "as:totalItems",
                    "@type": "xsd:nonNegativeInteger",
                },
                "units": "as:units",
                "updated": {"@id": "as:updated", "@type": "xsd:dateTime"},
                "width": {"@id": "as:width", "@type": "xsd:nonNegativeInteger"},
                "describes": {"@id": "as:describes", "@type": "@id"},
                "formerType": {"@id": "as:formerType", "@type": "@id"},
                "deleted": {"@id": "as:deleted", "@type": "xsd:dateTime"},
                "inbox": {"@id": "ldp:inbox", "@type": "@id"},
                "outbox": {"@id": "as:outbox", "@type": "@id"},
                "following": {"@id": "as:following", "@type": "@id"},
                "followers": {"@id": "as:followers", "@type": "@id"},
                "streams": {"@id": "as:streams", "@type": "@id"},
                "preferredUsername": "as:preferredUsername",
                "endpoints": {"@id": "as:endpoints", "@type": "@id"},
                "uploadMedia": {"@id": "as:uploadMedia", "@type": "@id"},
                "proxyUrl": {"@id": "as:proxyUrl", "@type": "@id"},
                "liked": {"@id": "as:liked", "@type": "@id"},
                "oauthAuthorizationEndpoint": {
                    "@id": "as:oauthAuthorizationEndpoint",
                    "@type": "@id",
                },
                "oauthTokenEndpoint": {"@id": "as:oauthTokenEndpoint", "@type": "@id"},
                "provideClientKey": {"@id": "as:provideClientKey", "@type": "@id"},
                "signClientKey": {"@id": "as:signClientKey", "@type": "@id"},
                "sharedInbox": {"@id": "as:sharedInbox", "@type": "@id"},
                "Public": {"@id": "as:Public", "@type": "@id"},
                "source": "as:source",
                "likes": {"@id": "as:likes", "@type": "@id"},
                "shares": {"@id": "as:shares", "@type": "@id"},
                "alsoKnownAs": {"@id": "as:alsoKnownAs", "@type": "@id"},
            }
        },
    },
    "w3id.org/security/v1": {
        "contentType": "application/ld+json",
        "documentUrl": "http://w3id.org/security/v1",
        "contextUrl": None,
        "document": {
            "@context": {
                "id": "@id",
                "type": "@type",
                "dc": "http://purl.org/dc/terms/",
                "sec": "https://w3id.org/security#",
                "xsd": "http://www.w3.org/2001/XMLSchema#",
                "EcdsaKoblitzSignature2016": "sec:EcdsaKoblitzSignature2016",
                "Ed25519Signature2018": "sec:Ed25519Signature2018",
                "EncryptedMessage": "sec:EncryptedMessage",
                "GraphSignature2012": "sec:GraphSignature2012",
                "LinkedDataSignature2015": "sec:LinkedDataSignature2015",
                "LinkedDataSignature2016": "sec:LinkedDataSignature2016",
                "CryptographicKey": "sec:Key",
                "authenticationTag": "sec:authenticationTag",
                "canonicalizationAlgorithm": "sec:canonicalizationAlgorithm",
                "cipherAlgorithm": "sec:cipherAlgorithm",
                "cipherData": "sec:cipherData",
                "cipherKey": "sec:cipherKey",
                "created": {"@id": "dc:created", "@type": "xsd:dateTime"},
                "creator": {"@id": "dc:creator", "@type": "@id"},
                "digestAlgorithm": "sec:digestAlgorithm",
                "digestValue": "sec:digestValue",
                "domain": "sec:domain",
                "encryptionKey": "sec:encryptionKey",
                "expiration": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
                "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
                "initializationVector": "sec:initializationVector",
                "iterationCount": "sec:iterationCount",
                "nonce": "sec:nonce",
                "normalizationAlgorithm": "sec:normalizationAlgorithm",
                "owner": {"@id": "sec:owner", "@type": "@id"},
                "password": "sec:password",
                "privateKey": {"@id": "sec:privateKey", "@type": "@id"},
                "privateKeyPem": "sec:privateKeyPem",
                "publicKey": {"@id": "sec:publicKey", "@type": "@id"},
                "publicKeyBase58": "sec:publicKeyBase58",
                "publicKeyPem": "sec:publicKeyPem",
                "publicKeyWif": "sec:publicKeyWif",
                "publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"},
                "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"},
                "salt": "sec:salt",
                "signature": "sec:signature",
                "signatureAlgorithm": "sec:signingAlgorithm",
                "signatureValue": "sec:signatureValue",
            }
        },
    },
    "*/schemas/litepub-0.1.jsonld": {
        "contentType": "application/ld+json",
        "documentUrl": "http://w3id.org/security/v1",
        "contextUrl": None,
        "document": {
            "@context": [
                "https://www.w3.org/ns/activitystreams",
                "https://w3id.org/security/v1",
                {
                    "Emoji": "toot:Emoji",
                    "Hashtag": "as:Hashtag",
                    "PropertyValue": "schema:PropertyValue",
                    "atomUri": "ostatus:atomUri",
                    "conversation": {"@id": "ostatus:conversation", "@type": "@id"},
                    "discoverable": "toot:discoverable",
                    "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
                    "capabilities": "litepub:capabilities",
                    "ostatus": "http://ostatus.org#",
                    "schema": "http://schema.org#",
                    "toot": "http://joinmastodon.org/ns#",
                    "misskey": "https://misskey-hub.net/ns#",
                    "fedibird": "http://fedibird.com/ns#",
                    "value": "schema:value",
                    "sensitive": "as:sensitive",
                    "litepub": "http://litepub.social/ns#",
                    "invisible": "litepub:invisible",
                    "directMessage": "litepub:directMessage",
                    "listMessage": {"@id": "litepub:listMessage", "@type": "@id"},
                    "quoteUrl": "as:quoteUrl",
                    "quoteUri": "fedibird:quoteUri",
                    "oauthRegistrationEndpoint": {
                        "@id": "litepub:oauthRegistrationEndpoint",
                        "@type": "@id",
                    },
                    "EmojiReact": "litepub:EmojiReact",
                    "ChatMessage": "litepub:ChatMessage",
                    "alsoKnownAs": {"@id": "as:alsoKnownAs", "@type": "@id"},
                    "vcard": "http://www.w3.org/2006/vcard/ns#",
                    "formerRepresentations": "litepub:formerRepresentations",
                },
            ]
        },
    },
}


def builtin_document_loader(url: str, options={}):
    # Get URL without scheme
    pieces = urllib_parse.urlparse(url)
    if pieces.hostname is None:
        raise JsonLdError(
            f"No schema built-in for {url!r}",
            "jsonld.LoadDocumentError",
            code="loading document failed",
            cause="NoHostnameError",
        )
    key = pieces.hostname + pieces.path.rstrip("/")
    try:
        return schemas[key]
    except KeyError:
        try:
            key = "*" + pieces.path.rstrip("/")
            return schemas[key]
        except KeyError:
            raise JsonLdError(
                f"No schema built-in for {key!r}",
                "jsonld.LoadDocumentError",
                code="loading document failed",
                cause="KeyError",
            )


def canonicalise(json_data, include_security=False):
    """
    Given an ActivityPub JSON-LD document, round-trips it through the LD
    systems to end up in a canonicalised, compacted format.

    For most well-structured incoming data this won't actually do anything,
    but it's probably good to abide by the spec.
    """
    if not isinstance(json_data, (dict, list)):
        raise ValueError("Pass decoded JSON data into LDDocument")
    return jsonld.compact(
        jsonld.expand(json_data),
        (
            [
                "https://www.w3.org/ns/activitystreams",
                "https://w3id.org/security/v1",
            ]
            if include_security
            else "https://www.w3.org/ns/activitystreams"
        ),
    )