solid-logic 4.0.8-3 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/2729812e0bfc062ee77a.js +1 -0
- package/dist/a9c30b64ceaae48b72f0.js +2045 -0
- package/dist/acl/aclLogic.d.ts +5 -0
- package/dist/acl/aclLogic.d.ts.map +1 -0
- package/dist/authSession/authSession.d.ts +42 -0
- package/dist/authSession/authSession.d.ts.map +1 -0
- package/dist/authSession/events.d.ts +24 -0
- package/dist/authSession/events.d.ts.map +1 -0
- package/dist/authSession/issuer.d.ts +8 -0
- package/dist/authSession/issuer.d.ts.map +1 -0
- package/dist/authSession/session.d.ts +45 -0
- package/dist/authSession/session.d.ts.map +1 -0
- package/dist/authn/SolidAuthnLogic.d.ts +42 -0
- package/dist/authn/SolidAuthnLogic.d.ts.map +1 -0
- package/dist/authn/authUtil.d.ts +17 -0
- package/dist/authn/authUtil.d.ts.map +1 -0
- package/dist/authn/serverLogout.d.ts +6 -0
- package/dist/authn/serverLogout.d.ts.map +1 -0
- package/dist/chat/chatLogic.d.ts +3 -0
- package/dist/chat/chatLogic.d.ts.map +1 -0
- package/dist/inbox/inboxLogic.d.ts +3 -0
- package/dist/inbox/inboxLogic.d.ts.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/issuer/issuerLogic.d.ts +8 -0
- package/dist/issuer/issuerLogic.d.ts.map +1 -0
- package/dist/logic/CustomError.d.ts +21 -0
- package/dist/logic/CustomError.d.ts.map +1 -0
- package/dist/logic/solidLogic.d.ts +6 -0
- package/dist/logic/solidLogic.d.ts.map +1 -0
- package/dist/logic/solidLogicSingleton.d.ts +4 -0
- package/dist/logic/solidLogicSingleton.d.ts.map +1 -0
- package/dist/profile/profileAclDocuments.d.ts +2 -0
- package/dist/profile/profileAclDocuments.d.ts.map +1 -0
- package/dist/profile/profileDocuments.d.ts +2 -0
- package/dist/profile/profileDocuments.d.ts.map +1 -0
- package/dist/profile/profileLogic.d.ts +3 -0
- package/dist/profile/profileLogic.d.ts.map +1 -0
- package/dist/solid-logic.esm.js +2358 -0
- package/dist/solid-logic.esm.js.map +1 -0
- package/dist/solid-logic.esm.min.js +2 -0
- package/dist/solid-logic.esm.min.js.map +1 -0
- package/dist/solid-logic.js +7548 -0
- package/dist/solid-logic.js.map +1 -0
- package/dist/solid-logic.min.js +2 -0
- package/dist/solid-logic.min.js.map +1 -0
- package/dist/typeIndex/typeIndexAclDocuments.d.ts +2 -0
- package/dist/typeIndex/typeIndexAclDocuments.d.ts.map +1 -0
- package/dist/typeIndex/typeIndexDocuments.d.ts +3 -0
- package/dist/typeIndex/typeIndexDocuments.d.ts.map +1 -0
- package/dist/typeIndex/typeIndexLogic.d.ts +3 -0
- package/dist/typeIndex/typeIndexLogic.d.ts.map +1 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/util/containerLogic.d.ts +11 -0
- package/dist/util/containerLogic.d.ts.map +1 -0
- package/dist/util/debug.d.ts +5 -0
- package/dist/util/debug.d.ts.map +1 -0
- package/dist/util/ns.d.ts +2 -0
- package/dist/util/ns.d.ts.map +1 -0
- package/dist/util/utilityLogic.d.ts +17 -0
- package/dist/util/utilityLogic.d.ts.map +1 -0
- package/dist/util/utils.d.ts +8 -0
- package/dist/util/utils.d.ts.map +1 -0
- package/package.json +33 -27
- package/dist/_virtual/_rolldown/runtime.cjs.js +0 -1
- package/dist/acl/aclLogic.cjs.js +0 -2
- package/dist/acl/aclLogic.cjs.js.map +0 -1
- package/dist/acl/aclLogic.esm.js +0 -56
- package/dist/acl/aclLogic.esm.js.map +0 -1
- package/dist/authSession/authSession.cjs.js +0 -2
- package/dist/authSession/authSession.cjs.js.map +0 -1
- package/dist/authSession/authSession.esm.js +0 -22
- package/dist/authSession/authSession.esm.js.map +0 -1
- package/dist/authSession/events.cjs.js +0 -2
- package/dist/authSession/events.cjs.js.map +0 -1
- package/dist/authSession/events.esm.js +0 -19
- package/dist/authSession/events.esm.js.map +0 -1
- package/dist/authSession/issuer.cjs.js +0 -2
- package/dist/authSession/issuer.cjs.js.map +0 -1
- package/dist/authSession/issuer.esm.js +0 -18
- package/dist/authSession/issuer.esm.js.map +0 -1
- package/dist/authSession/session.cjs.js +0 -2
- package/dist/authSession/session.cjs.js.map +0 -1
- package/dist/authSession/session.esm.js +0 -102
- package/dist/authSession/session.esm.js.map +0 -1
- package/dist/authn/SolidAuthnLogic.cjs.js +0 -2
- package/dist/authn/SolidAuthnLogic.cjs.js.map +0 -1
- package/dist/authn/SolidAuthnLogic.esm.js +0 -116
- package/dist/authn/SolidAuthnLogic.esm.js.map +0 -1
- package/dist/authn/authUtil.cjs.js +0 -2
- package/dist/authn/authUtil.cjs.js.map +0 -1
- package/dist/authn/authUtil.esm.js +0 -33
- package/dist/authn/authUtil.esm.js.map +0 -1
- package/dist/authn/serverLogout.cjs.js +0 -2
- package/dist/authn/serverLogout.cjs.js.map +0 -1
- package/dist/authn/serverLogout.esm.js +0 -25
- package/dist/authn/serverLogout.esm.js.map +0 -1
- package/dist/chat/chatLogic.cjs.js +0 -21
- package/dist/chat/chatLogic.cjs.js.map +0 -1
- package/dist/chat/chatLogic.esm.js +0 -116
- package/dist/chat/chatLogic.esm.js.map +0 -1
- package/dist/inbox/inboxLogic.cjs.js +0 -2
- package/dist/inbox/inboxLogic.cjs.js.map +0 -1
- package/dist/inbox/inboxLogic.esm.js +0 -37
- package/dist/inbox/inboxLogic.esm.js.map +0 -1
- package/dist/index.cjs.js +0 -2
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.esm.js +0 -13
- package/dist/index.esm.js.map +0 -1
- package/dist/issuer/issuerLogic.cjs.js +0 -2
- package/dist/issuer/issuerLogic.cjs.js.map +0 -1
- package/dist/issuer/issuerLogic.esm.js +0 -30
- package/dist/issuer/issuerLogic.esm.js.map +0 -1
- package/dist/logic/CustomError.cjs.js +0 -2
- package/dist/logic/CustomError.cjs.js.map +0 -1
- package/dist/logic/CustomError.esm.js +0 -14
- package/dist/logic/CustomError.esm.js.map +0 -1
- package/dist/logic/solidLogic.cjs.js +0 -2
- package/dist/logic/solidLogic.cjs.js.map +0 -1
- package/dist/logic/solidLogic.esm.js +0 -47
- package/dist/logic/solidLogic.esm.js.map +0 -1
- package/dist/logic/solidLogicSingleton.cjs.js +0 -2
- package/dist/logic/solidLogicSingleton.cjs.js.map +0 -1
- package/dist/logic/solidLogicSingleton.esm.js +0 -19
- package/dist/logic/solidLogicSingleton.esm.js.map +0 -1
- package/dist/profile/profileAclDocuments.cjs.js +0 -3
- package/dist/profile/profileAclDocuments.cjs.js.map +0 -1
- package/dist/profile/profileAclDocuments.esm.js +0 -17
- package/dist/profile/profileAclDocuments.esm.js.map +0 -1
- package/dist/profile/profileDocuments.cjs.js +0 -3
- package/dist/profile/profileDocuments.cjs.js.map +0 -1
- package/dist/profile/profileDocuments.esm.js +0 -15
- package/dist/profile/profileDocuments.esm.js.map +0 -1
- package/dist/profile/profileLogic.cjs.js +0 -2
- package/dist/profile/profileLogic.cjs.js.map +0 -1
- package/dist/profile/profileLogic.esm.js +0 -184
- package/dist/profile/profileLogic.esm.js.map +0 -1
- package/dist/typeIndex/typeIndexAclDocuments.cjs.js +0 -3
- package/dist/typeIndex/typeIndexAclDocuments.cjs.js.map +0 -1
- package/dist/typeIndex/typeIndexAclDocuments.esm.js +0 -35
- package/dist/typeIndex/typeIndexAclDocuments.esm.js.map +0 -1
- package/dist/typeIndex/typeIndexDocuments.cjs.js +0 -4
- package/dist/typeIndex/typeIndexDocuments.cjs.js.map +0 -1
- package/dist/typeIndex/typeIndexDocuments.esm.js +0 -21
- package/dist/typeIndex/typeIndexDocuments.esm.js.map +0 -1
- package/dist/typeIndex/typeIndexLogic.cjs.js +0 -2
- package/dist/typeIndex/typeIndexLogic.cjs.js.map +0 -1
- package/dist/typeIndex/typeIndexLogic.esm.js +0 -185
- package/dist/typeIndex/typeIndexLogic.esm.js.map +0 -1
- package/dist/util/containerLogic.cjs.js +0 -2
- package/dist/util/containerLogic.cjs.js.map +0 -1
- package/dist/util/containerLogic.esm.js +0 -37
- package/dist/util/containerLogic.esm.js.map +0 -1
- package/dist/util/debug.cjs.js +0 -2
- package/dist/util/debug.cjs.js.map +0 -1
- package/dist/util/debug.esm.js +0 -11
- package/dist/util/debug.esm.js.map +0 -1
- package/dist/util/ns.cjs.js +0 -2
- package/dist/util/ns.cjs.js.map +0 -1
- package/dist/util/ns.esm.js +0 -8
- package/dist/util/ns.esm.js.map +0 -1
- package/dist/util/utilityLogic.cjs.js +0 -6
- package/dist/util/utilityLogic.cjs.js.map +0 -1
- package/dist/util/utilityLogic.esm.js +0 -149
- package/dist/util/utilityLogic.esm.js.map +0 -1
- package/dist/util/utils.cjs.js +0 -2
- package/dist/util/utils.cjs.js.map +0 -1
- package/dist/util/utils.esm.js +0 -22
- package/dist/util/utils.esm.js.map +0 -1
|
@@ -0,0 +1,2358 @@
|
|
|
1
|
+
import { Session as __WEBPACK_EXTERNAL_MODULE__uvdsl_solid_oidc_client_browser_144ab6cc_Session__ } from "@uvdsl/solid-oidc-client-browser";
|
|
2
|
+
import * as __WEBPACK_EXTERNAL_MODULE__uvdsl_solid_oidc_client_browser_core_9566af4e__ from "@uvdsl/solid-oidc-client-browser/core";
|
|
3
|
+
import * as __WEBPACK_EXTERNAL_MODULE_rdflib__ from "rdflib";
|
|
4
|
+
/******/ var __webpack_modules__ = ({
|
|
5
|
+
|
|
6
|
+
/***/ 386
|
|
7
|
+
(module) {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Provides a way to access commonly used namespaces
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
*
|
|
14
|
+
* ```
|
|
15
|
+
* const $rdf = require('rdflib'); //or any other RDF/JS-compatible library
|
|
16
|
+
* const ns = require('solid-namespace')($rdf);
|
|
17
|
+
* const store = $rdf.graph();
|
|
18
|
+
*
|
|
19
|
+
* let me = ...;
|
|
20
|
+
* let name = store.any(me, ns.vcard(‘fn’)) || store.any(me, ns.foaf(‘name’));
|
|
21
|
+
* ```
|
|
22
|
+
* @module vocab
|
|
23
|
+
*/
|
|
24
|
+
const aliases = {
|
|
25
|
+
acl: 'http://www.w3.org/ns/auth/acl#',
|
|
26
|
+
arg: 'http://www.w3.org/ns/pim/arg#',
|
|
27
|
+
as: 'https://www.w3.org/ns/activitystreams#',
|
|
28
|
+
bookmark: 'http://www.w3.org/2002/01/bookmark#',
|
|
29
|
+
cal: 'http://www.w3.org/2002/12/cal/ical#',
|
|
30
|
+
cco: 'http://www.ontologyrepository.com/CommonCoreOntologies/',
|
|
31
|
+
cert: 'http://www.w3.org/ns/auth/cert#',
|
|
32
|
+
contact: 'http://www.w3.org/2000/10/swap/pim/contact#',
|
|
33
|
+
dc: 'http://purl.org/dc/elements/1.1/',
|
|
34
|
+
dct: 'http://purl.org/dc/terms/',
|
|
35
|
+
doap: 'http://usefulinc.com/ns/doap#',
|
|
36
|
+
foaf: 'http://xmlns.com/foaf/0.1/',
|
|
37
|
+
geo: 'http://www.w3.org/2003/01/geo/wgs84_pos#',
|
|
38
|
+
gpx: 'http://www.w3.org/ns/pim/gpx#',
|
|
39
|
+
gr: 'http://purl.org/goodrelations/v1#',
|
|
40
|
+
http: 'http://www.w3.org/2007/ont/http#',
|
|
41
|
+
httph: 'http://www.w3.org/2007/ont/httph#',
|
|
42
|
+
icalTZ: 'http://www.w3.org/2002/12/cal/icaltzd#', // Beware: not cal:
|
|
43
|
+
ldp: 'http://www.w3.org/ns/ldp#',
|
|
44
|
+
link: 'http://www.w3.org/2007/ont/link#',
|
|
45
|
+
log: 'http://www.w3.org/2000/10/swap/log#',
|
|
46
|
+
meeting: 'http://www.w3.org/ns/pim/meeting#',
|
|
47
|
+
mo: 'http://purl.org/ontology/mo/',
|
|
48
|
+
org: 'http://www.w3.org/ns/org#',
|
|
49
|
+
owl: 'http://www.w3.org/2002/07/owl#',
|
|
50
|
+
pad: 'http://www.w3.org/ns/pim/pad#',
|
|
51
|
+
patch: 'http://www.w3.org/ns/pim/patch#',
|
|
52
|
+
prov: 'http://www.w3.org/ns/prov#',
|
|
53
|
+
pto: 'http://www.productontology.org/id/',
|
|
54
|
+
qu: 'http://www.w3.org/2000/10/swap/pim/qif#',
|
|
55
|
+
trip: 'http://www.w3.org/ns/pim/trip#',
|
|
56
|
+
rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
|
57
|
+
rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
|
|
58
|
+
rss: 'http://purl.org/rss/1.0/',
|
|
59
|
+
sched: 'http://www.w3.org/ns/pim/schedule#',
|
|
60
|
+
schema: 'http://schema.org/', // @@ beware confusion with documents no 303
|
|
61
|
+
sioc: 'http://rdfs.org/sioc/ns#',
|
|
62
|
+
skos: 'http://www.w3.org/2004/02/skos/core#',
|
|
63
|
+
solid: 'http://www.w3.org/ns/solid/terms#',
|
|
64
|
+
space: 'http://www.w3.org/ns/pim/space#',
|
|
65
|
+
stat: 'http://www.w3.org/ns/posix/stat#',
|
|
66
|
+
tab: 'http://www.w3.org/2007/ont/link#',
|
|
67
|
+
tabont: 'http://www.w3.org/2007/ont/link#',
|
|
68
|
+
ui: 'http://www.w3.org/ns/ui#',
|
|
69
|
+
vann: 'http://purl.org/vocab/vann/',
|
|
70
|
+
vcard: 'http://www.w3.org/2006/vcard/ns#',
|
|
71
|
+
wf: 'http://www.w3.org/2005/01/wf/flow#',
|
|
72
|
+
xsd: 'http://www.w3.org/2001/XMLSchema#',
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param [rdflib] {RDF} Optional RDF Library (such as rdflib.js or rdf-ext) to inject
|
|
77
|
+
*/
|
|
78
|
+
function vocab (rdf = { namedNode: u => u }) {
|
|
79
|
+
const namespaces = {}
|
|
80
|
+
for (const alias in aliases) {
|
|
81
|
+
const expansion = aliases[alias]
|
|
82
|
+
namespaces[alias] = function (localName = '') {
|
|
83
|
+
return rdf.namedNode(expansion + localName)
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return namespaces
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
module.exports = vocab
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
/***/ }
|
|
94
|
+
|
|
95
|
+
/******/ });
|
|
96
|
+
/************************************************************************/
|
|
97
|
+
/******/ // The module cache
|
|
98
|
+
/******/ var __webpack_module_cache__ = {};
|
|
99
|
+
/******/
|
|
100
|
+
/******/ // The require function
|
|
101
|
+
/******/ function __webpack_require__(moduleId) {
|
|
102
|
+
/******/ // Check if module is in cache
|
|
103
|
+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
104
|
+
/******/ if (cachedModule !== undefined) {
|
|
105
|
+
/******/ return cachedModule.exports;
|
|
106
|
+
/******/ }
|
|
107
|
+
/******/ // Create a new module (and put it into the cache)
|
|
108
|
+
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
109
|
+
/******/ // no module.id needed
|
|
110
|
+
/******/ // no module.loaded needed
|
|
111
|
+
/******/ exports: {}
|
|
112
|
+
/******/ };
|
|
113
|
+
/******/
|
|
114
|
+
/******/ // Execute the module function
|
|
115
|
+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
116
|
+
/******/
|
|
117
|
+
/******/ // Return the exports of the module
|
|
118
|
+
/******/ return module.exports;
|
|
119
|
+
/******/ }
|
|
120
|
+
/******/
|
|
121
|
+
/************************************************************************/
|
|
122
|
+
/******/ /* webpack/runtime/compat get default export */
|
|
123
|
+
/******/ (() => {
|
|
124
|
+
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
125
|
+
/******/ __webpack_require__.n = (module) => {
|
|
126
|
+
/******/ var getter = module && module.__esModule ?
|
|
127
|
+
/******/ () => (module['default']) :
|
|
128
|
+
/******/ () => (module);
|
|
129
|
+
/******/ __webpack_require__.d(getter, { a: getter });
|
|
130
|
+
/******/ return getter;
|
|
131
|
+
/******/ };
|
|
132
|
+
/******/ })();
|
|
133
|
+
/******/
|
|
134
|
+
/******/ /* webpack/runtime/define property getters */
|
|
135
|
+
/******/ (() => {
|
|
136
|
+
/******/ // define getter functions for harmony exports
|
|
137
|
+
/******/ __webpack_require__.d = (exports, definition) => {
|
|
138
|
+
/******/ for(var key in definition) {
|
|
139
|
+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
140
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
141
|
+
/******/ }
|
|
142
|
+
/******/ }
|
|
143
|
+
/******/ };
|
|
144
|
+
/******/ })();
|
|
145
|
+
/******/
|
|
146
|
+
/******/ /* webpack/runtime/global */
|
|
147
|
+
/******/ (() => {
|
|
148
|
+
/******/ __webpack_require__.g = (function() {
|
|
149
|
+
/******/ if (typeof globalThis === 'object') return globalThis;
|
|
150
|
+
/******/ try {
|
|
151
|
+
/******/ return this || new Function('return this')();
|
|
152
|
+
/******/ } catch (e) {
|
|
153
|
+
/******/ if (typeof window === 'object') return window;
|
|
154
|
+
/******/ }
|
|
155
|
+
/******/ })();
|
|
156
|
+
/******/ })();
|
|
157
|
+
/******/
|
|
158
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
159
|
+
/******/ (() => {
|
|
160
|
+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
161
|
+
/******/ })();
|
|
162
|
+
/******/
|
|
163
|
+
/************************************************************************/
|
|
164
|
+
|
|
165
|
+
;// ./src/util/debug.ts
|
|
166
|
+
function log(...args) {
|
|
167
|
+
console.log(...args);
|
|
168
|
+
}
|
|
169
|
+
function warn(...args) {
|
|
170
|
+
console.warn(...args);
|
|
171
|
+
}
|
|
172
|
+
function error(...args) {
|
|
173
|
+
console.error(...args);
|
|
174
|
+
}
|
|
175
|
+
function trace(...args) {
|
|
176
|
+
console.trace(...args);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
;// external "@uvdsl/solid-oidc-client-browser"
|
|
180
|
+
|
|
181
|
+
;// external "@uvdsl/solid-oidc-client-browser/core"
|
|
182
|
+
const core_namespaceObject = __WEBPACK_EXTERNAL_MODULE__uvdsl_solid_oidc_client_browser_core_9566af4e__;
|
|
183
|
+
;// ./src/authSession/session.ts
|
|
184
|
+
/**
|
|
185
|
+
* OIDC session factory.
|
|
186
|
+
*
|
|
187
|
+
* Everything needed to create and configure the underlying auth session:
|
|
188
|
+
* - Session database backends (in-memory and IndexedDB)
|
|
189
|
+
* - Session instantiation (WebSession or SessionCore, with local-dev
|
|
190
|
+
* fallbacks for environments where the service worker can't load)
|
|
191
|
+
* - Login compatibility shim that normalises legacy call-site signatures
|
|
192
|
+
* and resolves the canonical issuer through /.well-known discovery
|
|
193
|
+
*
|
|
194
|
+
* The raw session instance (_session) is consumed by authSession.ts where
|
|
195
|
+
* the legacy event layer and logout listener are attached.
|
|
196
|
+
*
|
|
197
|
+
* DO NOT import _session directly — always go through authSession.ts so
|
|
198
|
+
* the event wiring is guaranteed to run.
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Session databases
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
class MemorySessionDatabase {
|
|
206
|
+
constructor() {
|
|
207
|
+
this.map = new Map();
|
|
208
|
+
}
|
|
209
|
+
shouldPreserveExistingRefreshToken(id, value) {
|
|
210
|
+
return id === 'refresh_token' && (value == null || value === '') && this.map.has(id);
|
|
211
|
+
}
|
|
212
|
+
async init() {
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
async setItem(id, value) {
|
|
216
|
+
// Some Solid IdPs do not include refresh_token on refresh responses.
|
|
217
|
+
// Keep the previous token instead of overwriting it with null/undefined.
|
|
218
|
+
if (this.shouldPreserveExistingRefreshToken(id, value)) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
this.map.set(id, value);
|
|
222
|
+
}
|
|
223
|
+
async getItem(id) {
|
|
224
|
+
return this.map.has(id) ? this.map.get(id) : null;
|
|
225
|
+
}
|
|
226
|
+
async deleteItem(id) {
|
|
227
|
+
this.map.delete(id);
|
|
228
|
+
}
|
|
229
|
+
async clear() {
|
|
230
|
+
this.map.clear();
|
|
231
|
+
}
|
|
232
|
+
close() {
|
|
233
|
+
// No-op for in-memory database
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
class IndexedDbSessionDatabase {
|
|
237
|
+
constructor() {
|
|
238
|
+
this.db = null;
|
|
239
|
+
this.dbName = 'soidc';
|
|
240
|
+
this.storeName = 'session';
|
|
241
|
+
this.dbVersion = 1;
|
|
242
|
+
}
|
|
243
|
+
async shouldPreserveExistingRefreshToken(id, value) {
|
|
244
|
+
if (id !== 'refresh_token' || !(value == null || value === '')) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
const existing = await this.getItem(id);
|
|
248
|
+
return existing != null && existing !== '';
|
|
249
|
+
}
|
|
250
|
+
async init() {
|
|
251
|
+
if (this.db)
|
|
252
|
+
return this;
|
|
253
|
+
if (typeof indexedDB === 'undefined') {
|
|
254
|
+
throw new Error('IndexedDB is not available in this environment');
|
|
255
|
+
}
|
|
256
|
+
await new Promise((resolve, reject) => {
|
|
257
|
+
const request = indexedDB.open(this.dbName, this.dbVersion);
|
|
258
|
+
request.onerror = () => reject(request.error);
|
|
259
|
+
request.onsuccess = () => {
|
|
260
|
+
this.db = request.result;
|
|
261
|
+
resolve();
|
|
262
|
+
};
|
|
263
|
+
request.onupgradeneeded = () => {
|
|
264
|
+
const db = request.result;
|
|
265
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
266
|
+
db.createObjectStore(this.storeName);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
async setItem(id, value) {
|
|
273
|
+
await this.init();
|
|
274
|
+
// Some Solid IdPs do not include refresh_token on refresh responses.
|
|
275
|
+
// Keep the previous token instead of overwriting it with null/undefined.
|
|
276
|
+
if (await this.shouldPreserveExistingRefreshToken(id, value)) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
await this.withStore('readwrite', store => store.put(value, id));
|
|
280
|
+
}
|
|
281
|
+
async getItem(id) {
|
|
282
|
+
await this.init();
|
|
283
|
+
return this.withStore('readonly', store => store.get(id));
|
|
284
|
+
}
|
|
285
|
+
async deleteItem(id) {
|
|
286
|
+
await this.init();
|
|
287
|
+
await this.withStore('readwrite', store => store.delete(id));
|
|
288
|
+
}
|
|
289
|
+
async clear() {
|
|
290
|
+
await this.init();
|
|
291
|
+
await this.withStore('readwrite', store => store.clear());
|
|
292
|
+
}
|
|
293
|
+
close() {
|
|
294
|
+
if (this.db) {
|
|
295
|
+
this.db.close();
|
|
296
|
+
this.db = null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
withStore(mode, op) {
|
|
300
|
+
return new Promise((resolve, reject) => {
|
|
301
|
+
if (!this.db) {
|
|
302
|
+
reject(new Error('Session database not initialized'));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const tx = this.db.transaction(this.storeName, mode);
|
|
306
|
+
const store = tx.objectStore(this.storeName);
|
|
307
|
+
const request = op(store);
|
|
308
|
+
let result = null;
|
|
309
|
+
tx.onerror = () => { var _a; return reject((_a = tx.error) !== null && _a !== void 0 ? _a : request.error); };
|
|
310
|
+
tx.onabort = () => { var _a, _b; return reject((_b = (_a = tx.error) !== null && _a !== void 0 ? _a : request.error) !== null && _b !== void 0 ? _b : new Error('IndexedDB transaction aborted')); };
|
|
311
|
+
tx.oncomplete = () => resolve(result);
|
|
312
|
+
request.onerror = () => reject(request.error);
|
|
313
|
+
request.onsuccess = () => {
|
|
314
|
+
var _a;
|
|
315
|
+
result = (_a = request.result) !== null && _a !== void 0 ? _a : null;
|
|
316
|
+
};
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// Session instantiation
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
function getSessionCoreCtor() {
|
|
324
|
+
var _a, _b, _c;
|
|
325
|
+
const coreAny = core_namespaceObject;
|
|
326
|
+
const candidate = (_c = (_a = coreAny.SessionCore) !== null && _a !== void 0 ? _a : (_b = coreAny.default) === null || _b === void 0 ? void 0 : _b.SessionCore) !== null && _c !== void 0 ? _c : coreAny.default;
|
|
327
|
+
if (typeof candidate !== 'function') {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
return candidate;
|
|
331
|
+
}
|
|
332
|
+
const SessionCoreCtor = getSessionCoreCtor();
|
|
333
|
+
function createSession() {
|
|
334
|
+
const shouldSkipWorkerInLocalDev = typeof window !== 'undefined' && (() => {
|
|
335
|
+
const host = window.location.hostname;
|
|
336
|
+
// In local NSS setups (including subdomain mode like alice.localhost),
|
|
337
|
+
// worker-based session storage can be brittle and lose state on reload.
|
|
338
|
+
// Prefer SessionCore + IndexedDB for deterministic persistence.
|
|
339
|
+
return host === 'localhost' || host === '127.0.0.1' || host.endsWith('.localhost');
|
|
340
|
+
})();
|
|
341
|
+
if (shouldSkipWorkerInLocalDev) {
|
|
342
|
+
if (SessionCoreCtor) {
|
|
343
|
+
return new SessionCoreCtor(undefined, { database: new IndexedDbSessionDatabase() });
|
|
344
|
+
}
|
|
345
|
+
return new __WEBPACK_EXTERNAL_MODULE__uvdsl_solid_oidc_client_browser_144ab6cc_Session__();
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
return new __WEBPACK_EXTERNAL_MODULE__uvdsl_solid_oidc_client_browser_144ab6cc_Session__();
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
// In some deployments, worker URL resolution can become file:// and fail cross-origin.
|
|
352
|
+
// Fall back to SessionCore so auth still works without background refresh worker.
|
|
353
|
+
// Use IndexedDB to keep refresh-token persistence across page reloads.
|
|
354
|
+
console.warn('solid-logic: falling back to non-worker auth session:', error);
|
|
355
|
+
try {
|
|
356
|
+
if (SessionCoreCtor) {
|
|
357
|
+
return new SessionCoreCtor(undefined, { database: new IndexedDbSessionDatabase() });
|
|
358
|
+
}
|
|
359
|
+
return new __WEBPACK_EXTERNAL_MODULE__uvdsl_solid_oidc_client_browser_144ab6cc_Session__();
|
|
360
|
+
}
|
|
361
|
+
catch (dbError) {
|
|
362
|
+
console.warn('solid-logic: IndexedDB unavailable, using in-memory session database:', dbError);
|
|
363
|
+
if (SessionCoreCtor) {
|
|
364
|
+
return new SessionCoreCtor(undefined, { database: new MemorySessionDatabase() });
|
|
365
|
+
}
|
|
366
|
+
return new __WEBPACK_EXTERNAL_MODULE__uvdsl_solid_oidc_client_browser_144ab6cc_Session__();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// ---------------------------------------------------------------------------
|
|
371
|
+
// Singleton session
|
|
372
|
+
// ---------------------------------------------------------------------------
|
|
373
|
+
const _session = createSession();
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
;// ./src/authSession/issuer.ts
|
|
377
|
+
/**
|
|
378
|
+
* Issuer discovery utilities.
|
|
379
|
+
*
|
|
380
|
+
* Resolves OIDC issuer endpoints from /.well-known/openid-configuration
|
|
381
|
+
* so that login can use the canonical issuer host.
|
|
382
|
+
*/
|
|
383
|
+
async function discoverIssuerFromWellKnown(issuer) {
|
|
384
|
+
try {
|
|
385
|
+
const issuerUrl = new URL(issuer);
|
|
386
|
+
const wellKnownUrl = new URL('/.well-known/openid-configuration', issuerUrl.origin);
|
|
387
|
+
const wellKnownResponse = await fetch(wellKnownUrl.toString(), { credentials: 'include' });
|
|
388
|
+
if (!wellKnownResponse.ok) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
const wellKnownPayload = await wellKnownResponse.json();
|
|
392
|
+
if (typeof (wellKnownPayload === null || wellKnownPayload === void 0 ? void 0 : wellKnownPayload.issuer) !== 'string' || !wellKnownPayload.issuer) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
return wellKnownPayload.issuer.replace(/\/$/, '');
|
|
396
|
+
}
|
|
397
|
+
catch (_err) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
async function resolveIssuerForLogin(issuer) {
|
|
402
|
+
// Prefer the issuer advertised by discovery; if app and issuer hosts still differ,
|
|
403
|
+
// redirecting to the canonical issuer host is cleaner than rewriting the issuer here.
|
|
404
|
+
const discoveredIssuer = await discoverIssuerFromWellKnown(issuer);
|
|
405
|
+
if (discoveredIssuer) {
|
|
406
|
+
return discoveredIssuer;
|
|
407
|
+
}
|
|
408
|
+
return issuer;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
;// ./src/authSession/events.ts
|
|
412
|
+
/**
|
|
413
|
+
* Legacy event compatibility layer.
|
|
414
|
+
*
|
|
415
|
+
* Pure EventEmitter-style shim — no side effects, no uvdsl dependencies.
|
|
416
|
+
* Wired into the auth session by authSession.ts.
|
|
417
|
+
*/
|
|
418
|
+
/**
|
|
419
|
+
* Minimal EventEmitter-style shim so that existing consumers using
|
|
420
|
+
* `authSession.events.on('login' | 'logout' | 'sessionRestore', handler)`
|
|
421
|
+
* continue working without modification.
|
|
422
|
+
*
|
|
423
|
+
* Events are emitted by SolidAuthnLogic.checkUser() (login/sessionRestore)
|
|
424
|
+
* and by the sessionStateChange listener in authSession.ts (logout).
|
|
425
|
+
*/
|
|
426
|
+
class SessionEvents {
|
|
427
|
+
constructor() {
|
|
428
|
+
this.listeners = new Map();
|
|
429
|
+
}
|
|
430
|
+
on(event, handler) {
|
|
431
|
+
if (!this.listeners.has(event))
|
|
432
|
+
this.listeners.set(event, new Set());
|
|
433
|
+
this.listeners.get(event).add(handler);
|
|
434
|
+
}
|
|
435
|
+
off(event, handler) {
|
|
436
|
+
var _a;
|
|
437
|
+
(_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.delete(handler);
|
|
438
|
+
}
|
|
439
|
+
emit(event, ...args) {
|
|
440
|
+
var _a;
|
|
441
|
+
(_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.forEach(h => h(...args));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
;// ./src/authSession/authSession.ts
|
|
446
|
+
/**
|
|
447
|
+
* Auth session wiring.
|
|
448
|
+
*
|
|
449
|
+
* Takes the raw OIDC session from session.ts and layers on:
|
|
450
|
+
* - Login compatibility shim (normalises legacy call-site signatures
|
|
451
|
+
* and resolves the canonical issuer)
|
|
452
|
+
* - SessionEvents shim (legacy EventEmitter-style API)
|
|
453
|
+
* - Logout listener (emits 'logout' on session deactivation)
|
|
454
|
+
*
|
|
455
|
+
* Exports the fully assembled authSession.
|
|
456
|
+
*/
|
|
457
|
+
var _a;
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
// ---------------------------------------------------------------------------
|
|
462
|
+
// Login compatibility shim
|
|
463
|
+
// ---------------------------------------------------------------------------
|
|
464
|
+
// Wraps _session.login() so that call sites with different calling
|
|
465
|
+
// conventions all work. The underlying session expects:
|
|
466
|
+
// login(issuer: string, redirectUrl: string)
|
|
467
|
+
//
|
|
468
|
+
// idpOrOptions can be:
|
|
469
|
+
// - a string (issuer URL) — passed through with less resolution
|
|
470
|
+
// - an options object with any of these field-name variants:
|
|
471
|
+
// issuer: oidcIssuer | idp | issuer
|
|
472
|
+
// redirect: redirectUrl | redirect_uri | redirectUri
|
|
473
|
+
// (all redirect field names map to the same value: the URL the IdP
|
|
474
|
+
// should send the browser back to after authentication)
|
|
475
|
+
// - anything else — passed through to the underlying session as-is
|
|
476
|
+
//
|
|
477
|
+
// In all cases the issuer is resolved through
|
|
478
|
+
// /.well-known/openid-configuration before redirect so the canonical
|
|
479
|
+
// issuer host is used.
|
|
480
|
+
const sessionAny = _session;
|
|
481
|
+
const originalLogin = typeof sessionAny.login === 'function'
|
|
482
|
+
? sessionAny.login.bind(_session)
|
|
483
|
+
: undefined;
|
|
484
|
+
if (originalLogin) {
|
|
485
|
+
const compatLogin = async (idpOrOptions, redirectUri) => {
|
|
486
|
+
var _a, _b, _c, _d;
|
|
487
|
+
if (idpOrOptions && typeof idpOrOptions === 'object' && !Array.isArray(idpOrOptions)) {
|
|
488
|
+
const oidcIssuer = (_b = (_a = idpOrOptions.oidcIssuer) !== null && _a !== void 0 ? _a : idpOrOptions.idp) !== null && _b !== void 0 ? _b : idpOrOptions.issuer;
|
|
489
|
+
const redirectUrl = (_d = (_c = idpOrOptions.redirectUrl) !== null && _c !== void 0 ? _c : idpOrOptions.redirect_uri) !== null && _d !== void 0 ? _d : idpOrOptions.redirectUri;
|
|
490
|
+
if (typeof oidcIssuer === 'string' && typeof redirectUrl === 'string') {
|
|
491
|
+
return originalLogin(await resolveIssuerForLogin(oidcIssuer), redirectUrl);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (typeof idpOrOptions === 'string') {
|
|
495
|
+
return originalLogin(await resolveIssuerForLogin(idpOrOptions), redirectUri);
|
|
496
|
+
}
|
|
497
|
+
return originalLogin(idpOrOptions, redirectUri);
|
|
498
|
+
};
|
|
499
|
+
sessionAny.login = compatLogin;
|
|
500
|
+
}
|
|
501
|
+
// ---------------------------------------------------------------------------
|
|
502
|
+
// Legacy event layer
|
|
503
|
+
// ---------------------------------------------------------------------------
|
|
504
|
+
const events = new SessionEvents();
|
|
505
|
+
// Emit the legacy 'logout' event when the session transitions from active to inactive.
|
|
506
|
+
// 'login' and 'sessionRestore' are emitted in SolidAuthnLogic.checkUser()
|
|
507
|
+
// because only that call site knows which path activated the session.
|
|
508
|
+
let _wasActive = (_a = _session.isActive) !== null && _a !== void 0 ? _a : Boolean(_session.webId);
|
|
509
|
+
if (typeof _session.addEventListener === 'function') {
|
|
510
|
+
;
|
|
511
|
+
_session.addEventListener('sessionStateChange', () => {
|
|
512
|
+
var _a;
|
|
513
|
+
const isNowActive = (_a = _session.isActive) !== null && _a !== void 0 ? _a : Boolean(_session.webId);
|
|
514
|
+
if (_wasActive && !isNowActive) {
|
|
515
|
+
events.emit('logout');
|
|
516
|
+
}
|
|
517
|
+
_wasActive = isNowActive;
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
const authSession = Object.assign(_session, { events });
|
|
521
|
+
// Legacy `info` compatibility shape.
|
|
522
|
+
// The uvdsl session stores state on `webId_`/`isActive_` and exposes them via
|
|
523
|
+
// `webId`/`isActive` getters, but legacy consumers (e.g. solid-ui's
|
|
524
|
+
// `loginStatusBox` widget, `SolidAuthnLogic.currentUser()`'s fallback path)
|
|
525
|
+
// read `authSession.info.webId` / `authSession.info.isLoggedIn`. Expose those
|
|
526
|
+
// as a derived getter so legacy login/logout UI renders the correct state
|
|
527
|
+
// (logout button instead of a mislabelled login button).
|
|
528
|
+
Object.defineProperty(authSession, 'info', {
|
|
529
|
+
enumerable: true,
|
|
530
|
+
configurable: true,
|
|
531
|
+
get() {
|
|
532
|
+
const sessionAny = _session;
|
|
533
|
+
const isActive = sessionAny.isActive === true || Boolean(sessionAny.webId);
|
|
534
|
+
return {
|
|
535
|
+
webId: sessionAny.webId,
|
|
536
|
+
isLoggedIn: isActive
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
;// external "rdflib"
|
|
542
|
+
const external_rdflib_namespaceObject = __WEBPACK_EXTERNAL_MODULE_rdflib__;
|
|
543
|
+
// EXTERNAL MODULE: ./node_modules/solid-namespace/index.js
|
|
544
|
+
var solid_namespace = __webpack_require__(386);
|
|
545
|
+
var solid_namespace_default = /*#__PURE__*/__webpack_require__.n(solid_namespace);
|
|
546
|
+
;// ./src/util/ns.ts
|
|
547
|
+
// Namespaces we commonly use and have common prefixes for around Solid
|
|
548
|
+
// Delegate to this which takes RDFlib as param.
|
|
549
|
+
|
|
550
|
+
const ns_ns = solid_namespace_default()(external_rdflib_namespaceObject);
|
|
551
|
+
|
|
552
|
+
;// ./src/acl/aclLogic.ts
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
const ACL_LINK = (0,external_rdflib_namespaceObject.sym)('http://www.iana.org/assignments/link-relations/acl');
|
|
556
|
+
function createAclLogic(store) {
|
|
557
|
+
const ns = ns_ns;
|
|
558
|
+
async function findAclDocUrl(url) {
|
|
559
|
+
await store.fetcher.load(url);
|
|
560
|
+
const docNode = store.any(url, ACL_LINK);
|
|
561
|
+
if (!docNode) {
|
|
562
|
+
throw new Error(`No ACL link discovered for ${url}`);
|
|
563
|
+
}
|
|
564
|
+
return docNode.value;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Simple Access Control
|
|
568
|
+
*
|
|
569
|
+
* This function sets up a simple default ACL for a resource, with
|
|
570
|
+
* RWC for the owner, and a specified access (default none) for the public.
|
|
571
|
+
* In all cases owner has read write control.
|
|
572
|
+
* Parameter lists modes allowed to public
|
|
573
|
+
*
|
|
574
|
+
* @param options
|
|
575
|
+
* @param options.public eg ['Read', 'Write']
|
|
576
|
+
*
|
|
577
|
+
* @returns Resolves with aclDoc uri on successful write
|
|
578
|
+
*/
|
|
579
|
+
function setACLUserPublic(docURI, me, options) {
|
|
580
|
+
const aclDoc = store.any(store.sym(docURI), ACL_LINK);
|
|
581
|
+
return Promise.resolve()
|
|
582
|
+
.then(() => {
|
|
583
|
+
if (aclDoc) {
|
|
584
|
+
return aclDoc;
|
|
585
|
+
}
|
|
586
|
+
return fetchACLRel(docURI).catch(err => {
|
|
587
|
+
throw new Error(`Error fetching rel=ACL header for ${docURI}: ${err}`);
|
|
588
|
+
});
|
|
589
|
+
})
|
|
590
|
+
.then(aclDoc => {
|
|
591
|
+
const aclText = genACLText(docURI, me, aclDoc.uri, options);
|
|
592
|
+
if (!store.fetcher) {
|
|
593
|
+
throw new Error('Cannot PUT this, store has no fetcher');
|
|
594
|
+
}
|
|
595
|
+
return store.fetcher
|
|
596
|
+
.webOperation('PUT', aclDoc.uri, {
|
|
597
|
+
data: aclText,
|
|
598
|
+
contentType: 'text/turtle'
|
|
599
|
+
})
|
|
600
|
+
.then(result => {
|
|
601
|
+
if (!result.ok) {
|
|
602
|
+
throw new Error('Error writing ACL text: ' + result.error);
|
|
603
|
+
}
|
|
604
|
+
return aclDoc;
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* @param docURI
|
|
610
|
+
* @returns
|
|
611
|
+
*/
|
|
612
|
+
function fetchACLRel(docURI) {
|
|
613
|
+
const fetcher = store.fetcher;
|
|
614
|
+
if (!fetcher) {
|
|
615
|
+
throw new Error('Cannot fetch ACL rel, store has no fetcher');
|
|
616
|
+
}
|
|
617
|
+
return fetcher.load(docURI).then(result => {
|
|
618
|
+
if (!result.ok) {
|
|
619
|
+
throw new Error('fetchACLRel: While loading:' + result.error);
|
|
620
|
+
}
|
|
621
|
+
const aclDoc = store.any(store.sym(docURI), ACL_LINK);
|
|
622
|
+
if (!aclDoc) {
|
|
623
|
+
throw new Error('fetchACLRel: No Link rel=ACL header for ' + docURI);
|
|
624
|
+
}
|
|
625
|
+
return aclDoc;
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* @param docURI
|
|
630
|
+
* @param me
|
|
631
|
+
* @param aclURI
|
|
632
|
+
* @param options
|
|
633
|
+
*
|
|
634
|
+
* @returns Serialized ACL
|
|
635
|
+
*/
|
|
636
|
+
function genACLText(docURI, me, aclURI, options = {}) {
|
|
637
|
+
const optPublic = options.public || [];
|
|
638
|
+
const g = (0,external_rdflib_namespaceObject.graph)();
|
|
639
|
+
const auth = (0,external_rdflib_namespaceObject.Namespace)('http://www.w3.org/ns/auth/acl#');
|
|
640
|
+
let a = g.sym(`${aclURI}#a1`);
|
|
641
|
+
const acl = g.sym(aclURI);
|
|
642
|
+
const doc = g.sym(docURI);
|
|
643
|
+
g.add(a, ns.rdf('type'), auth('Authorization'), acl);
|
|
644
|
+
g.add(a, auth('accessTo'), doc, acl);
|
|
645
|
+
if (options.defaultForNew) {
|
|
646
|
+
g.add(a, auth('default'), doc, acl);
|
|
647
|
+
}
|
|
648
|
+
g.add(a, auth('agent'), me, acl);
|
|
649
|
+
g.add(a, auth('mode'), auth('Read'), acl);
|
|
650
|
+
g.add(a, auth('mode'), auth('Write'), acl);
|
|
651
|
+
g.add(a, auth('mode'), auth('Control'), acl);
|
|
652
|
+
if (optPublic.length) {
|
|
653
|
+
a = g.sym(`${aclURI}#a2`);
|
|
654
|
+
g.add(a, ns.rdf('type'), auth('Authorization'), acl);
|
|
655
|
+
g.add(a, auth('accessTo'), doc, acl);
|
|
656
|
+
g.add(a, auth('agentClass'), ns.foaf('Agent'), acl);
|
|
657
|
+
for (let p = 0; p < optPublic.length; p++) {
|
|
658
|
+
g.add(a, auth('mode'), auth(optPublic[p]), acl); // Like 'Read' etc
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return (0,external_rdflib_namespaceObject.serialize)(acl, g, aclURI);
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
findAclDocUrl,
|
|
665
|
+
setACLUserPublic,
|
|
666
|
+
genACLText
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
;// ./src/authn/authUtil.ts
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* find a user or app's context as set in window.SolidAppContext
|
|
675
|
+
* this is a const, not a function, because we have problems to jest mock it otherwise
|
|
676
|
+
* see: https://github.com/facebook/jest/issues/936#issuecomment-545080082 for more
|
|
677
|
+
* @return {any} - an appContext object
|
|
678
|
+
*/
|
|
679
|
+
const appContext = () => {
|
|
680
|
+
let { SolidAppContext } = window;
|
|
681
|
+
SolidAppContext || (SolidAppContext = {});
|
|
682
|
+
SolidAppContext.viewingNoAuthPage = false;
|
|
683
|
+
if (SolidAppContext.noAuth && window.document) {
|
|
684
|
+
const currentPage = window.document.location.href;
|
|
685
|
+
if (currentPage.startsWith(SolidAppContext.noAuth)) {
|
|
686
|
+
SolidAppContext.viewingNoAuthPage = true;
|
|
687
|
+
const params = new URLSearchParams(window.document.location.search);
|
|
688
|
+
if (params) {
|
|
689
|
+
let viewedPage = SolidAppContext.viewedPage = params.get('uri') || null;
|
|
690
|
+
if (viewedPage) {
|
|
691
|
+
viewedPage = decodeURI(viewedPage);
|
|
692
|
+
if (!viewedPage.startsWith(SolidAppContext.noAuth)) {
|
|
693
|
+
const ary = viewedPage.split(/\//);
|
|
694
|
+
SolidAppContext.idp = ary[0] + '//' + ary[2];
|
|
695
|
+
SolidAppContext.viewingNoAuthPage = false;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return SolidAppContext;
|
|
702
|
+
};
|
|
703
|
+
/**
|
|
704
|
+
* Returns `sym($SolidTestEnvironment.username)` if
|
|
705
|
+
* `$SolidTestEnvironment.username` is defined as a global
|
|
706
|
+
* or
|
|
707
|
+
* returns testID defined in the HTML page
|
|
708
|
+
* @returns {NamedNode|null}
|
|
709
|
+
*/
|
|
710
|
+
function offlineTestID() {
|
|
711
|
+
const { $SolidTestEnvironment } = window;
|
|
712
|
+
if (typeof $SolidTestEnvironment !== 'undefined' &&
|
|
713
|
+
$SolidTestEnvironment.username) {
|
|
714
|
+
// Test setup
|
|
715
|
+
log('Assuming the user is ' + $SolidTestEnvironment.username);
|
|
716
|
+
return (0,external_rdflib_namespaceObject.sym)($SolidTestEnvironment.username);
|
|
717
|
+
}
|
|
718
|
+
// hack that makes SolidOS work in offline mode by adding the webId directly in html
|
|
719
|
+
// example usage: https://github.com/solidos/mashlib/blob/29b8b53c46bf02e0e219f0bacd51b0e9951001dd/test/contact/local.html#L37
|
|
720
|
+
if (typeof document !== 'undefined' &&
|
|
721
|
+
document.location &&
|
|
722
|
+
('' + document.location).slice(0, 16) === 'http://localhost') {
|
|
723
|
+
const div = document.getElementById('appTarget');
|
|
724
|
+
if (!div)
|
|
725
|
+
return null;
|
|
726
|
+
const id = div.getAttribute('testID');
|
|
727
|
+
if (!id)
|
|
728
|
+
return null;
|
|
729
|
+
log('Assuming user is ' + id);
|
|
730
|
+
return (0,external_rdflib_namespaceObject.sym)(id);
|
|
731
|
+
}
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
;// ./src/authn/SolidAuthnLogic.ts
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
class SolidAuthnLogic {
|
|
740
|
+
constructor(solidAuthSession) {
|
|
741
|
+
this.checkUserInFlight = null;
|
|
742
|
+
this.sessionRestoreHookAttached = false;
|
|
743
|
+
this.fallbackWebId = null;
|
|
744
|
+
this.session = solidAuthSession;
|
|
745
|
+
}
|
|
746
|
+
// we created authSession getter because we want to access it as authn.authSession externally
|
|
747
|
+
get authSession() { return this.session; }
|
|
748
|
+
currentUser() {
|
|
749
|
+
var _a, _b;
|
|
750
|
+
const app = appContext();
|
|
751
|
+
if (app.viewingNoAuthPage) {
|
|
752
|
+
return (0,external_rdflib_namespaceObject.sym)(app.webId);
|
|
753
|
+
}
|
|
754
|
+
const sessionAny = this.session;
|
|
755
|
+
const infoWebId = (_a = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.info) === null || _a === void 0 ? void 0 : _a.webId;
|
|
756
|
+
const sessionWebId = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.webId;
|
|
757
|
+
const webId = infoWebId || sessionWebId || this.fallbackWebId;
|
|
758
|
+
const infoLoggedIn = (_b = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.info) === null || _b === void 0 ? void 0 : _b.isLoggedIn;
|
|
759
|
+
const sessionActive = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.isActive;
|
|
760
|
+
const isLoggedIn = infoLoggedIn === true || sessionActive === true ||
|
|
761
|
+
((infoLoggedIn == null && sessionActive == null) ? Boolean(webId) : false) ||
|
|
762
|
+
Boolean(this.fallbackWebId);
|
|
763
|
+
if (this && this.session && webId && isLoggedIn) {
|
|
764
|
+
return (0,external_rdflib_namespaceObject.sym)(webId);
|
|
765
|
+
}
|
|
766
|
+
return offlineTestID(); // null unless testing
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Retrieves currently logged in webId from either
|
|
770
|
+
* defaultTestUser or SolidAuth
|
|
771
|
+
* Also activates a session after login
|
|
772
|
+
* @param [setUserCallback] Optional callback
|
|
773
|
+
* @returns Resolves with webId uri, if no callback provided
|
|
774
|
+
*/
|
|
775
|
+
async checkUser(setUserCallback) {
|
|
776
|
+
var _a;
|
|
777
|
+
// Save hash for "restorePreviousSession"
|
|
778
|
+
const preLoginRedirectHash = new URL(window.location.href).hash;
|
|
779
|
+
if (preLoginRedirectHash) {
|
|
780
|
+
window.localStorage.setItem('preLoginRedirectHash', preLoginRedirectHash);
|
|
781
|
+
}
|
|
782
|
+
const sessionAny = this.session;
|
|
783
|
+
if (!this.sessionRestoreHookAttached && typeof ((_a = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.events) === null || _a === void 0 ? void 0 : _a.on) === 'function') {
|
|
784
|
+
// Backward-compatible hook for auth clients exposing an EventEmitter-style API.
|
|
785
|
+
sessionAny.events.on('sessionRestore', (url) => {
|
|
786
|
+
log(`Session restored to ${url}`);
|
|
787
|
+
if (document.location.toString() !== url)
|
|
788
|
+
history.replaceState(null, '', url);
|
|
789
|
+
});
|
|
790
|
+
this.sessionRestoreHookAttached = true;
|
|
791
|
+
}
|
|
792
|
+
if (!this.checkUserInFlight) {
|
|
793
|
+
this.checkUserInFlight = this.resolveCurrentUser();
|
|
794
|
+
}
|
|
795
|
+
const inFlight = this.checkUserInFlight;
|
|
796
|
+
let me;
|
|
797
|
+
try {
|
|
798
|
+
me = await inFlight;
|
|
799
|
+
}
|
|
800
|
+
finally {
|
|
801
|
+
if (this.checkUserInFlight === inFlight) {
|
|
802
|
+
this.checkUserInFlight = null;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return Promise.resolve(setUserCallback ? setUserCallback(me) : me);
|
|
806
|
+
}
|
|
807
|
+
async resolveCurrentUser() {
|
|
808
|
+
var _a, _b, _c, _d, _e, _f;
|
|
809
|
+
const sessionAny = this.session;
|
|
810
|
+
/**
|
|
811
|
+
* Handle a successful authentication redirect
|
|
812
|
+
*/
|
|
813
|
+
const redirectUrl = new URL(window.location.href);
|
|
814
|
+
redirectUrl.hash = '';
|
|
815
|
+
if (typeof (sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.handleIncomingRedirect) === 'function') {
|
|
816
|
+
await sessionAny.handleIncomingRedirect({
|
|
817
|
+
restorePreviousSession: true,
|
|
818
|
+
url: redirectUrl.href
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
if (typeof (sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.restore) === 'function') {
|
|
823
|
+
const wasActive = (_a = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.isActive) !== null && _a !== void 0 ? _a : Boolean(sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.webId);
|
|
824
|
+
try {
|
|
825
|
+
await sessionAny.restore();
|
|
826
|
+
}
|
|
827
|
+
catch (error) {
|
|
828
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
829
|
+
if (!/no session to restore/i.test(message)) {
|
|
830
|
+
throw error;
|
|
831
|
+
}
|
|
832
|
+
log('No previous session to restore');
|
|
833
|
+
}
|
|
834
|
+
const isNowActive = (_b = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.isActive) !== null && _b !== void 0 ? _b : Boolean(sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.webId);
|
|
835
|
+
if (!wasActive && isNowActive) {
|
|
836
|
+
(_c = sessionAny.events) === null || _c === void 0 ? void 0 : _c.emit('sessionRestore', window.location.href);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
if (typeof (sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.handleRedirectFromLogin) === 'function') {
|
|
840
|
+
const wasActive = (_d = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.isActive) !== null && _d !== void 0 ? _d : Boolean(sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.webId);
|
|
841
|
+
await sessionAny.handleRedirectFromLogin();
|
|
842
|
+
const isNowActive = (_e = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.isActive) !== null && _e !== void 0 ? _e : Boolean(sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.webId);
|
|
843
|
+
if (!wasActive && isNowActive) {
|
|
844
|
+
(_f = sessionAny.events) === null || _f === void 0 ? void 0 : _f.emit('login');
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
// Check to see if a hash was stored in local storage
|
|
849
|
+
const postLoginRedirectHash = window.localStorage.getItem('preLoginRedirectHash');
|
|
850
|
+
if (postLoginRedirectHash) {
|
|
851
|
+
const curUrl = new URL(window.location.href);
|
|
852
|
+
if (curUrl.hash !== postLoginRedirectHash) {
|
|
853
|
+
if (history.pushState) {
|
|
854
|
+
// debug.log('Setting window.location.has using pushState')
|
|
855
|
+
history.pushState(null, document.title, postLoginRedirectHash);
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
// debug.warn('Setting window.location.has using location.hash')
|
|
859
|
+
location.hash = postLoginRedirectHash;
|
|
860
|
+
}
|
|
861
|
+
curUrl.hash = postLoginRedirectHash;
|
|
862
|
+
}
|
|
863
|
+
// See https://stackoverflow.com/questions/3870057/how-can-i-update-window-location-hash-without-jumping-the-document
|
|
864
|
+
// window.location.href = curUrl.toString()// @@ See https://developer.mozilla.org/en-US/docs/Web/API/Window/location
|
|
865
|
+
window.localStorage.setItem('preLoginRedirectHash', '');
|
|
866
|
+
}
|
|
867
|
+
// Check to see if already logged in / have the WebID
|
|
868
|
+
let me = offlineTestID();
|
|
869
|
+
if (me) {
|
|
870
|
+
return me;
|
|
871
|
+
}
|
|
872
|
+
let webId = this.webIdFromSession(sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.info, sessionAny);
|
|
873
|
+
if (!webId) {
|
|
874
|
+
// NSS-specific fallback: recover WebID from NSS cookie session when client restore is empty.
|
|
875
|
+
webId = await this.probeNssCookieBackedWebId();
|
|
876
|
+
}
|
|
877
|
+
if (webId) {
|
|
878
|
+
this.fallbackWebId = webId;
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
this.fallbackWebId = null;
|
|
882
|
+
}
|
|
883
|
+
if (webId) {
|
|
884
|
+
me = this.saveUser(webId);
|
|
885
|
+
}
|
|
886
|
+
if (me) {
|
|
887
|
+
log(`(Logged in as ${me} by authentication)`);
|
|
888
|
+
}
|
|
889
|
+
return me;
|
|
890
|
+
}
|
|
891
|
+
async probeNssCookieBackedWebId() {
|
|
892
|
+
if (typeof window === 'undefined') {
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
895
|
+
const { hostname, port, protocol } = window.location;
|
|
896
|
+
const localhostSuffix = '.localhost';
|
|
897
|
+
// NSS local pods use subdomains like alice.localhost.
|
|
898
|
+
if (!hostname.endsWith(localhostSuffix)) {
|
|
899
|
+
return null;
|
|
900
|
+
}
|
|
901
|
+
const podName = hostname.slice(0, -localhostSuffix.length);
|
|
902
|
+
if (!podName || podName === 'localhost' || podName.includes('.')) {
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
try {
|
|
906
|
+
// NSS returns 403 on this account page when the cookie session is valid.
|
|
907
|
+
const probeResponse = await fetch('/account/password/change', {
|
|
908
|
+
credentials: 'include',
|
|
909
|
+
redirect: 'manual',
|
|
910
|
+
cache: 'no-store'
|
|
911
|
+
});
|
|
912
|
+
if (probeResponse.status !== 403) {
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
const origin = `${protocol}//${hostname}${port ? `:${port}` : ''}`;
|
|
916
|
+
return `${origin}/profile/card#me`;
|
|
917
|
+
}
|
|
918
|
+
catch (_error) {
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Saves `webId` in `context.me`
|
|
924
|
+
* @param webId
|
|
925
|
+
* @param context
|
|
926
|
+
*
|
|
927
|
+
* @returns Returns the WebID, after setting it
|
|
928
|
+
*/
|
|
929
|
+
saveUser(webId, context) {
|
|
930
|
+
let webIdUri;
|
|
931
|
+
if (webId) {
|
|
932
|
+
webIdUri = (typeof webId === 'string') ? webId : webId.uri;
|
|
933
|
+
const me = (0,external_rdflib_namespaceObject.namedNode)(webIdUri);
|
|
934
|
+
if (context) {
|
|
935
|
+
context.me = me;
|
|
936
|
+
}
|
|
937
|
+
return me;
|
|
938
|
+
}
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* @returns {Promise<string|null>} Resolves with WebID URI or null
|
|
943
|
+
*/
|
|
944
|
+
webIdFromSession(sessionInfo, sessionRoot) {
|
|
945
|
+
const webId = (sessionInfo === null || sessionInfo === void 0 ? void 0 : sessionInfo.webId) || (sessionRoot === null || sessionRoot === void 0 ? void 0 : sessionRoot.webId);
|
|
946
|
+
if (!webId) {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
const infoLoggedIn = sessionInfo === null || sessionInfo === void 0 ? void 0 : sessionInfo.isLoggedIn;
|
|
950
|
+
const rootLoggedIn = sessionRoot === null || sessionRoot === void 0 ? void 0 : sessionRoot.isLoggedIn;
|
|
951
|
+
const rootActive = sessionRoot === null || sessionRoot === void 0 ? void 0 : sessionRoot.isActive;
|
|
952
|
+
if (infoLoggedIn === true || rootLoggedIn === true || rootActive === true) {
|
|
953
|
+
return webId;
|
|
954
|
+
}
|
|
955
|
+
if (infoLoggedIn === false && rootLoggedIn === false && rootActive === false) {
|
|
956
|
+
return null;
|
|
957
|
+
}
|
|
958
|
+
return webId;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
;// ./src/util/utils.ts
|
|
963
|
+
/* unused harmony import specifier */ var NamedNode;
|
|
964
|
+
|
|
965
|
+
function newThing(doc) {
|
|
966
|
+
return (0,external_rdflib_namespaceObject.sym)(doc.uri + '#' + 'id' + ('' + Date.now()));
|
|
967
|
+
}
|
|
968
|
+
function uniqueNodes(arr) {
|
|
969
|
+
const uris = arr.map(x => x.uri);
|
|
970
|
+
const set = new Set(uris);
|
|
971
|
+
const uris2 = Array.from(set);
|
|
972
|
+
const arr2 = uris2.map(u => new NamedNode(u));
|
|
973
|
+
return arr2; // Array.from(new Set(arr.map(x => x.uri))).map(u => sym(u))
|
|
974
|
+
}
|
|
975
|
+
function getArchiveUrl(baseUrl, date) {
|
|
976
|
+
const year = date.getUTCFullYear();
|
|
977
|
+
const month = ('0' + (date.getUTCMonth() + 1)).slice(-2);
|
|
978
|
+
const day = ('0' + (date.getUTCDate())).slice(-2);
|
|
979
|
+
const parts = baseUrl.split('/');
|
|
980
|
+
const filename = parts[parts.length - 1];
|
|
981
|
+
return new URL(`./archive/${year}/${month}/${day}/${filename}`, baseUrl).toString();
|
|
982
|
+
}
|
|
983
|
+
function differentOrigin(doc) {
|
|
984
|
+
if (!doc) {
|
|
985
|
+
return true;
|
|
986
|
+
}
|
|
987
|
+
return (`${window.location.origin}/` !== new URL(doc.value).origin);
|
|
988
|
+
}
|
|
989
|
+
function suggestPreferencesFile(me) {
|
|
990
|
+
const stripped = me.uri.replace('/profile/', '/').replace('/public/', '/');
|
|
991
|
+
// const stripped = me.uri.replace(\/[p|P]rofile/\g, '/').replace(\/[p|P]ublic/\g, '/')
|
|
992
|
+
const folderURI = stripped.split('/').slice(0, -1).join('/') + '/settings/';
|
|
993
|
+
const fileURI = folderURI + 'prefs.ttl';
|
|
994
|
+
return (0,external_rdflib_namespaceObject.sym)(fileURI);
|
|
995
|
+
}
|
|
996
|
+
function determineChatContainer(invitee, podRoot) {
|
|
997
|
+
// Create chat
|
|
998
|
+
// See https://gitter.im/solid/chat-app?at=5f3c800f855be416a23ae74a
|
|
999
|
+
const chatContainerStr = new URL(`IndividualChats/${new URL(invitee.value).host}/`, podRoot.value).toString();
|
|
1000
|
+
return new external_rdflib_namespaceObject.NamedNode(chatContainerStr);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
;// ./src/chat/chatLogic.ts
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
const CHAT_LOCATION_IN_CONTAINER = 'index.ttl#this';
|
|
1008
|
+
function createChatLogic(store, profileLogic) {
|
|
1009
|
+
const ns = ns_ns;
|
|
1010
|
+
async function setAcl(chatContainer, me, invitee) {
|
|
1011
|
+
// Some servers don't present a Link http response header
|
|
1012
|
+
// if the container doesn't exist yet, so refetch the container
|
|
1013
|
+
// now that it has been created:
|
|
1014
|
+
await store.fetcher.load(chatContainer);
|
|
1015
|
+
// FIXME: check the Why value on this quad:
|
|
1016
|
+
const chatAclDoc = store.any(chatContainer, new external_rdflib_namespaceObject.NamedNode('http://www.iana.org/assignments/link-relations/acl'));
|
|
1017
|
+
if (!chatAclDoc) {
|
|
1018
|
+
throw new Error('Chat ACL doc not found!');
|
|
1019
|
+
}
|
|
1020
|
+
const aclBody = `
|
|
1021
|
+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
|
|
1022
|
+
<#owner>
|
|
1023
|
+
a acl:Authorization;
|
|
1024
|
+
acl:agent <${me.value}>;
|
|
1025
|
+
acl:accessTo <.>;
|
|
1026
|
+
acl:default <.>;
|
|
1027
|
+
acl:mode
|
|
1028
|
+
acl:Read, acl:Write, acl:Control.
|
|
1029
|
+
<#invitee>
|
|
1030
|
+
a acl:Authorization;
|
|
1031
|
+
acl:agent <${invitee.value}>;
|
|
1032
|
+
acl:accessTo <.>;
|
|
1033
|
+
acl:default <.>;
|
|
1034
|
+
acl:mode
|
|
1035
|
+
acl:Read, acl:Append.
|
|
1036
|
+
`;
|
|
1037
|
+
await store.fetcher.webOperation('PUT', chatAclDoc.value, {
|
|
1038
|
+
data: aclBody,
|
|
1039
|
+
contentType: 'text/turtle',
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
async function addToPrivateTypeIndex(chatThing, me) {
|
|
1043
|
+
// Add to private type index
|
|
1044
|
+
const privateTypeIndex = store.any(me, ns.solid('privateTypeIndex'));
|
|
1045
|
+
if (!privateTypeIndex) {
|
|
1046
|
+
throw new Error('Private type index not found!');
|
|
1047
|
+
}
|
|
1048
|
+
await store.fetcher.load(privateTypeIndex);
|
|
1049
|
+
const reg = newThing(privateTypeIndex);
|
|
1050
|
+
const ins = [
|
|
1051
|
+
(0,external_rdflib_namespaceObject.st)(reg, ns.rdf('type'), ns.solid('TypeRegistration'), privateTypeIndex.doc()),
|
|
1052
|
+
(0,external_rdflib_namespaceObject.st)(reg, ns.solid('forClass'), ns.meeting('LongChat'), privateTypeIndex.doc()),
|
|
1053
|
+
(0,external_rdflib_namespaceObject.st)(reg, ns.solid('instance'), chatThing, privateTypeIndex.doc()),
|
|
1054
|
+
];
|
|
1055
|
+
await new Promise((resolve, reject) => {
|
|
1056
|
+
store.updater.update([], ins, function (_uri, ok, errm) {
|
|
1057
|
+
if (!ok) {
|
|
1058
|
+
reject(new Error(errm));
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
resolve(null);
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
async function findChat(invitee) {
|
|
1067
|
+
const me = await profileLogic.loadMe();
|
|
1068
|
+
const podRoot = await profileLogic.getPodRoot(me);
|
|
1069
|
+
const chatContainer = determineChatContainer(invitee, podRoot);
|
|
1070
|
+
let exists = true;
|
|
1071
|
+
try {
|
|
1072
|
+
await store.fetcher.load(new external_rdflib_namespaceObject.NamedNode(chatContainer.value + 'index.ttl#this'));
|
|
1073
|
+
}
|
|
1074
|
+
catch (e) {
|
|
1075
|
+
exists = false;
|
|
1076
|
+
}
|
|
1077
|
+
return { me, chatContainer, exists };
|
|
1078
|
+
}
|
|
1079
|
+
async function createChatThing(chatContainer, me) {
|
|
1080
|
+
const created = await mintNew({
|
|
1081
|
+
me,
|
|
1082
|
+
newBase: chatContainer.value,
|
|
1083
|
+
});
|
|
1084
|
+
return created.newInstance;
|
|
1085
|
+
}
|
|
1086
|
+
function mintNew(newPaneOptions) {
|
|
1087
|
+
const kb = store;
|
|
1088
|
+
const updater = kb.updater;
|
|
1089
|
+
if (newPaneOptions.me && !newPaneOptions.me.uri) {
|
|
1090
|
+
throw new Error('chat mintNew: Invalid userid ' + newPaneOptions.me);
|
|
1091
|
+
}
|
|
1092
|
+
const newInstance = (newPaneOptions.newInstance =
|
|
1093
|
+
newPaneOptions.newInstance ||
|
|
1094
|
+
kb.sym(newPaneOptions.newBase + CHAT_LOCATION_IN_CONTAINER));
|
|
1095
|
+
const newChatDoc = newInstance.doc();
|
|
1096
|
+
kb.add(newInstance, ns.rdf('type'), ns.meeting('LongChat'), newChatDoc);
|
|
1097
|
+
kb.add(newInstance, ns.dc('title'), 'Chat channel', newChatDoc);
|
|
1098
|
+
kb.add(newInstance, ns.dc('created'), (0,external_rdflib_namespaceObject.term)(new Date(Date.now())), newChatDoc);
|
|
1099
|
+
if (newPaneOptions.me) {
|
|
1100
|
+
kb.add(newInstance, ns.dc('author'), newPaneOptions.me, newChatDoc);
|
|
1101
|
+
}
|
|
1102
|
+
return new Promise(function (resolve, reject) {
|
|
1103
|
+
updater === null || updater === void 0 ? void 0 : updater.put(newChatDoc, kb.statementsMatching(undefined, undefined, undefined, newChatDoc), 'text/turtle', function (uri2, ok, message) {
|
|
1104
|
+
if (ok) {
|
|
1105
|
+
resolve({
|
|
1106
|
+
...newPaneOptions,
|
|
1107
|
+
newInstance,
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
reject(new Error('FAILED to save new chat channel at: ' + uri2 + ' : ' + message));
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Find (and optionally create) an individual chat between the current user and the given invitee
|
|
1118
|
+
* @param invitee - The person to chat with
|
|
1119
|
+
* @param createIfMissing - Whether the chat should be created, if missing
|
|
1120
|
+
* @returns null if missing, or a node referring to an already existing chat, or the newly created chat
|
|
1121
|
+
*/
|
|
1122
|
+
async function getChat(invitee, createIfMissing = true) {
|
|
1123
|
+
const { me, chatContainer, exists } = await findChat(invitee);
|
|
1124
|
+
if (exists) {
|
|
1125
|
+
return new external_rdflib_namespaceObject.NamedNode(chatContainer.value + CHAT_LOCATION_IN_CONTAINER);
|
|
1126
|
+
}
|
|
1127
|
+
if (createIfMissing) {
|
|
1128
|
+
const chatThing = await createChatThing(chatContainer, me);
|
|
1129
|
+
await sendInvite(invitee, chatThing);
|
|
1130
|
+
await setAcl(chatContainer, me, invitee);
|
|
1131
|
+
await addToPrivateTypeIndex(chatThing, me);
|
|
1132
|
+
return chatThing;
|
|
1133
|
+
}
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
async function sendInvite(invitee, chatThing) {
|
|
1137
|
+
var _a;
|
|
1138
|
+
await store.fetcher.load(invitee.doc());
|
|
1139
|
+
const inviteeInbox = store.any(invitee, ns.ldp('inbox'), undefined, invitee.doc());
|
|
1140
|
+
if (!inviteeInbox) {
|
|
1141
|
+
throw new Error(`Invitee inbox not found! ${invitee.value}`);
|
|
1142
|
+
}
|
|
1143
|
+
const inviteBody = `
|
|
1144
|
+
<> a <http://www.w3.org/ns/pim/meeting#LongChatInvite> ;
|
|
1145
|
+
${ns.rdf('seeAlso')} <${chatThing.value}> .
|
|
1146
|
+
`;
|
|
1147
|
+
const inviteResponse = await ((_a = store.fetcher) === null || _a === void 0 ? void 0 : _a.webOperation('POST', inviteeInbox.value, {
|
|
1148
|
+
data: inviteBody,
|
|
1149
|
+
contentType: 'text/turtle',
|
|
1150
|
+
}));
|
|
1151
|
+
const locationStr = inviteResponse === null || inviteResponse === void 0 ? void 0 : inviteResponse.headers.get('location');
|
|
1152
|
+
if (!locationStr) {
|
|
1153
|
+
throw new Error(`Invite sending returned a ${inviteResponse === null || inviteResponse === void 0 ? void 0 : inviteResponse.status}`);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
return {
|
|
1157
|
+
setAcl, addToPrivateTypeIndex, findChat, createChatThing, getChat, sendInvite, mintNew
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
;// ./src/inbox/inboxLogic.ts
|
|
1162
|
+
|
|
1163
|
+
function createInboxLogic(store, profileLogic, utilityLogic, containerLogic, aclLogic) {
|
|
1164
|
+
async function createInboxFor(peerWebId, nick) {
|
|
1165
|
+
const myWebId = await profileLogic.loadMe();
|
|
1166
|
+
const podRoot = await profileLogic.getPodRoot(myWebId);
|
|
1167
|
+
const ourInbox = `${podRoot.value}p2p-inboxes/${encodeURIComponent(nick)}/`;
|
|
1168
|
+
await containerLogic.createContainer(ourInbox);
|
|
1169
|
+
// const aclDocUrl = await aclLogic.findAclDocUrl(ourInbox);
|
|
1170
|
+
await utilityLogic.setSinglePeerAccess({
|
|
1171
|
+
ownerWebId: myWebId.value,
|
|
1172
|
+
peerWebId,
|
|
1173
|
+
accessToModes: 'acl:Append',
|
|
1174
|
+
target: ourInbox
|
|
1175
|
+
});
|
|
1176
|
+
return ourInbox;
|
|
1177
|
+
}
|
|
1178
|
+
async function getNewMessages(user) {
|
|
1179
|
+
if (!user) {
|
|
1180
|
+
user = await profileLogic.loadMe();
|
|
1181
|
+
}
|
|
1182
|
+
const inbox = await profileLogic.getMainInbox(user);
|
|
1183
|
+
const urls = await containerLogic.getContainerMembers(inbox);
|
|
1184
|
+
return urls.filter(url => !containerLogic.isContainer(url));
|
|
1185
|
+
}
|
|
1186
|
+
async function markAsRead(url, date) {
|
|
1187
|
+
const downloaded = await store.fetcher._fetch(url);
|
|
1188
|
+
if (downloaded.status !== 200) {
|
|
1189
|
+
throw new Error(`Not OK! ${url}`);
|
|
1190
|
+
}
|
|
1191
|
+
const archiveUrl = getArchiveUrl(url, date);
|
|
1192
|
+
const options = {
|
|
1193
|
+
method: 'PUT',
|
|
1194
|
+
body: await downloaded.text(),
|
|
1195
|
+
headers: [
|
|
1196
|
+
['Content-Type', downloaded.headers.get('Content-Type') || 'application/octet-stream']
|
|
1197
|
+
]
|
|
1198
|
+
};
|
|
1199
|
+
const uploaded = await store.fetcher._fetch(archiveUrl, options);
|
|
1200
|
+
if (uploaded.status.toString()[0] === '2') {
|
|
1201
|
+
await store.fetcher._fetch(url, {
|
|
1202
|
+
method: 'DELETE'
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return {
|
|
1207
|
+
createInboxFor,
|
|
1208
|
+
getNewMessages,
|
|
1209
|
+
markAsRead
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
;// ./src/logic/CustomError.ts
|
|
1214
|
+
class CustomError extends Error {
|
|
1215
|
+
constructor(message) {
|
|
1216
|
+
super(message);
|
|
1217
|
+
// see: typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html
|
|
1218
|
+
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
|
|
1219
|
+
this.name = new.target.name; // stack traces display correctly now
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
class UnauthorizedError extends CustomError {
|
|
1223
|
+
}
|
|
1224
|
+
class CrossOriginForbiddenError extends CustomError {
|
|
1225
|
+
}
|
|
1226
|
+
class SameOriginForbiddenError extends CustomError {
|
|
1227
|
+
}
|
|
1228
|
+
class NotFoundError extends CustomError {
|
|
1229
|
+
}
|
|
1230
|
+
class NotEditableError extends CustomError {
|
|
1231
|
+
}
|
|
1232
|
+
class WebOperationError extends CustomError {
|
|
1233
|
+
}
|
|
1234
|
+
class FetchError extends CustomError {
|
|
1235
|
+
constructor(status, message) {
|
|
1236
|
+
super(message);
|
|
1237
|
+
this.status = status;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
;// ./src/typeIndex/typeIndexDocuments.ts
|
|
1242
|
+
function publicTypeIndexDocument() {
|
|
1243
|
+
return [
|
|
1244
|
+
'@prefix solid: <http://www.w3.org/ns/solid/terms#>.',
|
|
1245
|
+
'<>',
|
|
1246
|
+
' a solid:TypeIndex ;',
|
|
1247
|
+
' a solid:ListedDocument.'
|
|
1248
|
+
].join('\n');
|
|
1249
|
+
}
|
|
1250
|
+
function privateTypeIndexDocument() {
|
|
1251
|
+
return [
|
|
1252
|
+
'@prefix solid: <http://www.w3.org/ns/solid/terms#>.',
|
|
1253
|
+
'<>',
|
|
1254
|
+
' a solid:TypeIndex ;',
|
|
1255
|
+
' a solid:UnlistedDocument.'
|
|
1256
|
+
].join('\n');
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
;// ./src/typeIndex/typeIndexAclDocuments.ts
|
|
1260
|
+
function publicTypeIndexAclDocument(webId, publicTypeIndexUri) {
|
|
1261
|
+
const fileName = new URL(publicTypeIndexUri).pathname.split('/').pop() || 'publicTypeIndex.ttl';
|
|
1262
|
+
return [
|
|
1263
|
+
'# ACL resource for the Public Type Index',
|
|
1264
|
+
'',
|
|
1265
|
+
'@prefix acl: <http://www.w3.org/ns/auth/acl#>.',
|
|
1266
|
+
'@prefix foaf: <http://xmlns.com/foaf/0.1/>.',
|
|
1267
|
+
'',
|
|
1268
|
+
'<#owner>',
|
|
1269
|
+
' a acl:Authorization;',
|
|
1270
|
+
'',
|
|
1271
|
+
' acl:agent',
|
|
1272
|
+
` <${webId}>;`,
|
|
1273
|
+
'',
|
|
1274
|
+
` acl:accessTo <./${fileName}>;`,
|
|
1275
|
+
'',
|
|
1276
|
+
' acl:mode',
|
|
1277
|
+
' acl:Read, acl:Write, acl:Control.',
|
|
1278
|
+
'',
|
|
1279
|
+
'# Public-readable',
|
|
1280
|
+
'<#public>',
|
|
1281
|
+
' a acl:Authorization;',
|
|
1282
|
+
'',
|
|
1283
|
+
' acl:agentClass foaf:Agent;',
|
|
1284
|
+
'',
|
|
1285
|
+
` acl:accessTo <./${fileName}>;`,
|
|
1286
|
+
'',
|
|
1287
|
+
' acl:mode acl:Read.'
|
|
1288
|
+
].join('\n');
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
;// ./src/profile/profileDocuments.ts
|
|
1292
|
+
function preferencesFileDocument() {
|
|
1293
|
+
return [
|
|
1294
|
+
'@prefix dct: <http://purl.org/dc/terms/>.',
|
|
1295
|
+
'@prefix pim: <http://www.w3.org/ns/pim/space#>.',
|
|
1296
|
+
'@prefix solid: <http://www.w3.org/ns/solid/terms#>.',
|
|
1297
|
+
'<>',
|
|
1298
|
+
' a pim:ConfigurationFile ;',
|
|
1299
|
+
' dct:title "Preferences file".'
|
|
1300
|
+
].join('\n');
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
;// ./src/profile/profileAclDocuments.ts
|
|
1304
|
+
function ownerOnlyContainerAclDocument(webId) {
|
|
1305
|
+
return [
|
|
1306
|
+
'@prefix acl: <http://www.w3.org/ns/auth/acl#>.',
|
|
1307
|
+
'',
|
|
1308
|
+
'<#owner>',
|
|
1309
|
+
'a acl:Authorization;',
|
|
1310
|
+
`acl:agent <${webId}>;`,
|
|
1311
|
+
'acl:accessTo <./>;',
|
|
1312
|
+
'acl:default <./>;',
|
|
1313
|
+
'acl:mode acl:Read, acl:Write, acl:Control.'
|
|
1314
|
+
].join('\n');
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
;// ./src/util/containerLogic.ts
|
|
1318
|
+
|
|
1319
|
+
/**
|
|
1320
|
+
* Container-related class
|
|
1321
|
+
*/
|
|
1322
|
+
function createContainerLogic(store) {
|
|
1323
|
+
function getContainerElements(containerNode) {
|
|
1324
|
+
return store
|
|
1325
|
+
.statementsMatching(containerNode, (0,external_rdflib_namespaceObject.sym)('http://www.w3.org/ns/ldp#contains'), undefined)
|
|
1326
|
+
.map((st) => st.object);
|
|
1327
|
+
}
|
|
1328
|
+
function isContainer(url) {
|
|
1329
|
+
const nodeToString = url.value;
|
|
1330
|
+
return nodeToString.charAt(nodeToString.length - 1) === '/';
|
|
1331
|
+
}
|
|
1332
|
+
async function createContainer(url) {
|
|
1333
|
+
const stringToNode = (0,external_rdflib_namespaceObject.sym)(url);
|
|
1334
|
+
if (!isContainer(stringToNode)) {
|
|
1335
|
+
throw new Error(`Not a container URL ${url}`);
|
|
1336
|
+
}
|
|
1337
|
+
// Copied from https://github.com/solidos/solid-crud-tests/blob/v3.1.0/test/surface/create-container.test.ts#L56-L64
|
|
1338
|
+
const result = await store.fetcher._fetch(url, {
|
|
1339
|
+
method: 'PUT',
|
|
1340
|
+
headers: {
|
|
1341
|
+
'Content-Type': 'text/turtle',
|
|
1342
|
+
'If-None-Match': '*',
|
|
1343
|
+
Link: '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"', // See https://github.com/solidos/node-solid-server/issues/1465
|
|
1344
|
+
},
|
|
1345
|
+
body: ' ', // work around https://github.com/michielbdejong/community-server/issues/4#issuecomment-776222863
|
|
1346
|
+
});
|
|
1347
|
+
// Treat 409 as idempotent success: another process/request already created the container.
|
|
1348
|
+
if (result.status === 409) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
if (result.status.toString()[0] !== '2') {
|
|
1352
|
+
throw new Error(`Not OK: got ${result.status} response while creating container at ${url}`);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
async function getContainerMembers(containerUrl) {
|
|
1356
|
+
await store.fetcher.load(containerUrl);
|
|
1357
|
+
return getContainerElements(containerUrl);
|
|
1358
|
+
}
|
|
1359
|
+
return {
|
|
1360
|
+
isContainer,
|
|
1361
|
+
createContainer,
|
|
1362
|
+
getContainerElements,
|
|
1363
|
+
getContainerMembers
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
;// ./src/profile/profileLogic.ts
|
|
1368
|
+
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
|
|
1373
|
+
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
function createProfileLogic(store, authn, utilityLogic) {
|
|
1380
|
+
const ns = ns_ns;
|
|
1381
|
+
const containerLogic = createContainerLogic(store);
|
|
1382
|
+
const loadPreferencesInFlight = new Map();
|
|
1383
|
+
const cachedPreferencesFileByWebId = new Map();
|
|
1384
|
+
function isAbsoluteHttpUri(uri) {
|
|
1385
|
+
return !!uri && (uri.startsWith('https://') || uri.startsWith('http://'));
|
|
1386
|
+
}
|
|
1387
|
+
function docDirUri(node) {
|
|
1388
|
+
const doc = node.doc();
|
|
1389
|
+
const dir = doc.dir();
|
|
1390
|
+
if ((dir === null || dir === void 0 ? void 0 : dir.uri) && isAbsoluteHttpUri(dir.uri))
|
|
1391
|
+
return dir.uri;
|
|
1392
|
+
const docUri = doc.uri;
|
|
1393
|
+
if (!docUri || !isAbsoluteHttpUri(docUri))
|
|
1394
|
+
return null;
|
|
1395
|
+
const withoutFragment = docUri.split('#')[0];
|
|
1396
|
+
const lastSlash = withoutFragment.lastIndexOf('/');
|
|
1397
|
+
if (lastSlash === -1)
|
|
1398
|
+
return null;
|
|
1399
|
+
return withoutFragment.slice(0, lastSlash + 1);
|
|
1400
|
+
}
|
|
1401
|
+
function suggestTypeIndexInPreferences(preferencesFile, filename) {
|
|
1402
|
+
const dirUri = docDirUri(preferencesFile);
|
|
1403
|
+
if (!dirUri)
|
|
1404
|
+
throw new Error(`Cannot derive directory for preferences file ${preferencesFile.uri}`);
|
|
1405
|
+
return (0,external_rdflib_namespaceObject.sym)(dirUri + filename);
|
|
1406
|
+
}
|
|
1407
|
+
function isNotFoundError(err) {
|
|
1408
|
+
var _a;
|
|
1409
|
+
if (((_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.status) === 404)
|
|
1410
|
+
return true;
|
|
1411
|
+
const text = `${(err === null || err === void 0 ? void 0 : err.message) || err || ''}`;
|
|
1412
|
+
return text.includes('404') || text.includes('Not Found');
|
|
1413
|
+
}
|
|
1414
|
+
async function ensureContainerExists(containerUri) {
|
|
1415
|
+
const containerNode = (0,external_rdflib_namespaceObject.sym)(containerUri);
|
|
1416
|
+
try {
|
|
1417
|
+
await store.fetcher.load(containerNode);
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
catch (err) {
|
|
1421
|
+
if (!isNotFoundError(err))
|
|
1422
|
+
throw err;
|
|
1423
|
+
}
|
|
1424
|
+
await containerLogic.createContainer(containerUri);
|
|
1425
|
+
}
|
|
1426
|
+
async function ensureOwnerOnlyAclForSettings(user, preferencesFile) {
|
|
1427
|
+
var _a;
|
|
1428
|
+
const dirUri = docDirUri(preferencesFile);
|
|
1429
|
+
if (!dirUri)
|
|
1430
|
+
throw new Error(`Cannot derive settings directory from ${preferencesFile.uri}`);
|
|
1431
|
+
await ensureContainerExists(dirUri);
|
|
1432
|
+
const containerNode = (0,external_rdflib_namespaceObject.sym)(dirUri);
|
|
1433
|
+
let aclDocUri;
|
|
1434
|
+
try {
|
|
1435
|
+
await store.fetcher.load(containerNode);
|
|
1436
|
+
aclDocUri = (_a = store.any(containerNode, ACL_LINK)) === null || _a === void 0 ? void 0 : _a.value;
|
|
1437
|
+
}
|
|
1438
|
+
catch (err) {
|
|
1439
|
+
if (!isNotFoundError(err))
|
|
1440
|
+
throw err;
|
|
1441
|
+
}
|
|
1442
|
+
if (!aclDocUri) {
|
|
1443
|
+
// Fallback for servers/tests where rel=acl is not exposed in mocked headers.
|
|
1444
|
+
aclDocUri = `${dirUri}.acl`;
|
|
1445
|
+
}
|
|
1446
|
+
const aclDoc = (0,external_rdflib_namespaceObject.sym)(aclDocUri);
|
|
1447
|
+
try {
|
|
1448
|
+
await store.fetcher.load(aclDoc);
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
catch (err) {
|
|
1452
|
+
if (!isNotFoundError(err))
|
|
1453
|
+
throw err;
|
|
1454
|
+
}
|
|
1455
|
+
await store.fetcher.webOperation('PUT', aclDoc.uri, {
|
|
1456
|
+
data: ownerOnlyContainerAclDocument(user.uri),
|
|
1457
|
+
contentType: 'text/turtle'
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
async function ensurePublicTypeIndexAclOnCreate(user, publicTypeIndex, ensureAcl = false) {
|
|
1461
|
+
var _a, _b, _c;
|
|
1462
|
+
const created = await utilityLogic.loadOrCreateWithContentOnCreate(publicTypeIndex, publicTypeIndexDocument());
|
|
1463
|
+
if (!created && !ensureAcl)
|
|
1464
|
+
return;
|
|
1465
|
+
let aclDocUri;
|
|
1466
|
+
try {
|
|
1467
|
+
await store.fetcher.load(publicTypeIndex);
|
|
1468
|
+
aclDocUri = (_a = store.any(publicTypeIndex, ACL_LINK)) === null || _a === void 0 ? void 0 : _a.value;
|
|
1469
|
+
}
|
|
1470
|
+
catch (err) {
|
|
1471
|
+
if (!isNotFoundError(err))
|
|
1472
|
+
throw err;
|
|
1473
|
+
}
|
|
1474
|
+
if (!aclDocUri) {
|
|
1475
|
+
aclDocUri = `${publicTypeIndex.uri}.acl`;
|
|
1476
|
+
}
|
|
1477
|
+
const aclDoc = (0,external_rdflib_namespaceObject.sym)(aclDocUri);
|
|
1478
|
+
try {
|
|
1479
|
+
await store.fetcher.webOperation('PUT', aclDoc.uri, {
|
|
1480
|
+
data: publicTypeIndexAclDocument(user.uri, publicTypeIndex.uri),
|
|
1481
|
+
contentType: 'text/turtle',
|
|
1482
|
+
headers: { 'If-None-Match': '*' }
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
catch (err) {
|
|
1486
|
+
const status = (_c = (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.status) !== null && _c !== void 0 ? _c : err === null || err === void 0 ? void 0 : err.status;
|
|
1487
|
+
if (status !== 412)
|
|
1488
|
+
throw err;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
async function ensurePrivateTypeIndexOnCreate(privateTypeIndex) {
|
|
1492
|
+
await utilityLogic.loadOrCreateWithContentOnCreate(privateTypeIndex, privateTypeIndexDocument());
|
|
1493
|
+
}
|
|
1494
|
+
async function initializePreferencesDefaults(user, preferencesFile) {
|
|
1495
|
+
const preferencesDoc = preferencesFile.doc();
|
|
1496
|
+
const profileDoc = user.doc();
|
|
1497
|
+
await store.fetcher.load(preferencesDoc);
|
|
1498
|
+
const profilePublicTypeIndex = store.any(user, ns.solid('publicTypeIndex'), null, profileDoc);
|
|
1499
|
+
const preferencesPublicTypeIndex = store.any(user, ns.solid('publicTypeIndex'), null, preferencesDoc);
|
|
1500
|
+
const publicTypeIndex = profilePublicTypeIndex ||
|
|
1501
|
+
preferencesPublicTypeIndex ||
|
|
1502
|
+
suggestTypeIndexInPreferences(preferencesFile, 'publicTypeIndex.ttl');
|
|
1503
|
+
const privateTypeIndex = store.any(user, ns.solid('privateTypeIndex'), null, preferencesDoc) ||
|
|
1504
|
+
suggestTypeIndexInPreferences(preferencesFile, 'privateTypeIndex.ttl');
|
|
1505
|
+
// Keep discovery consistent with typeIndexLogic, which resolves publicTypeIndex from the profile doc.
|
|
1506
|
+
const createdProfilePublicTypeIndexLink = !profilePublicTypeIndex;
|
|
1507
|
+
if (createdProfilePublicTypeIndexLink) {
|
|
1508
|
+
await utilityLogic.followOrCreateLinkWithContentOnCreate(user, ns.solid('publicTypeIndex'), publicTypeIndex, profileDoc, publicTypeIndexDocument());
|
|
1509
|
+
}
|
|
1510
|
+
const toInsert = [];
|
|
1511
|
+
if (!store.holds(preferencesDoc, ns.rdf('type'), ns.space('ConfigurationFile'), preferencesDoc)) {
|
|
1512
|
+
toInsert.push((0,external_rdflib_namespaceObject.st)(preferencesDoc, ns.rdf('type'), ns.space('ConfigurationFile'), preferencesDoc));
|
|
1513
|
+
}
|
|
1514
|
+
if (!store.holds(preferencesDoc, ns.dct('title'), undefined, preferencesDoc)) {
|
|
1515
|
+
toInsert.push((0,external_rdflib_namespaceObject.st)(preferencesDoc, ns.dct('title'), (0,external_rdflib_namespaceObject.literal)('Preferences file'), preferencesDoc));
|
|
1516
|
+
}
|
|
1517
|
+
if (!store.holds(user, ns.solid('publicTypeIndex'), publicTypeIndex, preferencesDoc)) {
|
|
1518
|
+
toInsert.push((0,external_rdflib_namespaceObject.st)(user, ns.solid('publicTypeIndex'), publicTypeIndex, preferencesDoc));
|
|
1519
|
+
}
|
|
1520
|
+
if (!store.holds(user, ns.solid('privateTypeIndex'), privateTypeIndex, preferencesDoc)) {
|
|
1521
|
+
toInsert.push((0,external_rdflib_namespaceObject.st)(user, ns.solid('privateTypeIndex'), privateTypeIndex, preferencesDoc));
|
|
1522
|
+
}
|
|
1523
|
+
if (toInsert.length > 0) {
|
|
1524
|
+
await store.updater.update([], toInsert);
|
|
1525
|
+
await store.fetcher.load(preferencesDoc);
|
|
1526
|
+
}
|
|
1527
|
+
await ensurePublicTypeIndexAclOnCreate(user, publicTypeIndex, createdProfilePublicTypeIndexLink);
|
|
1528
|
+
await ensurePrivateTypeIndexOnCreate(privateTypeIndex);
|
|
1529
|
+
}
|
|
1530
|
+
async function ensurePreferencesDocExists(preferencesFile) {
|
|
1531
|
+
var _a, _b;
|
|
1532
|
+
try {
|
|
1533
|
+
const created = await utilityLogic.loadOrCreateWithContentOnCreate(preferencesFile, preferencesFileDocument());
|
|
1534
|
+
if (created) {
|
|
1535
|
+
return true;
|
|
1536
|
+
}
|
|
1537
|
+
return false;
|
|
1538
|
+
}
|
|
1539
|
+
catch (err) {
|
|
1540
|
+
if (((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 401) {
|
|
1541
|
+
throw new UnauthorizedError();
|
|
1542
|
+
}
|
|
1543
|
+
if (((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 403) {
|
|
1544
|
+
if (differentOrigin(preferencesFile)) {
|
|
1545
|
+
throw new CrossOriginForbiddenError();
|
|
1546
|
+
}
|
|
1547
|
+
throw new SameOriginForbiddenError();
|
|
1548
|
+
}
|
|
1549
|
+
throw err;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
/**
|
|
1553
|
+
* loads the preference without throwing errors - if it can create it it does so.
|
|
1554
|
+
* remark: it still throws error if it cannot load profile.
|
|
1555
|
+
* @param user
|
|
1556
|
+
* @returns undefined if preferenceFile cannot be returned or NamedNode if it can find it or create it
|
|
1557
|
+
*/
|
|
1558
|
+
async function silencedLoadPreferences(user) {
|
|
1559
|
+
try {
|
|
1560
|
+
return await loadPreferences(user);
|
|
1561
|
+
}
|
|
1562
|
+
catch (err) {
|
|
1563
|
+
return undefined;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* loads the preference without returning different errors if it cannot create or load it.
|
|
1568
|
+
* remark: it also throws error if it cannot load profile.
|
|
1569
|
+
* @param user
|
|
1570
|
+
* @returns undefined if preferenceFile cannot be an Error or NamedNode if it can find it or create it
|
|
1571
|
+
*/
|
|
1572
|
+
async function loadPreferences(user) {
|
|
1573
|
+
const cachedPreferencesFile = cachedPreferencesFileByWebId.get(user.uri);
|
|
1574
|
+
if (cachedPreferencesFile) {
|
|
1575
|
+
return cachedPreferencesFile;
|
|
1576
|
+
}
|
|
1577
|
+
const inFlight = loadPreferencesInFlight.get(user.uri);
|
|
1578
|
+
if (inFlight) {
|
|
1579
|
+
return inFlight;
|
|
1580
|
+
}
|
|
1581
|
+
const run = (async () => {
|
|
1582
|
+
var _a;
|
|
1583
|
+
await loadProfile(user);
|
|
1584
|
+
const possiblePreferencesFile = suggestPreferencesFile(user);
|
|
1585
|
+
let preferencesFile;
|
|
1586
|
+
try {
|
|
1587
|
+
const existingPreferencesFile = store.any(user, ns.space('preferencesFile'), null, user.doc());
|
|
1588
|
+
if (existingPreferencesFile) {
|
|
1589
|
+
preferencesFile = existingPreferencesFile;
|
|
1590
|
+
}
|
|
1591
|
+
else {
|
|
1592
|
+
preferencesFile = await utilityLogic.followOrCreateLink(user, ns.space('preferencesFile'), possiblePreferencesFile, user.doc());
|
|
1593
|
+
}
|
|
1594
|
+
await ensureOwnerOnlyAclForSettings(user, preferencesFile);
|
|
1595
|
+
await ensurePreferencesDocExists(preferencesFile);
|
|
1596
|
+
await initializePreferencesDefaults(user, preferencesFile);
|
|
1597
|
+
}
|
|
1598
|
+
catch (err) {
|
|
1599
|
+
const message = `User ${user} has no pointer in profile to preferences file.`;
|
|
1600
|
+
warn(message);
|
|
1601
|
+
// we are listing the possible errors
|
|
1602
|
+
if (err instanceof NotEditableError) {
|
|
1603
|
+
throw err;
|
|
1604
|
+
}
|
|
1605
|
+
if (err instanceof WebOperationError) {
|
|
1606
|
+
throw err;
|
|
1607
|
+
}
|
|
1608
|
+
if (err instanceof UnauthorizedError) {
|
|
1609
|
+
throw err;
|
|
1610
|
+
}
|
|
1611
|
+
if (err instanceof CrossOriginForbiddenError) {
|
|
1612
|
+
throw err;
|
|
1613
|
+
}
|
|
1614
|
+
if (err instanceof SameOriginForbiddenError) {
|
|
1615
|
+
throw err;
|
|
1616
|
+
}
|
|
1617
|
+
if (err instanceof FetchError) {
|
|
1618
|
+
throw err;
|
|
1619
|
+
}
|
|
1620
|
+
throw err;
|
|
1621
|
+
}
|
|
1622
|
+
try {
|
|
1623
|
+
await store.fetcher.load(preferencesFile);
|
|
1624
|
+
}
|
|
1625
|
+
catch (err) { // Maybe a permission problem or origin problem
|
|
1626
|
+
const msg = `Unable to load preference of user ${user}: ${err}`;
|
|
1627
|
+
if (((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
|
|
1628
|
+
// Self-heal when a stale profile pointer references a missing preferences file.
|
|
1629
|
+
await ensureOwnerOnlyAclForSettings(user, preferencesFile);
|
|
1630
|
+
await ensurePreferencesDocExists(preferencesFile);
|
|
1631
|
+
await initializePreferencesDefaults(user, preferencesFile);
|
|
1632
|
+
await store.fetcher.load(preferencesFile);
|
|
1633
|
+
return preferencesFile;
|
|
1634
|
+
}
|
|
1635
|
+
warn(msg);
|
|
1636
|
+
if (err.response.status === 401) {
|
|
1637
|
+
throw new UnauthorizedError();
|
|
1638
|
+
}
|
|
1639
|
+
if (err.response.status === 403) {
|
|
1640
|
+
if (differentOrigin(preferencesFile)) {
|
|
1641
|
+
throw new CrossOriginForbiddenError();
|
|
1642
|
+
}
|
|
1643
|
+
throw new SameOriginForbiddenError();
|
|
1644
|
+
}
|
|
1645
|
+
/*if (err.response.status === 404) {
|
|
1646
|
+
throw new NotFoundError();
|
|
1647
|
+
}*/
|
|
1648
|
+
throw new Error(msg);
|
|
1649
|
+
}
|
|
1650
|
+
cachedPreferencesFileByWebId.set(user.uri, preferencesFile);
|
|
1651
|
+
return preferencesFile;
|
|
1652
|
+
})();
|
|
1653
|
+
loadPreferencesInFlight.set(user.uri, run);
|
|
1654
|
+
try {
|
|
1655
|
+
return await run;
|
|
1656
|
+
}
|
|
1657
|
+
finally {
|
|
1658
|
+
if (loadPreferencesInFlight.get(user.uri) === run) {
|
|
1659
|
+
loadPreferencesInFlight.delete(user.uri);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
async function loadProfile(user) {
|
|
1664
|
+
if (!user) {
|
|
1665
|
+
throw new Error('loadProfile: no user given.');
|
|
1666
|
+
}
|
|
1667
|
+
try {
|
|
1668
|
+
await store.fetcher.load(user.doc());
|
|
1669
|
+
}
|
|
1670
|
+
catch (err) {
|
|
1671
|
+
throw new Error(`Unable to load profile of user ${user}: ${err}`);
|
|
1672
|
+
}
|
|
1673
|
+
return user.doc();
|
|
1674
|
+
}
|
|
1675
|
+
async function loadMe() {
|
|
1676
|
+
const me = authn.currentUser();
|
|
1677
|
+
if (me === null) {
|
|
1678
|
+
throw new Error('Current user not found! Not logged in?');
|
|
1679
|
+
}
|
|
1680
|
+
await store.fetcher.load(me.doc());
|
|
1681
|
+
return me;
|
|
1682
|
+
}
|
|
1683
|
+
function getPodRoot(user) {
|
|
1684
|
+
const podRoot = findStorage(user);
|
|
1685
|
+
if (!podRoot) {
|
|
1686
|
+
throw new Error('User pod root not found!');
|
|
1687
|
+
}
|
|
1688
|
+
return podRoot;
|
|
1689
|
+
}
|
|
1690
|
+
async function getMainInbox(user) {
|
|
1691
|
+
await store.fetcher.load(user);
|
|
1692
|
+
const mainInbox = store.any(user, ns.ldp('inbox'), undefined, user.doc());
|
|
1693
|
+
if (!mainInbox) {
|
|
1694
|
+
throw new Error('User main inbox not found!');
|
|
1695
|
+
}
|
|
1696
|
+
return mainInbox;
|
|
1697
|
+
}
|
|
1698
|
+
function findStorage(me) {
|
|
1699
|
+
return store.any(me, ns.space('storage'), undefined, me.doc());
|
|
1700
|
+
}
|
|
1701
|
+
return {
|
|
1702
|
+
loadMe,
|
|
1703
|
+
getPodRoot,
|
|
1704
|
+
getMainInbox,
|
|
1705
|
+
findStorage,
|
|
1706
|
+
loadPreferences,
|
|
1707
|
+
loadProfile,
|
|
1708
|
+
silencedLoadPreferences
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
;// ./src/typeIndex/typeIndexLogic.ts
|
|
1713
|
+
|
|
1714
|
+
|
|
1715
|
+
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
function createTypeIndexLogic(store, authn, profileLogic, utilityLogic) {
|
|
1719
|
+
const ns = ns_ns;
|
|
1720
|
+
function isAbsoluteHttpUri(uri) {
|
|
1721
|
+
return !!uri && (uri.startsWith('https://') || uri.startsWith('http://'));
|
|
1722
|
+
}
|
|
1723
|
+
function getRegistrations(instance, theClass) {
|
|
1724
|
+
return store
|
|
1725
|
+
.each(undefined, ns.solid('instance'), instance)
|
|
1726
|
+
.filter((r) => {
|
|
1727
|
+
return store.holds(r, ns.solid('forClass'), theClass);
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
async function loadTypeIndexesFor(user) {
|
|
1731
|
+
if (!user)
|
|
1732
|
+
throw new Error('loadTypeIndexesFor: No user given');
|
|
1733
|
+
const profile = await profileLogic.loadProfile(user);
|
|
1734
|
+
let suggestion = null;
|
|
1735
|
+
try {
|
|
1736
|
+
suggestion = suggestPublicTypeIndex(user);
|
|
1737
|
+
}
|
|
1738
|
+
catch (err) {
|
|
1739
|
+
const message = `User ${user} has no usable profile document directory for publicTypeIndex.`;
|
|
1740
|
+
warn(message);
|
|
1741
|
+
}
|
|
1742
|
+
let publicTypeIndex;
|
|
1743
|
+
try {
|
|
1744
|
+
const existingPublicTypeIndex = store.any(user, ns.solid('publicTypeIndex'), undefined, profile);
|
|
1745
|
+
if (existingPublicTypeIndex) {
|
|
1746
|
+
publicTypeIndex = existingPublicTypeIndex;
|
|
1747
|
+
}
|
|
1748
|
+
else if (suggestion) {
|
|
1749
|
+
publicTypeIndex = await utilityLogic.followOrCreateLinkWithContentOnCreate(user, ns.solid('publicTypeIndex'), suggestion, profile, publicTypeIndexDocument());
|
|
1750
|
+
}
|
|
1751
|
+
else {
|
|
1752
|
+
publicTypeIndex = null;
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
catch (err) {
|
|
1756
|
+
const message = `User ${user} has no pointer in profile to publicTypeIndex file: ${err}`;
|
|
1757
|
+
warn(message);
|
|
1758
|
+
}
|
|
1759
|
+
const publicScopes = publicTypeIndex ? [{ label: 'public', index: publicTypeIndex, agent: user }] : [];
|
|
1760
|
+
let preferencesFile;
|
|
1761
|
+
try {
|
|
1762
|
+
preferencesFile = await profileLogic.silencedLoadPreferences(user);
|
|
1763
|
+
}
|
|
1764
|
+
catch (err) {
|
|
1765
|
+
preferencesFile = null;
|
|
1766
|
+
}
|
|
1767
|
+
let privateScopes;
|
|
1768
|
+
if (preferencesFile) { // watch out - can be in either as spec was not clear. Legacy is profile.
|
|
1769
|
+
// If there is a legacy one linked from the profile, use that.
|
|
1770
|
+
// Otherwiae use or make one linked from Preferences
|
|
1771
|
+
let suggestedPrivateTypeIndex = null;
|
|
1772
|
+
try {
|
|
1773
|
+
suggestedPrivateTypeIndex = suggestPrivateTypeIndex(preferencesFile);
|
|
1774
|
+
}
|
|
1775
|
+
catch (err) {
|
|
1776
|
+
const message = `User ${user} has no usable preferences document directory for privateTypeIndex.`;
|
|
1777
|
+
warn(message);
|
|
1778
|
+
}
|
|
1779
|
+
let privateTypeIndex;
|
|
1780
|
+
try {
|
|
1781
|
+
const existingPrivateTypeIndex = store.any(user, ns.solid('privateTypeIndex'), undefined, profile);
|
|
1782
|
+
if (existingPrivateTypeIndex) {
|
|
1783
|
+
privateTypeIndex = existingPrivateTypeIndex;
|
|
1784
|
+
}
|
|
1785
|
+
else if (suggestedPrivateTypeIndex) {
|
|
1786
|
+
privateTypeIndex = await utilityLogic.followOrCreateLinkWithContentOnCreate(user, ns.solid('privateTypeIndex'), suggestedPrivateTypeIndex, preferencesFile, privateTypeIndexDocument());
|
|
1787
|
+
}
|
|
1788
|
+
else {
|
|
1789
|
+
privateTypeIndex = null;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
catch (err) {
|
|
1793
|
+
const message = `User ${user} has no pointer in preference file to privateTypeIndex file: ${err}`;
|
|
1794
|
+
warn(message);
|
|
1795
|
+
}
|
|
1796
|
+
privateScopes = privateTypeIndex ? [{ label: 'private', index: privateTypeIndex, agent: user }] : [];
|
|
1797
|
+
}
|
|
1798
|
+
else {
|
|
1799
|
+
privateScopes = [];
|
|
1800
|
+
}
|
|
1801
|
+
const scopes = publicScopes.concat(privateScopes);
|
|
1802
|
+
if (scopes.length === 0)
|
|
1803
|
+
return scopes;
|
|
1804
|
+
const files = scopes.map(scope => scope.index);
|
|
1805
|
+
try {
|
|
1806
|
+
await store.fetcher.load(files);
|
|
1807
|
+
}
|
|
1808
|
+
catch (err) {
|
|
1809
|
+
warn('Problems loading type index: ', err);
|
|
1810
|
+
}
|
|
1811
|
+
return scopes;
|
|
1812
|
+
}
|
|
1813
|
+
async function loadCommunityTypeIndexes(user) {
|
|
1814
|
+
let preferencesFile;
|
|
1815
|
+
try {
|
|
1816
|
+
preferencesFile = await profileLogic.silencedLoadPreferences(user);
|
|
1817
|
+
}
|
|
1818
|
+
catch (err) {
|
|
1819
|
+
const message = `User ${user} has no pointer in profile to preferences file.`;
|
|
1820
|
+
warn(message);
|
|
1821
|
+
}
|
|
1822
|
+
if (preferencesFile) { // For now, pick up communities as simple links from the preferences file.
|
|
1823
|
+
const communities = store.each(user, ns.solid('community'), undefined, preferencesFile).concat(store.each(user, ns.solid('community'), undefined, user.doc()));
|
|
1824
|
+
let result = [];
|
|
1825
|
+
for (const org of communities) {
|
|
1826
|
+
if (org.termType !== 'NamedNode' || !isAbsoluteHttpUri(org.uri)) {
|
|
1827
|
+
warn(`Skipping malformed community node for ${user}: ${org}`);
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
try {
|
|
1831
|
+
result = result.concat(await loadTypeIndexesFor(org));
|
|
1832
|
+
}
|
|
1833
|
+
catch (err) {
|
|
1834
|
+
warn(`Skipping community type indexes for ${org.uri}: ${err}`);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
return result;
|
|
1838
|
+
}
|
|
1839
|
+
return []; // No communities
|
|
1840
|
+
}
|
|
1841
|
+
async function loadAllTypeIndexes(user) {
|
|
1842
|
+
return (await loadTypeIndexesFor(user)).concat(await loadCommunityTypeIndexes(user));
|
|
1843
|
+
}
|
|
1844
|
+
async function getScopedAppInstances(klass, user) {
|
|
1845
|
+
const scopes = await loadAllTypeIndexes(user);
|
|
1846
|
+
let scopedApps = [];
|
|
1847
|
+
for (const scope of scopes) {
|
|
1848
|
+
const scopedApps0 = await getScopedAppsFromIndex(scope, klass);
|
|
1849
|
+
scopedApps = scopedApps.concat(scopedApps0);
|
|
1850
|
+
}
|
|
1851
|
+
return scopedApps;
|
|
1852
|
+
}
|
|
1853
|
+
// This is the function signature which used to be in solid-ui/logic
|
|
1854
|
+
// Recommended to use getScopedAppInstances instead as it provides more information.
|
|
1855
|
+
//
|
|
1856
|
+
async function getAppInstances(klass) {
|
|
1857
|
+
const user = authn.currentUser();
|
|
1858
|
+
if (!user)
|
|
1859
|
+
throw new Error('getAppInstances: Must be logged in to find apps.');
|
|
1860
|
+
const scopedAppInstances = await getScopedAppInstances(klass, user);
|
|
1861
|
+
return scopedAppInstances.map(scoped => scoped.instance);
|
|
1862
|
+
}
|
|
1863
|
+
function docDirUri(node) {
|
|
1864
|
+
const doc = node.doc();
|
|
1865
|
+
const dir = doc.dir();
|
|
1866
|
+
if ((dir === null || dir === void 0 ? void 0 : dir.uri) && isAbsoluteHttpUri(dir.uri))
|
|
1867
|
+
return dir.uri;
|
|
1868
|
+
const docUri = doc.uri;
|
|
1869
|
+
if (!docUri || !isAbsoluteHttpUri(docUri)) {
|
|
1870
|
+
log(`docDirUri: missing or non-http(s) doc uri for ${node === null || node === void 0 ? void 0 : node.uri}`);
|
|
1871
|
+
return null;
|
|
1872
|
+
}
|
|
1873
|
+
const withoutFragment = docUri.split('#')[0];
|
|
1874
|
+
const lastSlash = withoutFragment.lastIndexOf('/');
|
|
1875
|
+
if (lastSlash === -1) {
|
|
1876
|
+
log(`docDirUri: no slash in doc uri ${docUri}`);
|
|
1877
|
+
return null;
|
|
1878
|
+
}
|
|
1879
|
+
return withoutFragment.slice(0, lastSlash + 1);
|
|
1880
|
+
}
|
|
1881
|
+
function suggestPublicTypeIndex(me) {
|
|
1882
|
+
const dirUri = docDirUri(me);
|
|
1883
|
+
if (!dirUri)
|
|
1884
|
+
throw new Error(`suggestPublicTypeIndex: Cannot derive directory for ${me.uri}`);
|
|
1885
|
+
return (0,external_rdflib_namespaceObject.sym)(dirUri + 'publicTypeIndex.ttl');
|
|
1886
|
+
}
|
|
1887
|
+
// Note this one is based off the pref file not the profile
|
|
1888
|
+
function suggestPrivateTypeIndex(preferencesFile) {
|
|
1889
|
+
const dirUri = docDirUri(preferencesFile);
|
|
1890
|
+
if (!dirUri)
|
|
1891
|
+
throw new Error(`suggestPrivateTypeIndex: Cannot derive directory for ${preferencesFile.uri}`);
|
|
1892
|
+
return (0,external_rdflib_namespaceObject.sym)(dirUri + 'privateTypeIndex.ttl');
|
|
1893
|
+
}
|
|
1894
|
+
/*
|
|
1895
|
+
* Register a new app in a type index
|
|
1896
|
+
* used in chat in bookmark.js (solid-ui)
|
|
1897
|
+
* Returns the registration object if successful else null
|
|
1898
|
+
*/
|
|
1899
|
+
async function registerInTypeIndex(instance, index, theClass) {
|
|
1900
|
+
const registration = newThing(index);
|
|
1901
|
+
const ins = [
|
|
1902
|
+
// See https://github.com/solid/solid/blob/main/proposals/data-discovery.md
|
|
1903
|
+
(0,external_rdflib_namespaceObject.st)(registration, ns.rdf('type'), ns.solid('TypeRegistration'), index),
|
|
1904
|
+
(0,external_rdflib_namespaceObject.st)(registration, ns.solid('forClass'), theClass, index),
|
|
1905
|
+
(0,external_rdflib_namespaceObject.st)(registration, ns.solid('instance'), instance, index)
|
|
1906
|
+
];
|
|
1907
|
+
try {
|
|
1908
|
+
await store.updater.update([], ins);
|
|
1909
|
+
}
|
|
1910
|
+
catch (err) {
|
|
1911
|
+
const msg = `Unable to register ${instance} in index ${index}: ${err}`;
|
|
1912
|
+
console.warn(msg);
|
|
1913
|
+
return null;
|
|
1914
|
+
}
|
|
1915
|
+
return registration;
|
|
1916
|
+
}
|
|
1917
|
+
async function deleteTypeIndexRegistration(item) {
|
|
1918
|
+
const reg = store.the(null, ns.solid('instance'), item.instance, item.scope.index);
|
|
1919
|
+
if (!reg)
|
|
1920
|
+
throw new Error(`deleteTypeIndexRegistration: No registration found for ${item.instance}`);
|
|
1921
|
+
const statements = store.statementsMatching(reg, null, null, item.scope.index);
|
|
1922
|
+
await store.updater.update(statements, []);
|
|
1923
|
+
}
|
|
1924
|
+
async function getScopedAppsFromIndex(scope, theClass) {
|
|
1925
|
+
const index = scope.index;
|
|
1926
|
+
const results = [];
|
|
1927
|
+
const registrations = store.statementsMatching(null, ns.solid('instance'), null, index)
|
|
1928
|
+
.concat(store.statementsMatching(null, ns.solid('instanceContainer'), null, index))
|
|
1929
|
+
.map(st => st.subject);
|
|
1930
|
+
for (const reg of registrations) {
|
|
1931
|
+
const klass = store.any(reg, ns.solid('forClass'), null, index);
|
|
1932
|
+
if (!theClass || klass.sameTerm(theClass)) {
|
|
1933
|
+
const instances = store.each(reg, ns.solid('instance'), null, index);
|
|
1934
|
+
for (const instance of instances) {
|
|
1935
|
+
results.push({ instance, type: klass, scope });
|
|
1936
|
+
}
|
|
1937
|
+
const containers = store.each(reg, ns.solid('instanceContainer'), null, index);
|
|
1938
|
+
for (const instance of containers) {
|
|
1939
|
+
await store.fetcher.load(instance);
|
|
1940
|
+
results.push({ instance: (0,external_rdflib_namespaceObject.sym)(instance.value), type: klass, scope });
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
return results;
|
|
1945
|
+
}
|
|
1946
|
+
return {
|
|
1947
|
+
registerInTypeIndex,
|
|
1948
|
+
getRegistrations,
|
|
1949
|
+
loadTypeIndexesFor,
|
|
1950
|
+
loadCommunityTypeIndexes,
|
|
1951
|
+
loadAllTypeIndexes,
|
|
1952
|
+
getScopedAppInstances,
|
|
1953
|
+
getAppInstances,
|
|
1954
|
+
suggestPublicTypeIndex,
|
|
1955
|
+
suggestPrivateTypeIndex,
|
|
1956
|
+
deleteTypeIndexRegistration,
|
|
1957
|
+
getScopedAppsFromIndex
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
;// ./src/util/utilityLogic.ts
|
|
1962
|
+
|
|
1963
|
+
|
|
1964
|
+
|
|
1965
|
+
|
|
1966
|
+
function createUtilityLogic(store, aclLogic, containerLogic) {
|
|
1967
|
+
async function recursiveDelete(containerNode) {
|
|
1968
|
+
try {
|
|
1969
|
+
if (containerLogic.isContainer(containerNode)) {
|
|
1970
|
+
const aclDocUrl = await aclLogic.findAclDocUrl(containerNode);
|
|
1971
|
+
await store.fetcher._fetch(aclDocUrl, { method: 'DELETE' });
|
|
1972
|
+
const containerMembers = await containerLogic.getContainerMembers(containerNode);
|
|
1973
|
+
await Promise.all(containerMembers.map((url) => recursiveDelete(url)));
|
|
1974
|
+
}
|
|
1975
|
+
return store.fetcher._fetch(containerNode.value, { method: 'DELETE' });
|
|
1976
|
+
}
|
|
1977
|
+
catch (e) {
|
|
1978
|
+
log(`Please manually remove ${containerNode.value} from your system.`, e);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Create a resource if it really does not exist.
|
|
1983
|
+
* Be absolutely sure something does not exist before creating a new empty file,
|
|
1984
|
+
* as otherwise existing content could be deleted.
|
|
1985
|
+
*/
|
|
1986
|
+
async function loadOrCreateIfNotExists(doc) {
|
|
1987
|
+
var _a, _b, _c, _d, _e;
|
|
1988
|
+
try {
|
|
1989
|
+
return await store.fetcher.load(doc);
|
|
1990
|
+
}
|
|
1991
|
+
catch (err) {
|
|
1992
|
+
if (((_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
|
|
1993
|
+
try {
|
|
1994
|
+
await store.fetcher.webOperation('PUT', doc.uri, {
|
|
1995
|
+
data: '',
|
|
1996
|
+
contentType: 'text/turtle',
|
|
1997
|
+
headers: { 'If-None-Match': '*' }
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
catch (putErr) {
|
|
2001
|
+
const status = (_c = (_b = putErr === null || putErr === void 0 ? void 0 : putErr.response) === null || _b === void 0 ? void 0 : _b.status) !== null && _c !== void 0 ? _c : putErr === null || putErr === void 0 ? void 0 : putErr.status;
|
|
2002
|
+
if (status !== 412) {
|
|
2003
|
+
const msg = `createIfNotExists: PUT FAILED: ${doc}: ${putErr}`;
|
|
2004
|
+
throw new WebOperationError(msg);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
return await store.fetcher.load(doc);
|
|
2008
|
+
}
|
|
2009
|
+
if (((_d = err === null || err === void 0 ? void 0 : err.response) === null || _d === void 0 ? void 0 : _d.status) === 401) {
|
|
2010
|
+
throw new UnauthorizedError();
|
|
2011
|
+
}
|
|
2012
|
+
if (((_e = err === null || err === void 0 ? void 0 : err.response) === null || _e === void 0 ? void 0 : _e.status) === 403) {
|
|
2013
|
+
if (differentOrigin(doc)) {
|
|
2014
|
+
throw new CrossOriginForbiddenError();
|
|
2015
|
+
}
|
|
2016
|
+
throw new SameOriginForbiddenError();
|
|
2017
|
+
}
|
|
2018
|
+
const msg = `createIfNotExists doc load error: ${doc}: ${err}`;
|
|
2019
|
+
throw new FetchError(err === null || err === void 0 ? void 0 : err.status, `${(err === null || err === void 0 ? void 0 : err.message) || ''}${msg}`);
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
async function loadOrCreateWithContentOnCreate(doc, data) {
|
|
2023
|
+
var _a, _b, _c, _d;
|
|
2024
|
+
try {
|
|
2025
|
+
await store.fetcher.load(doc);
|
|
2026
|
+
return false;
|
|
2027
|
+
}
|
|
2028
|
+
catch (err) {
|
|
2029
|
+
const status = (_b = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.status) !== null && _b !== void 0 ? _b : err === null || err === void 0 ? void 0 : err.status;
|
|
2030
|
+
if (status !== 404) {
|
|
2031
|
+
throw err;
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
try {
|
|
2035
|
+
await store.fetcher.webOperation('PUT', doc.uri, {
|
|
2036
|
+
data,
|
|
2037
|
+
contentType: 'text/turtle',
|
|
2038
|
+
headers: { 'If-None-Match': '*' }
|
|
2039
|
+
});
|
|
2040
|
+
return true;
|
|
2041
|
+
}
|
|
2042
|
+
catch (err) {
|
|
2043
|
+
const status = (_d = (_c = err === null || err === void 0 ? void 0 : err.response) === null || _c === void 0 ? void 0 : _c.status) !== null && _d !== void 0 ? _d : err === null || err === void 0 ? void 0 : err.status;
|
|
2044
|
+
if (status === 412) {
|
|
2045
|
+
// Another client created the resource between our 404 check and PUT.
|
|
2046
|
+
return false;
|
|
2047
|
+
}
|
|
2048
|
+
throw err;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
/*
|
|
2052
|
+
* Follow link from this doc to another thing, or else make a new link.
|
|
2053
|
+
* Returns existing object, or creates it if non-existent.
|
|
2054
|
+
*/
|
|
2055
|
+
async function followOrCreateLink(subject, predicate, object, doc) {
|
|
2056
|
+
await store.fetcher.load(doc);
|
|
2057
|
+
const result = store.any(subject, predicate, null, doc);
|
|
2058
|
+
if (result)
|
|
2059
|
+
return result;
|
|
2060
|
+
if (!store.updater.editable(doc)) {
|
|
2061
|
+
const msg = `followOrCreateLink: cannot edit ${doc.value}`;
|
|
2062
|
+
warn(msg);
|
|
2063
|
+
throw new NotEditableError(msg);
|
|
2064
|
+
}
|
|
2065
|
+
try {
|
|
2066
|
+
await store.updater.update([], [(0,external_rdflib_namespaceObject.st)(subject, predicate, object, doc)]);
|
|
2067
|
+
}
|
|
2068
|
+
catch (err) {
|
|
2069
|
+
const msg = `followOrCreateLink: Error making link in ${doc} to ${object}: ${err}`;
|
|
2070
|
+
warn(msg);
|
|
2071
|
+
throw new WebOperationError(err);
|
|
2072
|
+
}
|
|
2073
|
+
try {
|
|
2074
|
+
await loadOrCreateIfNotExists(object);
|
|
2075
|
+
}
|
|
2076
|
+
catch (err) {
|
|
2077
|
+
warn(`followOrCreateLink: Error loading or saving new linked document: ${object}: ${err}`);
|
|
2078
|
+
throw err;
|
|
2079
|
+
}
|
|
2080
|
+
return object;
|
|
2081
|
+
}
|
|
2082
|
+
async function followOrCreateLinkWithContentOnCreate(subject, predicate, object, doc, data) {
|
|
2083
|
+
await store.fetcher.load(doc);
|
|
2084
|
+
const result = store.any(subject, predicate, null, doc);
|
|
2085
|
+
if (result)
|
|
2086
|
+
return result;
|
|
2087
|
+
if (!store.updater.editable(doc)) {
|
|
2088
|
+
const msg = `followOrCreateLinkWithContentOnCreate: cannot edit ${doc.value}`;
|
|
2089
|
+
warn(msg);
|
|
2090
|
+
throw new NotEditableError(msg);
|
|
2091
|
+
}
|
|
2092
|
+
try {
|
|
2093
|
+
await store.updater.update([], [(0,external_rdflib_namespaceObject.st)(subject, predicate, object, doc)]);
|
|
2094
|
+
}
|
|
2095
|
+
catch (err) {
|
|
2096
|
+
const msg = `followOrCreateLinkWithContentOnCreate: Error making link in ${doc} to ${object}: ${err}`;
|
|
2097
|
+
warn(msg);
|
|
2098
|
+
throw new WebOperationError(msg);
|
|
2099
|
+
}
|
|
2100
|
+
try {
|
|
2101
|
+
await loadOrCreateWithContentOnCreate(object, data);
|
|
2102
|
+
}
|
|
2103
|
+
catch (err) {
|
|
2104
|
+
warn(`followOrCreateLinkWithContentOnCreate: Error loading or saving new linked document: ${object}: ${err}`);
|
|
2105
|
+
throw err;
|
|
2106
|
+
}
|
|
2107
|
+
return object;
|
|
2108
|
+
}
|
|
2109
|
+
// Copied from
|
|
2110
|
+
// https://github.com/solidos/web-access-control-tests/blob/v3.0.0/test/surface/delete.test.ts#L5
|
|
2111
|
+
async function setSinglePeerAccess(options) {
|
|
2112
|
+
let str = [
|
|
2113
|
+
'@prefix acl: <http://www.w3.org/ns/auth/acl#>.',
|
|
2114
|
+
'',
|
|
2115
|
+
'<#alice> a acl:Authorization;\n acl:agent <' + options.ownerWebId + '>;',
|
|
2116
|
+
` acl:accessTo <${options.target}>;`,
|
|
2117
|
+
` acl:default <${options.target}>;`,
|
|
2118
|
+
' acl:mode acl:Read, acl:Write, acl:Control.',
|
|
2119
|
+
''
|
|
2120
|
+
].join('\n');
|
|
2121
|
+
if (options.accessToModes) {
|
|
2122
|
+
str += [
|
|
2123
|
+
'<#bobAccessTo> a acl:Authorization;',
|
|
2124
|
+
` acl:agent <${options.peerWebId}>;`,
|
|
2125
|
+
` acl:accessTo <${options.target}>;`,
|
|
2126
|
+
` acl:mode ${options.accessToModes}.`,
|
|
2127
|
+
''
|
|
2128
|
+
].join('\n');
|
|
2129
|
+
}
|
|
2130
|
+
if (options.defaultModes) {
|
|
2131
|
+
str += [
|
|
2132
|
+
'<#bobDefault> a acl:Authorization;',
|
|
2133
|
+
` acl:agent <${options.peerWebId}>;`,
|
|
2134
|
+
` acl:default <${options.target}>;`,
|
|
2135
|
+
` acl:mode ${options.defaultModes}.`,
|
|
2136
|
+
''
|
|
2137
|
+
].join('\n');
|
|
2138
|
+
}
|
|
2139
|
+
const aclDocUrl = await aclLogic.findAclDocUrl((0,external_rdflib_namespaceObject.sym)(options.target));
|
|
2140
|
+
return store.fetcher._fetch(aclDocUrl, {
|
|
2141
|
+
method: 'PUT',
|
|
2142
|
+
body: str,
|
|
2143
|
+
headers: [['Content-Type', 'text/turtle']]
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
async function createEmptyRdfDoc(doc, comment) {
|
|
2147
|
+
await store.fetcher.webOperation('PUT', doc.uri, {
|
|
2148
|
+
data: `# ${new Date()} ${comment}\n`,
|
|
2149
|
+
contentType: 'text/turtle'
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
return {
|
|
2153
|
+
recursiveDelete,
|
|
2154
|
+
setSinglePeerAccess,
|
|
2155
|
+
createEmptyRdfDoc,
|
|
2156
|
+
followOrCreateLink,
|
|
2157
|
+
followOrCreateLinkWithContentOnCreate,
|
|
2158
|
+
loadOrCreateIfNotExists,
|
|
2159
|
+
loadOrCreateWithContentOnCreate
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
;// ./src/logic/solidLogic.ts
|
|
2164
|
+
|
|
2165
|
+
|
|
2166
|
+
|
|
2167
|
+
|
|
2168
|
+
|
|
2169
|
+
|
|
2170
|
+
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
|
|
2174
|
+
/*
|
|
2175
|
+
** It is important to distinquish `fetch`, a function provided by the browser
|
|
2176
|
+
** and `Fetcher`, a helper object for the rdflib Store which turns it
|
|
2177
|
+
** into a `ConnectedStore` or a `LiveStore`. A Fetcher object is
|
|
2178
|
+
** available at store.fetcher, and `fetch` function at `store.fetcher._fetch`,
|
|
2179
|
+
*/
|
|
2180
|
+
function createSolidLogic(specialFetch, session) {
|
|
2181
|
+
log('SolidLogic: Unique instance created. There should only be one of these.');
|
|
2182
|
+
const store = external_rdflib_namespaceObject.graph();
|
|
2183
|
+
external_rdflib_namespaceObject.fetcher(store, { fetch: specialFetch.fetch }); // Attach a web I/O module, store.fetcher
|
|
2184
|
+
store.updater = new external_rdflib_namespaceObject.UpdateManager(store); // Add real-time live updates store.updater
|
|
2185
|
+
store.features = []; // disable automatic node merging on store load
|
|
2186
|
+
const authn = new SolidAuthnLogic(session);
|
|
2187
|
+
const acl = createAclLogic(store);
|
|
2188
|
+
const containerLogic = createContainerLogic(store);
|
|
2189
|
+
const utilityLogic = createUtilityLogic(store, acl, containerLogic);
|
|
2190
|
+
const profile = createProfileLogic(store, authn, utilityLogic);
|
|
2191
|
+
const chat = createChatLogic(store, profile);
|
|
2192
|
+
const inbox = createInboxLogic(store, profile, utilityLogic, containerLogic, acl);
|
|
2193
|
+
const typeIndex = createTypeIndexLogic(store, authn, profile, utilityLogic);
|
|
2194
|
+
log('SolidAuthnLogic initialized');
|
|
2195
|
+
function load(doc) {
|
|
2196
|
+
return store.fetcher.load(doc);
|
|
2197
|
+
}
|
|
2198
|
+
// @@@@ use the one in rdflib.js when it is available and delete this
|
|
2199
|
+
function updatePromise(del, ins = []) {
|
|
2200
|
+
return new Promise((resolve, reject) => {
|
|
2201
|
+
store.updater.update(del, ins, function (_uri, ok, errorBody) {
|
|
2202
|
+
if (!ok) {
|
|
2203
|
+
reject(new Error(errorBody));
|
|
2204
|
+
}
|
|
2205
|
+
else {
|
|
2206
|
+
resolve();
|
|
2207
|
+
}
|
|
2208
|
+
}); // callback
|
|
2209
|
+
}); // promise
|
|
2210
|
+
}
|
|
2211
|
+
function clearStore() {
|
|
2212
|
+
store.statements.slice().forEach(store.remove.bind(store));
|
|
2213
|
+
}
|
|
2214
|
+
return {
|
|
2215
|
+
store,
|
|
2216
|
+
authn,
|
|
2217
|
+
acl,
|
|
2218
|
+
inbox,
|
|
2219
|
+
chat,
|
|
2220
|
+
profile,
|
|
2221
|
+
typeIndex,
|
|
2222
|
+
load,
|
|
2223
|
+
updatePromise,
|
|
2224
|
+
clearStore
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
;// ./src/logic/solidLogicSingleton.ts
|
|
2229
|
+
|
|
2230
|
+
|
|
2231
|
+
|
|
2232
|
+
const _fetch = async (url, requestInit) => {
|
|
2233
|
+
var _a;
|
|
2234
|
+
const omitCreds = requestInit && requestInit.credentials && requestInit.credentials == 'omit';
|
|
2235
|
+
const sessionAny = authSession;
|
|
2236
|
+
const sessionWebId = ((_a = sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.info) === null || _a === void 0 ? void 0 : _a.webId) || (sessionAny === null || sessionAny === void 0 ? void 0 : sessionAny.webId);
|
|
2237
|
+
if (sessionWebId && !omitCreds) { // see https://github.com/solidos/solidos/issues/114
|
|
2238
|
+
// In fact fetch should respect credentials omit itself
|
|
2239
|
+
const authenticatedFetch = (typeof sessionAny.fetch === 'function')
|
|
2240
|
+
? sessionAny.fetch.bind(sessionAny)
|
|
2241
|
+
: (typeof sessionAny.authFetch === 'function' ? sessionAny.authFetch.bind(sessionAny) : null);
|
|
2242
|
+
if (authenticatedFetch) {
|
|
2243
|
+
return authenticatedFetch(url, requestInit);
|
|
2244
|
+
}
|
|
2245
|
+
return window.fetch(url, requestInit);
|
|
2246
|
+
}
|
|
2247
|
+
else {
|
|
2248
|
+
return window.fetch(url, requestInit);
|
|
2249
|
+
}
|
|
2250
|
+
};
|
|
2251
|
+
// Global singleton pattern to ensure unique store across library versions
|
|
2252
|
+
const SINGLETON_SYMBOL = Symbol.for('solid-logic-singleton');
|
|
2253
|
+
const globalTarget = (typeof window !== 'undefined' ? window : __webpack_require__.g);
|
|
2254
|
+
function getOrCreateSingleton() {
|
|
2255
|
+
if (!globalTarget[SINGLETON_SYMBOL]) {
|
|
2256
|
+
log('SolidLogic: Creating new global singleton instance.');
|
|
2257
|
+
globalTarget[SINGLETON_SYMBOL] = createSolidLogic({ fetch: _fetch }, authSession);
|
|
2258
|
+
log('Unique quadstore initialized.');
|
|
2259
|
+
}
|
|
2260
|
+
else {
|
|
2261
|
+
log('SolidLogic: Using existing global singleton instance.');
|
|
2262
|
+
}
|
|
2263
|
+
return globalTarget[SINGLETON_SYMBOL];
|
|
2264
|
+
}
|
|
2265
|
+
//this const makes solidLogicSingleton global accessible in mashlib
|
|
2266
|
+
const solidLogicSingleton = getOrCreateSingleton();
|
|
2267
|
+
|
|
2268
|
+
|
|
2269
|
+
;// ./src/authn/serverLogout.ts
|
|
2270
|
+
async function performServerSideLogout(options = {}) {
|
|
2271
|
+
if (typeof window === 'undefined') {
|
|
2272
|
+
return false;
|
|
2273
|
+
}
|
|
2274
|
+
const issuer = options.issuer || '';
|
|
2275
|
+
const postLogoutRedirectPath = options.postLogoutRedirectPath || '/';
|
|
2276
|
+
// Provider-specific logout endpoint discovery (OIDC end_session_endpoint).
|
|
2277
|
+
try {
|
|
2278
|
+
if (issuer) {
|
|
2279
|
+
const wellKnownUri = new URL(issuer);
|
|
2280
|
+
wellKnownUri.pathname = '/.well-known/openid-configuration';
|
|
2281
|
+
const wellKnownResult = await fetch(wellKnownUri.toString(), { credentials: 'include' });
|
|
2282
|
+
if (wellKnownResult.status === 200) {
|
|
2283
|
+
const openidConfiguration = await wellKnownResult.json();
|
|
2284
|
+
if (openidConfiguration && openidConfiguration.end_session_endpoint) {
|
|
2285
|
+
await fetch(openidConfiguration.end_session_endpoint, { credentials: 'include' });
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
catch (_err) {
|
|
2291
|
+
// Continue with local logout even if provider logout is unavailable.
|
|
2292
|
+
}
|
|
2293
|
+
// NSS well-known logout endpoint clears cookie-backed server sessions.
|
|
2294
|
+
try {
|
|
2295
|
+
const logoutResponse = await fetch('/.well-known/solid/logout', { credentials: 'include' });
|
|
2296
|
+
if (logoutResponse.ok || logoutResponse.redirected) {
|
|
2297
|
+
window.location.assign(postLogoutRedirectPath);
|
|
2298
|
+
return true;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
catch (_err) {
|
|
2302
|
+
// Not all deployments expose this endpoint.
|
|
2303
|
+
}
|
|
2304
|
+
return false;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
;// ./src/issuer/issuerLogic.ts
|
|
2308
|
+
const DEFAULT_ISSUERS = [
|
|
2309
|
+
{
|
|
2310
|
+
name: 'Solid Community',
|
|
2311
|
+
uri: 'https://solidcommunity.net'
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
name: 'Solid Web',
|
|
2315
|
+
uri: 'https://solidweb.org'
|
|
2316
|
+
},
|
|
2317
|
+
{
|
|
2318
|
+
name: 'Solid Web ME',
|
|
2319
|
+
uri: 'https://solidweb.me'
|
|
2320
|
+
},
|
|
2321
|
+
{
|
|
2322
|
+
name: 'Inrupt.com',
|
|
2323
|
+
uri: 'https://login.inrupt.com'
|
|
2324
|
+
}
|
|
2325
|
+
];
|
|
2326
|
+
/**
|
|
2327
|
+
* @returns - A list of suggested OIDC issuers
|
|
2328
|
+
*/
|
|
2329
|
+
function getSuggestedIssuers() {
|
|
2330
|
+
// Suggest a default list of OIDC issuers
|
|
2331
|
+
const issuers = [...DEFAULT_ISSUERS];
|
|
2332
|
+
// Suggest the current host if not already included
|
|
2333
|
+
const { host, origin } = new URL(location.href);
|
|
2334
|
+
const hosts = issuers.map(({ uri }) => new URL(uri).host);
|
|
2335
|
+
if (!hosts.includes(host)) {
|
|
2336
|
+
issuers.unshift({ name: host, uri: origin });
|
|
2337
|
+
}
|
|
2338
|
+
return issuers;
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
;// ./src/index.ts
|
|
2342
|
+
// Make these variables directly accessible as it is what you need most of the time
|
|
2343
|
+
// This also makes these variable globaly accesible in mashlib
|
|
2344
|
+
|
|
2345
|
+
const authn = solidLogicSingleton.authn;
|
|
2346
|
+
const src_authSession = solidLogicSingleton.authn.authSession;
|
|
2347
|
+
const store = solidLogicSingleton.store;
|
|
2348
|
+
|
|
2349
|
+
|
|
2350
|
+
|
|
2351
|
+
|
|
2352
|
+
|
|
2353
|
+
|
|
2354
|
+
|
|
2355
|
+
|
|
2356
|
+
export { ACL_LINK, CrossOriginForbiddenError, FetchError, NotEditableError, NotFoundError, SameOriginForbiddenError, UnauthorizedError, WebOperationError, appContext, src_authSession as authSession, authn, createTypeIndexLogic, getSuggestedIssuers, offlineTestID, performServerSideLogout, solidLogicSingleton, store };
|
|
2357
|
+
|
|
2358
|
+
//# sourceMappingURL=solid-logic.esm.js.map
|