summaryrefslogtreecommitdiffstats
path: root/core/ld.py
blob: 346708cb84c4767daf8c59d7bf2a6f132477c5c2 (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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
import datetime
import urllib.parse as urllib_parse
from typing import Dict, List, Optional, Union

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",
            }
        },
    },
    "w3id.org/identity/v1": {
        "contentType": "application/ld+json",
        "documentUrl": "http://w3id.org/identity/v1",
        "contextUrl": None,
        "document": {
            "@context": {
                "id": "@id",
                "type": "@type",
                "cred": "https://w3id.org/credentials#",
                "dc": "http://purl.org/dc/terms/",
                "identity": "https://w3id.org/identity#",
                "perm": "https://w3id.org/permissions#",
                "ps": "https://w3id.org/payswarm#",
                "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
                "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
                "sec": "https://w3id.org/security#",
                "schema": "http://schema.org/",
                "xsd": "http://www.w3.org/2001/XMLSchema#",
                "Group": "https://www.w3.org/ns/activitystreams#Group",
                "claim": {"@id": "cred:claim", "@type": "@id"},
                "credential": {"@id": "cred:credential", "@type": "@id"},
                "issued": {"@id": "cred:issued", "@type": "xsd:dateTime"},
                "issuer": {"@id": "cred:issuer", "@type": "@id"},
                "recipient": {"@id": "cred:recipient", "@type": "@id"},
                "Credential": "cred:Credential",
                "CryptographicKeyCredential": "cred:CryptographicKeyCredential",
                "about": {"@id": "schema:about", "@type": "@id"},
                "address": {"@id": "schema:address", "@type": "@id"},
                "addressCountry": "schema:addressCountry",
                "addressLocality": "schema:addressLocality",
                "addressRegion": "schema:addressRegion",
                "comment": "rdfs:comment",
                "created": {"@id": "dc:created", "@type": "xsd:dateTime"},
                "creator": {"@id": "dc:creator", "@type": "@id"},
                "description": "schema:description",
                "email": "schema:email",
                "familyName": "schema:familyName",
                "givenName": "schema:givenName",
                "image": {"@id": "schema:image", "@type": "@id"},
                "label": "rdfs:label",
                "name": "schema:name",
                "postalCode": "schema:postalCode",
                "streetAddress": "schema:streetAddress",
                "title": "dc:title",
                "url": {"@id": "schema:url", "@type": "@id"},
                "Person": "schema:Person",
                "PostalAddress": "schema:PostalAddress",
                "Organization": "schema:Organization",
                "identityService": {"@id": "identity:identityService", "@type": "@id"},
                "idp": {"@id": "identity:idp", "@type": "@id"},
                "Identity": "identity:Identity",
                "paymentProcessor": "ps:processor",
                "preferences": {"@id": "ps:preferences", "@type": "@vocab"},
                "cipherAlgorithm": "sec:cipherAlgorithm",
                "cipherData": "sec:cipherData",
                "cipherKey": "sec:cipherKey",
                "digestAlgorithm": "sec:digestAlgorithm",
                "digestValue": "sec:digestValue",
                "domain": "sec:domain",
                "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
                "initializationVector": "sec:initializationVector",
                "member": {"@id": "schema:member", "@type": "@id"},
                "memberOf": {"@id": "schema:memberOf", "@type": "@id"},
                "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"},
                "publicKeyPem": "sec:publicKeyPem",
                "publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"},
                "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"},
                "signature": "sec:signature",
                "signatureAlgorithm": "sec:signatureAlgorithm",
                "signatureValue": "sec:signatureValue",
                "CryptographicKey": "sec:Key",
                "EncryptedMessage": "sec:EncryptedMessage",
                "GraphSignature2012": "sec:GraphSignature2012",
                "LinkedDataSignature2015": "sec:LinkedDataSignature2015",
                "accessControl": {"@id": "perm:accessControl", "@type": "@id"},
                "writePermission": {"@id": "perm:writePermission", "@type": "@id"},
            }
        },
    },
    "*/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",
                },
            ]
        },
    },
}

DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"


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: Dict, include_security: bool = False) -> Dict:
    """
    Given an ActivityPub JSON-LD document, round-trips it through the LD
    systems to end up in a canonicalised, compacted format.

    If no context is provided, supplies one automatically.

    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):
        raise ValueError("Pass decoded JSON data into LDDocument")
    context: Union[str, List[str]]
    if include_security:
        context = [
            "https://www.w3.org/ns/activitystreams",
            "https://w3id.org/security/v1",
        ]
    else:
        context = "https://www.w3.org/ns/activitystreams"
    if "@context" not in json_data:
        json_data["@context"] = context

    return jsonld.compact(jsonld.expand(json_data), context)


def format_ld_date(value: datetime.datetime) -> str:
    return value.strftime(DATETIME_FORMAT)


def parse_ld_date(value: Optional[str]) -> Optional[datetime.datetime]:
    if value is None:
        return None
    return datetime.datetime.strptime(value, DATETIME_FORMAT).replace(
        tzinfo=datetime.timezone.utc
    )