@hitchy/plugin-odem-rest 0.4.8 → 0.5.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/LICENSE +1 -1
- package/api/services/odem-rest/cors.js +204 -4
- package/coverage/tmp/coverage-17048-1649848422081-0.json +1 -0
- package/index.js +64 -81
- package/package.json +11 -9
package/LICENSE
CHANGED
|
@@ -29,8 +29,30 @@
|
|
|
29
29
|
"use strict";
|
|
30
30
|
|
|
31
31
|
module.exports = function() {
|
|
32
|
+
const Config = this.config;
|
|
32
33
|
const Services = this.runtime.services;
|
|
33
34
|
|
|
35
|
+
const CommonlyAcceptedHeaders = [
|
|
36
|
+
"Accept", "Accept-Language", "Authorization", "Content-Language",
|
|
37
|
+
"Content-Type", "If-Match", "If-None-Match", "If-Modified-Since",
|
|
38
|
+
"If-Unmodified-Since", "If-Range", "DNT", "Expect"
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const Accepted = {
|
|
42
|
+
schema: {
|
|
43
|
+
methods: ["GET"],
|
|
44
|
+
headers: CommonlyAcceptedHeaders,
|
|
45
|
+
},
|
|
46
|
+
model: {
|
|
47
|
+
methods: [ "GET", "POST", "HEAD" ],
|
|
48
|
+
headers: CommonlyAcceptedHeaders,
|
|
49
|
+
},
|
|
50
|
+
item: {
|
|
51
|
+
methods: [ "GET", "PUT", "PATCH", "HEAD", "DELETE" ],
|
|
52
|
+
headers: CommonlyAcceptedHeaders,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
34
56
|
/**
|
|
35
57
|
* Handles CORS-related behaviours.
|
|
36
58
|
*
|
|
@@ -44,8 +66,38 @@ module.exports = function() {
|
|
|
44
66
|
* @returns {HitchyRequestPolicyHandler} generated function suitable for registering as routing policy handler
|
|
45
67
|
*/
|
|
46
68
|
static getCommonRequestFilter() {
|
|
47
|
-
return (
|
|
48
|
-
|
|
69
|
+
return ( req, res, next ) => {
|
|
70
|
+
const origin = req.headers.origin;
|
|
71
|
+
|
|
72
|
+
if ( origin != null ) {
|
|
73
|
+
const { model: { origins } = {} } = Config;
|
|
74
|
+
|
|
75
|
+
if ( !origins || ( Array.isArray( origins ) && origins.indexOf( origin ) > -1 ) ) {
|
|
76
|
+
res.setHeader( "Access-Control-Allow-Origin", origin );
|
|
77
|
+
res.setHeader( "Access-Control-Max-Age", 600 );
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
next();
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generates function for use as a routing policy filtering CORS-related
|
|
87
|
+
* aspects of requests for all models' schemata.
|
|
88
|
+
*
|
|
89
|
+
* @returns {HitchyRequestPolicyHandler} generated function suitable for registering as routing policy handler
|
|
90
|
+
*/
|
|
91
|
+
static getRequestFilterForSchemata() {
|
|
92
|
+
return ( req, res, next ) => {
|
|
93
|
+
if ( !res.headersSent ) {
|
|
94
|
+
this.handleMethods( null, null, req, res, Accepted.schema.methods );
|
|
95
|
+
|
|
96
|
+
if ( res.hasHeader( "Access-Control-Allow-Origin" ) ) {
|
|
97
|
+
this.handleHeaders( null, null, req, res, Accepted.schema.headers );
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
49
101
|
next();
|
|
50
102
|
};
|
|
51
103
|
}
|
|
@@ -59,8 +111,75 @@ module.exports = function() {
|
|
|
59
111
|
*/
|
|
60
112
|
static getRequestFilterForModel( model ) { // eslint-disable-line no-unused-vars
|
|
61
113
|
return ( req, res, next ) => {
|
|
62
|
-
if (
|
|
63
|
-
|
|
114
|
+
if ( !res.headersSent ) {
|
|
115
|
+
if ( Services.OdemRestSchema.mayBeExposed( req, model ) ) {
|
|
116
|
+
this.handleMethods( model, null, req, res, Accepted.model.methods );
|
|
117
|
+
|
|
118
|
+
if ( res.hasHeader( "Access-Control-Allow-Origin" ) ) {
|
|
119
|
+
this.handleHeaders( model, null, req, res, Accepted.model.headers );
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
// revoke any access permission commonly granted before
|
|
123
|
+
res.removeHeader( "Access-Control-Allow-Origin" );
|
|
124
|
+
res.removeHeader( "Access-Control-Allow-Methods" );
|
|
125
|
+
res.removeHeader( "Access-Control-Allow-Headers" );
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
next();
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Generates function for use as a routing policy filtering CORS-related
|
|
135
|
+
* aspects of requests in scope of provided model.
|
|
136
|
+
*
|
|
137
|
+
* @param {class<Model>} model class of particular model
|
|
138
|
+
* @returns {HitchyRequestPolicyHandler} generated function suitable for registering as routing policy handler
|
|
139
|
+
*/
|
|
140
|
+
static getRequestFilterForModelSchema( model ) { // eslint-disable-line no-unused-vars
|
|
141
|
+
return ( req, res, next ) => {
|
|
142
|
+
if ( !res.headersSent ) {
|
|
143
|
+
if ( Services.OdemRestSchema.mayBeExposed( req, model ) ) {
|
|
144
|
+
this.handleMethods( model, null, req, res, Accepted.schema.methods );
|
|
145
|
+
|
|
146
|
+
if ( res.hasHeader( "Access-Control-Allow-Origin" ) ) {
|
|
147
|
+
this.handleHeaders( model, null, req, res, Accepted.schema.headers );
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
// revoke any access permission commonly granted before
|
|
151
|
+
res.removeHeader( "Access-Control-Allow-Origin" );
|
|
152
|
+
res.removeHeader( "Access-Control-Allow-Methods" );
|
|
153
|
+
res.removeHeader( "Access-Control-Allow-Headers" );
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
next();
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Generates function for use as a routing policy filtering CORS-related
|
|
163
|
+
* aspects of requests in scope of provided model.
|
|
164
|
+
*
|
|
165
|
+
* @param {class<Model>} model class of particular model
|
|
166
|
+
* @returns {HitchyRequestPolicyHandler} generated function suitable for registering as routing policy handler
|
|
167
|
+
*/
|
|
168
|
+
static getRequestFilterForModelItem( model ) { // eslint-disable-line no-unused-vars
|
|
169
|
+
return ( req, res, next ) => {
|
|
170
|
+
if ( !res.headersSent && req.params.uuid !== ".schema" ) {
|
|
171
|
+
if ( Services.OdemRestSchema.mayBeExposed( req, model ) ) {
|
|
172
|
+
this.handleMethods( model, req.params.uuid, req, res, Accepted.item.methods );
|
|
173
|
+
|
|
174
|
+
if ( res.hasHeader( "Access-Control-Allow-Origin" ) ) {
|
|
175
|
+
this.handleHeaders( model, req.params.uuid, req, res, Accepted.item.headers );
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
// revoke any access permission commonly granted before
|
|
179
|
+
res.removeHeader( "Access-Control-Allow-Origin" );
|
|
180
|
+
res.removeHeader( "Access-Control-Allow-Methods" );
|
|
181
|
+
res.removeHeader( "Access-Control-Allow-Headers" );
|
|
182
|
+
}
|
|
64
183
|
}
|
|
65
184
|
|
|
66
185
|
next();
|
|
@@ -81,6 +200,87 @@ module.exports = function() {
|
|
|
81
200
|
next();
|
|
82
201
|
};
|
|
83
202
|
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Injects response header describing available request methods for URL
|
|
206
|
+
* processing selected model and optionally addressed item.
|
|
207
|
+
*
|
|
208
|
+
* @param {class<Model>} model implementation of model selected by request URL
|
|
209
|
+
* @param {string} item UUID of model's item addressed in request URL
|
|
210
|
+
* @param {HitchyIncomingMessage} req request descriptor
|
|
211
|
+
* @param {HitchyServerResponse} res response manager
|
|
212
|
+
* @param {string[]} accepted comma-separated list of methods to accept by default
|
|
213
|
+
* @returns {void}
|
|
214
|
+
* @protected
|
|
215
|
+
*/
|
|
216
|
+
static handleMethods( model, item, req, res, accepted ) {
|
|
217
|
+
const methods = req.headers["access-control-request-method"] || "";
|
|
218
|
+
|
|
219
|
+
if ( methods.length > 0 && res.hasHeader( "Access-Control-Allow-Origin" ) ) {
|
|
220
|
+
// CORS preflight request
|
|
221
|
+
let filtered = methods
|
|
222
|
+
.trim()
|
|
223
|
+
.split( /[\s,]+/ )
|
|
224
|
+
.filter( method => method && accepted.indexOf( method ) > -1 );
|
|
225
|
+
|
|
226
|
+
if ( !filtered.length ) {
|
|
227
|
+
filtered = accepted;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
filtered.sort( ( l, r ) => l.toUpperCase().localeCompare( r.toUpperCase() ) );
|
|
231
|
+
|
|
232
|
+
res.setHeader( "Access-Control-Allow-Methods", filtered.join( "," ) );
|
|
233
|
+
} else if ( req.headers.origin == null ) {
|
|
234
|
+
// legacy OPTIONS request for list of supported methods
|
|
235
|
+
accepted.sort( ( l, r ) => l.toUpperCase().localeCompare( r.toUpperCase() ) );
|
|
236
|
+
|
|
237
|
+
res.setHeader( "Allow", accepted.join( "," ) );
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Injects response header describing available request headers for URL
|
|
243
|
+
* processing selected model and optionally addressed item.
|
|
244
|
+
*
|
|
245
|
+
* @param {class<Model>} model implementation of model selected by request URL
|
|
246
|
+
* @param {string} item UUID of model's item addressed in request URL
|
|
247
|
+
* @param {HitchyIncomingMessage} req request descriptor
|
|
248
|
+
* @param {HitchyServerResponse} res response manager
|
|
249
|
+
* @param {string[]} accepted comma-separated list of accepted headers
|
|
250
|
+
* @returns {void}
|
|
251
|
+
* @protected
|
|
252
|
+
*/
|
|
253
|
+
static handleHeaders( model, item, req, res, accepted ) {
|
|
254
|
+
const headers = req.headers["access-control-request-headers"] || "";
|
|
255
|
+
|
|
256
|
+
if ( headers.length > 0 ) {
|
|
257
|
+
// CORS preflight request
|
|
258
|
+
const _accepted = accepted.map( header => header.toLowerCase() );
|
|
259
|
+
|
|
260
|
+
let filtered = headers
|
|
261
|
+
.trim()
|
|
262
|
+
.split( /[\s,]+/ )
|
|
263
|
+
.filter( method => {
|
|
264
|
+
const name = ( method || "" ).toLowerCase();
|
|
265
|
+
|
|
266
|
+
if ( name && _accepted.indexOf( name ) > -1 ) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// TODO add support for configuration listing permitted headers
|
|
271
|
+
|
|
272
|
+
return name.startsWith( "x-" );
|
|
273
|
+
} );
|
|
274
|
+
|
|
275
|
+
if ( !filtered.length ) {
|
|
276
|
+
filtered = accepted;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
filtered.sort( ( l, r ) => l.toLowerCase().localeCompare( r.toLowerCase() ) );
|
|
280
|
+
|
|
281
|
+
res.setHeader( "Access-Control-Allow-Headers", filtered.join( "," ) );
|
|
282
|
+
}
|
|
283
|
+
}
|
|
84
284
|
}
|
|
85
285
|
|
|
86
286
|
return OdemRestCors;
|