@lblod/mu-auth-sudo 0.6.0 → 1.0.0-beta.2
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 +14 -3
- package/dist/auth-sudo.js +130 -171
- package/package.json +24 -18
package/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
NPM package for a SPARQL client for mu.semte.ch that overrules access rights in queries through a mu-auth-sudo header.
|
|
3
3
|
|
|
4
4
|
## Usage
|
|
5
|
-
```
|
|
5
|
+
```bash
|
|
6
6
|
npm install @lblod/mu-auth-sudo
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Include the following in your code
|
|
10
|
-
```
|
|
10
|
+
```js
|
|
11
11
|
import { querySudo as query, updateSudo as update } from '@lblod/mu-auth-sudo';
|
|
12
12
|
|
|
13
13
|
|
|
@@ -26,9 +26,20 @@ await update(updateString, extraHeaders);
|
|
|
26
26
|
|
|
27
27
|
// With custom connection options (this should be exceptional, make sure you know what you're doing)
|
|
28
28
|
|
|
29
|
-
const connectionOptions = { sparqlEndpoint: 'http://the.custom.endpoint/sparql', mayRetry: true
|
|
29
|
+
const connectionOptions = { sparqlEndpoint: 'http://the.custom.endpoint/sparql', mayRetry: true, };
|
|
30
|
+
|
|
31
|
+
await update(updateString, extraHeaders, connectionOptions);
|
|
32
|
+
|
|
33
|
+
// Authentication via digest or basic auth
|
|
34
|
+
const connectionOptions = {
|
|
35
|
+
sparqlEndpoint: 'http://the.custom.endpoint/sparql',
|
|
36
|
+
authUser: "dba",
|
|
37
|
+
authPassword: "mypass",
|
|
38
|
+
authType: "digest"
|
|
39
|
+
};
|
|
30
40
|
|
|
31
41
|
await update(updateString, extraHeaders, connectionOptions);
|
|
42
|
+
|
|
32
43
|
```
|
|
33
44
|
|
|
34
45
|
## Logging
|
package/dist/auth-sudo.js
CHANGED
|
@@ -1,187 +1,146 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
5
17
|
});
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.updateSudo = exports.querySudo = void 0;
|
|
30
|
+
const express_http_context_1 = __importDefault(require("express-http-context"));
|
|
31
|
+
const env_var_1 = __importDefault(require("env-var"));
|
|
32
|
+
const node_fetch_1 = __importStar(require("node-fetch"));
|
|
33
|
+
const digest_fetch_1 = __importDefault(require("digest-fetch"));
|
|
34
|
+
const SPARQL_ENDPOINT = env_var_1.default.get('MU_SPARQL_ENDPOINT').required().asString();
|
|
35
|
+
const LOG_SPARQL_ALL = env_var_1.default.get('LOG_SPARQL_ALL').required().asString();
|
|
36
|
+
const LOG_SPARQL_QUERIES = env_var_1.default.get('LOG_SPARQL_QUERIES').default(LOG_SPARQL_ALL).asBool();
|
|
37
|
+
const LOG_SPARQL_UPDATES = env_var_1.default.get('LOG_SPARQL_UPDATES').default(LOG_SPARQL_ALL).asBool();
|
|
38
|
+
const DEBUG_AUTH_HEADERS = env_var_1.default.get('DEBUG_AUTH_HEADERS').required().asBool();
|
|
24
39
|
// The following configuration options are considered optional, but may be overriden as a temporary workaround for issues. Thus, a last resort.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
const RETRY = env_var_1.default.get('SUDO_QUERY_RETRY').default('false').asBool();
|
|
41
|
+
const RETRY_MAX_ATTEMPTS = env_var_1.default.get('SUDO_QUERY_RETRY_MAX_ATTEMPTS').default('5').asInt();
|
|
42
|
+
const RETRY_FOR_HTTP_STATUS_CODES = env_var_1.default.get('SUDO_QUERY_RETRY_FOR_HTTP_STATUS_CODES').default('').asArray();
|
|
43
|
+
const RETRY_FOR_CONNECTION_ERRORS = env_var_1.default.get('SUDO_QUERY_RETRY_FOR_CONNECTION_ERRORS').default('ECONNRESET,ETIMEDOUT,EAI_AGAIN').asArray();
|
|
44
|
+
const RETRY_TIMEOUT_INCREMENT_FACTOR = env_var_1.default.get('SUDO_QUERY_RETRY_TIMEOUT_INCREMENT_FACTOR').default('0.3').asFloat();
|
|
45
|
+
function defaultHeaders() {
|
|
46
|
+
const headers = new node_fetch_1.Headers();
|
|
47
|
+
headers.set('content-type', 'application/x-www-form-urlencoded');
|
|
48
|
+
headers.set('mu-auth-sudo', 'true');
|
|
49
|
+
headers.set('Accept', 'application/sparql-results+json');
|
|
50
|
+
if (express_http_context_1.default.get('request')) {
|
|
51
|
+
headers.set('mu-session-id', express_http_context_1.default.get('request').get('mu-session-id'));
|
|
52
|
+
headers.set('mu-call-id', express_http_context_1.default.get('request').get('mu-call-id'));
|
|
53
|
+
}
|
|
54
|
+
return headers;
|
|
55
|
+
}
|
|
56
|
+
async function executeRawQuery(queryString, extraHeaders = {}, connectionOptions = {}, attempt = 0) {
|
|
57
|
+
const sparqlEndpoint = connectionOptions.sparqlEndpoint ?? SPARQL_ENDPOINT;
|
|
58
|
+
const headers = defaultHeaders();
|
|
59
|
+
for (const key of Object.keys(extraHeaders)) {
|
|
60
|
+
headers.append(key, extraHeaders[key]);
|
|
61
|
+
}
|
|
62
|
+
if (DEBUG_AUTH_HEADERS) {
|
|
63
|
+
const stringifiedHeaders = Array.from(headers.entries())
|
|
64
|
+
.filter(([key, value]) => key.startsWith('mu-'))
|
|
65
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
66
|
+
.join("\n");
|
|
67
|
+
console.log(`Headers set on SPARQL client: ${stringifiedHeaders}`);
|
|
47
68
|
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
if (_expressHttpContext2.default.get('request')) {
|
|
51
|
-
options.requestDefaults.headers['mu-session-id'] = _expressHttpContext2.default.get('request').get('mu-session-id');
|
|
52
|
-
options.requestDefaults.headers['mu-call-id'] = _expressHttpContext2.default.get('request').get('mu-call-id');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (extraHeaders) {
|
|
56
|
-
var _iteratorNormalCompletion = true;
|
|
57
|
-
var _didIteratorError = false;
|
|
58
|
-
var _iteratorError = undefined;
|
|
59
|
-
|
|
60
69
|
try {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
// note that URLSearchParams is used because it correctly encodes for form-urlencoded
|
|
71
|
+
const formData = new URLSearchParams();
|
|
72
|
+
formData.set("query", queryString);
|
|
73
|
+
headers.append('Content-Length', formData.toString().length.toString());
|
|
74
|
+
let response;
|
|
75
|
+
if (connectionOptions.authUser && connectionOptions.authPassword) {
|
|
76
|
+
const client = new digest_fetch_1.default(connectionOptions.authUser, connectionOptions.authPassword, { basic: connectionOptions.authType === 'basic' });
|
|
77
|
+
response = await client.fetch(sparqlEndpoint, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
body: formData.toString(),
|
|
80
|
+
headers
|
|
81
|
+
});
|
|
73
82
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
else {
|
|
84
|
+
response = await (0, node_fetch_1.default)(sparqlEndpoint, {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
body: formData.toString(),
|
|
87
|
+
headers
|
|
88
|
+
});
|
|
77
89
|
}
|
|
78
|
-
|
|
90
|
+
return await response.json();
|
|
79
91
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (LOG_SPARQL_QUERIES) {
|
|
96
|
-
console.log(queryString);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
|
|
101
|
-
var response = await sudoSparqlClient(extraHeaders, connectionOptions).query(queryString).executeRaw();
|
|
102
|
-
return maybeParseJSON(response.body);
|
|
103
|
-
} catch (ex) {
|
|
104
|
-
|
|
105
|
-
if (mayRetry(ex, attempt, connectionOptions)) {
|
|
106
|
-
|
|
107
|
-
attempt += 1;
|
|
108
|
-
|
|
109
|
-
var sleepTime = nextAttemptTimeout(attempt);
|
|
110
|
-
console.log('Sleeping ' + sleepTime + ' ms before next attempt');
|
|
111
|
-
await new Promise(function (r) {
|
|
112
|
-
return setTimeout(r, sleepTime);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
return await executeRawQuery(queryString, extraHeaders, connectionOptions, attempt);
|
|
116
|
-
} else {
|
|
117
|
-
console.log('Failed Query:\n ' + queryString);
|
|
118
|
-
throw ex;
|
|
92
|
+
catch (ex) {
|
|
93
|
+
if (mayRetry(ex, attempt, connectionOptions)) {
|
|
94
|
+
attempt += 1;
|
|
95
|
+
const sleepTime = nextAttemptTimeout(attempt);
|
|
96
|
+
console.log(`Sleeping ${sleepTime} ms before next attempt`);
|
|
97
|
+
await new Promise(r => setTimeout(r, sleepTime));
|
|
98
|
+
return await executeRawQuery(queryString, extraHeaders, connectionOptions, attempt);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log(`Failed Query:
|
|
102
|
+
${queryString}`);
|
|
103
|
+
throw ex;
|
|
104
|
+
}
|
|
119
105
|
}
|
|
120
|
-
}
|
|
121
106
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (LOG_SPARQL_QUERIES) {
|
|
128
|
-
console.log(queryString);
|
|
129
|
-
}
|
|
130
|
-
return executeRawQuery(queryString, extraHeaders, connectionOptions);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function updateSudo(queryString) {
|
|
134
|
-
var extraHeaders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
135
|
-
var connectionOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
136
|
-
|
|
137
|
-
if (LOG_SPARQL_UPDATES) {
|
|
138
|
-
console.log(queryString);
|
|
139
|
-
}
|
|
140
|
-
return executeRawQuery(queryString, extraHeaders, connectionOptions);
|
|
107
|
+
function querySudo(queryString, extraHeaders = {}, connectionOptions = {}) {
|
|
108
|
+
if (LOG_SPARQL_QUERIES) {
|
|
109
|
+
console.log(queryString);
|
|
110
|
+
}
|
|
111
|
+
return executeRawQuery(queryString, extraHeaders, connectionOptions);
|
|
141
112
|
}
|
|
142
|
-
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
113
|
+
exports.querySudo = querySudo;
|
|
114
|
+
function updateSudo(queryString, extraHeaders = {}, connectionOptions = {}) {
|
|
115
|
+
if (LOG_SPARQL_UPDATES) {
|
|
116
|
+
console.log(queryString);
|
|
117
|
+
}
|
|
118
|
+
return executeRawQuery(queryString, extraHeaders, connectionOptions);
|
|
150
119
|
}
|
|
151
|
-
|
|
152
|
-
function mayRetry(error, attempt) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
var mayRetry = false;
|
|
159
|
-
|
|
160
|
-
if (!(RETRY || connectionOptions.mayRetry)) {
|
|
161
|
-
mayRetry = false;
|
|
162
|
-
} else if (attempt < RETRY_MAX_ATTEMPTS) {
|
|
163
|
-
if (error.code && RETRY_FOR_CONNECTION_ERRORS.includes(error.code)) {
|
|
164
|
-
mayRetry = true;
|
|
165
|
-
} else if (error.httpStatus && RETRY_FOR_HTTP_STATUS_CODES.includes('' + error.httpStatus)) {
|
|
166
|
-
mayRetry = true;
|
|
120
|
+
exports.updateSudo = updateSudo;
|
|
121
|
+
function mayRetry(error, attempt, connectionOptions = {}) {
|
|
122
|
+
console.log(`Checking retry allowed for error: ${error} and attempt: ${attempt}`);
|
|
123
|
+
let mayRetry = false;
|
|
124
|
+
if (!(RETRY || connectionOptions.mayRetry)) {
|
|
125
|
+
mayRetry = false;
|
|
167
126
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
127
|
+
else if (attempt < RETRY_MAX_ATTEMPTS) {
|
|
128
|
+
if (error.code && RETRY_FOR_CONNECTION_ERRORS.includes(error.code)) {
|
|
129
|
+
mayRetry = true;
|
|
130
|
+
}
|
|
131
|
+
else if (error.httpStatus && RETRY_FOR_HTTP_STATUS_CODES.includes(`${error.httpStatus}`)) {
|
|
132
|
+
mayRetry = true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
console.log(`Retry allowed? ${mayRetry}`);
|
|
136
|
+
return mayRetry;
|
|
173
137
|
}
|
|
174
|
-
|
|
175
138
|
function nextAttemptTimeout(attempt) {
|
|
176
|
-
|
|
177
|
-
|
|
139
|
+
// expected to be milliseconds
|
|
140
|
+
return Math.round(Math.exp(RETRY_TIMEOUT_INCREMENT_FACTOR * attempt + 10));
|
|
178
141
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
updateSudo: updateSudo
|
|
142
|
+
const defaultExport = {
|
|
143
|
+
querySudo,
|
|
144
|
+
updateSudo
|
|
183
145
|
};
|
|
184
|
-
|
|
185
|
-
exports.default = _exports;
|
|
186
|
-
exports.querySudo = querySudo;
|
|
187
|
-
exports.updateSudo = updateSudo;
|
|
146
|
+
exports.default = defaultExport;
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lblod/mu-auth-sudo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
4
4
|
"description": "this package provides an alternative sparql client for the mu-javascript-template that has sudo rights.",
|
|
5
5
|
"main": "dist/auth-sudo.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"prepare": "npm run build",
|
|
9
|
+
"lint": "tslint -p tsconfig.json",
|
|
7
10
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
-
"
|
|
9
|
-
"prepublish": "rm -rf dist && babel src --out-dir dist/"
|
|
11
|
+
"release": "release-it"
|
|
10
12
|
},
|
|
11
13
|
"repository": {
|
|
12
14
|
"type": "git",
|
|
@@ -22,24 +24,28 @@
|
|
|
22
24
|
"bugs": {
|
|
23
25
|
"url": "https://github.com/lblod/mu-auth-sudo/issues"
|
|
24
26
|
},
|
|
25
|
-
"babel": {
|
|
26
|
-
"presets": [
|
|
27
|
-
"es2015"
|
|
28
|
-
],
|
|
29
|
-
"plugins": [
|
|
30
|
-
"add-module-exports"
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
27
|
"homepage": "https://github.com/lblod/mu-auth-sudo#readme",
|
|
34
28
|
"devDependencies": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
29
|
+
"@types/node-fetch": "^2.6.2",
|
|
30
|
+
"express-http-context": "^1.2.4",
|
|
31
|
+
"prettier": "^2.7.1",
|
|
32
|
+
"release-it": "^15.5.0",
|
|
33
|
+
"tslint": "^6.1.3",
|
|
34
|
+
"tslint-config-prettier": "^1.18.0",
|
|
35
|
+
"typescript": "^4.8.4"
|
|
40
36
|
},
|
|
41
37
|
"dependencies": {
|
|
38
|
+
"digest-fetch": "^1.3.0",
|
|
42
39
|
"env-var": "^7.0.1",
|
|
43
|
-
"
|
|
44
|
-
}
|
|
40
|
+
"node-fetch": "^2.6.7"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"express-http-context": "~1.2.4"
|
|
44
|
+
},
|
|
45
|
+
"volta": {
|
|
46
|
+
"node": "14.20.1"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist/**/*"
|
|
50
|
+
]
|
|
45
51
|
}
|